yaze 0.3.2
Link to the Past ROM Editor
 
Loading...
Searching...
No Matches
object_editor_panel.cc
Go to the documentation of this file.
1// Related header
3#include <algorithm>
4#include <cstddef>
5#include <cstdint>
6#include <memory>
7#include <vector>
8
9// Third-party library headers
10#include "absl/strings/str_format.h"
13#include "imgui/imgui.h"
14
15// Project headers
19#include "app/gui/core/icons.h"
21#include "rom/rom.h"
27
28namespace yaze {
29namespace editor {
30
32 gfx::IRenderer* renderer, Rom* rom, DungeonCanvasViewer* canvas_viewer,
33 std::shared_ptr<zelda3::DungeonObjectEditor> object_editor)
34 : renderer_(renderer),
35 rom_(rom),
36 canvas_viewer_(canvas_viewer),
37 object_selector_(rom),
38 object_editor_(object_editor) {
39 emulator_preview_.Initialize(renderer, rom);
40
41 // Initialize object parser for static editor info lookup
42 if (rom) {
43 object_parser_ = std::make_unique<zelda3::ObjectParser>(rom);
44 }
45
46 // Wire up object selector callback
48 [this](const zelda3::RoomObject& obj) {
49 preview_object_ = obj;
51 if (canvas_viewer_) {
54 }
55
56 // Sync with backend editor if available
57 if (object_editor_) {
59 object_editor_->SetCurrentObjectType(obj.id_);
60 }
61 });
62
63 // Wire up double-click callback for static object editor
65 [this](int obj_id) { OpenStaticObjectEditor(obj_id); });
66
67 // Wire up selection change callback for property panel sync
69}
70
80
82 if (!canvas_viewer_)
83 return;
84
85 auto& interaction = canvas_viewer_->object_interaction();
87
88 // Sync with backend editor if available
89 if (object_editor_) {
90 auto indices = interaction.GetSelectedObjectIndices();
91 // Clear backend selection first
92 (void)object_editor_->ClearSelection();
93
94 // Add each selected index to backend
95 for (size_t idx : indices) {
96 (void)object_editor_->AddToSelection(idx);
97 }
98 }
99}
100
101void ObjectEditorPanel::Draw(bool* p_open) {
102 const auto& theme = AgentUI::GetTheme();
103
104 // Door Section (Collapsible)
105 if (ImGui::CollapsingHeader(ICON_MD_DOOR_FRONT " Doors", ImGuiTreeNodeFlags_DefaultOpen)) {
107 }
108
109 ImGui::Separator();
110
111 // Object Browser - takes up available space
112 float available_height = ImGui::GetContentRegionAvail().y;
113 // Reserve space for status indicator at bottom
114 float reserved_height = 60.0f;
115 // Reduce browser height when static editor is open to give it more space
117 reserved_height += 200.0f;
118 }
119 float browser_height = std::max(150.0f, available_height - reserved_height);
120
121 ImGui::BeginChild("ObjectBrowserRegion", ImVec2(0, browser_height), true);
123 ImGui::EndChild();
124
125 ImGui::Separator();
126
127 // Static Object Editor (if open)
130 ImGui::Separator();
131 }
132
133 // Status indicator: show current interaction state
134 {
135 bool is_placing = has_preview_object_ && canvas_viewer_ &&
137 if (!is_placing && has_preview_object_) {
138 has_preview_object_ = false;
139 }
140 if (is_placing) {
141 ImGui::TextColored(theme.status_warning,
142 ICON_MD_ADD_CIRCLE " Placing: Object 0x%02X",
144 ImGui::SameLine();
145 if (ImGui::SmallButton(ICON_MD_CANCEL " Cancel")) {
147 }
148 } else {
149 ImGui::TextColored(
150 theme.text_secondary_gray, ICON_MD_MOUSE
151 " Selection Mode - Click to select, drag to multi-select");
152 ImGui::TextColored(theme.text_secondary_gray, ICON_MD_MENU
153 " Right-click the canvas for Cut/Copy/Paste options");
154 }
155 }
156
157 // Current object info
159
160 ImGui::Separator();
161
162 // Emulator Preview (Collapsible)
163 bool preview_open = ImGui::CollapsingHeader(ICON_MD_MONITOR " Preview");
164 show_emulator_preview_ = preview_open;
165
166 if (preview_open) {
167 ImGui::PushID("PreviewSection");
169 ImGui::PopID();
170 }
171
172 // Handle keyboard shortcuts
174}
175
179
181 // In agent mode, we might force tabs open or change layout
182 (void)enabled;
183}
184
186 // Delegate to the DungeonObjectSelector component
188}
189
191 const auto& theme = AgentUI::GetTheme();
192
193 // Common door types for the grid
194 static constexpr std::array<zelda3::DoorType, 20> kDoorTypes = {{
215 }};
216
217 // Placement mode indicator
219 ImGui::TextColored(theme.status_warning,
220 ICON_MD_PLACE " Placing: %s - Click wall to place",
221 std::string(zelda3::GetDoorTypeName(selected_door_type_)).c_str());
222 if (ImGui::SmallButton(ICON_MD_CANCEL " Cancel")) {
223 door_placement_mode_ = false;
224 if (canvas_viewer_) {
227 }
228 }
229 ImGui::Separator();
230 }
231
232 // Door type selector grid with preview thumbnails
233 ImGui::Text(ICON_MD_CATEGORY " Select Door Type:");
234
235 constexpr float kPreviewSize = 32.0f;
236 constexpr int kItemsPerRow = 5;
237 float panel_width = ImGui::GetContentRegionAvail().x;
238 int items_per_row = std::max(1, static_cast<int>(panel_width / (kPreviewSize + 8)));
239
240 ImGui::BeginChild("##DoorTypeGrid", ImVec2(0, 80), true, ImGuiWindowFlags_HorizontalScrollbar);
241
242 int col = 0;
243 for (size_t i = 0; i < kDoorTypes.size(); ++i) {
244 auto door_type = kDoorTypes[i];
245 bool is_selected = (selected_door_type_ == door_type);
246
247 ImGui::PushID(static_cast<int>(i));
248
249 // Color-coded button for door type
250 ImVec4 button_color;
251 // Color-code by door category
252 int type_val = static_cast<int>(door_type);
253 if (type_val <= 0x12) { // Standard doors
254 button_color = ImVec4(0.3f, 0.5f, 0.7f, 1.0f); // Blue
255 } else if (type_val <= 0x1E) { // Shutter/special
256 button_color = ImVec4(0.7f, 0.5f, 0.3f, 1.0f); // Orange
257 } else { // Markers
258 button_color = ImVec4(0.5f, 0.7f, 0.3f, 1.0f); // Green
259 }
260
261 if (is_selected) {
262 button_color.x += 0.2f;
263 button_color.y += 0.2f;
264 button_color.z += 0.2f;
265 }
266
267 ImGui::PushStyleColor(ImGuiCol_Button, button_color);
268 ImGui::PushStyleColor(ImGuiCol_ButtonHovered,
269 ImVec4(button_color.x + 0.1f, button_color.y + 0.1f, button_color.z + 0.1f, 1.0f));
270 ImGui::PushStyleColor(ImGuiCol_ButtonActive,
271 ImVec4(button_color.x + 0.2f, button_color.y + 0.2f, button_color.z + 0.2f, 1.0f));
272
273 // Draw button with door type abbreviation
274 std::string label = absl::StrFormat("%02X", type_val);
275 if (ImGui::Button(label.c_str(), ImVec2(kPreviewSize, kPreviewSize))) {
276 selected_door_type_ = door_type;
278 if (canvas_viewer_) {
281 }
282 }
283
284 ImGui::PopStyleColor(3);
285
286 // Tooltip with full name
287 if (ImGui::IsItemHovered()) {
288 ImGui::SetTooltip("%s (0x%02X)\nClick to select for placement",
289 std::string(zelda3::GetDoorTypeName(door_type)).c_str(), type_val);
290 }
291
292 // Selection highlight
293 if (is_selected) {
294 ImVec2 min = ImGui::GetItemRectMin();
295 ImVec2 max = ImGui::GetItemRectMax();
296 ImGui::GetWindowDrawList()->AddRect(
297 min, max, IM_COL32(255, 255, 0, 255), 0.0f, 0, 2.0f);
298 }
299
300 ImGui::PopID();
301
302 col++;
303 if (col < items_per_row && i < kDoorTypes.size() - 1) {
304 ImGui::SameLine();
305 } else {
306 col = 0;
307 }
308 }
309
310 ImGui::EndChild();
311
312 // Show current room's doors
313 auto* rooms = object_selector_.get_rooms();
314 if (rooms && current_room_id_ >= 0 && current_room_id_ < 296) {
315 const auto& room = (*rooms)[current_room_id_];
316 const auto& doors = room.GetDoors();
317
318 if (!doors.empty()) {
319 ImGui::Text(ICON_MD_LIST " Room Doors (%zu):", doors.size());
320
321 ImGui::BeginChild("##DoorList", ImVec2(0, 80), true);
322 for (size_t i = 0; i < doors.size(); ++i) {
323 const auto& door = doors[i];
324 auto [tile_x, tile_y] = door.GetTileCoords();
325
326 ImGui::PushID(static_cast<int>(i));
327
328 std::string type_name(zelda3::GetDoorTypeName(door.type));
329 std::string dir_name(zelda3::GetDoorDirectionName(door.direction));
330
331 ImGui::Text("[%zu] %s (%s)", i, type_name.c_str(), dir_name.c_str());
332 ImGui::SameLine();
333 ImGui::TextColored(theme.text_secondary_gray, "@ (%d,%d)", tile_x, tile_y);
334
335 ImGui::SameLine();
336 if (ImGui::SmallButton(ICON_MD_DELETE "##Del")) {
337 auto& mutable_room = (*rooms)[current_room_id_];
338 mutable_room.RemoveDoor(i);
339 }
340
341 ImGui::PopID();
342 }
343 ImGui::EndChild();
344 } else {
345 ImGui::TextColored(theme.text_secondary_gray,
346 ICON_MD_INFO " No doors in this room");
347 }
348 }
349}
350
352 const auto& theme = AgentUI::GetTheme();
353
354 ImGui::TextColored(theme.text_secondary_gray,
355 ICON_MD_INFO " Real-time object rendering preview");
357 "Uses SNES emulation to render objects accurately.\n"
358 "May impact performance.");
359
360 ImGui::Separator();
361
362 ImGui::BeginChild("##EmulatorPreviewRegion", ImVec2(0, 260), true);
364 ImGui::EndChild();
365}
366
368 const auto& theme = AgentUI::GetTheme();
369
370 // Show selection state at top - with extra safety checks
372 auto& interaction = canvas_viewer_->object_interaction();
373 auto selected = interaction.GetSelectedObjectIndices();
374
375 if (!selected.empty()) {
376 ImGui::TextColored(theme.status_success,
377 ICON_MD_CHECK_CIRCLE " Selected:");
378 ImGui::SameLine();
379
380 if (selected.size() == 1) {
381 if (object_editor_) {
382 const auto& objects = object_editor_->GetObjects();
383 if (selected[0] < objects.size()) {
384 const auto& obj = objects[selected[0]];
385 ImGui::Text("Object #%zu (ID: 0x%02X)", selected[0], obj.id_);
386 ImGui::TextColored(theme.text_secondary_gray,
387 " Position: (%d, %d) Size: 0x%02X Layer: %s",
388 obj.x_, obj.y_, obj.size_,
389 obj.layer_ == zelda3::RoomObject::BG1 ? "BG1"
390 : obj.layer_ == zelda3::RoomObject::BG2 ? "BG2"
391 : "BG3");
392 }
393 } else {
394 ImGui::Text("1 object");
395 }
396 } else {
397 ImGui::Text("%zu objects", selected.size());
398 ImGui::SameLine();
399 ImGui::TextColored(theme.text_secondary_gray,
400 "(Shift+click to add, Ctrl+click to toggle)");
401 }
402 ImGui::Separator();
403 }
404 }
405
406 ImGui::BeginGroup();
407
408 ImGui::TextColored(theme.text_info, ICON_MD_INFO " Current:");
409
411 ImGui::SameLine();
412 ImGui::Text("ID: 0x%02X", preview_object_.id_);
413 ImGui::SameLine();
414 ImGui::Text("Layer: %s",
417 : "BG3");
418 }
419
420 ImGui::EndGroup();
421 ImGui::Separator();
422
423 // Delegate property editing to the backend
424 if (object_editor_) {
425 object_editor_->DrawPropertyUI();
426 }
427}
428
429// =============================================================================
430// Static Object Editor (opened via double-click)
431// =============================================================================
432
434 static_editor_open_ = true;
435 static_editor_object_id_ = object_id;
437
438 // Sync with object selector for visual indicator
440
441 // Fetch draw routine info for this object
442 if (object_parser_) {
443 static_editor_draw_info_ = object_parser_->GetObjectDrawInfo(object_id);
444 }
445
446 // Render the object preview using ObjectDrawer
447 auto* rooms = object_selector_.get_rooms();
448 if (rom_ && rom_->is_loaded() && rooms && current_room_id_ >= 0 &&
449 current_room_id_ < static_cast<int>(rooms->size())) {
450 auto& room = (*rooms)[current_room_id_];
451
452 // Ensure room graphics are loaded
453 if (!room.IsLoaded()) {
454 room.LoadRoomGraphics(room.blockset);
455 }
456
457 // Clear preview buffer and initialize bitmap
460
461 // Create a preview object at top-left of canvas (tile 2,2 = pixel 16,16)
462 // to fit within the 128x128 preview area with some margin
463 zelda3::RoomObject preview_obj(object_id, 2, 2, 0x12, 0);
464 preview_obj.SetRom(rom_);
465 preview_obj.EnsureTilesLoaded();
466
467 if (preview_obj.tiles().empty()) {
468 return; // No tiles to draw
469 }
470
471 // Get room graphics data
472 const uint8_t* gfx_data = room.get_gfx_buffer().data();
473
474 // Apply palette to bitmap
475 auto& bitmap = static_preview_buffer_.bitmap();
476 gfx::PaletteGroup palette_group;
477 auto* game_data = object_selector_.game_data();
478 if (game_data && !game_data->palette_groups.dungeon_main.empty()) {
479 // Use the entire dungeon_main palette group
480 palette_group = game_data->palette_groups.dungeon_main;
481
482 std::vector<SDL_Color> colors(256);
483 size_t color_index = 0;
484 for (size_t pal_idx = 0; pal_idx < palette_group.size() && color_index < 256; ++pal_idx) {
485 const auto& pal = palette_group[pal_idx];
486 for (size_t i = 0; i < pal.size() && color_index < 256; ++i) {
487 ImVec4 rgb = pal[i].rgb();
488 colors[color_index++] = {
489 static_cast<Uint8>(rgb.x),
490 static_cast<Uint8>(rgb.y),
491 static_cast<Uint8>(rgb.z),
492 255
493 };
494 }
495 }
496 colors[255] = {0, 0, 0, 0}; // Transparent
497 bitmap.SetPalette(colors);
498 if (bitmap.surface()) {
499 SDL_SetColorKey(bitmap.surface(), SDL_TRUE, 255);
500 SDL_SetSurfaceBlendMode(bitmap.surface(), SDL_BLENDMODE_BLEND);
501 }
502 }
503
504 // Create drawer with room's graphics data
505 zelda3::ObjectDrawer drawer(rom_, current_room_id_, gfx_data);
506 drawer.InitializeDrawRoutines();
507
508 auto status = drawer.DrawObject(preview_obj, static_preview_buffer_,
509 static_preview_buffer_, palette_group);
510 if (status.ok()) {
511 // Sync bitmap data to SDL surface
512 if (bitmap.modified() && bitmap.surface() && bitmap.mutable_data().size() > 0) {
513 SDL_LockSurface(bitmap.surface());
514 size_t surface_size = bitmap.surface()->h * bitmap.surface()->pitch;
515 size_t data_size = bitmap.mutable_data().size();
516 if (surface_size >= data_size) {
517 memcpy(bitmap.surface()->pixels, bitmap.mutable_data().data(), data_size);
518 }
519 SDL_UnlockSurface(bitmap.surface());
520 }
521
522 // Create texture
526
527 static_preview_rendered_ = bitmap.texture() != nullptr;
528 }
529 }
530}
531
533 static_editor_open_ = false;
535
536 // Clear the visual indicator in object selector
538}
539
541 const auto& theme = AgentUI::GetTheme();
542
543 ImGui::PushStyleColor(
544 ImGuiCol_Header, ImVec4(0.15f, 0.25f, 0.35f, 1.0f)); // Slate blue header
545 ImGui::PushStyleColor(ImGuiCol_HeaderHovered,
546 ImVec4(0.20f, 0.30f, 0.40f, 1.0f));
547
548 bool header_open = ImGui::CollapsingHeader(
549 absl::StrFormat(ICON_MD_CONSTRUCTION " Object 0x%02X - %s",
552 .c_str(),
553 ImGuiTreeNodeFlags_DefaultOpen);
554
555 ImGui::PopStyleColor(2);
556
557 if (header_open) {
558 ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(8, 6));
559
560 // Two-column layout: Info | Preview
561 if (ImGui::BeginTable("StaticEditorLayout", 2,
562 ImGuiTableFlags_BordersInnerV)) {
563 ImGui::TableSetupColumn("Info", ImGuiTableColumnFlags_WidthFixed, 200);
564 ImGui::TableSetupColumn("Preview", ImGuiTableColumnFlags_WidthStretch);
565
566 ImGui::TableNextRow();
567
568 // Left column: Object information
569 ImGui::TableNextColumn();
570 {
571 // Object ID with hex/decimal display
572 ImGui::TextColored(theme.text_info, ICON_MD_TAG " Object ID");
573 ImGui::SameLine();
574 ImGui::Text("0x%02X (%d)", static_editor_object_id_,
576
577 ImGui::Spacing();
578
579 // Draw routine info
580 ImGui::TextColored(theme.text_info, ICON_MD_BRUSH " Draw Routine");
581 ImGui::Indent();
582 ImGui::Text("ID: %d", static_editor_draw_info_.draw_routine_id);
583 ImGui::Text("Name: %s", static_editor_draw_info_.routine_name.c_str());
584 ImGui::Unindent();
585
586 ImGui::Spacing();
587
588 // Tile and size info
589 ImGui::TextColored(theme.text_info, ICON_MD_GRID_VIEW " Tile Info");
590 ImGui::Indent();
591 ImGui::Text("Tile Count: %d", static_editor_draw_info_.tile_count);
592 ImGui::Text("Orientation: %s",
595 : "Both");
597 ImGui::TextColored(theme.status_warning, ICON_MD_LAYERS " Both BG");
598 }
599 ImGui::Unindent();
600
601 ImGui::Spacing();
602 ImGui::Separator();
603 ImGui::Spacing();
604
605 // Action buttons (vertical layout)
606 if (ImGui::Button(ICON_MD_CONTENT_COPY " Copy ID", ImVec2(-1, 0))) {
607 ImGui::SetClipboardText(
608 absl::StrFormat("0x%02X", static_editor_object_id_).c_str());
609 }
610 if (ImGui::IsItemHovered()) {
611 ImGui::SetTooltip("Copy object ID to clipboard");
612 }
613
614 if (ImGui::Button(ICON_MD_CODE " Export ASM", ImVec2(-1, 0))) {
615 // TODO: Implement ASM export (Phase 5)
616 }
617 if (ImGui::IsItemHovered()) {
618 ImGui::SetTooltip("Export object draw routine as ASM (Phase 5)");
619 }
620
621 ImGui::Spacing();
622
623 // Close button at bottom
624 ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.5f, 0.2f, 0.2f, 1.0f));
625 ImGui::PushStyleColor(ImGuiCol_ButtonHovered,
626 ImVec4(0.6f, 0.3f, 0.3f, 1.0f));
627 if (ImGui::Button(ICON_MD_CLOSE " Close", ImVec2(-1, 0))) {
629 }
630 ImGui::PopStyleColor(2);
631 }
632
633 // Right column: Preview canvas
634 ImGui::TableNextColumn();
635 {
636 ImGui::TextColored(theme.text_secondary_gray, "Preview:");
637
638 gui::PreviewPanelOpts preview_opts;
639 preview_opts.canvas_size = ImVec2(128, 128);
640 preview_opts.render_popups = false;
641 preview_opts.grid_step = 0.0f;
642 preview_opts.ensure_texture = true;
643
644 gui::CanvasFrameOptions frame_opts;
645 frame_opts.canvas_size = preview_opts.canvas_size;
646 frame_opts.draw_context_menu = false;
647 frame_opts.draw_grid = preview_opts.grid_step > 0.0f;
648 if (preview_opts.grid_step > 0.0f) {
649 frame_opts.grid_step = preview_opts.grid_step;
650 }
651 frame_opts.draw_overlay = true;
652 frame_opts.render_popups = preview_opts.render_popups;
653
654 auto rt = gui::BeginCanvas(static_preview_canvas_, frame_opts);
655
657 auto& bitmap = static_preview_buffer_.bitmap();
658 gui::RenderPreviewPanel(rt, bitmap, preview_opts);
659 } else {
661 preview_opts);
663 ImVec2(24, 56), "No preview available",
664 ImGui::GetColorU32(theme.text_secondary_gray));
665 }
666 gui::EndCanvas(static_preview_canvas_, rt, frame_opts);
667
668 // Usage hint
669 ImGui::Spacing();
670 ImGui::TextColored(theme.text_secondary_gray, ICON_MD_INFO
671 " Double-click objects in browser\n"
672 "to view their draw routine info.");
673 }
674
675 ImGui::EndTable();
676 }
677
678 ImGui::PopStyleVar();
679 }
680}
681
682// =============================================================================
683// Keyboard Shortcuts
684// =============================================================================
685
687 if (!ImGui::IsWindowFocused(ImGuiFocusedFlags_RootAndChildWindows)) {
688 return;
689 }
690
691 const ImGuiIO& io = ImGui::GetIO();
692
693 // Ctrl+A: Select all objects
694 if (ImGui::IsKeyPressed(ImGuiKey_A) && io.KeyCtrl && !io.KeyShift) {
696 }
697
698 // Ctrl+Shift+A: Deselect all
699 if (ImGui::IsKeyPressed(ImGuiKey_A) && io.KeyCtrl && io.KeyShift) {
701 }
702
703 // Delete: Remove selected objects
704 if (ImGui::IsKeyPressed(ImGuiKey_Delete)) {
706 }
707
708 // Ctrl+D: Duplicate selected objects
709 if (ImGui::IsKeyPressed(ImGuiKey_D) && io.KeyCtrl) {
711 }
712
713 // Ctrl+C: Copy selected objects
714 if (ImGui::IsKeyPressed(ImGuiKey_C) && io.KeyCtrl) {
716 }
717
718 // Ctrl+V: Paste objects
719 if (ImGui::IsKeyPressed(ImGuiKey_V) && io.KeyCtrl) {
720 PasteObjects();
721 }
722
723 // Ctrl+Z: Undo
724 if (ImGui::IsKeyPressed(ImGuiKey_Z) && io.KeyCtrl && !io.KeyShift) {
725 if (object_editor_) {
726 object_editor_->Undo();
727 }
728 }
729
730 // Ctrl+Shift+Z or Ctrl+Y: Redo
731 if ((ImGui::IsKeyPressed(ImGuiKey_Z) && io.KeyCtrl && io.KeyShift) ||
732 (ImGui::IsKeyPressed(ImGuiKey_Y) && io.KeyCtrl)) {
733 if (object_editor_) {
734 object_editor_->Redo();
735 }
736 }
737
738 // G: Toggle grid
739 if (ImGui::IsKeyPressed(ImGuiKey_G) && !io.KeyCtrl) {
741 }
742
743 // I: Toggle object ID labels
744 if (ImGui::IsKeyPressed(ImGuiKey_I) && !io.KeyCtrl) {
746 }
747
748 // Arrow keys: Nudge selected objects
749 if (!io.KeyCtrl) {
750 int dx = 0, dy = 0;
751 if (ImGui::IsKeyPressed(ImGuiKey_LeftArrow))
752 dx = -1;
753 if (ImGui::IsKeyPressed(ImGuiKey_RightArrow))
754 dx = 1;
755 if (ImGui::IsKeyPressed(ImGuiKey_UpArrow))
756 dy = -1;
757 if (ImGui::IsKeyPressed(ImGuiKey_DownArrow))
758 dy = 1;
759
760 if (dx != 0 || dy != 0) {
761 NudgeSelectedObjects(dx, dy);
762 }
763 }
764
765 // Tab: Cycle through objects
766 if (ImGui::IsKeyPressed(ImGuiKey_Tab) && !io.KeyCtrl) {
767 CycleObjectSelection(io.KeyShift ? -1 : 1);
768 }
769
770 // Escape: Cancel placement or deselect all
771 if (ImGui::IsKeyPressed(ImGuiKey_Escape)) {
775 } else {
777 }
778 }
779}
780
788
791 return;
792
793 auto& interaction = canvas_viewer_->object_interaction();
794 const auto& objects = object_editor_->GetObjects();
795 std::vector<size_t> all_indices;
796
797 for (size_t i = 0; i < objects.size(); ++i) {
798 all_indices.push_back(i);
799 }
800
801 interaction.SetSelectedObjects(all_indices);
802}
803
809
812 return;
813
814 auto& interaction = canvas_viewer_->object_interaction();
815 const auto& selected = interaction.GetSelectedObjectIndices();
816
817 if (selected.empty())
818 return;
819
820 // Show confirmation for bulk delete (more than 5 objects)
821 if (selected.size() > 5) {
823 return;
824 }
825
827}
828
831 return;
832
833 auto& interaction = canvas_viewer_->object_interaction();
834 const auto& selected = interaction.GetSelectedObjectIndices();
835
836 if (selected.empty())
837 return;
838
839 // Delete in reverse order to maintain indices
840 std::vector<size_t> sorted_indices(selected.begin(), selected.end());
841 std::sort(sorted_indices.rbegin(), sorted_indices.rend());
842
843 for (size_t idx : sorted_indices) {
844 object_editor_->DeleteObject(idx);
845 }
846
847 interaction.ClearSelection();
848}
849
852 return;
853
854 auto& interaction = canvas_viewer_->object_interaction();
855 const auto& selected = interaction.GetSelectedObjectIndices();
856
857 if (selected.empty())
858 return;
859
860 std::vector<size_t> new_indices;
861
862 for (size_t idx : selected) {
863 auto new_idx = object_editor_->DuplicateObject(idx, 1, 1);
864 if (new_idx.has_value()) {
865 new_indices.push_back(*new_idx);
866 }
867 }
868
869 interaction.SetSelectedObjects(new_indices);
870}
871
874 return;
875
876 auto& interaction = canvas_viewer_->object_interaction();
877 const auto& selected = interaction.GetSelectedObjectIndices();
878
879 if (selected.empty())
880 return;
881
882 object_editor_->CopySelectedObjects(selected);
883}
884
887 return;
888
889 auto new_indices = object_editor_->PasteObjects();
890
891 if (!new_indices.empty()) {
893 }
894}
895
898 return;
899
900 auto& interaction = canvas_viewer_->object_interaction();
901 const auto& selected = interaction.GetSelectedObjectIndices();
902
903 if (selected.empty())
904 return;
905
906 for (size_t idx : selected) {
907 object_editor_->MoveObject(idx, dx, dy);
908 }
909}
910
913 return;
914
915 auto& interaction = canvas_viewer_->object_interaction();
916 const auto& selected = interaction.GetSelectedObjectIndices();
917 const auto& objects = object_editor_->GetObjects();
918
919 size_t total_objects = objects.size();
920 if (total_objects == 0)
921 return;
922
923 size_t current_idx = selected.empty() ? 0 : selected.front();
924 size_t next_idx = (current_idx + direction + total_objects) % total_objects;
925
926 interaction.SetSelectedObjects({next_idx});
927}
928
931 return;
932
933 const auto& objects = object_editor_->GetObjects();
934 if (index >= objects.size())
935 return;
936
937 // TODO: Implement ScrollTo in DungeonCanvasViewer
938 (void)objects;
939}
940
941} // namespace editor
942} // namespace yaze
The Rom class is used to load, save, and modify Rom data. This is a generic SNES ROM container and do...
Definition rom.h:24
bool is_loaded() const
Definition rom.h:128
DungeonObjectInteraction & object_interaction()
void SetPreviewObject(const zelda3::RoomObject &object)
void SetSelectedObjects(const std::vector< size_t > &indices)
std::vector< size_t > GetSelectedObjectIndices() const
void SetSelectionChangeCallback(std::function< void()> callback)
void SetDoorPlacementMode(bool enabled, zelda3::DoorType type=zelda3::DoorType::NormalDoor)
void SetObjectDoubleClickCallback(std::function< void(int)> callback)
std::array< zelda3::Room, 0x128 > * get_rooms()
void SetObjectSelectedCallback(std::function< void(const zelda3::RoomObject &)> callback)
ObjectEditorPanel(gfx::IRenderer *renderer, Rom *rom, DungeonCanvasViewer *canvas_viewer, std::shared_ptr< zelda3::DungeonObjectEditor > object_editor=nullptr)
std::unique_ptr< zelda3::ObjectParser > object_parser_
gui::DungeonObjectEmulatorPreview emulator_preview_
gfx::BackgroundBuffer static_preview_buffer_
void Draw(bool *p_open) override
Draw the panel content.
std::shared_ptr< zelda3::DungeonObjectEditor > object_editor_
zelda3::ObjectDrawInfo static_editor_draw_info_
void QueueTextureCommand(TextureCommandType type, Bitmap *bitmap)
Definition arena.cc:34
void ProcessTextureQueue(IRenderer *renderer)
Definition arena.cc:110
static Arena & Get()
Definition arena.cc:19
Defines an abstract interface for all rendering operations.
Definition irenderer.h:40
void AddTextAt(ImVec2 local_pos, const std::string &text, uint32_t color)
Definition canvas.cc:2425
void Initialize(gfx::IRenderer *renderer, Rom *rom, zelda3::GameData *game_data=nullptr, emu::render::EmulatorRenderService *render_service=nullptr)
Draws dungeon objects to background buffers using game patterns.
void InitializeDrawRoutines()
Initialize draw routine registry Must be called before drawing objects.
absl::Status DrawObject(const RoomObject &object, gfx::BackgroundBuffer &bg1, gfx::BackgroundBuffer &bg2, const gfx::PaletteGroup &palette_group, const DungeonState *state=nullptr, gfx::BackgroundBuffer *layout_bg1=nullptr)
Draw a room object to background buffers.
const std::vector< gfx::TileInfo > & tiles() const
Definition room_object.h:88
void SetRom(Rom *rom)
Definition room_object.h:68
#define ICON_MD_GRID_VIEW
Definition icons.h:897
#define ICON_MD_MONITOR
Definition icons.h:1233
#define ICON_MD_INFO
Definition icons.h:993
#define ICON_MD_CANCEL
Definition icons.h:364
#define ICON_MD_PLACE
Definition icons.h:1477
#define ICON_MD_CONSTRUCTION
Definition icons.h:458
#define ICON_MD_BRUSH
Definition icons.h:325
#define ICON_MD_CODE
Definition icons.h:434
#define ICON_MD_LIST
Definition icons.h:1094
#define ICON_MD_LAYERS
Definition icons.h:1068
#define ICON_MD_DOOR_FRONT
Definition icons.h:613
#define ICON_MD_CHECK_CIRCLE
Definition icons.h:400
#define ICON_MD_MOUSE
Definition icons.h:1251
#define ICON_MD_DELETE
Definition icons.h:530
#define ICON_MD_MENU
Definition icons.h:1196
#define ICON_MD_CONTENT_COPY
Definition icons.h:465
#define ICON_MD_CLOSE
Definition icons.h:418
#define ICON_MD_CATEGORY
Definition icons.h:382
#define ICON_MD_TAG
Definition icons.h:1940
#define ICON_MD_ADD_CIRCLE
Definition icons.h:95
const AgentUITheme & GetTheme()
void EndCanvas(Canvas &canvas)
Definition canvas.cc:1509
void BeginCanvas(Canvas &canvas, ImVec2 child_size)
Definition canvas.cc:1486
void HelpMarker(const char *desc)
bool RenderPreviewPanel(const CanvasRuntime &rt, gfx::Bitmap &bmp, const PreviewPanelOpts &opts)
Definition canvas.cc:2156
@ FancyDungeonExit
Fancy dungeon exit.
@ SmallKeyDoor
Small key door.
@ SmallKeyStairsDown
Small key stairs (downwards)
@ SmallKeyStairsUp
Small key stairs (upwards)
@ DungeonSwapMarker
Dungeon swap marker.
@ NormalDoor
Normal door (upper layer)
@ BombableDoor
Bombable door.
@ LayerSwapMarker
Layer swap marker.
@ ExplodingWall
Exploding wall.
@ TopSidedShutter
Top-sided shutter door.
@ NormalDoorLower
Normal door (lower layer)
@ BottomSidedShutter
Bottom-sided shutter door.
@ CurtainDoor
Curtain door.
@ WaterfallDoor
Waterfall door.
@ BigKeyDoor
Big key door.
@ EyeWatchDoor
Eye watch door.
@ ExitMarker
Exit marker.
@ DoubleSidedShutter
Double sided shutter door.
constexpr std::string_view GetDoorDirectionName(DoorDirection dir)
Get human-readable name for door direction.
Definition door_types.h:161
constexpr std::string_view GetDoorTypeName(DoorType type)
Get human-readable name for door type.
Definition door_types.h:106
Represents a group of palettes.
std::optional< float > grid_step
Definition canvas.h:70