Files
yaze/src/app/gui/canvas/canvas_context_menu.cc
scawful 6c0e7a96a5 refactor: Update GUI components and integrate new palette widget
- Removed references to the old `EnhancedPaletteEditor` and replaced it with the new `PaletteWidget` across various files, including canvas and context menu implementations.
- Updated CMake configurations to include the new `palette_widget` source files, ensuring proper integration into the build system.
- Refactored GUI code to enhance modularity and maintainability, improving the overall user experience in palette management and editing functionalities.
- Introduced new UI components for background rendering and editor selection dialogs, enhancing the application's graphical interface.
2025-10-04 23:59:08 -04:00

542 lines
19 KiB
C++

#include "canvas_context_menu.h"
#include <algorithm>
#include <sstream>
#include <iomanip>
#include "app/gfx/performance_profiler.h"
#include "app/gfx/performance_dashboard.h"
#include "app/gui/widgets/palette_widget.h"
#include "app/gui/bpp_format_ui.h"
#include "app/gui/icons.h"
#include "app/gui/canvas/canvas_modals.h"
#include "imgui/imgui.h"
namespace yaze {
namespace gui {
namespace canvas {
namespace {
inline void Dispatch(
const std::function<void(CanvasContextMenu::Command, const CanvasConfig&)>& handler,
CanvasContextMenu::Command command, CanvasConfig config) {
if (handler) {
handler(command, config);
}
}
} // namespace
void CanvasContextMenu::Initialize(const std::string& canvas_id) {
canvas_id_ = canvas_id;
enabled_ = true;
current_usage_ = CanvasUsage::kTilePainting;
// Initialize canvas state
canvas_size_ = ImVec2(0, 0);
content_size_ = ImVec2(0, 0);
global_scale_ = 1.0F;
grid_step_ = 32.0F;
enable_grid_ = true;
enable_hex_labels_ = false;
enable_custom_labels_ = false;
enable_context_menu_ = true;
is_draggable_ = false;
auto_resize_ = false;
scrolling_ = ImVec2(0, 0);
// Create default menu items
CreateDefaultMenuItems();
}
void CanvasContextMenu::SetUsageMode(CanvasUsage usage) {
current_usage_ = usage;
}
void CanvasContextMenu::AddMenuItem(const ContextMenuItem& item) {
global_items_.push_back(item);
}
void CanvasContextMenu::AddMenuItem(const ContextMenuItem& item, CanvasUsage usage) {
usage_specific_items_[usage].push_back(item);
}
void CanvasContextMenu::ClearMenuItems() {
global_items_.clear();
usage_specific_items_.clear();
}
void CanvasContextMenu::Render(
const std::string& context_id, const ImVec2& mouse_pos,
const gfx::Bitmap* bitmap, const gfx::SnesPalette* palette,
const std::function<void(Command, const CanvasConfig&)>& command_handler,
CanvasConfig current_config) {
if (!enabled_) return;
// Context menu (under default mouse threshold)
if (ImVec2 drag_delta = ImGui::GetMouseDragDelta(ImGuiMouseButton_Right);
enable_context_menu_ && drag_delta.x == 0.0F && drag_delta.y == 0.0F) {
ImGui::OpenPopupOnItemClick(context_id.c_str(), ImGuiPopupFlags_MouseButtonRight);
}
// Contents of the Context Menu
if (ImGui::BeginPopup(context_id.c_str())) {
// Render usage-specific menu first
RenderUsageSpecificMenu();
// Add separator if there are usage-specific items
if (!usage_specific_items_[current_usage_].empty()) {
ImGui::Separator();
}
// Render view controls
RenderViewControlsMenu(command_handler, current_config);
ImGui::Separator();
// Render canvas properties
RenderCanvasPropertiesMenu(command_handler, current_config);
ImGui::Separator();
// Render bitmap operations if bitmap is available
if (bitmap) {
RenderBitmapOperationsMenu(bitmap);
ImGui::Separator();
}
// Render palette operations if palette is available
if (palette) {
RenderPaletteOperationsMenu(palette);
ImGui::Separator();
}
if (bitmap) {
RenderBppOperationsMenu(bitmap);
ImGui::Separator();
}
RenderPerformanceMenu();
ImGui::Separator();
RenderGridControlsMenu(command_handler, current_config);
ImGui::Separator();
RenderScalingControlsMenu(command_handler, current_config);
// Render global menu items
if (!global_items_.empty()) {
ImGui::Separator();
RenderMenuSection("Custom Actions", global_items_);
}
ImGui::EndPopup();
}
}
bool CanvasContextMenu::ShouldShowContextMenu() const {
return enabled_ && enable_context_menu_;
}
void CanvasContextMenu::SetCanvasState(const ImVec2& canvas_size,
const ImVec2& content_size,
float global_scale,
float grid_step,
bool enable_grid,
bool enable_hex_labels,
bool enable_custom_labels,
bool enable_context_menu,
bool is_draggable,
bool auto_resize,
const ImVec2& scrolling) {
canvas_size_ = canvas_size;
content_size_ = content_size;
global_scale_ = global_scale;
grid_step_ = grid_step;
enable_grid_ = enable_grid;
enable_hex_labels_ = enable_hex_labels;
enable_custom_labels_ = enable_custom_labels;
enable_context_menu_ = enable_context_menu;
is_draggable_ = is_draggable;
auto_resize_ = auto_resize;
scrolling_ = scrolling;
}
void CanvasContextMenu::RenderMenuItem(const ContextMenuItem& item) {
if (!item.visible_condition()) {
return;
}
if (!item.enabled_condition()) {
ImGui::BeginDisabled();
}
if (item.subitems.empty()) {
// Simple menu item
if (ImGui::MenuItem(item.label.c_str(),
item.shortcut.empty() ? nullptr : item.shortcut.c_str())) {
item.callback();
}
} else {
// Menu with subitems
if (ImGui::BeginMenu(item.label.c_str())) {
for (const auto& subitem : item.subitems) {
RenderMenuItem(subitem);
}
ImGui::EndMenu();
}
}
if (!item.enabled_condition()) {
ImGui::EndDisabled();
}
if (item.separator_after) {
ImGui::Separator();
}
}
void CanvasContextMenu::RenderMenuSection(const std::string& title,
const std::vector<ContextMenuItem>& items) {
if (items.empty()) return;
ImGui::TextColored(ImVec4(0.7F, 0.7F, 0.7F, 1.0F), "%s", title.c_str());
for (const auto& item : items) {
RenderMenuItem(item);
}
}
void CanvasContextMenu::RenderUsageSpecificMenu() {
auto it = usage_specific_items_.find(current_usage_);
if (it == usage_specific_items_.end() || it->second.empty()) {
return;
}
std::string usage_name = GetUsageModeName(current_usage_);
ImVec4 usage_color = GetUsageModeColor(current_usage_);
ImGui::TextColored(usage_color, "%s %s Mode", ICON_MD_COLOR_LENS, usage_name.c_str());
ImGui::Separator();
for (const auto& item : it->second) {
RenderMenuItem(item);
}
}
void CanvasContextMenu::RenderViewControlsMenu(
const std::function<void(Command, const CanvasConfig&)>& command_handler,
CanvasConfig current_config) {
if (ImGui::BeginMenu("View Controls")) {
if (ImGui::MenuItem("Reset View", "Ctrl+R")) {
Dispatch(command_handler, Command::kResetView, current_config);
}
if (ImGui::MenuItem("Zoom to Fit", "Ctrl+F")) {
Dispatch(command_handler, Command::kZoomToFit, current_config);
}
if (ImGui::MenuItem("Zoom In", "Ctrl++")) {
CanvasConfig updated = current_config;
updated.global_scale *= 1.25F;
Dispatch(command_handler, Command::kSetScale, updated);
}
if (ImGui::MenuItem("Zoom Out", "Ctrl+-")) {
CanvasConfig updated = current_config;
updated.global_scale *= 0.8F;
Dispatch(command_handler, Command::kSetScale, updated);
}
ImGui::Separator();
if (ImGui::MenuItem("Show Grid", nullptr, enable_grid_)) {
CanvasConfig updated = current_config;
updated.enable_grid = !enable_grid_;
Dispatch(command_handler, Command::kToggleGrid, updated);
}
if (ImGui::MenuItem("Show Hex Labels", nullptr, enable_hex_labels_)) {
CanvasConfig updated = current_config;
updated.enable_hex_labels = !enable_hex_labels_;
Dispatch(command_handler, Command::kToggleHexLabels, updated);
}
if (ImGui::MenuItem("Show Custom Labels", nullptr, enable_custom_labels_)) {
CanvasConfig updated = current_config;
updated.enable_custom_labels = !enable_custom_labels_;
Dispatch(command_handler, Command::kToggleCustomLabels, updated);
}
ImGui::EndMenu();
}
}
void CanvasContextMenu::RenderCanvasPropertiesMenu(
const std::function<void(Command, const CanvasConfig&)>& command_handler,
CanvasConfig current_config) {
if (ImGui::BeginMenu(ICON_MD_SETTINGS " Canvas Properties")) {
ImGui::Text("Canvas Size: %.0f x %.0f", canvas_size_.x, canvas_size_.y);
ImGui::Text("Content Size: %.0f x %.0f", content_size_.x, content_size_.y);
ImGui::Text("Global Scale: %.2f", global_scale_);
ImGui::Text("Grid Step: %.1f", grid_step_);
ImGui::Text("Mouse Position: %.0f x %.0f", 0.0F, 0.0F); // Would need actual mouse pos
if (ImGui::MenuItem("Advanced Properties...")) {
CanvasConfig updated = current_config;
updated.enable_grid = enable_grid_;
updated.enable_hex_labels = enable_hex_labels_;
updated.enable_custom_labels = enable_custom_labels_;
updated.enable_context_menu = enable_context_menu_;
updated.is_draggable = is_draggable_;
updated.auto_resize = auto_resize_;
updated.grid_step = grid_step_;
updated.canvas_size = canvas_size_;
updated.content_size = content_size_;
updated.scrolling = scrolling_;
Dispatch(command_handler, Command::kOpenAdvancedProperties, updated);
}
ImGui::EndMenu();
}
}
void CanvasContextMenu::RenderBitmapOperationsMenu(const gfx::Bitmap* bitmap) {
if (ImGui::BeginMenu(ICON_MD_IMAGE " Bitmap Operations")) {
ImGui::Text("Size: %d x %d", bitmap->width(), bitmap->height());
ImGui::Text("Format: %s", "Unknown"); // Would need format detection
if (ImGui::MenuItem("Edit Bitmap Data...")) {
// Open bitmap data editor
}
if (ImGui::MenuItem("Export Bitmap...")) {
// Export bitmap
}
if (ImGui::MenuItem("Import Bitmap...")) {
// Import bitmap
}
ImGui::EndMenu();
}
}
void CanvasContextMenu::RenderPaletteOperationsMenu(const gfx::SnesPalette* palette) {
if (ImGui::BeginMenu(ICON_MD_PALETTE " Palette Operations")) {
ImGui::Text("Colors: %zu", palette->size());
if (ImGui::MenuItem("Edit Palette...")) {
// Open palette editor
}
if (ImGui::MenuItem("Color Analysis...")) {
// Open color analysis
}
if (ImGui::MenuItem("Apply ROM Palette...")) {
// Apply ROM palette
}
ImGui::EndMenu();
}
}
void CanvasContextMenu::RenderBppOperationsMenu(const gfx::Bitmap* bitmap) {
if (ImGui::BeginMenu(ICON_MD_SWAP_HORIZ " BPP Operations")) {
if (ImGui::MenuItem("Format Analysis...")) {
// Open BPP analysis
}
if (ImGui::MenuItem("Convert Format...")) {
// Open BPP conversion dialog
}
if (ImGui::MenuItem("Format Comparison...")) {
// Open format comparison tool
}
ImGui::EndMenu();
}
}
void CanvasContextMenu::RenderPerformanceMenu() {
if (ImGui::BeginMenu(ICON_MD_TRENDING_UP " Performance")) {
auto& profiler = gfx::PerformanceProfiler::Get();
auto canvas_stats = profiler.GetStats("canvas_operations");
auto draw_stats = profiler.GetStats("canvas_draw");
ImGui::Text("Canvas Operations: %zu", canvas_stats.sample_count);
ImGui::Text("Average Time: %.2f ms", draw_stats.avg_time_us / 1000.0);
if (ImGui::MenuItem("Performance Dashboard...")) {
gfx::PerformanceDashboard::Get().SetVisible(true);
}
if (ImGui::MenuItem("Usage Report...")) {
// Open usage report
}
ImGui::EndMenu();
}
}
void CanvasContextMenu::RenderGridControlsMenu(
const std::function<void(Command, const CanvasConfig&)>& command_handler,
CanvasConfig current_config) {
if (ImGui::BeginMenu(ICON_MD_GRID_ON " Grid Controls")) {
const struct GridOption {
const char* label;
float value;
} options[] = {{"8x8", 8.0F}, {"16x16", 16.0F},
{"32x32", 32.0F}, {"64x64", 64.0F}};
for (const auto& option : options) {
bool selected = grid_step_ == option.value;
if (ImGui::MenuItem(option.label, nullptr, selected)) {
CanvasConfig updated = current_config;
updated.grid_step = option.value;
Dispatch(command_handler, Command::kSetGridStep, updated);
}
}
ImGui::EndMenu();
}
}
void CanvasContextMenu::RenderScalingControlsMenu(
const std::function<void(Command, const CanvasConfig&)>& command_handler,
CanvasConfig current_config) {
if (ImGui::BeginMenu(ICON_MD_ZOOM_IN " Scaling Controls")) {
const struct ScaleOption {
const char* label;
float value;
} options[] = {{"0.25x", 0.25F}, {"0.5x", 0.5F}, {"1x", 1.0F},
{"2x", 2.0F}, {"4x", 4.0F}, {"8x", 8.0F}};
for (const auto& option : options) {
if (ImGui::MenuItem(option.label)) {
CanvasConfig updated = current_config;
updated.global_scale = option.value;
Dispatch(command_handler, Command::kSetScale, updated);
}
}
ImGui::EndMenu();
}
}
void CanvasContextMenu::RenderMaterialIcon(const std::string& icon_name, const ImVec4& color) {
// Simple material icon rendering using Unicode symbols
static std::unordered_map<std::string, const char*> 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},
{"view", ICON_MD_VISIBILITY}, {"properties", ICON_MD_SETTINGS}, {"bitmap", ICON_MD_IMAGE}, {"palette", ICON_MD_PALETTE},
{"bpp", ICON_MD_SWAP_HORIZ}, {"performance", ICON_MD_TRENDING_UP}, {"grid", ICON_MD_GRID_ON}, {"scaling", ICON_MD_ZOOM_IN}
};
auto it = icon_map.find(icon_name);
if (it != icon_map.end()) {
ImGui::TextColored(color, "%s", it->second);
}
}
std::string CanvasContextMenu::GetUsageModeName(CanvasUsage usage) const {
switch (usage) {
case CanvasUsage::kTilePainting: return "Tile Painting";
case CanvasUsage::kTileSelecting: return "Tile Selecting";
case CanvasUsage::kSelectRectangle: return "Rectangle Selection";
case CanvasUsage::kColorPainting: return "Color Painting";
case CanvasUsage::kBitmapEditing: return "Bitmap Editing";
case CanvasUsage::kPaletteEditing: return "Palette Editing";
case CanvasUsage::kBppConversion: return "BPP Conversion";
case CanvasUsage::kPerformanceMode: return "Performance Mode";
case CanvasUsage::kUnknown: return "Unknown";
default: return "Unknown";
}
}
ImVec4 CanvasContextMenu::GetUsageModeColor(CanvasUsage usage) const {
switch (usage) {
case CanvasUsage::kTilePainting: return ImVec4(0.2F, 1.0F, 0.2F, 1.0F); // Green
case CanvasUsage::kTileSelecting: return ImVec4(0.2F, 0.8F, 1.0F, 1.0F); // Blue
case CanvasUsage::kSelectRectangle: return ImVec4(1.0F, 0.8F, 0.2F, 1.0F); // Yellow
case CanvasUsage::kColorPainting: return ImVec4(1.0F, 0.2F, 1.0F, 1.0F); // Magenta
case CanvasUsage::kBitmapEditing: return ImVec4(1.0F, 0.5F, 0.2F, 1.0F); // Orange
case CanvasUsage::kPaletteEditing: return ImVec4(0.8F, 0.2F, 1.0F, 1.0F); // Purple
case CanvasUsage::kBppConversion: return ImVec4(0.2F, 1.0F, 1.0F, 1.0F); // Cyan
case CanvasUsage::kPerformanceMode: return ImVec4(1.0F, 0.2F, 0.2F, 1.0F); // Red
case CanvasUsage::kUnknown: return ImVec4(0.7F, 0.7F, 0.7F, 1.0F); // Gray
default: return ImVec4(0.7F, 0.7F, 0.7F, 1.0F); // Gray
}
}
void CanvasContextMenu::CreateDefaultMenuItems() {
// Create default menu items for different usage modes
// Tile Painting mode items
ContextMenuItem tile_paint_item("Paint Tile", "paint", []() {
// Tile painting action
});
usage_specific_items_[CanvasUsage::kTilePainting].push_back(tile_paint_item);
// Tile Selecting mode items
ContextMenuItem tile_select_item("Select Tile", "select", []() {
// Tile selection action
});
usage_specific_items_[CanvasUsage::kTileSelecting].push_back(tile_select_item);
// Rectangle Selection mode items
ContextMenuItem rect_select_item("Select Rectangle", "rect", []() {
// Rectangle selection action
});
usage_specific_items_[CanvasUsage::kSelectRectangle].push_back(rect_select_item);
// Color Painting mode items
ContextMenuItem color_paint_item("Paint Color", "color", []() {
// Color painting action
});
usage_specific_items_[CanvasUsage::kColorPainting].push_back(color_paint_item);
// Bitmap Editing mode items
ContextMenuItem bitmap_edit_item("Edit Bitmap", "edit", []() {
// Bitmap editing action
});
usage_specific_items_[CanvasUsage::kBitmapEditing].push_back(bitmap_edit_item);
// Palette Editing mode items
ContextMenuItem palette_edit_item("Edit Palette", "palette", []() {
// Palette editing action
});
usage_specific_items_[CanvasUsage::kPaletteEditing].push_back(palette_edit_item);
// BPP Conversion mode items
ContextMenuItem bpp_convert_item("Convert Format", "convert", []() {
// BPP conversion action
});
usage_specific_items_[CanvasUsage::kBppConversion].push_back(bpp_convert_item);
// Performance Mode items
ContextMenuItem perf_item("Performance Analysis", "perf", []() {
// Performance analysis action
});
usage_specific_items_[CanvasUsage::kPerformanceMode].push_back(perf_item);
}
CanvasContextMenu::ContextMenuItem CanvasContextMenu::CreateViewMenuItem(const std::string& label,
const std::string& icon,
std::function<void()> callback) {
return ContextMenuItem(label, icon, callback);
}
CanvasContextMenu::ContextMenuItem CanvasContextMenu::CreateBitmapMenuItem(const std::string& label,
const std::string& icon,
std::function<void()> callback) {
return ContextMenuItem(label, icon, callback);
}
CanvasContextMenu::ContextMenuItem CanvasContextMenu::CreatePaletteMenuItem(const std::string& label,
const std::string& icon,
std::function<void()> callback) {
return ContextMenuItem(label, icon, callback);
}
CanvasContextMenu::ContextMenuItem CanvasContextMenu::CreateBppMenuItem(const std::string& label,
const std::string& icon,
std::function<void()> callback) {
return ContextMenuItem(label, icon, callback);
}
CanvasContextMenu::ContextMenuItem CanvasContextMenu::CreatePerformanceMenuItem(const std::string& label,
const std::string& icon,
std::function<void()> callback) {
return ContextMenuItem(label, icon, callback);
}
} // namespace canvas
} // namespace gui
} // namespace yaze