24#include "imgui/imgui.h"
38 if (object_id < 0x100)
40 if (object_id < 0x200)
42 if (object_id >= 0xF80)
50 static const char* kGroupNames[] = {
65 constexpr size_t kCount =
sizeof(kGroupNames) /
sizeof(kGroupNames[0]);
66 return blockset < kCount ? kGroupNames[blockset] :
"Custom";
71 static const char* kNames[] = {
72 "Nothing",
"Green Rupee",
"Rock",
"Bee",
73 "Heart (4)",
"Bomb (4)",
"Heart",
"Blue Rupee",
74 "Key",
"Arrow (5)",
"Bomb (1)",
"Heart",
75 "Magic (Small)",
"Full Magic",
"Cucco",
"Green Soldier",
76 "Bush Stal",
"Blue Soldier",
"Landmine",
"Heart",
77 "Fairy",
"Heart",
"Nothing (22)",
"Hole",
78 "Warp",
"Staircase",
"Bombable",
"Switch",
80 constexpr size_t kCount =
sizeof(kNames) /
sizeof(kNames[0]);
81 return item < kCount ? kNames[item] :
"Unknown";
88 std::function<
void(
int)> on_room_selected,
90 std::function<
void(
int)> on_save_room,
93 std::function<
const std::deque<int>&()> get_recent_rooms,
94 std::function<
void(
int)> forget_recent_room,
95 std::function<
void(
const std::string&)> show_panel,
96 std::function<
void(
bool)> set_workflow_mode,
Rom* rom)
97 : room_selector_(room_selector),
98 current_room_id_(current_room_id),
99 on_room_selected_(std::move(on_room_selected)),
100 on_room_selected_with_intent_(std::move(on_room_selected_with_intent)),
101 on_save_room_(std::move(on_save_room)),
102 get_viewer_(std::move(get_viewer)),
103 get_compare_viewer_(std::move(get_compare_viewer)),
104 get_recent_rooms_(std::move(get_recent_rooms)),
105 forget_recent_room_(std::move(forget_recent_room)),
106 show_panel_(std::move(show_panel)),
107 set_workflow_mode_(std::move(set_workflow_mode)),
111 return "dungeon.workbench";
114 return "Dungeon Workbench";
137 ImGui::TextDisabled(
ICON_MD_INFO " Load a ROM to edit dungeon rooms.");
141 ImGui::TextColored(theme.text_error_red,
"Dungeon Workbench not wired");
172 constexpr ImGuiTableFlags kLayoutFlags =
173 ImGuiTableFlags_Resizable | ImGuiTableFlags_NoBordersInBody |
174 ImGuiTableFlags_NoPadInnerX | ImGuiTableFlags_NoPadOuterX;
186 if (left_just_expanded || right_just_expanded) {
190 std::snprintf(table_id,
sizeof(table_id),
"##DungeonWorkbenchLayout_%d",
193 if (!ImGui::BeginTable(table_id, 3, kLayoutFlags)) {
204 ImGuiTableColumnFlags left_flags = ImGuiTableColumnFlags_WidthFixed;
206 left_flags |= ImGuiTableColumnFlags_NoResize;
208 ImGuiTableColumnFlags right_flags = ImGuiTableColumnFlags_WidthFixed;
210 right_flags |= ImGuiTableColumnFlags_NoResize;
213 ImGui::TableSetupColumn(
"Sidebar", left_flags, left_w);
214 ImGui::TableSetupColumn(
"Canvas", ImGuiTableColumnFlags_WidthStretch);
215 ImGui::TableSetupColumn(
"Inspector", right_flags, right_w);
216 ImGui::TableNextRow();
218 float measured_left_w = 0.0f;
219 float measured_right_w = 0.0f;
222 ImGui::TableNextColumn();
224 measured_left_w = ImGui::GetContentRegionAvail().x;
226 "##DungeonWorkbenchSidebar",
231 ImGui::SameLine(ImGui::GetWindowWidth() - btn - 8.0f);
236 if (ImGui::IsItemHovered()) {
237 ImGui::SetTooltip(
"Collapse room browser");
241 ImGui::PushID(
"RoomSelectorEmbedded");
248 ImGui::BeginChild(
"##DungeonWorkbenchSidebarCollapsed", ImVec2(0, 0),
true);
249 const float avail = ImGui::GetContentRegionAvail().x;
250 const float expand_btn_w = btn;
252 ImGui::SetCursorPosX(std::max(0.0f, (avail - expand_btn_w) * 0.5f));
253 ImGui::SetCursorPosY(8.0f);
255 ImVec2(expand_btn_w, btn))) {
258 if (ImGui::IsItemHovered()) {
259 ImGui::SetTooltip(
"Show room browser");
268 ImGui::TableNextColumn();
270 "##DungeonWorkbenchCanvas",
273 if (primary_viewer) {
291 static std::string s_undo_desc;
294 s_undo_desc.empty() ? nullptr : s_undo_desc.c_str();
297 static std::string s_redo_desc;
300 s_redo_desc.empty() ? nullptr : s_redo_desc.c_str();
309 ImGui::TextDisabled(
"No active viewer");
315 ImGui::TableNextColumn();
317 measured_right_w = ImGui::GetContentRegionAvail().x;
319 "##DungeonWorkbenchInspector",
321 if (inspector_open) {
324 ImGui::SameLine(ImGui::GetWindowWidth() - btn - 8.0f);
329 if (ImGui::IsItemHovered()) {
330 ImGui::SetTooltip(
"Collapse inspector");
334 if (primary_viewer) {
337 ImGui::TextDisabled(
"No active viewer");
343 ImGui::BeginChild(
"##DungeonWorkbenchInspectorCollapsed", ImVec2(0, 0),
345 const float avail = ImGui::GetContentRegionAvail().x;
346 const float expand_btn_w = btn;
348 ImGui::SetCursorPosX(std::max(0.0f, (avail - expand_btn_w) * 0.5f));
349 ImGui::SetCursorPosY(8.0f);
351 ImVec2(expand_btn_w, btn))) {
354 if (ImGui::IsItemHovered()) {
355 ImGui::SetTooltip(
"Show inspector");
361 if (show_left && measured_left_w > 0.0f) {
364 if (show_right && measured_right_w > 0.0f) {
377 if (recent.empty()) {
382 std::vector<int> recent_ids(recent.begin(), recent.end());
383 std::vector<int> to_forget;
385 constexpr ImGuiTabBarFlags kFlags = ImGuiTabBarFlags_AutoSelectNewTabs |
386 ImGuiTabBarFlags_FittingPolicyScroll |
387 ImGuiTabBarFlags_TabListPopupButton;
390 const ImVec2 frame_pad = ImGui::GetStyle().FramePadding;
392 const float extra_y = is_touch ? 6.0f : 1.0f;
393 const float extra_x = is_touch ? 4.0f : 0.0f;
395 ImGuiStyleVar_FramePadding,
396 ImVec2(frame_pad.x + extra_x, frame_pad.y + extra_y));
399 for (
int room_id : recent_ids) {
401 const ImGuiTabItemFlags tab_flags =
405 if (room_name.empty() || room_name ==
"Unknown") {
406 snprintf(tab_label,
sizeof(tab_label),
"%03X##recent_%03X", room_id,
409 snprintf(tab_label,
sizeof(tab_label),
"%03X %.12s##recent_%03X",
410 room_id, room_name.c_str(), room_id);
412 const bool selected = ImGui::BeginTabItem(tab_label, &open, tab_flags);
415 to_forget.push_back(room_id);
418 if (ImGui::IsItemHovered()) {
420 ImGui::SetTooltip(
"[%03X] %s", room_id, label.c_str());
427 if (ImGui::BeginPopupContextItem()) {
438 to_forget.push_back(room_id);
452 for (
int rid : to_forget) {
485 constexpr ImGuiTableFlags kSplitFlags =
486 ImGuiTableFlags_Resizable | ImGuiTableFlags_NoPadOuterX |
487 ImGuiTableFlags_NoPadInnerX | ImGuiTableFlags_BordersInnerV;
489 if (!ImGui::BeginTable(
"##DungeonWorkbenchSplit", 2, kSplitFlags)) {
494 ImGui::TableSetupColumn(
"Active", ImGuiTableColumnFlags_WidthStretch);
495 ImGui::TableSetupColumn(
"Compare", ImGuiTableColumnFlags_WidthStretch);
496 ImGui::TableNextRow();
499 ImGui::TableNextColumn();
503 if (split_active_open) {
509 ImGui::TableNextColumn();
513 if (split_compare_open) {
514 if (
auto* compare_viewer =
517 compare_viewer->canvas().ApplyScaleSnapshot(
522 ImGui::TextDisabled(
"No compare viewer");
538 static const char*
const kShortNames[] = {
539 "Sewers",
"HC",
"Eastern",
"Desert",
"A-Tower",
"Swamp",
"PoD",
540 "Misery",
"Skull",
"Ice",
"Hera",
"Thieves",
"Turtle",
"GT",
542 constexpr int kVanillaCount =
543 static_cast<int>(
sizeof(kShortNames) /
sizeof(kShortNames[0]));
545 auto AddRoom = [&](
int room_id,
int dungeon_id) {
550 if (dungeon_id >= 0 && dungeon_id < kVanillaCount) {
554 snprintf(buf,
sizeof(buf),
"Dungeon %02X", dungeon_id);
560 for (
int i = 0; i < 0x84; ++i) {
563 if (did >= 0 && did < kVanillaCount) {
567 snprintf(buf,
sizeof(buf),
"Dungeon %02X", did);
573 for (
int i = 0; i < 0x14; ++i) {
584 constexpr ImGuiTabBarFlags kFlags = ImGuiTabBarFlags_FittingPolicyResizeDown;
622 const std::string room_label =
627 ImGui::Text(
"Room: 0x%03X (%d)", room_id, room_id);
631 snprintf(buf,
sizeof(buf),
"0x%03X", room_id);
632 ImGui::SetClipboardText(buf);
634 if (ImGui::IsItemHovered()) {
635 ImGui::SetTooltip(
"Copy room ID (0x%03X) to clipboard", room_id);
638 ImGui::TextUnformatted(
"Room: None");
647 const char* group_name =
nullptr;
651 group_name = cache_it->second.c_str();
655 auto* rooms = viewer.
rooms();
656 if (rooms && room_id <
static_cast<int>(rooms->size())) {
657 group_name = GetBlocksetGroupName((*rooms)[room_id].blockset());
664 ImGui::TextDisabled(
"%s", room_label.c_str());
667 ImGui::TextDisabled(
"%s", room_label.c_str());
673 if (ImGui::Button(
ICON_MD_SAVE " Save Room", ImVec2(-1, 0))) {
679 if (ImGui::Button(
ICON_MD_IMAGE " Room Graphics", ImVec2(-1, 0))) {
691 if (ImGui::IsItemHovered()) {
693 "Jump to a D6 Goron Mines minecart room.\n"
694 "Rooms flagged for minecart audit (2026-02-13).");
701 static constexpr D6Room kD6Rooms[] = {
702 {0xA8,
"0xA8 Entry"},
703 {0xB8,
"0xB8 L-Shape"},
704 {0xD8,
"0xD8 3-Net"},
705 {0xDA,
"0xDA U-Shape"},
708 constexpr ImGuiTableFlags kFlags =
709 ImGuiTableFlags_SizingStretchSame | ImGuiTableFlags_NoPadOuterX;
710 if (ImGui::BeginTable(
"##D6QuickNav", 2, kFlags)) {
711 for (
const auto& r : kD6Rooms) {
712 ImGui::TableNextColumn();
714 const bool is_current = (room_id == r.id);
716 ImGui::BeginDisabled();
718 if (ImGui::Button(r.label, ImVec2(-1, 0))) {
725 ImGui::EndDisabled();
727 if (ImGui::IsItemHovered() && !is_current) {
728 ImGui::SetTooltip(
"Jump to room 0x%03X", r.id);
739 if (recent.size() > 1) {
743 ImGui::BeginChild(
"##RecentRoomsQuickJump", ImVec2(0, 0),
744 ImGuiChildFlags_AutoResizeY);
745 for (
int rid : recent) {
751 if (name.empty() || name ==
"Unknown") {
752 snprintf(label,
sizeof(label),
ICON_MD_ROOM " 0x%03X", rid);
754 snprintf(label,
sizeof(label),
ICON_MD_ROOM " 0x%03X %.16s", rid,
757 if (ImGui::Selectable(label)) {
760 if (ImGui::IsItemHovered()) {
761 ImGui::SetTooltip(
"Jump to room 0x%03X\n%s", rid, name.c_str());
772 if (
auto* rooms = viewer.
rooms();
773 rooms && room_id >= 0 && room_id < static_cast<int>(rooms->size())) {
774 auto& room = (*rooms)[room_id];
776 uint8_t blockset_val = room.blockset();
777 uint8_t palette_val = room.palette();
778 uint8_t layout_val = room.layout_id();
779 uint8_t spriteset_val = room.spriteset();
781 constexpr float kHexW = 92.0f;
783 constexpr ImGuiTableFlags kPropsFlags = ImGuiTableFlags_BordersInnerV |
784 ImGuiTableFlags_RowBg |
785 ImGuiTableFlags_NoPadOuterX;
786 if (ImGui::BeginTable(
"##WorkbenchRoomProps", 2, kPropsFlags)) {
787 ImGui::TableSetupColumn(
"Prop", ImGuiTableColumnFlags_WidthFixed, 90.0f);
788 ImGui::TableSetupColumn(
"Val", ImGuiTableColumnFlags_WidthStretch);
794 room.SetBlockset(blockset_val);
795 if (room.rom() && room.rom()->is_loaded()) {
796 room.RenderRoomGraphics();
799 if (ImGui::IsItemHovered()) {
800 ImGui::SetTooltip(
"Blockset (0-51)");
807 room.SetPalette(palette_val);
808 if (room.rom() && room.rom()->is_loaded()) {
809 room.RenderRoomGraphics();
816 if (ImGui::IsItemHovered()) {
817 ImGui::SetTooltip(
"Palette (0-47)");
824 room.SetLayoutId(layout_val);
825 room.MarkLayoutDirty();
826 if (room.rom() && room.rom()->is_loaded()) {
827 room.RenderRoomGraphics();
830 if (ImGui::IsItemHovered()) {
831 ImGui::SetTooltip(
"Layout (0-7)");
838 room.SetSpriteset(spriteset_val);
839 if (room.rom() && room.rom()->is_loaded()) {
840 room.RenderRoomGraphics();
843 if (ImGui::IsItemHovered()) {
844 ImGui::SetTooltip(
"Spriteset (0-8F)");
851 ImGui::TextDisabled(
"Room properties unavailable");
858 ImGui::TextColored(theme.text_info,
"Placement active");
861 interaction.mode_manager().CancelCurrentMode();
872 const size_t obj_count = interaction.GetSelectionCount();
873 const bool has_entity = interaction.HasEntitySelection();
875 if (!has_entity && obj_count == 0) {
876 ImGui::TextDisabled(
ICON_MD_INFO " Click an object or entity to inspect");
885 interaction.ClearSelection();
888 const auto indices = interaction.GetSelectedObjectIndices();
891 if (indices.size() > 1 && room_id >= 0 && viewer.
rooms()) {
892 auto& room = (*viewer.
rooms())[room_id];
893 auto& objects = room.GetTileObjects();
895 for (
size_t i = 0; i < indices.size() && i < 8; ++i) {
896 size_t idx = indices[i];
897 if (idx < objects.size()) {
898 auto& obj = objects[idx];
900 ImGui::BulletText(
"0x%03X %s", obj.id_, name.c_str());
903 if (indices.size() > 8) {
904 ImGui::TextDisabled(
" ... and %zu more", indices.size() - 8);
909 if (indices.size() == 1 && room_id >= 0 && viewer.
rooms()) {
910 auto& room = (*viewer.
rooms())[room_id];
911 auto& objects = room.GetTileObjects();
912 const size_t idx = indices.front();
913 if (idx < objects.size()) {
914 auto& obj = objects[idx];
920 ImGui::TextColored(theme.text_primary,
"%s", obj_name.c_str());
921 ImGui::TextDisabled(
"%s (Type %d) #%zu in list",
922 GetObjectCategory(obj.id_), subtype, idx);
927 constexpr ImGuiTableFlags kPropsFlags = ImGuiTableFlags_BordersInnerV |
928 ImGuiTableFlags_RowBg |
929 ImGuiTableFlags_NoPadOuterX;
930 if (ImGui::BeginTable(
"##SelObjProps", 2, kPropsFlags)) {
931 ImGui::TableSetupColumn(
"Prop", ImGuiTableColumnFlags_WidthFixed,
933 ImGui::TableSetupColumn(
"Val", ImGuiTableColumnFlags_WidthStretch);
937 uint16_t obj_id =
static_cast<uint16_t
>(obj.id_ & 0x0FFF);
942 interaction.SetObjectId(idx,
static_cast<int16_t
>(obj_id));
950 ImGui::SetNextItemWidth(60);
952 ImGui::DragInt(
"##SelObjX", &pos_x, 0.1f, 0, 63,
"X:%d");
954 ImGui::SetNextItemWidth(60);
956 ImGui::DragInt(
"##SelObjY", &pos_y, 0.1f, 0, 63,
"Y:%d");
957 if (x_changed || y_changed) {
958 int delta_x = pos_x - obj.x_;
959 int delta_y = pos_y - obj.y_;
960 interaction.entity_coordinator().tile_handler().MoveObjects(
961 room_id, {idx}, delta_x, delta_y);
967 uint8_t size = obj.size_ & 0x0F;
971 interaction.SetObjectSize(idx, size);
977 int layer =
static_cast<int>(obj.GetLayerValue());
978 const char* layer_names[] = {
"BG1",
"BG2",
"BG3"};
979 ImGui::SetNextItemWidth(-1);
980 if (ImGui::Combo(
"##SelObjLayer", &layer, layer_names,
981 IM_ARRAYSIZE(layer_names))) {
982 layer = std::clamp(layer, 0, 2);
983 interaction.SetObjectLayer(
990 ImGui::TextDisabled(
"(%d, %d)", obj.x_ * 8, obj.y_ * 8);
1000 if (has_entity && room_id >= 0 && viewer.
rooms()) {
1001 const auto sel = interaction.GetSelectedEntity();
1002 auto& room = (*viewer.
rooms())[room_id];
1007 const auto& doors = room.GetDoors();
1008 if (sel.index < doors.size()) {
1009 const auto& door = doors[sel.index];
1015 ImGui::TextDisabled(
"Direction: %s Position: 0x%02X",
1016 dir_name.c_str(), door.position);
1018 auto [tile_x, tile_y] = door.GetTileCoords();
1019 auto [pixel_x, pixel_y] = door.GetPixelCoords();
1020 ImGui::TextDisabled(
"Tile: (%d, %d) Pixel: (%d, %d)", tile_x, tile_y,
1026 const auto& sprites = room.GetSprites();
1027 if (sel.index < sprites.size()) {
1028 const auto& sprite = sprites[sel.index];
1032 sprite_name.c_str());
1033 ImGui::TextDisabled(
"ID: 0x%02X Subtype: %d Layer: %d", sprite.id(),
1034 sprite.subtype(), sprite.layer());
1035 ImGui::TextDisabled(
"Pos: (%d, %d) Pixel: (%d, %d)", sprite.x(),
1036 sprite.y(), sprite.x() * 16, sprite.y() * 16);
1039 if (sprite.subtype() == 0x07 && sprite.id() >= 0x01 &&
1040 sprite.id() <= 0x1A) {
1042 ImGui::TextColored(theme.text_warning_yellow,
1044 overlord_name.c_str());
1050 const auto& items = room.GetPotItems();
1051 if (sel.index < items.size()) {
1052 const auto& pot_item = items[sel.index];
1053 const char* item_name = GetPotItemName(pot_item.item);
1057 ImGui::TextDisabled(
"Item ID: 0x%02X Raw Pos: 0x%04X", pot_item.item,
1059 ImGui::TextDisabled(
"Pixel: (%d, %d) Tile: (%d, %d)",
1060 pot_item.GetPixelX(), pot_item.GetPixelY(),
1061 pot_item.GetTileX(), pot_item.GetTileY());
1071 interaction.entity_coordinator().DeleteSelectedEntity();
1072 interaction.ClearEntitySelection();
1080 if (ImGui::Checkbox(
"Grid (8x8)", &val))
1084 if (ImGui::Checkbox(
"Object Bounds", &val)) {
1089 if (ImGui::Checkbox(
"Hover Coordinates", &val)) {
1094 if (ImGui::Checkbox(
"Camera Quadrants", &val)) {
1099 if (ImGui::Checkbox(
"Track Collision", &val)) {
1104 if (ImGui::Checkbox(
"Custom Collision", &val)) {
1109 if (ImGui::Checkbox(
"Water Fill (Oracle)", &val)) {
1114 if (ImGui::Checkbox(
"Minecart Pathing", &val)) {
1119 if (ImGui::Checkbox(
"Track Gaps", &val)) {
1124 if (ImGui::Checkbox(
"Track Routes", &val)) {
1129 if (ImGui::Checkbox(
"Custom Objects (Oracle)", &val)) {
1132 if (ImGui::IsItemHovered()) {
1134 "Highlight custom-draw objects (IDs 0x31/0x32)\n"
1135 "with a cyan overlay showing position and subtype.");
1142 ImGui::TextDisabled(
"No panel launcher available");
1146 constexpr ImGuiTableFlags kFlags =
1147 ImGuiTableFlags_SizingStretchSame | ImGuiTableFlags_NoPadOuterX;
1148 if (!ImGui::BeginTable(
"##WorkbenchToolsGrid", 2, kFlags)) {
1152 ImGui::TableNextRow();
1153 ImGui::TableNextColumn();
1157 ImGui::TableNextColumn();
1162 ImGui::TableNextRow();
1163 ImGui::TableNextColumn();
1167 ImGui::TableNextColumn();
1175 if (ImGui::Button(
ICON_MD_KEYBOARD " Keyboard Shortcuts", ImVec2(-1, 0))) {
The Rom class is used to load, save, and modify Rom data. This is a generic SNES ROM container and do...
bool show_object_bounds() const
std::array< zelda3::Room, 0x128 > * rooms() const
void set_show_water_fill_overlay(bool show)
void set_show_custom_collision_overlay(bool show)
int current_room_id() const
void set_show_track_route_overlay(bool show)
bool show_track_gap_overlay() const
void set_show_custom_objects_overlay(bool show)
bool show_custom_objects_overlay() const
void set_show_track_collision_overlay(bool show)
DungeonObjectInteraction & object_interaction()
void set_show_minecart_sprite_overlay(bool show)
bool show_coordinate_overlay() const
bool show_track_collision_overlay() const
bool show_minecart_sprite_overlay() const
bool show_water_fill_overlay() const
bool show_camera_quadrant_overlay() const
void set_show_object_bounds(bool show)
bool CanNavigateRooms() const
void set_show_coordinate_overlay(bool show)
void NavigateToRoom(int target_room)
bool show_track_route_overlay() const
void set_show_track_gap_overlay(bool show)
void DrawDungeonCanvas(int room_id)
void set_show_camera_quadrant_overlay(bool show)
bool show_custom_collision_overlay() const
void set_show_grid(bool show)
InteractionModeManager & mode_manager()
Handles room and entrance selection UI.
void DrawRoomSelector(RoomSelectionIntent single_click_intent=RoomSelectionIntent::kFocusInWorkbench)
static void Draw(const DungeonStatusBarState &state)
static DungeonStatusBarState BuildState(const DungeonCanvasViewer &viewer, const char *tool_mode, bool room_dirty)
std::function< void(int)> on_room_selected_
std::string GetDisplayName() const override
Human-readable name shown in menus and title bars.
std::function< void(const std::string &) show_panel_)
void DrawInspectorShelfView(DungeonCanvasViewer &viewer)
DungeonWorkbenchPanel(DungeonRoomSelector *room_selector, int *current_room_id, std::function< void(int)> on_room_selected, std::function< void(int, RoomSelectionIntent)> on_room_selected_with_intent, std::function< void(int)> on_save_room, std::function< DungeonCanvasViewer *()> get_viewer, std::function< DungeonCanvasViewer *()> get_compare_viewer, std::function< const std::deque< int > &()> get_recent_rooms, std::function< void(int)> forget_recent_room, std::function< void(const std::string &)> show_panel, std::function< void(bool)> set_workflow_mode, Rom *rom=nullptr)
void DrawInspectorShelf(DungeonCanvasViewer &viewer)
DungeonWorkbenchLayoutState layout_state_
std::string GetIcon() const override
Material Design icon for this panel.
std::function< const char *()> get_tool_mode_
std::function< DungeonCanvasViewer *()> get_compare_viewer_
std::function< void(int)> on_save_room_
void DrawSplitView(DungeonCanvasViewer &primary_viewer)
std::function< void(int)> forget_recent_room_
std::function< const std::deque< int > &()> get_recent_rooms_
std::function< void()> on_undo_
std::function< bool()> can_undo_
std::function< int()> undo_depth_
std::function< void(int, RoomSelectionIntent)> on_room_selected_with_intent_
void DrawInspector(DungeonCanvasViewer &viewer)
void DrawInspectorShelfRoom(DungeonCanvasViewer &viewer)
std::unordered_map< int, std::string > room_dungeon_cache_
int GetPriority() const override
Get display priority for menu ordering.
std::string GetEditorCategory() const override
Editor category this panel belongs to.
void DrawRecentRoomTabs()
std::function< std::string()> redo_desc_
std::function< bool()> can_redo_
char compare_search_buf_[64]
void BuildRoomDungeonCache()
std::string GetId() const override
Unique identifier for this panel.
void Draw(bool *p_open) override
Draw the panel content.
void DrawInspectorShelfTools(DungeonCanvasViewer &viewer)
bool show_shortcut_legend_
DungeonRoomSelector * room_selector_
bool room_dungeon_cache_built_
void DrawInspectorShelfSelection(DungeonCanvasViewer &viewer)
std::function< void(bool)> set_workflow_mode_
std::function< void()> on_redo_
std::function< DungeonCanvasViewer *()> get_viewer_
std::function< std::string()> undo_desc_
bool IsPlacementActive() const
Check if any placement mode is active.
static void Draw(bool *p_open)
CanvasConfig & GetConfig()
static float GetMinTouchTarget()
static float GetTouchSafeWidgetHeight()
static bool BeginContentChild(const char *id, const ImVec2 &min_size, bool border=false, ImGuiWindowFlags flags=0)
static void EndContentChild()
static void PropertyRow(const char *label, std::function< void()> widget_callback)
static bool IsTouchDevice()
RAII guard for ImGui style vars.
Dungeon Room Entrance or Spawn Point.
#define ICON_MD_COMPARE_ARROWS
#define ICON_MD_VISIBILITY
#define ICON_MD_INVENTORY
#define ICON_MD_DOOR_FRONT
#define ICON_MD_CHEVRON_LEFT
#define ICON_MD_SELECT_ALL
#define ICON_MD_OPEN_IN_NEW
#define ICON_MD_CONTENT_COPY
#define ICON_MD_INVENTORY_2
#define ICON_MD_CHEVRON_RIGHT
#define ICON_MD_WORKSPACES
const AgentUITheme & GetTheme()
const char * GetPotItemName(uint8_t item)
const char * GetObjectCategory(int object_id)
const char * GetBlocksetGroupName(uint8_t blockset)
Editors are the view controllers for the application.
RoomSelectionIntent
Intent for room selection in the dungeon editor.
bool BeginThemedTabBar(const char *id, ImGuiTabBarFlags flags)
A stylized tab bar with "Mission Control" branding.
InputHexResult InputHexByteEx(const char *label, uint8_t *data, float input_width, bool no_step)
InputHexResult InputHexWordEx(const char *label, uint16_t *data, float input_width, bool no_step)
std::string GetSpriteLabel(int id)
Convenience function to get a sprite label.
std::string GetRoomLabel(int id)
Convenience function to get a room label.
int GetObjectSubtype(int object_id)
std::string GetOverlordLabel(int id)
Convenience function to get an overlord label.
constexpr std::string_view GetDoorDirectionName(DoorDirection dir)
Get human-readable name for door direction.
std::string GetObjectName(int object_id)
constexpr std::string_view GetDoorTypeName(DoorType type)
Get human-readable name for door type.
bool show_right_inspector
static constexpr float kContentMinHeightCanvas
static constexpr float kContentMinWidthSidebar