Update canvas system with enhanced interaction and performance tracking features

- Introduced a new CanvasInteractionHandler for managing tile interactions, including painting and selection modes.
- Added CanvasContextMenu for improved user interaction with context-specific options.
- Implemented CanvasPerformanceIntegration to monitor and report performance metrics during canvas operations.
- Developed CanvasUsageTracker to track user interactions and usage patterns within the canvas.
- Refactored existing canvas utilities and integrated new modal systems for enhanced user experience.
- Updated CMake configuration to include new canvas components and ensure proper linking with existing libraries.
This commit is contained in:
scawful
2025-09-30 13:10:32 -04:00
parent 9e0f614ce8
commit 991366113e
18 changed files with 4792 additions and 59 deletions

View File

@@ -70,6 +70,12 @@ void Canvas::InitializeDefaults() {
// Initialize palette editor
palette_editor_ = std::make_unique<EnhancedPaletteEditor>();
// Initialize interaction handler
interaction_handler_.Initialize(canvas_id_);
// Initialize enhanced components
InitializeEnhancedComponents();
// Initialize legacy compatibility variables to match config
enable_grid_ = config_.enable_grid;
enable_hex_tile_labels_ = config_.enable_hex_labels;
@@ -86,6 +92,93 @@ void Canvas::InitializeDefaults() {
void Canvas::Cleanup() {
palette_editor_.reset();
selection_.Clear();
// Stop performance monitoring before cleanup to prevent segfault
if (performance_integration_) {
performance_integration_->StopMonitoring();
}
// Cleanup enhanced components
modals_.reset();
context_menu_.reset();
usage_tracker_.reset();
performance_integration_.reset();
}
void Canvas::InitializeEnhancedComponents() {
// Initialize modals system
modals_ = std::make_unique<canvas::CanvasModals>();
// Initialize context menu system
context_menu_ = std::make_unique<canvas::CanvasContextMenu>();
context_menu_->Initialize(canvas_id_);
// Initialize usage tracker
usage_tracker_ = std::make_shared<canvas::CanvasUsageTracker>();
usage_tracker_->Initialize(canvas_id_);
canvas::CanvasUsageManager::Get().RegisterTracker(canvas_id_, usage_tracker_);
// Initialize performance integration
performance_integration_ = std::make_shared<canvas::CanvasPerformanceIntegration>();
performance_integration_->Initialize(canvas_id_);
performance_integration_->SetUsageTracker(usage_tracker_);
canvas::CanvasPerformanceManager::Get().RegisterIntegration(canvas_id_, performance_integration_);
// Start performance monitoring
performance_integration_->StartMonitoring();
usage_tracker_->StartSession();
}
void Canvas::SetUsageMode(canvas::CanvasUsage usage) {
if (usage_tracker_) {
usage_tracker_->SetUsageMode(usage);
}
if (context_menu_) {
context_menu_->SetUsageMode(usage);
}
}
canvas::CanvasUsage Canvas::GetUsageMode() const {
if (usage_tracker_) {
return usage_tracker_->GetCurrentStats().usage_mode;
}
return canvas::CanvasUsage::kUnknown;
}
void Canvas::RecordCanvasOperation(const std::string& operation_name, double time_ms) {
if (usage_tracker_) {
usage_tracker_->RecordOperation(operation_name, time_ms);
}
if (performance_integration_) {
performance_integration_->RecordOperation(operation_name, time_ms, GetUsageMode());
}
}
void Canvas::ShowPerformanceUI() {
if (performance_integration_) {
performance_integration_->RenderPerformanceUI();
}
}
void Canvas::ShowUsageReport() {
if (usage_tracker_) {
std::string report = usage_tracker_->ExportUsageReport();
// Show report in a modal or window
if (modals_) {
// Create a simple text display modal
ImGui::OpenPopup("Canvas Usage Report");
if (ImGui::BeginPopupModal("Canvas Usage Report", nullptr, ImGuiWindowFlags_AlwaysAutoResize)) {
ImGui::Text("Canvas Usage Report");
ImGui::Separator();
ImGui::TextWrapped("%s", report.c_str());
ImGui::Separator();
if (ImGui::Button("Close")) {
ImGui::CloseCurrentPopup();
}
ImGui::EndPopup();
}
}
}
}
void Canvas::InitializePaletteEditor(Rom* rom) {
@@ -174,6 +267,24 @@ ImVec2 Canvas::GetLastClickPosition() const {
return ImVec2(-1, -1); // Invalid position
}
// ==================== Modern ImGui-Style Interface ====================
void Canvas::Begin(ImVec2 canvas_size) {
// Modern ImGui-style begin - combines DrawBackground + DrawContextMenu
DrawBackground(canvas_size);
DrawContextMenu();
}
void Canvas::End() {
// Modern ImGui-style end - automatically draws grid and overlay
if (config_.enable_grid) {
DrawGrid();
}
DrawOverlay();
}
// ==================== Legacy Interface ====================
void Canvas::UpdateColorPainter(gfx::Bitmap &bitmap, const ImVec4 &color,
const std::function<void()> &event,
int tile_size, float scale) {
@@ -250,6 +361,119 @@ void Canvas::DrawContextMenu() {
const ImVec2 origin(canvas_p0_.x + scrolling_.x,
canvas_p0_.y + scrolling_.y); // Lock scrolled origin
const ImVec2 mouse_pos(io.MousePos.x - origin.x, io.MousePos.y - origin.y);
// Update canvas state for enhanced components
if (usage_tracker_) {
usage_tracker_->UpdateCanvasState(canvas_sz_, config_.content_size,
global_scale_, custom_step_,
enable_grid_, enable_hex_tile_labels_,
enable_custom_labels_);
}
// Use enhanced context menu if available
if (context_menu_) {
canvas::CanvasConfig snapshot;
snapshot.canvas_size = canvas_sz_;
snapshot.content_size = config_.content_size;
snapshot.global_scale = global_scale_;
snapshot.grid_step = custom_step_;
snapshot.enable_grid = enable_grid_;
snapshot.enable_hex_labels = enable_hex_tile_labels_;
snapshot.enable_custom_labels = enable_custom_labels_;
snapshot.enable_context_menu = enable_context_menu_;
snapshot.is_draggable = draggable_;
snapshot.auto_resize = config_.auto_resize;
snapshot.scrolling = scrolling_;
context_menu_->SetCanvasState(canvas_sz_, config_.content_size,
global_scale_, custom_step_, enable_grid_,
enable_hex_tile_labels_, enable_custom_labels_,
enable_context_menu_, draggable_,
config_.auto_resize, scrolling_);
context_menu_->Render(
context_id_, mouse_pos, bitmap_,
bitmap_ ? bitmap_->mutable_palette() : nullptr,
[this](canvas::CanvasContextMenu::Command command,
const canvas::CanvasConfig& updated_config) {
switch (command) {
case canvas::CanvasContextMenu::Command::kResetView:
ResetView();
break;
case canvas::CanvasContextMenu::Command::kZoomToFit:
if (bitmap_) {
SetZoomToFit(*bitmap_);
}
break;
case canvas::CanvasContextMenu::Command::kZoomIn:
SetGlobalScale(config_.global_scale * 1.25f);
break;
case canvas::CanvasContextMenu::Command::kZoomOut:
SetGlobalScale(config_.global_scale * 0.8f);
break;
case canvas::CanvasContextMenu::Command::kToggleGrid:
config_.enable_grid = !config_.enable_grid;
enable_grid_ = config_.enable_grid;
break;
case canvas::CanvasContextMenu::Command::kToggleHexLabels:
config_.enable_hex_labels = !config_.enable_hex_labels;
enable_hex_tile_labels_ = config_.enable_hex_labels;
break;
case canvas::CanvasContextMenu::Command::kToggleCustomLabels:
config_.enable_custom_labels = !config_.enable_custom_labels;
enable_custom_labels_ = config_.enable_custom_labels;
break;
case canvas::CanvasContextMenu::Command::kToggleContextMenu:
config_.enable_context_menu = !config_.enable_context_menu;
enable_context_menu_ = config_.enable_context_menu;
break;
case canvas::CanvasContextMenu::Command::kToggleAutoResize:
config_.auto_resize = !config_.auto_resize;
break;
case canvas::CanvasContextMenu::Command::kToggleDraggable:
config_.is_draggable = !config_.is_draggable;
draggable_ = config_.is_draggable;
break;
case canvas::CanvasContextMenu::Command::kSetGridStep:
config_.grid_step = updated_config.grid_step;
custom_step_ = config_.grid_step;
break;
case canvas::CanvasContextMenu::Command::kSetScale:
config_.global_scale = updated_config.global_scale;
global_scale_ = config_.global_scale;
break;
case canvas::CanvasContextMenu::Command::kOpenAdvancedProperties:
if (modals_) {
canvas::CanvasConfig modal_config = updated_config;
modal_config.on_config_changed =
[this](const canvas::CanvasConfig& cfg) { ApplyConfigSnapshot(cfg); };
modal_config.on_scale_changed =
[this](const canvas::CanvasConfig& cfg) { ApplyScaleSnapshot(cfg); };
modals_->ShowAdvancedProperties(canvas_id_, modal_config, bitmap_);
}
break;
case canvas::CanvasContextMenu::Command::kOpenScalingControls:
if (modals_) {
canvas::CanvasConfig modal_config = updated_config;
modal_config.on_config_changed =
[this](const canvas::CanvasConfig& cfg) { ApplyConfigSnapshot(cfg); };
modal_config.on_scale_changed =
[this](const canvas::CanvasConfig& cfg) { ApplyScaleSnapshot(cfg); };
modals_->ShowScalingControls(canvas_id_, modal_config, bitmap_);
}
break;
default:
break;
}
},
snapshot);
if (modals_) {
modals_->Render();
}
return;
}
static bool show_bitmap_data = false;
if (show_bitmap_data && bitmap_ != nullptr) {
@@ -461,8 +685,6 @@ void Canvas::ClearContextMenuItems() {
context_menu_items_.clear();
}
// Old ShowPaletteEditor method removed - now handled by EnhancedPaletteEditor
void Canvas::SetZoomToFit(const gfx::Bitmap& bitmap) {
if (!bitmap.is_active()) return;
@@ -486,6 +708,35 @@ void Canvas::ResetView() {
scrolling_ = ImVec2(0, 0);
}
void Canvas::ApplyConfigSnapshot(const canvas::CanvasConfig& snapshot) {
config_.enable_grid = snapshot.enable_grid;
config_.enable_hex_labels = snapshot.enable_hex_labels;
config_.enable_custom_labels = snapshot.enable_custom_labels;
config_.enable_context_menu = snapshot.enable_context_menu;
config_.is_draggable = snapshot.is_draggable;
config_.auto_resize = snapshot.auto_resize;
config_.grid_step = snapshot.grid_step;
config_.global_scale = snapshot.global_scale;
config_.canvas_size = snapshot.canvas_size;
config_.content_size = snapshot.content_size;
config_.custom_canvas_size = snapshot.canvas_size.x > 0 && snapshot.canvas_size.y > 0;
enable_grid_ = config_.enable_grid;
enable_hex_tile_labels_ = config_.enable_hex_labels;
enable_custom_labels_ = config_.enable_custom_labels;
enable_context_menu_ = config_.enable_context_menu;
draggable_ = config_.is_draggable;
custom_step_ = config_.grid_step;
global_scale_ = config_.global_scale;
scrolling_ = snapshot.scrolling;
}
void Canvas::ApplyScaleSnapshot(const canvas::CanvasConfig& snapshot) {
config_.global_scale = snapshot.global_scale;
global_scale_ = config_.global_scale;
scrolling_ = snapshot.scrolling;
}
bool Canvas::DrawTilePainter(const Bitmap &bitmap, int size, float scale) {
const ImGuiIO &io = GetIO();
const bool is_hovered = IsItemHovered();
@@ -913,7 +1164,7 @@ void Canvas::DrawBitmapGroup(std::vector<int> &group, gfx::Tilemap &tilemap,
}
// OPTIMIZATION: Use optimized rendering for large groups to improve performance
bool use_optimized_rendering = group.size() > 16; // Optimize for large selections
bool use_optimized_rendering = group.size() > 128; // Optimize for large selections
// Pre-calculate common values to avoid repeated computation
const float tile_scale = tile_size * scale;
@@ -954,8 +1205,7 @@ void Canvas::DrawBitmapGroup(std::vector<int> &group, gfx::Tilemap &tilemap,
int tile_id = group[i];
// Check if tile_id is within the range of tile16_individual_
auto tilemap_size =
tilemap.atlas.width() * tilemap.atlas.height() / tilemap.map_size.x;
auto tilemap_size = tilemap.map_size.x;
if (tile_id >= 0 && tile_id < tilemap_size) {
// Calculate the position of the tile within the rectangle
int tile_pos_x = (x + start_tile_x) * tile_size * scale;
@@ -1108,7 +1358,7 @@ void Canvas::DrawOverlay() {
.grid_step = config_.grid_step
};
// Use high-level utility function
// Use high-level utility function with local points (synchronized from interaction handler)
CanvasUtils::DrawCanvasOverlay(ctx, points_, selected_points_);
}
@@ -1266,6 +1516,36 @@ void TableCanvasPipeline(gui::Canvas &canvas, gfx::Bitmap &bitmap,
}
void Canvas::ShowAdvancedCanvasProperties() {
// Use the new modal system if available
if (modals_) {
canvas::CanvasConfig modal_config;
modal_config.canvas_size = canvas_sz_;
modal_config.content_size = config_.content_size;
modal_config.global_scale = global_scale_;
modal_config.grid_step = custom_step_;
modal_config.enable_grid = enable_grid_;
modal_config.enable_hex_labels = enable_hex_tile_labels_;
modal_config.enable_custom_labels = enable_custom_labels_;
modal_config.enable_context_menu = enable_context_menu_;
modal_config.is_draggable = draggable_;
modal_config.auto_resize = config_.auto_resize;
modal_config.scrolling = scrolling_;
modal_config.on_config_changed = [this](const canvas::CanvasConfig& updated_config) {
// Update legacy variables when config changes
enable_grid_ = updated_config.enable_grid;
enable_hex_tile_labels_ = updated_config.enable_hex_labels;
enable_custom_labels_ = updated_config.enable_custom_labels;
};
modal_config.on_scale_changed = [this](const canvas::CanvasConfig& updated_config) {
global_scale_ = updated_config.global_scale;
scrolling_ = updated_config.scrolling;
};
modals_->ShowAdvancedProperties(canvas_id_, modal_config, bitmap_);
return;
}
// Fallback to legacy modal system
if (ImGui::BeginPopupModal("Advanced Canvas Properties", nullptr, ImGuiWindowFlags_AlwaysAutoResize)) {
ImGui::Text("Advanced Canvas Configuration");
ImGui::Separator();
@@ -1346,6 +1626,39 @@ void Canvas::ShowAdvancedCanvasProperties() {
// Old ShowPaletteManager method removed - now handled by EnhancedPaletteEditor
void Canvas::ShowScalingControls() {
// Use the new modal system if available
if (modals_) {
canvas::CanvasConfig modal_config;
modal_config.canvas_size = canvas_sz_;
modal_config.content_size = config_.content_size;
modal_config.global_scale = global_scale_;
modal_config.grid_step = custom_step_;
modal_config.enable_grid = enable_grid_;
modal_config.enable_hex_labels = enable_hex_tile_labels_;
modal_config.enable_custom_labels = enable_custom_labels_;
modal_config.enable_context_menu = enable_context_menu_;
modal_config.is_draggable = draggable_;
modal_config.auto_resize = config_.auto_resize;
modal_config.scrolling = scrolling_;
modal_config.on_config_changed = [this](const canvas::CanvasConfig& updated_config) {
// Update legacy variables when config changes
enable_grid_ = updated_config.enable_grid;
enable_hex_tile_labels_ = updated_config.enable_hex_labels;
enable_custom_labels_ = updated_config.enable_custom_labels;
enable_context_menu_ = updated_config.enable_context_menu;
};
modal_config.on_scale_changed = [this](const canvas::CanvasConfig& updated_config) {
draggable_ = updated_config.is_draggable;
custom_step_ = updated_config.grid_step;
global_scale_ = updated_config.global_scale;
scrolling_ = updated_config.scrolling;
};
modals_->ShowScalingControls(canvas_id_, modal_config);
return;
}
// Fallback to legacy modal system
if (ImGui::BeginPopupModal("Scaling Controls", nullptr, ImGuiWindowFlags_AlwaysAutoResize)) {
ImGui::Text("Canvas Scaling and Display Controls");
ImGui::Separator();
@@ -1439,8 +1752,6 @@ void Canvas::ShowScalingControls() {
}
}
// Old ROM palette management methods removed - now handled by EnhancedPaletteEditor
// BPP format management methods
void Canvas::ShowBppFormatSelector() {
if (!bpp_format_ui_) {