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#include "util/i18n/tr.h"
3
4#include <algorithm>
5#include <cmath>
6#include <string>
7
11#include "app/gfx/core/bitmap.h"
18#include "app/gui/core/style.h"
19#include "imgui/imgui.h"
20
21namespace yaze::gui {
22
23// Define constructors and destructor in .cc to avoid incomplete type issues
24// with unique_ptr
25
26// Default constructor
27Canvas::Canvas() : renderer_(nullptr) {
29}
30
32 // The outer ImVector never runs its elements' destructors, so each nested
33 // ImVector<std::string>'s backing array leaks when labels_ is destroyed.
34 // clear() releases that array (flagged by AddressSanitizer/LeakSanitizer
35 // otherwise). Do NOT destroy the individual std::strings: labels are copied
36 // in via ImVector::operator= (a shallow memcpy), so their internal pointers
37 // are not independently owned and destroying them would be a bad free.
38 for (auto& label_group : labels_) {
39 label_group.clear();
40 }
41}
42
43Canvas::Canvas(const std::string& id) : Canvas() {
44 Init(id, ImVec2(0, 0));
45}
46
47Canvas::Canvas(const std::string& id, ImVec2 canvas_size) : Canvas() {
48 Init(id, canvas_size);
49}
50
51Canvas::Canvas(const std::string& id, ImVec2 canvas_size,
52 CanvasGridSize grid_size)
53 : Canvas() {
54 Init(id, canvas_size);
56}
57
58Canvas::Canvas(const std::string& id, ImVec2 canvas_size,
59 CanvasGridSize grid_size, float global_scale)
60 : Canvas() {
61 Init(id, canvas_size);
65}
66
70
71Canvas::Canvas(gfx::IRenderer* renderer, const std::string& id) : Canvas() {
73 Init(id, ImVec2(0, 0));
74}
75
76Canvas::Canvas(gfx::IRenderer* renderer, const std::string& id,
77 ImVec2 canvas_size)
78 : Canvas() {
80 Init(id, canvas_size);
81}
82
83Canvas::Canvas(gfx::IRenderer* renderer, const std::string& id,
84 ImVec2 canvas_size, CanvasGridSize grid_size)
85 : Canvas() {
87 Init(id, canvas_size);
89}
90
91Canvas::Canvas(gfx::IRenderer* renderer, const std::string& id,
92 ImVec2 canvas_size, CanvasGridSize grid_size, float global_scale)
93 : Canvas() {
95 Init(id, canvas_size);
99}
100
107
110 return;
111 }
113 return;
114 }
115 performance_integration_ = std::make_shared<CanvasPerformanceIntegration>();
118 performance_integration_->StartMonitoring();
119}
120
134
135void Canvas::Init(const std::string& id, ImVec2 canvas_size) {
136 canvas_id_ = id;
137 context_id_ = id + "Context";
138 if (canvas_size.x > 0 || canvas_size.y > 0) {
142 }
144}
145
147 if (!extensions_) {
148 extensions_ = std::make_unique<CanvasExtensions>();
149 }
150 return *extensions_;
151}
152
153using ImGui::GetContentRegionAvail;
154using ImGui::GetCursorScreenPos;
155using ImGui::GetIO;
156using ImGui::GetWindowDrawList;
157using ImGui::IsItemActive;
158using ImGui::IsItemHovered;
159using ImGui::IsMouseClicked;
160using ImGui::IsMouseDragging;
161using ImGui::Text;
162
163constexpr uint32_t kRectangleColor = IM_COL32(32, 32, 32, 255);
164constexpr uint32_t kWhiteColor = IM_COL32(255, 255, 255, 255);
165
166constexpr ImGuiButtonFlags kMouseFlags =
167 ImGuiButtonFlags_MouseButtonLeft | ImGuiButtonFlags_MouseButtonRight;
168
169namespace {
170ImVec2 AlignPosToGrid(ImVec2 pos, float scale) {
171 return ImVec2(std::floor(pos.x / scale) * scale,
172 std::floor(pos.y / scale) * scale);
173}
174} // namespace
175
176// Canvas class implementation begins here
177
179 // Initialize configuration with sensible defaults
180 config_.enable_grid = true;
184 config_.is_draggable = false;
185 config_.grid_step = 32.0f;
186 config_.global_scale = 1.0f;
187 config_.canvas_size = ImVec2(0, 0);
190
191 // Initialize selection state
193
194 // Note: palette_editor is now in CanvasExtensions (lazy-initialized)
195
196 // Initialize interaction handler
198
199 // Initialize enhanced components
201
202 // Initialize legacy compatibility variables to match config
213}
214
216 float old_scale = global_scale_;
217 global_scale_ += 0.25f;
219
220 // Publish zoom changed event
222 bus->Publish(
224 }
225}
226
228 float old_scale = global_scale_;
229 global_scale_ -= 0.25f;
231
232 // Publish zoom changed event
234 bus->Publish(
236 }
237}
238
239void Canvas::set_global_scale(float scale) {
240 float old_scale = global_scale_;
241 global_scale_ = scale;
242 config_.global_scale = scale;
243
244 // Publish zoom changed event only if scale actually changed
245 if (old_scale != scale) {
247 bus->Publish(
249 }
250 }
251}
252
254 // Cleanup extensions (if initialized)
255 if (extensions_) {
256 extensions_->Cleanup();
257 }
258 extensions_.reset();
259
261
262 // Stop performance monitoring before cleanup to prevent segfault
264 performance_integration_->StopMonitoring();
265 }
266
267 // Cleanup enhanced components (non-extension ones)
268 context_menu_.reset();
269 usage_tracker_.reset();
271}
272
276 points_.clear();
277 selected_tiles_.clear();
278 selected_points_.clear();
279 selected_tile_pos_ = ImVec2(-1, -1);
280 select_rect_active_ = false;
281 drawn_tile_pos_ = ImVec2(-1, -1);
282}
283
285 // Note: modals is now in CanvasExtensions (lazy-initialized on first use)
286
287 // Initialize context menu system
288 context_menu_ = std::make_unique<CanvasContextMenu>();
289 context_menu_->Initialize(canvas_id_);
290
291 // Initialize usage tracker (optional, controlled by config.enable_metrics)
293 usage_tracker_ = std::make_shared<CanvasUsageTracker>();
294 usage_tracker_->Initialize(canvas_id_);
295 usage_tracker_->StartSession();
296 // CanvasPerformanceIntegration is created lazily on first use
297 // (ShowPerformanceUI / RecordCanvasOperation) to avoid idle overhead.
298 }
299}
300
302 if (usage_tracker_) {
303 usage_tracker_->SetUsageMode(usage);
304 }
305 if (context_menu_) {
306 context_menu_->SetUsageMode(usage);
307 }
308 config_.usage_mode = usage;
309}
310
311void Canvas::RecordCanvasOperation(const std::string& operation_name,
312 double time_ms) {
313 if (usage_tracker_) {
314 usage_tracker_->RecordOperation(operation_name, time_ms);
315 }
318 performance_integration_->RecordOperation(operation_name, time_ms,
319 usage_mode());
320 }
321}
322
326 performance_integration_->RenderPerformanceUI();
327 }
328}
329
331 if (usage_tracker_) {
332 std::string report = usage_tracker_->ExportUsageReport();
333 // Show report in a modal or window (uses ImGui directly, no modals_ needed)
334 ImGui::OpenPopup("Canvas Usage Report");
335 if (ImGui::BeginPopupModal("Canvas Usage Report", nullptr,
336 ImGuiWindowFlags_AlwaysAutoResize)) {
337 ImGui::Text(tr("Canvas Usage Report"));
338 ImGui::Separator();
339 ImGui::TextWrapped("%s", report.c_str());
340 ImGui::Separator();
341 if (ImGui::Button(tr("Close"))) {
342 ImGui::CloseCurrentPopup();
343 }
344 ImGui::EndPopup();
345 }
346 }
347}
348
350 rom_ = rom;
351 auto& ext = EnsureExtensions();
352 ext.InitializePaletteEditor();
353 if (ext.palette_editor) {
354 ext.palette_editor->Initialize(rom);
355 }
356}
357
360 if (extensions_ && extensions_->palette_editor && game_data) {
361 extensions_->palette_editor->Initialize(game_data);
362 }
363}
364
366 if (bitmap_) {
367 auto& ext = EnsureExtensions();
368 ext.InitializePaletteEditor();
369 if (ext.palette_editor) {
370 auto mutable_palette = bitmap_->mutable_palette();
371 ext.palette_editor->ShowPaletteEditor(*mutable_palette,
372 "Canvas Palette Editor");
373 }
374 }
375}
376
378 if (bitmap_) {
379 auto& ext = EnsureExtensions();
380 ext.InitializePaletteEditor();
381 if (ext.palette_editor) {
382 ext.palette_editor->ShowColorAnalysis(*bitmap_, "Canvas Color Analysis");
383 }
384 }
385}
386
387bool Canvas::ApplyROMPalette(int group_index, int palette_index) {
388 if (bitmap_ && extensions_ && extensions_->palette_editor) {
389 return extensions_->palette_editor->ApplyROMPalette(bitmap_, group_index,
390 palette_index);
391 }
392 return false;
393}
394
395// Size reporting methods for table integration
400
405
406void Canvas::ReserveTableSpace(const std::string& label) {
409}
410
411bool Canvas::BeginTableCanvas(const std::string& label) {
412 if (config_.auto_resize) {
413 ImVec2 preferred_size = GetPreferredSize();
414 CanvasUtils::SetNextCanvasSize(preferred_size, true);
415 }
416
417 // Begin child window that properly reports size to tables
418 std::string child_id = canvas_id_ + "_TableChild";
419 ImVec2 child_size = config_.auto_resize ? ImVec2(0, 0) : config_.canvas_size;
420
421 // Use NoScrollbar - canvas handles its own scrolling via internal mechanism
422 bool result =
423 ImGui::BeginChild(child_id.c_str(), child_size,
424 true, // Always show border for table integration
425 ImGuiWindowFlags_NoScrollbar);
426
427 if (!label.empty()) {
428 ImGui::Text("%s", label.c_str());
429 }
430
431 return result;
432}
433
435 ImGui::EndChild();
436}
437
438CanvasRuntime Canvas::BeginInTable(const std::string& label,
439 const CanvasFrameOptions& options) {
440 // Calculate child size from options or auto-resize
441 ImVec2 child_size = options.canvas_size;
442 if (child_size.x <= 0 || child_size.y <= 0) {
444 }
445
446 if (config_.auto_resize && child_size.x > 0 && child_size.y > 0) {
447 CanvasUtils::SetNextCanvasSize(child_size, true);
448 }
449
450 // Begin child window for table integration
451 // Use NoScrollbar - canvas handles its own scrolling via internal mechanism
452 std::string child_id = canvas_id_ + "_TableChild";
453 ImGuiWindowFlags child_flags = ImGuiWindowFlags_NoScrollbar;
454 if (options.show_scrollbar) {
455 child_flags = ImGuiWindowFlags_AlwaysVerticalScrollbar;
456 }
457 ImGui::BeginChild(child_id.c_str(), child_size, true, child_flags);
458
459 if (!label.empty()) {
460 ImGui::Text("%s", label.c_str());
461 }
462
463 // Draw background and set up canvas state
464 Begin(options);
465
466 // Build and return runtime
468 if (options.grid_step.has_value()) {
469 rt.grid_step = options.grid_step.value();
470 }
471 return rt;
472}
473
475 const CanvasFrameOptions& options) {
476 // Draw grid if enabled
477 if (options.draw_grid) {
478 float step = options.grid_step.value_or(config_.grid_step);
479 DrawGrid(step);
480 }
481
482 // Draw overlay
483 if (options.draw_overlay) {
484 DrawOverlay();
485 }
486
487 // Render persistent popups if enabled
488 if (options.render_popups) {
490 }
491
492 ImGui::EndChild();
493}
494
495// Improved interaction detection methods
497 return !points_.empty() && points_.size() >= 2;
498}
499
500bool Canvas::WasClicked(ImGuiMouseButton button) const {
501 return ImGui::IsItemClicked(button) && HasValidSelection();
502}
503
504bool Canvas::WasDoubleClicked(ImGuiMouseButton button) const {
505 return ImGui::IsItemHovered() && ImGui::IsMouseDoubleClicked(button) &&
507}
508
510 if (HasValidSelection()) {
511 return points_[0]; // Return the first point of the selection
512 }
513 return ImVec2(-1, -1); // Invalid position
514}
515
516// ==================== Modern ImGui-Style Interface ====================
517
518void Canvas::Begin(ImVec2 canvas_size) {
519 // Modern ImGui-style begin - combines DrawBackground + DrawContextMenu
522}
523
525 // Modern ImGui-style end - automatically draws grid and overlay
526 if (config_.enable_grid) {
527 DrawGrid();
528 }
529 DrawOverlay();
530
531 // Render any persistent popups from context menu actions
533}
534
535void Canvas::Begin(const CanvasFrameOptions& options) {
537
538 // Only wrap in child window if explicitly requested
539 if (options.use_child_window) {
540 // Calculate effective size
541 ImVec2 effective_size = options.canvas_size;
542 if (effective_size.x == 0 && effective_size.y == 0) {
543 if (IsAutoResize()) {
544 effective_size = GetPreferredSize();
545 } else {
546 effective_size = GetCurrentSize();
547 }
548 }
549
550 ImGuiWindowFlags child_flags = ImGuiWindowFlags_None;
551 if (options.show_scrollbar) {
552 child_flags |= ImGuiWindowFlags_AlwaysVerticalScrollbar;
553 }
554 ImGui::BeginChild(canvas_id().c_str(), effective_size, true, child_flags);
555 }
556
557 // Apply grid step from options if specified
558 if (options.grid_step.has_value()) {
559 SetCustomGridStep(options.grid_step.value());
560 }
561
564
565 if (options.draw_context_menu) {
567 }
568}
569
570void Canvas::End(const CanvasFrameOptions& options) {
571 if (options.draw_grid) {
572 DrawGrid(options.grid_step.value_or(GetGridStep()));
573 }
574 if (options.draw_overlay) {
575 DrawOverlay();
576 }
577 if (options.render_popups) {
579 }
580 // Only end child if we started one
581 if (options.use_child_window) {
582 ImGui::EndChild();
583 }
584}
585
586// ==================== Legacy Interface ====================
587
589 gfx::Bitmap& bitmap, const ImVec4& color,
590 const std::function<void()>& event,
591 int tile_size, float scale) {
592 config_.global_scale = scale;
593 global_scale_ = scale; // Legacy compatibility
596 DrawBitmap(bitmap, 2, scale);
597 if (DrawSolidTilePainter(color, tile_size)) {
598 event();
599 bitmap.UpdateTexture();
600 }
601 DrawGrid();
602 DrawOverlay();
603}
604
605void Canvas::UpdateInfoGrid(ImVec2 bg_size, float grid_size, int label_id) {
607 enable_custom_labels_ = true; // Legacy compatibility
608 DrawBackground(bg_size);
609 DrawInfoGrid(grid_size, 8, label_id);
610 DrawOverlay();
611}
612
613void Canvas::DrawBackground(ImVec2 canvas_size) {
614 draw_list_ = GetWindowDrawList();
615
616 // Phase 1: Calculate geometry using new helper
618 config_, canvas_size, GetCursorScreenPos(), GetContentRegionAvail());
619
621
622 // Update config if explicit size provided
623 if (canvas_size.x != 0) {
625 }
626
627 // Phase 1: Render background using helper
629
630 ImGui::InvisibleButton(canvas_id_.c_str(), state_.geometry.scaled_size,
632
633 // CRITICAL FIX: Always update hover mouse position when hovering over canvas
634 // This fixes the regression where CheckForCurrentMap() couldn't track hover
635 // Phase 1: Use geometry helper for mouse calculation
636 if (IsItemHovered()) {
637 const ImGuiIO& io = GetIO();
640 state_.is_hovered = true;
641 is_hovered_ = true;
642 } else {
643 state_.is_hovered = false;
644 is_hovered_ = false;
645 }
646
647 // iOS/tablet gestures currently synthesize ImGui wheel deltas (see src/ios/main.mm).
648 // Consume them here to pan/zoom the canvas without triggering ImGui window scrolling.
649 if (LayoutHelpers::IsTouchDevice() && IsItemHovered()) {
650 ImGuiIO& io = GetIO();
651 const float wheel_x = io.MouseWheelH;
652 const float wheel_y = io.MouseWheel;
653
654 if (wheel_x != 0.0f || wheel_y != 0.0f) {
655 // Prevent parent windows/child regions from scrolling on touch gestures.
656 io.MouseWheelH = 0.0f;
657 io.MouseWheel = 0.0f;
658
659 if (io.KeyCtrl && wheel_y != 0.0f) {
660 // Ctrl+wheel: zoom (pinch on iOS).
661 constexpr float kMinScale = 0.25f;
662 constexpr float kMaxScale = 8.0f;
663 const float unclamped = global_scale_ * (1.0f + wheel_y);
664 const float new_scale = std::clamp(unclamped, kMinScale, kMaxScale);
665
666 if (new_scale != global_scale_) {
667 const ImVec2 new_scroll = ComputeScrollForZoomAtScreenPos(
668 state_.geometry, global_scale_, new_scale, io.MousePos);
669 set_global_scale(new_scale);
670 state_.geometry.scrolling = new_scroll;
673 }
674 } else {
675 // Plain wheel: pan (two-finger pan on iOS).
676 constexpr float kTouchWheelToPixels = 10.0f;
678 ImVec2(wheel_x * kTouchWheelToPixels,
679 wheel_y * kTouchWheelToPixels));
682 }
683 }
684 }
685
686 // Pan handling (Phase 1: Use geometry helper)
687 if (config_.is_draggable && IsItemHovered()) {
688 const ImGuiIO& io = GetIO();
689 const bool is_active = IsItemActive(); // Held
690
691 // Pan (we use a zero mouse threshold when there's no context menu)
692 if (const float mouse_threshold_for_pan =
693 enable_context_menu_ ? -1.0f : 0.0f;
694 is_active &&
695 IsMouseDragging(ImGuiMouseButton_Right, mouse_threshold_for_pan)) {
696 ApplyScrollDelta(state_.geometry, io.MouseDelta);
699 }
700 }
701}
702
704 const ImGuiIO& io = GetIO();
705 const ImVec2 scaled_sz(canvas_sz_.x * global_scale_,
707 const ImVec2 origin(canvas_p0_.x + scrolling_.x,
708 canvas_p0_.y + scrolling_.y); // Lock scrolled origin
709 const ImVec2 mouse_pos(io.MousePos.x - origin.x, io.MousePos.y - origin.y);
710
711 // Update canvas state for enhanced components
712 if (usage_tracker_) {
713 usage_tracker_->UpdateCanvasState(
716 }
717
718 // Use enhanced context menu if available
719 if (context_menu_) {
720 CanvasConfig snapshot;
721 snapshot.canvas_size = canvas_sz_;
723 snapshot.global_scale = global_scale_;
724 snapshot.grid_step = custom_step_;
725 snapshot.enable_grid = enable_grid_;
729 snapshot.is_draggable = draggable_;
731 snapshot.scrolling = scrolling_;
732
733 context_menu_->SetCanvasState(
737
738 context_menu_->Render(
739 context_id_, mouse_pos, rom_, bitmap_,
740 bitmap_ ? bitmap_->mutable_palette() : nullptr,
741 [this](CanvasContextMenu::Command command,
742 const CanvasConfig& updated_config) {
743 switch (command) {
745 ResetView();
746 break;
748 if (bitmap_) {
750 }
751 break;
754 break;
757 break;
761 break;
765 break;
769 break;
773 break;
776 break;
780 break;
782 config_.grid_step = updated_config.grid_step;
784 break;
786 config_.global_scale = updated_config.global_scale;
788 break;
790 auto& ext = EnsureExtensions();
791 ext.InitializeModals();
792 if (ext.modals) {
793 CanvasConfig modal_config = updated_config;
794 modal_config.on_config_changed =
795 [this](const CanvasConfig& cfg) {
797 };
798 modal_config.on_scale_changed =
799 [this](const CanvasConfig& cfg) {
801 };
802 ext.modals->ShowAdvancedProperties(canvas_id_, modal_config,
803 bitmap_);
804 }
805 } break;
807 auto& ext = EnsureExtensions();
808 ext.InitializeModals();
809 if (ext.modals) {
810 CanvasConfig modal_config = updated_config;
811 modal_config.on_config_changed =
812 [this](const CanvasConfig& cfg) {
814 };
815 modal_config.on_scale_changed =
816 [this](const CanvasConfig& cfg) {
818 };
819 ext.modals->ShowScalingControls(canvas_id_, modal_config,
820 bitmap_);
821 }
822 } break;
823 default:
824 break;
825 }
826 },
827 snapshot, this); // Phase 4: Pass Canvas* for editor menu integration
828
829 if (extensions_ && extensions_->modals) {
830 extensions_->modals->Render();
831 }
832
833 return;
834 }
835
836 // Draw enhanced property dialogs
839}
840
842 // Phase 4: Use RenderMenuItem from canvas_menu.h for consistent rendering
843 auto popup_callback = [this](const std::string& id,
844 std::function<void()> callback) {
845 popup_registry_.Open(id, callback);
846 };
847
848 gui::RenderMenuItem(item, popup_callback);
849}
850
852 // Phase 4: Add to editor menu definition
853 // Items are added to a default section with editor-specific priority
854 if (editor_menu_.sections.empty()) {
855 CanvasMenuSection section;
857 section.separator_after = true;
858 editor_menu_.sections.push_back(section);
859 }
860
861 // Add to the last section (or create new if the last isn't editor-specific)
862 auto& last_section = editor_menu_.sections.back();
863 if (last_section.priority != MenuSectionPriority::kEditorSpecific) {
864 CanvasMenuSection new_section;
866 new_section.separator_after = true;
867 editor_menu_.sections.push_back(new_section);
868 editor_menu_.sections.back().items.push_back(item);
869 } else {
870 last_section.items.push_back(item);
871 }
872}
873
877
878void Canvas::OpenPersistentPopup(const std::string& popup_id,
879 std::function<void()> render_callback) {
880 // Phase 4: Simplified popup management (no legacy synchronization)
881 popup_registry_.Open(popup_id, render_callback);
882}
883
884void Canvas::ClosePersistentPopup(const std::string& popup_id) {
885 // Phase 4: Simplified popup management (no legacy synchronization)
886 popup_registry_.Close(popup_id);
887}
888
890 // Phase 4: Simplified rendering (no legacy synchronization)
892}
893
895 if (!bitmap.is_active())
896 return;
897
898 ImVec2 available = ImGui::GetContentRegionAvail();
899 float scale_x = available.x / bitmap.width();
900 float scale_y = available.y / bitmap.height();
901 config_.global_scale = std::min(scale_x, scale_y);
902
903 // Ensure minimum readable scale
904 if (config_.global_scale < 0.25f)
905 config_.global_scale = 0.25f;
906
907 global_scale_ = config_.global_scale; // Legacy compatibility
908
909 // Center the view
910 scrolling_ = ImVec2(0, 0);
911}
912
914 config_.global_scale = 1.0f;
915 global_scale_ = 1.0f; // Legacy compatibility
916 scrolling_ = ImVec2(0, 0);
917 config_.scrolling = ImVec2(0, 0); // Sync config for persistence
918}
919
943
949
950bool Canvas::DrawTilePainter(const Bitmap& bitmap, int size, float scale) {
951 const ImGuiIO& io = GetIO();
952 const bool is_hovered = IsItemHovered();
953 is_hovered_ = is_hovered;
954 // Lock scrolled origin
955 const ImVec2 origin(canvas_p0_.x + scrolling_.x, canvas_p0_.y + scrolling_.y);
956 const ImVec2 mouse_pos(io.MousePos.x - origin.x, io.MousePos.y - origin.y);
957 const auto scaled_size = size * scale;
958
959 // Erase the hover when the mouse is not in the canvas window.
960 if (!is_hovered) {
961 points_.clear();
962 return false;
963 }
964
965 // Reset the previous tile hover
966 if (!points_.empty()) {
967 points_.clear();
968 }
969
970 // Calculate the coordinates of the mouse
971 ImVec2 paint_pos = AlignPosToGrid(mouse_pos, scaled_size);
972 mouse_pos_in_canvas_ = paint_pos;
973 auto paint_pos_end =
974 ImVec2(paint_pos.x + scaled_size, paint_pos.y + scaled_size);
975 points_.push_back(paint_pos);
976 points_.push_back(paint_pos_end);
977
978 if (bitmap.is_active()) {
979 draw_list_->AddImage((ImTextureID)(intptr_t)bitmap.texture(),
980 ImVec2(origin.x + paint_pos.x, origin.y + paint_pos.y),
981 ImVec2(origin.x + paint_pos.x + scaled_size,
982 origin.y + paint_pos.y + scaled_size));
983 }
984
985 if (IsMouseClicked(ImGuiMouseButton_Left) &&
986 ImGui::IsMouseDragging(ImGuiMouseButton_Left)) {
987 // Draw the currently selected tile on the overworld here
988 // Save the coordinates of the selected tile.
989 drawn_tile_pos_ = paint_pos;
990 return true;
991 }
992
993 return false;
994}
995
996bool Canvas::DrawTilemapPainter(gfx::Tilemap& tilemap, int current_tile) {
997 // Update hover state for backward compatibility
998 is_hovered_ = IsItemHovered();
999
1000 // Clear points if not hovered (legacy behavior)
1001 if (!is_hovered_) {
1002 points_.clear();
1003 return false;
1004 }
1005
1006 // Build runtime and delegate to stateless helper
1008 ImVec2 drawn_pos;
1009 bool result = gui::DrawTilemapPainter(rt, tilemap, current_tile, &drawn_pos);
1010
1011 // Sync legacy state from stateless call
1012 if (is_hovered_) {
1013 const ImGuiIO& io = GetIO();
1014 const ImVec2 origin(canvas_p0_.x + scrolling_.x,
1015 canvas_p0_.y + scrolling_.y);
1016 const ImVec2 mouse_pos(io.MousePos.x - origin.x, io.MousePos.y - origin.y);
1017 const float scaled_size = tilemap.tile_size.x * global_scale_;
1018 ImVec2 paint_pos = AlignPosToGrid(mouse_pos, scaled_size);
1019 mouse_pos_in_canvas_ = paint_pos;
1020
1021 points_.clear();
1022 points_.push_back(paint_pos);
1023 points_.push_back(
1024 ImVec2(paint_pos.x + scaled_size, paint_pos.y + scaled_size));
1025 }
1026
1027 if (result) {
1028 drawn_tile_pos_ = drawn_pos;
1029 }
1030
1031 return result;
1032}
1033
1034bool Canvas::DrawSolidTilePainter(const ImVec4& color, int tile_size) {
1035 const ImGuiIO& io = GetIO();
1036 const bool is_hovered = IsItemHovered();
1037 is_hovered_ = is_hovered;
1038 // Lock scrolled origin
1039 const ImVec2 origin(canvas_p0_.x + scrolling_.x, canvas_p0_.y + scrolling_.y);
1040 const ImVec2 mouse_pos(io.MousePos.x - origin.x, io.MousePos.y - origin.y);
1041 auto scaled_tile_size = tile_size * global_scale_;
1042 static bool is_dragging = false;
1043 static ImVec2 start_drag_pos;
1044
1045 // Erase the hover when the mouse is not in the canvas window.
1046 if (!is_hovered) {
1047 points_.clear();
1048 return false;
1049 }
1050
1051 // Reset the previous tile hover
1052 if (!points_.empty()) {
1053 points_.clear();
1054 }
1055
1056 // Calculate the coordinates of the mouse
1057 ImVec2 paint_pos = AlignPosToGrid(mouse_pos, scaled_tile_size);
1058 mouse_pos_in_canvas_ = paint_pos;
1059
1060 // Clamp the size to a grid
1061 paint_pos.x = std::clamp(paint_pos.x, 0.0f, canvas_sz_.x * global_scale_);
1062 paint_pos.y = std::clamp(paint_pos.y, 0.0f, canvas_sz_.y * global_scale_);
1063
1064 points_.push_back(paint_pos);
1065 points_.push_back(
1066 ImVec2(paint_pos.x + scaled_tile_size, paint_pos.y + scaled_tile_size));
1067
1068 draw_list_->AddRectFilled(
1069 ImVec2(origin.x + paint_pos.x + 1, origin.y + paint_pos.y + 1),
1070 ImVec2(origin.x + paint_pos.x + scaled_tile_size,
1071 origin.y + paint_pos.y + scaled_tile_size),
1072 IM_COL32(color.x * 255, color.y * 255, color.z * 255, 255));
1073
1074 if (IsMouseClicked(ImGuiMouseButton_Left)) {
1075 is_dragging = true;
1076 start_drag_pos = paint_pos;
1077 }
1078
1079 if (is_dragging && ImGui::IsMouseReleased(ImGuiMouseButton_Left)) {
1080 is_dragging = false;
1081 drawn_tile_pos_ = start_drag_pos;
1082 return true;
1083 }
1084
1085 return false;
1086}
1087
1088void Canvas::DrawTileOnBitmap(int tile_size, gfx::Bitmap* bitmap,
1089 ImVec4 color) {
1090 const ImVec2 position = drawn_tile_pos_;
1091 int tile_index_x = static_cast<int>(position.x / global_scale_) / tile_size;
1092 int tile_index_y = static_cast<int>(position.y / global_scale_) / tile_size;
1093
1094 ImVec2 start_position(tile_index_x * tile_size, tile_index_y * tile_size);
1095
1096 // Update the bitmap's pixel data based on the start_position and color
1097 for (int y = 0; y < tile_size; ++y) {
1098 for (int x = 0; x < tile_size; ++x) {
1099 // Calculate the actual pixel index in the bitmap
1100 int pixel_index =
1101 (start_position.y + y) * bitmap->width() + (start_position.x + x);
1102
1103 // Write the color to the pixel
1104 bitmap->WriteColor(pixel_index, color);
1105 }
1106 }
1107}
1108
1109bool Canvas::DrawTileSelector(int size, int size_y) {
1110 // Update hover state for backward compatibility
1111 is_hovered_ = IsItemHovered();
1112
1113 if (size_y == 0) {
1114 size_y = size;
1115 }
1116
1117 // Build runtime and delegate to stateless helper
1119 ImVec2 selected_pos;
1120 bool double_clicked = gui::DrawTileSelector(rt, size, size_y, &selected_pos);
1121
1122 // Sync legacy state: update points_ on click
1123 if (is_hovered_ && IsMouseClicked(ImGuiMouseButton_Left)) {
1124 const ImGuiIO& io = GetIO();
1125 const ImVec2 origin(canvas_p0_.x + scrolling_.x,
1126 canvas_p0_.y + scrolling_.y);
1127 const ImVec2 mouse_pos(io.MousePos.x - origin.x, io.MousePos.y - origin.y);
1128 ImVec2 painter_pos = AlignPosToGrid(mouse_pos, static_cast<float>(size));
1129
1130 points_.clear();
1131 points_.push_back(painter_pos);
1132 points_.push_back(ImVec2(painter_pos.x + size, painter_pos.y + size_y));
1133 mouse_pos_in_canvas_ = painter_pos;
1134 }
1135
1136 return double_clicked;
1137}
1138
1139void Canvas::DrawSelectRect(int current_map, int tile_size, float scale) {
1140 gfx::ScopedTimer timer("canvas_select_rect");
1141
1142 // Update hover state
1143 is_hovered_ = IsItemHovered();
1144 if (!is_hovered_) {
1145 return;
1146 }
1147
1148 // Build runtime and delegate to stateless helper
1150 rt.scale = scale; // Use the passed scale, not global_scale_
1151
1152 // Use a temporary selection to capture output from stateless helper
1153 CanvasSelection temp_selection;
1154 temp_selection.selected_tiles = selected_tiles_;
1155 temp_selection.selected_tile_pos = selected_tile_pos_;
1156 temp_selection.select_rect_active = select_rect_active_;
1157 for (int i = 0; i < selected_points_.size(); ++i) {
1158 temp_selection.selected_points.push_back(selected_points_[i]);
1159 }
1160
1161 gui::DrawSelectRect(rt, current_map, tile_size, scale, temp_selection);
1162
1163 // Sync back to legacy members
1164 selected_tiles_ = temp_selection.selected_tiles;
1165 selected_tile_pos_ = temp_selection.selected_tile_pos;
1166 select_rect_active_ = temp_selection.select_rect_active;
1167 selected_points_.clear();
1168 for (const auto& pt : temp_selection.selected_points) {
1169 selected_points_.push_back(pt);
1170 }
1171}
1172
1173void Canvas::DrawBitmap(Bitmap& bitmap, int border_offset, float scale) {
1174 if (!bitmap.is_active()) {
1175 return;
1176 }
1177 bitmap_ = &bitmap;
1178
1179 // Update content size for table integration
1180 config_.content_size = ImVec2(bitmap.width(), bitmap.height());
1181
1182 // Phase 1: Use rendering helper
1183 RenderBitmapOnCanvas(draw_list_, state_.geometry, bitmap, border_offset,
1184 scale);
1185}
1186
1187void Canvas::DrawBitmap(Bitmap& bitmap, int x_offset, int y_offset, float scale,
1188 int alpha) {
1189 if (!bitmap.is_active()) {
1190 return;
1191 }
1192 bitmap_ = &bitmap;
1193
1194 // Update content size for table integration
1195 // CRITICAL: Store UNSCALED bitmap size as content - scale is applied during
1196 // rendering
1197 config_.content_size = ImVec2(bitmap.width(), bitmap.height());
1198
1199 // Phase 1: Use rendering helper
1200 RenderBitmapOnCanvas(draw_list_, state_.geometry, bitmap, x_offset, y_offset,
1201 scale, alpha);
1202}
1203
1204void Canvas::DrawBitmap(Bitmap& bitmap, ImVec2 dest_pos, ImVec2 dest_size,
1205 ImVec2 src_pos, ImVec2 src_size) {
1206 if (!bitmap.is_active()) {
1207 return;
1208 }
1209 bitmap_ = &bitmap;
1210
1211 // Update content size for table integration
1212 config_.content_size = ImVec2(bitmap.width(), bitmap.height());
1213
1214 // Phase 1: Use rendering helper
1215 RenderBitmapOnCanvas(draw_list_, state_.geometry, bitmap, dest_pos, dest_size,
1216 src_pos, src_size);
1217}
1218
1219// TODO: Add parameters for sizing and positioning
1220void Canvas::DrawBitmapTable(const BitmapTable& gfx_bin) {
1221 for (const auto& [key, value] : gfx_bin) {
1222 // Skip null or inactive bitmaps without valid textures
1223 if (!value || !value->is_active() || !value->texture()) {
1224 continue;
1225 }
1226 int offset = 0x40 * (key + 1);
1227 int top_left_y = canvas_p0_.y + 2;
1228 if (key >= 1) {
1229 top_left_y = canvas_p0_.y + 0x40 * key;
1230 }
1231 draw_list_->AddImage((ImTextureID)(intptr_t)value->texture(),
1232 ImVec2(canvas_p0_.x + 2, top_left_y),
1233 ImVec2(canvas_p0_.x + 0x100, canvas_p0_.y + offset));
1234 }
1235}
1236
1237void Canvas::DrawOutline(int x, int y, int w, int h) {
1239 IM_COL32(255, 255, 255, 200));
1240}
1241
1242void Canvas::DrawOutlineWithColor(int x, int y, int w, int h, ImVec4 color) {
1244 y, w, h, color);
1245}
1246
1247void Canvas::DrawOutlineWithColor(int x, int y, int w, int h, uint32_t color) {
1249 color);
1250}
1251
1252void Canvas::DrawBitmapGroup(std::vector<int>& group, gfx::Tilemap& tilemap,
1253 int tile_size, float /*scale*/, int local_map_size,
1254 ImVec2 total_map_size) {
1255 if (selected_points_.size() != 2) {
1256 // points_ should contain exactly two points
1257 return;
1258 }
1259 if (group.empty()) {
1260 // group should not be empty
1261 return;
1262 }
1263
1264 // CRITICAL: Use config_.global_scale for consistency with DrawOverlay
1265 // which also uses config_.global_scale for the selection rectangle outline.
1266 // Using the passed 'scale' parameter would cause misalignment if they differ.
1267 const float effective_scale = config_.global_scale;
1268
1269 // OPTIMIZATION: Use optimized rendering for large groups to improve
1270 // performance
1271 bool use_optimized_rendering =
1272 group.size() > 128; // Optimize for large selections
1273
1274 // Use provided map sizes for proper boundary handling
1275 const int small_map = local_map_size;
1276 const float large_map_width = total_map_size.x;
1277 const float large_map_height = total_map_size.y;
1278
1279 // Pre-calculate common values to avoid repeated computation
1280 const float tile_scale = tile_size * effective_scale;
1281 const int atlas_tiles_per_row = tilemap.atlas.width() / tilemap.tile_size.x;
1282
1283 // Top-left and bottom-right corners of the rectangle (in world coordinates)
1284 ImVec2 rect_top_left = selected_points_[0];
1285 ImVec2 rect_bottom_right = selected_points_[1];
1286
1287 // Calculate the start and end tiles in the grid
1288 // selected_points are now in world coordinates, so divide by tile_size only
1289 int start_tile_x = static_cast<int>(std::floor(rect_top_left.x / tile_size));
1290 int start_tile_y = static_cast<int>(std::floor(rect_top_left.y / tile_size));
1291 int end_tile_x =
1292 static_cast<int>(std::floor(rect_bottom_right.x / tile_size));
1293 int end_tile_y =
1294 static_cast<int>(std::floor(rect_bottom_right.y / tile_size));
1295
1296 if (start_tile_x > end_tile_x)
1297 std::swap(start_tile_x, end_tile_x);
1298 if (start_tile_y > end_tile_y)
1299 std::swap(start_tile_y, end_tile_y);
1300
1301 // Calculate the size of the rectangle in 16x16 grid form
1302 int rect_width = (end_tile_x - start_tile_x) * tile_size;
1303 int rect_height = (end_tile_y - start_tile_y) * tile_size;
1304
1305 int tiles_per_row = rect_width / tile_size;
1306 int tiles_per_col = rect_height / tile_size;
1307
1308 int i = 0;
1309 for (int y = 0; y < tiles_per_col + 1; ++y) {
1310 for (int x = 0; x < tiles_per_row + 1; ++x) {
1311 // Check bounds to prevent access violations
1312 if (i >= static_cast<int>(group.size())) {
1313 break;
1314 }
1315
1316 int tile_id = group[i];
1317
1318 // Check if tile_id is within the range of tile16_individual_
1319 auto tilemap_size = tilemap.map_size.x;
1320 if (tile_id >= 0 && tile_id < tilemap_size) {
1321 // Calculate the position of the tile within the rectangle
1322 int tile_pos_x = (x + start_tile_x) * tile_size * effective_scale;
1323 int tile_pos_y = (y + start_tile_y) * tile_size * effective_scale;
1324
1325 // OPTIMIZATION: Use pre-calculated values for better performance with
1326 // large selections
1327 if (tilemap.atlas.is_active() && tilemap.atlas.texture() &&
1328 atlas_tiles_per_row > 0) {
1329 int atlas_tile_x =
1330 (tile_id % atlas_tiles_per_row) * tilemap.tile_size.x;
1331 int atlas_tile_y =
1332 (tile_id / atlas_tiles_per_row) * tilemap.tile_size.y;
1333
1334 // Simple bounds check
1335 if (atlas_tile_x >= 0 && atlas_tile_x < tilemap.atlas.width() &&
1336 atlas_tile_y >= 0 && atlas_tile_y < tilemap.atlas.height()) {
1337 // Calculate UV coordinates once for efficiency
1338 const float atlas_width = static_cast<float>(tilemap.atlas.width());
1339 const float atlas_height =
1340 static_cast<float>(tilemap.atlas.height());
1341 ImVec2 uv0 =
1342 ImVec2(atlas_tile_x / atlas_width, atlas_tile_y / atlas_height);
1343 ImVec2 uv1 =
1344 ImVec2((atlas_tile_x + tilemap.tile_size.x) / atlas_width,
1345 (atlas_tile_y + tilemap.tile_size.y) / atlas_height);
1346
1347 // Calculate screen positions
1348 float screen_x = canvas_p0_.x + scrolling_.x + tile_pos_x;
1349 float screen_y = canvas_p0_.y + scrolling_.y + tile_pos_y;
1350 float screen_w = tilemap.tile_size.x * effective_scale;
1351 float screen_h = tilemap.tile_size.y * effective_scale;
1352
1353 // Use higher alpha for large selections to make them more visible
1354 uint32_t alpha_color = use_optimized_rendering
1355 ? IM_COL32(255, 255, 255, 200)
1356 : IM_COL32(255, 255, 255, 150);
1357
1358 // Draw from atlas texture with optimized parameters
1359 draw_list_->AddImage(
1360 (ImTextureID)(intptr_t)tilemap.atlas.texture(),
1361 ImVec2(screen_x, screen_y),
1362 ImVec2(screen_x + screen_w, screen_y + screen_h), uv0, uv1,
1363 alpha_color);
1364 }
1365 }
1366 }
1367 i++;
1368 }
1369 // Break outer loop if we've run out of tiles
1370 if (i >= static_cast<int>(group.size())) {
1371 break;
1372 }
1373 }
1374
1375 // Performance optimization completed - tiles are now rendered with
1376 // pre-calculated values
1377
1378 // Reposition rectangle to follow mouse, but clamp to prevent wrapping across
1379 // map boundaries
1380 const ImGuiIO& io = GetIO();
1381 const ImVec2 origin(canvas_p0_.x + scrolling_.x, canvas_p0_.y + scrolling_.y);
1382 const ImVec2 mouse_pos(io.MousePos.x - origin.x, io.MousePos.y - origin.y);
1383
1384 // CRITICAL FIX: Clamp BEFORE grid alignment for smoother dragging behavior
1385 // This prevents the rectangle from even attempting to cross boundaries during
1386 // drag
1387 ImVec2 clamped_mouse_pos = mouse_pos;
1388
1390 // Calculate which local map the mouse is in
1391 int mouse_local_map_x = static_cast<int>(mouse_pos.x) / small_map;
1392 int mouse_local_map_y = static_cast<int>(mouse_pos.y) / small_map;
1393
1394 // Calculate where the rectangle END would be if we place it at mouse
1395 // position
1396 float potential_end_x = mouse_pos.x + rect_width;
1397 float potential_end_y = mouse_pos.y + rect_height;
1398
1399 // Check if this would cross local map boundary (512x512 blocks)
1400 int potential_end_map_x = static_cast<int>(potential_end_x) / small_map;
1401 int potential_end_map_y = static_cast<int>(potential_end_y) / small_map;
1402
1403 // Clamp mouse position to prevent crossing during drag
1404 if (potential_end_map_x != mouse_local_map_x) {
1405 // Would cross horizontal boundary - clamp mouse to safe zone
1406 float max_mouse_x = (mouse_local_map_x + 1) * small_map - rect_width;
1407 clamped_mouse_pos.x = std::min(mouse_pos.x, max_mouse_x);
1408 }
1409
1410 if (potential_end_map_y != mouse_local_map_y) {
1411 // Would cross vertical boundary - clamp mouse to safe zone
1412 float max_mouse_y = (mouse_local_map_y + 1) * small_map - rect_height;
1413 clamped_mouse_pos.y = std::min(mouse_pos.y, max_mouse_y);
1414 }
1415 }
1416
1417 // Now grid-align the clamped position (in screen coords)
1418 auto new_start_pos_screen =
1419 AlignPosToGrid(clamped_mouse_pos, tile_size * effective_scale);
1420
1421 // Convert to world coordinates for storage (selected_points_ stores world coords)
1422 ImVec2 new_start_pos_world(new_start_pos_screen.x / effective_scale,
1423 new_start_pos_screen.y / effective_scale);
1424
1425 // Additional safety: clamp to overall map bounds (in world coordinates)
1426 new_start_pos_world.x =
1427 std::clamp(new_start_pos_world.x, 0.0f, large_map_width - rect_width);
1428 new_start_pos_world.y =
1429 std::clamp(new_start_pos_world.y, 0.0f, large_map_height - rect_height);
1430
1431 selected_points_.clear();
1432 selected_points_.push_back(new_start_pos_world);
1433 selected_points_.push_back(ImVec2(new_start_pos_world.x + rect_width,
1434 new_start_pos_world.y + rect_height));
1435 select_rect_active_ = true;
1436}
1437
1438void Canvas::DrawRect(int x, int y, int w, int h, ImVec4 color) {
1440 color, config_.global_scale);
1441}
1442
1443void Canvas::DrawText(const std::string& text, int x, int y) {
1446}
1447
1452
1453void Canvas::DrawInfoGrid(float grid_step, int tile_id_offset, int label_id) {
1454 // Draw grid + all lines in the canvas
1455 draw_list_->PushClipRect(canvas_p0_, canvas_p1_, true);
1456 if (enable_grid_) {
1457 if (custom_step_ != 0.f)
1458 grid_step = custom_step_;
1459 grid_step *= global_scale_; // Apply global scale to grid step
1460
1461 DrawGridLines(grid_step);
1462 DrawCustomHighlight(grid_step);
1463
1464 if (!enable_custom_labels_) {
1465 return;
1466 }
1467
1468 // Draw the contents of labels on the grid
1469 for (float x = fmodf(scrolling_.x, grid_step);
1470 x < canvas_sz_.x * global_scale_; x += grid_step) {
1471 for (float y = fmodf(scrolling_.y, grid_step);
1472 y < canvas_sz_.y * global_scale_; y += grid_step) {
1473 int tile_x = (x - scrolling_.x) / grid_step;
1474 int tile_y = (y - scrolling_.y) / grid_step;
1475 int tile_id = tile_x + (tile_y * tile_id_offset);
1476
1477 if (tile_id >= labels_[label_id].size()) {
1478 break;
1479 }
1480 std::string label = labels_[label_id][tile_id];
1481 draw_list_->AddText(
1482 ImVec2(canvas_p0_.x + x + (grid_step / 2) - tile_id_offset,
1483 canvas_p0_.y + y + (grid_step / 2) - tile_id_offset),
1484 kWhiteColor, label.data());
1485 }
1486 }
1487 }
1488}
1489
1494
1495void Canvas::DrawGrid(float grid_step, int tile_id_offset) {
1496 if (config_.grid_step != 0.f)
1497 grid_step = config_.grid_step;
1498
1499 // Create render context for utilities
1502 .canvas_p0 = canvas_p0_,
1503 .canvas_p1 = canvas_p1_,
1504 .scrolling = scrolling_,
1505 .global_scale = config_.global_scale,
1506 .enable_grid = config_.enable_grid,
1507 .enable_hex_labels = config_.enable_hex_labels,
1508 .grid_step = grid_step};
1509
1510 // Use high-level utility function
1512
1513 // Draw custom labels if enabled
1515 draw_list_->PushClipRect(canvas_p0_, canvas_p1_, true);
1517 tile_id_offset);
1518 draw_list_->PopClipRect();
1519 }
1520}
1521
1523 // Create render context for utilities
1526 .canvas_p0 = canvas_p0_,
1527 .canvas_p1 = canvas_p1_,
1528 .scrolling = scrolling_,
1529 .global_scale = config_.global_scale,
1530 .enable_grid = config_.enable_grid,
1531 .enable_hex_labels = config_.enable_hex_labels,
1532 .grid_step = config_.grid_step};
1533
1534 // Use high-level utility function with local points (synchronized from
1535 // interaction handler)
1537
1538 // Render any persistent popups from context menu actions
1540}
1541
1543 // Based on ImGui demo, should be adapted to use for OAM
1544 ImDrawList* draw_list = ImGui::GetWindowDrawList();
1545 {
1546 Text(tr("Blue shape is drawn first: appears in back"));
1547 Text(tr("Red shape is drawn after: appears in front"));
1548 ImVec2 p0 = ImGui::GetCursorScreenPos();
1549 draw_list->AddRectFilled(ImVec2(p0.x, p0.y), ImVec2(p0.x + 50, p0.y + 50),
1550 IM_COL32(0, 0, 255, 255)); // Blue
1551 draw_list->AddRectFilled(ImVec2(p0.x + 25, p0.y + 25),
1552 ImVec2(p0.x + 75, p0.y + 75),
1553 IM_COL32(255, 0, 0, 255)); // Red
1554 ImGui::Dummy(ImVec2(75, 75));
1555 }
1556 ImGui::Separator();
1557 {
1558 Text(tr("Blue shape is drawn first, into channel 1: appears in front"));
1559 Text(tr("Red shape is drawn after, into channel 0: appears in back"));
1560 ImVec2 p1 = ImGui::GetCursorScreenPos();
1561
1562 // Create 2 channels and draw a Blue shape THEN a Red shape.
1563 // You can create any number of channels. Tables API use 1 channel per
1564 // column in order to better batch draw calls.
1565 draw_list->ChannelsSplit(2);
1566 draw_list->ChannelsSetCurrent(1);
1567 draw_list->AddRectFilled(ImVec2(p1.x, p1.y), ImVec2(p1.x + 50, p1.y + 50),
1568 IM_COL32(0, 0, 255, 255)); // Blue
1569 draw_list->ChannelsSetCurrent(0);
1570 draw_list->AddRectFilled(ImVec2(p1.x + 25, p1.y + 25),
1571 ImVec2(p1.x + 75, p1.y + 75),
1572 IM_COL32(255, 0, 0, 255)); // Red
1573
1574 // Flatten/reorder channels. Red shape is in channel 0 and it appears
1575 // below the Blue shape in channel 1. This works by copying draw indices
1576 // only (vertices are not copied).
1577 draw_list->ChannelsMerge();
1578 ImGui::Dummy(ImVec2(75, 75));
1579 Text(
1580 tr("After reordering, contents of channel 0 appears below channel 1."));
1581 }
1582}
1583
1585 // Use the new modal system (lazy-initialized via extensions)
1586 auto& ext = EnsureExtensions();
1587 ext.InitializeModals();
1588 if (ext.modals) {
1589 CanvasConfig modal_config;
1590 modal_config.canvas_size = canvas_sz_;
1591 modal_config.content_size = config_.content_size;
1592 modal_config.global_scale = global_scale_;
1593 modal_config.grid_step = custom_step_;
1594 modal_config.enable_grid = enable_grid_;
1598 modal_config.is_draggable = draggable_;
1599 modal_config.auto_resize = config_.auto_resize;
1600 modal_config.scrolling = scrolling_;
1601 modal_config.on_config_changed =
1602 [this](const CanvasConfig& updated_config) {
1603 // Update legacy variables when config changes
1604 enable_grid_ = updated_config.enable_grid;
1605 enable_hex_tile_labels_ = updated_config.enable_hex_labels;
1606 enable_custom_labels_ = updated_config.enable_custom_labels;
1607 };
1608 modal_config.on_scale_changed = [this](const CanvasConfig& updated_config) {
1609 global_scale_ = updated_config.global_scale;
1610 scrolling_ = updated_config.scrolling;
1611 };
1612
1613 ext.modals->ShowAdvancedProperties(canvas_id_, modal_config, bitmap_);
1614 return;
1615 }
1616
1617 // Fallback to legacy modal system
1618 if (ImGui::BeginPopupModal("Advanced Canvas Properties", nullptr,
1619 ImGuiWindowFlags_AlwaysAutoResize)) {
1620 ImGui::Text(tr("Advanced Canvas Configuration"));
1621 ImGui::Separator();
1622
1623 // Canvas properties (read-only info)
1624 ImGui::Text(tr("Canvas Properties"));
1625 ImGui::Text(tr("ID: %s"), canvas_id_.c_str());
1626 ImGui::Text(tr("Canvas Size: %.0f x %.0f"), config_.canvas_size.x,
1628 ImGui::Text(tr("Content Size: %.0f x %.0f"), config_.content_size.x,
1630 ImGui::Text(tr("Global Scale: %.3f"), config_.global_scale);
1631 ImGui::Text(tr("Grid Step: %.1f"), config_.grid_step);
1632
1633 if (config_.content_size.x > 0 && config_.content_size.y > 0) {
1634 ImVec2 min_size = GetMinimumSize();
1635 ImVec2 preferred_size = GetPreferredSize();
1636 ImGui::Text(tr("Minimum Size: %.0f x %.0f"), min_size.x, min_size.y);
1637 ImGui::Text(tr("Preferred Size: %.0f x %.0f"), preferred_size.x,
1638 preferred_size.y);
1639 }
1640
1641 // Editable properties using new config system
1642 ImGui::Separator();
1643 ImGui::Text(tr("View Settings"));
1644 if (ImGui::Checkbox(tr("Enable Grid"), &config_.enable_grid)) {
1645 enable_grid_ = config_.enable_grid; // Legacy sync
1646 }
1647 if (ImGui::Checkbox(tr("Enable Hex Labels"), &config_.enable_hex_labels)) {
1649 }
1650 if (ImGui::Checkbox(tr("Enable Custom Labels"),
1653 }
1654 if (ImGui::Checkbox(tr("Enable Context Menu"),
1657 }
1658 if (ImGui::Checkbox(tr("Draggable"), &config_.is_draggable)) {
1659 draggable_ = config_.is_draggable; // Legacy sync
1660 }
1661 if (ImGui::Checkbox(tr("Auto Resize for Tables"), &config_.auto_resize)) {
1662 // Auto resize setting changed
1663 }
1664
1665 // Grid controls
1666 ImGui::Separator();
1667 ImGui::Text(tr("Grid Configuration"));
1668 if (ImGui::SliderFloat(tr("Grid Step"), &config_.grid_step, 1.0f, 128.0f,
1669 "%.1f")) {
1670 custom_step_ = config_.grid_step; // Legacy sync
1671 }
1672
1673 // Scale controls
1674 ImGui::Separator();
1675 ImGui::Text(tr("Scale Configuration"));
1676 if (ImGui::SliderFloat(tr("Global Scale"), &config_.global_scale, 0.1f,
1677 10.0f, "%.2f")) {
1678 global_scale_ = config_.global_scale; // Legacy sync
1679 }
1680
1681 // Scrolling controls
1682 ImGui::Separator();
1683 ImGui::Text(tr("Scrolling Configuration"));
1684 ImGui::Text(tr("Current Scroll: %.1f, %.1f"), scrolling_.x, scrolling_.y);
1685 if (ImGui::Button(tr("Reset Scroll"))) {
1686 scrolling_ = ImVec2(0, 0);
1687 }
1688 ImGui::SameLine();
1689 if (ImGui::Button(tr("Center View"))) {
1690 if (bitmap_) {
1691 scrolling_ = ImVec2(
1693 2.0f,
1695 config_.canvas_size.y) /
1696 2.0f);
1697 }
1698 }
1699
1700 if (ImGui::Button(tr("Close"))) {
1701 ImGui::CloseCurrentPopup();
1702 }
1703 ImGui::EndPopup();
1704 }
1705}
1706
1707// Old ShowPaletteManager method removed - now handled by PaletteWidget
1708
1710 // Use the new modal system (lazy-initialized via extensions)
1711 auto& ext = EnsureExtensions();
1712 ext.InitializeModals();
1713 if (ext.modals) {
1714 CanvasConfig modal_config;
1715 modal_config.canvas_size = canvas_sz_;
1716 modal_config.content_size = config_.content_size;
1717 modal_config.global_scale = global_scale_;
1718 modal_config.grid_step = custom_step_;
1719 modal_config.enable_grid = enable_grid_;
1723 modal_config.is_draggable = draggable_;
1724 modal_config.auto_resize = config_.auto_resize;
1725 modal_config.scrolling = scrolling_;
1726 modal_config.on_config_changed =
1727 [this](const CanvasConfig& updated_config) {
1728 // Update legacy variables when config changes
1729 enable_grid_ = updated_config.enable_grid;
1730 enable_hex_tile_labels_ = updated_config.enable_hex_labels;
1731 enable_custom_labels_ = updated_config.enable_custom_labels;
1732 enable_context_menu_ = updated_config.enable_context_menu;
1733 };
1734 modal_config.on_scale_changed = [this](const CanvasConfig& updated_config) {
1735 draggable_ = updated_config.is_draggable;
1736 custom_step_ = updated_config.grid_step;
1737 global_scale_ = updated_config.global_scale;
1738 scrolling_ = updated_config.scrolling;
1739 };
1740
1741 ext.modals->ShowScalingControls(canvas_id_, modal_config);
1742 return;
1743 }
1744
1745 // Fallback to legacy modal system
1746 if (ImGui::BeginPopupModal("Scaling Controls", nullptr,
1747 ImGuiWindowFlags_AlwaysAutoResize)) {
1748 ImGui::Text(tr("Canvas Scaling and Display Controls"));
1749 ImGui::Separator();
1750
1751 // Global scale with new config system
1752 ImGui::Text(tr("Global Scale: %.3f"), config_.global_scale);
1753 if (ImGui::SliderFloat("##GlobalScale", &config_.global_scale, 0.1f, 10.0f,
1754 "%.2f")) {
1755 global_scale_ = config_.global_scale; // Legacy sync
1756 }
1757
1758 // Preset scale buttons
1759 ImGui::Text(tr("Preset Scales:"));
1760 if (ImGui::Button(tr("0.25x"))) {
1761 config_.global_scale = 0.25f;
1763 }
1764 ImGui::SameLine();
1765 if (ImGui::Button(tr("0.5x"))) {
1766 config_.global_scale = 0.5f;
1768 }
1769 ImGui::SameLine();
1770 if (ImGui::Button(tr("1x"))) {
1771 config_.global_scale = 1.0f;
1773 }
1774 ImGui::SameLine();
1775 if (ImGui::Button(tr("2x"))) {
1776 config_.global_scale = 2.0f;
1778 }
1779 ImGui::SameLine();
1780 if (ImGui::Button(tr("4x"))) {
1781 config_.global_scale = 4.0f;
1783 }
1784 ImGui::SameLine();
1785 if (ImGui::Button(tr("8x"))) {
1786 config_.global_scale = 8.0f;
1788 }
1789
1790 // Grid configuration
1791 ImGui::Separator();
1792 ImGui::Text(tr("Grid Configuration"));
1793 ImGui::Text(tr("Grid Step: %.1f"), config_.grid_step);
1794 if (ImGui::SliderFloat("##GridStep", &config_.grid_step, 1.0f, 128.0f,
1795 "%.1f")) {
1796 custom_step_ = config_.grid_step; // Legacy sync
1797 }
1798
1799 // Grid size presets
1800 ImGui::Text(tr("Grid Presets:"));
1801 if (ImGui::Button(tr("8x8"))) {
1802 config_.grid_step = 8.0f;
1804 }
1805 ImGui::SameLine();
1806 if (ImGui::Button(tr("16x16"))) {
1807 config_.grid_step = 16.0f;
1809 }
1810 ImGui::SameLine();
1811 if (ImGui::Button(tr("32x32"))) {
1812 config_.grid_step = 32.0f;
1814 }
1815 ImGui::SameLine();
1816 if (ImGui::Button(tr("64x64"))) {
1817 config_.grid_step = 64.0f;
1819 }
1820
1821 // Canvas size info
1822 ImGui::Separator();
1823 ImGui::Text(tr("Canvas Information"));
1824 ImGui::Text(tr("Canvas Size: %.0f x %.0f"), config_.canvas_size.x,
1826 ImGui::Text(tr("Scaled Size: %.0f x %.0f"),
1829 if (bitmap_) {
1830 ImGui::Text(tr("Bitmap Size: %d x %d"), bitmap_->width(),
1831 bitmap_->height());
1832 ImGui::Text(
1833 tr("Effective Scale: %.3f x %.3f"),
1836 }
1837
1838 if (ImGui::Button(tr("Close"))) {
1839 ImGui::CloseCurrentPopup();
1840 }
1841 ImGui::EndPopup();
1842 }
1843}
1844
1845// BPP format management methods
1847 auto& ext = EnsureExtensions();
1848 ext.InitializeBppUI(canvas_id_);
1849
1850 if (bitmap_ && ext.bpp_format_ui) {
1851 ext.bpp_format_ui->RenderFormatSelector(
1853 [this](gfx::BppFormat format) { ConvertBitmapFormat(format); });
1854 }
1855}
1856
1858 auto& ext = EnsureExtensions();
1859 ext.InitializeBppUI(canvas_id_);
1860
1861 if (bitmap_ && ext.bpp_format_ui) {
1862 ext.bpp_format_ui->RenderAnalysisPanel(*bitmap_, bitmap_->palette());
1863 }
1864}
1865
1867 auto& ext = EnsureExtensions();
1868 if (!ext.bpp_conversion_dialog) {
1869 ext.bpp_conversion_dialog = std::make_unique<gui::BppConversionDialog>(
1870 canvas_id_ + "_bpp_conversion");
1871 }
1872
1873 if (bitmap_ && ext.bpp_conversion_dialog) {
1874 ext.bpp_conversion_dialog->Show(
1875 *bitmap_, bitmap_->palette(),
1876 [this](gfx::BppFormat format, bool /*preserve_palette*/) {
1877 ConvertBitmapFormat(format);
1878 });
1879 }
1880
1881 if (ext.bpp_conversion_dialog) {
1882 ext.bpp_conversion_dialog->Render();
1883 }
1884}
1885
1887 if (!bitmap_)
1888 return false;
1889
1890 gfx::BppFormat current_format = GetCurrentBppFormat();
1891 if (current_format == target_format) {
1892 return true; // No conversion needed
1893 }
1894
1895 try {
1896 // Convert the bitmap data
1897 auto converted_data = gfx::BppFormatManager::Get().ConvertFormat(
1898 bitmap_->vector(), current_format, target_format, bitmap_->width(),
1899 bitmap_->height());
1900
1901 // Update the bitmap with converted data
1902 bitmap_->set_data(converted_data);
1903
1904 // Update the renderer
1906
1907 return true;
1908 } catch (const std::exception& e) {
1909 SDL_Log("Failed to convert bitmap format: %s", e.what());
1910 return false;
1911 }
1912}
1913
1921
1922// Phase 4A: Canvas Automation API
1924 auto& ext = EnsureExtensions();
1925 ext.InitializeAutomation(this);
1926 return ext.automation_api.get();
1927}
1928
1929// =============================================================================
1930// Canvas::AddXxxAt Methods
1931// =============================================================================
1932
1933void Canvas::AddImageAt(ImTextureID texture, ImVec2 local_top_left,
1934 ImVec2 size) {
1935 if (draw_list_ == nullptr)
1936 return;
1937 ImVec2 screen_pos(canvas_p0_.x + local_top_left.x * global_scale_,
1938 canvas_p0_.y + local_top_left.y * global_scale_);
1939 ImVec2 screen_end(screen_pos.x + size.x * global_scale_,
1940 screen_pos.y + size.y * global_scale_);
1941 draw_list_->AddImage(texture, screen_pos, screen_end);
1942}
1943
1944void Canvas::AddRectFilledAt(ImVec2 local_top_left, ImVec2 size,
1945 uint32_t color) {
1946 if (draw_list_ == nullptr)
1947 return;
1948 ImVec2 screen_pos(canvas_p0_.x + local_top_left.x * global_scale_,
1949 canvas_p0_.y + local_top_left.y * global_scale_);
1950 ImVec2 screen_end(screen_pos.x + size.x * global_scale_,
1951 screen_pos.y + size.y * global_scale_);
1952 draw_list_->AddRectFilled(screen_pos, screen_end, color);
1953}
1954
1955void Canvas::AddTextAt(ImVec2 local_pos, const std::string& text,
1956 uint32_t color) {
1957 if (draw_list_ == nullptr)
1958 return;
1959 ImVec2 screen_pos(canvas_p0_.x + local_pos.x * global_scale_,
1960 canvas_p0_.y + local_pos.y * global_scale_);
1961 draw_list_->AddText(screen_pos, color, text.c_str());
1962}
1963
1964// =============================================================================
1965// CanvasFrame RAII Class
1966// =============================================================================
1967
1969 : canvas_(&canvas), options_(options), active_(true) {
1971}
1972
1974 if (active_) {
1976 }
1977}
1978
1980 : canvas_(other.canvas_), options_(other.options_), active_(other.active_) {
1981 other.active_ = false;
1982}
1983
1985 if (this != &other) {
1986 if (active_) {
1987 canvas_->End(options_);
1988 }
1989 canvas_ = other.canvas_;
1990 options_ = other.options_;
1991 active_ = other.active_;
1992 other.active_ = false;
1993 }
1994 return *this;
1995}
1996
1997} // namespace yaze::gui
The Rom class is used to load, save, and modify Rom data. This is a generic SNES ROM container and do...
Definition rom.h:28
Represents a bitmap image optimized for SNES ROM hacking.
Definition bitmap.h:67
const SnesPalette & palette() const
Definition bitmap.h:389
TextureHandle texture() const
Definition bitmap.h:401
const std::vector< uint8_t > & vector() const
Definition bitmap.h:402
bool is_active() const
Definition bitmap.h:405
SnesPalette * mutable_palette()
Definition bitmap.h:390
void WriteColor(int position, const ImVec4 &color)
Write a color to a pixel at the given position.
Definition bitmap.cc:643
int height() const
Definition bitmap.h:395
void set_data(const std::vector< uint8_t > &data)
Definition bitmap.cc:864
int width() const
Definition bitmap.h:394
void UpdateTexture()
Updates the underlying SDL_Texture when it already exists.
Definition bitmap.cc:307
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:60
RAII timer for automatic timing management.
Programmatic interface for controlling canvas operations.
Lightweight RAII guard for existing Canvas instances.
Definition canvas.h:709
CanvasFrameOptions options_
Definition canvas.h:726
CanvasFrame & operator=(const CanvasFrame &)=delete
CanvasFrame(Canvas &canvas, CanvasFrameOptions options=CanvasFrameOptions())
Definition canvas.cc:1968
void ClearState()
Clear all interaction state.
void Initialize(const std::string &canvas_id)
Initialize the interaction handler.
Modern, robust canvas for drawing and manipulating graphics.
Definition canvas.h:64
ImVec2 scrolling_
Definition canvas.h:533
CanvasState state_
Definition canvas.h:506
ImVector< ImVec2 > points_
Definition canvas.h:543
int highlight_tile_id
Definition canvas.h:521
void DrawBitmap(Bitmap &bitmap, int border_offset, float scale)
Definition canvas.cc:1173
PopupRegistry popup_registry_
Definition canvas.h:517
Rom * rom() const
Definition canvas.h:465
std::string canvas_id_
Definition canvas.h:547
void ShowScalingControls()
Definition canvas.cc:1709
bool WasDoubleClicked(ImGuiMouseButton button=ImGuiMouseButton_Left) const
Definition canvas.cc:504
CanvasConfig config_
Definition canvas.h:496
ImVec2 selected_tile_pos_
Definition canvas.h:553
auto global_scale() const
Definition canvas.h:399
ImVec2 canvas_p1_
Definition canvas.h:536
void ShowBppAnalysis()
Definition canvas.cc:1857
gfx::IRenderer * renderer() const
Definition canvas.h:96
void DrawOutlineWithColor(int x, int y, int w, int h, ImVec4 color)
Definition canvas.cc:1242
void SetUsageMode(CanvasUsage usage)
Definition canvas.cc:301
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:1252
bool BeginTableCanvas(const std::string &label="")
Definition canvas.cc:411
void InitializeEnhancedComponents()
Definition canvas.cc:284
CanvasRuntime BuildCurrentRuntime() const
Definition canvas.h:480
void ShowBppConversionDialog()
Definition canvas.cc:1866
CanvasAutomationAPI * GetAutomationAPI()
Definition canvas.cc:1923
void ShowAdvancedCanvasProperties()
Definition canvas.cc:1584
void ApplyScaleSnapshot(const CanvasConfig &snapshot)
Definition canvas.cc:944
void UpdateInfoGrid(ImVec2 bg_size, float grid_size=64.0f, int label_id=0)
Definition canvas.cc:605
void DrawContextMenu()
Definition canvas.cc:703
void EnsurePerformanceIntegration()
Definition canvas.cc:108
ImVec2 mouse_pos_in_canvas_
Definition canvas.h:538
bool DrawTilemapPainter(gfx::Tilemap &tilemap, int current_tile)
Definition canvas.cc:996
bool DrawSolidTilePainter(const ImVec4 &color, int size)
Definition canvas.cc:1034
bool enable_context_menu_
Definition canvas.h:560
auto draw_list() const
Definition canvas.h:349
CanvasMenuDefinition editor_menu_
Definition canvas.h:513
void ApplyConfigSnapshot(const CanvasConfig &snapshot)
Definition canvas.cc:920
void DrawLayeredElements()
Definition canvas.cc:1542
void ReserveTableSpace(const std::string &label="")
Definition canvas.cc:406
bool enable_custom_labels_
Definition canvas.h:559
void AddTextAt(ImVec2 local_pos, const std::string &text, uint32_t color)
Definition canvas.cc:1955
void ShowUsageReport()
Definition canvas.cc:330
void SetRenderer(gfx::IRenderer *renderer)
Definition canvas.h:95
ImVec2 GetMinimumSize() const
Definition canvas.cc:396
void AddRectFilledAt(ImVec2 local_top_left, ImVec2 size, uint32_t color)
Definition canvas.cc:1944
bool DrawTileSelector(int size, int size_y=0)
Definition canvas.cc:1109
void ClearSelection()
Definition canvas.cc:273
bool ConvertBitmapFormat(gfx::BppFormat target_format)
Definition canvas.cc:1886
void DrawGridLines(float grid_step)
Definition canvas.cc:1448
void SetCustomGridStep(float step)
Definition canvas.h:118
void ShowPerformanceUI()
Definition canvas.cc:323
zelda3::GameData * game_data() const
Definition canvas.h:467
bool custom_canvas_size_
Definition canvas.h:561
void ClearContextMenuItems()
Definition canvas.cc:874
void AddImageAt(ImTextureID texture, ImVec2 local_top_left, ImVec2 size)
Definition canvas.cc:1933
void SetGameData(zelda3::GameData *game_data)
Definition canvas.cc:358
void DrawRect(int x, int y, int w, int h, ImVec4 color)
Definition canvas.cc:1438
bool HasValidSelection() const
Definition canvas.cc:496
bool DrawTilePainter(const Bitmap &bitmap, int size, float scale=1.0f)
Definition canvas.cc:950
ImDrawList * draw_list_
Definition canvas.h:530
ImVector< ImVec2 > selected_points_
Definition canvas.h:552
ImVec2 GetCurrentSize() const
Definition canvas.h:276
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:588
void DrawTileOnBitmap(int tile_size, gfx::Bitmap *bitmap, ImVec4 color)
Definition canvas.cc:1088
void DrawCustomHighlight(float grid_step)
Definition canvas.cc:1490
bool select_rect_active_
Definition canvas.h:554
Bitmap * bitmap_
Definition canvas.h:527
std::unique_ptr< CanvasExtensions > extensions_
Definition canvas.h:502
CanvasGridSize grid_size() const
Definition canvas.h:126
void AddContextMenuItem(const gui::CanvasMenuItem &item)
Definition canvas.cc:851
ImVec2 GetPreferredSize() const
Definition canvas.cc:401
float GetGridStep() const
Definition canvas.h:382
CanvasInteractionHandler interaction_handler_
Definition canvas.h:197
void InitializePaletteEditor(Rom *rom)
Definition canvas.cc:349
void Begin(ImVec2 canvas_size=ImVec2(0, 0))
Begin canvas rendering (ImGui-style)
Definition canvas.cc:518
auto canvas_size() const
Definition canvas.h:359
void SetZoomToFit(const gfx::Bitmap &bitmap)
Definition canvas.cc:894
bool WasClicked(ImGuiMouseButton button=ImGuiMouseButton_Left) const
Definition canvas.cc:500
ImVector< ImVector< std::string > > labels_
Definition canvas.h:544
gfx::BppFormat GetCurrentBppFormat() const
Definition canvas.cc:1914
auto canvas_id() const
Definition canvas.h:406
void ClosePersistentPopup(const std::string &popup_id)
Definition canvas.cc:884
void ShowBppFormatSelector()
Definition canvas.cc:1846
void RecordCanvasOperation(const std::string &operation_name, double time_ms)
Definition canvas.cc:311
void RenderPersistentPopups()
Definition canvas.cc:889
void set_global_scale(float scale)
Definition canvas.cc:239
void SetGridSize(CanvasGridSize grid_size)
Definition canvas.h:99
bool IsAutoResize() const
Definition canvas.h:278
std::shared_ptr< CanvasUsageTracker > usage_tracker_
Definition canvas.h:195
void End()
End canvas rendering (ImGui-style)
Definition canvas.cc:524
void EndInTable(CanvasRuntime &runtime, const CanvasFrameOptions &options)
Definition canvas.cc:474
void DrawSelectRect(int current_map, int tile_size=0x10, float scale=1.0f)
Definition canvas.cc:1139
std::unique_ptr< CanvasContextMenu > context_menu_
Definition canvas.h:194
auto usage_mode() const
Definition canvas.h:252
ImVec2 GetLastClickPosition() const
Definition canvas.cc:509
zelda3::GameData * game_data_
Definition canvas.h:529
void ShowPaletteEditor()
Definition canvas.cc:365
float global_scale_
Definition canvas.h:556
void DrawOutline(int x, int y, int w, int h)
Definition canvas.cc:1237
float custom_step_
Definition canvas.h:555
void DrawInfoGrid(float grid_step=64.0f, int tile_id_offset=8, int label_id=0)
Definition canvas.cc:1453
CanvasSelection selection_
Definition canvas.h:497
CanvasRuntime BeginInTable(const std::string &label, const CanvasFrameOptions &options)
Begin canvas in table cell with frame options (modern API) Returns CanvasRuntime for stateless helper...
Definition canvas.cc:438
bool enable_hex_tile_labels_
Definition canvas.h:558
ImVec2 canvas_p0_
Definition canvas.h:535
void OpenPersistentPopup(const std::string &popup_id, std::function< void()> render_callback)
Definition canvas.cc:878
void DrawBitmapTable(const BitmapTable &gfx_bin)
Definition canvas.cc:1220
std::string context_id_
Definition canvas.h:548
void DrawBackground(ImVec2 canvas_size=ImVec2(0, 0))
Definition canvas.cc:613
ImVec2 canvas_sz_
Definition canvas.h:534
void InitializeDefaults()
Definition canvas.cc:178
void EndTableCanvas()
Definition canvas.cc:434
std::shared_ptr< CanvasPerformanceIntegration > performance_integration_
Definition canvas.h:196
void Init(const CanvasConfig &config)
Initialize canvas with configuration (post-construction) Preferred over constructor parameters for ne...
Definition canvas.cc:121
void SetGlobalScale(float scale)
Definition canvas.h:380
void DrawGrid(float grid_step=64.0f, int tile_id_offset=8)
Definition canvas.cc:1495
void DrawContextMenuItem(const gui::CanvasMenuItem &item)
Definition canvas.cc:841
void DrawText(const std::string &text, int x, int y)
Definition canvas.cc:1443
ImVec2 drawn_tile_pos_
Definition canvas.h:537
void SyncLegacyGeometryFromState()
Definition canvas.cc:101
CanvasExtensions & EnsureExtensions()
Definition canvas.cc:146
std::vector< ImVec2 > selected_tiles_
Definition canvas.h:551
bool ApplyROMPalette(int group_index, int palette_index)
Definition canvas.cc:387
void ShowColorAnalysis()
Definition canvas.cc:377
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.
::yaze::EventBus * event_bus()
Get the current EventBus instance.
BppFormat
BPP format enumeration for SNES graphics.
@ kBpp8
8 bits per pixel (256 colors)
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:170
Graphical User Interface (GUI) components for the application.
constexpr uint32_t kWhiteColor
Definition canvas.cc:164
constexpr uint32_t kRectangleColor
Definition canvas.cc:163
CanvasUsage
Canvas usage patterns and tracking.
void BeginPadding(int i)
Definition style.cc:277
bool DrawTileSelector(const CanvasRuntime &rt, int size, int size_y, ImVec2 *out_selected_pos)
bool DrawTilemapPainter(const CanvasRuntime &rt, gfx::Tilemap &tilemap, int current_tile, ImVec2 *out_drawn_pos)
void ApplyScrollDelta(CanvasGeometry &geometry, ImVec2 delta)
Apply scroll delta to geometry.
ImVec2 ComputeScrollForZoomAtScreenPos(const CanvasGeometry &geometry, float old_scale, float new_scale, ImVec2 mouse_screen_pos)
Compute new scroll offset to keep a canvas point locked under the mouse.
ImVec2 CalculateMouseInCanvas(const CanvasGeometry &geometry, ImVec2 mouse_screen_pos)
Calculate mouse position in canvas space.
void EndPadding()
Definition style.cc:281
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.
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)
constexpr ImGuiButtonFlags kMouseFlags
Definition canvas.cc:166
void DrawSelectRect(const CanvasRuntime &rt, int current_map, int tile_size, float scale, CanvasSelection &selection)
static ZoomChangedEvent Create(const std::string &src, float old_z, float new_z, size_t session=0)
int y
Y coordinate or height.
Definition tilemap.h:21
int x
X coordinate or width.
Definition tilemap.h:20
Tilemap structure for SNES tile-based graphics management.
Definition tilemap.h:118
Pair tile_size
Size of individual tiles (8x8 or 16x16)
Definition tilemap.h:123
Pair map_size
Size of tilemap in tiles.
Definition tilemap.h:124
Bitmap atlas
Master bitmap containing all tiles.
Definition tilemap.h:119
Unified configuration for canvas display and interaction.
std::function< void(const CanvasConfig &) on_config_changed)
std::function< void(const CanvasConfig &) on_scale_changed)
Optional extension modules for Canvas.
std::optional< float > grid_step
std::vector< CanvasMenuSection > sections
Declarative menu item definition.
Definition canvas_menu.h:64
Menu section grouping related menu items.
MenuSectionPriority priority
Selection state for canvas interactions.
std::vector< ImVec2 > selected_tiles
std::vector< ImVec2 > selected_points
CanvasGeometry geometry