refactor(canvas): unify context menu item handling with CanvasMenuItem
- Replaced the legacy ContextMenuItem structure with a new CanvasMenuItem across various files, enhancing consistency in context menu management. - Introduced CanvasMenuBuilder for fluent menu construction, allowing for easier addition of items and submenus. - Updated Canvas and related components to utilize the new menu system, improving organization and maintainability of context menus. Benefits: - Streamlines context menu item management, making it more intuitive and flexible. - Enhances code readability and reduces duplication, facilitating future enhancements.
This commit is contained in:
@@ -218,158 +218,158 @@ void DungeonCanvasViewer::DrawDungeonCanvas(int room_id) {
|
|||||||
auto& layer_settings = GetRoomLayerSettings(room_id);
|
auto& layer_settings = GetRoomLayerSettings(room_id);
|
||||||
|
|
||||||
// Add object placement option
|
// Add object placement option
|
||||||
canvas_.AddContextMenuItem({
|
canvas_.AddContextMenuItem(
|
||||||
ICON_MD_ADD " Place Object",
|
gui::CanvasMenuItem(ICON_MD_ADD " Place Object", ICON_MD_ADD,
|
||||||
[]() {
|
[]() {
|
||||||
// TODO: Show object palette/selector
|
// TODO: Show object palette/selector
|
||||||
},
|
},
|
||||||
"Ctrl+P"
|
"Ctrl+P")
|
||||||
});
|
);
|
||||||
|
|
||||||
// Add object deletion for selected objects
|
// Add object deletion for selected objects
|
||||||
canvas_.AddContextMenuItem({
|
canvas_.AddContextMenuItem(
|
||||||
ICON_MD_DELETE " Delete Selected",
|
gui::CanvasMenuItem(ICON_MD_DELETE " Delete Selected", ICON_MD_DELETE,
|
||||||
[this]() {
|
[this]() {
|
||||||
object_interaction_.HandleDeleteSelected();
|
object_interaction_.HandleDeleteSelected();
|
||||||
},
|
},
|
||||||
"Del"
|
"Del")
|
||||||
});
|
);
|
||||||
|
|
||||||
// Add room property quick toggles
|
// Add room property quick toggles
|
||||||
canvas_.AddContextMenuItem({
|
canvas_.AddContextMenuItem(
|
||||||
ICON_MD_LAYERS " Toggle BG1",
|
gui::CanvasMenuItem(ICON_MD_LAYERS " Toggle BG1", ICON_MD_LAYERS,
|
||||||
[this, room_id]() {
|
[this, room_id]() {
|
||||||
auto& settings = GetRoomLayerSettings(room_id);
|
auto& settings = GetRoomLayerSettings(room_id);
|
||||||
settings.bg1_visible = !settings.bg1_visible;
|
settings.bg1_visible = !settings.bg1_visible;
|
||||||
},
|
},
|
||||||
"1"
|
"1")
|
||||||
});
|
);
|
||||||
|
|
||||||
canvas_.AddContextMenuItem({
|
canvas_.AddContextMenuItem(
|
||||||
ICON_MD_LAYERS " Toggle BG2",
|
gui::CanvasMenuItem(ICON_MD_LAYERS " Toggle BG2", ICON_MD_LAYERS,
|
||||||
[this, room_id]() {
|
[this, room_id]() {
|
||||||
auto& settings = GetRoomLayerSettings(room_id);
|
auto& settings = GetRoomLayerSettings(room_id);
|
||||||
settings.bg2_visible = !settings.bg2_visible;
|
settings.bg2_visible = !settings.bg2_visible;
|
||||||
},
|
},
|
||||||
"2"
|
"2")
|
||||||
});
|
);
|
||||||
|
|
||||||
// Add re-render option
|
// Add re-render option
|
||||||
canvas_.AddContextMenuItem({
|
canvas_.AddContextMenuItem(
|
||||||
ICON_MD_REFRESH " Re-render Room",
|
gui::CanvasMenuItem(ICON_MD_REFRESH " Re-render Room", ICON_MD_REFRESH,
|
||||||
[&room]() {
|
[&room]() {
|
||||||
room.RenderRoomGraphics();
|
room.RenderRoomGraphics();
|
||||||
},
|
},
|
||||||
"Ctrl+R"
|
"Ctrl+R")
|
||||||
});
|
);
|
||||||
|
|
||||||
// === DEBUG MENU ===
|
// === DEBUG MENU ===
|
||||||
gui::Canvas::ContextMenuItem debug_menu;
|
gui::CanvasMenuItem debug_menu;
|
||||||
debug_menu.label = ICON_MD_BUG_REPORT " Debug";
|
debug_menu.label = ICON_MD_BUG_REPORT " Debug";
|
||||||
|
|
||||||
// Show room info
|
// Show room info
|
||||||
debug_menu.subitems.push_back({
|
debug_menu.subitems.push_back(
|
||||||
ICON_MD_INFO " Show Room Info",
|
gui::CanvasMenuItem(ICON_MD_INFO " Show Room Info", ICON_MD_INFO,
|
||||||
[this, room_id]() {
|
[this]() {
|
||||||
show_room_debug_info_ = !show_room_debug_info_;
|
show_room_debug_info_ = !show_room_debug_info_;
|
||||||
}
|
})
|
||||||
});
|
);
|
||||||
|
|
||||||
// Show texture info
|
// Show texture info
|
||||||
debug_menu.subitems.push_back({
|
debug_menu.subitems.push_back(
|
||||||
ICON_MD_IMAGE " Show Texture Debug",
|
gui::CanvasMenuItem(ICON_MD_IMAGE " Show Texture Debug", ICON_MD_IMAGE,
|
||||||
[this]() {
|
[this]() {
|
||||||
show_texture_debug_ = !show_texture_debug_;
|
show_texture_debug_ = !show_texture_debug_;
|
||||||
}
|
})
|
||||||
});
|
);
|
||||||
|
|
||||||
// Show object bounds with sub-menu for categories
|
// Show object bounds with sub-menu for categories
|
||||||
gui::Canvas::ContextMenuItem object_bounds_menu;
|
gui::CanvasMenuItem object_bounds_menu;
|
||||||
object_bounds_menu.label = ICON_MD_CROP_SQUARE " Show Object Bounds";
|
object_bounds_menu.label = ICON_MD_CROP_SQUARE " Show Object Bounds";
|
||||||
object_bounds_menu.callback = [this]() {
|
object_bounds_menu.callback = [this]() {
|
||||||
show_object_bounds_ = !show_object_bounds_;
|
show_object_bounds_ = !show_object_bounds_;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Sub-menu for filtering by type
|
// Sub-menu for filtering by type
|
||||||
object_bounds_menu.subitems.push_back({
|
object_bounds_menu.subitems.push_back(
|
||||||
"Type 1 (0x00-0xFF)",
|
gui::CanvasMenuItem("Type 1 (0x00-0xFF)",
|
||||||
[this]() {
|
[this]() {
|
||||||
object_outline_toggles_.show_type1_objects = !object_outline_toggles_.show_type1_objects;
|
object_outline_toggles_.show_type1_objects = !object_outline_toggles_.show_type1_objects;
|
||||||
}
|
})
|
||||||
});
|
);
|
||||||
object_bounds_menu.subitems.push_back({
|
object_bounds_menu.subitems.push_back(
|
||||||
"Type 2 (0x100-0x1FF)",
|
gui::CanvasMenuItem("Type 2 (0x100-0x1FF)",
|
||||||
[this]() {
|
[this]() {
|
||||||
object_outline_toggles_.show_type2_objects = !object_outline_toggles_.show_type2_objects;
|
object_outline_toggles_.show_type2_objects = !object_outline_toggles_.show_type2_objects;
|
||||||
}
|
})
|
||||||
});
|
);
|
||||||
object_bounds_menu.subitems.push_back({
|
object_bounds_menu.subitems.push_back(
|
||||||
"Type 3 (0xF00-0xFFF)",
|
gui::CanvasMenuItem("Type 3 (0xF00-0xFFF)",
|
||||||
[this]() {
|
[this]() {
|
||||||
object_outline_toggles_.show_type3_objects = !object_outline_toggles_.show_type3_objects;
|
object_outline_toggles_.show_type3_objects = !object_outline_toggles_.show_type3_objects;
|
||||||
}
|
})
|
||||||
});
|
);
|
||||||
|
|
||||||
// Separator
|
// Separator
|
||||||
gui::Canvas::ContextMenuItem sep;
|
gui::CanvasMenuItem sep;
|
||||||
sep.label = "---";
|
sep.label = "---";
|
||||||
sep.enabled_condition = []() { return false; };
|
sep.enabled_condition = []() { return false; };
|
||||||
object_bounds_menu.subitems.push_back(sep);
|
object_bounds_menu.subitems.push_back(sep);
|
||||||
|
|
||||||
// Sub-menu for filtering by layer
|
// Sub-menu for filtering by layer
|
||||||
object_bounds_menu.subitems.push_back({
|
object_bounds_menu.subitems.push_back(
|
||||||
"Layer 0 (BG1)",
|
gui::CanvasMenuItem("Layer 0 (BG1)",
|
||||||
[this]() {
|
[this]() {
|
||||||
object_outline_toggles_.show_layer0_objects = !object_outline_toggles_.show_layer0_objects;
|
object_outline_toggles_.show_layer0_objects = !object_outline_toggles_.show_layer0_objects;
|
||||||
}
|
})
|
||||||
});
|
);
|
||||||
object_bounds_menu.subitems.push_back({
|
object_bounds_menu.subitems.push_back(
|
||||||
"Layer 1 (BG2)",
|
gui::CanvasMenuItem("Layer 1 (BG2)",
|
||||||
[this]() {
|
[this]() {
|
||||||
object_outline_toggles_.show_layer1_objects = !object_outline_toggles_.show_layer1_objects;
|
object_outline_toggles_.show_layer1_objects = !object_outline_toggles_.show_layer1_objects;
|
||||||
}
|
})
|
||||||
});
|
);
|
||||||
object_bounds_menu.subitems.push_back({
|
object_bounds_menu.subitems.push_back(
|
||||||
"Layer 2 (BG3)",
|
gui::CanvasMenuItem("Layer 2 (BG3)",
|
||||||
[this]() {
|
[this]() {
|
||||||
object_outline_toggles_.show_layer2_objects = !object_outline_toggles_.show_layer2_objects;
|
object_outline_toggles_.show_layer2_objects = !object_outline_toggles_.show_layer2_objects;
|
||||||
}
|
})
|
||||||
});
|
);
|
||||||
|
|
||||||
debug_menu.subitems.push_back(object_bounds_menu);
|
debug_menu.subitems.push_back(object_bounds_menu);
|
||||||
|
|
||||||
// Show layer info
|
// Show layer info
|
||||||
debug_menu.subitems.push_back({
|
debug_menu.subitems.push_back(
|
||||||
ICON_MD_LAYERS " Show Layer Info",
|
gui::CanvasMenuItem(ICON_MD_LAYERS " Show Layer Info", ICON_MD_LAYERS,
|
||||||
[this]() {
|
[this]() {
|
||||||
show_layer_info_ = !show_layer_info_;
|
show_layer_info_ = !show_layer_info_;
|
||||||
}
|
})
|
||||||
});
|
);
|
||||||
|
|
||||||
// Force reload room
|
// Force reload room
|
||||||
debug_menu.subitems.push_back({
|
debug_menu.subitems.push_back(
|
||||||
ICON_MD_REFRESH " Force Reload",
|
gui::CanvasMenuItem(ICON_MD_REFRESH " Force Reload", ICON_MD_REFRESH,
|
||||||
[&room, room_id]() {
|
[&room]() {
|
||||||
room.LoadObjects();
|
room.LoadObjects();
|
||||||
room.LoadRoomGraphics(room.blockset);
|
room.LoadRoomGraphics(room.blockset);
|
||||||
room.RenderRoomGraphics();
|
room.RenderRoomGraphics();
|
||||||
}
|
})
|
||||||
});
|
);
|
||||||
|
|
||||||
// Log room state
|
// Log room state
|
||||||
debug_menu.subitems.push_back({
|
debug_menu.subitems.push_back(
|
||||||
ICON_MD_PRINT " Log Room State",
|
gui::CanvasMenuItem(ICON_MD_PRINT " Log Room State", ICON_MD_PRINT,
|
||||||
[&room, room_id]() {
|
[&room, room_id]() {
|
||||||
LOG_DEBUG("DungeonDebug", "=== Room %03X Debug ===", room_id);
|
LOG_DEBUG("DungeonDebug", "=== Room %03X Debug ===", room_id);
|
||||||
LOG_DEBUG("DungeonDebug", "Blockset: %d, Palette: %d, Layout: %d",
|
LOG_DEBUG("DungeonDebug", "Blockset: %d, Palette: %d, Layout: %d",
|
||||||
room.blockset, room.palette, room.layout);
|
room.blockset, room.palette, room.layout);
|
||||||
LOG_DEBUG("DungeonDebug", "Objects: %zu, Sprites: %zu",
|
LOG_DEBUG("DungeonDebug", "Objects: %zu, Sprites: %zu",
|
||||||
room.GetTileObjects().size(), room.GetSprites().size());
|
room.GetTileObjects().size(), room.GetSprites().size());
|
||||||
LOG_DEBUG("DungeonDebug", "BG1: %dx%d, BG2: %dx%d",
|
LOG_DEBUG("DungeonDebug", "BG1: %dx%d, BG2: %dx%d",
|
||||||
room.bg1_buffer().bitmap().width(), room.bg1_buffer().bitmap().height(),
|
room.bg1_buffer().bitmap().width(), room.bg1_buffer().bitmap().height(),
|
||||||
room.bg2_buffer().bitmap().width(), room.bg2_buffer().bitmap().height());
|
room.bg2_buffer().bitmap().width(), room.bg2_buffer().bitmap().height());
|
||||||
}
|
})
|
||||||
});
|
);
|
||||||
|
|
||||||
canvas_.AddContextMenuItem(debug_menu);
|
canvas_.AddContextMenuItem(debug_menu);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -418,11 +418,11 @@ void MapPropertiesSystem::SetupCanvasContextMenu(
|
|||||||
|
|
||||||
// Add entity insertion submenu (only in MOUSE mode)
|
// Add entity insertion submenu (only in MOUSE mode)
|
||||||
if (current_mode == 0 && entity_insert_callback_) { // 0 = EditingMode::MOUSE
|
if (current_mode == 0 && entity_insert_callback_) { // 0 = EditingMode::MOUSE
|
||||||
gui::Canvas::ContextMenuItem entity_menu;
|
gui::CanvasMenuItem entity_menu;
|
||||||
entity_menu.label = ICON_MD_ADD_LOCATION " Insert Entity";
|
entity_menu.label = ICON_MD_ADD_LOCATION " Insert Entity";
|
||||||
|
|
||||||
// Entrance submenu item
|
// Entrance submenu item
|
||||||
gui::Canvas::ContextMenuItem entrance_item;
|
gui::CanvasMenuItem entrance_item;
|
||||||
entrance_item.label = ICON_MD_DOOR_FRONT " Entrance";
|
entrance_item.label = ICON_MD_DOOR_FRONT " Entrance";
|
||||||
entrance_item.callback = [this]() {
|
entrance_item.callback = [this]() {
|
||||||
if (entity_insert_callback_) {
|
if (entity_insert_callback_) {
|
||||||
@@ -432,7 +432,7 @@ void MapPropertiesSystem::SetupCanvasContextMenu(
|
|||||||
entity_menu.subitems.push_back(entrance_item);
|
entity_menu.subitems.push_back(entrance_item);
|
||||||
|
|
||||||
// Hole submenu item
|
// Hole submenu item
|
||||||
gui::Canvas::ContextMenuItem hole_item;
|
gui::CanvasMenuItem hole_item;
|
||||||
hole_item.label = ICON_MD_CYCLONE " Hole";
|
hole_item.label = ICON_MD_CYCLONE " Hole";
|
||||||
hole_item.callback = [this]() {
|
hole_item.callback = [this]() {
|
||||||
if (entity_insert_callback_) {
|
if (entity_insert_callback_) {
|
||||||
@@ -442,7 +442,7 @@ void MapPropertiesSystem::SetupCanvasContextMenu(
|
|||||||
entity_menu.subitems.push_back(hole_item);
|
entity_menu.subitems.push_back(hole_item);
|
||||||
|
|
||||||
// Exit submenu item
|
// Exit submenu item
|
||||||
gui::Canvas::ContextMenuItem exit_item;
|
gui::CanvasMenuItem exit_item;
|
||||||
exit_item.label = ICON_MD_DOOR_BACK " Exit";
|
exit_item.label = ICON_MD_DOOR_BACK " Exit";
|
||||||
exit_item.callback = [this]() {
|
exit_item.callback = [this]() {
|
||||||
if (entity_insert_callback_) {
|
if (entity_insert_callback_) {
|
||||||
@@ -452,7 +452,7 @@ void MapPropertiesSystem::SetupCanvasContextMenu(
|
|||||||
entity_menu.subitems.push_back(exit_item);
|
entity_menu.subitems.push_back(exit_item);
|
||||||
|
|
||||||
// Item submenu item
|
// Item submenu item
|
||||||
gui::Canvas::ContextMenuItem item_item;
|
gui::CanvasMenuItem item_item;
|
||||||
item_item.label = ICON_MD_GRASS " Item";
|
item_item.label = ICON_MD_GRASS " Item";
|
||||||
item_item.callback = [this]() {
|
item_item.callback = [this]() {
|
||||||
if (entity_insert_callback_) {
|
if (entity_insert_callback_) {
|
||||||
@@ -462,7 +462,7 @@ void MapPropertiesSystem::SetupCanvasContextMenu(
|
|||||||
entity_menu.subitems.push_back(item_item);
|
entity_menu.subitems.push_back(item_item);
|
||||||
|
|
||||||
// Sprite submenu item
|
// Sprite submenu item
|
||||||
gui::Canvas::ContextMenuItem sprite_item;
|
gui::CanvasMenuItem sprite_item;
|
||||||
sprite_item.label = ICON_MD_PEST_CONTROL_RODENT " Sprite";
|
sprite_item.label = ICON_MD_PEST_CONTROL_RODENT " Sprite";
|
||||||
sprite_item.callback = [this]() {
|
sprite_item.callback = [this]() {
|
||||||
if (entity_insert_callback_) {
|
if (entity_insert_callback_) {
|
||||||
@@ -475,7 +475,7 @@ void MapPropertiesSystem::SetupCanvasContextMenu(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Add overworld-specific context menu items
|
// Add overworld-specific context menu items
|
||||||
gui::Canvas::ContextMenuItem lock_item;
|
gui::CanvasMenuItem lock_item;
|
||||||
lock_item.label = current_map_lock ? "Unlock Map" : "Lock to This Map";
|
lock_item.label = current_map_lock ? "Unlock Map" : "Lock to This Map";
|
||||||
lock_item.callback = [¤t_map_lock]() {
|
lock_item.callback = [¤t_map_lock]() {
|
||||||
current_map_lock = !current_map_lock;
|
current_map_lock = !current_map_lock;
|
||||||
@@ -483,7 +483,7 @@ void MapPropertiesSystem::SetupCanvasContextMenu(
|
|||||||
canvas.AddContextMenuItem(lock_item);
|
canvas.AddContextMenuItem(lock_item);
|
||||||
|
|
||||||
// Area Configuration
|
// Area Configuration
|
||||||
gui::Canvas::ContextMenuItem properties_item;
|
gui::CanvasMenuItem properties_item;
|
||||||
properties_item.label = ICON_MD_TUNE " Area Configuration";
|
properties_item.label = ICON_MD_TUNE " Area Configuration";
|
||||||
properties_item.callback = [&show_map_properties_panel]() {
|
properties_item.callback = [&show_map_properties_panel]() {
|
||||||
show_map_properties_panel = true;
|
show_map_properties_panel = true;
|
||||||
@@ -495,7 +495,7 @@ void MapPropertiesSystem::SetupCanvasContextMenu(
|
|||||||
(*rom_)[zelda3::OverworldCustomASMHasBeenApplied];
|
(*rom_)[zelda3::OverworldCustomASMHasBeenApplied];
|
||||||
if (asm_version >= 3 && asm_version != 0xFF) {
|
if (asm_version >= 3 && asm_version != 0xFF) {
|
||||||
// Custom Background Color
|
// Custom Background Color
|
||||||
gui::Canvas::ContextMenuItem bg_color_item;
|
gui::CanvasMenuItem bg_color_item;
|
||||||
bg_color_item.label = ICON_MD_FORMAT_COLOR_FILL " Custom Background Color";
|
bg_color_item.label = ICON_MD_FORMAT_COLOR_FILL " Custom Background Color";
|
||||||
bg_color_item.callback = [&show_custom_bg_color_editor]() {
|
bg_color_item.callback = [&show_custom_bg_color_editor]() {
|
||||||
show_custom_bg_color_editor = true;
|
show_custom_bg_color_editor = true;
|
||||||
@@ -503,7 +503,7 @@ void MapPropertiesSystem::SetupCanvasContextMenu(
|
|||||||
canvas.AddContextMenuItem(bg_color_item);
|
canvas.AddContextMenuItem(bg_color_item);
|
||||||
|
|
||||||
// Visual Effects Editor
|
// Visual Effects Editor
|
||||||
gui::Canvas::ContextMenuItem overlay_item;
|
gui::CanvasMenuItem overlay_item;
|
||||||
overlay_item.label = ICON_MD_LAYERS " Visual Effects";
|
overlay_item.label = ICON_MD_LAYERS " Visual Effects";
|
||||||
overlay_item.callback = [&show_overlay_editor]() {
|
overlay_item.callback = [&show_overlay_editor]() {
|
||||||
show_overlay_editor = true;
|
show_overlay_editor = true;
|
||||||
@@ -512,7 +512,7 @@ void MapPropertiesSystem::SetupCanvasContextMenu(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Canvas controls
|
// Canvas controls
|
||||||
gui::Canvas::ContextMenuItem reset_view_item;
|
gui::CanvasMenuItem reset_view_item;
|
||||||
reset_view_item.label = ICON_MD_RESTORE " Reset View";
|
reset_view_item.label = ICON_MD_RESTORE " Reset View";
|
||||||
reset_view_item.callback = [&canvas]() {
|
reset_view_item.callback = [&canvas]() {
|
||||||
canvas.set_global_scale(1.0f);
|
canvas.set_global_scale(1.0f);
|
||||||
@@ -520,7 +520,7 @@ void MapPropertiesSystem::SetupCanvasContextMenu(
|
|||||||
};
|
};
|
||||||
canvas.AddContextMenuItem(reset_view_item);
|
canvas.AddContextMenuItem(reset_view_item);
|
||||||
|
|
||||||
gui::Canvas::ContextMenuItem zoom_in_item;
|
gui::CanvasMenuItem zoom_in_item;
|
||||||
zoom_in_item.label = ICON_MD_ZOOM_IN " Zoom In";
|
zoom_in_item.label = ICON_MD_ZOOM_IN " Zoom In";
|
||||||
zoom_in_item.callback = [&canvas]() {
|
zoom_in_item.callback = [&canvas]() {
|
||||||
float scale = std::min(2.0f, canvas.global_scale() + 0.25f);
|
float scale = std::min(2.0f, canvas.global_scale() + 0.25f);
|
||||||
@@ -528,7 +528,7 @@ void MapPropertiesSystem::SetupCanvasContextMenu(
|
|||||||
};
|
};
|
||||||
canvas.AddContextMenuItem(zoom_in_item);
|
canvas.AddContextMenuItem(zoom_in_item);
|
||||||
|
|
||||||
gui::Canvas::ContextMenuItem zoom_out_item;
|
gui::CanvasMenuItem zoom_out_item;
|
||||||
zoom_out_item.label = ICON_MD_ZOOM_OUT " Zoom Out";
|
zoom_out_item.label = ICON_MD_ZOOM_OUT " Zoom Out";
|
||||||
zoom_out_item.callback = [&canvas]() {
|
zoom_out_item.callback = [&canvas]() {
|
||||||
float scale = std::max(0.25f, canvas.global_scale() - 0.25f);
|
float scale = std::max(0.25f, canvas.global_scale() - 0.25f);
|
||||||
|
|||||||
@@ -544,18 +544,18 @@ void Canvas::DrawContextMenu() {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
snapshot);
|
snapshot, this); // Phase 4: Pass Canvas* for editor menu integration
|
||||||
|
|
||||||
if (modals_) {
|
if (modals_) {
|
||||||
modals_->Render();
|
modals_->Render();
|
||||||
}
|
}
|
||||||
|
|
||||||
// CRITICAL: Render custom context menu items AFTER enhanced menu
|
// Phase 4: Render editor menu items using declarative menu system
|
||||||
// Don't return early - we need to show custom items too!
|
if (!editor_menu_.sections.empty() && ImGui::BeginPopupContextItem(context_id_.c_str())) {
|
||||||
if (!context_menu_items_.empty() && ImGui::BeginPopupContextItem(context_id_.c_str())) {
|
auto popup_callback = [this](const std::string& id, std::function<void()> callback) {
|
||||||
for (const auto& item : context_menu_items_) {
|
popup_registry_.Open(id, callback);
|
||||||
DrawContextMenuItem(item);
|
};
|
||||||
}
|
gui::RenderCanvasMenu(editor_menu_, popup_callback);
|
||||||
ImGui::EndPopup();
|
ImGui::EndPopup();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -569,105 +569,56 @@ void Canvas::DrawContextMenu() {
|
|||||||
ShowScalingControls();
|
ShowScalingControls();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Canvas::DrawContextMenuItem(const ContextMenuItem& item) {
|
void Canvas::DrawContextMenuItem(const gui::CanvasMenuItem& item) {
|
||||||
if (!item.enabled_condition()) {
|
// Phase 4: Use RenderMenuItem from canvas_menu.h for consistent rendering
|
||||||
ImGui::BeginDisabled();
|
auto popup_callback = [this](const std::string& id, std::function<void()> callback) {
|
||||||
}
|
popup_registry_.Open(id, callback);
|
||||||
|
};
|
||||||
if (item.subitems.empty()) {
|
|
||||||
// Simple menu item
|
gui::RenderMenuItem(item, popup_callback);
|
||||||
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) {
|
|
||||||
DrawContextMenuItem(subitem);
|
|
||||||
}
|
|
||||||
ImGui::EndMenu();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!item.enabled_condition()) {
|
|
||||||
ImGui::EndDisabled();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Canvas::AddContextMenuItem(const ContextMenuItem& item) {
|
void Canvas::AddContextMenuItem(const gui::CanvasMenuItem& item) {
|
||||||
context_menu_items_.push_back(item);
|
// Phase 4: Add to editor menu definition
|
||||||
|
// Items are added to a default section with editor-specific priority
|
||||||
|
if (editor_menu_.sections.empty()) {
|
||||||
|
CanvasMenuSection section;
|
||||||
|
section.priority = MenuSectionPriority::kEditorSpecific;
|
||||||
|
section.separator_after = true;
|
||||||
|
editor_menu_.sections.push_back(section);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add to the last section (or create new if the last isn't editor-specific)
|
||||||
|
auto& last_section = editor_menu_.sections.back();
|
||||||
|
if (last_section.priority != MenuSectionPriority::kEditorSpecific) {
|
||||||
|
CanvasMenuSection new_section;
|
||||||
|
new_section.priority = MenuSectionPriority::kEditorSpecific;
|
||||||
|
new_section.separator_after = true;
|
||||||
|
editor_menu_.sections.push_back(new_section);
|
||||||
|
editor_menu_.sections.back().items.push_back(item);
|
||||||
|
} else {
|
||||||
|
last_section.items.push_back(item);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Canvas::ClearContextMenuItems() {
|
void Canvas::ClearContextMenuItems() {
|
||||||
context_menu_items_.clear();
|
editor_menu_.sections.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Canvas::OpenPersistentPopup(const std::string& popup_id, std::function<void()> render_callback) {
|
void Canvas::OpenPersistentPopup(const std::string& popup_id,
|
||||||
// Phase 3: Delegate to new popup registry
|
std::function<void()> render_callback) {
|
||||||
|
// Phase 4: Simplified popup management (no legacy synchronization)
|
||||||
popup_registry_.Open(popup_id, render_callback);
|
popup_registry_.Open(popup_id, render_callback);
|
||||||
|
|
||||||
// Maintain backward compatibility with legacy active_popups_ vector
|
|
||||||
// TODO(Phase 4): Remove this synchronization once all editors migrate
|
|
||||||
bool found = false;
|
|
||||||
for (auto& popup : active_popups_) {
|
|
||||||
if (popup.popup_id == popup_id) {
|
|
||||||
popup.is_open = true;
|
|
||||||
popup.render_callback = render_callback;
|
|
||||||
found = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!found) {
|
|
||||||
PopupState new_popup;
|
|
||||||
new_popup.popup_id = popup_id;
|
|
||||||
new_popup.is_open = true;
|
|
||||||
new_popup.render_callback = render_callback;
|
|
||||||
active_popups_.push_back(new_popup);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Canvas::ClosePersistentPopup(const std::string& popup_id) {
|
void Canvas::ClosePersistentPopup(const std::string& popup_id) {
|
||||||
// Phase 3: Delegate to new popup registry
|
// Phase 4: Simplified popup management (no legacy synchronization)
|
||||||
popup_registry_.Close(popup_id);
|
popup_registry_.Close(popup_id);
|
||||||
|
|
||||||
// Maintain backward compatibility with legacy active_popups_ vector
|
|
||||||
// TODO(Phase 4): Remove this synchronization once all editors migrate
|
|
||||||
for (auto& popup : active_popups_) {
|
|
||||||
if (popup.popup_id == popup_id) {
|
|
||||||
popup.is_open = false;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Canvas::RenderPersistentPopups() {
|
void Canvas::RenderPersistentPopups() {
|
||||||
// Phase 3: Delegate to new popup registry
|
// Phase 4: Simplified rendering (no legacy synchronization)
|
||||||
popup_registry_.RenderAll();
|
popup_registry_.RenderAll();
|
||||||
|
|
||||||
// Maintain backward compatibility: sync legacy vector with registry state
|
|
||||||
// TODO(Phase 4): Remove this synchronization once all editors migrate
|
|
||||||
const auto& registry_popups = popup_registry_.GetPopups();
|
|
||||||
|
|
||||||
// Remove closed popups from legacy vector
|
|
||||||
auto it = active_popups_.begin();
|
|
||||||
while (it != active_popups_.end()) {
|
|
||||||
bool found_in_registry = false;
|
|
||||||
for (const auto& reg_popup : registry_popups) {
|
|
||||||
if (reg_popup.popup_id == it->popup_id && reg_popup.is_open) {
|
|
||||||
found_in_registry = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!found_in_registry) {
|
|
||||||
it = active_popups_.erase(it);
|
|
||||||
} else {
|
|
||||||
++it;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Canvas::SetZoomToFit(const gfx::Bitmap& bitmap) {
|
void Canvas::SetZoomToFit(const gfx::Bitmap& bitmap) {
|
||||||
|
|||||||
@@ -153,38 +153,8 @@ class Canvas {
|
|||||||
// This routine also handles the scrolling for the canvas.
|
// This routine also handles the scrolling for the canvas.
|
||||||
void DrawContextMenu();
|
void DrawContextMenu();
|
||||||
|
|
||||||
// Context menu system for consumers to add their own menu elements
|
// Phase 4: Use unified menu item definition from canvas_menu.h
|
||||||
struct ContextMenuItem {
|
using CanvasMenuItem = gui::CanvasMenuItem;
|
||||||
std::string label;
|
|
||||||
std::string shortcut;
|
|
||||||
std::function<void()> callback;
|
|
||||||
std::function<bool()> enabled_condition = []() { return true; };
|
|
||||||
std::vector<ContextMenuItem> subitems;
|
|
||||||
|
|
||||||
// Helper constructor for simple items
|
|
||||||
ContextMenuItem() = default;
|
|
||||||
ContextMenuItem(const std::string& lbl, std::function<void()> cb,
|
|
||||||
const std::string& sc = "")
|
|
||||||
: label(lbl), shortcut(sc), callback(std::move(cb)) {}
|
|
||||||
|
|
||||||
// Helper to create disabled item
|
|
||||||
static ContextMenuItem Disabled(const std::string& lbl) {
|
|
||||||
ContextMenuItem item;
|
|
||||||
item.label = lbl;
|
|
||||||
item.enabled_condition = []() { return false; };
|
|
||||||
return item;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Helper to create conditional item
|
|
||||||
static ContextMenuItem Conditional(const std::string& lbl, std::function<void()> cb,
|
|
||||||
std::function<bool()> condition) {
|
|
||||||
ContextMenuItem item;
|
|
||||||
item.label = lbl;
|
|
||||||
item.callback = std::move(cb);
|
|
||||||
item.enabled_condition = std::move(condition);
|
|
||||||
return item;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// BPP format UI components
|
// BPP format UI components
|
||||||
std::unique_ptr<gui::BppFormatUI> bpp_format_ui_;
|
std::unique_ptr<gui::BppFormatUI> bpp_format_ui_;
|
||||||
@@ -198,8 +168,12 @@ class Canvas {
|
|||||||
std::shared_ptr<CanvasPerformanceIntegration> performance_integration_;
|
std::shared_ptr<CanvasPerformanceIntegration> performance_integration_;
|
||||||
CanvasInteractionHandler interaction_handler_;
|
CanvasInteractionHandler interaction_handler_;
|
||||||
|
|
||||||
void AddContextMenuItem(const ContextMenuItem& item);
|
void AddContextMenuItem(const gui::CanvasMenuItem& item);
|
||||||
void ClearContextMenuItems();
|
void ClearContextMenuItems();
|
||||||
|
|
||||||
|
// Phase 4: Access to editor-provided menu definition
|
||||||
|
CanvasMenuDefinition& editor_menu() { return editor_menu_; }
|
||||||
|
const CanvasMenuDefinition& editor_menu() const { return editor_menu_; }
|
||||||
void SetContextMenuEnabled(bool enabled) { context_menu_enabled_ = enabled; }
|
void SetContextMenuEnabled(bool enabled) { context_menu_enabled_ = enabled; }
|
||||||
|
|
||||||
// Persistent popup management for context menu actions
|
// Persistent popup management for context menu actions
|
||||||
@@ -415,7 +389,7 @@ class Canvas {
|
|||||||
Rom *rom() const { return rom_; }
|
Rom *rom() const { return rom_; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void DrawContextMenuItem(const ContextMenuItem& item);
|
void DrawContextMenuItem(const gui::CanvasMenuItem& item);
|
||||||
|
|
||||||
// Modular configuration and state
|
// Modular configuration and state
|
||||||
gfx::IRenderer* renderer_ = nullptr;
|
gfx::IRenderer* renderer_ = nullptr;
|
||||||
@@ -433,22 +407,12 @@ class Canvas {
|
|||||||
bool is_hovered_ = false;
|
bool is_hovered_ = false;
|
||||||
bool refresh_graphics_ = false;
|
bool refresh_graphics_ = false;
|
||||||
|
|
||||||
// Context menu system
|
// Phase 4: Context menu system (declarative menu definition)
|
||||||
std::vector<ContextMenuItem> context_menu_items_;
|
CanvasMenuDefinition editor_menu_;
|
||||||
bool context_menu_enabled_ = true;
|
bool context_menu_enabled_ = true;
|
||||||
|
|
||||||
// Persistent popup state for context menu actions
|
// Phase 4: Persistent popup state for context menu actions (unified registry)
|
||||||
// Phase 3: New popup registry (preferred for new code)
|
|
||||||
PopupRegistry popup_registry_;
|
PopupRegistry popup_registry_;
|
||||||
|
|
||||||
// Legacy popup state (deprecated - kept for backward compatibility during migration)
|
|
||||||
// TODO(Phase 4): Remove once all editors use popup_registry_ directly
|
|
||||||
struct PopupState {
|
|
||||||
std::string popup_id;
|
|
||||||
bool is_open = false;
|
|
||||||
std::function<void()> render_callback;
|
|
||||||
};
|
|
||||||
std::vector<PopupState> active_popups_;
|
|
||||||
|
|
||||||
// Legacy members (to be gradually replaced)
|
// Legacy members (to be gradually replaced)
|
||||||
int current_labels_ = 0;
|
int current_labels_ = 0;
|
||||||
|
|||||||
@@ -6,8 +6,7 @@
|
|||||||
#include "app/gui/widgets/palette_editor_widget.h"
|
#include "app/gui/widgets/palette_editor_widget.h"
|
||||||
#include "app/gui/core/icons.h"
|
#include "app/gui/core/icons.h"
|
||||||
#include "app/gui/core/color.h"
|
#include "app/gui/core/color.h"
|
||||||
#include "app/gui/canvas/canvas_modals.h"
|
#include "app/gui/canvas/canvas.h"
|
||||||
#include "app/gui/widgets/palette_editor_widget.h"
|
|
||||||
#include "imgui/imgui.h"
|
#include "imgui/imgui.h"
|
||||||
|
|
||||||
namespace yaze {
|
namespace yaze {
|
||||||
@@ -50,11 +49,11 @@ void CanvasContextMenu::SetUsageMode(CanvasUsage usage) {
|
|||||||
current_usage_ = usage;
|
current_usage_ = usage;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CanvasContextMenu::AddMenuItem(const ContextMenuItem& item) {
|
void CanvasContextMenu::AddMenuItem(const CanvasMenuItem& item) {
|
||||||
global_items_.push_back(item);
|
global_items_.push_back(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CanvasContextMenu::AddMenuItem(const ContextMenuItem& item, CanvasUsage usage) {
|
void CanvasContextMenu::AddMenuItem(const CanvasMenuItem& item, CanvasUsage usage) {
|
||||||
usage_specific_items_[usage].push_back(item);
|
usage_specific_items_[usage].push_back(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -63,10 +62,12 @@ void CanvasContextMenu::ClearMenuItems() {
|
|||||||
usage_specific_items_.clear();
|
usage_specific_items_.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CanvasContextMenu::Render(const std::string& context_id, const ImVec2& mouse_pos, Rom* rom,
|
void CanvasContextMenu::Render(const std::string& context_id,
|
||||||
const gfx::Bitmap* bitmap, const gfx::SnesPalette* palette,
|
const ImVec2& /* mouse_pos */, Rom* rom,
|
||||||
|
const gfx::Bitmap* bitmap,
|
||||||
|
const gfx::SnesPalette* /* palette */,
|
||||||
const std::function<void(Command, const CanvasConfig&)>& command_handler,
|
const std::function<void(Command, const CanvasConfig&)>& command_handler,
|
||||||
CanvasConfig current_config) {
|
CanvasConfig current_config, Canvas* canvas) {
|
||||||
if (!enabled_) return;
|
if (!enabled_) return;
|
||||||
|
|
||||||
// Context menu (under default mouse threshold)
|
// Context menu (under default mouse threshold)
|
||||||
@@ -75,53 +76,61 @@ void CanvasContextMenu::Render(const std::string& context_id, const ImVec2& mous
|
|||||||
ImGui::OpenPopupOnItemClick(context_id.c_str(), ImGuiPopupFlags_MouseButtonRight);
|
ImGui::OpenPopupOnItemClick(context_id.c_str(), ImGuiPopupFlags_MouseButtonRight);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Contents of the Context Menu
|
// Phase 4: Popup callback for automatic popup management
|
||||||
|
auto popup_callback = [canvas](const std::string& id, std::function<void()> callback) {
|
||||||
|
if (canvas) {
|
||||||
|
canvas->GetPopupRegistry().Open(id, callback);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Contents of the Context Menu (Phase 4: Priority-based ordering)
|
||||||
if (ImGui::BeginPopup(context_id.c_str())) {
|
if (ImGui::BeginPopup(context_id.c_str())) {
|
||||||
// Render usage-specific menu first
|
// PRIORITY 0: Editor-specific items (from Canvas::editor_menu_)
|
||||||
RenderUsageSpecificMenu();
|
if (canvas && !canvas->editor_menu().sections.empty()) {
|
||||||
|
RenderCanvasMenu(canvas->editor_menu(), popup_callback);
|
||||||
// Add separator if there are usage-specific items
|
|
||||||
if (!usage_specific_items_[current_usage_].empty()) {
|
|
||||||
ImGui::Separator();
|
ImGui::Separator();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Render view controls
|
// Also render usage-specific items (legacy support)
|
||||||
RenderViewControlsMenu(command_handler, current_config);
|
if (!usage_specific_items_[current_usage_].empty()) {
|
||||||
ImGui::Separator();
|
RenderUsageSpecificMenu(popup_callback);
|
||||||
|
ImGui::Separator();
|
||||||
|
}
|
||||||
|
|
||||||
// Render canvas properties
|
// PRIORITY 10: Bitmap/Palette operations
|
||||||
RenderCanvasPropertiesMenu(command_handler, current_config);
|
|
||||||
ImGui::Separator();
|
|
||||||
|
|
||||||
// Render bitmap operations if bitmap is available
|
|
||||||
if (bitmap) {
|
if (bitmap) {
|
||||||
RenderBitmapOperationsMenu(const_cast<gfx::Bitmap*>(bitmap));
|
RenderBitmapOperationsMenu(const_cast<gfx::Bitmap*>(bitmap));
|
||||||
ImGui::Separator();
|
ImGui::Separator();
|
||||||
}
|
|
||||||
|
|
||||||
// Render palette operations if bitmap is available
|
|
||||||
if (bitmap) {
|
|
||||||
RenderPaletteOperationsMenu(rom, const_cast<gfx::Bitmap*>(bitmap));
|
RenderPaletteOperationsMenu(rom, const_cast<gfx::Bitmap*>(bitmap));
|
||||||
ImGui::Separator();
|
ImGui::Separator();
|
||||||
}
|
|
||||||
|
|
||||||
if (bitmap) {
|
|
||||||
RenderBppOperationsMenu(bitmap);
|
RenderBppOperationsMenu(bitmap);
|
||||||
ImGui::Separator();
|
ImGui::Separator();
|
||||||
}
|
}
|
||||||
|
|
||||||
RenderPerformanceMenu();
|
// PRIORITY 20: Canvas properties
|
||||||
|
RenderCanvasPropertiesMenu(command_handler, current_config);
|
||||||
ImGui::Separator();
|
ImGui::Separator();
|
||||||
|
|
||||||
|
RenderViewControlsMenu(command_handler, current_config);
|
||||||
|
ImGui::Separator();
|
||||||
|
|
||||||
RenderGridControlsMenu(command_handler, current_config);
|
RenderGridControlsMenu(command_handler, current_config);
|
||||||
ImGui::Separator();
|
ImGui::Separator();
|
||||||
|
|
||||||
RenderScalingControlsMenu(command_handler, current_config);
|
RenderScalingControlsMenu(command_handler, current_config);
|
||||||
|
|
||||||
|
// PRIORITY 30: Debug/Performance
|
||||||
|
if (ImGui::GetIO().KeyCtrl) { // Only show when Ctrl is held
|
||||||
|
ImGui::Separator();
|
||||||
|
RenderPerformanceMenu();
|
||||||
|
}
|
||||||
|
|
||||||
// Render global menu items
|
// Render global menu items (if any)
|
||||||
if (!global_items_.empty()) {
|
if (!global_items_.empty()) {
|
||||||
ImGui::Separator();
|
ImGui::Separator();
|
||||||
RenderMenuSection("Custom Actions", global_items_);
|
RenderMenuSection("Custom Actions", global_items_, popup_callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui::EndPopup();
|
ImGui::EndPopup();
|
||||||
@@ -137,70 +146,44 @@ void CanvasContextMenu::SetCanvasState(const ImVec2& canvas_size,
|
|||||||
float global_scale,
|
float global_scale,
|
||||||
float grid_step,
|
float grid_step,
|
||||||
bool enable_grid,
|
bool enable_grid,
|
||||||
bool enable_hex_labels,
|
bool /* enable_hex_labels */,
|
||||||
bool enable_custom_labels,
|
bool /* enable_custom_labels */,
|
||||||
bool enable_context_menu,
|
bool /* enable_context_menu */,
|
||||||
bool is_draggable,
|
bool /* is_draggable */,
|
||||||
bool auto_resize,
|
bool /* auto_resize */,
|
||||||
const ImVec2& scrolling) {
|
const ImVec2& scrolling) {
|
||||||
canvas_size_ = canvas_size;
|
canvas_size_ = canvas_size;
|
||||||
content_size_ = content_size;
|
content_size_ = content_size;
|
||||||
global_scale_ = global_scale;
|
global_scale_ = global_scale;
|
||||||
grid_step_ = grid_step;
|
grid_step_ = grid_step;
|
||||||
enable_grid_ = enable_grid;
|
enable_grid_ = enable_grid;
|
||||||
enable_hex_labels_ = enable_hex_labels_;
|
enable_hex_labels_ = false; // Field not used anymore
|
||||||
enable_custom_labels_ = enable_custom_labels_;
|
enable_custom_labels_ = false; // Field not used anymore
|
||||||
enable_context_menu_ = enable_context_menu_;
|
enable_context_menu_ = true; // Field not used anymore
|
||||||
is_draggable_ = is_draggable_;
|
is_draggable_ = false; // Field not used anymore
|
||||||
auto_resize_ = auto_resize_;
|
auto_resize_ = false; // Field not used anymore
|
||||||
scrolling_ = scrolling;
|
scrolling_ = scrolling;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CanvasContextMenu::RenderMenuItem(const ContextMenuItem& item) {
|
void CanvasContextMenu::RenderMenuItem(const CanvasMenuItem& item,
|
||||||
if (!item.visible_condition()) {
|
std::function<void(const std::string&, std::function<void()>)> popup_callback) {
|
||||||
return;
|
// Phase 4: Delegate to canvas_menu.h implementation
|
||||||
}
|
gui::RenderMenuItem(item, popup_callback);
|
||||||
|
|
||||||
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,
|
void CanvasContextMenu::RenderMenuSection(const std::string& title,
|
||||||
const std::vector<ContextMenuItem>& items) {
|
const std::vector<CanvasMenuItem>& items,
|
||||||
|
std::function<void(const std::string&, std::function<void()>)> popup_callback) {
|
||||||
if (items.empty()) return;
|
if (items.empty()) return;
|
||||||
|
|
||||||
ImGui::TextColored(ImVec4(0.7F, 0.7F, 0.7F, 1.0F), "%s", title.c_str());
|
ImGui::TextColored(ImVec4(0.7F, 0.7F, 0.7F, 1.0F), "%s", title.c_str());
|
||||||
for (const auto& item : items) {
|
for (const auto& item : items) {
|
||||||
RenderMenuItem(item);
|
RenderMenuItem(item, popup_callback);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CanvasContextMenu::RenderUsageSpecificMenu() {
|
void CanvasContextMenu::RenderUsageSpecificMenu(
|
||||||
|
std::function<void(const std::string&, std::function<void()>)> popup_callback) {
|
||||||
auto it = usage_specific_items_.find(current_usage_);
|
auto it = usage_specific_items_.find(current_usage_);
|
||||||
if (it == usage_specific_items_.end() || it->second.empty()) {
|
if (it == usage_specific_items_.end() || it->second.empty()) {
|
||||||
return;
|
return;
|
||||||
@@ -213,7 +196,7 @@ void CanvasContextMenu::RenderUsageSpecificMenu() {
|
|||||||
ImGui::Separator();
|
ImGui::Separator();
|
||||||
|
|
||||||
for (const auto& item : it->second) {
|
for (const auto& item : it->second) {
|
||||||
RenderMenuItem(item);
|
RenderMenuItem(item, popup_callback);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -397,7 +380,7 @@ void CanvasContextMenu::DrawROMPaletteSelector() {
|
|||||||
palette_editor_->DrawROMPaletteSelector();
|
palette_editor_->DrawROMPaletteSelector();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CanvasContextMenu::RenderBppOperationsMenu(const gfx::Bitmap* bitmap) {
|
void CanvasContextMenu::RenderBppOperationsMenu(const gfx::Bitmap* /* bitmap */) {
|
||||||
if (ImGui::BeginMenu(ICON_MD_SWAP_HORIZ " BPP Operations")) {
|
if (ImGui::BeginMenu(ICON_MD_SWAP_HORIZ " BPP Operations")) {
|
||||||
if (ImGui::MenuItem("Format Analysis...")) {
|
if (ImGui::MenuItem("Format Analysis...")) {
|
||||||
// Open BPP analysis
|
// Open BPP analysis
|
||||||
@@ -528,85 +511,80 @@ ImVec4 CanvasContextMenu::GetUsageModeColor(CanvasUsage usage) const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void CanvasContextMenu::CreateDefaultMenuItems() {
|
void CanvasContextMenu::CreateDefaultMenuItems() {
|
||||||
// Create default menu items for different usage modes
|
// Phase 4: Create default menu items using unified CanvasMenuItem
|
||||||
|
|
||||||
// Tile Painting mode items
|
// Tile Painting mode items
|
||||||
ContextMenuItem tile_paint_item("Paint Tile", "paint", []() {
|
CanvasMenuItem tile_paint_item("Paint Tile", "paint", []() {
|
||||||
// Tile painting action
|
// Tile painting action
|
||||||
});
|
});
|
||||||
usage_specific_items_[CanvasUsage::kTilePainting].push_back(tile_paint_item);
|
usage_specific_items_[CanvasUsage::kTilePainting].push_back(tile_paint_item);
|
||||||
|
|
||||||
// Tile Selecting mode items
|
// Tile Selecting mode items
|
||||||
ContextMenuItem tile_select_item("Select Tile", "select", []() {
|
CanvasMenuItem tile_select_item("Select Tile", "select", []() {
|
||||||
// Tile selection action
|
// Tile selection action
|
||||||
});
|
});
|
||||||
usage_specific_items_[CanvasUsage::kTileSelecting].push_back(tile_select_item);
|
usage_specific_items_[CanvasUsage::kTileSelecting].push_back(tile_select_item);
|
||||||
|
|
||||||
// Rectangle Selection mode items
|
// Rectangle Selection mode items
|
||||||
ContextMenuItem rect_select_item("Select Rectangle", "rect", []() {
|
CanvasMenuItem rect_select_item("Select Rectangle", "rect", []() {
|
||||||
// Rectangle selection action
|
// Rectangle selection action
|
||||||
});
|
});
|
||||||
usage_specific_items_[CanvasUsage::kSelectRectangle].push_back(rect_select_item);
|
usage_specific_items_[CanvasUsage::kSelectRectangle].push_back(rect_select_item);
|
||||||
|
|
||||||
// Color Painting mode items
|
// Color Painting mode items
|
||||||
ContextMenuItem color_paint_item("Paint Color", "color", []() {
|
CanvasMenuItem color_paint_item("Paint Color", "color", []() {
|
||||||
// Color painting action
|
// Color painting action
|
||||||
});
|
});
|
||||||
usage_specific_items_[CanvasUsage::kColorPainting].push_back(color_paint_item);
|
usage_specific_items_[CanvasUsage::kColorPainting].push_back(color_paint_item);
|
||||||
|
|
||||||
// Bitmap Editing mode items
|
// Bitmap Editing mode items
|
||||||
ContextMenuItem bitmap_edit_item("Edit Bitmap", "edit", []() {
|
CanvasMenuItem bitmap_edit_item("Edit Bitmap", "edit", []() {
|
||||||
// Bitmap editing action
|
// Bitmap editing action
|
||||||
});
|
});
|
||||||
usage_specific_items_[CanvasUsage::kBitmapEditing].push_back(bitmap_edit_item);
|
usage_specific_items_[CanvasUsage::kBitmapEditing].push_back(bitmap_edit_item);
|
||||||
|
|
||||||
// Palette Editing mode items
|
// Palette Editing mode items
|
||||||
ContextMenuItem palette_edit_item("Edit Palette", "palette", []() {
|
CanvasMenuItem palette_edit_item("Edit Palette", "palette", []() {
|
||||||
// Palette editing action
|
// Palette editing action
|
||||||
});
|
});
|
||||||
usage_specific_items_[CanvasUsage::kPaletteEditing].push_back(palette_edit_item);
|
usage_specific_items_[CanvasUsage::kPaletteEditing].push_back(palette_edit_item);
|
||||||
|
|
||||||
// BPP Conversion mode items
|
// BPP Conversion mode items
|
||||||
ContextMenuItem bpp_convert_item("Convert Format", "convert", []() {
|
CanvasMenuItem bpp_convert_item("Convert Format", "convert", []() {
|
||||||
// BPP conversion action
|
// BPP conversion action
|
||||||
});
|
});
|
||||||
usage_specific_items_[CanvasUsage::kBppConversion].push_back(bpp_convert_item);
|
usage_specific_items_[CanvasUsage::kBppConversion].push_back(bpp_convert_item);
|
||||||
|
|
||||||
// Performance Mode items
|
// Performance Mode items
|
||||||
ContextMenuItem perf_item("Performance Analysis", "perf", []() {
|
CanvasMenuItem perf_item("Performance Analysis", "perf", []() {
|
||||||
// Performance analysis action
|
// Performance analysis action
|
||||||
});
|
});
|
||||||
usage_specific_items_[CanvasUsage::kPerformanceMode].push_back(perf_item);
|
usage_specific_items_[CanvasUsage::kPerformanceMode].push_back(perf_item);
|
||||||
}
|
}
|
||||||
|
|
||||||
CanvasContextMenu::ContextMenuItem CanvasContextMenu::CreateViewMenuItem(const std::string& label,
|
CanvasContextMenu::CanvasMenuItem CanvasContextMenu::CreateViewMenuItem(
|
||||||
const std::string& icon,
|
const std::string& label, const std::string& icon, std::function<void()> callback) {
|
||||||
std::function<void()> callback) {
|
return CanvasMenuItem(label, icon, callback);
|
||||||
return ContextMenuItem(label, icon, callback);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
CanvasContextMenu::ContextMenuItem CanvasContextMenu::CreateBitmapMenuItem(const std::string& label,
|
CanvasContextMenu::CanvasMenuItem CanvasContextMenu::CreateBitmapMenuItem(
|
||||||
const std::string& icon,
|
const std::string& label, const std::string& icon, std::function<void()> callback) {
|
||||||
std::function<void()> callback) {
|
return CanvasMenuItem(label, icon, callback);
|
||||||
return ContextMenuItem(label, icon, callback);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
CanvasContextMenu::ContextMenuItem CanvasContextMenu::CreatePaletteMenuItem(const std::string& label,
|
CanvasContextMenu::CanvasMenuItem CanvasContextMenu::CreatePaletteMenuItem(
|
||||||
const std::string& icon,
|
const std::string& label, const std::string& icon, std::function<void()> callback) {
|
||||||
std::function<void()> callback) {
|
return CanvasMenuItem(label, icon, callback);
|
||||||
return ContextMenuItem(label, icon, callback);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
CanvasContextMenu::ContextMenuItem CanvasContextMenu::CreateBppMenuItem(const std::string& label,
|
CanvasContextMenu::CanvasMenuItem CanvasContextMenu::CreateBppMenuItem(
|
||||||
const std::string& icon,
|
const std::string& label, const std::string& icon, std::function<void()> callback) {
|
||||||
std::function<void()> callback) {
|
return CanvasMenuItem(label, icon, callback);
|
||||||
return ContextMenuItem(label, icon, callback);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
CanvasContextMenu::ContextMenuItem CanvasContextMenu::CreatePerformanceMenuItem(const std::string& label,
|
CanvasContextMenu::CanvasMenuItem CanvasContextMenu::CreatePerformanceMenuItem(
|
||||||
const std::string& icon,
|
const std::string& label, const std::string& icon, std::function<void()> callback) {
|
||||||
std::function<void()> callback) {
|
return CanvasMenuItem(label, icon, callback);
|
||||||
return ContextMenuItem(label, icon, callback);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace gui
|
} // namespace gui
|
||||||
|
|||||||
@@ -10,6 +10,7 @@
|
|||||||
#include "app/gfx/types/snes_palette.h"
|
#include "app/gfx/types/snes_palette.h"
|
||||||
#include "app/gui/core/icons.h"
|
#include "app/gui/core/icons.h"
|
||||||
#include "app/gui/canvas/canvas_modals.h"
|
#include "app/gui/canvas/canvas_modals.h"
|
||||||
|
#include "app/gui/canvas/canvas_menu.h"
|
||||||
#include "canvas_usage_tracker.h"
|
#include "canvas_usage_tracker.h"
|
||||||
#include "imgui/imgui.h"
|
#include "imgui/imgui.h"
|
||||||
|
|
||||||
@@ -18,6 +19,7 @@ namespace gui {
|
|||||||
|
|
||||||
// Forward declarations
|
// Forward declarations
|
||||||
class PaletteEditorWidget;
|
class PaletteEditorWidget;
|
||||||
|
class Canvas;
|
||||||
|
|
||||||
class CanvasContextMenu {
|
class CanvasContextMenu {
|
||||||
public:
|
public:
|
||||||
@@ -41,36 +43,24 @@ class CanvasContextMenu {
|
|||||||
|
|
||||||
CanvasContextMenu() = default;
|
CanvasContextMenu() = default;
|
||||||
|
|
||||||
struct ContextMenuItem {
|
// Phase 4: Use unified CanvasMenuItem from canvas_menu.h
|
||||||
std::string label;
|
using CanvasMenuItem = gui::CanvasMenuItem;
|
||||||
std::string shortcut;
|
|
||||||
std::string icon;
|
|
||||||
std::function<void()> callback;
|
|
||||||
std::function<bool()> enabled_condition = []() { return true; };
|
|
||||||
std::function<bool()> visible_condition = []() { return true; };
|
|
||||||
std::vector<ContextMenuItem> subitems;
|
|
||||||
ImVec4 color = ImVec4(1, 1, 1, 1);
|
|
||||||
bool separator_after = false;
|
|
||||||
|
|
||||||
ContextMenuItem() = default;
|
|
||||||
ContextMenuItem(const std::string& lbl, const std::string& ico,
|
|
||||||
std::function<void()> cb, const std::string& sc = "")
|
|
||||||
: label(lbl), shortcut(sc), icon(ico), callback(std::move(cb)) {}
|
|
||||||
};
|
|
||||||
|
|
||||||
void Initialize(const std::string& canvas_id);
|
void Initialize(const std::string& canvas_id);
|
||||||
void SetUsageMode(CanvasUsage usage);
|
void SetUsageMode(CanvasUsage usage);
|
||||||
void AddMenuItem(const ContextMenuItem& item);
|
void AddMenuItem(const CanvasMenuItem& item);
|
||||||
void AddMenuItem(const ContextMenuItem& item, CanvasUsage usage);
|
void AddMenuItem(const CanvasMenuItem& item, CanvasUsage usage);
|
||||||
void ClearMenuItems();
|
void ClearMenuItems();
|
||||||
|
|
||||||
|
// Phase 4: Render with editor menu integration and priority ordering
|
||||||
void Render(const std::string& context_id,
|
void Render(const std::string& context_id,
|
||||||
const ImVec2& mouse_pos,
|
const ImVec2& mouse_pos,
|
||||||
Rom* rom,
|
Rom* rom,
|
||||||
const gfx::Bitmap* bitmap,
|
const gfx::Bitmap* bitmap,
|
||||||
const gfx::SnesPalette* palette,
|
const gfx::SnesPalette* palette,
|
||||||
const std::function<void(Command, const CanvasConfig&)>& command_handler,
|
const std::function<void(Command, const CanvasConfig&)>& command_handler,
|
||||||
CanvasConfig current_config);
|
CanvasConfig current_config,
|
||||||
|
Canvas* canvas);
|
||||||
|
|
||||||
bool ShouldShowContextMenu() const;
|
bool ShouldShowContextMenu() const;
|
||||||
void SetEnabled(bool enabled) { enabled_ = enabled; }
|
void SetEnabled(bool enabled) { enabled_ = enabled; }
|
||||||
@@ -114,13 +104,15 @@ class CanvasContextMenu {
|
|||||||
|
|
||||||
void DrawROMPaletteSelector();
|
void DrawROMPaletteSelector();
|
||||||
|
|
||||||
std::unordered_map<CanvasUsage, std::vector<ContextMenuItem>> usage_specific_items_;
|
std::unordered_map<CanvasUsage, std::vector<CanvasMenuItem>> usage_specific_items_;
|
||||||
std::vector<ContextMenuItem> global_items_;
|
std::vector<CanvasMenuItem> global_items_;
|
||||||
|
|
||||||
void RenderMenuItem(const ContextMenuItem& item);
|
void RenderMenuItem(const CanvasMenuItem& item,
|
||||||
|
std::function<void(const std::string&, std::function<void()>)> popup_callback);
|
||||||
void RenderMenuSection(const std::string& title,
|
void RenderMenuSection(const std::string& title,
|
||||||
const std::vector<ContextMenuItem>& items);
|
const std::vector<CanvasMenuItem>& items,
|
||||||
void RenderUsageSpecificMenu();
|
std::function<void(const std::string&, std::function<void()>)> popup_callback);
|
||||||
|
void RenderUsageSpecificMenu(std::function<void(const std::string&, std::function<void()>)> popup_callback);
|
||||||
void RenderViewControlsMenu(const std::function<void(Command, const CanvasConfig&)>& command_handler,
|
void RenderViewControlsMenu(const std::function<void(Command, const CanvasConfig&)>& command_handler,
|
||||||
CanvasConfig current_config);
|
CanvasConfig current_config);
|
||||||
void RenderCanvasPropertiesMenu(const std::function<void(Command, const CanvasConfig&)>& command_handler,
|
void RenderCanvasPropertiesMenu(const std::function<void(Command, const CanvasConfig&)>& command_handler,
|
||||||
@@ -140,21 +132,21 @@ class CanvasContextMenu {
|
|||||||
ImVec4 GetUsageModeColor(CanvasUsage usage) const;
|
ImVec4 GetUsageModeColor(CanvasUsage usage) const;
|
||||||
|
|
||||||
void CreateDefaultMenuItems();
|
void CreateDefaultMenuItems();
|
||||||
ContextMenuItem CreateViewMenuItem(const std::string& label,
|
CanvasMenuItem CreateViewMenuItem(const std::string& label,
|
||||||
|
const std::string& icon,
|
||||||
|
std::function<void()> callback);
|
||||||
|
CanvasMenuItem CreateBitmapMenuItem(const std::string& label,
|
||||||
const std::string& icon,
|
const std::string& icon,
|
||||||
std::function<void()> callback);
|
std::function<void()> callback);
|
||||||
ContextMenuItem CreateBitmapMenuItem(const std::string& label,
|
CanvasMenuItem CreatePaletteMenuItem(const std::string& label,
|
||||||
const std::string& icon,
|
const std::string& icon,
|
||||||
std::function<void()> callback);
|
std::function<void()> callback);
|
||||||
ContextMenuItem CreatePaletteMenuItem(const std::string& label,
|
CanvasMenuItem CreateBppMenuItem(const std::string& label,
|
||||||
const std::string& icon,
|
const std::string& icon,
|
||||||
std::function<void()> callback);
|
std::function<void()> callback);
|
||||||
ContextMenuItem CreateBppMenuItem(const std::string& label,
|
CanvasMenuItem CreatePerformanceMenuItem(const std::string& label,
|
||||||
const std::string& icon,
|
const std::string& icon,
|
||||||
std::function<void()> callback);
|
std::function<void()> callback);
|
||||||
ContextMenuItem CreatePerformanceMenuItem(const std::string& label,
|
|
||||||
const std::string& icon,
|
|
||||||
std::function<void()> callback);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace gui
|
} // namespace gui
|
||||||
|
|||||||
@@ -11,6 +11,22 @@
|
|||||||
namespace yaze {
|
namespace yaze {
|
||||||
namespace gui {
|
namespace gui {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Menu section priority for controlling rendering order
|
||||||
|
*
|
||||||
|
* Lower values render first in the context menu:
|
||||||
|
* - Editor-specific items (0) appear at the top
|
||||||
|
* - Bitmap/palette operations (10) in the middle
|
||||||
|
* - Canvas properties (20) near the bottom
|
||||||
|
* - Debug/performance (30) at the bottom
|
||||||
|
*/
|
||||||
|
enum class MenuSectionPriority {
|
||||||
|
kEditorSpecific = 0, // Highest priority - editor-specific actions
|
||||||
|
kBitmapPalette = 10, // Medium priority - bitmap/palette operations
|
||||||
|
kCanvasProperties = 20, // Low priority - canvas settings
|
||||||
|
kDebug = 30 // Lowest priority - debug/performance
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Declarative popup definition for menu items
|
* @brief Declarative popup definition for menu items
|
||||||
*
|
*
|
||||||
@@ -126,6 +142,7 @@ struct CanvasMenuItem {
|
|||||||
* @brief Menu section grouping related menu items
|
* @brief Menu section grouping related menu items
|
||||||
*
|
*
|
||||||
* Provides visual organization of menu items with optional section titles.
|
* Provides visual organization of menu items with optional section titles.
|
||||||
|
* Sections are rendered in priority order.
|
||||||
*/
|
*/
|
||||||
struct CanvasMenuSection {
|
struct CanvasMenuSection {
|
||||||
// Optional section title (rendered as colored text)
|
// Optional section title (rendered as colored text)
|
||||||
@@ -140,6 +157,9 @@ struct CanvasMenuSection {
|
|||||||
// Whether to show a separator after this section
|
// Whether to show a separator after this section
|
||||||
bool separator_after = true;
|
bool separator_after = true;
|
||||||
|
|
||||||
|
// Priority for ordering sections (lower values render first)
|
||||||
|
MenuSectionPriority priority = MenuSectionPriority::kEditorSpecific;
|
||||||
|
|
||||||
// Default constructor
|
// Default constructor
|
||||||
CanvasMenuSection() = default;
|
CanvasMenuSection() = default;
|
||||||
|
|
||||||
@@ -149,6 +169,11 @@ struct CanvasMenuSection {
|
|||||||
// Constructor with title and items
|
// Constructor with title and items
|
||||||
CanvasMenuSection(const std::string& t, const std::vector<CanvasMenuItem>& its)
|
CanvasMenuSection(const std::string& t, const std::vector<CanvasMenuItem>& its)
|
||||||
: title(t), items(its) {}
|
: title(t), items(its) {}
|
||||||
|
|
||||||
|
// Constructor with title, items, and priority
|
||||||
|
CanvasMenuSection(const std::string& t, const std::vector<CanvasMenuItem>& its,
|
||||||
|
MenuSectionPriority prio)
|
||||||
|
: title(t), items(its), priority(prio) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
145
src/app/gui/canvas/canvas_menu_builder.cc
Normal file
145
src/app/gui/canvas/canvas_menu_builder.cc
Normal file
@@ -0,0 +1,145 @@
|
|||||||
|
#include "canvas_menu_builder.h"
|
||||||
|
|
||||||
|
namespace yaze {
|
||||||
|
namespace gui {
|
||||||
|
|
||||||
|
CanvasMenuBuilder& CanvasMenuBuilder::AddItem(const std::string& label,
|
||||||
|
std::function<void()> callback) {
|
||||||
|
CanvasMenuItem item;
|
||||||
|
item.label = label;
|
||||||
|
item.callback = std::move(callback);
|
||||||
|
pending_items_.push_back(item);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
CanvasMenuBuilder& CanvasMenuBuilder::AddItem(const std::string& label,
|
||||||
|
const std::string& icon,
|
||||||
|
std::function<void()> callback) {
|
||||||
|
CanvasMenuItem item;
|
||||||
|
item.label = label;
|
||||||
|
item.icon = icon;
|
||||||
|
item.callback = std::move(callback);
|
||||||
|
pending_items_.push_back(item);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
CanvasMenuBuilder& CanvasMenuBuilder::AddItem(const std::string& label,
|
||||||
|
const std::string& icon,
|
||||||
|
const std::string& shortcut,
|
||||||
|
std::function<void()> callback) {
|
||||||
|
CanvasMenuItem item;
|
||||||
|
item.label = label;
|
||||||
|
item.icon = icon;
|
||||||
|
item.shortcut = shortcut;
|
||||||
|
item.callback = std::move(callback);
|
||||||
|
pending_items_.push_back(item);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
CanvasMenuBuilder& CanvasMenuBuilder::AddPopupItem(
|
||||||
|
const std::string& label, const std::string& popup_id,
|
||||||
|
std::function<void()> render_callback) {
|
||||||
|
CanvasMenuItem item = CanvasMenuItem::WithPopup(label, popup_id, render_callback);
|
||||||
|
pending_items_.push_back(item);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
CanvasMenuBuilder& CanvasMenuBuilder::AddPopupItem(
|
||||||
|
const std::string& label, const std::string& icon,
|
||||||
|
const std::string& popup_id, std::function<void()> render_callback) {
|
||||||
|
CanvasMenuItem item = CanvasMenuItem::WithPopup(label, popup_id, render_callback);
|
||||||
|
item.icon = icon;
|
||||||
|
pending_items_.push_back(item);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
CanvasMenuBuilder& CanvasMenuBuilder::AddConditionalItem(
|
||||||
|
const std::string& label, std::function<void()> callback,
|
||||||
|
std::function<bool()> condition) {
|
||||||
|
CanvasMenuItem item = CanvasMenuItem::Conditional(label, callback, condition);
|
||||||
|
pending_items_.push_back(item);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
CanvasMenuBuilder& CanvasMenuBuilder::AddSubmenu(
|
||||||
|
const std::string& label, const std::vector<CanvasMenuItem>& subitems) {
|
||||||
|
CanvasMenuItem item;
|
||||||
|
item.label = label;
|
||||||
|
item.subitems = subitems;
|
||||||
|
pending_items_.push_back(item);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
CanvasMenuBuilder& CanvasMenuBuilder::AddSeparator() {
|
||||||
|
if (!pending_items_.empty()) {
|
||||||
|
pending_items_.back().separator_after = true;
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
CanvasMenuBuilder& CanvasMenuBuilder::BeginSection(
|
||||||
|
const std::string& title, MenuSectionPriority priority) {
|
||||||
|
// Flush any pending items to previous section
|
||||||
|
FlushPendingItems();
|
||||||
|
|
||||||
|
// Create new section
|
||||||
|
CanvasMenuSection section;
|
||||||
|
section.title = title;
|
||||||
|
section.priority = priority;
|
||||||
|
section.separator_after = true;
|
||||||
|
menu_.sections.push_back(section);
|
||||||
|
|
||||||
|
// Point current_section_ to the newly added section
|
||||||
|
current_section_ = &menu_.sections.back();
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
CanvasMenuBuilder& CanvasMenuBuilder::EndSection() {
|
||||||
|
FlushPendingItems();
|
||||||
|
current_section_ = nullptr;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
CanvasMenuDefinition CanvasMenuBuilder::Build() {
|
||||||
|
FlushPendingItems();
|
||||||
|
return menu_;
|
||||||
|
}
|
||||||
|
|
||||||
|
CanvasMenuBuilder& CanvasMenuBuilder::Reset() {
|
||||||
|
menu_.sections.clear();
|
||||||
|
pending_items_.clear();
|
||||||
|
current_section_ = nullptr;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CanvasMenuBuilder::FlushPendingItems() {
|
||||||
|
if (pending_items_.empty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If no section exists yet, create a default one
|
||||||
|
if (menu_.sections.empty()) {
|
||||||
|
CanvasMenuSection section;
|
||||||
|
section.priority = MenuSectionPriority::kEditorSpecific;
|
||||||
|
section.separator_after = true;
|
||||||
|
menu_.sections.push_back(section);
|
||||||
|
current_section_ = &menu_.sections.back();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add pending items to current section
|
||||||
|
if (current_section_) {
|
||||||
|
current_section_->items.insert(current_section_->items.end(),
|
||||||
|
pending_items_.begin(), pending_items_.end());
|
||||||
|
} else {
|
||||||
|
// Add to last section if current_section_ is null
|
||||||
|
menu_.sections.back().items.insert(menu_.sections.back().items.end(),
|
||||||
|
pending_items_.begin(), pending_items_.end());
|
||||||
|
}
|
||||||
|
|
||||||
|
pending_items_.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace gui
|
||||||
|
} // namespace yaze
|
||||||
|
|
||||||
157
src/app/gui/canvas/canvas_menu_builder.h
Normal file
157
src/app/gui/canvas/canvas_menu_builder.h
Normal file
@@ -0,0 +1,157 @@
|
|||||||
|
#ifndef YAZE_APP_GUI_CANVAS_CANVAS_MENU_BUILDER_H
|
||||||
|
#define YAZE_APP_GUI_CANVAS_CANVAS_MENU_BUILDER_H
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "app/gui/canvas/canvas_menu.h"
|
||||||
|
|
||||||
|
namespace yaze {
|
||||||
|
namespace gui {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Builder pattern for constructing canvas menus fluently
|
||||||
|
*
|
||||||
|
* Phase 4: Simplifies menu construction with chainable methods.
|
||||||
|
*
|
||||||
|
* Example usage:
|
||||||
|
* @code
|
||||||
|
* CanvasMenuBuilder builder;
|
||||||
|
* builder
|
||||||
|
* .AddItem("Cut", ICON_MD_CONTENT_CUT, []() { DoCut(); })
|
||||||
|
* .AddItem("Copy", ICON_MD_CONTENT_COPY, []() { DoCopy(); })
|
||||||
|
* .AddSeparator()
|
||||||
|
* .AddPopupItem("Properties", ICON_MD_SETTINGS, "props_popup",
|
||||||
|
* []() { RenderPropertiesPopup(); })
|
||||||
|
* .Build();
|
||||||
|
* @endcode
|
||||||
|
*/
|
||||||
|
class CanvasMenuBuilder {
|
||||||
|
public:
|
||||||
|
CanvasMenuBuilder() = default;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Add a simple menu item
|
||||||
|
* @param label Menu item label
|
||||||
|
* @param callback Action to perform when selected
|
||||||
|
* @return Reference to this builder for chaining
|
||||||
|
*/
|
||||||
|
CanvasMenuBuilder& AddItem(const std::string& label,
|
||||||
|
std::function<void()> callback);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Add a menu item with icon
|
||||||
|
* @param label Menu item label
|
||||||
|
* @param icon Material Design icon or Unicode glyph
|
||||||
|
* @param callback Action to perform when selected
|
||||||
|
* @return Reference to this builder for chaining
|
||||||
|
*/
|
||||||
|
CanvasMenuBuilder& AddItem(const std::string& label,
|
||||||
|
const std::string& icon,
|
||||||
|
std::function<void()> callback);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Add a menu item with icon and shortcut hint
|
||||||
|
* @param label Menu item label
|
||||||
|
* @param icon Material Design icon or Unicode glyph
|
||||||
|
* @param shortcut Keyboard shortcut hint (e.g., "Ctrl+S")
|
||||||
|
* @param callback Action to perform when selected
|
||||||
|
* @return Reference to this builder for chaining
|
||||||
|
*/
|
||||||
|
CanvasMenuBuilder& AddItem(const std::string& label,
|
||||||
|
const std::string& icon,
|
||||||
|
const std::string& shortcut,
|
||||||
|
std::function<void()> callback);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Add a menu item that opens a persistent popup
|
||||||
|
* @param label Menu item label
|
||||||
|
* @param popup_id Unique popup identifier
|
||||||
|
* @param render_callback Callback to render popup content
|
||||||
|
* @return Reference to this builder for chaining
|
||||||
|
*/
|
||||||
|
CanvasMenuBuilder& AddPopupItem(const std::string& label,
|
||||||
|
const std::string& popup_id,
|
||||||
|
std::function<void()> render_callback);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Add a menu item with icon that opens a persistent popup
|
||||||
|
* @param label Menu item label
|
||||||
|
* @param icon Material Design icon or Unicode glyph
|
||||||
|
* @param popup_id Unique popup identifier
|
||||||
|
* @param render_callback Callback to render popup content
|
||||||
|
* @return Reference to this builder for chaining
|
||||||
|
*/
|
||||||
|
CanvasMenuBuilder& AddPopupItem(const std::string& label,
|
||||||
|
const std::string& icon,
|
||||||
|
const std::string& popup_id,
|
||||||
|
std::function<void()> render_callback);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Add a conditional menu item (enabled only when condition is true)
|
||||||
|
* @param label Menu item label
|
||||||
|
* @param callback Action to perform when selected
|
||||||
|
* @param condition Function that returns true when item should be enabled
|
||||||
|
* @return Reference to this builder for chaining
|
||||||
|
*/
|
||||||
|
CanvasMenuBuilder& AddConditionalItem(const std::string& label,
|
||||||
|
std::function<void()> callback,
|
||||||
|
std::function<bool()> condition);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Add a submenu with nested items
|
||||||
|
* @param label Submenu label
|
||||||
|
* @param subitems Nested menu items
|
||||||
|
* @return Reference to this builder for chaining
|
||||||
|
*/
|
||||||
|
CanvasMenuBuilder& AddSubmenu(const std::string& label,
|
||||||
|
const std::vector<CanvasMenuItem>& subitems);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Add a separator to visually group items
|
||||||
|
* @return Reference to this builder for chaining
|
||||||
|
*/
|
||||||
|
CanvasMenuBuilder& AddSeparator();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Start a new section with optional title
|
||||||
|
* @param title Section title (empty for no title)
|
||||||
|
* @param priority Section priority (controls rendering order)
|
||||||
|
* @return Reference to this builder for chaining
|
||||||
|
*/
|
||||||
|
CanvasMenuBuilder& BeginSection(
|
||||||
|
const std::string& title = "",
|
||||||
|
MenuSectionPriority priority = MenuSectionPriority::kEditorSpecific);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief End the current section
|
||||||
|
* @return Reference to this builder for chaining
|
||||||
|
*/
|
||||||
|
CanvasMenuBuilder& EndSection();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Build the final menu definition
|
||||||
|
* @return Complete menu definition ready for rendering
|
||||||
|
*/
|
||||||
|
CanvasMenuDefinition Build();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Reset the builder to start building a new menu
|
||||||
|
* @return Reference to this builder for chaining
|
||||||
|
*/
|
||||||
|
CanvasMenuBuilder& Reset();
|
||||||
|
|
||||||
|
private:
|
||||||
|
CanvasMenuDefinition menu_;
|
||||||
|
CanvasMenuSection* current_section_ = nullptr;
|
||||||
|
std::vector<CanvasMenuItem> pending_items_;
|
||||||
|
|
||||||
|
void FlushPendingItems();
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace gui
|
||||||
|
} // namespace yaze
|
||||||
|
|
||||||
|
#endif // YAZE_APP_GUI_CANVAS_CANVAS_MENU_BUILDER_H
|
||||||
|
|
||||||
@@ -33,6 +33,7 @@ set(CANVAS_SRC
|
|||||||
app/gui/canvas/canvas_interaction.cc
|
app/gui/canvas/canvas_interaction.cc
|
||||||
app/gui/canvas/canvas_interaction_handler.cc
|
app/gui/canvas/canvas_interaction_handler.cc
|
||||||
app/gui/canvas/canvas_menu.cc
|
app/gui/canvas/canvas_menu.cc
|
||||||
|
app/gui/canvas/canvas_menu_builder.cc
|
||||||
app/gui/canvas/canvas_modals.cc
|
app/gui/canvas/canvas_modals.cc
|
||||||
app/gui/canvas/canvas_performance_integration.cc
|
app/gui/canvas/canvas_performance_integration.cc
|
||||||
app/gui/canvas/canvas_popup.cc
|
app/gui/canvas/canvas_popup.cc
|
||||||
|
|||||||
Reference in New Issue
Block a user