yaze 0.3.2
Link to the Past ROM Editor
 
Loading...
Searching...
No Matches
canvas.cc
Go to the documentation of this file.
1#include "canvas.h"
2
3#include <cmath>
4#include <string>
11#include "imgui/imgui.h"
12
13namespace yaze::gui {
14
15
16// Define constructors and destructor in .cc to avoid incomplete type issues with unique_ptr
17
18// Default constructor
19Canvas::Canvas() : renderer_(nullptr) { InitializeDefaults(); }
20
21// Legacy constructors (renderer is optional for backward compatibility)
22Canvas::Canvas(const std::string& id)
23 : renderer_(nullptr), canvas_id_(id), context_id_(id + "Context") {
25}
26
27Canvas::Canvas(const std::string& id, ImVec2 canvas_size)
28 : renderer_(nullptr), canvas_id_(id), context_id_(id + "Context") {
32}
33
34Canvas::Canvas(const std::string& id, ImVec2 canvas_size, CanvasGridSize grid_size)
35 : renderer_(nullptr), canvas_id_(id), context_id_(id + "Context") {
40}
41
42Canvas::Canvas(const std::string& id, ImVec2 canvas_size, CanvasGridSize grid_size, float global_scale)
43 : renderer_(nullptr), canvas_id_(id), context_id_(id + "Context") {
49}
50
51// New constructors with renderer support (for migration to IRenderer pattern)
52Canvas::Canvas(gfx::IRenderer* renderer) : renderer_(renderer) { InitializeDefaults(); }
53
54Canvas::Canvas(gfx::IRenderer* renderer, const std::string& id)
55 : renderer_(renderer), canvas_id_(id), context_id_(id + "Context") {
57}
58
59Canvas::Canvas(gfx::IRenderer* renderer, const std::string& id, ImVec2 canvas_size)
60 : renderer_(renderer), canvas_id_(id), context_id_(id + "Context") {
64}
65
66Canvas::Canvas(gfx::IRenderer* renderer, const std::string& id, ImVec2 canvas_size, CanvasGridSize grid_size)
67 : renderer_(renderer), canvas_id_(id), context_id_(id + "Context") {
72}
73
74Canvas::Canvas(gfx::IRenderer* renderer, const std::string& id, ImVec2 canvas_size, CanvasGridSize grid_size, float global_scale)
75 : renderer_(renderer), canvas_id_(id), context_id_(id + "Context") {
81}
82
83Canvas::~Canvas() = default;
84
85using ImGui::GetContentRegionAvail;
86using ImGui::GetCursorScreenPos;
87using ImGui::GetIO;
88using ImGui::GetWindowDrawList;
89using ImGui::IsItemActive;
90using ImGui::IsItemHovered;
91using ImGui::IsMouseClicked;
92using ImGui::IsMouseDragging;
93using ImGui::Text;
94
95constexpr uint32_t kRectangleColor = IM_COL32(32, 32, 32, 255);
96constexpr uint32_t kWhiteColor = IM_COL32(255, 255, 255, 255);
97
98constexpr ImGuiButtonFlags kMouseFlags =
99 ImGuiButtonFlags_MouseButtonLeft | ImGuiButtonFlags_MouseButtonRight;
100
101namespace {
102ImVec2 AlignPosToGrid(ImVec2 pos, float scale) {
103 return ImVec2(std::floor(pos.x / scale) * scale,
104 std::floor(pos.y / scale) * scale);
105}
106} // namespace
107
108// Canvas class implementation begins here
109
111 // Initialize configuration with sensible defaults
112 config_.enable_grid = true;
116 config_.is_draggable = false;
117 config_.grid_step = 32.0f;
118 config_.global_scale = 1.0f;
119 config_.canvas_size = ImVec2(0, 0);
122
123 // Initialize selection state
125
126 // Initialize palette editor
127 palette_editor_ = std::make_unique<PaletteEditorWidget>();
128
129 // Initialize interaction handler
131
132 // Initialize enhanced components
134
135 // Initialize legacy compatibility variables to match config
146}
147
149 palette_editor_.reset();
151
152 // Stop performance monitoring before cleanup to prevent segfault
154 performance_integration_->StopMonitoring();
155 }
156
157 // Cleanup enhanced components
158 modals_.reset();
159 context_menu_.reset();
160 usage_tracker_.reset();
162}
163
165 // Initialize modals system
166 modals_ = std::make_unique<CanvasModals>();
167
168 // Initialize context menu system
169 context_menu_ = std::make_unique<CanvasContextMenu>();
170 context_menu_->Initialize(canvas_id_);
171
172 // Initialize usage tracker (optional, controlled by config.enable_metrics)
174 usage_tracker_ = std::make_shared<CanvasUsageTracker>();
175 usage_tracker_->Initialize(canvas_id_);
176 usage_tracker_->StartSession();
177
178 // Initialize performance integration
180 std::make_shared<CanvasPerformanceIntegration>();
183 performance_integration_->StartMonitoring();
184 }
185}
186
188 if (usage_tracker_) {
189 usage_tracker_->SetUsageMode(usage);
190 }
191 if (context_menu_) {
192 context_menu_->SetUsageMode(usage);
193 }
194 config_.usage_mode = usage;
195}
196
197void Canvas::RecordCanvasOperation(const std::string& operation_name,
198 double time_ms) {
199 if (usage_tracker_) {
200 usage_tracker_->RecordOperation(operation_name, time_ms);
201 }
203 performance_integration_->RecordOperation(operation_name, time_ms,
204 usage_mode());
205 }
206}
207
210 performance_integration_->RenderPerformanceUI();
211 }
212}
213
215 if (usage_tracker_) {
216 std::string report = usage_tracker_->ExportUsageReport();
217 // Show report in a modal or window
218 if (modals_) {
219 // Create a simple text display modal
220 ImGui::OpenPopup("Canvas Usage Report");
221 if (ImGui::BeginPopupModal("Canvas Usage Report", nullptr,
222 ImGuiWindowFlags_AlwaysAutoResize)) {
223 ImGui::Text("Canvas Usage Report");
224 ImGui::Separator();
225 ImGui::TextWrapped("%s", report.c_str());
226 ImGui::Separator();
227 if (ImGui::Button("Close")) {
228 ImGui::CloseCurrentPopup();
229 }
230 ImGui::EndPopup();
231 }
232 }
233 }
234}
235
237 rom_ = rom;
238 if (palette_editor_) {
239 palette_editor_->Initialize(rom);
240 }
241}
242
244 if (palette_editor_ && bitmap_) {
245 auto mutable_palette = bitmap_->mutable_palette();
246 palette_editor_->ShowPaletteEditor(*mutable_palette,
247 "Canvas Palette Editor");
248 }
249}
250
252 if (palette_editor_ && bitmap_) {
253 palette_editor_->ShowColorAnalysis(*bitmap_, "Canvas Color Analysis");
254 }
255}
256
257bool Canvas::ApplyROMPalette(int group_index, int palette_index) {
258 if (palette_editor_ && bitmap_) {
259 return palette_editor_->ApplyROMPalette(bitmap_, group_index, palette_index);
260 }
261 return false;
262}
263
264// Size reporting methods for table integration
269
274
275void Canvas::ReserveTableSpace(const std::string& label) {
278}
279
280bool Canvas::BeginTableCanvas(const std::string& label) {
281 if (config_.auto_resize) {
282 ImVec2 preferred_size = GetPreferredSize();
283 CanvasUtils::SetNextCanvasSize(preferred_size, true);
284 }
285
286 // Begin child window that properly reports size to tables
287 std::string child_id = canvas_id_ + "_TableChild";
288 ImVec2 child_size = config_.auto_resize ? ImVec2(0, 0) : config_.canvas_size;
289
290 bool result =
291 ImGui::BeginChild(child_id.c_str(), child_size,
292 true, // Always show border for table integration
293 ImGuiWindowFlags_AlwaysVerticalScrollbar);
294
295 if (!label.empty()) {
296 ImGui::Text("%s", label.c_str());
297 }
298
299 return result;
300}
301
303 ImGui::EndChild();
304}
305
306// Improved interaction detection methods
308 return !points_.empty() && points_.size() >= 2;
309}
310
311bool Canvas::WasClicked(ImGuiMouseButton button) const {
312 return ImGui::IsItemClicked(button) && HasValidSelection();
313}
314
315bool Canvas::WasDoubleClicked(ImGuiMouseButton button) const {
316 return ImGui::IsItemHovered() && ImGui::IsMouseDoubleClicked(button) &&
318}
319
321 if (HasValidSelection()) {
322 return points_[0]; // Return the first point of the selection
323 }
324 return ImVec2(-1, -1); // Invalid position
325}
326
327// ==================== Modern ImGui-Style Interface ====================
328
329void Canvas::Begin(ImVec2 canvas_size) {
330 // Modern ImGui-style begin - combines DrawBackground + DrawContextMenu
333}
334
336 // Modern ImGui-style end - automatically draws grid and overlay
337 if (config_.enable_grid) {
338 DrawGrid();
339 }
340 DrawOverlay();
341
342 // Render any persistent popups from context menu actions
344}
345
346// ==================== Legacy Interface ====================
347
348void Canvas::UpdateColorPainter(gfx::IRenderer* /*renderer*/, gfx::Bitmap& bitmap, const ImVec4& color,
349 const std::function<void()>& event,
350 int tile_size, float scale) {
351 config_.global_scale = scale;
352 global_scale_ = scale; // Legacy compatibility
355 DrawBitmap(bitmap, 2, scale);
356 if (DrawSolidTilePainter(color, tile_size)) {
357 event();
358 bitmap.UpdateTexture();
359 }
360 DrawGrid();
361 DrawOverlay();
362}
363
364void Canvas::UpdateInfoGrid(ImVec2 bg_size, float grid_size, int label_id) {
366 enable_custom_labels_ = true; // Legacy compatibility
367 DrawBackground(bg_size);
368 DrawInfoGrid(grid_size, 8, label_id);
369 DrawOverlay();
370}
371
372void Canvas::DrawBackground(ImVec2 canvas_size) {
373 draw_list_ = GetWindowDrawList();
374
375 // Phase 1: Calculate geometry using new helper
378 GetCursorScreenPos(),
379 GetContentRegionAvail());
380
381 // Sync legacy fields for backward compatibility
386
387 // Update config if explicit size provided
388 if (canvas_size.x != 0) {
390 }
391
392 // Phase 1: Render background using helper
394
395 ImGui::InvisibleButton(canvas_id_.c_str(), state_.geometry.scaled_size, kMouseFlags);
396
397 // CRITICAL FIX: Always update hover mouse position when hovering over canvas
398 // This fixes the regression where CheckForCurrentMap() couldn't track hover
399 // Phase 1: Use geometry helper for mouse calculation
400 if (IsItemHovered()) {
401 const ImGuiIO& io = GetIO();
404 state_.is_hovered = true;
405 is_hovered_ = true;
406 } else {
407 state_.is_hovered = false;
408 is_hovered_ = false;
409 }
410
411 // Pan handling (Phase 1: Use geometry helper)
412 if (config_.is_draggable && IsItemHovered()) {
413 const ImGuiIO& io = GetIO();
414 const bool is_active = IsItemActive(); // Held
415
416 // Pan (we use a zero mouse threshold when there's no context menu)
417 if (const float mouse_threshold_for_pan =
418 enable_context_menu_ ? -1.0f : 0.0f;
419 is_active &&
420 IsMouseDragging(ImGuiMouseButton_Right, mouse_threshold_for_pan)) {
421 ApplyScrollDelta(state_.geometry, io.MouseDelta);
422 scrolling_ = state_.geometry.scrolling; // Sync legacy field
423 config_.scrolling = scrolling_; // Sync config
424 }
425 }
426}
427
429 const ImGuiIO& io = GetIO();
430 const ImVec2 scaled_sz(canvas_sz_.x * global_scale_,
432 const ImVec2 origin(canvas_p0_.x + scrolling_.x,
433 canvas_p0_.y + scrolling_.y); // Lock scrolled origin
434 const ImVec2 mouse_pos(io.MousePos.x - origin.x, io.MousePos.y - origin.y);
435
436 // Update canvas state for enhanced components
437 if (usage_tracker_) {
438 usage_tracker_->UpdateCanvasState(
441 }
442
443 // Use enhanced context menu if available
444 if (context_menu_) {
445 CanvasConfig snapshot;
446 snapshot.canvas_size = canvas_sz_;
448 snapshot.global_scale = global_scale_;
449 snapshot.grid_step = custom_step_;
450 snapshot.enable_grid = enable_grid_;
454 snapshot.is_draggable = draggable_;
456 snapshot.scrolling = scrolling_;
457
458 context_menu_->SetCanvasState(
462
463 context_menu_->Render(
464 context_id_, mouse_pos, rom_, bitmap_,
465 bitmap_ ? bitmap_->mutable_palette() : nullptr,
466 [this](CanvasContextMenu::Command command,
467 const CanvasConfig& updated_config) {
468 switch (command) {
470 ResetView();
471 break;
473 if (bitmap_) {
475 }
476 break;
479 break;
482 break;
486 break;
490 break;
494 break;
498 break;
501 break;
505 break;
507 config_.grid_step = updated_config.grid_step;
509 break;
511 config_.global_scale = updated_config.global_scale;
513 break;
515 if (modals_) {
516 CanvasConfig modal_config = updated_config;
517 modal_config.on_config_changed =
518 [this](const CanvasConfig& cfg) {
520 };
521 modal_config.on_scale_changed =
522 [this](const CanvasConfig& cfg) {
524 };
525 modals_->ShowAdvancedProperties(canvas_id_, modal_config,
526 bitmap_);
527 }
528 break;
530 if (modals_) {
531 CanvasConfig modal_config = updated_config;
532 modal_config.on_config_changed =
533 [this](const CanvasConfig& cfg) {
535 };
536 modal_config.on_scale_changed =
537 [this](const CanvasConfig& cfg) {
539 };
540 modals_->ShowScalingControls(canvas_id_, modal_config, bitmap_);
541 }
542 break;
543 default:
544 break;
545 }
546 },
547 snapshot, this); // Phase 4: Pass Canvas* for editor menu integration
548
549 if (modals_) {
550 modals_->Render();
551 }
552
553 return;
554 }
555
556
557
558 // Draw enhanced property dialogs
561}
562
564 // Phase 4: Use RenderMenuItem from canvas_menu.h for consistent rendering
565 auto popup_callback = [this](const std::string& id, std::function<void()> callback) {
566 popup_registry_.Open(id, callback);
567 };
568
569 gui::RenderMenuItem(item, popup_callback);
570}
571
573 // Phase 4: Add to editor menu definition
574 // Items are added to a default section with editor-specific priority
575 if (editor_menu_.sections.empty()) {
576 CanvasMenuSection section;
578 section.separator_after = true;
579 editor_menu_.sections.push_back(section);
580 }
581
582 // Add to the last section (or create new if the last isn't editor-specific)
583 auto& last_section = editor_menu_.sections.back();
584 if (last_section.priority != MenuSectionPriority::kEditorSpecific) {
585 CanvasMenuSection new_section;
587 new_section.separator_after = true;
588 editor_menu_.sections.push_back(new_section);
589 editor_menu_.sections.back().items.push_back(item);
590 } else {
591 last_section.items.push_back(item);
592 }
593}
594
598
599void Canvas::OpenPersistentPopup(const std::string& popup_id,
600 std::function<void()> render_callback) {
601 // Phase 4: Simplified popup management (no legacy synchronization)
602 popup_registry_.Open(popup_id, render_callback);
603}
604
605void Canvas::ClosePersistentPopup(const std::string& popup_id) {
606 // Phase 4: Simplified popup management (no legacy synchronization)
607 popup_registry_.Close(popup_id);
608}
609
611 // Phase 4: Simplified rendering (no legacy synchronization)
613}
614
616 if (!bitmap.is_active())
617 return;
618
619 ImVec2 available = ImGui::GetContentRegionAvail();
620 float scale_x = available.x / bitmap.width();
621 float scale_y = available.y / bitmap.height();
622 config_.global_scale = std::min(scale_x, scale_y);
623
624 // Ensure minimum readable scale
625 if (config_.global_scale < 0.25f)
626 config_.global_scale = 0.25f;
627
628 global_scale_ = config_.global_scale; // Legacy compatibility
629
630 // Center the view
631 scrolling_ = ImVec2(0, 0);
632}
633
635 config_.global_scale = 1.0f;
636 global_scale_ = 1.0f; // Legacy compatibility
637 scrolling_ = ImVec2(0, 0);
638}
639
663
669
670bool Canvas::DrawTilePainter(const Bitmap& bitmap, int size, float scale) {
671 const ImGuiIO& io = GetIO();
672 const bool is_hovered = IsItemHovered();
673 is_hovered_ = is_hovered;
674 // Lock scrolled origin
675 const ImVec2 origin(canvas_p0_.x + scrolling_.x, canvas_p0_.y + scrolling_.y);
676 const ImVec2 mouse_pos(io.MousePos.x - origin.x, io.MousePos.y - origin.y);
677 const auto scaled_size = size * scale;
678
679 // Erase the hover when the mouse is not in the canvas window.
680 if (!is_hovered) {
681 points_.clear();
682 return false;
683 }
684
685 // Reset the previous tile hover
686 if (!points_.empty()) {
687 points_.clear();
688 }
689
690 // Calculate the coordinates of the mouse
691 ImVec2 paint_pos = AlignPosToGrid(mouse_pos, scaled_size);
692 mouse_pos_in_canvas_ = paint_pos;
693 auto paint_pos_end =
694 ImVec2(paint_pos.x + scaled_size, paint_pos.y + scaled_size);
695 points_.push_back(paint_pos);
696 points_.push_back(paint_pos_end);
697
698 if (bitmap.is_active()) {
699 draw_list_->AddImage((ImTextureID)(intptr_t)bitmap.texture(),
700 ImVec2(origin.x + paint_pos.x, origin.y + paint_pos.y),
701 ImVec2(origin.x + paint_pos.x + scaled_size,
702 origin.y + paint_pos.y + scaled_size));
703 }
704
705 if (IsMouseClicked(ImGuiMouseButton_Left) &&
706 ImGui::IsMouseDragging(ImGuiMouseButton_Left)) {
707 // Draw the currently selected tile on the overworld here
708 // Save the coordinates of the selected tile.
709 drawn_tile_pos_ = paint_pos;
710 return true;
711 }
712
713 return false;
714}
715
716bool Canvas::DrawTilemapPainter(gfx::Tilemap& tilemap, int current_tile) {
717 const ImGuiIO& io = GetIO();
718 const bool is_hovered = IsItemHovered();
719 is_hovered_ = is_hovered;
720 const ImVec2 origin(canvas_p0_.x + scrolling_.x, canvas_p0_.y + scrolling_.y);
721 const ImVec2 mouse_pos(io.MousePos.x - origin.x, io.MousePos.y - origin.y);
722
723 // Safety check: ensure tilemap is properly initialized
724 if (!tilemap.atlas.is_active() || tilemap.tile_size.x <= 0) {
725 return false;
726 }
727
728 const auto scaled_size = tilemap.tile_size.x * global_scale_;
729
730 if (!is_hovered) {
731 points_.clear();
732 return false;
733 }
734
735 if (!points_.empty()) {
736 points_.clear();
737 }
738
739 ImVec2 paint_pos = AlignPosToGrid(mouse_pos, scaled_size);
740 mouse_pos_in_canvas_ = paint_pos;
741
742 points_.push_back(paint_pos);
743 points_.push_back(
744 ImVec2(paint_pos.x + scaled_size, paint_pos.y + scaled_size));
745
746 // CRITICAL FIX: Disable tile cache system to prevent crashes
747 // Just draw a simple preview tile using the atlas directly
748 if (tilemap.atlas.is_active() && tilemap.atlas.texture()) {
749 // Draw the tile directly from the atlas without caching
750 int tiles_per_row = tilemap.atlas.width() / tilemap.tile_size.x;
751 if (tiles_per_row > 0) {
752 int tile_x = (current_tile % tiles_per_row) * tilemap.tile_size.x;
753 int tile_y = (current_tile / tiles_per_row) * tilemap.tile_size.y;
754
755 // Simple bounds check
756 if (tile_x >= 0 && tile_x < tilemap.atlas.width() && tile_y >= 0 &&
757 tile_y < tilemap.atlas.height()) {
758
759 // Draw directly from atlas texture
760 ImVec2 uv0 =
761 ImVec2(static_cast<float>(tile_x) / tilemap.atlas.width(),
762 static_cast<float>(tile_y) / tilemap.atlas.height());
763 ImVec2 uv1 = ImVec2(static_cast<float>(tile_x + tilemap.tile_size.x) /
764 tilemap.atlas.width(),
765 static_cast<float>(tile_y + tilemap.tile_size.y) /
766 tilemap.atlas.height());
767
768 draw_list_->AddImage(
769 (ImTextureID)(intptr_t)tilemap.atlas.texture(),
770 ImVec2(origin.x + paint_pos.x, origin.y + paint_pos.y),
771 ImVec2(origin.x + paint_pos.x + scaled_size,
772 origin.y + paint_pos.y + scaled_size),
773 uv0, uv1);
774 }
775 }
776 }
777
778 if (IsMouseClicked(ImGuiMouseButton_Left) ||
779 ImGui::IsMouseDragging(ImGuiMouseButton_Left)) {
780 drawn_tile_pos_ = paint_pos;
781 return true;
782 }
783
784 return false;
785}
786
787bool Canvas::DrawSolidTilePainter(const ImVec4& color, int tile_size) {
788 const ImGuiIO& io = GetIO();
789 const bool is_hovered = IsItemHovered();
790 is_hovered_ = is_hovered;
791 // Lock scrolled origin
792 const ImVec2 origin(canvas_p0_.x + scrolling_.x, canvas_p0_.y + scrolling_.y);
793 const ImVec2 mouse_pos(io.MousePos.x - origin.x, io.MousePos.y - origin.y);
794 auto scaled_tile_size = tile_size * global_scale_;
795 static bool is_dragging = false;
796 static ImVec2 start_drag_pos;
797
798 // Erase the hover when the mouse is not in the canvas window.
799 if (!is_hovered) {
800 points_.clear();
801 return false;
802 }
803
804 // Reset the previous tile hover
805 if (!points_.empty()) {
806 points_.clear();
807 }
808
809 // Calculate the coordinates of the mouse
810 ImVec2 paint_pos = AlignPosToGrid(mouse_pos, scaled_tile_size);
811 mouse_pos_in_canvas_ = paint_pos;
812
813 // Clamp the size to a grid
814 paint_pos.x = std::clamp(paint_pos.x, 0.0f, canvas_sz_.x * global_scale_);
815 paint_pos.y = std::clamp(paint_pos.y, 0.0f, canvas_sz_.y * global_scale_);
816
817 points_.push_back(paint_pos);
818 points_.push_back(
819 ImVec2(paint_pos.x + scaled_tile_size, paint_pos.y + scaled_tile_size));
820
821 draw_list_->AddRectFilled(
822 ImVec2(origin.x + paint_pos.x + 1, origin.y + paint_pos.y + 1),
823 ImVec2(origin.x + paint_pos.x + scaled_tile_size,
824 origin.y + paint_pos.y + scaled_tile_size),
825 IM_COL32(color.x * 255, color.y * 255, color.z * 255, 255));
826
827 if (IsMouseClicked(ImGuiMouseButton_Left)) {
828 is_dragging = true;
829 start_drag_pos = paint_pos;
830 }
831
832 if (is_dragging && ImGui::IsMouseReleased(ImGuiMouseButton_Left)) {
833 is_dragging = false;
834 drawn_tile_pos_ = start_drag_pos;
835 return true;
836 }
837
838 return false;
839}
840
841void Canvas::DrawTileOnBitmap(int tile_size, gfx::Bitmap* bitmap,
842 ImVec4 color) {
843 const ImVec2 position = drawn_tile_pos_;
844 int tile_index_x = static_cast<int>(position.x / global_scale_) / tile_size;
845 int tile_index_y = static_cast<int>(position.y / global_scale_) / tile_size;
846
847 ImVec2 start_position(tile_index_x * tile_size, tile_index_y * tile_size);
848
849 // Update the bitmap's pixel data based on the start_position and color
850 for (int y = 0; y < tile_size; ++y) {
851 for (int x = 0; x < tile_size; ++x) {
852 // Calculate the actual pixel index in the bitmap
853 int pixel_index =
854 (start_position.y + y) * bitmap->width() + (start_position.x + x);
855
856 // Write the color to the pixel
857 bitmap->WriteColor(pixel_index, color);
858 }
859 }
860}
861
862bool Canvas::DrawTileSelector(int size, int size_y) {
863 const ImGuiIO& io = GetIO();
864 const bool is_hovered = IsItemHovered();
865 is_hovered_ = is_hovered;
866 const ImVec2 origin(canvas_p0_.x + scrolling_.x, canvas_p0_.y + scrolling_.y);
867 const ImVec2 mouse_pos(io.MousePos.x - origin.x, io.MousePos.y - origin.y);
868 if (size_y == 0) {
869 size_y = size;
870 }
871
872 if (is_hovered && IsMouseClicked(ImGuiMouseButton_Left)) {
873 if (!points_.empty()) {
874 points_.clear();
875 }
876 ImVec2 painter_pos = AlignPosToGrid(mouse_pos, size);
877
878 points_.push_back(painter_pos);
879 points_.push_back(ImVec2(painter_pos.x + size, painter_pos.y + size_y));
880 mouse_pos_in_canvas_ = painter_pos;
881 }
882
883 if (is_hovered && ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left)) {
884 return true;
885 }
886
887 return false;
888}
889
890void Canvas::DrawSelectRect(int current_map, int tile_size, float scale) {
891 gfx::ScopedTimer timer("canvas_select_rect");
892
893 const ImGuiIO& io = GetIO();
894 const ImVec2 origin(canvas_p0_.x + scrolling_.x, canvas_p0_.y + scrolling_.y);
895 const ImVec2 mouse_pos(io.MousePos.x - origin.x, io.MousePos.y - origin.y);
896 static ImVec2 drag_start_pos;
897 const float scaled_size = tile_size * scale;
898 static bool dragging = false;
899 constexpr int small_map_size = 0x200;
900
901 // Only handle mouse events if the canvas is hovered
902 const bool is_hovered = IsItemHovered();
903 if (!is_hovered) {
904 return;
905 }
906
907 // Calculate superX and superY accounting for world offset
908 int superY, superX;
909 if (current_map < 0x40) {
910 // Light World
911 superY = current_map / 8;
912 superX = current_map % 8;
913 } else if (current_map < 0x80) {
914 // Dark World
915 superY = (current_map - 0x40) / 8;
916 superX = (current_map - 0x40) % 8;
917 } else {
918 // Special World
919 superY = (current_map - 0x80) / 8;
920 superX = (current_map - 0x80) % 8;
921 }
922
923 // Handle right click for single tile selection
924 if (IsMouseClicked(ImGuiMouseButton_Right)) {
925 ImVec2 painter_pos = AlignPosToGrid(mouse_pos, scaled_size);
926 int painter_x = painter_pos.x;
927 int painter_y = painter_pos.y;
928
929 auto tile16_x = (painter_x % small_map_size) / (small_map_size / 0x20);
930 auto tile16_y = (painter_y % small_map_size) / (small_map_size / 0x20);
931
932 int index_x = superX * 0x20 + tile16_x;
933 int index_y = superY * 0x20 + tile16_y;
934 selected_tile_pos_ = ImVec2(index_x, index_y);
935 selected_points_.clear();
936 select_rect_active_ = false;
937
938 // Start drag position for rectangle selection
939 drag_start_pos = AlignPosToGrid(mouse_pos, scaled_size);
940 }
941
942 // Calculate the rectangle's top-left and bottom-right corners
943 ImVec2 drag_end_pos = AlignPosToGrid(mouse_pos, scaled_size);
944 if (ImGui::IsMouseDragging(ImGuiMouseButton_Right)) {
945 // FIX: Origin used to be canvas_p0_, revert if there is regression.
946 auto start = ImVec2(origin.x + drag_start_pos.x,
947 origin.y + drag_start_pos.y);
948 auto end = ImVec2(origin.x + drag_end_pos.x + tile_size,
949 origin.y + drag_end_pos.y + tile_size);
950 draw_list_->AddRect(start, end, kWhiteColor);
951 dragging = true;
952 }
953
954 if (dragging && !ImGui::IsMouseDown(ImGuiMouseButton_Right)) {
955 // Release dragging mode
956 dragging = false;
957
958 // Calculate the bounds of the rectangle in terms of 16x16 tile indices
959 constexpr int tile16_size = 16;
960 int start_x = std::floor(drag_start_pos.x / scaled_size) * tile16_size;
961 int start_y = std::floor(drag_start_pos.y / scaled_size) * tile16_size;
962 int end_x = std::floor(drag_end_pos.x / scaled_size) * tile16_size;
963 int end_y = std::floor(drag_end_pos.y / scaled_size) * tile16_size;
964
965 // Swap the start and end positions if they are in the wrong order
966 if (start_x > end_x)
967 std::swap(start_x, end_x);
968 if (start_y > end_y)
969 std::swap(start_y, end_y);
970
971 selected_tiles_.clear();
972 selected_tiles_.reserve(((end_x - start_x) / tile16_size + 1) *
973 ((end_y - start_y) / tile16_size + 1));
974
975 // Number of tiles per local map (since each tile is 16x16)
976 constexpr int tiles_per_local_map = small_map_size / 16;
977
978 // Loop through the tiles in the rectangle and store their positions
979 for (int y = start_y; y <= end_y; y += tile16_size) {
980 for (int x = start_x; x <= end_x; x += tile16_size) {
981 // Determine which local map (512x512) the tile is in
982 int local_map_x = (x / small_map_size) % 8;
983 int local_map_y = (y / small_map_size) % 8;
984
985 // Calculate the tile's position within its local map
986 int tile16_x = (x % small_map_size) / tile16_size;
987 int tile16_y = (y % small_map_size) / tile16_size;
988
989 // Calculate the index within the overall map structure
990 int index_x = local_map_x * tiles_per_local_map + tile16_x;
991 int index_y = local_map_y * tiles_per_local_map + tile16_y;
992
993 selected_tiles_.emplace_back(index_x, index_y);
994 }
995 }
996 // Clear and add the calculated rectangle points
997 selected_points_.clear();
998 selected_points_.push_back(drag_start_pos);
999 selected_points_.push_back(drag_end_pos);
1000 select_rect_active_ = true;
1001 }
1002}
1003
1004void Canvas::DrawBitmap(Bitmap& bitmap, int border_offset, float scale) {
1005 if (!bitmap.is_active()) {
1006 return;
1007 }
1008 bitmap_ = &bitmap;
1009
1010 // Update content size for table integration
1011 config_.content_size = ImVec2(bitmap.width(), bitmap.height());
1012
1013 // Phase 1: Use rendering helper
1014 RenderBitmapOnCanvas(draw_list_, state_.geometry, bitmap, border_offset, scale);
1015}
1016
1017void Canvas::DrawBitmap(Bitmap& bitmap, int x_offset, int y_offset, float scale,
1018 int alpha) {
1019 if (!bitmap.is_active()) {
1020 return;
1021 }
1022 bitmap_ = &bitmap;
1023
1024 // Update content size for table integration
1025 // CRITICAL: Store UNSCALED bitmap size as content - scale is applied during rendering
1026 config_.content_size = ImVec2(bitmap.width(), bitmap.height());
1027
1028 // Phase 1: Use rendering helper
1029 RenderBitmapOnCanvas(draw_list_, state_.geometry, bitmap, x_offset, y_offset, scale, alpha);
1030}
1031
1032void Canvas::DrawBitmap(Bitmap& bitmap, ImVec2 dest_pos, ImVec2 dest_size,
1033 ImVec2 src_pos, ImVec2 src_size) {
1034 if (!bitmap.is_active()) {
1035 return;
1036 }
1037 bitmap_ = &bitmap;
1038
1039 // Update content size for table integration
1040 config_.content_size = ImVec2(bitmap.width(), bitmap.height());
1041
1042 // Phase 1: Use rendering helper
1043 RenderBitmapOnCanvas(draw_list_, state_.geometry, bitmap, dest_pos, dest_size, src_pos, src_size);
1044}
1045
1046// TODO: Add parameters for sizing and positioning
1047void Canvas::DrawBitmapTable(const BitmapTable& gfx_bin) {
1048 for (const auto& [key, value] : gfx_bin) {
1049 int offset = 0x40 * (key + 1);
1050 int top_left_y = canvas_p0_.y + 2;
1051 if (key >= 1) {
1052 top_left_y = canvas_p0_.y + 0x40 * key;
1053 }
1054 draw_list_->AddImage((ImTextureID)(intptr_t)value.texture(),
1055 ImVec2(canvas_p0_.x + 2, top_left_y),
1056 ImVec2(canvas_p0_.x + 0x100, canvas_p0_.y + offset));
1057 }
1058}
1059
1060void Canvas::DrawOutline(int x, int y, int w, int h) {
1062 IM_COL32(255, 255, 255, 200));
1063}
1064
1065void Canvas::DrawOutlineWithColor(int x, int y, int w, int h, ImVec4 color) {
1067 y, w, h, color);
1068}
1069
1070void Canvas::DrawOutlineWithColor(int x, int y, int w, int h, uint32_t color) {
1072 color);
1073}
1074
1075void Canvas::DrawBitmapGroup(std::vector<int>& group, gfx::Tilemap& tilemap,
1076 int tile_size, float scale, int local_map_size,
1077 ImVec2 total_map_size) {
1078 if (selected_points_.size() != 2) {
1079 // points_ should contain exactly two points
1080 return;
1081 }
1082 if (group.empty()) {
1083 // group should not be empty
1084 return;
1085 }
1086
1087 // OPTIMIZATION: Use optimized rendering for large groups to improve performance
1088 bool use_optimized_rendering =
1089 group.size() > 128; // Optimize for large selections
1090
1091 // Use provided map sizes for proper boundary handling
1092 const int small_map = local_map_size;
1093 const float large_map_width = total_map_size.x;
1094 const float large_map_height = total_map_size.y;
1095
1096 // Pre-calculate common values to avoid repeated computation
1097 const float tile_scale = tile_size * scale;
1098 const int atlas_tiles_per_row = tilemap.atlas.width() / tilemap.tile_size.x;
1099
1100 // Top-left and bottom-right corners of the rectangle
1101 ImVec2 rect_top_left = selected_points_[0];
1102 ImVec2 rect_bottom_right = selected_points_[1];
1103
1104 // Calculate the start and end tiles in the grid
1105 int start_tile_x =
1106 static_cast<int>(std::floor(rect_top_left.x / (tile_size * scale)));
1107 int start_tile_y =
1108 static_cast<int>(std::floor(rect_top_left.y / (tile_size * scale)));
1109 int end_tile_x =
1110 static_cast<int>(std::floor(rect_bottom_right.x / (tile_size * scale)));
1111 int end_tile_y =
1112 static_cast<int>(std::floor(rect_bottom_right.y / (tile_size * scale)));
1113
1114 if (start_tile_x > end_tile_x)
1115 std::swap(start_tile_x, end_tile_x);
1116 if (start_tile_y > end_tile_y)
1117 std::swap(start_tile_y, end_tile_y);
1118
1119 // Calculate the size of the rectangle in 16x16 grid form
1120 int rect_width = (end_tile_x - start_tile_x) * tile_size;
1121 int rect_height = (end_tile_y - start_tile_y) * tile_size;
1122
1123 int tiles_per_row = rect_width / tile_size;
1124 int tiles_per_col = rect_height / tile_size;
1125
1126 int i = 0;
1127 for (int y = 0; y < tiles_per_col + 1; ++y) {
1128 for (int x = 0; x < tiles_per_row + 1; ++x) {
1129 // Check bounds to prevent access violations
1130 if (i >= static_cast<int>(group.size())) {
1131 break;
1132 }
1133
1134 int tile_id = group[i];
1135
1136 // Check if tile_id is within the range of tile16_individual_
1137 auto tilemap_size = tilemap.map_size.x;
1138 if (tile_id >= 0 && tile_id < tilemap_size) {
1139 // Calculate the position of the tile within the rectangle
1140 int tile_pos_x = (x + start_tile_x) * tile_size * scale;
1141 int tile_pos_y = (y + start_tile_y) * tile_size * scale;
1142
1143 // OPTIMIZATION: Use pre-calculated values for better performance with large selections
1144 if (tilemap.atlas.is_active() && tilemap.atlas.texture() &&
1145 atlas_tiles_per_row > 0) {
1146 int atlas_tile_x =
1147 (tile_id % atlas_tiles_per_row) * tilemap.tile_size.x;
1148 int atlas_tile_y =
1149 (tile_id / atlas_tiles_per_row) * tilemap.tile_size.y;
1150
1151 // Simple bounds check
1152 if (atlas_tile_x >= 0 && atlas_tile_x < tilemap.atlas.width() &&
1153 atlas_tile_y >= 0 && atlas_tile_y < tilemap.atlas.height()) {
1154
1155 // Calculate UV coordinates once for efficiency
1156 const float atlas_width = static_cast<float>(tilemap.atlas.width());
1157 const float atlas_height =
1158 static_cast<float>(tilemap.atlas.height());
1159 ImVec2 uv0 =
1160 ImVec2(atlas_tile_x / atlas_width, atlas_tile_y / atlas_height);
1161 ImVec2 uv1 =
1162 ImVec2((atlas_tile_x + tilemap.tile_size.x) / atlas_width,
1163 (atlas_tile_y + tilemap.tile_size.y) / atlas_height);
1164
1165 // Calculate screen positions
1166 float screen_x = canvas_p0_.x + scrolling_.x + tile_pos_x;
1167 float screen_y = canvas_p0_.y + scrolling_.y + tile_pos_y;
1168 float screen_w = tilemap.tile_size.x * scale;
1169 float screen_h = tilemap.tile_size.y * scale;
1170
1171 // Use higher alpha for large selections to make them more visible
1172 uint32_t alpha_color = use_optimized_rendering
1173 ? IM_COL32(255, 255, 255, 200)
1174 : IM_COL32(255, 255, 255, 150);
1175
1176 // Draw from atlas texture with optimized parameters
1177 draw_list_->AddImage(
1178 (ImTextureID)(intptr_t)tilemap.atlas.texture(),
1179 ImVec2(screen_x, screen_y),
1180 ImVec2(screen_x + screen_w, screen_y + screen_h), uv0, uv1,
1181 alpha_color);
1182 }
1183 }
1184 }
1185 i++;
1186 }
1187 // Break outer loop if we've run out of tiles
1188 if (i >= static_cast<int>(group.size())) {
1189 break;
1190 }
1191 }
1192
1193 // Performance optimization completed - tiles are now rendered with pre-calculated values
1194
1195 // Reposition rectangle to follow mouse, but clamp to prevent wrapping across map boundaries
1196 const ImGuiIO& io = GetIO();
1197 const ImVec2 origin(canvas_p0_.x + scrolling_.x, canvas_p0_.y + scrolling_.y);
1198 const ImVec2 mouse_pos(io.MousePos.x - origin.x, io.MousePos.y - origin.y);
1199
1200 // CRITICAL FIX: Clamp BEFORE grid alignment for smoother dragging behavior
1201 // This prevents the rectangle from even attempting to cross boundaries during drag
1202 ImVec2 clamped_mouse_pos = mouse_pos;
1203
1205 // Calculate which local map the mouse is in
1206 int mouse_local_map_x = static_cast<int>(mouse_pos.x) / small_map;
1207 int mouse_local_map_y = static_cast<int>(mouse_pos.y) / small_map;
1208
1209 // Calculate where the rectangle END would be if we place it at mouse position
1210 float potential_end_x = mouse_pos.x + rect_width;
1211 float potential_end_y = mouse_pos.y + rect_height;
1212
1213 // Check if this would cross local map boundary (512x512 blocks)
1214 int potential_end_map_x = static_cast<int>(potential_end_x) / small_map;
1215 int potential_end_map_y = static_cast<int>(potential_end_y) / small_map;
1216
1217 // Clamp mouse position to prevent crossing during drag
1218 if (potential_end_map_x != mouse_local_map_x) {
1219 // Would cross horizontal boundary - clamp mouse to safe zone
1220 float max_mouse_x = (mouse_local_map_x + 1) * small_map - rect_width;
1221 clamped_mouse_pos.x = std::min(mouse_pos.x, max_mouse_x);
1222 }
1223
1224 if (potential_end_map_y != mouse_local_map_y) {
1225 // Would cross vertical boundary - clamp mouse to safe zone
1226 float max_mouse_y = (mouse_local_map_y + 1) * small_map - rect_height;
1227 clamped_mouse_pos.y = std::min(mouse_pos.y, max_mouse_y);
1228 }
1229 }
1230
1231 // Now grid-align the clamped position
1232 auto new_start_pos = AlignPosToGrid(clamped_mouse_pos, tile_size * scale);
1233
1234 // Additional safety: clamp to overall map bounds
1235 new_start_pos.x =
1236 std::clamp(new_start_pos.x, 0.0f, large_map_width - rect_width);
1237 new_start_pos.y =
1238 std::clamp(new_start_pos.y, 0.0f, large_map_height - rect_height);
1239
1240 selected_points_.clear();
1241 selected_points_.push_back(new_start_pos);
1242 selected_points_.push_back(
1243 ImVec2(new_start_pos.x + rect_width, new_start_pos.y + rect_height));
1244 select_rect_active_ = true;
1245}
1246
1247void Canvas::DrawRect(int x, int y, int w, int h, ImVec4 color) {
1249 color, config_.global_scale);
1250}
1251
1252void Canvas::DrawText(const std::string& text, int x, int y) {
1255}
1256
1261
1262void Canvas::DrawInfoGrid(float grid_step, int tile_id_offset, int label_id) {
1263 // Draw grid + all lines in the canvas
1264 draw_list_->PushClipRect(canvas_p0_, canvas_p1_, true);
1265 if (enable_grid_) {
1266 if (custom_step_ != 0.f)
1267 grid_step = custom_step_;
1268 grid_step *= global_scale_; // Apply global scale to grid step
1269
1270 DrawGridLines(grid_step);
1271 DrawCustomHighlight(grid_step);
1272
1273 if (!enable_custom_labels_) {
1274 return;
1275 }
1276
1277 // Draw the contents of labels on the grid
1278 for (float x = fmodf(scrolling_.x, grid_step);
1279 x < canvas_sz_.x * global_scale_; x += grid_step) {
1280 for (float y = fmodf(scrolling_.y, grid_step);
1281 y < canvas_sz_.y * global_scale_; y += grid_step) {
1282 int tile_x = (x - scrolling_.x) / grid_step;
1283 int tile_y = (y - scrolling_.y) / grid_step;
1284 int tile_id = tile_x + (tile_y * tile_id_offset);
1285
1286 if (tile_id >= labels_[label_id].size()) {
1287 break;
1288 }
1289 std::string label = labels_[label_id][tile_id];
1290 draw_list_->AddText(
1291 ImVec2(canvas_p0_.x + x + (grid_step / 2) - tile_id_offset,
1292 canvas_p0_.y + y + (grid_step / 2) - tile_id_offset),
1293 kWhiteColor, label.data());
1294 }
1295 }
1296 }
1297}
1298
1303
1304void Canvas::DrawGrid(float grid_step, int tile_id_offset) {
1305 if (config_.grid_step != 0.f)
1306 grid_step = config_.grid_step;
1307
1308 // Create render context for utilities
1311 .canvas_p0 = canvas_p0_,
1312 .canvas_p1 = canvas_p1_,
1313 .scrolling = scrolling_,
1314 .global_scale = config_.global_scale,
1315 .enable_grid = config_.enable_grid,
1316 .enable_hex_labels = config_.enable_hex_labels,
1317 .grid_step = grid_step};
1318
1319 // Use high-level utility function
1321
1322 // Draw custom labels if enabled
1324 draw_list_->PushClipRect(canvas_p0_, canvas_p1_, true);
1326 tile_id_offset);
1327 draw_list_->PopClipRect();
1328 }
1329}
1330
1332 // Create render context for utilities
1335 .canvas_p0 = canvas_p0_,
1336 .canvas_p1 = canvas_p1_,
1337 .scrolling = scrolling_,
1338 .global_scale = config_.global_scale,
1339 .enable_grid = config_.enable_grid,
1340 .enable_hex_labels = config_.enable_hex_labels,
1341 .grid_step = config_.grid_step};
1342
1343 // Use high-level utility function with local points (synchronized from interaction handler)
1345
1346 // Render any persistent popups from context menu actions
1348}
1349
1351 // Based on ImGui demo, should be adapted to use for OAM
1352 ImDrawList* draw_list = ImGui::GetWindowDrawList();
1353 {
1354 Text("Blue shape is drawn first: appears in back");
1355 Text("Red shape is drawn after: appears in front");
1356 ImVec2 p0 = ImGui::GetCursorScreenPos();
1357 draw_list->AddRectFilled(ImVec2(p0.x, p0.y), ImVec2(p0.x + 50, p0.y + 50),
1358 IM_COL32(0, 0, 255, 255)); // Blue
1359 draw_list->AddRectFilled(ImVec2(p0.x + 25, p0.y + 25),
1360 ImVec2(p0.x + 75, p0.y + 75),
1361 IM_COL32(255, 0, 0, 255)); // Red
1362 ImGui::Dummy(ImVec2(75, 75));
1363 }
1364 ImGui::Separator();
1365 {
1366 Text("Blue shape is drawn first, into channel 1: appears in front");
1367 Text("Red shape is drawn after, into channel 0: appears in back");
1368 ImVec2 p1 = ImGui::GetCursorScreenPos();
1369
1370 // Create 2 channels and draw a Blue shape THEN a Red shape.
1371 // You can create any number of channels. Tables API use 1 channel per
1372 // column in order to better batch draw calls.
1373 draw_list->ChannelsSplit(2);
1374 draw_list->ChannelsSetCurrent(1);
1375 draw_list->AddRectFilled(ImVec2(p1.x, p1.y), ImVec2(p1.x + 50, p1.y + 50),
1376 IM_COL32(0, 0, 255, 255)); // Blue
1377 draw_list->ChannelsSetCurrent(0);
1378 draw_list->AddRectFilled(ImVec2(p1.x + 25, p1.y + 25),
1379 ImVec2(p1.x + 75, p1.y + 75),
1380 IM_COL32(255, 0, 0, 255)); // Red
1381
1382 // Flatten/reorder channels. Red shape is in channel 0 and it appears
1383 // below the Blue shape in channel 1. This works by copying draw indices
1384 // only (vertices are not copied).
1385 draw_list->ChannelsMerge();
1386 ImGui::Dummy(ImVec2(75, 75));
1387 Text("After reordering, contents of channel 0 appears below channel 1.");
1388 }
1389}
1390
1391void BeginCanvas(Canvas& canvas, ImVec2 child_size) {
1393
1394 // Use improved canvas sizing for table integration
1395 ImVec2 effective_size = child_size;
1396 if (child_size.x == 0 && child_size.y == 0) {
1397 // Auto-size based on canvas configuration
1398 if (canvas.IsAutoResize()) {
1399 effective_size = canvas.GetPreferredSize();
1400 } else {
1401 effective_size = canvas.GetCurrentSize();
1402 }
1403 }
1404
1405 ImGui::BeginChild(canvas.canvas_id().c_str(), effective_size, true,
1406 ImGuiWindowFlags_AlwaysVerticalScrollbar);
1407 canvas.DrawBackground();
1409 canvas.DrawContextMenu();
1410}
1411
1412void EndCanvas(Canvas& canvas) {
1413 canvas.DrawGrid();
1414 canvas.DrawOverlay();
1415 ImGui::EndChild();
1416}
1417
1418void GraphicsBinCanvasPipeline(int width, int height, int tile_size,
1419 int num_sheets_to_load, int canvas_id,
1420 bool is_loaded, gfx::BitmapTable& graphics_bin) {
1421 gui::Canvas canvas;
1422 if (ImGuiID child_id =
1423 ImGui::GetID((ImTextureID)(intptr_t)(intptr_t)canvas_id);
1424 ImGui::BeginChild(child_id, ImGui::GetContentRegionAvail(), true,
1425 ImGuiWindowFlags_AlwaysVerticalScrollbar)) {
1426 canvas.DrawBackground(ImVec2(width + 1, num_sheets_to_load * height + 1));
1427 canvas.DrawContextMenu();
1428 if (is_loaded) {
1429 for (const auto& [key, value] : graphics_bin) {
1430 int offset = height * (key + 1);
1431 int top_left_y = canvas.zero_point().y + 2;
1432 if (key >= 1) {
1433 top_left_y = canvas.zero_point().y + height * key;
1434 }
1435 canvas.draw_list()->AddImage(
1436 (ImTextureID)(intptr_t)value.texture(),
1437 ImVec2(canvas.zero_point().x + 2, top_left_y),
1438 ImVec2(canvas.zero_point().x + 0x100,
1439 canvas.zero_point().y + offset));
1440 }
1441 }
1442 canvas.DrawTileSelector(tile_size);
1443 canvas.DrawGrid(tile_size);
1444 canvas.DrawOverlay();
1445 // Phase 3: Render persistent popups (previously only available via End())
1446 canvas.RenderPersistentPopups();
1447 }
1448 ImGui::EndChild();
1449}
1450
1451void BitmapCanvasPipeline(gui::Canvas& canvas, gfx::Bitmap& bitmap, int width,
1452 int height, int tile_size, bool is_loaded,
1453 bool scrollbar, int canvas_id) {
1454 auto draw_canvas = [&](gui::Canvas& canvas, gfx::Bitmap& bitmap, int width,
1455 int height, int tile_size, bool is_loaded) {
1456 canvas.DrawBackground(ImVec2(width + 1, height + 1));
1457 canvas.DrawContextMenu();
1458 canvas.DrawBitmap(bitmap, 2, is_loaded);
1459 canvas.DrawTileSelector(tile_size);
1460 canvas.DrawGrid(tile_size);
1461 canvas.DrawOverlay();
1462 // Phase 3: Render persistent popups (previously only available via End())
1463 canvas.RenderPersistentPopups();
1464 };
1465
1466 if (scrollbar) {
1467 if (ImGuiID child_id =
1468 ImGui::GetID((ImTextureID)(intptr_t)(intptr_t)canvas_id);
1469 ImGui::BeginChild(child_id, ImGui::GetContentRegionAvail(), true,
1470 ImGuiWindowFlags_AlwaysVerticalScrollbar)) {
1471 draw_canvas(canvas, bitmap, width, height, tile_size, is_loaded);
1472 }
1473 ImGui::EndChild();
1474 } else {
1475 draw_canvas(canvas, bitmap, width, height, tile_size, is_loaded);
1476 }
1477}
1478
1480 const std::string& label, bool auto_resize) {
1481 // Configure canvas for table integration
1482 canvas.SetAutoResize(auto_resize);
1483
1484 if (auto_resize && bitmap.is_active()) {
1485 // Auto-calculate size based on bitmap content
1486 ImVec2 content_size = ImVec2(bitmap.width(), bitmap.height());
1487 ImVec2 preferred_size = CanvasUtils::CalculatePreferredCanvasSize(
1488 content_size, canvas.GetGlobalScale());
1489 canvas.SetCanvasSize(preferred_size);
1490 }
1491
1492 // Begin table-aware canvas
1493 if (canvas.BeginTableCanvas(label)) {
1494 // Draw the canvas content
1495 canvas.DrawBackground();
1496 canvas.DrawContextMenu();
1497
1498 if (bitmap.is_active()) {
1499 canvas.DrawBitmap(bitmap, 2, 2, canvas.GetGlobalScale());
1500 }
1501
1502 canvas.DrawGrid();
1503 canvas.DrawOverlay();
1504 // Phase 3: Render persistent popups (previously only available via End())
1505 canvas.RenderPersistentPopups();
1506 }
1507 canvas.EndTableCanvas();
1508}
1509
1511 // Use the new modal system if available
1512 if (modals_) {
1513 CanvasConfig modal_config;
1514 modal_config.canvas_size = canvas_sz_;
1515 modal_config.content_size = config_.content_size;
1516 modal_config.global_scale = global_scale_;
1517 modal_config.grid_step = custom_step_;
1518 modal_config.enable_grid = enable_grid_;
1522 modal_config.is_draggable = draggable_;
1523 modal_config.auto_resize = config_.auto_resize;
1524 modal_config.scrolling = scrolling_;
1525 modal_config.on_config_changed =
1526 [this](const CanvasConfig& updated_config) {
1527 // Update legacy variables when config changes
1528 enable_grid_ = updated_config.enable_grid;
1529 enable_hex_tile_labels_ = updated_config.enable_hex_labels;
1530 enable_custom_labels_ = updated_config.enable_custom_labels;
1531 };
1532 modal_config.on_scale_changed =
1533 [this](const CanvasConfig& updated_config) {
1534 global_scale_ = updated_config.global_scale;
1535 scrolling_ = updated_config.scrolling;
1536 };
1537
1538 modals_->ShowAdvancedProperties(canvas_id_, modal_config, bitmap_);
1539 return;
1540 }
1541
1542 // Fallback to legacy modal system
1543 if (ImGui::BeginPopupModal("Advanced Canvas Properties", nullptr,
1544 ImGuiWindowFlags_AlwaysAutoResize)) {
1545 ImGui::Text("Advanced Canvas Configuration");
1546 ImGui::Separator();
1547
1548 // Canvas properties (read-only info)
1549 ImGui::Text("Canvas Properties");
1550 ImGui::Text("ID: %s", canvas_id_.c_str());
1551 ImGui::Text("Canvas Size: %.0f x %.0f", config_.canvas_size.x,
1553 ImGui::Text("Content Size: %.0f x %.0f", config_.content_size.x,
1555 ImGui::Text("Global Scale: %.3f", config_.global_scale);
1556 ImGui::Text("Grid Step: %.1f", config_.grid_step);
1557
1558 if (config_.content_size.x > 0 && config_.content_size.y > 0) {
1559 ImVec2 min_size = GetMinimumSize();
1560 ImVec2 preferred_size = GetPreferredSize();
1561 ImGui::Text("Minimum Size: %.0f x %.0f", min_size.x, min_size.y);
1562 ImGui::Text("Preferred Size: %.0f x %.0f", preferred_size.x,
1563 preferred_size.y);
1564 }
1565
1566 // Editable properties using new config system
1567 ImGui::Separator();
1568 ImGui::Text("View Settings");
1569 if (ImGui::Checkbox("Enable Grid", &config_.enable_grid)) {
1570 enable_grid_ = config_.enable_grid; // Legacy sync
1571 }
1572 if (ImGui::Checkbox("Enable Hex Labels", &config_.enable_hex_labels)) {
1574 }
1575 if (ImGui::Checkbox("Enable Custom Labels",
1578 }
1579 if (ImGui::Checkbox("Enable Context Menu", &config_.enable_context_menu)) {
1581 }
1582 if (ImGui::Checkbox("Draggable", &config_.is_draggable)) {
1583 draggable_ = config_.is_draggable; // Legacy sync
1584 }
1585 if (ImGui::Checkbox("Auto Resize for Tables", &config_.auto_resize)) {
1586 // Auto resize setting changed
1587 }
1588
1589 // Grid controls
1590 ImGui::Separator();
1591 ImGui::Text("Grid Configuration");
1592 if (ImGui::SliderFloat("Grid Step", &config_.grid_step, 1.0f, 128.0f,
1593 "%.1f")) {
1594 custom_step_ = config_.grid_step; // Legacy sync
1595 }
1596
1597 // Scale controls
1598 ImGui::Separator();
1599 ImGui::Text("Scale Configuration");
1600 if (ImGui::SliderFloat("Global Scale", &config_.global_scale, 0.1f, 10.0f,
1601 "%.2f")) {
1602 global_scale_ = config_.global_scale; // Legacy sync
1603 }
1604
1605 // Scrolling controls
1606 ImGui::Separator();
1607 ImGui::Text("Scrolling Configuration");
1608 ImGui::Text("Current Scroll: %.1f, %.1f", scrolling_.x, scrolling_.y);
1609 if (ImGui::Button("Reset Scroll")) {
1610 scrolling_ = ImVec2(0, 0);
1611 }
1612 ImGui::SameLine();
1613 if (ImGui::Button("Center View")) {
1614 if (bitmap_) {
1615 scrolling_ = ImVec2(
1617 2.0f,
1619 config_.canvas_size.y) /
1620 2.0f);
1621 }
1622 }
1623
1624 if (ImGui::Button("Close")) {
1625 ImGui::CloseCurrentPopup();
1626 }
1627 ImGui::EndPopup();
1628 }
1629}
1630
1631// Old ShowPaletteManager method removed - now handled by PaletteWidget
1632
1634 // Use the new modal system if available
1635 if (modals_) {
1636 CanvasConfig modal_config;
1637 modal_config.canvas_size = canvas_sz_;
1638 modal_config.content_size = config_.content_size;
1639 modal_config.global_scale = global_scale_;
1640 modal_config.grid_step = custom_step_;
1641 modal_config.enable_grid = enable_grid_;
1645 modal_config.is_draggable = draggable_;
1646 modal_config.auto_resize = config_.auto_resize;
1647 modal_config.scrolling = scrolling_;
1648 modal_config.on_config_changed =
1649 [this](const CanvasConfig& updated_config) {
1650 // Update legacy variables when config changes
1651 enable_grid_ = updated_config.enable_grid;
1652 enable_hex_tile_labels_ = updated_config.enable_hex_labels;
1653 enable_custom_labels_ = updated_config.enable_custom_labels;
1654 enable_context_menu_ = updated_config.enable_context_menu;
1655 };
1656 modal_config.on_scale_changed =
1657 [this](const CanvasConfig& updated_config) {
1658 draggable_ = updated_config.is_draggable;
1659 custom_step_ = updated_config.grid_step;
1660 global_scale_ = updated_config.global_scale;
1661 scrolling_ = updated_config.scrolling;
1662 };
1663
1664 modals_->ShowScalingControls(canvas_id_, modal_config);
1665 return;
1666 }
1667
1668 // Fallback to legacy modal system
1669 if (ImGui::BeginPopupModal("Scaling Controls", nullptr,
1670 ImGuiWindowFlags_AlwaysAutoResize)) {
1671 ImGui::Text("Canvas Scaling and Display Controls");
1672 ImGui::Separator();
1673
1674 // Global scale with new config system
1675 ImGui::Text("Global Scale: %.3f", config_.global_scale);
1676 if (ImGui::SliderFloat("##GlobalScale", &config_.global_scale, 0.1f, 10.0f,
1677 "%.2f")) {
1678 global_scale_ = config_.global_scale; // Legacy sync
1679 }
1680
1681 // Preset scale buttons
1682 ImGui::Text("Preset Scales:");
1683 if (ImGui::Button("0.25x")) {
1684 config_.global_scale = 0.25f;
1686 }
1687 ImGui::SameLine();
1688 if (ImGui::Button("0.5x")) {
1689 config_.global_scale = 0.5f;
1691 }
1692 ImGui::SameLine();
1693 if (ImGui::Button("1x")) {
1694 config_.global_scale = 1.0f;
1696 }
1697 ImGui::SameLine();
1698 if (ImGui::Button("2x")) {
1699 config_.global_scale = 2.0f;
1701 }
1702 ImGui::SameLine();
1703 if (ImGui::Button("4x")) {
1704 config_.global_scale = 4.0f;
1706 }
1707 ImGui::SameLine();
1708 if (ImGui::Button("8x")) {
1709 config_.global_scale = 8.0f;
1711 }
1712
1713 // Grid configuration
1714 ImGui::Separator();
1715 ImGui::Text("Grid Configuration");
1716 ImGui::Text("Grid Step: %.1f", config_.grid_step);
1717 if (ImGui::SliderFloat("##GridStep", &config_.grid_step, 1.0f, 128.0f,
1718 "%.1f")) {
1719 custom_step_ = config_.grid_step; // Legacy sync
1720 }
1721
1722 // Grid size presets
1723 ImGui::Text("Grid Presets:");
1724 if (ImGui::Button("8x8")) {
1725 config_.grid_step = 8.0f;
1727 }
1728 ImGui::SameLine();
1729 if (ImGui::Button("16x16")) {
1730 config_.grid_step = 16.0f;
1732 }
1733 ImGui::SameLine();
1734 if (ImGui::Button("32x32")) {
1735 config_.grid_step = 32.0f;
1737 }
1738 ImGui::SameLine();
1739 if (ImGui::Button("64x64")) {
1740 config_.grid_step = 64.0f;
1742 }
1743
1744 // Canvas size info
1745 ImGui::Separator();
1746 ImGui::Text("Canvas Information");
1747 ImGui::Text("Canvas Size: %.0f x %.0f", config_.canvas_size.x,
1749 ImGui::Text("Scaled Size: %.0f x %.0f",
1752 if (bitmap_) {
1753 ImGui::Text("Bitmap Size: %d x %d", bitmap_->width(), bitmap_->height());
1754 ImGui::Text(
1755 "Effective Scale: %.3f x %.3f",
1758 }
1759
1760 if (ImGui::Button("Close")) {
1761 ImGui::CloseCurrentPopup();
1762 }
1763 ImGui::EndPopup();
1764 }
1765}
1766
1767// BPP format management methods
1769 if (!bpp_format_ui_) {
1771 std::make_unique<gui::BppFormatUI>(canvas_id_ + "_bpp_format");
1772 }
1773
1774 if (bitmap_) {
1775 bpp_format_ui_->RenderFormatSelector(
1777 [this](gfx::BppFormat format) { ConvertBitmapFormat(format); });
1778 }
1779}
1780
1782 if (!bpp_format_ui_) {
1784 std::make_unique<gui::BppFormatUI>(canvas_id_ + "_bpp_format");
1785 }
1786
1787 if (bitmap_) {
1788 bpp_format_ui_->RenderAnalysisPanel(*bitmap_, bitmap_->palette());
1789 }
1790}
1791
1794 bpp_conversion_dialog_ = std::make_unique<gui::BppConversionDialog>(
1795 canvas_id_ + "_bpp_conversion");
1796 }
1797
1798 if (bitmap_) {
1800 *bitmap_, bitmap_->palette(),
1801 [this](gfx::BppFormat format, bool /*preserve_palette*/) {
1802 ConvertBitmapFormat(format);
1803 });
1804 }
1805
1806 bpp_conversion_dialog_->Render();
1807}
1808
1810 if (!bitmap_)
1811 return false;
1812
1813 gfx::BppFormat current_format = GetCurrentBppFormat();
1814 if (current_format == target_format) {
1815 return true; // No conversion needed
1816 }
1817
1818 try {
1819 // Convert the bitmap data
1820 auto converted_data = gfx::BppFormatManager::Get().ConvertFormat(
1821 bitmap_->vector(), current_format, target_format, bitmap_->width(),
1822 bitmap_->height());
1823
1824 // Update the bitmap with converted data
1825 bitmap_->set_data(converted_data);
1826
1827 // Update the renderer
1829
1830 return true;
1831 } catch (const std::exception& e) {
1832 SDL_Log("Failed to convert bitmap format: %s", e.what());
1833 return false;
1834 }
1835}
1836
1844
1845// Phase 4A: Canvas Automation API
1847 if (!automation_api_) {
1848 automation_api_ = std::make_unique<CanvasAutomationAPI>(this);
1849 }
1850 return automation_api_.get();
1851}
1852
1853} // namespace yaze::gui
The Rom class is used to load, save, and modify Rom data.
Definition rom.h:74
Represents a bitmap image optimized for SNES ROM hacking.
Definition bitmap.h:66
const SnesPalette & palette() const
Definition bitmap.h:277
TextureHandle texture() const
Definition bitmap.h:289
const std::vector< uint8_t > & vector() const
Definition bitmap.h:290
bool is_active() const
Definition bitmap.h:293
SnesPalette * mutable_palette()
Definition bitmap.h:278
void WriteColor(int position, const ImVec4 &color)
Write a color to a pixel at the given position.
Definition bitmap.cc:522
int height() const
Definition bitmap.h:283
void set_data(const std::vector< uint8_t > &data)
Definition bitmap.cc:743
int width() const
Definition bitmap.h:282
void UpdateTexture()
Updates the underlying SDL_Texture when it already exists.
Definition bitmap.cc:246
BppFormat DetectFormat(const std::vector< uint8_t > &data, int width, int height)
Detect BPP format from bitmap data.
std::vector< uint8_t > ConvertFormat(const std::vector< uint8_t > &data, BppFormat from_format, BppFormat to_format, int width, int height)
Convert bitmap data between BPP formats.
static BppFormatManager & Get()
Defines an abstract interface for all rendering operations.
Definition irenderer.h:35
RAII timer for automatic timing management.
Programmatic interface for controlling canvas operations.
void Initialize(const std::string &canvas_id)
Initialize the interaction handler.
Modern, robust canvas for drawing and manipulating graphics.
Definition canvas.h:59
ImVec2 scrolling_
Definition canvas.h:430
CanvasState state_
Definition canvas.h:401
ImVector< ImVec2 > points_
Definition canvas.h:439
int highlight_tile_id
Definition canvas.h:419
void DrawBitmap(Bitmap &bitmap, int border_offset, float scale)
Definition canvas.cc:1004
PopupRegistry popup_registry_
Definition canvas.h:415
Rom * rom() const
Definition canvas.h:389
std::string canvas_id_
Definition canvas.h:443
void ShowScalingControls()
Definition canvas.cc:1633
bool WasDoubleClicked(ImGuiMouseButton button=ImGuiMouseButton_Left) const
Definition canvas.cc:315
CanvasConfig config_
Definition canvas.h:396
ImVec2 selected_tile_pos_
Definition canvas.h:449
auto global_scale() const
Definition canvas.h:329
ImVec2 canvas_p1_
Definition canvas.h:433
void ShowBppAnalysis()
Definition canvas.cc:1781
void DrawOutlineWithColor(int x, int y, int w, int h, ImVec4 color)
Definition canvas.cc:1065
void SetUsageMode(CanvasUsage usage)
Definition canvas.cc:187
void DrawBitmapGroup(std::vector< int > &group, gfx::Tilemap &tilemap, int tile_size, float scale=1.0f, int local_map_size=0x200, ImVec2 total_map_size=ImVec2(0x1000, 0x1000))
Draw group of bitmaps for multi-tile selection preview.
Definition canvas.cc:1075
bool BeginTableCanvas(const std::string &label="")
Definition canvas.cc:280
void InitializeEnhancedComponents()
Definition canvas.cc:164
void ShowBppConversionDialog()
Definition canvas.cc:1792
CanvasAutomationAPI * GetAutomationAPI()
Definition canvas.cc:1846
void ShowAdvancedCanvasProperties()
Definition canvas.cc:1510
void ApplyScaleSnapshot(const CanvasConfig &snapshot)
Definition canvas.cc:664
void UpdateInfoGrid(ImVec2 bg_size, float grid_size=64.0f, int label_id=0)
Definition canvas.cc:364
std::unique_ptr< gui::BppFormatUI > bpp_format_ui_
Definition canvas.h:160
void DrawContextMenu()
Definition canvas.cc:428
ImVec2 mouse_pos_in_canvas_
Definition canvas.h:435
bool DrawTilemapPainter(gfx::Tilemap &tilemap, int current_tile)
Definition canvas.cc:716
bool DrawSolidTilePainter(const ImVec4 &color, int size)
Definition canvas.cc:787
bool enable_context_menu_
Definition canvas.h:456
auto draw_list() const
Definition canvas.h:293
CanvasMenuDefinition editor_menu_
Definition canvas.h:411
std::unique_ptr< CanvasModals > modals_
Definition canvas.h:165
void ApplyConfigSnapshot(const CanvasConfig &snapshot)
Definition canvas.cc:640
void DrawLayeredElements()
Definition canvas.cc:1350
void ReserveTableSpace(const std::string &label="")
Definition canvas.cc:275
bool enable_custom_labels_
Definition canvas.h:455
void ShowUsageReport()
Definition canvas.cc:214
ImVec2 GetMinimumSize() const
Definition canvas.cc:265
bool DrawTileSelector(int size, int size_y=0)
Definition canvas.cc:862
bool ConvertBitmapFormat(gfx::BppFormat target_format)
Definition canvas.cc:1809
void DrawGridLines(float grid_step)
Definition canvas.cc:1257
void ShowPerformanceUI()
Definition canvas.cc:208
bool custom_canvas_size_
Definition canvas.h:457
void ClearContextMenuItems()
Definition canvas.cc:595
void DrawRect(int x, int y, int w, int h, ImVec4 color)
Definition canvas.cc:1247
bool HasValidSelection() const
Definition canvas.cc:307
bool DrawTilePainter(const Bitmap &bitmap, int size, float scale=1.0f)
Definition canvas.cc:670
ImDrawList * draw_list_
Definition canvas.h:427
ImVector< ImVec2 > selected_points_
Definition canvas.h:448
ImVec2 GetCurrentSize() const
Definition canvas.h:238
void SetCanvasSize(ImVec2 canvas_size)
Definition canvas.h:309
void UpdateColorPainter(gfx::IRenderer *renderer, gfx::Bitmap &bitmap, const ImVec4 &color, const std::function< void()> &event, int tile_size, float scale=1.0f)
Definition canvas.cc:348
void DrawTileOnBitmap(int tile_size, gfx::Bitmap *bitmap, ImVec4 color)
Definition canvas.cc:841
void DrawCustomHighlight(float grid_step)
Definition canvas.cc:1299
bool select_rect_active_
Definition canvas.h:450
Bitmap * bitmap_
Definition canvas.h:425
CanvasGridSize grid_size() const
Definition canvas.h:102
void AddContextMenuItem(const gui::CanvasMenuItem &item)
Definition canvas.cc:572
ImVec2 GetPreferredSize() const
Definition canvas.cc:270
CanvasInteractionHandler interaction_handler_
Definition canvas.h:169
void InitializePaletteEditor(Rom *rom)
Definition canvas.cc:236
void Begin(ImVec2 canvas_size=ImVec2(0, 0))
Begin canvas rendering (ImGui-style)
Definition canvas.cc:329
auto canvas_size() const
Definition canvas.h:298
void SetZoomToFit(const gfx::Bitmap &bitmap)
Definition canvas.cc:615
bool WasClicked(ImGuiMouseButton button=ImGuiMouseButton_Left) const
Definition canvas.cc:311
ImVector< ImVector< std::string > > labels_
Definition canvas.h:440
gfx::BppFormat GetCurrentBppFormat() const
Definition canvas.cc:1837
auto canvas_id() const
Definition canvas.h:336
void ClosePersistentPopup(const std::string &popup_id)
Definition canvas.cc:605
void ShowBppFormatSelector()
Definition canvas.cc:1768
void RecordCanvasOperation(const std::string &operation_name, double time_ms)
Definition canvas.cc:197
void RenderPersistentPopups()
Definition canvas.cc:610
void SetGridSize(CanvasGridSize grid_size)
Definition canvas.h:82
bool IsAutoResize() const
Definition canvas.h:240
std::shared_ptr< CanvasUsageTracker > usage_tracker_
Definition canvas.h:167
void End()
End canvas rendering (ImGui-style)
Definition canvas.cc:335
float GetGlobalScale() const
Definition canvas.h:313
std::unique_ptr< gui::BppConversionDialog > bpp_conversion_dialog_
Definition canvas.h:161
void DrawSelectRect(int current_map, int tile_size=0x10, float scale=1.0f)
Definition canvas.cc:890
auto zero_point() const
Definition canvas.h:294
std::unique_ptr< CanvasContextMenu > context_menu_
Definition canvas.h:166
auto usage_mode() const
Definition canvas.h:218
ImVec2 GetLastClickPosition() const
Definition canvas.cc:320
void ShowPaletteEditor()
Definition canvas.cc:243
float global_scale_
Definition canvas.h:452
void DrawOutline(int x, int y, int w, int h)
Definition canvas.cc:1060
std::unique_ptr< PaletteEditorWidget > palette_editor_
Definition canvas.h:398
float custom_step_
Definition canvas.h:451
void DrawInfoGrid(float grid_step=64.0f, int tile_id_offset=8, int label_id=0)
Definition canvas.cc:1262
CanvasSelection selection_
Definition canvas.h:397
bool enable_hex_tile_labels_
Definition canvas.h:454
ImVec2 canvas_p0_
Definition canvas.h:432
void OpenPersistentPopup(const std::string &popup_id, std::function< void()> render_callback)
Definition canvas.cc:599
void DrawBitmapTable(const BitmapTable &gfx_bin)
Definition canvas.cc:1047
std::string context_id_
Definition canvas.h:444
void DrawBackground(ImVec2 canvas_size=ImVec2(0, 0))
Definition canvas.cc:372
ImVec2 canvas_sz_
Definition canvas.h:431
void InitializeDefaults()
Definition canvas.cc:110
void EndTableCanvas()
Definition canvas.cc:302
std::unique_ptr< CanvasAutomationAPI > automation_api_
Definition canvas.h:404
std::shared_ptr< CanvasPerformanceIntegration > performance_integration_
Definition canvas.h:168
void SetAutoResize(bool auto_resize)
Definition canvas.h:239
void SetGlobalScale(float scale)
Definition canvas.h:314
void DrawGrid(float grid_step=64.0f, int tile_id_offset=8)
Definition canvas.cc:1304
void DrawContextMenuItem(const gui::CanvasMenuItem &item)
Definition canvas.cc:563
void DrawText(const std::string &text, int x, int y)
Definition canvas.cc:1252
ImVec2 drawn_tile_pos_
Definition canvas.h:434
std::vector< ImVec2 > selected_tiles_
Definition canvas.h:447
bool ApplyROMPalette(int group_index, int palette_index)
Definition canvas.cc:257
void ShowColorAnalysis()
Definition canvas.cc:251
void Close(const std::string &popup_id)
Close a persistent popup.
void RenderAll()
Render all active popups.
void Open(const std::string &popup_id, std::function< void()> render_callback)
Open a persistent popup.
BppFormat
BPP format enumeration for SNES graphics.
@ kBpp8
8 bits per pixel (256 colors)
std::unordered_map< int, gfx::Bitmap > BitmapTable
Definition bitmap.h:365
void ReserveCanvasSpace(ImVec2 canvas_size, const std::string &label)
void SetNextCanvasSize(ImVec2 size, bool auto_resize)
void DrawCanvasRect(ImDrawList *draw_list, ImVec2 canvas_p0, ImVec2 scrolling, int x, int y, int w, int h, ImVec4 color, float global_scale)
void DrawCanvasLabels(const CanvasRenderContext &ctx, const ImVector< ImVector< std::string > > &labels, int current_labels, int tile_id_offset)
void DrawCanvasOverlay(const CanvasRenderContext &ctx, const ImVector< ImVec2 > &points, const ImVector< ImVec2 > &selected_points)
ImVec2 CalculateMinimumCanvasSize(ImVec2 content_size, float global_scale, float padding)
void DrawCanvasOutline(ImDrawList *draw_list, ImVec2 canvas_p0, ImVec2 scrolling, int x, int y, int w, int h, uint32_t color)
void DrawCanvasOutlineWithColor(ImDrawList *draw_list, ImVec2 canvas_p0, ImVec2 scrolling, int x, int y, int w, int h, ImVec4 color)
void DrawCanvasGrid(const CanvasRenderContext &ctx, int highlight_tile_id)
ImVec2 CalculatePreferredCanvasSize(ImVec2 content_size, float global_scale, float min_scale)
void DrawCanvasText(ImDrawList *draw_list, ImVec2 canvas_p0, ImVec2 scrolling, const std::string &text, int x, int y, float global_scale)
void DrawCustomHighlight(ImDrawList *draw_list, ImVec2 canvas_p0, ImVec2 scrolling, int highlight_tile_id, float grid_step)
void DrawCanvasGridLines(ImDrawList *draw_list, ImVec2 canvas_p0, ImVec2 canvas_p1, ImVec2 scrolling, float grid_step, float global_scale)
ImVec2 AlignPosToGrid(ImVec2 pos, float scale)
Definition canvas.cc:102
Graphical User Interface (GUI) components for the application.
constexpr uint32_t kWhiteColor
Definition canvas.cc:96
constexpr uint32_t kRectangleColor
Definition canvas.cc:95
void BitmapCanvasPipeline(gui::Canvas &canvas, gfx::Bitmap &bitmap, int width, int height, int tile_size, bool is_loaded, bool scrollbar, int canvas_id)
Definition canvas.cc:1451
CanvasUsage
Canvas usage patterns and tracking.
void EndCanvas(Canvas &canvas)
Definition canvas.cc:1412
void BeginPadding(int i)
Definition style.cc:272
void BeginCanvas(Canvas &canvas, ImVec2 child_size)
Definition canvas.cc:1391
void GraphicsBinCanvasPipeline(int width, int height, int tile_size, int num_sheets_to_load, int canvas_id, bool is_loaded, gfx::BitmapTable &graphics_bin)
Definition canvas.cc:1418
void ApplyScrollDelta(CanvasGeometry &geometry, ImVec2 delta)
Apply scroll delta to geometry.
ImVec2 CalculateMouseInCanvas(const CanvasGeometry &geometry, ImVec2 mouse_screen_pos)
Calculate mouse position in canvas space.
void EndPadding()
Definition style.cc:276
void RenderCanvasBackground(ImDrawList *draw_list, const CanvasGeometry &geometry)
Render canvas background and border.
CanvasGeometry CalculateCanvasGeometry(const CanvasConfig &config, ImVec2 requested_size, ImVec2 cursor_screen_pos, ImVec2 content_region_avail)
Calculate canvas geometry from configuration and ImGui context.
CanvasGridSize
Definition canvas.h:46
void RenderMenuItem(const CanvasMenuItem &item, std::function< void(const std::string &, std::function< void()>)> popup_opened_callback)
Render a single menu item.
Definition canvas_menu.cc:6
void RenderBitmapOnCanvas(ImDrawList *draw_list, const CanvasGeometry &geometry, gfx::Bitmap &bitmap, int, float scale)
Render bitmap on canvas (border offset variant)
void TableCanvasPipeline(gui::Canvas &canvas, gfx::Bitmap &bitmap, const std::string &label, bool auto_resize)
Definition canvas.cc:1479
constexpr ImGuiButtonFlags kMouseFlags
Definition canvas.cc:98
int y
Y coordinate or height.
Definition tilemap.h:20
int x
X coordinate or width.
Definition tilemap.h:19
Tilemap structure for SNES tile-based graphics management.
Definition tilemap.h:109
Pair tile_size
Size of individual tiles (8x8 or 16x16)
Definition tilemap.h:113
Pair map_size
Size of tilemap in tiles.
Definition tilemap.h:114
Bitmap atlas
Master bitmap containing all tiles.
Definition tilemap.h:110
Unified configuration for canvas display and interaction.
std::function< void(const CanvasConfig &)> on_config_changed
std::function< void(const CanvasConfig &)> on_scale_changed
std::vector< CanvasMenuSection > sections
Declarative menu item definition.
Definition canvas_menu.h:63
Menu section grouping related menu items.
MenuSectionPriority priority
CanvasGeometry geometry