15#include "absl/strings/str_format.h"
49#include "imgui/imgui.h"
68 if (object_id < 0x100)
70 if (object_id < 0x200)
72 if (object_id >= 0xF80)
78 switch (layer_value) {
91 switch (layer_value) {
93 return "Upper layer (BG1)";
95 return "Lower layer (BG2)";
103 return std::clamp(desired_width, min_width, std::max(min_width, max_width));
121 const ImVec2 min = ImGui::GetItemRectMin();
122 const ImVec2 max = ImGui::GetItemRectMax();
133 bool standalone_window_open) {
134 return standalone_window_open
140 float total_width,
float min_canvas_width,
float min_sidebar_width,
141 float splitter_width,
bool want_left,
bool want_right) {
146 auto required_width = [&](
bool left,
bool right,
bool compact_left,
147 bool compact_right) {
148 float required = min_canvas_width;
150 left ? (compact_left ? GetCompactSidebarWidth(
false, min_sidebar_width)
154 right ? (compact_right ? GetCompactSidebarWidth(
true, min_sidebar_width)
158 required += splitter_width;
161 required += splitter_width;
195 float total_width,
float min_canvas_width,
float min_sidebar_width,
196 float splitter_width,
float stored_left_width,
float stored_right_width,
197 bool want_left,
bool want_right) {
200 total_width, min_canvas_width, min_sidebar_width, splitter_width,
201 want_left, want_right);
203 const float compact_left_width =
204 GetCompactSidebarWidth(
false, min_sidebar_width);
205 const float compact_right_width =
206 GetCompactSidebarWidth(
true, min_sidebar_width);
219 : stored_right_width)
222 const float max_left_width =
223 total_width - right_width - min_canvas_width -
226 const float max_right_width =
227 total_width - left_width - min_canvas_width -
231 left_width = ClampWorkbenchPaneWidth(
238 right_width = ClampWorkbenchPaneWidth(
245 float center_width = total_width - left_width - right_width;
247 center_width -= splitter_width;
250 center_width -= splitter_width;
252 if (center_width < min_canvas_width) {
253 float deficit = min_canvas_width - center_width;
257 left_width -= shrink;
263 right_width -= shrink;
266 center_width = std::max(
267 1.0f, total_width - left_width - right_width -
280 std::function<
void(
int)> on_room_selected,
282 std::function<
void(
int)> on_save_room,
283 std::function<
void()> on_save_all_rooms,
286 std::function<
const std::deque<int>&()> get_recent_rooms,
287 std::function<
void(
int)> forget_recent_room,
288 std::function<
void(
bool)> set_workflow_mode,
Rom* rom)
289 : room_selector_(room_selector),
290 current_room_id_(current_room_id),
291 on_room_selected_(std::move(on_room_selected)),
292 on_room_selected_with_intent_(std::move(on_room_selected_with_intent)),
293 on_save_room_(std::move(on_save_room)),
294 on_save_all_rooms_(std::move(on_save_all_rooms)),
295 get_viewer_(std::move(get_viewer)),
296 get_compare_viewer_(std::move(get_compare_viewer)),
297 get_recent_rooms_(std::move(get_recent_rooms)),
298 forget_recent_room_(std::move(forget_recent_room)),
299 set_workflow_mode_(std::move(set_workflow_mode)),
305 return "dungeon.workbench";
308 return "Dungeon Workbench";
380 std::ifstream file(path, std::ios::binary);
385 std::stringstream buffer;
386 buffer << file.rdbuf();
394 for (
const auto& layer : overlay->layers) {
407 ImGui::SetNextWindowSize(ImVec2(1120.0f, 700.0f), ImGuiCond_FirstUseEver);
408 if (!ImGui::Begin(
ICON_MD_PREVIEW " Proposal Preview##DungeonProposalPreview",
416 json_options.
filters.push_back({
"Proposal Overlay",
"json"});
417 json_options.
filters.push_back({
"All Files",
"*"});
419 const std::string path =
430 ImGui::EndDisabled();
432 ImGui::SetNextItemWidth(-1.0f);
433 if (ImGui::InputTextWithHint(
"##ProposalPath", tr(
"overlay .json path"),
435 ImGuiInputTextFlags_EnterReturnsTrue)) {
440 ImVec4(1.0f, 0.45f, 0.4f, 1.0f));
448 tr(
"Open a proposal overlay (.json) to render its rooms from the "
449 "loaded ROM with the proposed changes drawn on top. This window "
450 "is read-only: it never edits rooms or the ROM."));
456 if (!overlay.
title.empty()) {
457 ImGui::TextUnformatted(overlay.
title.c_str());
459 if (!overlay.
status.empty()) {
461 ImGui::TextDisabled(
"(%s)", overlay.
status.c_str());
463 ImGui::TextDisabled(
"%s",
464 tr(
"Read-only preview. Nothing here edits the ROM."));
469 for (
size_t i = 0; i < overlay.
layers.size(); ++i) {
470 const auto& layer = overlay.
layers[i];
472 const uint32_t c = layer.color_rgba;
474 absl::StrFormat(
"##ProposalLayerSwatch%d", i).c_str(),
475 ImVec4(((c >> 24) & 0xFF) / 255.0f, ((c >> 16) & 0xFF) / 255.0f,
476 ((c >> 8) & 0xFF) / 255.0f, 1.0f),
477 ImGuiColorEditFlags_NoTooltip, ImVec2(12, 12));
478 ImGui::SameLine(0, 4);
482 absl::StrFormat(
"%s##ProposalLayer%d", layer.label, i).c_str(),
488 ImGui::EndDisabled();
490 ImGui::SetNextItemWidth(160.0f);
493 for (
const auto& room : overlay.
rooms) {
495 if (ImGui::SmallButton(
496 absl::StrFormat(
"Open $%02X##ProposalOpenRoom", room.room_id)
504 const float notes_height =
505 overlay.
notes.empty()
507 : std::min(140.0f, ImGui::GetTextLineHeightWithSpacing() *
508 (overlay.
notes.size() + 1.5f));
509 if (ImGui::BeginChild(
"##ProposalPreviewBody", ImVec2(0.0f, -notes_height),
510 true, ImGuiWindowFlags_HorizontalScrollbar)) {
516 if (!overlay.
notes.empty() &&
517 ImGui::BeginChild(
"##ProposalPreviewNotes", ImVec2(0.0f, 0.0f),
false)) {
518 for (
const auto& note : overlay.
notes) {
519 ImGui::BulletText(
"%s", note.c_str());
522 if (!overlay.
notes.empty()) {
605 float button_size,
bool compact) {
606 const bool sidebar_open = ImGui::BeginChild(
"##DungeonWorkbenchSidebar",
607 ImVec2(width, height),
false);
617 const bool can_open_overview =
true;
618 const float collapse_w =
623 const float spacing = ImGui::GetStyle().ItemSpacing.x;
624 const float action_cluster_w =
625 collapse_w + (can_open_overview ? (spacing + menu_w) : 0.0f);
629 nullptr, compact, action_cluster_w, [&]() {
630 if (can_open_overview) {
633 "Open room review tools",
true)) {
634 ImGui::OpenPopup(
"##WorkbenchSidebarQuickActions");
636 if (ImGui::BeginPopup(
"##WorkbenchSidebarQuickActions")) {
647 const bool can_open_shortcuts =
650 const std::string keyboard_shortcuts =
653 keyboard_shortcuts.c_str(),
false,
654 can_open_shortcuts) &&
664 "Collapse navigation pane")) {
674 struct PlacementChoice {
679 constexpr PlacementChoice choices[] = {
684 float choice_width = 0.0f;
685 for (
const auto& choice : choices)
686 choice_width = std::max(choice_width, ImGui::CalcTextSize(choice.label).x);
687 choice_width += ImGui::GetStyle().FramePadding.x * 2.0f +
688 ImGui::GetStyle().CellPadding.x * 2.0f;
690 ImGui::GetContentRegionAvail().x >= choice_width * 4.0f ? 4 : 2;
691 if (ImGui::BeginTable(
"##PlacementKinds", columns,
692 ImGuiTableFlags_SizingStretchSame |
693 ImGuiTableFlags_NoPadInnerX |
694 ImGuiTableFlags_NoPadOuterX)) {
695 for (
const auto& choice : choices) {
696 ImGui::TableNextColumn();
698 ImGuiCol_Button, ImGui::GetStyleColorVec4(
sidebar_mode_ == choice.mode
699 ? ImGuiCol_ButtonActive
701 const bool pressed = ImGui::Button(
703 ImVec2(ImGui::GetContentRegionAvail().x, ImGui::GetFrameHeight()));
705 absl::StrFormat(
"Place %s", choice.label));
707 const ImVec2 min = ImGui::GetItemRectMin();
708 const ImVec2 max = ImGui::GetItemRectMax();
709 ImGui::GetWindowDrawList()->AddLine(
710 ImVec2(min.x, max.y - 1), ImVec2(max.x, max.y - 1),
711 ImGui::GetColorU32(ImGuiCol_CheckMark), 2.0f);
721 ImGui::PushID(
"WorkbenchSidebarMode");
722 const auto draw_external = [
this](
const char* id) {
725 ImGui::TextWrapped(
"Open in a separate window.");
730 const float available = std::max(1.0f, ImGui::GetContentRegionAvail().y);
731 const float separator = 6.0f;
734 if (ImGui::BeginChild(
"##NavigatorTools", ImVec2(0, upper),
false)) {
751 ImGui::TextDisabled(
"Placement library unavailable");
752 }
else if (!draw_external(content->
GetId().c_str())) {
754 selector->DrawInWorkbench();
756 sprites->DrawInWorkbench();
758 doors->DrawInWorkbench();
760 items->DrawInWorkbench();
762 content->
Draw(
nullptr);
766 const ImVec2 divider = ImGui::GetCursorScreenPos();
767 ImGui::InvisibleButton(
"##NavigatorSplit", ImVec2(-1, separator));
768 if (ImGui::IsItemHovered() || ImGui::IsItemActive())
769 ImGui::SetMouseCursor(ImGuiMouseCursor_ResizeNS);
770 if (ImGui::IsItemActive())
774 ImGui::GetWindowDrawList()->AddLine(
775 ImVec2(divider.x, divider.y + separator * 0.5f),
776 ImVec2(ImGui::GetItemRectMax().x, divider.y + separator * 0.5f),
777 ImGui::GetColorU32(ImGuiCol_Separator));
778 if (ImGui::BeginChild(
"##NavigatorMatrix", ImVec2(0, 0),
false)) {
779 if (!draw_external(
"dungeon.room_matrix")) {
783 ImGui::TextDisabled(
"Room Matrix unavailable");
795 ImGui::TextDisabled(
ICON_MD_INFO " Load a ROM to edit dungeon rooms.");
799 ImGui::TextColored(theme.text_error_red, tr(
"Dungeon Workbench not wired"));
807 const float total_w = std::max(ImGui::GetContentRegionAvail().x, 1.0f);
811 const float min_sidebar_w =
813 const float min_canvas_w = std::max(420.0f, min_sidebar_w + 96.0f);
816 total_w, min_canvas_w, min_sidebar_w, splitter_w,
866 const float total_h = std::max(ImGui::GetContentRegionAvail().y, 1.0f);
886 ImGui::SameLine(0.0f, 0.0f);
888 "##DungeonWorkbenchLeftSplitter", total_h,
890 total_w - left_w - min_canvas_w - (show_left ? splitter_w : 0.0f),
894 ImGui::SameLine(0.0f, 0.0f);
898 ImGui::SameLine(0.0f, 0.0f);
900 "##DungeonWorkbenchRightSplitter", total_h,
902 total_w - right_w - min_canvas_w -
903 (show_right ? splitter_w : 0.0f),
907 ImGui::SameLine(0.0f, 0.0f);
917 ImGui::SameLine(0.0f, 0.0f);
919 "##DungeonWorkbenchLeftSplitter", total_h,
921 total_w - right_w - min_canvas_w -
922 (show_right ? splitter_w : 0.0f),
929 ImGui::SameLine(0.0f, 0.0f);
934 ImGui::SameLine(0.0f, 0.0f);
936 "##DungeonWorkbenchRightSplitter", total_h,
938 total_w - left_w - min_canvas_w - (show_left ? splitter_w : 0.0f),
942 ImGui::SameLine(0.0f, 0.0f);
949 if (primary_viewer) {
956 const bool canvas_open = ImGui::BeginChild(
957 "##DungeonWorkbenchCanvas", ImVec2(width, height),
false,
958 ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse);
960 if (primary_viewer) {
966 const float status_bar_reserved =
968 ImGui::GetFontSize() + ImGui::GetStyle().FramePadding.y * 2.0f,
970 ImGui::GetStyle().ItemSpacing.y;
973 ImGuiWindowFlags body_flags =
974 ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse;
975 if (connected_mode) {
978 const ImVec2 content_size =
981 ImGui::SetNextWindowContentSize(
982 ImVec2(content_size.x * scale, content_size.y * scale));
983 body_flags = ImGuiWindowFlags_HorizontalScrollbar |
984 ImGuiWindowFlags_NoScrollWithMouse;
986 const bool body_open = ImGui::BeginChild(
987 "##DungeonCanvasBody", ImVec2(0.0f, -status_bar_reserved),
false,
990 if (connected_mode) {
1007 const char* tool_mode =
1009 bool room_dirty =
false;
1010 if (
auto* rooms = primary_viewer->
rooms();
1013 room_dirty = room->HasUnsavedChanges();
1021 status.workflow_primary =
true;
1027 static std::string s_undo_desc;
1029 status.undo_desc = s_undo_desc.empty() ? nullptr : s_undo_desc.c_str();
1032 static std::string s_redo_desc;
1034 status.redo_desc = s_redo_desc.empty() ? nullptr : s_redo_desc.c_str();
1041 status.on_selection = [
this]() {
1047 ImGui::TextDisabled(tr(
"No active viewer"));
1053 if (primary_viewer) {
1059 float button_size,
bool compact,
1061 const bool inspector_open = ImGui::BeginChild(
"##DungeonWorkbenchInspector",
1062 ImVec2(width, height),
true);
1063 if (inspector_open) {
1068 ImGui::TextDisabled(tr(
"No active viewer"));
1079 const char* collapse_icon =
1081 const float collapse_w =
1083 const float swap_w =
1085 const float spacing = ImGui::GetStyle().ItemSpacing.x;
1086 const float action_cluster_w = swap_w + collapse_w + spacing;
1090 "##DungeonWorkbenchInspectorHeader",
1092 tools_mode ?
"Tools" :
"Inspect",
1094 nullptr, compact, action_cluster_w, [&]() {
1098 :
"Move inspector to the left side "
1099 "(ZScream layout)")) {
1105 ImGui::SameLine(0.0f, spacing);
1108 "Collapse inspector")) {
1142 constexpr ImGuiTableFlags kSplitFlags =
1143 ImGuiTableFlags_Resizable | ImGuiTableFlags_NoPadOuterX |
1144 ImGuiTableFlags_NoPadInnerX | ImGuiTableFlags_BordersInnerV;
1146 if (!ImGui::BeginTable(
"##DungeonWorkbenchSplit", 2, kSplitFlags)) {
1151 ImGui::TableSetupColumn(
"Active", ImGuiTableColumnFlags_WidthStretch);
1152 ImGui::TableSetupColumn(
"Compare", ImGuiTableColumnFlags_WidthStretch);
1153 ImGui::TableNextRow();
1159 ImGui::TableNextColumn();
1160 ImGui::AlignTextToFramePadding();
1162 ImGui::TextDisabled(
1168 "##SplitActive", ImVec2(1.0f, 1.0f),
false,
1169 ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse);
1170 if (split_active_open) {
1176 ImGui::TableNextColumn();
1177 ImGui::AlignTextToFramePadding();
1179 compare_viewer ? compare_viewer->
project() : active_project;
1180 ImGui::TextDisabled(
1186 "##SplitCompare", ImVec2(1.0f, 1.0f),
false,
1187 ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse);
1188 if (split_compare_open) {
1189 if (compare_viewer) {
1196 ImGui::TextDisabled(tr(
"No compare viewer"));
1213 static const char*
const kShortNames[] = {
1214 "Sewers",
"HC",
"Eastern",
"Desert",
"A-Tower",
"Swamp",
"PoD",
1215 "Misery",
"Skull",
"Ice",
"Hera",
"Thieves",
"Turtle",
"GT",
1217 constexpr int kVanillaCount =
1218 static_cast<int>(
sizeof(kShortNames) /
sizeof(kShortNames[0]));
1220 auto AddRoom = [&](
int room_id,
int dungeon_id) {
1225 if (dungeon_id >= 0 && dungeon_id < kVanillaCount) {
1229 snprintf(buf,
sizeof(buf),
"Dungeon %02X", dungeon_id);
1241 if (did >= 0 && did < kVanillaCount) {
1245 snprintf(buf,
sizeof(buf),
"Dungeon %02X", did);
1264 ImGuiStyleVar_ItemSpacing,
1265 ImVec2(std::max(ImGui::GetStyle().ItemSpacing.x, 4.0f),
1266 std::max(4.0f, ImGui::GetStyle().ItemSpacing.y - 1.0f)));
1273 flags.kSaveSprites = value;
1274 flags.kSaveRoomHeaders = value;
1275 flags.kSaveChests = value;
1276 flags.kSavePotItems = value;
1277 flags.kSaveEntrances = value;
1278 flags.kSavePalettes = value;
1279 flags.kSaveCollision = value;
1280 flags.kSaveBlocks = value;
1281 flags.kSaveTorches = value;
1282 flags.kSavePits = value;
1287 ImGui::TextUnformatted(tr(
"Room content to apply"));
1288 constexpr ImGuiTableFlags kFlags =
1289 ImGuiTableFlags_SizingStretchSame | ImGuiTableFlags_NoPadOuterX;
1290 if (ImGui::BeginTable(
"##WorkbenchSaveContentFlags", 2, kFlags,
1291 ImVec2(320.0f, 0.0f))) {
1292 auto draw_checkbox = [](
const char* label,
bool* value) {
1293 ImGui::TableNextColumn();
1294 ImGui::Checkbox(label, value);
1296 ImGui::TableNextRow();
1297 draw_checkbox(
"Room objects", &flags.kSaveObjects);
1298 draw_checkbox(
"Room sprites", &flags.kSaveSprites);
1299 ImGui::TableNextRow();
1300 draw_checkbox(
"Room properties", &flags.kSaveRoomHeaders);
1301 draw_checkbox(
"Chest contents", &flags.kSaveChests);
1302 ImGui::TableNextRow();
1303 draw_checkbox(
"Items under pots", &flags.kSavePotItems);
1304 draw_checkbox(
"Palettes", &flags.kSavePalettes);
1305 ImGui::TableNextRow();
1306 draw_checkbox(
"Custom collision", &flags.kSaveCollision);
1307 ImGui::TableNextColumn();
1308 ImGui::BeginDisabled();
1309 ImGui::Checkbox(
"Water Fill", &flags.kSaveWaterFillZones);
1310 ImGui::EndDisabled();
1311 if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled)) {
1313 "Water Fill saving is fixed when the project opens. Change "
1314 "save_dungeon_water_fill_zones in the .yaze file, then reopen the "
1317 ImGui::TableNextRow();
1318 draw_checkbox(
"Push blocks", &flags.kSaveBlocks);
1319 draw_checkbox(
"Torches", &flags.kSaveTorches);
1320 ImGui::TableNextRow();
1321 draw_checkbox(
"Pit damage", &flags.kSavePits);
1322 draw_checkbox(
"Entrances", &flags.kSaveEntrances);
1326 if (ImGui::SmallButton(tr(
"Select all##WorkbenchSaveContent"))) {
1330 if (ImGui::SmallButton(tr(
"Select none##WorkbenchSaveContent"))) {
1335 const auto apply_room_label =
1336 absl::StrFormat(
ICON_MD_SAVE " Apply room 0x%03X", room_id);
1347 tr(
"Apply uses the checked content. File > Save ROM "
1348 "writes the applied changes to disk."));
1355 if (ImGui::BeginPopup(
"##PitDamageSlotChooser")) {
1356 ImGui::CloseCurrentPopup();
1365 bool enabled =
false;
1366 ImGui::BeginDisabled();
1367 ImGui::Checkbox(tr(
"Pit damage##PitDamageEnabled"), &enabled);
1368 ImGui::EndDisabled();
1369 if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled)) {
1370 ImGui::SetTooltip(tr(
"Available after room data loads."));
1377 if (!membership.room_valid) {
1378 ImGui::TextDisabled(tr(
"No valid room selected."));
1382 const uint16_t current_room = membership.room_id;
1383 bool room_deals_damage = membership.deals_damage;
1384 if (ImGui::Checkbox(tr(
"Pit damage##PitDamageEnabled"), &room_deals_damage)) {
1386 ImGui::OpenPopup(
"##PitDamageSlotChooser");
1392 if (ImGui::IsItemHovered()) {
1394 "Choose which room gives up or receives a pit-damage slot when "
1395 "changing this room.");
1398 if (membership.dirty) {
1399 ImGui::TextColored(theme.status_warning,
1403 auto show_status = [&]() {
1407 ImGui::PushTextWrapPos(0.0f);
1413 ImGui::PopTextWrapPos();
1416 if (!ImGui::BeginPopup(
"##PitDamageSlotChooser")) {
1423 if (membership.deals_damage) {
1424 if (!membership.suggested_replacement_room.has_value()) {
1425 ImGui::PushTextWrapPos(0.0f);
1426 ImGui::TextDisabled(tr(
"No available non-damaging room slot."));
1427 ImGui::PopTextWrapPos();
1434 ImGui::TextUnformatted(tr(
"Move pit damage to room:"));
1435 ImGui::SetNextItemWidth(88.0f);
1438 &edit_value, 88.0f,
true);
1443 if (ImGui::IsItemHovered()) {
1445 tr(
"Replacement room must not already be in the table."));
1447 const bool replace_clicked =
1448 ImGui::Button(tr(
"Disable pit damage##PitDamageReplaceCurrent"));
1450 if (replace_clicked) {
1456 ? absl::StrFormat(
"Room 0x%03X removed; slot now 0x%03X",
1458 : std::string(status.message());
1461 ImGui::CloseCurrentPopup();
1465 if (!membership.suggested_victim_room.has_value()) {
1466 ImGui::PushTextWrapPos(0.0f);
1467 ImGui::TextDisabled(tr(
"No existing pit-damage slot is available."));
1468 ImGui::PopTextWrapPos();
1475 ImGui::TextUnformatted(tr(
"Take pit damage from room:"));
1476 ImGui::SetNextItemWidth(88.0f);
1484 if (ImGui::IsItemHovered()) {
1485 ImGui::SetTooltip(tr(
"This room will stop dealing pit damage."));
1487 const bool add_clicked =
1488 ImGui::Button(tr(
"Enable pit damage##PitDamageAddCurrent"));
1496 ? absl::StrFormat(
"Room 0x%03X added; replaced 0x%03X",
1498 : std::string(status.message());
1501 ImGui::CloseCurrentPopup();
1507 if (ImGui::Button(tr(
"Cancel##PitDamageCancel"))) {
1509 ImGui::CloseCurrentPopup();
1517 ImGui::TextDisabled(tr(
"Include Pit damage under Save content."));
1536 if (
const auto* project = viewer.
project()) {
1545 constexpr const char* kPopupId =
"Dungeon Map##WorkbenchDungeonMapPopup";
1547 const ImVec2 viewport = ImGui::GetMainViewport()->WorkSize;
1548 ImGui::SetNextWindowSize(ImVec2(std::min(1040.0f, viewport.x * 0.85f),
1549 std::min(780.0f, viewport.y * 0.85f)),
1550 ImGuiCond_Appearing);
1551 ImGui::OpenPopup(kPopupId);
1555 bool popup_open =
true;
1556 if (ImGui::BeginPopupModal(kPopupId, &popup_open,
1557 ImGuiWindowFlags_NoSavedSettings)) {
1559 ImGui::CloseCurrentPopup();
1564 const ImVec2 map_size =
1565 ImVec2(std::max(1.0f, ImGui::GetContentRegionAvail().x),
1566 std::max(1.0f, ImGui::GetContentRegionAvail().y -
1567 ImGui::GetFrameHeightWithSpacing()));
1568 if (ImGui::BeginChild(
"##WorkbenchDungeonMapBody", map_size,
false)) {
1573 ImGui::TextDisabled(tr(
"Dungeon map unavailable"));
1577 ImGui::CloseCurrentPopup();
1665 const std::string standalone_id = content->
GetId();
1677 viewer->object_interaction().HandleMouseRelease();
1678 viewer->object_interaction().CancelPlacement();
1679 viewer->object_interaction().mode_manager().CancelCurrentMode();
1692 const std::string standalone_id = content->
GetId();
1693 if (standalone_id.empty() ||
1708 return "custom_collision";
1710 return "water_fill";
1714 return "object_coverage";
1716 return "object_selector";
1724 return "room_graphics";
1739 return "Custom Collision";
1741 return "Water Fill";
1743 return "Minecart Tracks";
1745 return "Object Coverage";
1747 return "Object Selector";
1749 return "Door Tools";
1751 return "Sprite Tools";
1753 return "Item Tools";
1755 return "Room Graphics";
1768 return "Room tag tools are not available.";
1770 return "Custom collision tools are not available.";
1772 return "Water fill tools are not available.";
1774 return "Minecart track tools are not available.";
1776 return "Object coverage is not available.";
1778 return "Object selector is not available.";
1780 return "Door tools are not available.";
1782 return "Sprite tools are not available.";
1784 return "Item tools are not available.";
1786 return "Room graphics tools are not available.";
1788 return "Palette tools are not available.";
1790 return "No Workbench tool selected.";
1792 return "Tool is not available.";
1799 const char* unavailable_message) {
1801 ImGui::TextDisabled(
"%s", unavailable_message);
1804 content->
Draw(
nullptr);
1847 if (
auto* selector =
1849 selector->DrawInWorkbench();
1888 const bool pressed = ImGui::Button(
"Room tools");
1892 "Specialized room tools");
1895 ImGui::OpenPopup(
"##WorkbenchToolPickerPopup");
1896 if (ImGui::BeginPopup(
"##WorkbenchToolPickerPopup")) {
1897 for (
const auto tool : tools) {
1899 const bool selected = ImGui::MenuItem(
1917 const bool back = ImGui::Button(
"Back to Inspect");
1927 const bool can_pop_out =
1929 ImGui::BeginDisabled(!can_pop_out);
1930 const bool pop_out = ImGui::Button(
"Pop out");
1935 ImGui::EndDisabled();
1936 if (pop_out && can_pop_out) {
1941 ImGui::TextDisabled(
"%s is open in a separate window.",
1950 const bool body_open = ImGui::BeginChild(
"##WorkbenchToolInspectorBody",
1951 ImVec2(0.0f, 0.0f),
false);
1966 ImGui::PushID(
"InspectedSelection");
1968 if (!placing_object && interaction.mode_manager().IsPlacementActive()) {
1969 ImGui::TextUnformatted(interaction.mode_manager().GetModeName());
1970 if (ImGui::Button(
"Done placing (Esc)")) {
1971 interaction.CancelPlacement();
1977 }
else if (!placing_object && (interaction.GetSelectionCount() > 0 ||
1978 interaction.HasEntitySelection())) {
1985 ImGui::PushID(
"InspectedRoom");
1994 if (!active_preview) {
1998 const auto object = *active_preview;
2001 ImGui::PushID(
"PlacementInspector");
2007 ImGui::TextDisabled(
"0x%03X",
object.id_);
2009 constexpr ImGuiTableFlags kFlags = ImGuiTableFlags_BordersInnerV |
2010 ImGuiTableFlags_RowBg |
2011 ImGuiTableFlags_NoPadOuterX;
2012 if (ImGui::BeginTable(
"Properties", 2, kFlags)) {
2013 ImGui::TableSetupColumn(
"Property", ImGuiTableColumnFlags_WidthFixed,
2015 ImGui::TableSetupColumn(
"Value", ImGuiTableColumnFlags_WidthStretch);
2016 uint8_t requested_size =
object.size_;
2018 interaction.SetPlacementPreviewSize(requested_size);
2022 int layer =
object.GetLayerValue();
2023 const char* stream_names[] = {
"Primary",
"BG2 overlay",
"BG1 overlay"};
2024 const char* layer_names[] = {
"Upper layer (BG1)",
"Lower layer (BG2)"};
2025 ImGui::SetNextItemWidth(-1);
2026 if (ImGui::Combo(
"##Layer", &layer,
2027 uses_stream ? stream_names : layer_names,
2028 uses_stream ? 3 : 2)) {
2029 interaction.SetPlacementPreviewLayer(layer);
2033 "Layer for the next placed object");
2038 bool repeat = interaction.GetPlacementPolicy() ==
2039 DungeonObjectInteraction::PlacementPolicy::kRepeat;
2040 if (ImGui::Checkbox(tr(
"Keep placing copies"), &repeat)) {
2041 interaction.SetPlacementPolicy(
2042 repeat ? DungeonObjectInteraction::PlacementPolicy::kRepeat
2043 : DungeonObjectInteraction::PlacementPolicy::kOnce);
2048 "Keep placing copies after each click");
2050 ImGui::TextWrapped(
"%s", repeat
2051 ? tr(
"Click the canvas to place each copy.")
2052 : tr(
"Click the canvas to place, then edit."));
2053 if (!description.wheel_hint.empty()) {
2054 ImGui::TextWrapped(
"%s", description.wheel_hint.c_str());
2058 interaction.CancelPlacement();
2059 if (interaction.GetSelectionCount() > 0) {
2066 "button",
"finish_placement",
2067 "Finish placement and inspect the selected object");
2079 const std::vector<size_t> indices = {index};
2083 if (interaction.BeginPlacementFromSelection()) {
2090 "Place another object with this size and layer");
2092 constexpr ImGuiTableFlags kFlags =
2093 ImGuiTableFlags_SizingStretchSame | ImGuiTableFlags_NoPadOuterX;
2094 if (ImGui::BeginTable(
"##SelectedObjectActions", 2, kFlags)) {
2095 auto action = [](
const char* label,
const char* id) {
2096 ImGui::TableNextColumn();
2103 (void)interaction.HandleDuplicateSelected();
2106 handler.DeleteObjects(room_id, indices);
2109 handler.SendToFront(room_id, indices);
2112 handler.SendToBack(room_id, indices);
2124 const size_t obj_count = interaction.GetSelectionCount();
2125 const bool has_entity = interaction.HasEntitySelection();
2127 if (!has_entity && obj_count == 0) {
2129 ImGui::TextDisabled(tr(
"Click an object or entity to inspect"));
2134 if (obj_count > 0) {
2136 ImGui::TextColored(theme.text_primary,
ICON_MD_WIDGETS " %zu object%s",
2137 obj_count, obj_count == 1 ?
"" :
"s");
2138 if (obj_count == 1) {
2140 ImGui::TextDisabled(tr(
"Focused selection"));
2143 const auto indices = interaction.GetSelectedObjectIndices();
2146 if (indices.size() > 1 && room_id >= 0 && viewer.
rooms()) {
2147 auto& room = (*viewer.
rooms())[room_id];
2148 auto& objects = room.GetTileObjects();
2150 " Multi-selection");
2151 for (
size_t i = 0; i < indices.size() && i < 8; ++i) {
2152 size_t idx = indices[i];
2153 if (idx < objects.size()) {
2154 auto& obj = objects[idx];
2156 ImGui::BulletText(tr(
"0x%03X %s"), obj.id_, name.c_str());
2159 if (indices.size() > 8) {
2160 ImGui::TextDisabled(tr(
" ... and %zu more"), indices.size() - 8);
2166 auto& tile_handler = interaction.entity_coordinator().tile_handler();
2167 const std::vector<size_t> selection_copy(indices.begin(), indices.end());
2168 bool has_room_stream_object =
false;
2169 bool has_special_layer_object =
false;
2170 for (
const size_t index : selection_copy) {
2171 if (index >= objects.size()) {
2175 has_room_stream_object =
true;
2177 has_special_layer_object =
true;
2180 const bool has_editable_size =
2186 static int bulk_nudge_dx = 0;
2187 static int bulk_nudge_dy = 0;
2188 ImGui::TextDisabled(
2189 tr(
"Nudge selection (%d px steps)"),
2190 interaction.entity_coordinator().SelectionMoveStepPixels());
2191 ImGui::PushButtonRepeat(
true);
2193 interaction.NudgeSelected(0, -1);
2196 interaction.NudgeSelected(0, 1);
2199 interaction.NudgeSelected(-1, 0);
2202 interaction.NudgeSelected(1, 0);
2203 ImGui::PopButtonRepeat();
2205 ImGui::SetNextItemWidth(60);
2206 ImGui::DragInt(
"##BulkNudgeDx", &bulk_nudge_dx, 0.25f, -63, 63,
"Δx:%d");
2208 ImGui::SetNextItemWidth(60);
2209 ImGui::DragInt(
"##BulkNudgeDy", &bulk_nudge_dy, 0.25f, -63, 63,
"Δy:%d");
2211 if (ImGui::Button(tr(
"Apply##BulkNudgeApply")) &&
2212 (bulk_nudge_dx != 0 || bulk_nudge_dy != 0)) {
2213 interaction.NudgeSelected(bulk_nudge_dx, bulk_nudge_dy);
2219 if (has_room_stream_object && !has_special_layer_object) {
2220 ImGui::TextDisabled(tr(
"Object stream (set all)"));
2221 if (ImGui::SmallButton(tr(
"Primary##BulkPlacement0")))
2222 tile_handler.UpdateObjectsLayer(room_id, selection_copy, 0);
2224 if (ImGui::SmallButton(tr(
"BG2 overlay##BulkPlacement1")))
2225 tile_handler.UpdateObjectsLayer(room_id, selection_copy, 1);
2227 if (ImGui::SmallButton(tr(
"BG1 overlay##BulkPlacement2")))
2228 tile_handler.UpdateObjectsLayer(room_id, selection_copy, 2);
2229 }
else if (has_special_layer_object && !has_room_stream_object) {
2230 ImGui::TextDisabled(tr(
"Special layer (set all)"));
2231 if (ImGui::SmallButton(tr(
"Upper (BG1)##BulkPlacement0")))
2232 tile_handler.UpdateObjectsLayer(room_id, selection_copy, 0);
2234 if (ImGui::SmallButton(tr(
"Lower (BG2)##BulkPlacement1")))
2235 tile_handler.UpdateObjectsLayer(room_id, selection_copy, 1);
2237 ImGui::TextDisabled(tr(
"Stored placement (mixed selection)"));
2238 if (ImGui::SmallButton(tr(
"Primary / Upper (BG1)##BulkPlacement0")))
2239 tile_handler.UpdateObjectsLayer(room_id, selection_copy, 0);
2241 if (ImGui::SmallButton(tr(
"BG2 overlay / Lower (BG2)##BulkPlacement1")))
2242 tile_handler.UpdateObjectsLayer(room_id, selection_copy, 1);
2246 ImGui::TextDisabled(tr(
"Stacking"));
2248 tile_handler.SendToFront(room_id, selection_copy);
2251 tile_handler.SendToBack(room_id, selection_copy);
2254 tile_handler.MoveForward(room_id, selection_copy);
2257 tile_handler.MoveBackward(room_id, selection_copy);
2260 ImGui::TextDisabled(tr(
"Size"));
2261 if (!has_editable_size) {
2262 ImGui::BeginDisabled();
2265 tile_handler.ResizeObjects(room_id, selection_copy, -1);
2267 if (ImGui::SmallButton(
ICON_MD_ADD "##BulkSizeInc"))
2268 tile_handler.ResizeObjects(room_id, selection_copy, 1);
2269 if (!has_editable_size) {
2270 ImGui::EndDisabled();
2275 (void)interaction.HandleDuplicateSelected();
2279 {ImGuiCol_Button, theme.status_error},
2280 {ImGuiCol_ButtonHovered,
2281 ImVec4(theme.status_error.x + 0.1f, theme.status_error.y + 0.05f,
2282 theme.status_error.z + 0.05f, 1.0f)},
2285 ImGui::OpenPopup(
"##BulkDeleteConfirm");
2288 if (ImGui::BeginPopup(
"##BulkDeleteConfirm")) {
2291 selection_copy.size(), selection_copy.size() == 1 ?
"" :
"s");
2293 if (ImGui::Button(tr(
"Cancel##BulkDelCancel"))) {
2294 ImGui::CloseCurrentPopup();
2299 {ImGuiCol_Button, theme.status_error},
2302 tile_handler.DeleteObjects(room_id, selection_copy);
2303 ImGui::CloseCurrentPopup();
2311 if (indices.size() == 1 && room_id >= 0 && viewer.
rooms()) {
2312 auto& room = (*viewer.
rooms())[room_id];
2313 auto& objects = room.GetTileObjects();
2314 const size_t idx = indices.front();
2315 if (idx < objects.size()) {
2318 const auto obj = objects[idx];
2322 int displayed_layer = obj.GetLayerValue();
2323 const int pixel_x = obj.x_ * 8;
2324 const int pixel_y = obj.y_ * 8;
2329 ImGui::TextColored(theme.text_primary,
"%s", obj_name.c_str());
2330 ImGui::TextDisabled(tr(
"%s (Type %d) #%zu in list"),
2331 GetObjectCategory(obj.id_), subtype, idx);
2334 constexpr ImGuiTableFlags kPropsFlags = ImGuiTableFlags_BordersInnerV |
2335 ImGuiTableFlags_RowBg |
2336 ImGuiTableFlags_NoPadOuterX;
2337 if (ImGui::BeginTable(
"##SelObjProps", 2, kPropsFlags)) {
2338 ImGui::TableSetupColumn(
"Prop", ImGuiTableColumnFlags_WidthFixed,
2340 ImGui::TableSetupColumn(
"Val", ImGuiTableColumnFlags_WidthStretch);
2344 uint16_t obj_id =
static_cast<uint16_t
>(obj.id_ & 0x0FFF);
2349 interaction.SetObjectId(idx,
static_cast<int16_t
>(obj_id));
2357 ImGui::SetNextItemWidth(60);
2359 ImGui::DragInt(
"##SelObjX", &pos_x, 0.1f, 0, 63,
"X:%d");
2363 "Selected room object X coordinate");
2366 ImGui::SetNextItemWidth(60);
2368 ImGui::DragInt(
"##SelObjY", &pos_y, 0.1f, 0, 63,
"Y:%d");
2372 "Selected room object Y coordinate");
2374 if (x_changed || y_changed) {
2375 int delta_x = pos_x - obj.x_;
2376 int delta_y = pos_y - obj.y_;
2377 interaction.entity_coordinator().tile_handler().MoveObjects(
2378 room_id, {idx}, delta_x, delta_y);
2382 uint8_t requested_size = obj.size_;
2384 interaction.SetObjectSize(idx, requested_size);
2388 uses_room_stream ?
"Stream" :
"Layer", [&]() {
2389 int layer = displayed_layer;
2390 const char* stream_names[] = {
"Primary",
"BG2 overlay",
2392 const char* special_layer_names[] = {
"Upper layer (BG1)",
2393 "Lower layer (BG2)"};
2394 ImGui::SetNextItemWidth(-1);
2396 "##SelObjLayer", &layer,
2397 uses_room_stream ? stream_names : special_layer_names,
2398 uses_room_stream ? IM_ARRAYSIZE(stream_names)
2399 : IM_ARRAYSIZE(special_layer_names))) {
2400 const int max_layer = uses_room_stream ? 2 : 1;
2401 layer = std::clamp(layer, 0, max_layer);
2402 if (interaction.SetObjectLayer(
2405 displayed_layer = layer;
2410 ImGui::TextDisabled(
2411 "%s", uses_room_stream ? GetObjectStreamName(displayed_layer)
2412 : GetSpecialLayerName(displayed_layer));
2417 ImGui::TextDisabled(
"(%d, %d)", pixel_x, pixel_y);
2423 if (!description.wheel_hint.empty()) {
2424 ImGui::TextWrapped(
"%s", description.wheel_hint.c_str());
2428 const auto current_indices = interaction.GetSelectedObjectIndices();
2429 if (current_indices.size() == 1) {
2438 " Entity Selection");
The Rom class is used to load, save, and modify Rom data. This is a generic SNES ROM container and do...
const auto & vector() const
void SetCanvasViewer(DungeonCanvasViewer *viewer)
void SetInteraction(DungeonObjectInteraction *interaction)
void Draw(bool *p_open) override
Draw the panel content.
DungeonRoomStore * rooms() const
std::optional< int > DrawConnectedRoomMatrix(int center_room_id)
int current_room_id() const
float ConnectedCanvasScale() const
DungeonObjectInteraction & object_interaction()
ImVec2 GetConnectedContentSize(int center_room_id)
const project::YazeProject * project() const
void DrawProposalPreview(const DungeonProposalOverlay &overlay, const std::vector< char > &layer_visible, bool show_overlay, float scale)
void DrawDungeonCanvas(int room_id)
void DrawGraphicsContext(float width=-1.0f)
WindowContent for displaying multiple rooms in a spatial dungeon layout.
const zelda3::RoomObject * GetPlacementPreview() const
InteractionModeManager & mode_manager()
InteractionCoordinator & entity_coordinator()
Get the interaction coordinator for entity handling.
Handles room and entrance selection UI.
ImVector< int > & mutable_active_rooms()
static void Draw(const DungeonStatusBarState &state)
static DungeonStatusBarState BuildState(const DungeonCanvasViewer &viewer, const char *tool_mode, bool room_dirty)
void DrawInspector(DungeonCanvasViewer &viewer, bool compact)
bool room_dungeon_cache_built_
void DrawInspectorShelfSelection(DungeonCanvasViewer &viewer)
std::function< void(int)> on_room_selected_
void SetAllSaveFlags(bool value)
const char * GetActiveToolIdForTesting() const
bool IsStandaloneToolOpen(WorkbenchTool tool) const
DungeonWorkbenchContent(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< void()> on_save_all_rooms, std::function< DungeonCanvasViewer *()> get_viewer, std::function< DungeonCanvasViewer *(int)> get_compare_viewer, std::function< const std::deque< int > &()> get_recent_rooms, std::function< void(int)> forget_recent_room, std::function< void(bool)> set_workflow_mode, Rom *rom=nullptr)
DungeonEntrancesPanel * entrance_navigator_content_
bool pit_damage_status_error_
void FocusEntranceBrowser()
void DrawProposalPreviewWindow(DungeonCanvasViewer &viewer)
std::function< bool(const std::string &) is_standalone_tool_open_)
std::function< bool()> can_redo_
int GetPriority() const override
Get display priority for menu ordering.
CustomCollisionPanel * custom_collision_panel_
DungeonWorkbenchPitDamageControlRects pit_damage_control_rects_
void DrawSidebarModeTabs()
void RequestProposalPreview()
SidebarMode sidebar_mode_
uint16_t pit_damage_victim_room_id_
WorkbenchTool active_tool_
void DrawSelectedObjectActions(DungeonCanvasViewer &viewer, size_t index)
void OpenTool(WorkbenchTool tool)
bool IsWorkbenchToolAvailable(WorkbenchTool tool) const
WaterFillPanel * water_fill_panel_
void DrawInspectorHeader(float button_size, bool compact)
void FocusSelectionInspector()
std::function< void()> on_redo_
WindowContent * object_selector_content_
void OpenObjectCoverageTool()
void DrawSidebarPane(float width, float height, float button_size, bool compact)
DungeonMapPanel * GetEmbeddedDungeonMap(DungeonCanvasViewer &viewer)
void DrawSidebarContent()
InspectorMode inspector_mode_
void DrawInspectorPane(float width, float height, float button_size, bool compact, DungeonCanvasViewer *viewer)
int pit_damage_chooser_room_id_
std::string proposal_error_
std::vector< char > proposal_layer_visible_
void DrawInspectorShelfRoom(DungeonCanvasViewer &viewer)
WindowContent * GetWorkbenchToolContent(WorkbenchTool tool) const
const char * GetInspectorModeIdForTesting() const
WindowContent * sprite_editor_content_
std::function< int()> undo_depth_
std::function< void()> open_keyboard_shortcuts_
std::string GetEditorCategory() const override
Editor category this panel belongs to.
std::optional< DungeonProposalOverlay > proposal_overlay_
void OpenRoomGraphicsTool()
std::function< std::string()> undo_desc_
void FocusRoomInspector()
std::function< bool()> can_undo_
RoomTagEditorPanel * room_tag_panel_
void DrawPitDamageControls(int room_id)
void DrawWorkbenchTool(DungeonCanvasViewer &viewer, WorkbenchTool tool)
DungeonRoomSelector * room_selector_
void DrawSidebarHeader(float button_size, bool compact)
std::function< void()> on_undo_
const char * GetWorkbenchToolShortLabel(WorkbenchTool tool) const
std::function< std::string()> redo_desc_
void DrawDungeonMapPopup(DungeonCanvasViewer &viewer)
std::unique_ptr< DungeonMapPanel > embedded_dungeon_map_
char compare_search_buf_[64]
char proposal_path_[1024]
void DrawInspectorToolPanel(DungeonCanvasViewer &viewer)
WindowContent * item_editor_content_
std::function< void(int, RoomSelectionIntent)> on_room_selected_with_intent_
void SetEmbeddedEditorPanels(WindowContent *object_selector, WindowContent *door_editor, WindowContent *sprite_editor, WindowContent *item_editor, WindowContent *room_graphics, WindowContent *palette_editor)
MinecartTrackEditorPanel * minecart_track_panel_
void SetEmbeddedToolPanels(RoomTagEditorPanel *room_tags, CustomCollisionPanel *custom_collision, WaterFillPanel *water_fill, MinecartTrackEditorPanel *minecart_tracks)
void DrawCanvasPane(float width, float height, DungeonCanvasViewer *primary_viewer)
std::function< void(DungeonCanvasViewer &) on_primary_canvas_drawn_)
const char * GetWorkbenchToolId(WorkbenchTool tool) const
const char * GetWorkbenchToolUnavailableMessage(WorkbenchTool tool) const
std::function< void(int)> on_save_room_
bool DrawObjectPlacementInspector(DungeonCanvasViewer &viewer)
const char * GetSidebarModeIdForTesting() const
WindowContent * palette_editor_content_
WindowContent * room_graphics_content_
std::string pit_damage_status_message_
std::string GetId() const override
Unique identifier for this panel.
bool LoadProposalOverlay(const std::string &path)
void DrawSplitView(DungeonCanvasViewer &primary_viewer)
void OpenObjectSelectorTool()
RoomMatrixContent * room_matrix_content_
std::function< zelda3::PitDamageTable *()> get_pit_damage_table_
void CloseToolInspector()
std::function< void(int)> prepare_room_render_context_
std::function< void()> on_save_all_rooms_
WindowContent * object_coverage_content_
std::function< void(bool)> set_workflow_mode_
void ShowConnectedGraph()
std::string GetIcon() const override
Material Design icon for this panel.
std::function< DungeonCanvasViewer *(int)> get_compare_viewer_
std::unordered_map< int, std::string > room_dungeon_cache_
std::string GetDisplayName() const override
Human-readable name shown in menus and title bars.
void RequestDungeonMapPopup()
std::function< void(bool)> on_inspector_side_changed_
~DungeonWorkbenchContent() override
void DrawApplyScopeControls(int room_id)
uint16_t pit_damage_replacement_room_id_
void Draw(bool *p_open) override
Draw the panel content.
void OpenCustomCollisionTool()
DungeonWorkbenchLayoutState layout_state_
bool open_dungeon_map_popup_
bool proposal_show_overlay_
bool show_proposal_preview_
std::function< bool(const std::string &) open_and_focus_standalone_tool_)
bool IsToolInspectorActiveForTesting() const
std::function< void(int)> forget_recent_room_
std::function< const std::deque< int > &()> get_recent_rooms_
void DrawInspectorToolPicker()
float navigator_tools_fraction_
void DrawInspectorShelf(DungeonCanvasViewer &viewer, bool compact)
void BuildRoomDungeonCache()
std::function< DungeonCanvasViewer *()> get_viewer_
WindowContent * door_editor_content_
TileObjectHandler & tile_handler()
const char * GetModeName() const
Get mode name for debugging/UI.
WindowContent for placing and managing dungeon pot items.
void set_canvas_editing(bool enabled)
void set_collision_stamping(bool enabled)
void Draw(bool *p_open) override
Draw the panel content.
Browse and place dungeon objects.
void Draw(bool *p_open) override
Draw the panel content.
WindowContent showing all room tag slots and their usage across rooms.
void SetCurrentRoomId(int room_id)
void Draw(bool *p_open) override
Draw the panel content.
WindowContent for placing and managing dungeon sprites.
void SetCanvasViewer(DungeonCanvasViewer *viewer)
void SetInteraction(DungeonObjectInteraction *interaction)
void Draw(bool *p_open) override
Draw the panel content.
Base interface for all logical window content components.
virtual void Draw(bool *p_open)=0
Draw the panel content.
virtual std::string GetId() const =0
Unique identifier for this panel.
void ApplyScaleSnapshot(const CanvasConfig &snapshot)
CanvasConfig & GetConfig()
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)
RAII guard for ImGui style colors.
RAII guard for ImGui style vars.
static std::string ShowOpenFileDialog()
ShowOpenFileDialog opens a file dialog and returns the selected filepath. Uses global feature flag to...
Dungeon Room Entrance or Spawn Point.
const absl::Status & load_status() const
#define ICON_MD_FOLDER_OPEN
#define ICON_MD_SUMMARIZE
#define ICON_MD_VIEW_QUILT
#define ICON_MD_ARROW_FORWARD
#define ICON_MD_SWAP_HORIZ
#define ICON_MD_COMPARE_ARROWS
#define ICON_MD_OPEN_WITH
#define ICON_MD_FLIP_TO_FRONT
#define ICON_MD_ARROW_DOWNWARD
#define ICON_MD_MORE_HORIZ
#define ICON_MD_KEYBOARD_ARROW_DOWN
#define ICON_MD_CHEVRON_LEFT
#define ICON_MD_ARROW_UPWARD
#define ICON_MD_CHECK_CIRCLE
#define ICON_MD_KEYBOARD_ARROW_UP
#define ICON_MD_ARROW_BACK
#define ICON_MD_SELECT_ALL
#define ICON_MD_CONTENT_COPY
#define ICON_MD_CHEVRON_RIGHT
#define ICON_MD_FLIP_TO_BACK
#define ICON_MD_WORKSPACES
#define ICON_MD_ADD_CIRCLE
#define ICON_MD_CROP_FREE
const AgentUITheme & GetTheme()
constexpr float kCompactLeftSidebarScale
const char * GetObjectStreamName(int layer_value)
float GetCompactSidebarWidth(bool right_sidebar, float min_sidebar_width)
DungeonWorkbenchTestRect CaptureLastItemRectForTesting()
constexpr float kCompactLeftSidebarMinWidth
constexpr float kCompactRightSidebarMinWidth
const char * GetSpecialLayerName(int layer_value)
constexpr float kCompactRightSidebarScale
float ClampWorkbenchPaneWidth(float desired_width, float min_width, float max_width)
const char * GetObjectCategory(int object_id)
std::string GetRoomLabel(const project::YazeProject *project, int room_id)
void DrawPaneHeader(const char *table_id, const char *icon, const char *title, const char *compact_title, const char *subtitle, bool compact, float action_width, const std::function< void()> &draw_actions)
float CalcIconButtonWidth(const char *icon, float button_height)
ObjectSizeDescription DescribeObjectSize(const zelda3::RoomObject &object)
bool DrawObjectSizeControls(const zelda3::RoomObject &object, uint8_t *requested_size)
bool DrawActionButton(const char *label, const ImVec2 &size)
bool HasEditableRoomObjectSize(std::span< const zelda3::RoomObject > objects, std::span< const size_t > selected_indices)
void DrawInspectorSectionHeader(const char *label)
bool DrawHeaderIconAction(const char *id, const char *icon, float button_size, const char *tooltip, bool active=false)
constexpr const char * kConnected
Editors are the view controllers for the application.
void DrawDungeonEntityInspector(DungeonCanvasViewer &viewer, const std::function< void(int, size_t)> &jump_to_reciprocal)
absl::StatusOr< DungeonProposalOverlay > ParseDungeonProposalOverlay(std::string_view json_text, int max_room_id)
DungeonWorkbenchPaneLayout ResolveDungeonWorkbenchPaneLayout(float total_width, float min_canvas_width, float min_sidebar_width, float splitter_width, float stored_left_width, float stored_right_width, bool want_left, bool want_right)
PitDamageMembershipState BuildPitDamageMembershipState(const zelda3::PitDamageTable *table, int room_id, uint16_t replacement_fallback, uint16_t victim_fallback)
absl::Status RemoveCurrentRoomFromPitDamage(zelda3::PitDamageTable *table, uint16_t current_room_id, uint16_t replacement_room_id)
RoomSelectionIntent
Intent for room selection in the dungeon editor.
DungeonWorkbenchResponsiveLayout ResolveDungeonWorkbenchResponsiveLayout(float total_width, float min_canvas_width, float min_sidebar_width, float splitter_width, bool want_left, bool want_right)
DungeonWorkbenchToolRequestTarget
bool DrawDungeonWorkbenchVerticalSplitter(const char *id, float height, float *pane_width, float min_width, float max_width, bool resize_from_left_edge, float collapse_threshold)
DungeonWorkbenchToolRequestTarget ResolveDungeonWorkbenchToolRequestTarget(bool standalone_window_open)
absl::Status AddCurrentRoomToPitDamage(zelda3::PitDamageTable *table, uint16_t current_room_id, uint16_t victim_room_id)
std::string FormatCtrlShiftShortcut(ImGuiKey key)
Convenience function for Ctrl+Shift+key shortcuts.
void AutoRegisterLastItem(const std::string &widget_type, const std::string &explicit_label, const std::string &description)
Automatically register the last ImGui item.
InputHexResult InputHexWordEx(const char *label, uint16_t *data, float input_width, bool no_step)
int GetObjectSubtype(int object_id)
constexpr int kNumRegularDungeonEntrances
bool UsesRoomObjectStream(const RoomObject &object)
constexpr int kNumDungeonSpawnPoints
std::string GetObjectName(int object_id)
constexpr int kNumberOfRooms
absl::StatusOr< EntranceLayout > ResolveEntranceLayout(std::span< const uint8_t > data)
struct yaze::core::FeatureFlags::Flags::Dungeon dungeon
std::vector< ProposalRoom > rooms
std::vector< std::string > notes
std::vector< ProposalLayer > layers
bool show_connected_canvas_view
bool show_right_inspector
DungeonWorkbenchResponsiveLayout responsive
DungeonWorkbenchTestRect replace_current
DungeonWorkbenchTestRect add_current
static constexpr float kStatusBarHeight
static constexpr float kSplitterWidth
static constexpr float kContentMinWidthSidebar
Modern project structure with comprehensive settings consolidation.
std::vector< FileDialogFilter > filters