summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--editor/app.cpp1
-rw-r--r--editor/app.hpp23
-rw-r--r--editor/imgui-inspect.cpp46
-rw-r--r--editor/imgui-raii.cpp12
-rw-r--r--editor/imgui-raii.hpp3
-rw-r--r--editor/imgui-scenery.cpp4
-rw-r--r--editor/imgui.cpp65
-rw-r--r--editor/inspect-types.cpp7
-rw-r--r--editor/save.cpp1
-rw-r--r--editor/update.cpp45
-rw-r--r--loader/loader.cpp11
-rw-r--r--loader/loader.hpp1
-rw-r--r--src/local-coords.hpp1
-rw-r--r--src/scenery.cpp2
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)