diff options
-rw-r--r-- | editor/app.cpp | 1 | ||||
-rw-r--r-- | editor/app.hpp | 23 | ||||
-rw-r--r-- | editor/imgui-inspect.cpp | 46 | ||||
-rw-r--r-- | editor/imgui-raii.cpp | 12 | ||||
-rw-r--r-- | editor/imgui-raii.hpp | 3 | ||||
-rw-r--r-- | editor/imgui-scenery.cpp | 4 | ||||
-rw-r--r-- | editor/imgui.cpp | 65 | ||||
-rw-r--r-- | editor/inspect-types.cpp | 7 | ||||
-rw-r--r-- | editor/save.cpp | 1 | ||||
-rw-r--r-- | editor/update.cpp | 45 | ||||
-rw-r--r-- | loader/loader.cpp | 11 | ||||
-rw-r--r-- | loader/loader.hpp | 1 | ||||
-rw-r--r-- | src/local-coords.hpp | 1 | ||||
-rw-r--r-- | src/scenery.cpp | 2 |
14 files changed, 185 insertions, 37 deletions
diff --git a/editor/app.cpp b/editor/app.cpp index a4455426..ac456c22 100644 --- a/editor/app.cpp +++ b/editor/app.cpp @@ -24,6 +24,7 @@ app::app(fm_settings&& opts) : chunk_coords coord{0 ,0}; maybe_initialize_chunk_(coord, w[coord]); reset_camera_offset(); + inspectors.reserve(16); } app::~app() diff --git a/editor/app.hpp b/editor/app.hpp index 76919533..526582ae 100644 --- a/editor/app.hpp +++ b/editor/app.hpp @@ -27,7 +27,6 @@ struct anim_atlas; struct cursor_state final { Optional<Vector2i> pixel; Optional<global_coords> tile; - bool in_imgui = false; }; @@ -39,6 +38,17 @@ enum class Cursor: std::uint32_t { No, Hand, Hidden, HiddenLocked, }; +enum class popup_target_type : unsigned char { + none, scenery, +}; + +struct popup_target final { + chunk_coords c; + local_coords pos; + popup_target_type target = popup_target_type::none; + bool operator==(const popup_target&) const; +}; + struct app final : floormat_app { static int run_from_argv(int argc, const char* const* argv); @@ -96,6 +106,7 @@ private: void draw_collision_boxes(); void draw_editor_pane(float main_menu_height); void draw_inspector(); + bool check_inspector_exists(popup_target p); void draw_editor_tile_pane_atlas(tile_editor& ed, StringView name, const std::shared_ptr<tile_atlas>& atlas); void draw_editor_scenery_pane(scenery_editor& ed); void set_cursor_from_imgui(); @@ -105,10 +116,14 @@ private: float draw_main_menu(); void draw_fps(); void draw_tile_under_cursor(); + void do_popup_menu(); + void do_open_popup(); + void kill_popups(bool hard); void render_menu(); void do_key(key k, int mods); void do_key(key k); + void do_set_mode(editor_mode mode); void do_rotate(bool backward); void apply_commands(const key_set& k); int get_key_modifiers(); @@ -130,9 +145,13 @@ private: editor _editor; key_set keys; std::array<int, key_set::COUNT> key_modifiers = {}; + std::vector<popup_target> inspectors; cursor_state cursor; - Optional<global_coords> inspected_scenery; + popup_target _popup_target; bool _enable_render_bboxes : 1 = false; + bool _pending_popup : 1 = false; + + static const StringView SCENERY_POPUP_NAME; }; } // namespace floormat diff --git a/editor/imgui-inspect.cpp b/editor/imgui-inspect.cpp index d4bf9da2..3b17c106 100644 --- a/editor/imgui-inspect.cpp +++ b/editor/imgui-inspect.cpp @@ -1,10 +1,13 @@ #include "app.hpp" +#include "compat/format.hpp" #include "inspect.hpp" #include "main/clickable.hpp" #include "floormat/main.hpp" #include "src/world.hpp" +#include "src/anim-atlas.hpp" #include "imgui-raii.hpp" #include "chunk.inl" +#include "loader/loader.hpp" namespace floormat { @@ -14,21 +17,40 @@ void app::draw_inspector() { auto b = push_id("inspector"); auto& w = M->world(); - if (cursor.pixel) - if (const auto* sc = find_clickable_scenery(cursor.pixel)) - inspected_scenery = {InPlaceInit, sc->chunk, sc->pos}; - if (inspected_scenery) + + constexpr auto max_inspectors = 4; // todo change later to 32 + if (auto size = inspectors.size(); size > max_inspectors) + { + auto end = inspectors.begin() + (std::ptrdiff_t)size - max_inspectors; + inspectors.erase(inspectors.begin(), end); + } + + const auto dpi = M->dpi_scale(); + + for (auto i = inspectors.size()-1; i != -1_uz; i--) { - auto [c, t] = w[*inspected_scenery]; - if (auto s = t.scenery()) + auto [ch, pos, target] = inspectors[i]; + auto [c, t] = w[{ch, pos}]; + auto s = t.scenery(); + + if (!s) { - char buf[32]; std::snprintf(buf, sizeof buf, "i_0x%p", (void*)&s); - auto b = push_id(buf); - auto dpi = M->dpi_scale(); - ImGui::SetNextWindowSize({300*dpi[0], 0}); - auto b2 = begin_window("inspector"_s); - c.with_scenery_bbox_update(s.index(), [&] { entities::inspect_type(s); }); + inspectors.erase(inspectors.begin() + (int)i); + continue; } + + char buf[128]; + snformat(buf, "i-{}-{}x{}-{}x{}"_cf, (int)target, ch.x, ch.y, (int)pos.x, (int)pos.y); + + auto b1 = push_id(buf); + ImGui::SetNextWindowSize({300*dpi[0], 0}); + auto name = loader.strip_prefix(s.atlas->name()); + snformat(buf, "{} ({}x{} -> {}x{})"_cf, name, ch.x, ch.y, (int)pos.x, (int)pos.y); + bool is_open = true; + if (auto b2 = begin_window(buf, &is_open)) + c.with_scenery_bbox_update(s.index(), [&] { return entities::inspect_type(s); }); + else if (!is_open) + inspectors.erase(inspectors.begin() + (int)i); } } diff --git a/editor/imgui-raii.cpp b/editor/imgui-raii.cpp index 7a5bc340..e499f21c 100644 --- a/editor/imgui-raii.cpp +++ b/editor/imgui-raii.cpp @@ -81,6 +81,14 @@ raii_wrapper begin_combo(StringView name, StringView preview, ImGuiComboFlags fl return {}; } +raii_wrapper begin_popup(StringView name, ImGuiWindowFlags flags) +{ + if (ImGui::BeginPopup(name.data(), flags)) + return {&ImGui::EndPopup}; + else + return {}; +} + raii_wrapper begin_list_box(Containers::StringView name, ImVec2 size) { if (ImGui::BeginListBox(name.data(), size)) @@ -113,11 +121,11 @@ raii_wrapper begin_main_menu() return {}; } -raii_wrapper begin_window(Containers::StringView name, ImGuiWindowFlags flags) +raii_wrapper begin_window(Containers::StringView name, bool* p_open, ImGuiWindowFlags flags) { if (name.isEmpty()) name = "floormat editor"; - if (ImGui::Begin(name.data(), nullptr, flags)) + if (ImGui::Begin(name.data(), p_open, flags)) return {&ImGui::End}; else return {}; diff --git a/editor/imgui-raii.hpp b/editor/imgui-raii.hpp index 5b20415b..8df8de38 100644 --- a/editor/imgui-raii.hpp +++ b/editor/imgui-raii.hpp @@ -23,7 +23,7 @@ private: F dtor = nullptr; }; -[[nodiscard]] raii_wrapper begin_window(StringView name = {}, ImGuiWindowFlags flags = ImGuiWindowFlags_None); +[[nodiscard]] raii_wrapper begin_window(StringView name = {}, bool* p_open = nullptr, ImGuiWindowFlags flags = ImGuiWindowFlags_None); [[nodiscard]] raii_wrapper begin_main_menu(); [[nodiscard]] raii_wrapper begin_menu(StringView name, bool enabled = true); [[nodiscard]] raii_wrapper begin_list_box(StringView name, ImVec2 size = {}); @@ -31,6 +31,7 @@ private: [[nodiscard]] raii_wrapper tree_node(StringView name, ImGuiTreeNodeFlags flags = ImGuiTreeNodeFlags_None); [[nodiscard]] raii_wrapper begin_disabled(bool is_disabled = true); [[nodiscard]] raii_wrapper begin_combo(StringView name, StringView preview, ImGuiComboFlags flags = 0); +[[nodiscard]] raii_wrapper begin_popup(StringView name, ImGuiWindowFlags flags = 0); [[nodiscard]] raii_wrapper push_style_var(ImGuiStyleVar_ var, Vector2 value); [[nodiscard]] raii_wrapper push_style_var(ImGuiStyleVar_ var, float value); diff --git a/editor/imgui-scenery.cpp b/editor/imgui-scenery.cpp index 85e7516f..e418924c 100644 --- a/editor/imgui-scenery.cpp +++ b/editor/imgui-scenery.cpp @@ -79,9 +79,7 @@ void app::draw_editor_scenery_pane(scenery_editor& ed) } if (ImGui::TableSetColumnIndex(3)) { - StringView name = scenery.proto.atlas->name(); - if (name.hasPrefix(loader.SCENERY_PATH)) - name = name.exceptPrefix(loader.SCENERY_PATH.size()); + StringView name = loader.strip_prefix(scenery.proto.atlas->name()); if (auto last = name.findLast('/')) name = name.prefix(last.data()); else diff --git a/editor/imgui.cpp b/editor/imgui.cpp index ed2e1482..b8173eb3 100644 --- a/editor/imgui.cpp +++ b/editor/imgui.cpp @@ -2,12 +2,16 @@ #include "floormat/main.hpp" #include "compat/format.hpp" #include "imgui-raii.hpp" +#include "src/world.hpp" +#include <Corrade/Containers/Optional.h> #include <Magnum/Math/Color.h> namespace floormat { using namespace floormat::imgui; +bool popup_target::operator==(const popup_target&) const = default; + void app::init_imgui(Vector2i size) { if (!_imgui.context()) @@ -104,6 +108,8 @@ void app::draw_ui() draw_tile_under_cursor(); if (_editor.mode() == editor_mode::none) draw_inspector(); + if (_popup_target.target != popup_target_type::none) + do_popup_menu(); ImGui::EndFrame(); } @@ -179,7 +185,7 @@ void app::draw_editor_pane(float main_menu_height) ImGui::SetNextFrameWantCaptureKeyboard(false); ImGui::SetNextWindowSize({425 * dpi[0], window_size[1] - main_menu_height - style.WindowPadding.y}); if (const auto flags = ImGuiWindowFlags_(ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoSavedSettings); - auto b = begin_window({}, flags)) + auto b = begin_window({}, nullptr, flags)) { const auto b2 = push_id("editor-pane"); if (auto b3 = begin_list_box("##atlases", {-FLT_MIN, -1})) @@ -194,12 +200,59 @@ void app::draw_editor_pane(float main_menu_height) } } -void app::do_escape() +const StringView app::SCENERY_POPUP_NAME = "##scenery-popup"_s; + +void app::do_open_popup() +{ + fm_assert(_popup_target.target != popup_target_type::none); + _pending_popup = true; +} + +bool app::check_inspector_exists(popup_target p) +{ + for (const auto& p2 : inspectors) + if (p2 == p) + return true; + return false; +} + +void app::do_popup_menu() +{ + fm_assert(_popup_target.target != popup_target_type::none); + auto& w = M->world(); + + auto b0 = push_id(SCENERY_POPUP_NAME); + + if (_pending_popup) + { + _pending_popup = false; + fm_assert(_popup_target.target != popup_target_type::none); + ImGui::OpenPopup(SCENERY_POPUP_NAME.data(), ImGuiPopupFlags_NoOpenOverItems); + } + + if (auto b1 = begin_popup(SCENERY_POPUP_NAME)) + { + auto [ch, pos, target] = _popup_target; + //if (_popup_target.target != popup_target_type::scenery) {...} + auto [c, t] = w[{ch, pos}]; + auto sc = t.scenery(); + const bool b_act = sc.can_activate(), b_ins = sc && !check_inspector_exists(_popup_target); + if (ImGui::MenuItem("Activate", nullptr, false, b_act)) + sc.activate(); + if (ImGui::MenuItem("Inspect", nullptr, false, b_ins)) + inspectors.push_back(std::exchange(_popup_target, {})); + } +} + +void app::kill_popups(bool hard) { - if (auto* ed = _editor.current_scenery_editor()) - ed->clear_selection(); - if (auto* ed = _editor.current_tile_editor()) - ed->clear_selection(); + _popup_target = { .target = popup_target_type::none }; + + if (hard) + { + inspectors.clear(); + } + ImGui::FocusWindow(nullptr); } diff --git a/editor/inspect-types.cpp b/editor/inspect-types.cpp index 185cfad7..8f84ebd7 100644 --- a/editor/inspect-types.cpp +++ b/editor/inspect-types.cpp @@ -27,12 +27,7 @@ struct entity_accessors<scenery_ref> { using frame_t = scenery::frame_t; return std::tuple{ entity::type<StringView>::field{"name"_s, - [](const scenery_ref& x) { - StringView name = x.atlas->name(); - if (name.hasPrefix(loader.SCENERY_PATH)) - name = name.exceptPrefix(loader.SCENERY_PATH.size()); - return name; - }, + [](const scenery_ref& x) { return loader.strip_prefix(x.atlas->name()); }, [](scenery_ref&, StringView) {}, constantly(field_status::readonly), }, diff --git a/editor/save.cpp b/editor/save.cpp index c05f3e5f..8c33b435 100644 --- a/editor/save.cpp +++ b/editor/save.cpp @@ -36,6 +36,7 @@ void app::do_quicksave() void app::do_quickload() { + kill_popups(true); if (!ensure_save_directory()) return; if (!Path::exists(quicksave_file)) diff --git a/editor/update.cpp b/editor/update.cpp index 62dcc977..3c8f9777 100644 --- a/editor/update.cpp +++ b/editor/update.cpp @@ -53,6 +53,11 @@ void app::do_mouse_up_down(std::uint8_t button, bool is_down, int mods) auto& w = M->world(); update_cursor_tile(cursor.pixel); + if (is_down && !cursor.in_imgui) + { + _popup_target = {}; + } + if (is_down && cursor.tile && !cursor.in_imgui) { switch (_editor.mode()) @@ -69,6 +74,22 @@ void app::do_mouse_up_down(std::uint8_t button, bool is_down, int mods) return (void)s.activate(); } } + // TODO it should open on mouseup if still on the same item as on mousedown + else if (button == mouse_button_right) + { + if (auto* cl = find_clickable_scenery(*cursor.pixel)) + { + auto [c, t] = w[{cl->chunk, cl->pos}]; + if (auto s = t.scenery()) + { + _popup_target = { + .c = cl->chunk, .pos = cl->pos, + .target = popup_target_type::scenery, + }; + do_open_popup(); + } + } + } break; case editor_mode::floor: case editor_mode::walls: @@ -115,6 +136,22 @@ void app::do_rotate(bool backward) } } +void app::do_set_mode(editor_mode mode) +{ + if (mode != _editor.mode()) + kill_popups(true); + _editor.set_mode(mode); +} + +void app::do_escape() +{ + if (auto* ed = _editor.current_scenery_editor()) + ed->clear_selection(); + if (auto* ed = _editor.current_tile_editor()) + ed->clear_selection(); + kill_popups(false); +} + void app::do_key(key k, int mods) { (void)mods; @@ -127,13 +164,13 @@ void app::do_key(key k, int mods) case key_rotate_tile: return do_rotate(false); case key_mode_none: - return _editor.set_mode(editor_mode::none); + return do_set_mode(editor_mode::none); case key_mode_floor: - return _editor.set_mode(editor_mode::floor); + return do_set_mode(editor_mode::floor); case key_mode_walls: - return _editor.set_mode(editor_mode::walls); + return do_set_mode(editor_mode::walls); case key_mode_scenery: - return _editor.set_mode(editor_mode::scenery); + return do_set_mode(editor_mode::scenery); case key_mode_collisions: return void(_enable_render_bboxes = !_enable_render_bboxes); case key_quicksave: diff --git a/loader/loader.cpp b/loader/loader.cpp index a07f96be..e9401385 100644 --- a/loader/loader.cpp +++ b/loader/loader.cpp @@ -22,6 +22,17 @@ loader_& loader = loader_::default_loader(); loader_::loader_() = default; loader_::~loader_() = default; +StringView loader_::strip_prefix(StringView name) +{ + if (name.hasPrefix(IMAGE_PATH)) + return name.exceptPrefix(IMAGE_PATH.size()); + if (name.hasPrefix(ANIM_PATH)) + return name.exceptPrefix(ANIM_PATH.size()); + if (name.hasPrefix(SCENERY_PATH)) + return name.exceptPrefix(SCENERY_PATH.size()); + return name; +} + const StringView loader_::IMAGE_PATH = "share/floormat/images/"_s; const StringView loader_::ANIM_PATH = "share/floormat/anim/"_s; const StringView loader_::SCENERY_PATH = "share/floormat/scenery/"_s; diff --git a/loader/loader.hpp b/loader/loader.hpp index 843d0a72..577f45c5 100644 --- a/loader/loader.hpp +++ b/loader/loader.hpp @@ -31,6 +31,7 @@ struct loader_ virtual const std::vector<serialized_scenery>& sceneries() = 0; virtual const scenery_proto& scenery(StringView name) noexcept(false) = 0; virtual StringView startup_directory() noexcept = 0; + static StringView strip_prefix(StringView name); loader_(const loader_&) = delete; loader_& operator=(const loader_&) = delete; diff --git a/src/local-coords.hpp b/src/local-coords.hpp index a26d8c80..eef511d6 100644 --- a/src/local-coords.hpp +++ b/src/local-coords.hpp @@ -15,6 +15,7 @@ struct local_coords final { constexpr local_coords(T x, T y) noexcept; constexpr local_coords(std::uint8_t x, std::uint8_t y) noexcept : x{x}, y{y} {} constexpr std::uint8_t to_index() const noexcept { return y*TILE_MAX_DIM + x; } + constexpr bool operator==(const local_coords&) const noexcept = default; template<typename T> explicit constexpr operator Math::Vector2<T>() const noexcept { return Math::Vector2<T>(T(x), T(y)); } template<typename T> explicit constexpr operator Math::Vector3<T>() const noexcept { return Math::Vector3<T>(T(x), T(y), T(0)); } diff --git a/src/scenery.cpp b/src/scenery.cpp index 17f2066c..fca45411 100644 --- a/src/scenery.cpp +++ b/src/scenery.cpp @@ -77,7 +77,7 @@ void scenery_ref::rotate(rotation new_r) bool scenery_ref::can_activate() const noexcept { - return frame.interactive; + return atlas && frame.interactive; } bool scenery_ref::update(float dt) |