yaze 0.3.2
Link to the Past ROM Editor
 
Loading...
Searching...
No Matches
dungeon_canvas_context_menu.cc
Go to the documentation of this file.
2
3#include <optional>
4#include <string>
5#include <utility>
6
7#include "absl/strings/str_format.h"
10#include "app/gui/core/icons.h"
11#include "imgui/imgui.h"
12#include "util/log.h"
17
18namespace yaze::editor {
19
25
28 return;
29 }
30
31 for (auto& item : BuildSelectionContextMenuItems(room_id)) {
32 canvas_.AddContextMenuItem(std::move(item));
33 }
34 auto insert_menu = BuildInsertContextMenu();
35 insert_menu.separator_after = true;
36 canvas_.AddContextMenuItem(insert_menu);
37}
38
54
56 auto enabled_if = [](bool enabled) {
57 return [enabled]() {
58 return enabled;
59 };
60 };
61
62 gui::CanvasMenuItem insert_menu;
63 insert_menu.label = "Insert";
64 insert_menu.icon = ICON_MD_ADD_CIRCLE;
65
66 gui::CanvasMenuItem insert_object_item("Object...", ICON_MD_WIDGETS,
67 [this]() {
70 }
71 });
72 insert_object_item.enabled_condition =
73 enabled_if(show_object_panel_callback_ != nullptr);
74 insert_menu.subitems.push_back(std::move(insert_object_item));
75
76 gui::CanvasMenuItem insert_sprite_item("Sprite...", ICON_MD_PERSON, [this]() {
79 }
80 });
81 insert_sprite_item.enabled_condition =
82 enabled_if(show_sprite_panel_callback_ != nullptr);
83 insert_menu.subitems.push_back(std::move(insert_sprite_item));
84
85 gui::CanvasMenuItem insert_item_item("Item...", ICON_MD_INVENTORY, [this]() {
88 }
89 });
90 insert_item_item.enabled_condition =
91 enabled_if(show_item_panel_callback_ != nullptr);
92 insert_menu.subitems.push_back(std::move(insert_item_item));
93
94 const bool door_mode = object_interaction_.IsDoorPlacementActive();
95 insert_menu.subitems.emplace_back(
96 door_mode ? "Cancel Door Placement" : "Door (Normal)", ICON_MD_DOOR_FRONT,
97 [this, door_mode]() {
100 });
101
102 return insert_menu;
103}
104
105std::optional<zelda3::RoomObject>
107 if (!rooms_ || room_id < 0 || room_id >= zelda3::kNumberOfRooms) {
108 return std::nullopt;
109 }
110
111 const ImGuiIO& io = ImGui::GetIO();
112 const DungeonCanvasTransform transform(
114 const auto [canvas_x, canvas_y] =
115 transform.ScreenToRoomPixelCoordinates(io.MousePos);
116 if (!dungeon_coords::IsWithinBounds(canvas_x, canvas_y)) {
117 return std::nullopt;
118 }
119 const auto hovered_index = object_interaction_.entity_coordinator()
120 .tile_handler()
121 .GetEntityAtPosition(canvas_x, canvas_y);
122 if (!hovered_index.has_value()) {
123 return std::nullopt;
124 }
125
126 const auto& objects = (*rooms_)[room_id].GetTileObjects();
127 if (*hovered_index >= objects.size()) {
128 return std::nullopt;
129 }
130 return objects[*hovered_index];
131}
132
133std::vector<gui::CanvasMenuItem>
135 auto& interaction = object_interaction_;
136 const auto selected = interaction.GetSelectedObjectIndices();
137 const bool has_selection = !selected.empty();
138 const bool single_selection = selected.size() == 1;
139 const bool group_selection = selected.size() > 1;
140 const bool has_clipboard = interaction.HasClipboardData();
141 const bool placing_object = interaction.IsObjectLoaded();
142 const bool valid_room =
143 rooms_ && room_id >= 0 && room_id < zelda3::kNumberOfRooms;
144 const bool room_has_objects =
145 valid_room && !(*rooms_)[room_id].GetTileObjects().empty();
146 bool has_room_stream_object = false;
147 bool has_special_layer_object = false;
148 if (has_selection && valid_room) {
149 const auto& objects = (*rooms_)[room_id].GetTileObjects();
150 for (const size_t index : selected) {
151 if (index >= objects.size()) {
152 continue;
153 }
154 if (zelda3::UsesRoomObjectStream(objects[index])) {
155 has_room_stream_object = true;
156 } else {
157 has_special_layer_object = true;
158 }
159 }
160 }
161
162 std::vector<gui::CanvasMenuItem> items;
163
164 bool can_edit_selected_graphics = false;
165 std::function<void()> edit_selected_graphics;
166 bool can_edit_selected_tiles = false;
167 std::function<void()> edit_selected_tiles;
168 if (single_selection && valid_room) {
169 auto& room = (*rooms_)[room_id];
170 const auto& objects = room.GetTileObjects();
171 if (selected[0] < objects.size()) {
172 const auto object = objects[selected[0]];
173 can_edit_selected_graphics = edit_graphics_callback_ != nullptr ||
175 edit_selected_graphics = [this, room_id, object]() {
177 edit_graphics_callback_(room_id, object);
178 } else if (show_room_graphics_callback_) {
180 }
181 };
182 can_edit_selected_tiles =
183 edit_object_tiles_callback_ != nullptr &&
185 edit_selected_tiles = [this, room_id, object]() {
187 edit_object_tiles_callback_(room_id, object);
188 }
189 };
190 }
191 }
192
193 auto enabled_if = [](bool enabled) {
194 return [enabled]() {
195 return enabled;
196 };
197 };
198 auto paste_item = [&]() {
200 "Paste", ICON_MD_CONTENT_PASTE,
201 [&interaction]() { interaction.HandlePasteObjects(); }, "Ctrl+V");
202 item.enabled_condition = enabled_if(has_clipboard);
203 return item;
204 };
205 auto disabled_item = [](const char* label, const char* icon,
206 const char* shortcut = nullptr) {
208 item.label = label;
209 item.icon = icon;
210 if (shortcut != nullptr) {
211 item.shortcut = shortcut;
212 }
213 item.enabled_condition = []() {
214 return false;
215 };
216 return item;
217 };
218 auto layer_item = [&interaction](const char* label, int layer,
219 const char* shortcut) {
220 return gui::CanvasMenuItem(
221 label, ICON_MD_LAYERS,
222 [&interaction, layer]() { interaction.SendSelectedToLayer(layer); },
223 shortcut);
224 };
225
226 const auto context_sample_object = GetObjectUnderContextCursor(room_id);
227 std::optional<size_t> sample_item_index;
228 if (context_sample_object.has_value()) {
229 sample_item_index = items.size();
230 items.emplace_back("Sample Object", ICON_MD_COLORIZE,
231 [this, object = *context_sample_object]() {
232 SetPreviewObject(object);
233 });
234 }
235
236 if (has_selection) {
237 items.emplace_back(
238 "Cut", ICON_MD_CONTENT_CUT,
239 [&interaction]() {
240 interaction.HandleCopySelected();
241 interaction.HandleDeleteSelected();
242 },
243 "Ctrl+X");
244 items.emplace_back(
245 "Copy", ICON_MD_CONTENT_COPY,
246 [&interaction]() { interaction.HandleCopySelected(); }, "Ctrl+C");
247 items.push_back(paste_item());
248 items.emplace_back(
249 "Delete", ICON_MD_DELETE,
250 [&interaction]() { interaction.HandleDeleteSelected(); }, "Del");
251
252 items.emplace_back(
253 "Bring to Front", ICON_MD_FLIP_TO_FRONT,
254 [&interaction]() { interaction.SendSelectedToFront(); },
255 "Ctrl+Shift+]");
256 items.emplace_back(
257 "Send to Back", ICON_MD_FLIP_TO_BACK,
258 [&interaction]() { interaction.SendSelectedToBack(); }, "Ctrl+Shift+[");
259
260 gui::CanvasMenuItem z_order;
261 z_order.label = "Z Order";
262 z_order.icon = ICON_MD_LAYERS;
263 z_order.subitems.emplace_back(
264 "Bring Forward", ICON_MD_ARROW_UPWARD,
265 [&interaction]() { interaction.BringSelectedForward(); }, "Ctrl+]");
266 z_order.subitems.emplace_back(
267 "Send Backward", ICON_MD_ARROW_DOWNWARD,
268 [&interaction]() { interaction.SendSelectedBackward(); }, "Ctrl+[");
269 items.push_back(std::move(z_order));
270
271 if (has_room_stream_object && !has_special_layer_object) {
272 items.push_back(layer_item("Object Stream: Primary", 0, "1"));
273 items.push_back(layer_item("Object Stream: BG2 Overlay", 1, "2"));
274 items.push_back(layer_item("Object Stream: BG1 Overlay", 2, "3"));
275 } else if (has_special_layer_object && !has_room_stream_object) {
276 items.push_back(layer_item("Upper Layer (BG1)", 0, "1"));
277 items.push_back(layer_item("Lower Layer (BG2)", 1, "2"));
278 } else {
279 items.push_back(
280 layer_item("Placement 0: Primary / Upper Layer (BG1)", 0, "1"));
281 items.push_back(
282 layer_item("Placement 1: BG2 Overlay / Lower Layer (BG2)", 1, "2"));
283 }
284
285 gui::CanvasMenuItem yaze_more;
286 yaze_more.label = "More";
287 yaze_more.icon = ICON_MD_MORE_HORIZ;
288 yaze_more.subitems.emplace_back(
289 "Duplicate", ICON_MD_CONTENT_PASTE,
290 [&interaction]() {
291 interaction.HandleCopySelected();
292 interaction.HandlePasteObjects();
293 },
294 "Ctrl+D");
295 if (group_selection) {
296 yaze_more.subitems.push_back(
297 disabled_item("Save As New Layout...", ICON_MD_SAVE_AS, nullptr));
298 }
299 items.push_back(std::move(yaze_more));
300 }
301
302 if (can_edit_selected_graphics) {
303 items.emplace_back("Edit Graphics...", ICON_MD_IMAGE,
304 std::move(edit_selected_graphics));
305 }
306 if (can_edit_selected_tiles) {
307 items.emplace_back("Edit Object Tiles...", ICON_MD_GRID_ON,
308 std::move(edit_selected_tiles));
309 }
310
311 const bool has_entity_selection = interaction.HasEntitySelection();
312 if (has_entity_selection && !has_selection) {
313 items.emplace_back(
314 "Delete", ICON_MD_DELETE,
315 [&interaction]() { interaction.HandleDeleteSelected(); }, "Del");
316 }
317
318 if (!has_selection && !has_entity_selection) {
319 items.push_back(paste_item());
320 items.push_back(disabled_item("Delete", ICON_MD_DELETE, "Del"));
321 gui::CanvasMenuItem delete_all_item(
322 "Delete All", ICON_MD_DELETE_FOREVER,
323 [&interaction]() { interaction.HandleDeleteAllObjects(); });
324 delete_all_item.enabled_condition = enabled_if(room_has_objects);
325 items.push_back(std::move(delete_all_item));
326 }
327
328 if (placing_object) {
329 gui::CanvasMenuItem cancel_item(
330 "Cancel Placement", ICON_MD_CANCEL,
331 [&interaction]() { interaction.CancelPlacement(); }, "Esc");
332 if (!items.empty()) {
333 items.back().separator_after = true;
334 }
335 items.push_back(std::move(cancel_item));
336 }
337
338 if (sample_item_index.has_value() && items.size() > *sample_item_index + 1) {
339 items[*sample_item_index].separator_after = true;
340 }
341
342 if (!items.empty()) {
343 items.back().separator_after = true;
344 }
345 return items;
346}
347
349 gui::CanvasMenuItem room_menu;
350 room_menu.label = "Room";
351 room_menu.icon = ICON_MD_HOME;
353 room_menu.subitems.emplace_back(
354 "Apply Room to ROM", ICON_MD_SAVE,
355 [this, room_id]() { save_room_callback_(room_id); }, "Ctrl+Shift+S");
356 }
357 room_menu.subitems.emplace_back(
358 "Re-render Room", ICON_MD_REFRESH,
359 [this, room_id]() {
360 if (rooms_ && room_id >= 0 && room_id < zelda3::kNumberOfRooms) {
361 (*rooms_)[room_id].RenderRoomGraphics();
362 }
363 },
364 "Ctrl+R");
365 if (rooms_ && !(*rooms_)[room_id].GetTileObjects().empty()) {
366 room_menu.subitems.emplace_back(
367 "Delete All Objects", ICON_MD_DELETE_FOREVER,
369 }
370 return room_menu;
371}
372
374 gui::CanvasMenuItem report_menu;
375 report_menu.label = "Report";
376 report_menu.icon = ICON_MD_BUG_REPORT;
377 report_menu.subitems.emplace_back(
378 "Render Issue...", ICON_MD_BUG_REPORT, [this, room_id]() {
379 if (!rooms_ || room_id < 0 || room_id >= zelda3::kNumberOfRooms) {
380 return;
381 }
383 absl::StrFormat("Report Room 0x%03X Render Issue", room_id),
384 absl::StrFormat("Room 0x%03X render mismatch", room_id),
385 "Dungeon Render Issue Report",
386 BuildDrawIssueReport((*rooms_)[room_id], room_id), room_id,
389 });
390 report_menu.subitems.emplace_back(
391 "Palette Issue...", ICON_MD_PALETTE, [this, room_id]() {
392 if (!rooms_ || room_id < 0 || room_id >= zelda3::kNumberOfRooms) {
393 return;
394 }
396 absl::StrFormat("Report Room 0x%03X Palette Issue", room_id),
397 absl::StrFormat("Room 0x%03X palette mismatch", room_id),
398 "Dungeon Palette Issue Report",
399 BuildDrawIssueReport((*rooms_)[room_id], room_id), room_id,
401 });
404 report_menu.subitems.emplace_back(
405 "Selection Issue...", ICON_MD_FACT_CHECK, [this, room_id]() {
406 if (!rooms_ || room_id < 0 || room_id >= zelda3::kNumberOfRooms) {
407 return;
408 }
410 absl::StrFormat("Report Selection Issue for Room 0x%03X",
411 room_id),
412 absl::StrFormat("Room 0x%03X selection or entity mismatch",
413 room_id),
414 "Dungeon Selection Issue Report",
415 BuildSelectionIssueReport((*rooms_)[room_id], room_id), room_id,
418 });
419 }
420 return report_menu;
421}
422
423std::optional<gui::CanvasMenuItem> DungeonCanvasViewer::BuildOpenContextMenu() {
424 gui::CanvasMenuItem open_menu;
425 open_menu.label = "Open";
426 open_menu.icon = ICON_MD_OPEN_IN_NEW;
428 open_menu.subitems.emplace_back("Room Matrix", ICON_MD_GRID_VIEW,
429 [this]() { show_room_matrix_callback_(); });
430 }
432 open_menu.subitems.emplace_back("Room Graphics", ICON_MD_IMAGE, [this]() {
434 });
435 }
437 open_menu.subitems.emplace_back("Door Editor", ICON_MD_DOOR_FRONT,
438 [this]() { show_door_editor_callback_(); });
439 }
441 open_menu.subitems.emplace_back("Entrance List", ICON_MD_LOGIN, [this]() {
443 });
444 }
445 if (open_menu.subitems.empty()) {
446 return std::nullopt;
447 }
448 return open_menu;
449}
450
452 int room_id) {
453 gui::CanvasMenuItem copy_export_menu;
454 copy_export_menu.label = "Copy / Export";
455 copy_export_menu.icon = ICON_MD_CONTENT_COPY;
456 copy_export_menu.subitems.emplace_back(
457 "Copy Room Summary", ICON_MD_ASSIGNMENT, [this, room_id]() {
458 if (!rooms_ || room_id < 0 || room_id >= zelda3::kNumberOfRooms) {
459 return;
460 }
461 ImGui::SetClipboardText(
462 BuildRoomMetadataSummary((*rooms_)[room_id], room_id).c_str());
463 });
464 copy_export_menu.subitems.emplace_back(
465 "Copy Room ID", ICON_MD_CONTENT_COPY, [room_id]() {
466 ImGui::SetClipboardText(absl::StrFormat("0x%03X", room_id).c_str());
467 });
468 if (rooms_ && room_id >= 0 && room_id < zelda3::kNumberOfRooms) {
469 const std::string room_label = zelda3::GetRoomLabel(room_id);
470 copy_export_menu.subitems.emplace_back(
471 "Copy Room Name", ICON_MD_CONTENT_COPY,
472 [room_label]() { ImGui::SetClipboardText(room_label.c_str()); });
473 }
474 copy_export_menu.subitems.emplace_back(
475 "Export Layout Template...", ICON_MD_FILE_DOWNLOAD, [this, room_id]() {
476 if (!rooms_ || room_id < 0 || room_id >= zelda3::kNumberOfRooms) {
477 return;
478 }
479 auto& room = (*rooms_)[room_id];
480 auto result = zelda3::ExportRoomLayoutTemplate(room);
481 if (result.ok()) {
482 ImGui::SetClipboardText(result.value().c_str());
483 }
484 });
485 return copy_export_menu;
486}
487
489 int room_id) {
490 gui::CanvasMenuItem layers_menu;
491 layers_menu.label = "Layers";
492 layers_menu.icon = ICON_MD_VISIBILITY;
493
494 gui::CanvasMenuItem bg1_layout_item("BG1 Layout", [this, room_id]() {
495 auto& mgr = GetRoomLayerManager(room_id);
496 mgr.SetLayerVisible(zelda3::LayerType::BG1_Layout,
497 !mgr.IsLayerVisible(zelda3::LayerType::BG1_Layout));
498 });
499 bg1_layout_item.checked_condition = [this, room_id]() {
500 return GetRoomLayerManager(room_id).IsLayerVisible(
502 };
503 layers_menu.subitems.push_back(std::move(bg1_layout_item));
504
505 gui::CanvasMenuItem bg1_objects_item("BG1 Objects", [this, room_id]() {
506 auto& mgr = GetRoomLayerManager(room_id);
507 mgr.SetLayerVisible(zelda3::LayerType::BG1_Objects,
508 !mgr.IsLayerVisible(zelda3::LayerType::BG1_Objects));
509 });
510 bg1_objects_item.checked_condition = [this, room_id]() {
511 return GetRoomLayerManager(room_id).IsLayerVisible(
513 };
514 layers_menu.subitems.push_back(std::move(bg1_objects_item));
515
516 gui::CanvasMenuItem bg2_layout_item("BG2 Layout", [this, room_id]() {
517 auto& mgr = GetRoomLayerManager(room_id);
518 mgr.SetLayerVisible(zelda3::LayerType::BG2_Layout,
519 !mgr.IsLayerVisible(zelda3::LayerType::BG2_Layout));
520 });
521 bg2_layout_item.checked_condition = [this, room_id]() {
522 return GetRoomLayerManager(room_id).IsLayerVisible(
524 };
525 layers_menu.subitems.push_back(std::move(bg2_layout_item));
526
527 gui::CanvasMenuItem bg2_objects_item("BG2 Objects", [this, room_id]() {
528 auto& mgr = GetRoomLayerManager(room_id);
529 mgr.SetLayerVisible(zelda3::LayerType::BG2_Objects,
530 !mgr.IsLayerVisible(zelda3::LayerType::BG2_Objects));
531 });
532 bg2_objects_item.checked_condition = [this, room_id]() {
533 return GetRoomLayerManager(room_id).IsLayerVisible(
535 };
536 layers_menu.subitems.push_back(std::move(bg2_objects_item));
537
538 gui::CanvasMenuItem sprites_item("Sprites", ICON_MD_PERSON, [this]() {
540 });
541 sprites_item.checked_condition = [this]() {
543 };
544 layers_menu.subitems.push_back(std::move(sprites_item));
545
546 gui::CanvasMenuItem pot_items_item("Pot Items", ICON_MD_INVENTORY, [this]() {
548 });
549 pot_items_item.checked_condition = [this]() {
551 };
552 layers_menu.subitems.push_back(std::move(pot_items_item));
553
554 return layers_menu;
555}
556
558 gui::CanvasMenuItem overlays_menu;
559 overlays_menu.label = "Overlays";
560 overlays_menu.icon = ICON_MD_LAYERS;
561
562 gui::CanvasMenuItem coordinates_item(
563 "Coordinates", ICON_MD_MY_LOCATION,
565 coordinates_item.checked_condition = [this]() {
567 };
568 overlays_menu.subitems.push_back(std::move(coordinates_item));
569
570 gui::CanvasMenuItem minecart_toggle(
571 "Minecart Tracks", ICON_MD_TRAIN,
573 minecart_toggle.enabled_condition = [this]() {
574 return minecart_track_panel_ != nullptr;
575 };
576 minecart_toggle.checked_condition = [this]() {
578 };
579 overlays_menu.subitems.push_back(std::move(minecart_toggle));
580
581 gui::CanvasMenuItem custom_collision_item(
582 "Custom Collision", ICON_MD_GRID_ON, [this]() {
584 });
585 custom_collision_item.checked_condition = [this]() {
587 };
588 overlays_menu.subitems.push_back(std::move(custom_collision_item));
589
590 gui::CanvasMenuItem track_collision_item(
591 "Track Collision", ICON_MD_LAYERS, [this]() {
593 });
594 track_collision_item.checked_condition = [this]() {
596 };
597 overlays_menu.subitems.push_back(std::move(track_collision_item));
598
599 gui::CanvasMenuItem camera_quadrants_item(
600 "Camera Quadrants", ICON_MD_GRID_VIEW, [this]() {
602 });
603 camera_quadrants_item.checked_condition = [this]() {
605 };
606 overlays_menu.subitems.push_back(std::move(camera_quadrants_item));
607
608 gui::CanvasMenuItem minecart_sprites_item(
609 "Minecart Sprites", ICON_MD_TRAIN, [this]() {
611 });
612 minecart_sprites_item.checked_condition = [this]() {
614 };
615 overlays_menu.subitems.push_back(std::move(minecart_sprites_item));
616
618 gui::CanvasMenuItem collision_legend_item(
619 "Collision Legend", ICON_MD_INFO, [this]() {
621 });
622 collision_legend_item.checked_condition = [this]() {
624 };
625 overlays_menu.subitems.push_back(std::move(collision_legend_item));
626 }
627
628 gui::CanvasMenuItem object_bounds_item(
629 "Object Bounds", ICON_MD_CROP_SQUARE,
631 object_bounds_item.checked_condition = [this]() {
632 return show_object_bounds_;
633 };
634 overlays_menu.subitems.push_back(std::move(object_bounds_item));
635
636 return overlays_menu;
637}
638
640 gui::CanvasMenuItem debug_menu;
641 debug_menu.label = "Debug";
642 debug_menu.icon = ICON_MD_BUG_REPORT;
643
644 debug_menu.subitems.push_back(gui::CanvasMenuItem(
645 "Room Info", ICON_MD_INFO,
647 debug_menu.subitems.back().checked_condition = [this]() {
649 };
650
651 debug_menu.subitems.push_back(gui::CanvasMenuItem(
652 "Texture Debug", ICON_MD_IMAGE,
653 [this]() { show_texture_debug_ = !show_texture_debug_; }));
654 debug_menu.subitems.back().checked_condition = [this]() {
655 return show_texture_debug_;
656 };
657
658 gui::CanvasMenuItem object_bounds_menu;
659 object_bounds_menu.label = "Object Bounds Filter";
660 object_bounds_menu.icon = ICON_MD_CROP_SQUARE;
661
662 object_bounds_menu.subitems.push_back(
663 gui::CanvasMenuItem("Type 1 (0x00-0xFF)", [this]() {
666 }));
667 object_bounds_menu.subitems.back().checked_condition = [this]() {
669 };
670 object_bounds_menu.subitems.push_back(
671 gui::CanvasMenuItem("Type 2 (0x100-0x1FF)", [this]() {
674 }));
675 object_bounds_menu.subitems.back().checked_condition = [this]() {
677 };
678 object_bounds_menu.subitems.push_back(
679 gui::CanvasMenuItem("Type 3 (0xF00-0xFFF)", [this]() {
682 }));
683 object_bounds_menu.subitems.back().checked_condition = [this]() {
685 };
686
688 sep.label = "---";
689 sep.enabled_condition = []() {
690 return false;
691 };
692 object_bounds_menu.subitems.push_back(std::move(sep));
693
694 object_bounds_menu.subitems.push_back(
695 gui::CanvasMenuItem("Primary / Upper layer (BG1)", [this]() {
698 }));
699 object_bounds_menu.subitems.back().checked_condition = [this]() {
701 };
702 object_bounds_menu.subitems.push_back(
703 gui::CanvasMenuItem("BG2 overlay / Lower layer (BG2)", [this]() {
706 }));
707 object_bounds_menu.subitems.back().checked_condition = [this]() {
709 };
710 object_bounds_menu.subitems.push_back(
711 gui::CanvasMenuItem("BG1 overlay (room stream only)", [this]() {
714 }));
715 object_bounds_menu.subitems.back().checked_condition = [this]() {
717 };
718 debug_menu.subitems.push_back(std::move(object_bounds_menu));
719
720 debug_menu.subitems.push_back(
722 [this]() { show_layer_info_ = !show_layer_info_; }));
723 debug_menu.subitems.back().checked_condition = [this]() {
724 return show_layer_info_;
725 };
726
727 debug_menu.subitems.push_back(
728 gui::CanvasMenuItem("Force Reload", ICON_MD_REFRESH, [this, room_id]() {
729 if (rooms_ && room_id >= 0 && room_id < zelda3::kNumberOfRooms) {
730 (*rooms_)[room_id].ReloadGraphics(current_entrance_blockset_);
731 }
732 }));
733
734 debug_menu.subitems.push_back(
735 gui::CanvasMenuItem("Log Room State", ICON_MD_PRINT, [this, room_id]() {
736 if (!rooms_ || room_id < 0 || room_id >= zelda3::kNumberOfRooms) {
737 return;
738 }
739 const auto& room = (*rooms_)[room_id];
740 LOG_DEBUG("DungeonDebug", "=== Room %03X Debug ===", room_id);
741 LOG_DEBUG("DungeonDebug", "Blockset: %d, Palette: %d, Layout: %d",
742 room.blockset(), room.palette(), room.layout_id());
743 LOG_DEBUG("DungeonDebug", "Objects: %zu, Sprites: %zu",
744 room.GetTileObjects().size(), room.GetSprites().size());
745 LOG_DEBUG("DungeonDebug", "BG1: %dx%d, BG2: %dx%d",
746 room.bg1_buffer().bitmap().width(),
747 room.bg1_buffer().bitmap().height(),
748 room.bg2_buffer().bitmap().width(),
749 room.bg2_buffer().bitmap().height());
750 }));
751
752 return debug_menu;
753}
754
755} // namespace yaze::editor
bool is_loaded() const
Definition rom.h:155
std::pair< int, int > ScreenToRoomPixelCoordinates(ImVec2 screen) const
std::optional< zelda3::RoomObject > GetObjectUnderContextCursor(int room_id)
gui::CanvasMenuItem BuildLayerVisibilityContextMenu(int room_id)
gui::CanvasMenuItem BuildRoomContextMenu(int room_id)
gui::CanvasMenuItem BuildDebugContextMenu(int room_id)
std::vector< gui::CanvasMenuItem > BuildSelectionContextMenuItems(int room_id)
std::function< void()> show_door_editor_callback_
std::function< void(int, const zelda3::RoomObject &) edit_object_tiles_callback_)
std::function< void(int, const zelda3::RoomObject &) edit_graphics_callback_)
std::function< void()> show_entrance_list_callback_
DungeonObjectInteraction object_interaction_
void OpenIssueReportPopup(const std::string &title, const std::string &summary, const std::string &kind_label, const std::string &diagnostics, int room_id, int default_category_index)
std::string BuildRoomMetadataSummary(const zelda3::Room &room, int room_id) const
gui::CanvasMenuItem BuildReportContextMenu(int room_id)
void SetPreviewObject(const zelda3::RoomObject &object)
std::function< void()> show_object_panel_callback_
zelda3::RoomLayerManager & GetRoomLayerManager(int room_id)
std::string BuildDrawIssueReport(const zelda3::Room &room, int room_id) const
std::function< void()> show_room_matrix_callback_
std::function< void()> show_room_graphics_callback_
std::function< void()> show_item_panel_callback_
std::function< void()> show_sprite_panel_callback_
std::optional< gui::CanvasMenuItem > BuildOpenContextMenu()
MinecartTrackEditorPanel * minecart_track_panel_
std::string BuildSelectionIssueReport(const zelda3::Room &room, int room_id) const
gui::CanvasMenuItem BuildCopyExportContextMenu(int room_id)
std::function< void(int)> save_room_callback_
std::vector< size_t > GetSelectedObjectIndices() const
InteractionCoordinator & entity_coordinator()
Get the interaction coordinator for entity handling.
void SetDoorPlacementMode(bool enabled, zelda3::DoorType type=zelda3::DoorType::NormalDoor)
std::optional< size_t > GetEntityAtPosition(int canvas_x, int canvas_y) const override
Get entity at canvas position.
auto global_scale() const
Definition canvas.h:399
void ClearContextMenuItems()
Definition canvas.cc:874
void AddContextMenuItem(const gui::CanvasMenuItem &item)
Definition canvas.cc:851
auto zero_point() const
Definition canvas.h:350
auto scrolling() const
Definition canvas.h:352
static bool IsEditableStandardObject(int16_t object_id)
bool IsLayerVisible(LayerType layer) const
#define ICON_MD_GRID_VIEW
Definition icons.h:897
#define ICON_MD_COLORIZE
Definition icons.h:441
#define ICON_MD_MY_LOCATION
Definition icons.h:1270
#define ICON_MD_CONTENT_CUT
Definition icons.h:466
#define ICON_MD_INFO
Definition icons.h:993
#define ICON_MD_CANCEL
Definition icons.h:364
#define ICON_MD_TRAIN
Definition icons.h:2005
#define ICON_MD_FILE_DOWNLOAD
Definition icons.h:744
#define ICON_MD_SAVE_AS
Definition icons.h:1646
#define ICON_MD_REFRESH
Definition icons.h:1572
#define ICON_MD_FLIP_TO_FRONT
Definition icons.h:802
#define ICON_MD_ARROW_DOWNWARD
Definition icons.h:180
#define ICON_MD_VISIBILITY
Definition icons.h:2101
#define ICON_MD_BUG_REPORT
Definition icons.h:327
#define ICON_MD_WIDGETS
Definition icons.h:2156
#define ICON_MD_MORE_HORIZ
Definition icons.h:1241
#define ICON_MD_LOGIN
Definition icons.h:1146
#define ICON_MD_CONTENT_PASTE
Definition icons.h:467
#define ICON_MD_HOME
Definition icons.h:953
#define ICON_MD_GRID_ON
Definition icons.h:896
#define ICON_MD_FACT_CHECK
Definition icons.h:721
#define ICON_MD_LAYERS
Definition icons.h:1068
#define ICON_MD_INVENTORY
Definition icons.h:1011
#define ICON_MD_DOOR_FRONT
Definition icons.h:613
#define ICON_MD_CROP_SQUARE
Definition icons.h:500
#define ICON_MD_ARROW_UPWARD
Definition icons.h:189
#define ICON_MD_IMAGE
Definition icons.h:982
#define ICON_MD_PERSON
Definition icons.h:1415
#define ICON_MD_ASSIGNMENT
Definition icons.h:194
#define ICON_MD_SAVE
Definition icons.h:1644
#define ICON_MD_DELETE
Definition icons.h:530
#define ICON_MD_PALETTE
Definition icons.h:1370
#define ICON_MD_OPEN_IN_NEW
Definition icons.h:1354
#define ICON_MD_CONTENT_COPY
Definition icons.h:465
#define ICON_MD_DELETE_FOREVER
Definition icons.h:531
#define ICON_MD_PRINT
Definition icons.h:1515
#define ICON_MD_FLIP_TO_BACK
Definition icons.h:801
#define ICON_MD_ADD_CIRCLE
Definition icons.h:95
#define LOG_DEBUG(category, format,...)
Definition log.h:103
bool IsWithinBounds(int canvas_x, int canvas_y, int margin=0)
Check if coordinates are within room bounds.
Editors are the view controllers for the application.
DungeonIssueCategory GetDefaultSelectionIssueCategory(const DungeonObjectInteraction &object_interaction)
constexpr int ToIssueCategoryIndex(DungeonIssueCategory category)
@ NormalDoor
Normal door (upper layer)
std::string GetRoomLabel(int id)
Convenience function to get a room label.
absl::StatusOr< std::string > ExportRoomLayoutTemplate(const Room &room)
Export a room's layout as a JSON template string.
bool UsesRoomObjectStream(const RoomObject &object)
constexpr int kNumberOfRooms
Declarative menu item definition.
Definition canvas_menu.h:64
std::function< bool()> checked_condition
Definition canvas_menu.h:91
std::vector< CanvasMenuItem > subitems
Definition canvas_menu.h:94
std::function< bool()> enabled_condition
Definition canvas_menu.h:81