#include "canvas_modals.h" #include #include #include #include "app/gfx/performance_profiler.h" #include "app/gfx/performance_dashboard.h" #include "app/gui/enhanced_palette_editor.h" #include "app/gui/bpp_format_ui.h" #include "app/gui/icons.h" #include "imgui/imgui.h" namespace yaze { namespace gui { namespace canvas { namespace { void DispatchConfigCallback(const std::function& callback, const CanvasConfig& config) { if (callback) { callback(config); } } void DispatchScaleCallback(const std::function& callback, const CanvasConfig& config) { if (callback) { callback(config); } } } // namespace void CanvasModals::ShowAdvancedProperties(const std::string& canvas_id, const CanvasConfig& config, const gfx::Bitmap* bitmap) { std::string modal_id = canvas_id + "_advanced_properties"; auto render_func = [=]() mutable { CanvasConfig mutable_config = config; // Create mutable copy mutable_config.on_config_changed = config.on_config_changed; mutable_config.on_scale_changed = config.on_scale_changed; RenderAdvancedPropertiesModal(modal_id, mutable_config, bitmap); }; OpenModal(modal_id, render_func); } void CanvasModals::ShowScalingControls(const std::string& canvas_id, const CanvasConfig& config, const gfx::Bitmap* bitmap) { std::string modal_id = canvas_id + "_scaling_controls"; auto render_func = [=]() mutable { CanvasConfig mutable_config = config; // Create mutable copy mutable_config.on_config_changed = config.on_config_changed; mutable_config.on_scale_changed = config.on_scale_changed; RenderScalingControlsModal(modal_id, mutable_config, bitmap); }; OpenModal(modal_id, render_func); } void CanvasModals::ShowBppConversionDialog(const std::string& canvas_id, const BppConversionOptions& options) { std::string modal_id = canvas_id + "_bpp_conversion"; auto render_func = [=]() { RenderBppConversionModal(modal_id, options); }; OpenModal(modal_id, render_func); } void CanvasModals::ShowPaletteEditor(const std::string& canvas_id, const PaletteEditorOptions& options) { std::string modal_id = canvas_id + "_palette_editor"; auto render_func = [=]() { RenderPaletteEditorModal(modal_id, options); }; OpenModal(modal_id, render_func); } void CanvasModals::ShowColorAnalysis(const std::string& canvas_id, const ColorAnalysisOptions& options) { std::string modal_id = canvas_id + "_color_analysis"; auto render_func = [=]() { RenderColorAnalysisModal(modal_id, options); }; OpenModal(modal_id, render_func); } void CanvasModals::ShowPerformanceIntegration(const std::string& canvas_id, const PerformanceOptions& options) { std::string modal_id = canvas_id + "_performance"; auto render_func = [=]() { RenderPerformanceModal(modal_id, options); }; OpenModal(modal_id, render_func); } void CanvasModals::Render() { for (auto& modal : active_modals_) { if (modal.is_open) { modal.render_func(); } } // Remove closed modals active_modals_.erase( std::remove_if(active_modals_.begin(), active_modals_.end(), [](const ModalState& modal) { return !modal.is_open; }), active_modals_.end()); } bool CanvasModals::IsAnyModalOpen() const { return std::any_of(active_modals_.begin(), active_modals_.end(), [](const ModalState& modal) { return modal.is_open; }); } void CanvasModals::RenderAdvancedPropertiesModal(const std::string& canvas_id, CanvasConfig& config, const gfx::Bitmap* bitmap) { std::string modal_title = "Advanced Canvas Properties"; ImGui::SetNextWindowSize(ImVec2(600, 500), ImGuiCond_FirstUseEver); if (ImGui::BeginPopupModal(modal_title.c_str(), nullptr, ImGuiWindowFlags_AlwaysAutoResize)) { // Header with icon ImGui::Text("%s %s", ICON_MD_SETTINGS, modal_title.c_str()); ImGui::Separator(); // Canvas Information Section if (ImGui::CollapsingHeader(ICON_MD_ANALYTICS " Canvas Information", ImGuiTreeNodeFlags_DefaultOpen)) { ImGui::Columns(2, "CanvasInfo"); RenderMetricCard("Canvas Size", std::to_string(static_cast(config.canvas_size.x)) + " x " + std::to_string(static_cast(config.canvas_size.y)), ICON_MD_STRAIGHTEN, ImVec4(0.2F, 0.8F, 1.0F, 1.0F)); RenderMetricCard("Content Size", std::to_string(static_cast(config.content_size.x)) + " x " + std::to_string(static_cast(config.content_size.y)), ICON_MD_IMAGE, ImVec4(0.8F, 0.2F, 1.0F, 1.0F)); ImGui::NextColumn(); RenderMetricCard("Global Scale", std::to_string(static_cast(config.global_scale * 100)) + "%", ICON_MD_ZOOM_IN, ImVec4(1.0F, 0.8F, 0.2F, 1.0F)); RenderMetricCard("Grid Step", std::to_string(static_cast(config.grid_step)) + "px", ICON_MD_GRID_ON, ImVec4(0.2F, 1.0F, 0.2F, 1.0F)); ImGui::Columns(1); } // View Settings Section if (ImGui::CollapsingHeader("👁️ View Settings", ImGuiTreeNodeFlags_DefaultOpen)) { ImGui::Checkbox("Show Grid", &config.enable_grid); ImGui::SameLine(); RenderMaterialIcon("grid_on"); ImGui::Checkbox("Show Hex Labels", &config.enable_hex_labels); ImGui::SameLine(); RenderMaterialIcon("label"); ImGui::Checkbox("Show Custom Labels", &config.enable_custom_labels); ImGui::SameLine(); RenderMaterialIcon("edit"); ImGui::Checkbox("Enable Context Menu", &config.enable_context_menu); ImGui::SameLine(); RenderMaterialIcon("menu"); ImGui::Checkbox("Draggable Canvas", &config.is_draggable); ImGui::SameLine(); RenderMaterialIcon("drag_indicator"); ImGui::Checkbox("Auto Resize for Tables", &config.auto_resize); ImGui::SameLine(); RenderMaterialIcon("fit_screen"); } // Scale Controls Section if (ImGui::CollapsingHeader(ICON_MD_BUILD " Scale Controls", ImGuiTreeNodeFlags_DefaultOpen)) { RenderSliderWithIcon("Global Scale", "zoom_in", &config.global_scale, 0.1f, 10.0f, "%.2f"); RenderSliderWithIcon("Grid Step", "grid_on", &config.grid_step, 1.0f, 128.0f, "%.1f"); // Preset scale buttons ImGui::Text("Preset Scales:"); ImGui::SameLine(); const char* preset_labels[] = {"0.25x", "0.5x", "1x", "2x", "4x", "8x"}; const float preset_values[] = {0.25f, 0.5f, 1.0f, 2.0f, 4.0f, 8.0f}; for (int i = 0; i < 6; ++i) { if (i > 0) ImGui::SameLine(); if (ImGui::Button(preset_labels[i])) { config.global_scale = preset_values[i]; DispatchConfigCallback(config.on_config_changed, config); } } } // Scrolling Controls Section if (ImGui::CollapsingHeader("📜 Scrolling Controls")) { ImGui::Text("Current Scroll: %.1f, %.1f", config.scrolling.x, config.scrolling.y); if (ImGui::Button("Reset Scroll")) { config.scrolling = ImVec2(0, 0); DispatchConfigCallback(config.on_config_changed, config); } ImGui::SameLine(); if (ImGui::Button("Center View") && bitmap) { config.scrolling = ImVec2(-(bitmap->width() * config.global_scale - config.canvas_size.x) / 2.0f, -(bitmap->height() * config.global_scale - config.canvas_size.y) / 2.0f); DispatchConfigCallback(config.on_config_changed, config); } } // Performance Integration Section if (ImGui::CollapsingHeader(ICON_MD_TRENDING_UP " Performance")) { auto& profiler = gfx::PerformanceProfiler::Get(); // Get stats for canvas operations auto canvas_stats = profiler.GetStats("canvas_operations"); auto draw_stats = profiler.GetStats("canvas_draw"); RenderMetricCard("Canvas Operations", std::to_string(canvas_stats.sample_count) + " ops", "speed", ImVec4(0.2F, 1.0F, 0.2F, 1.0F)); RenderMetricCard("Average Time", std::to_string(draw_stats.avg_time_us / 1000.0) + " ms", "timer", ImVec4(1.0F, 0.8F, 0.2F, 1.0F)); if (ImGui::Button("Open Performance Dashboard")) { gfx::PerformanceDashboard::Get().SetVisible(true); } } // Action Buttons ImGui::Separator(); ImGui::Spacing(); if (ImGui::Button("Apply Changes", ImVec2(120, 0))) { DispatchConfigCallback(config.on_config_changed, config); ImGui::CloseCurrentPopup(); } ImGui::SameLine(); if (ImGui::Button("Cancel", ImVec2(120, 0))) { ImGui::CloseCurrentPopup(); } ImGui::SameLine(); if (ImGui::Button("Reset to Defaults", ImVec2(150, 0))) { config.global_scale = 1.0f; config.grid_step = 32.0f; config.enable_grid = true; config.enable_hex_labels = false; config.enable_custom_labels = false; config.enable_context_menu = true; config.is_draggable = false; config.auto_resize = false; config.scrolling = ImVec2(0, 0); DispatchConfigCallback(config.on_config_changed, config); } ImGui::EndPopup(); } } void CanvasModals::RenderScalingControlsModal(const std::string& canvas_id, CanvasConfig& config, const gfx::Bitmap* bitmap) { std::string modal_title = "Canvas Scaling Controls"; ImGui::SetNextWindowSize(ImVec2(500, 400), ImGuiCond_FirstUseEver); if (ImGui::BeginPopupModal(modal_title.c_str(), nullptr, ImGuiWindowFlags_AlwaysAutoResize)) { // Header with icon ImGui::Text("%s %s", ICON_MD_ZOOM_IN, modal_title.c_str()); ImGui::Separator(); // Global Scale Section ImGui::Text("Global Scale: %.3f", config.global_scale); RenderSliderWithIcon("##GlobalScale", "zoom_in", &config.global_scale, 0.1f, 10.0f, "%.2f"); // Preset scale buttons ImGui::Text("Preset Scales:"); const char* preset_labels[] = {"0.25x", "0.5x", "1x", "2x", "4x", "8x"}; const float preset_values[] = {0.25f, 0.5f, 1.0f, 2.0f, 4.0f, 8.0f}; for (int i = 0; i < 6; ++i) { if (i > 0) ImGui::SameLine(); if (ImGui::Button(preset_labels[i])) { config.global_scale = preset_values[i]; DispatchScaleCallback(config.on_scale_changed, config); } } ImGui::Separator(); // Grid Configuration Section ImGui::Text("Grid Step: %.1f", config.grid_step); RenderSliderWithIcon("##GridStep", "grid_on", &config.grid_step, 1.0f, 128.0f, "%.1f"); // Grid size presets ImGui::Text("Grid Presets:"); const char* grid_labels[] = {"8x8", "16x16", "32x32", "64x64"}; const float grid_values[] = {8.0f, 16.0f, 32.0f, 64.0f}; for (int i = 0; i < 4; ++i) { if (i > 0) ImGui::SameLine(); if (ImGui::Button(grid_labels[i])) { config.grid_step = grid_values[i]; DispatchScaleCallback(config.on_scale_changed, config); } } ImGui::Separator(); // Canvas Information Section ImGui::Text("Canvas Information"); ImGui::Text("Canvas Size: %.0f x %.0f", config.canvas_size.x, config.canvas_size.y); ImGui::Text("Scaled Size: %.0f x %.0f", config.canvas_size.x * config.global_scale, config.canvas_size.y * config.global_scale); if (bitmap) { ImGui::Text("Bitmap Size: %d x %d", bitmap->width(), bitmap->height()); ImGui::Text("Effective Scale: %.3f x %.3f", (config.canvas_size.x * config.global_scale) / bitmap->width(), (config.canvas_size.y * config.global_scale) / bitmap->height()); } // Action Buttons ImGui::Separator(); ImGui::Spacing(); if (ImGui::Button("Apply", ImVec2(120, 0))) { DispatchScaleCallback(config.on_scale_changed, config); ImGui::CloseCurrentPopup(); } ImGui::SameLine(); if (ImGui::Button("Cancel", ImVec2(120, 0))) { ImGui::CloseCurrentPopup(); } ImGui::EndPopup(); } } void CanvasModals::RenderBppConversionModal(const std::string& canvas_id, const BppConversionOptions& options) { std::string modal_title = "BPP Format Conversion"; ImGui::SetNextWindowSize(ImVec2(600, 500), ImGuiCond_FirstUseEver); if (ImGui::BeginPopupModal(modal_title.c_str(), nullptr, ImGuiWindowFlags_AlwaysAutoResize)) { // Header with icon ImGui::Text("%s %s", ICON_MD_SWAP_HORIZ, modal_title.c_str()); ImGui::Separator(); // Use the existing BppFormatUI for the conversion dialog static std::unique_ptr bpp_ui = std::make_unique(canvas_id + "_bpp_ui"); // Render the format selector if (options.bitmap && options.palette) { bpp_ui->RenderFormatSelector(const_cast(options.bitmap), *options.palette, options.on_convert); } // Action Buttons ImGui::Separator(); ImGui::Spacing(); if (ImGui::Button("Close", ImVec2(120, 0))) { ImGui::CloseCurrentPopup(); } ImGui::EndPopup(); } } void CanvasModals::RenderPaletteEditorModal(const std::string& canvas_id, const PaletteEditorOptions& options) { std::string modal_title = options.title.empty() ? "Palette Editor" : options.title; ImGui::SetNextWindowSize(ImVec2(800, 600), ImGuiCond_FirstUseEver); if (ImGui::BeginPopupModal(modal_title.c_str(), nullptr, ImGuiWindowFlags_AlwaysAutoResize)) { // Header with icon ImGui::Text("%s %s", ICON_MD_PALETTE, modal_title.c_str()); ImGui::Separator(); // Use the existing EnhancedPaletteEditor static std::unique_ptr palette_editor = std::make_unique(); if (options.palette) { palette_editor->ShowPaletteEditor(*options.palette, modal_title); } // Action Buttons ImGui::Separator(); ImGui::Spacing(); if (ImGui::Button("Close", ImVec2(120, 0))) { ImGui::CloseCurrentPopup(); } ImGui::EndPopup(); } } void CanvasModals::RenderColorAnalysisModal(const std::string& canvas_id, const ColorAnalysisOptions& options) { std::string modal_title = "Color Analysis"; ImGui::SetNextWindowSize(ImVec2(700, 500), ImGuiCond_FirstUseEver); if (ImGui::BeginPopupModal(modal_title.c_str(), nullptr, ImGuiWindowFlags_AlwaysAutoResize)) { // Header with icon ImGui::Text("%s %s", ICON_MD_ZOOM_IN, modal_title.c_str()); ImGui::Separator(); // Use the existing EnhancedPaletteEditor for color analysis static std::unique_ptr palette_editor = std::make_unique(); if (options.bitmap) { palette_editor->ShowColorAnalysis(*options.bitmap, modal_title); } // Action Buttons ImGui::Separator(); ImGui::Spacing(); if (ImGui::Button("Close", ImVec2(120, 0))) { ImGui::CloseCurrentPopup(); } ImGui::EndPopup(); } } void CanvasModals::RenderPerformanceModal(const std::string& canvas_id, const PerformanceOptions& options) { std::string modal_title = "Canvas Performance"; ImGui::SetNextWindowSize(ImVec2(500, 300), ImGuiCond_FirstUseEver); if (ImGui::BeginPopupModal(modal_title.c_str(), nullptr, ImGuiWindowFlags_AlwaysAutoResize)) { // Header with icon ImGui::Text("%s %s", ICON_MD_TRENDING_UP, modal_title.c_str()); ImGui::Separator(); // Performance metrics RenderMetricCard("Operation", options.operation_name, "speed", ImVec4(0.2f, 1.0f, 0.2f, 1.0f)); RenderMetricCard("Time", std::to_string(options.operation_time_ms) + " ms", "timer", ImVec4(1.0f, 0.8f, 0.2f, 1.0f)); // Get overall performance stats auto& profiler = gfx::PerformanceProfiler::Get(); auto canvas_stats = profiler.GetStats("canvas_operations"); auto draw_stats = profiler.GetStats("canvas_draw"); RenderMetricCard("Total Operations", std::to_string(canvas_stats.sample_count), "functions", ImVec4(0.2F, 0.8F, 1.0F, 1.0F)); RenderMetricCard("Average Time", std::to_string(draw_stats.avg_time_us / 1000.0) + " ms", "schedule", ImVec4(0.8F, 0.2F, 1.0F, 1.0F)); // Action Buttons ImGui::Separator(); ImGui::Spacing(); if (ImGui::Button("Open Dashboard", ImVec2(150, 0))) { gfx::PerformanceDashboard::Get().SetVisible(true); } ImGui::SameLine(); if (ImGui::Button("Close", ImVec2(120, 0))) { ImGui::CloseCurrentPopup(); } ImGui::EndPopup(); } } void CanvasModals::OpenModal(const std::string& id, std::function render_func) { // Check if modal already exists auto it = std::find_if(active_modals_.begin(), active_modals_.end(), [&id](const ModalState& modal) { return modal.id == id; }); if (it != active_modals_.end()) { it->is_open = true; it->render_func = render_func; } else { active_modals_.push_back({true, id, render_func}); } // Open the popup ImGui::OpenPopup(id.c_str()); } void CanvasModals::CloseModal(const std::string& id) { auto it = std::find_if(active_modals_.begin(), active_modals_.end(), [&id](const ModalState& modal) { return modal.id == id; }); if (it != active_modals_.end()) { it->is_open = false; } } bool CanvasModals::IsModalOpen(const std::string& id) const { auto it = std::find_if(active_modals_.begin(), active_modals_.end(), [&id](const ModalState& modal) { return modal.id == id; }); return it != active_modals_.end() && it->is_open; } void CanvasModals::RenderMaterialIcon(const std::string& icon_name, const ImVec4& color) { // Simple material icon rendering using Unicode symbols // In a real implementation, you'd use a proper icon font static std::unordered_map icon_map = { {"grid_on", ICON_MD_GRID_ON}, {"label", ICON_MD_LABEL}, {"edit", ICON_MD_EDIT}, {"menu", ICON_MD_MENU}, {"drag_indicator", ICON_MD_DRAG_INDICATOR}, {"fit_screen", ICON_MD_FIT_SCREEN}, {"zoom_in", ICON_MD_ZOOM_IN}, {"speed", ICON_MD_SPEED}, {"timer", ICON_MD_TIMER}, {"functions", ICON_MD_FUNCTIONS}, {"schedule", ICON_MD_SCHEDULE}, {"refresh", ICON_MD_REFRESH}, {"settings", ICON_MD_SETTINGS}, {"info", ICON_MD_INFO} }; auto it = icon_map.find(icon_name); if (it != icon_map.end()) { ImGui::TextColored(color, "%s", it->second); } } void CanvasModals::RenderMetricCard(const std::string& title, const std::string& value, const std::string& icon, const ImVec4& color) { ImGui::BeginGroup(); // Icon and title ImGui::Text("%s %s", icon.c_str(), title.c_str()); // Value with color ImGui::TextColored(color, "%s", value.c_str()); ImGui::EndGroup(); } void CanvasModals::RenderSliderWithIcon(const std::string& label, const std::string& icon, float* value, float min_val, float max_val, const char* format) { ImGui::Text("%s %s", icon.c_str(), label.c_str()); ImGui::SameLine(); ImGui::SetNextItemWidth(200); ImGui::SliderFloat(("##" + label).c_str(), value, min_val, max_val, format); } } // namespace canvas } // namespace gui } // namespace yaze