imgui-frontend-engineer: move layout designer into lab target
This commit is contained in:
@@ -70,25 +70,6 @@ set(
|
||||
app/editor/palette/palette_utility.cc
|
||||
app/editor/sprite/sprite_drawer.cc
|
||||
app/editor/sprite/sprite_editor.cc
|
||||
app/editor/layout/layout_coordinator.cc
|
||||
app/editor/layout/layout_manager.cc
|
||||
app/editor/layout/layout_orchestrator.cc
|
||||
app/editor/layout/layout_presets.cc
|
||||
app/editor/layout/window_delegate.cc
|
||||
app/editor/system/command_manager.cc
|
||||
app/editor/system/command_palette.cc
|
||||
app/editor/system/editor_activator.cc
|
||||
app/editor/system/panel_manager.cc
|
||||
app/editor/system/file_browser.cc
|
||||
app/editor/system/editor_registry.cc
|
||||
app/editor/system/extension_manager.cc
|
||||
app/editor/system/project_manager.cc
|
||||
app/editor/system/proposal_drawer.cc
|
||||
app/editor/system/rom_file_manager.cc
|
||||
app/editor/system/shortcut_manager.cc
|
||||
app/editor/system/session_coordinator.cc
|
||||
app/editor/system/user_settings.cc
|
||||
app/editor/system/shortcut_configurator.cc
|
||||
app/editor/menu/menu_orchestrator.cc
|
||||
app/editor/ui/popup_manager.cc
|
||||
app/editor/ui/dashboard_panel.cc
|
||||
@@ -105,16 +86,91 @@ set(
|
||||
app/editor/ui/welcome_screen.cc
|
||||
app/editor/ui/workspace_manager.cc
|
||||
|
||||
app/editor/layout_designer/layout_designer_window.cc
|
||||
app/editor/layout_designer/layout_serialization.cc
|
||||
app/editor/layout_designer/layout_definition.cc
|
||||
app/editor/layout_designer/widget_definition.cc
|
||||
app/editor/layout_designer/widget_code_generator.cc
|
||||
app/editor/layout_designer/theme_properties.cc
|
||||
app/editor/layout_designer/yaze_widgets.cc
|
||||
yaze.cc
|
||||
)
|
||||
|
||||
set(
|
||||
YAZE_EDITOR_SYSTEM_PANELS_SRC
|
||||
app/editor/layout/layout_coordinator.cc
|
||||
app/editor/layout/layout_manager.cc
|
||||
app/editor/layout/layout_orchestrator.cc
|
||||
app/editor/layout/layout_presets.cc
|
||||
app/editor/layout/window_delegate.cc
|
||||
app/editor/system/editor_activator.cc
|
||||
app/editor/system/editor_registry.cc
|
||||
app/editor/system/panel_manager.cc
|
||||
app/editor/system/file_browser.cc
|
||||
app/editor/system/proposal_drawer.cc
|
||||
)
|
||||
|
||||
set(
|
||||
YAZE_EDITOR_SYSTEM_SESSION_SRC
|
||||
app/editor/system/extension_manager.cc
|
||||
app/editor/system/project_manager.cc
|
||||
app/editor/system/rom_file_manager.cc
|
||||
app/editor/system/session_coordinator.cc
|
||||
app/editor/system/user_settings.cc
|
||||
)
|
||||
|
||||
set(
|
||||
YAZE_EDITOR_SYSTEM_SHORTCUTS_SRC
|
||||
app/editor/system/command_manager.cc
|
||||
app/editor/system/command_palette.cc
|
||||
app/editor/system/shortcut_manager.cc
|
||||
app/editor/system/shortcut_configurator.cc
|
||||
)
|
||||
|
||||
# Editor system split targets (panels/session/shortcuts)
|
||||
add_library(yaze_editor_system_panels STATIC ${YAZE_EDITOR_SYSTEM_PANELS_SRC})
|
||||
add_library(yaze_editor_system_session STATIC ${YAZE_EDITOR_SYSTEM_SESSION_SRC})
|
||||
add_library(yaze_editor_system_shortcuts STATIC ${YAZE_EDITOR_SYSTEM_SHORTCUTS_SRC})
|
||||
|
||||
foreach(target_name IN ITEMS
|
||||
yaze_editor_system_panels
|
||||
yaze_editor_system_session
|
||||
yaze_editor_system_shortcuts
|
||||
)
|
||||
target_precompile_headers(${target_name} PRIVATE
|
||||
"$<$<COMPILE_LANGUAGE:CXX>:${CMAKE_SOURCE_DIR}/src/yaze_pch.h>"
|
||||
)
|
||||
|
||||
target_include_directories(${target_name} PUBLIC
|
||||
${CMAKE_SOURCE_DIR}/src
|
||||
${CMAKE_SOURCE_DIR}/ext
|
||||
${CMAKE_SOURCE_DIR}/ext/imgui
|
||||
${CMAKE_SOURCE_DIR}/ext/imgui_test_engine
|
||||
${CMAKE_SOURCE_DIR}/incl
|
||||
${SDL2_INCLUDE_DIR}
|
||||
${PROJECT_BINARY_DIR}
|
||||
)
|
||||
endforeach()
|
||||
|
||||
target_link_libraries(yaze_editor_system_panels PUBLIC
|
||||
yaze_app_core_lib
|
||||
yaze_gfx
|
||||
yaze_gui
|
||||
yaze_util
|
||||
yaze_common
|
||||
ImGui
|
||||
)
|
||||
|
||||
target_link_libraries(yaze_editor_system_session PUBLIC
|
||||
yaze_editor_system_panels
|
||||
yaze_rom
|
||||
yaze_zelda3
|
||||
yaze_gui
|
||||
yaze_util
|
||||
yaze_common
|
||||
ImGui
|
||||
)
|
||||
|
||||
target_link_libraries(yaze_editor_system_shortcuts PUBLIC
|
||||
yaze_gui
|
||||
yaze_util
|
||||
yaze_common
|
||||
ImGui
|
||||
)
|
||||
|
||||
# Agent UI Theme is always needed (used by dungeon editor, etc.)
|
||||
list(APPEND YAZE_APP_EDITOR_SRC
|
||||
app/editor/agent/agent_ui_theme.cc
|
||||
@@ -155,6 +211,21 @@ endif()
|
||||
|
||||
add_library(yaze_editor STATIC ${YAZE_APP_EDITOR_SRC})
|
||||
|
||||
target_link_libraries(yaze_editor PUBLIC
|
||||
yaze_editor_system_panels
|
||||
yaze_editor_system_session
|
||||
yaze_editor_system_shortcuts
|
||||
yaze_app_core_lib
|
||||
yaze_rom
|
||||
yaze_gfx
|
||||
yaze_gui
|
||||
yaze_zelda3
|
||||
yaze_emulator # Needed for emulator integration (APU, PPU, SNES)
|
||||
yaze_util
|
||||
yaze_common
|
||||
ImGui
|
||||
)
|
||||
|
||||
target_precompile_headers(yaze_editor PRIVATE
|
||||
"$<$<COMPILE_LANGUAGE:CXX>:${CMAKE_SOURCE_DIR}/src/yaze_pch.h>"
|
||||
)
|
||||
@@ -169,18 +240,6 @@ target_include_directories(yaze_editor PUBLIC
|
||||
${PROJECT_BINARY_DIR}
|
||||
)
|
||||
|
||||
target_link_libraries(yaze_editor PUBLIC
|
||||
yaze_app_core_lib
|
||||
yaze_rom
|
||||
yaze_gfx
|
||||
yaze_gui
|
||||
yaze_zelda3
|
||||
yaze_emulator # Needed for emulator integration (APU, PPU, SNES)
|
||||
yaze_util
|
||||
yaze_common
|
||||
ImGui
|
||||
)
|
||||
|
||||
# Link agent runtime only when agent UI panels are enabled
|
||||
if(YAZE_BUILD_AGENT_UI AND NOT YAZE_MINIMAL_BUILD)
|
||||
if(TARGET yaze_agent)
|
||||
@@ -195,6 +254,7 @@ endif()
|
||||
|
||||
if(YAZE_ENABLE_JSON)
|
||||
if(TARGET nlohmann_json::nlohmann_json)
|
||||
target_link_libraries(yaze_editor_system_panels PUBLIC nlohmann_json::nlohmann_json)
|
||||
target_link_libraries(yaze_editor PUBLIC nlohmann_json::nlohmann_json)
|
||||
endif()
|
||||
|
||||
|
||||
@@ -481,9 +481,6 @@ void EditorManager::Initialize(gfx::IRenderer* renderer,
|
||||
emulator_.set_panel_manager(&panel_manager_);
|
||||
workspace_manager_.set_panel_manager(&panel_manager_);
|
||||
|
||||
// Initialize layout designer with panel + layout managers
|
||||
layout_designer_.Initialize(&panel_manager_, layout_manager_.get(), this);
|
||||
|
||||
// Point to a blank editor set when no ROM is loaded
|
||||
// current_editor_set_ = &blank_editor_set_;
|
||||
|
||||
@@ -1273,11 +1270,6 @@ absl::Status EditorManager::Update() {
|
||||
session_coordinator_->DrawSessionRenameDialog();
|
||||
}
|
||||
|
||||
// Draw Layout Designer if open
|
||||
if (layout_designer_.IsOpen()) {
|
||||
layout_designer_.Draw();
|
||||
}
|
||||
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
|
||||
@@ -41,7 +41,6 @@
|
||||
#include "app/editor/ui/ui_coordinator.h"
|
||||
#include "app/editor/ui/welcome_screen.h"
|
||||
#include "app/editor/ui/workspace_manager.h"
|
||||
#include "app/editor/layout_designer/layout_designer_window.h"
|
||||
#include "app/emu/emulator.h"
|
||||
#include "app/startup_flags.h"
|
||||
#include "rom/rom.h"
|
||||
@@ -114,7 +113,6 @@ class EditorManager : public SessionObserver {
|
||||
auto quit() const { return quit_; }
|
||||
auto version() const { return version_; }
|
||||
|
||||
void OpenLayoutDesigner() { layout_designer_.Open(); }
|
||||
|
||||
MenuBuilder& menu_builder() { return menu_builder_; }
|
||||
WorkspaceManager* workspace_manager() { return &workspace_manager_; }
|
||||
@@ -427,7 +425,6 @@ class EditorManager : public SessionObserver {
|
||||
StatusBar status_bar_; // Bottom status bar
|
||||
std::unique_ptr<ActivityBar> activity_bar_;
|
||||
WorkspaceManager workspace_manager_{&toast_manager_};
|
||||
layout_designer::LayoutDesignerWindow layout_designer_; // WYSIWYG layout designer
|
||||
|
||||
emu::input::InputConfig BuildInputConfigFromSettings() const;
|
||||
void PersistInputConfig(const emu::input::InputConfig& config);
|
||||
|
||||
@@ -1,341 +0,0 @@
|
||||
# Layout Designer - Quick Start Guide
|
||||
|
||||
## Opening the Designer
|
||||
|
||||
**Press `Ctrl+L`** or go to **`Tools > Layout Designer`**
|
||||
|
||||
---
|
||||
|
||||
## Mode 1: Panel Layout Design
|
||||
|
||||
**Use this to arrange where panels appear in your application**
|
||||
|
||||
### Visual Guide
|
||||
|
||||
```
|
||||
┌──────────────────────────────────────────────────────────────┐
|
||||
│ ◉ Panel Layout | ○ Widget Design │
|
||||
├────────────┬─────────────────────────────┬──────────────────┤
|
||||
│ PANELS │ CANVAS │ PROPERTIES │
|
||||
│ │ │ │
|
||||
│ Dungeon ▼ │ Drag panels here → │ │
|
||||
│ 🏰 Room │ │ │
|
||||
│ List │ [Drop zones appear │ │
|
||||
│ 📝 Object │ when dragging] │ │
|
||||
│ Editor │ │ │
|
||||
└────────────┴─────────────────────────────┴──────────────────┘
|
||||
```
|
||||
|
||||
### Steps
|
||||
|
||||
1. **Search** for panels in left palette
|
||||
2. **Drag** panel from palette
|
||||
3. **Drop** on canvas (left/right/top/bottom/center)
|
||||
4. **Watch** blue drop zone appear
|
||||
5. **Release** to dock panel
|
||||
6. **Repeat** to build complex layouts
|
||||
7. **Save** as JSON file
|
||||
|
||||
### Example: 3-Panel Layout
|
||||
|
||||
```
|
||||
Drag "Room List" → Drop LEFT
|
||||
Result: ┌─────┬───────┐
|
||||
│Room │ │
|
||||
│List │ │
|
||||
└─────┴───────┘
|
||||
|
||||
Drag "Object Editor" → Drop CENTER
|
||||
Result: ┌─────┬───────┐
|
||||
│Room │Object │
|
||||
│List │Editor │
|
||||
└─────┴───────┘
|
||||
|
||||
Drag "Palette" → Drop BOTTOM-RIGHT
|
||||
Result: ┌─────┬───────┐
|
||||
│Room │Object │
|
||||
│List ├───────┤
|
||||
│ │Palette│
|
||||
└─────┴───────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Mode 2: Widget Design
|
||||
|
||||
**Use this to design what's INSIDE a panel**
|
||||
|
||||
### Visual Guide
|
||||
|
||||
```
|
||||
┌──────────────────────────────────────────────────────────────┐
|
||||
│ ○ Panel Layout | ◉ Widget Design │
|
||||
├────────────┬─────────────────────────────┬──────────────────┤
|
||||
│ WIDGETS │ CANVAS │ PROPERTIES │
|
||||
│ │ │ │
|
||||
│ Basic ▼ │ Panel: My Panel │ Widget: Button │
|
||||
│ 📝 Text │ │ │
|
||||
│ 🔘 Button │ ┌──────────────────┐ │ label: [Save ] │
|
||||
│ ☑️ Check │ │ 📝 Text │ │ │
|
||||
│ 📋 Input │ │ ➖ Separator │ │ callback: │
|
||||
│ │ │ 🔘 Button │ │ [OnSave ] │
|
||||
│ Tables ▼ │ └──────────────────┘ │ │
|
||||
│ 📊 Table │ [Drop widgets here] │ tooltip: │
|
||||
│ ➡️ Column │ │ [Save file ] │
|
||||
└────────────┴─────────────────────────────┴──────────────────┘
|
||||
```
|
||||
|
||||
### Steps
|
||||
|
||||
1. **Switch** to Widget Design mode
|
||||
2. **Create** new panel design (or select existing)
|
||||
3. **Drag** widgets from palette
|
||||
4. **Drop** on canvas to add
|
||||
5. **Click** widget to select
|
||||
6. **Edit** properties in right panel
|
||||
7. **Export** code to copy
|
||||
|
||||
### Example: Simple Form
|
||||
|
||||
```
|
||||
1. Drag "Text" widget
|
||||
→ Set text: "Enter Name:"
|
||||
|
||||
2. Drag "InputText" widget
|
||||
→ Set label: "Name"
|
||||
→ Set hint: "Your name here"
|
||||
|
||||
3. Drag "Button" widget
|
||||
→ Set label: "Submit"
|
||||
→ Set callback: "OnSubmit"
|
||||
→ Set tooltip: "Submit the form"
|
||||
|
||||
4. Click "Export Code"
|
||||
|
||||
Generated:
|
||||
ImGui::Text("Enter Name:");
|
||||
ImGui::InputTextWithHint("Name", "Your name here",
|
||||
name_buffer_, sizeof(name_buffer_));
|
||||
if (ImGui::Button("Submit")) {
|
||||
OnSubmit();
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Widget Types Cheat Sheet
|
||||
|
||||
### Basic Widgets
|
||||
- 📝 **Text** - Display text
|
||||
- 🔘 **Button** - Clickable button
|
||||
- ☑️ **Checkbox** - Toggle boolean
|
||||
- 📋 **InputText** - Text input field
|
||||
- 🎚️ **Slider** - Value slider
|
||||
- 🎨 **ColorEdit** - Color picker
|
||||
|
||||
### Layout Widgets
|
||||
- ➖ **Separator** - Horizontal line
|
||||
- ↔️ **SameLine** - Place next widget on same line
|
||||
- ⬇️ **Spacing** - Add vertical space
|
||||
- 📏 **Dummy** - Invisible spacing
|
||||
|
||||
### Tables
|
||||
- 📊 **BeginTable** - Start a table (requires columns)
|
||||
- ➡️ **TableNextColumn** - Move to next column
|
||||
- ⬇️ **TableNextRow** - Move to next row
|
||||
|
||||
### Containers
|
||||
- 📦 **BeginGroup** - Group widgets together
|
||||
- 🪟 **BeginChild** - Scrollable sub-window
|
||||
- 🌲 **TreeNode** - Collapsible tree
|
||||
- 📑 **TabBar** - Tabbed interface
|
||||
|
||||
### Custom
|
||||
- 🖌️ **Canvas** - Custom drawing area
|
||||
- 📊 **ProgressBar** - Progress indicator
|
||||
- 🖼️ **Image** - Display image
|
||||
|
||||
---
|
||||
|
||||
## Tips & Tricks
|
||||
|
||||
### Panel Layout Tips
|
||||
|
||||
**Tip 1: Use Drop Zones Strategically**
|
||||
- **Left/Right** (30%) - Sidebars, lists
|
||||
- **Top/Bottom** (25%) - Toolbars, status
|
||||
- **Center** - Main content area
|
||||
|
||||
**Tip 2: Plan Before Designing**
|
||||
- Sketch layout on paper first
|
||||
- Identify main vs secondary panels
|
||||
- Group related panels together
|
||||
|
||||
**Tip 3: Test with Real Data**
|
||||
- Use Preview to see layout in action
|
||||
- Check panel visibility and sizing
|
||||
- Adjust ratios as needed
|
||||
|
||||
### Widget Design Tips
|
||||
|
||||
**Tip 1: Start Simple**
|
||||
- Add title text first
|
||||
- Add separators for structure
|
||||
- Build form from top to bottom
|
||||
|
||||
**Tip 2: Use Same Line**
|
||||
- Put related widgets on same line
|
||||
- Use for button groups (Apply/Cancel)
|
||||
- Use for label + input pairs
|
||||
|
||||
**Tip 3: Table Organization**
|
||||
- Use tables for data grids
|
||||
- Set columns before adding rows
|
||||
- Enable scrolling for large datasets
|
||||
|
||||
**Tip 4: Group Related Widgets**
|
||||
- Use BeginGroup for visual grouping
|
||||
- Use BeginChild for scrollable sections
|
||||
- Use CollapsingHeader for optional sections
|
||||
|
||||
---
|
||||
|
||||
## Common Patterns
|
||||
|
||||
### Pattern 1: Simple Panel
|
||||
```
|
||||
Text: "Title"
|
||||
Separator
|
||||
Content widgets...
|
||||
Separator
|
||||
Button: "Action"
|
||||
```
|
||||
|
||||
### Pattern 2: Form Panel
|
||||
```
|
||||
Text: "Field 1:"
|
||||
InputText: "field1"
|
||||
Text: "Field 2:"
|
||||
InputInt: "field2"
|
||||
Separator
|
||||
Button: "Submit" | SameLine | Button: "Cancel"
|
||||
```
|
||||
|
||||
### Pattern 3: List Panel
|
||||
```
|
||||
Text: "Items"
|
||||
Separator
|
||||
BeginTable: 3 columns
|
||||
(Add rows in code)
|
||||
EndTable
|
||||
Separator
|
||||
Button: "Add Item"
|
||||
```
|
||||
|
||||
### Pattern 4: Tabbed Panel
|
||||
```
|
||||
TabBar: "tabs"
|
||||
TabItem: "General"
|
||||
(General widgets)
|
||||
TabItem: "Advanced"
|
||||
(Advanced widgets)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Keyboard Shortcuts
|
||||
|
||||
| Shortcut | Action |
|
||||
|----------|--------|
|
||||
| `Ctrl+L` | Open Layout Designer |
|
||||
| `Ctrl+N` | New layout/design |
|
||||
| `Ctrl+O` | Open file |
|
||||
| `Ctrl+S` | Save file |
|
||||
| `Ctrl+P` | Preview |
|
||||
| `Del` | Delete selected (when implemented) |
|
||||
| `Esc` | Close designer |
|
||||
|
||||
---
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Q: Designer doesn't open
|
||||
**A:** Check that yaze is running and press `Ctrl+L` or use `Tools > Layout Designer`
|
||||
|
||||
### Q: Palette is empty (Panel mode)
|
||||
**A:** Load a ROM first. Some panels only appear when ROM is loaded.
|
||||
|
||||
### Q: Palette is empty (Widget mode)
|
||||
**A:** This is a bug. Widget palette should always show all 40+ widget types.
|
||||
|
||||
### Q: Can't drag widgets
|
||||
**A:** Make sure you click and hold on the widget, then drag to canvas.
|
||||
|
||||
### Q: Properties don't save
|
||||
**A:** Changes to properties are immediate. No "Apply" button needed currently.
|
||||
|
||||
### Q: Generated code doesn't compile
|
||||
**A:**
|
||||
- Check that all callbacks exist in your panel class
|
||||
- Add member variables for widget state
|
||||
- Replace TODO comments with actual logic
|
||||
|
||||
### Q: How to delete a widget?
|
||||
**A:** Currently not implemented. Will be added in Phase 10.
|
||||
|
||||
---
|
||||
|
||||
## Examples
|
||||
|
||||
### Example 1: Object Properties Panel
|
||||
|
||||
**Design in Widget Mode:**
|
||||
1. Text: "Object Properties"
|
||||
2. Separator
|
||||
3. InputInt: "ID" (0-255)
|
||||
4. InputInt: "X" (0-512)
|
||||
5. InputInt: "Y" (0-512)
|
||||
6. Checkbox: "Visible"
|
||||
7. Separator
|
||||
8. Button: "Apply"
|
||||
|
||||
**Result:** Clean property editor for objects
|
||||
|
||||
### Example 2: Room Selector Panel
|
||||
|
||||
**Design in Widget Mode:**
|
||||
1. Text: "Dungeon Rooms"
|
||||
2. Separator
|
||||
3. BeginTable: 4 columns (ID, Name, Type, Actions)
|
||||
4. Button: "Add Room"
|
||||
|
||||
**Result:** Professional room list with table
|
||||
|
||||
### Example 3: Complex Dashboard
|
||||
|
||||
**Design in Panel Layout Mode:**
|
||||
1. Left (20%): Navigation panel
|
||||
2. Center (60%): Main editor
|
||||
3. Right-Top (20%, 50%): Properties
|
||||
4. Right-Bottom (20%, 50%): Preview
|
||||
|
||||
**Then design each panel in Widget Mode:**
|
||||
- Navigation: Tree of categories
|
||||
- Main: Canvas widget
|
||||
- Properties: Form widgets
|
||||
- Preview: Image widget
|
||||
|
||||
**Result:** Complete IDE-like interface!
|
||||
|
||||
---
|
||||
|
||||
## Support
|
||||
|
||||
**Documentation:** See `docs/internal/architecture/imgui-layout-designer.md`
|
||||
**Examples:** Check `docs/internal/architecture/layout-designer-integration-example.md`
|
||||
**Issues:** Report in yaze repository
|
||||
|
||||
---
|
||||
|
||||
Happy designing! 🎨
|
||||
|
||||
@@ -1,309 +0,0 @@
|
||||
# YAZE ImGui Layout Designer
|
||||
|
||||
A WYSIWYG (What You See Is What You Get) visual designer for creating and managing ImGui panel layouts in the yaze application.
|
||||
|
||||
## Overview
|
||||
|
||||
The Layout Designer provides a visual interface for designing complex multi-panel layouts without writing DockBuilder code. It addresses the growing complexity of managing 15+ editor panels across multiple categories.
|
||||
|
||||
## Features
|
||||
|
||||
### Current (Phase 1)
|
||||
- ✅ Data model for layouts (panels, dock nodes, splits)
|
||||
- ✅ Basic UI structure (palette, canvas, properties)
|
||||
- ✅ Panel drag-and-drop from palette
|
||||
- ✅ Visual dock node rendering
|
||||
- ✅ Code generation preview (DockBuilder + LayoutPresets)
|
||||
- ✅ Layout validation
|
||||
|
||||
### Planned
|
||||
- ⏳ JSON import/export
|
||||
- ⏳ Runtime layout import (from current application state)
|
||||
- ⏳ Live preview (apply to application)
|
||||
- ⏳ Interactive split ratio adjustment
|
||||
- ⏳ Undo/Redo support
|
||||
- ⏳ Layout tree view with drag-to-reorder
|
||||
- ⏳ Panel search and filtering
|
||||
|
||||
## Quick Start
|
||||
|
||||
### Opening the Designer
|
||||
|
||||
```cpp
|
||||
// In EditorManager or main menu
|
||||
#include "app/editor/layout_designer/layout_designer_window.h"
|
||||
|
||||
// Member variable
|
||||
layout_designer::LayoutDesignerWindow layout_designer_;
|
||||
|
||||
// Initialize
|
||||
layout_designer_.Initialize(&panel_manager_);
|
||||
|
||||
// Open from menu
|
||||
if (ImGui::MenuItem(ICON_MD_DASHBOARD " Layout Designer")) {
|
||||
layout_designer_.Open();
|
||||
}
|
||||
|
||||
// Draw each frame
|
||||
if (layout_designer_.IsOpen()) {
|
||||
layout_designer_.Draw();
|
||||
}
|
||||
```
|
||||
|
||||
### Creating a Layout
|
||||
|
||||
1. **Open Designer:** Tools > Layout Designer
|
||||
2. **Create New Layout:** File > New (Ctrl+N)
|
||||
3. **Add Panels:**
|
||||
- Drag panels from palette on the left
|
||||
- Drop into canvas to create dock splits
|
||||
4. **Configure Properties:**
|
||||
- Select panel to edit properties
|
||||
- Adjust visibility, flags, priority
|
||||
5. **Preview:** Layout > Preview Layout
|
||||
6. **Export Code:** File > Export Code
|
||||
|
||||
### Example Generated Code
|
||||
|
||||
**DockBuilder Code:**
|
||||
```cpp
|
||||
void LayoutManager::BuildDungeonExpertLayout(ImGuiID dockspace_id) {
|
||||
ImGui::DockBuilderRemoveNode(dockspace_id);
|
||||
ImGui::DockBuilderAddNode(dockspace_id, ImGuiDockNodeFlags_DockSpace);
|
||||
|
||||
ImGuiID dock_main_id = dockspace_id;
|
||||
ImGuiID dock_left_id = ImGui::DockBuilderSplitNode(
|
||||
dock_main_id, ImGuiDir_Left, 0.25f, nullptr, &dock_main_id);
|
||||
|
||||
ImGui::DockBuilderDockWindow("Room List", dock_left_id);
|
||||
ImGui::DockBuilderDockWindow("Object Editor", dock_main_id);
|
||||
|
||||
ImGui::DockBuilderFinish(dockspace_id);
|
||||
}
|
||||
```
|
||||
|
||||
**Layout Preset:**
|
||||
```cpp
|
||||
PanelLayoutPreset LayoutPresets::GetDungeonExpertPreset() {
|
||||
return {
|
||||
.name = "Dungeon Expert",
|
||||
.description = "Optimized for advanced dungeon editing",
|
||||
.editor_type = EditorType::kDungeon,
|
||||
.default_visible_panels = {
|
||||
"dungeon.room_selector",
|
||||
"dungeon.object_editor",
|
||||
},
|
||||
.panel_positions = {
|
||||
{"dungeon.room_selector", DockPosition::Left},
|
||||
{"dungeon.object_editor", DockPosition::Center},
|
||||
}
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
## Architecture
|
||||
|
||||
### Data Model
|
||||
|
||||
```
|
||||
LayoutDefinition
|
||||
├── metadata (name, author, version, timestamps)
|
||||
├── canvas_size
|
||||
└── root: DockNode
|
||||
├── type (Root/Split/Leaf)
|
||||
├── split configuration (direction, ratio)
|
||||
├── children (for splits)
|
||||
└── panels (for leaves)
|
||||
├── panel_id
|
||||
├── display_name
|
||||
├── icon
|
||||
├── flags (closable, pinnable, etc.)
|
||||
└── properties (size, priority, etc.)
|
||||
```
|
||||
|
||||
### Components
|
||||
|
||||
- **LayoutDesignerWindow**: Main window coordinator
|
||||
- **LayoutDefinition**: Data model for complete layout
|
||||
- **DockNode**: Hierarchical dock structure
|
||||
- **LayoutPanel**: Panel configuration and metadata
|
||||
|
||||
## Usage Examples
|
||||
|
||||
### Example 1: Simple Two-Panel Layout
|
||||
|
||||
```cpp
|
||||
// Programmatically create a layout
|
||||
auto layout = LayoutDefinition::CreateEmpty("My Layout");
|
||||
|
||||
// Split root node horizontally
|
||||
layout.root->Split(ImGuiDir_Left, 0.3f);
|
||||
|
||||
// Add panel to left side
|
||||
LayoutPanel left_panel;
|
||||
left_panel.panel_id = "dungeon.room_selector";
|
||||
left_panel.display_name = "Room List";
|
||||
left_panel.icon = ICON_MD_LIST;
|
||||
layout.root->child_left->AddPanel(left_panel);
|
||||
|
||||
// Add panel to right side
|
||||
LayoutPanel right_panel;
|
||||
right_panel.panel_id = "dungeon.object_editor";
|
||||
right_panel.display_name = "Object Editor";
|
||||
right_panel.icon = ICON_MD_EDIT;
|
||||
layout.root->child_right->AddPanel(right_panel);
|
||||
|
||||
// Validate
|
||||
std::string error;
|
||||
if (layout.Validate(&error)) {
|
||||
// Export code or save to JSON
|
||||
}
|
||||
```
|
||||
|
||||
### Example 2: Import Current Layout
|
||||
|
||||
```cpp
|
||||
// Import from running application
|
||||
layout_designer_.ImportFromRuntime();
|
||||
|
||||
// Modify the imported layout
|
||||
auto* panel = current_layout_->FindPanel("dungeon.palette_editor");
|
||||
if (panel) {
|
||||
panel->visible_by_default = false;
|
||||
panel->priority = 50;
|
||||
}
|
||||
|
||||
// Export updated layout
|
||||
layout_designer_.ExportCode("new_layout.cc");
|
||||
```
|
||||
|
||||
### Example 3: Load from JSON
|
||||
|
||||
```cpp
|
||||
// Load saved layout
|
||||
layout_designer_.LoadLayout("layouts/dungeon_expert.json");
|
||||
|
||||
// Preview in application
|
||||
layout_designer_.PreviewLayout();
|
||||
|
||||
// Make adjustments...
|
||||
|
||||
// Save changes
|
||||
layout_designer_.SaveLayout("layouts/dungeon_expert_v2.json");
|
||||
```
|
||||
|
||||
## JSON Format
|
||||
|
||||
Layouts can be saved as JSON for version control and sharing:
|
||||
|
||||
```json
|
||||
{
|
||||
"layout": {
|
||||
"name": "Dungeon Expert",
|
||||
"version": "1.0.0",
|
||||
"editor_type": "Dungeon",
|
||||
"root_node": {
|
||||
"type": "split",
|
||||
"direction": "horizontal",
|
||||
"ratio": 0.3,
|
||||
"left": {
|
||||
"type": "leaf",
|
||||
"panels": [
|
||||
{
|
||||
"id": "dungeon.room_selector",
|
||||
"display_name": "Room List",
|
||||
"icon": "ICON_MD_LIST",
|
||||
"visible_by_default": true,
|
||||
"priority": 20
|
||||
}
|
||||
]
|
||||
},
|
||||
"right": {
|
||||
"type": "leaf",
|
||||
"panels": [
|
||||
{
|
||||
"id": "dungeon.object_editor",
|
||||
"display_name": "Object Editor",
|
||||
"priority": 30
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Benefits
|
||||
|
||||
### For Developers
|
||||
- ⚡ **Faster iteration:** Design layouts visually, no compile cycle
|
||||
- 🐛 **Fewer bugs:** See layout immediately, catch issues early
|
||||
- 📊 **Better organization:** Visual understanding of complex layouts
|
||||
- ✨ **Consistent code:** Generated code follows best practices
|
||||
|
||||
### For Users
|
||||
- 🎨 **Customizable workspace:** Create personalized layouts
|
||||
- 💾 **Save/load layouts:** Switch between workflows easily
|
||||
- 🤝 **Share layouts:** Import community layouts
|
||||
- 🎯 **Better UX:** Optimized panel arrangements
|
||||
|
||||
### For AI Agents
|
||||
- 🤖 **Programmatic control:** Generate layouts from descriptions
|
||||
- 🎯 **Task-specific layouts:** Optimize for specific agent tasks
|
||||
- 📝 **Reproducible environments:** Save agent workspace state
|
||||
|
||||
## Development Status
|
||||
|
||||
**Current Phase:** Phase 1 - Core Infrastructure ✅
|
||||
|
||||
**Next Phase:** Phase 2 - JSON Serialization
|
||||
|
||||
See [Architecture Doc](../../../../docs/internal/architecture/imgui-layout-designer.md) for complete implementation plan.
|
||||
|
||||
## Contributing
|
||||
|
||||
When adding new features to the Layout Designer:
|
||||
|
||||
1. Update the data model if needed (`layout_definition.h`)
|
||||
2. Add UI components to `LayoutDesignerWindow`
|
||||
3. Update code generation to support new features
|
||||
4. Add tests for data model and serialization
|
||||
5. Update this README with examples
|
||||
|
||||
## Testing
|
||||
|
||||
```bash
|
||||
# Build with layout designer
|
||||
cmake -B build -DYAZE_BUILD_LAYOUT_DESIGNER=ON
|
||||
cmake --build build
|
||||
|
||||
# Run tests
|
||||
./build/test/layout_designer_test
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
**Q: Designer window doesn't open**
|
||||
- Check that `Initialize()` was called with valid PanelManager
|
||||
- Ensure `Draw()` is called every frame when `IsOpen()` is true
|
||||
|
||||
**Q: Panels don't appear in palette**
|
||||
- Verify panels are registered with PanelManager
|
||||
- Check `GetAvailablePanels()` implementation
|
||||
|
||||
**Q: Generated code doesn't compile**
|
||||
- Validate layout before exporting (`Layout > Validate`)
|
||||
- Check panel IDs match registered panels
|
||||
- Ensure all nodes have valid split ratios (0.0 to 1.0)
|
||||
|
||||
## References
|
||||
|
||||
- [Full Architecture Doc](../../../../docs/internal/architecture/imgui-layout-designer.md)
|
||||
- [PanelManager Documentation](../system/panel_manager.h)
|
||||
- [LayoutManager Documentation](../ui/layout_manager.h)
|
||||
- [ImGui Docking Documentation](https://github.com/ocornut/imgui/wiki/Docking)
|
||||
|
||||
## License
|
||||
|
||||
Same as yaze project license.
|
||||
|
||||
@@ -1,360 +0,0 @@
|
||||
# Layout Designer Troubleshooting Guide
|
||||
|
||||
## Drag-and-Drop Not Working
|
||||
|
||||
### Symptom
|
||||
You can't drag panels from the palette to the canvas.
|
||||
|
||||
### Common Causes & Fixes
|
||||
|
||||
#### Issue 1: Drag Source Not Activating
|
||||
|
||||
**Check:**
|
||||
```
|
||||
1. Click and HOLD on a panel in the palette
|
||||
2. Drag (don't release immediately)
|
||||
3. You should see a tooltip with the panel name
|
||||
```
|
||||
|
||||
**If tooltip doesn't appear:**
|
||||
- The drag source isn't activating
|
||||
- Check that you're clicking on the selectable area
|
||||
- Try dragging more (need to move a few pixels)
|
||||
|
||||
**Debug:** Add logging in `DrawPalette()`:
|
||||
```cpp
|
||||
if (ImGui::BeginDragDropSource(ImGuiDragDropFlags_None)) {
|
||||
LOG_INFO("DragDrop", "Drag started for: %s", panel.name.c_str());
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
#### Issue 2: Drop Target Not Accepting
|
||||
|
||||
**Check:**
|
||||
```
|
||||
1. Drag a panel from palette
|
||||
2. Move mouse over canvas
|
||||
3. You should see blue drop zones appear
|
||||
```
|
||||
|
||||
**If drop zones don't appear:**
|
||||
- The drag payload might not be recognized
|
||||
- Drop target might not be set up correctly
|
||||
|
||||
**Fix in DrawCanvas():**
|
||||
```cpp
|
||||
// After ImGui::Dummy(scaled_size);
|
||||
if (ImGui::BeginDragDropTarget()) {
|
||||
LOG_INFO("DragDrop", "Canvas is drop target");
|
||||
|
||||
const ImGuiPayload* payload = ImGui::GetDragDropPayload();
|
||||
if (payload) {
|
||||
LOG_INFO("DragDrop", "Payload type: %s", payload->DataType);
|
||||
}
|
||||
|
||||
if (const ImGuiPayload* accepted =
|
||||
ImGui::AcceptDragDropPayload("PANEL_PALETTE")) {
|
||||
LOG_INFO("DragDrop", "Payload accepted!");
|
||||
// Handle drop
|
||||
}
|
||||
ImGui::EndDragDropTarget();
|
||||
}
|
||||
```
|
||||
|
||||
#### Issue 3: Drop Zones Not Appearing
|
||||
|
||||
**Cause:** `DrawDropZones()` not being called during drag
|
||||
|
||||
**Fix:** Ensure `is_drag_active` is true:
|
||||
```cpp
|
||||
// In DrawDockNode():
|
||||
const ImGuiPayload* drag_payload = ImGui::GetDragDropPayload();
|
||||
bool is_drag_active = drag_payload != nullptr &&
|
||||
drag_payload->DataType != nullptr &&
|
||||
strcmp(drag_payload->DataType, "PANEL_PALETTE") == 0;
|
||||
|
||||
if (is_drag_active) {
|
||||
LOG_INFO("DragDrop", "Drag active, checking mouse position");
|
||||
if (IsMouseOverRect(pos, rect_max)) {
|
||||
LOG_INFO("DragDrop", "Mouse over node, showing drop zones");
|
||||
DrawDropZones(pos, size, node);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### Issue 4: Payload Data Corruption
|
||||
|
||||
**Cause:** Copying struct incorrectly
|
||||
|
||||
**Fix:** Make sure PalettePanel is POD (plain old data):
|
||||
```cpp
|
||||
struct PalettePanel {
|
||||
std::string id; // ← Problem: std::string is not POD!
|
||||
std::string name;
|
||||
std::string icon;
|
||||
std::string category;
|
||||
std::string description;
|
||||
int priority;
|
||||
};
|
||||
```
|
||||
|
||||
**Solution:** Use char arrays or copy differently:
|
||||
```cpp
|
||||
// Option 1: Store index instead of struct
|
||||
int panel_index = GetPanelIndex(panel);
|
||||
ImGui::SetDragDropPayload("PANEL_PALETTE", &panel_index, sizeof(int));
|
||||
|
||||
// Then retrieve:
|
||||
int* index = static_cast<int*>(payload->Data);
|
||||
const PalettePanel& panel = GetPanelByIndex(*index);
|
||||
|
||||
// Option 2: Use stable pointer
|
||||
const PalettePanel* panel_ptr = &panel;
|
||||
ImGui::SetDragDropPayload("PANEL_PALETTE", &panel_ptr, sizeof(PalettePanel*));
|
||||
|
||||
// Then retrieve:
|
||||
const PalettePanel** ptr = static_cast<const PalettePanel**>(payload->Data);
|
||||
const PalettePanel& panel = **ptr;
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Quick Fix Implementation
|
||||
|
||||
Let me provide a tested, working implementation:
|
||||
|
||||
### Step 1: Fix Drag Source
|
||||
|
||||
```cpp
|
||||
// In DrawPalette() - use pointer instead of value
|
||||
if (ImGui::BeginDragDropSource(ImGuiDragDropFlags_None)) {
|
||||
// Store panel index in cache
|
||||
size_t panel_index = std::distance(category_panels.begin(),
|
||||
std::find_if(category_panels.begin(),
|
||||
category_panels.end(),
|
||||
[&](const auto& p) { return p.id == panel.id; }));
|
||||
|
||||
// Set payload with panel ID string (more stable)
|
||||
std::string panel_id = panel.id;
|
||||
ImGui::SetDragDropPayload("PANEL_PALETTE",
|
||||
panel_id.c_str(),
|
||||
panel_id.size() + 1); // +1 for null terminator
|
||||
|
||||
ImGui::Text("%s %s", panel.icon.c_str(), panel.name.c_str());
|
||||
ImGui::TextDisabled("Drop on canvas to add");
|
||||
ImGui::EndDragDropSource();
|
||||
}
|
||||
```
|
||||
|
||||
### Step 2: Fix Drop Target
|
||||
|
||||
```cpp
|
||||
// In DrawCanvas() - retrieve panel by ID
|
||||
if (const ImGuiPayload* payload =
|
||||
ImGui::AcceptDragDropPayload("PANEL_PALETTE")) {
|
||||
const char* panel_id_str = static_cast<const char*>(payload->Data);
|
||||
std::string panel_id(panel_id_str);
|
||||
|
||||
// Find panel in cache
|
||||
auto panels = GetAvailablePanels();
|
||||
auto it = std::find_if(panels.begin(), panels.end(),
|
||||
[&](const auto& p) { return p.id == panel_id; });
|
||||
|
||||
if (it != panels.end() && drop_target_node_) {
|
||||
const PalettePanel& panel = *it;
|
||||
|
||||
// Now create the LayoutPanel from cached data
|
||||
LayoutPanel new_panel;
|
||||
new_panel.panel_id = panel.id;
|
||||
new_panel.display_name = panel.name;
|
||||
new_panel.icon = panel.icon;
|
||||
new_panel.priority = panel.priority;
|
||||
|
||||
// Add to layout...
|
||||
if (drop_target_node_->IsLeaf() && drop_target_node_->panels.empty()) {
|
||||
drop_target_node_->AddPanel(new_panel);
|
||||
} else if (drop_direction_ != ImGuiDir_None) {
|
||||
// Split and add
|
||||
float split_ratio = (drop_direction_ == ImGuiDir_Right ||
|
||||
drop_direction_ == ImGuiDir_Down) ? 0.7f : 0.3f;
|
||||
drop_target_node_->Split(drop_direction_, split_ratio);
|
||||
|
||||
if (drop_direction_ == ImGuiDir_Left || drop_direction_ == ImGuiDir_Up) {
|
||||
drop_target_node_->child_left->AddPanel(new_panel);
|
||||
} else {
|
||||
drop_target_node_->child_right->AddPanel(new_panel);
|
||||
}
|
||||
}
|
||||
|
||||
current_layout_->Touch();
|
||||
LOG_INFO("LayoutDesigner", "Successfully added panel: %s", panel.name.c_str());
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Step 3: Ensure Drop Zones Appear
|
||||
|
||||
The key is that `DrawDropZones()` must actually set the variables:
|
||||
|
||||
```cpp
|
||||
// In DrawDropZones() - ensure we set drop state
|
||||
void DrawDropZones(...) {
|
||||
// ... existing drop zone rendering ...
|
||||
|
||||
if (is_hovered) {
|
||||
// IMPORTANT: Set these so drop knows what to do
|
||||
drop_target_node_ = target_node;
|
||||
drop_direction_ = zone;
|
||||
|
||||
LOG_INFO("DragDrop", "Drop zone active: %s on node",
|
||||
DirToString(zone).c_str());
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Testing Checklist
|
||||
|
||||
Run through these steps to verify drag-drop works:
|
||||
|
||||
- [ ] Open Layout Designer (Ctrl+L)
|
||||
- [ ] Verify Panel Layout mode is selected
|
||||
- [ ] See panels in left palette
|
||||
- [ ] Click and HOLD on "Room List" panel
|
||||
- [ ] Start dragging (move mouse while holding)
|
||||
- [ ] See tooltip appear with panel name
|
||||
- [ ] Move mouse over canvas (center area)
|
||||
- [ ] See canvas highlight in blue
|
||||
- [ ] See drop zones appear (Left, Right, Top, Bottom, Center areas in blue)
|
||||
- [ ] Move mouse to left side of canvas
|
||||
- [ ] See "← Left" text appear in drop zone
|
||||
- [ ] Release mouse
|
||||
- [ ] Panel should appear in canvas
|
||||
- [ ] See panel name displayed in dock node
|
||||
|
||||
If any step fails, check the corresponding section above.
|
||||
|
||||
---
|
||||
|
||||
## Enable Debug Logging
|
||||
|
||||
Add to start of `Draw()` method:
|
||||
|
||||
```cpp
|
||||
void LayoutDesignerWindow::Draw() {
|
||||
// Debug: Log drag-drop state
|
||||
const ImGuiPayload* payload = ImGui::GetDragDropPayload();
|
||||
static int last_log_frame = -1;
|
||||
int current_frame = ImGui::GetFrameCount();
|
||||
|
||||
if (payload && current_frame != last_log_frame) {
|
||||
LOG_INFO("DragDrop", "Frame %d: Dragging %s",
|
||||
current_frame, payload->DataType);
|
||||
last_log_frame = current_frame;
|
||||
}
|
||||
|
||||
// ... rest of Draw()
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Still Not Working?
|
||||
|
||||
### Nuclear Option: Simplified Test
|
||||
|
||||
Create a minimal test to verify ImGui drag-drop works:
|
||||
|
||||
```cpp
|
||||
void TestDragDrop() {
|
||||
// Source
|
||||
ImGui::Text("Drag me");
|
||||
if (ImGui::BeginDragDropSource()) {
|
||||
const char* test = "test";
|
||||
ImGui::SetDragDropPayload("TEST", test, 5);
|
||||
ImGui::Text("Dragging...");
|
||||
ImGui::EndDragDropSource();
|
||||
}
|
||||
|
||||
// Target
|
||||
ImGui::Button("Drop here", ImVec2(200, 200));
|
||||
if (ImGui::BeginDragDropTarget()) {
|
||||
if (const ImGuiPayload* p = ImGui::AcceptDragDropPayload("TEST")) {
|
||||
LOG_INFO("Test", "Drop worked!");
|
||||
}
|
||||
ImGui::EndDragDropTarget();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
If this works, the issue is in our implementation. If it doesn't, ImGui drag-drop might not be enabled.
|
||||
|
||||
---
|
||||
|
||||
## Quick Diagnostic
|
||||
|
||||
Add this to the top of `DrawCanvas()`:
|
||||
|
||||
```cpp
|
||||
void LayoutDesignerWindow::DrawCanvas() {
|
||||
// DIAGNOSTIC
|
||||
if (ImGui::GetDragDropPayload()) {
|
||||
ImGui::TextColored(ImVec4(0, 1, 0, 1),
|
||||
"DRAGGING: %s",
|
||||
ImGui::GetDragDropPayload()->DataType);
|
||||
} else {
|
||||
ImGui::TextColored(ImVec4(1, 0, 0, 1), "Not dragging");
|
||||
}
|
||||
ImGui::Separator();
|
||||
|
||||
// ... rest of method
|
||||
}
|
||||
```
|
||||
|
||||
This will show in real-time if dragging is detected.
|
||||
|
||||
---
|
||||
|
||||
## Most Likely Fix
|
||||
|
||||
The issue is probably the payload data type (std::string in struct). Here's the corrected implementation:
|
||||
|
||||
**In `layout_designer_window.h`, add:**
|
||||
```cpp
|
||||
// Cached panels with stable indices
|
||||
mutable std::vector<PalettePanel> panel_cache_;
|
||||
```
|
||||
|
||||
**In `DrawPalette()`, use index:**
|
||||
```cpp
|
||||
// When setting up drag source
|
||||
if (ImGui::BeginDragDropSource(ImGuiDragDropFlags_None)) {
|
||||
// Find index in current category_panels vector
|
||||
const PalettePanel* panel_ptr = &panel; // Stable pointer
|
||||
ImGui::SetDragDropPayload("PANEL_PALETTE", &panel_ptr, sizeof(PalettePanel*));
|
||||
ImGui::Text("%s %s", panel.icon.c_str(), panel.name.c_str());
|
||||
ImGui::TextDisabled("Drop on canvas to add");
|
||||
ImGui::EndDragDropSource();
|
||||
}
|
||||
```
|
||||
|
||||
**In `DrawCanvas()`, dereference pointer:**
|
||||
```cpp
|
||||
if (const ImGuiPayload* payload =
|
||||
ImGui::AcceptDragDropPayload("PANEL_PALETTE")) {
|
||||
const PalettePanel* const* panel_ptr_ptr =
|
||||
static_cast<const PalettePanel* const*>(payload->Data);
|
||||
const PalettePanel* panel = *panel_ptr_ptr;
|
||||
|
||||
// Now use panel->id, panel->name, etc.
|
||||
LayoutPanel new_panel;
|
||||
new_panel.panel_id = panel->id;
|
||||
new_panel.display_name = panel->name;
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
This avoids copying std::string in the payload!
|
||||
|
||||
@@ -1,232 +0,0 @@
|
||||
#include "app/editor/layout_designer/layout_definition.h"
|
||||
|
||||
#include <chrono>
|
||||
|
||||
namespace yaze {
|
||||
namespace editor {
|
||||
namespace layout_designer {
|
||||
|
||||
// ============================================================================
|
||||
// DockNode Implementation
|
||||
// ============================================================================
|
||||
|
||||
void DockNode::AddPanel(const LayoutPanel& panel) {
|
||||
if (type == DockNodeType::Split) {
|
||||
// Can only add panels to leaf/root nodes
|
||||
return;
|
||||
}
|
||||
panels.push_back(panel);
|
||||
}
|
||||
|
||||
void DockNode::Split(ImGuiDir direction, float ratio) {
|
||||
if (type == DockNodeType::Split) {
|
||||
// Already split
|
||||
return;
|
||||
}
|
||||
|
||||
type = DockNodeType::Split;
|
||||
split_dir = direction;
|
||||
split_ratio = ratio;
|
||||
|
||||
// Move existing panels to left child
|
||||
child_left = std::make_unique<DockNode>();
|
||||
child_left->type = DockNodeType::Leaf;
|
||||
child_left->panels = std::move(panels);
|
||||
panels.clear();
|
||||
|
||||
// Create empty right child
|
||||
child_right = std::make_unique<DockNode>();
|
||||
child_right->type = DockNodeType::Leaf;
|
||||
}
|
||||
|
||||
LayoutPanel* DockNode::FindPanel(const std::string& panel_id) {
|
||||
if (type == DockNodeType::Leaf) {
|
||||
for (auto& panel : panels) {
|
||||
if (panel.panel_id == panel_id) {
|
||||
return &panel;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Search children
|
||||
if (child_left) {
|
||||
if (auto* found = child_left->FindPanel(panel_id)) {
|
||||
return found;
|
||||
}
|
||||
}
|
||||
if (child_right) {
|
||||
if (auto* found = child_right->FindPanel(panel_id)) {
|
||||
return found;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
size_t DockNode::CountPanels() const {
|
||||
if (type == DockNodeType::Leaf || type == DockNodeType::Root) {
|
||||
return panels.size();
|
||||
}
|
||||
|
||||
size_t count = 0;
|
||||
if (child_left) {
|
||||
count += child_left->CountPanels();
|
||||
}
|
||||
if (child_right) {
|
||||
count += child_right->CountPanels();
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
std::unique_ptr<DockNode> DockNode::Clone() const {
|
||||
auto clone = std::make_unique<DockNode>();
|
||||
clone->type = type;
|
||||
clone->node_id = node_id;
|
||||
clone->split_dir = split_dir;
|
||||
clone->split_ratio = split_ratio;
|
||||
clone->flags = flags;
|
||||
clone->panels = panels;
|
||||
|
||||
if (child_left) {
|
||||
clone->child_left = child_left->Clone();
|
||||
}
|
||||
if (child_right) {
|
||||
clone->child_right = child_right->Clone();
|
||||
}
|
||||
|
||||
return clone;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// LayoutDefinition Implementation
|
||||
// ============================================================================
|
||||
|
||||
LayoutDefinition LayoutDefinition::CreateEmpty(const std::string& name) {
|
||||
LayoutDefinition layout;
|
||||
layout.name = name;
|
||||
layout.description = "Empty layout";
|
||||
layout.root = std::make_unique<DockNode>();
|
||||
layout.root->type = DockNodeType::Root;
|
||||
|
||||
auto now = std::chrono::system_clock::now();
|
||||
layout.created_timestamp = std::chrono::duration_cast<std::chrono::seconds>(
|
||||
now.time_since_epoch()).count();
|
||||
layout.modified_timestamp = layout.created_timestamp;
|
||||
|
||||
return layout;
|
||||
}
|
||||
|
||||
std::unique_ptr<LayoutDefinition> LayoutDefinition::Clone() const {
|
||||
auto clone = std::make_unique<LayoutDefinition>();
|
||||
clone->name = name;
|
||||
clone->description = description;
|
||||
clone->editor_type = editor_type;
|
||||
clone->canvas_size = canvas_size;
|
||||
clone->author = author;
|
||||
clone->version = version;
|
||||
clone->created_timestamp = created_timestamp;
|
||||
clone->modified_timestamp = modified_timestamp;
|
||||
|
||||
if (root) {
|
||||
clone->root = root->Clone();
|
||||
}
|
||||
|
||||
return clone;
|
||||
}
|
||||
|
||||
LayoutPanel* LayoutDefinition::FindPanel(const std::string& panel_id) const {
|
||||
if (!root) {
|
||||
return nullptr;
|
||||
}
|
||||
return root->FindPanel(panel_id);
|
||||
}
|
||||
|
||||
std::vector<LayoutPanel*> LayoutDefinition::GetAllPanels() const {
|
||||
std::vector<LayoutPanel*> result;
|
||||
|
||||
if (!root) {
|
||||
return result;
|
||||
}
|
||||
|
||||
// Recursive helper to collect panels
|
||||
std::function<void(DockNode*)> collect = [&](DockNode* node) {
|
||||
if (!node) return;
|
||||
|
||||
if (node->type == DockNodeType::Leaf) {
|
||||
for (auto& panel : node->panels) {
|
||||
result.push_back(&panel);
|
||||
}
|
||||
} else {
|
||||
collect(node->child_left.get());
|
||||
collect(node->child_right.get());
|
||||
}
|
||||
};
|
||||
|
||||
collect(root.get());
|
||||
return result;
|
||||
}
|
||||
|
||||
bool LayoutDefinition::Validate(std::string* error_message) const {
|
||||
if (name.empty()) {
|
||||
if (error_message) {
|
||||
*error_message = "Layout name cannot be empty";
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!root) {
|
||||
if (error_message) {
|
||||
*error_message = "Layout must have a root node";
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Validate that split nodes have both children
|
||||
std::function<bool(const DockNode*)> validate_node =
|
||||
[&](const DockNode* node) -> bool {
|
||||
if (!node) {
|
||||
if (error_message) {
|
||||
*error_message = "Null node found in tree";
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
if (node->type == DockNodeType::Split) {
|
||||
if (!node->child_left || !node->child_right) {
|
||||
if (error_message) {
|
||||
*error_message = "Split node must have both children";
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
if (node->split_ratio <= 0.0f || node->split_ratio >= 1.0f) {
|
||||
if (error_message) {
|
||||
*error_message = "Split ratio must be between 0.0 and 1.0";
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!validate_node(node->child_left.get())) {
|
||||
return false;
|
||||
}
|
||||
if (!validate_node(node->child_right.get())) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
return validate_node(root.get());
|
||||
}
|
||||
|
||||
void LayoutDefinition::Touch() {
|
||||
auto now = std::chrono::system_clock::now();
|
||||
modified_timestamp = std::chrono::duration_cast<std::chrono::seconds>(
|
||||
now.time_since_epoch()).count();
|
||||
}
|
||||
|
||||
} // namespace layout_designer
|
||||
} // namespace editor
|
||||
} // namespace yaze
|
||||
@@ -1,168 +0,0 @@
|
||||
#ifndef YAZE_APP_EDITOR_LAYOUT_DESIGNER_LAYOUT_DEFINITION_H_
|
||||
#define YAZE_APP_EDITOR_LAYOUT_DESIGNER_LAYOUT_DEFINITION_H_
|
||||
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "imgui/imgui.h"
|
||||
#include "app/editor/editor.h"
|
||||
|
||||
namespace yaze {
|
||||
namespace editor {
|
||||
namespace layout_designer {
|
||||
|
||||
/**
|
||||
* @enum DockNodeType
|
||||
* @brief Type of dock node in the layout tree
|
||||
*/
|
||||
enum class DockNodeType {
|
||||
Root, // Root dockspace node
|
||||
Split, // Container with horizontal/vertical split
|
||||
Leaf // Leaf node containing panels
|
||||
};
|
||||
|
||||
/**
|
||||
* @struct LayoutPanel
|
||||
* @brief Represents a single panel in a layout
|
||||
*
|
||||
* Contains all metadata needed to recreate the panel in a layout,
|
||||
* including size, position, flags, and visual properties.
|
||||
*/
|
||||
struct LayoutPanel {
|
||||
std::string panel_id; // Unique panel identifier (e.g., "dungeon.room_selector")
|
||||
std::string display_name; // Human-readable name
|
||||
std::string icon; // Icon identifier (e.g., "ICON_MD_LIST")
|
||||
|
||||
// Size configuration
|
||||
ImVec2 size = ImVec2(-1, -1); // Size in pixels (-1 = auto)
|
||||
float size_ratio = 0.0f; // Size ratio for dock splits (0.0 to 1.0)
|
||||
|
||||
// Visibility and priority
|
||||
bool visible_by_default = true;
|
||||
int priority = 100;
|
||||
|
||||
// Panel flags
|
||||
bool closable = true;
|
||||
bool minimizable = true;
|
||||
bool pinnable = true;
|
||||
bool headless = false;
|
||||
bool docking_allowed = true;
|
||||
ImGuiWindowFlags flags = ImGuiWindowFlags_None;
|
||||
|
||||
// Runtime state (for preview)
|
||||
ImGuiID dock_id = 0;
|
||||
bool is_floating = false;
|
||||
ImVec2 floating_pos = ImVec2(100, 100);
|
||||
ImVec2 floating_size = ImVec2(400, 300);
|
||||
};
|
||||
|
||||
/**
|
||||
* @struct DockNode
|
||||
* @brief Represents a dock node in the layout tree
|
||||
*
|
||||
* Hierarchical structure representing the docking layout.
|
||||
* Can be a split (with two children) or a leaf (containing panels).
|
||||
*/
|
||||
struct DockNode {
|
||||
DockNodeType type = DockNodeType::Leaf;
|
||||
ImGuiID node_id = 0;
|
||||
|
||||
// Split configuration (for type == Split)
|
||||
ImGuiDir split_dir = ImGuiDir_None; // Left, Right, Up, Down
|
||||
float split_ratio = 0.5f; // Split ratio (0.0 to 1.0)
|
||||
|
||||
// Children (for type == Split)
|
||||
std::unique_ptr<DockNode> child_left;
|
||||
std::unique_ptr<DockNode> child_right;
|
||||
|
||||
// Panels (for type == Leaf)
|
||||
std::vector<LayoutPanel> panels;
|
||||
|
||||
// Dock node flags
|
||||
ImGuiDockNodeFlags flags = ImGuiDockNodeFlags_None;
|
||||
|
||||
// Helper methods
|
||||
bool IsSplit() const { return type == DockNodeType::Split; }
|
||||
bool IsLeaf() const { return type == DockNodeType::Leaf || type == DockNodeType::Root; }
|
||||
bool IsRoot() const { return type == DockNodeType::Root; }
|
||||
|
||||
// Add a panel to this leaf node
|
||||
void AddPanel(const LayoutPanel& panel);
|
||||
|
||||
// Split this node in a direction
|
||||
void Split(ImGuiDir direction, float ratio);
|
||||
|
||||
// Find a panel by ID in the tree
|
||||
LayoutPanel* FindPanel(const std::string& panel_id);
|
||||
|
||||
// Count total panels in the tree
|
||||
size_t CountPanels() const;
|
||||
|
||||
// Clone the node and its children
|
||||
std::unique_ptr<DockNode> Clone() const;
|
||||
};
|
||||
|
||||
/**
|
||||
* @struct LayoutDefinition
|
||||
* @brief Complete layout definition with metadata
|
||||
*
|
||||
* Represents a full workspace layout that can be saved, loaded,
|
||||
* and applied to the editor. Includes the dock tree and metadata.
|
||||
*/
|
||||
struct LayoutDefinition {
|
||||
// Identity
|
||||
std::string name;
|
||||
std::string description;
|
||||
EditorType editor_type = EditorType::kUnknown;
|
||||
|
||||
// Layout structure
|
||||
std::unique_ptr<DockNode> root;
|
||||
ImVec2 canvas_size = ImVec2(1920, 1080);
|
||||
|
||||
// Metadata
|
||||
std::string author;
|
||||
std::string version = "1.0.0";
|
||||
int64_t created_timestamp = 0;
|
||||
int64_t modified_timestamp = 0;
|
||||
|
||||
// Helper methods
|
||||
|
||||
/**
|
||||
* @brief Create a default empty layout
|
||||
*/
|
||||
static LayoutDefinition CreateEmpty(const std::string& name);
|
||||
|
||||
/**
|
||||
* @brief Clone the layout definition
|
||||
*/
|
||||
std::unique_ptr<LayoutDefinition> Clone() const;
|
||||
|
||||
/**
|
||||
* @brief Find a panel by ID anywhere in the layout
|
||||
*/
|
||||
LayoutPanel* FindPanel(const std::string& panel_id) const;
|
||||
|
||||
/**
|
||||
* @brief Get all panels in the layout
|
||||
*/
|
||||
std::vector<LayoutPanel*> GetAllPanels() const;
|
||||
|
||||
/**
|
||||
* @brief Validate the layout structure
|
||||
* @return true if layout is valid
|
||||
*/
|
||||
bool Validate(std::string* error_message = nullptr) const;
|
||||
|
||||
/**
|
||||
* @brief Update the modified timestamp to current time
|
||||
*/
|
||||
void Touch();
|
||||
};
|
||||
|
||||
} // namespace layout_designer
|
||||
} // namespace editor
|
||||
} // namespace yaze
|
||||
|
||||
#endif // YAZE_APP_EDITOR_LAYOUT_DESIGNER_LAYOUT_DEFINITION_H_
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,248 +0,0 @@
|
||||
#ifndef YAZE_APP_EDITOR_LAYOUT_DESIGNER_LAYOUT_DESIGNER_WINDOW_H_
|
||||
#define YAZE_APP_EDITOR_LAYOUT_DESIGNER_LAYOUT_DESIGNER_WINDOW_H_
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <optional>
|
||||
|
||||
#include "app/editor/layout_designer/layout_definition.h"
|
||||
#include "app/editor/layout_designer/widget_definition.h"
|
||||
#include "app/editor/layout_designer/theme_properties.h"
|
||||
#include "app/editor/system/panel_manager.h"
|
||||
|
||||
#define IMGUI_DEFINE_MATH_OPERATORS
|
||||
#include "imgui/imgui.h"
|
||||
|
||||
namespace yaze { namespace editor { class LayoutManager; class EditorManager; } }
|
||||
|
||||
namespace yaze {
|
||||
namespace editor {
|
||||
namespace layout_designer {
|
||||
|
||||
/**
|
||||
* @enum DesignMode
|
||||
* @brief Design mode for the layout designer
|
||||
*/
|
||||
enum class DesignMode {
|
||||
PanelLayout, // Design panel/window layout
|
||||
WidgetDesign // Design internal panel widgets
|
||||
};
|
||||
|
||||
/**
|
||||
* @class LayoutDesignerWindow
|
||||
* @brief Main window for the WYSIWYG layout designer
|
||||
*
|
||||
* Provides a visual interface for designing ImGui panel layouts:
|
||||
* - Panel Layout Mode: Drag-and-drop panel placement, dock splits
|
||||
* - Widget Design Mode: Design internal panel layouts with widgets
|
||||
* - Properties editing
|
||||
* - Code generation
|
||||
* - Import/Export
|
||||
*/
|
||||
class LayoutDesignerWindow {
|
||||
public:
|
||||
LayoutDesignerWindow() = default;
|
||||
explicit LayoutDesignerWindow(yaze::editor::LayoutManager* layout_manager,
|
||||
PanelManager* panel_manager,
|
||||
yaze::editor::EditorManager* editor_manager)
|
||||
: layout_manager_(layout_manager),
|
||||
editor_manager_(editor_manager),
|
||||
panel_manager_(panel_manager) {}
|
||||
|
||||
/**
|
||||
* @brief Initialize the designer with manager references
|
||||
* @param panel_manager Reference to PanelManager for importing panels
|
||||
*/
|
||||
void Initialize(PanelManager* panel_manager, yaze::editor::LayoutManager* layout_manager = nullptr, yaze::editor::EditorManager* editor_manager = nullptr);
|
||||
|
||||
/**
|
||||
* @brief Open the designer window
|
||||
*/
|
||||
void Open();
|
||||
|
||||
/**
|
||||
* @brief Close the designer window
|
||||
*/
|
||||
void Close();
|
||||
|
||||
/**
|
||||
* @brief Check if designer window is open
|
||||
*/
|
||||
bool IsOpen() const { return is_open_; }
|
||||
|
||||
/**
|
||||
* @brief Draw the designer window (call every frame)
|
||||
*/
|
||||
void Draw();
|
||||
|
||||
/**
|
||||
* @brief Create a new empty layout
|
||||
*/
|
||||
void NewLayout();
|
||||
|
||||
/**
|
||||
* @brief Load a layout from JSON file
|
||||
*/
|
||||
void LoadLayout(const std::string& filepath);
|
||||
|
||||
/**
|
||||
* @brief Save current layout to JSON file
|
||||
*/
|
||||
void SaveLayout(const std::string& filepath);
|
||||
|
||||
/**
|
||||
* @brief Import layout from current runtime state
|
||||
*/
|
||||
void ImportFromRuntime();
|
||||
|
||||
/**
|
||||
* @brief Import a specific panel's design from runtime
|
||||
* @param panel_id The panel ID to import
|
||||
*/
|
||||
void ImportPanelDesign(const std::string& panel_id);
|
||||
|
||||
/**
|
||||
* @brief Export layout as C++ code
|
||||
*/
|
||||
void ExportCode(const std::string& filepath);
|
||||
|
||||
/**
|
||||
* @brief Apply current layout to the application (live preview)
|
||||
*/
|
||||
void PreviewLayout();
|
||||
|
||||
private:
|
||||
// Panel palette
|
||||
struct PalettePanel {
|
||||
std::string id;
|
||||
std::string name;
|
||||
std::string icon;
|
||||
std::string category;
|
||||
std::string description;
|
||||
int priority;
|
||||
};
|
||||
|
||||
// UI Components
|
||||
void DrawMenuBar();
|
||||
void DrawToolbar();
|
||||
void DrawPalette();
|
||||
void DrawCanvas();
|
||||
void DrawProperties();
|
||||
void DrawTreeView();
|
||||
void DrawCodePreview();
|
||||
|
||||
// Widget Design Mode UI
|
||||
void DrawWidgetPalette();
|
||||
void DrawWidgetCanvas();
|
||||
void DrawWidgetProperties();
|
||||
void DrawWidgetTree();
|
||||
void DrawWidgetCodePreview();
|
||||
|
||||
// Theme UI
|
||||
void DrawThemeProperties();
|
||||
|
||||
// Canvas interaction
|
||||
void HandleCanvasDragDrop();
|
||||
void DrawDockNode(DockNode* node, const ImVec2& pos, const ImVec2& size);
|
||||
void DrawDropZones(const ImVec2& pos, const ImVec2& size, DockNode* target_node);
|
||||
bool IsMouseOverRect(const ImVec2& rect_min, const ImVec2& rect_max) const;
|
||||
ImGuiDir GetDropZone(const ImVec2& mouse_pos, const ImVec2& rect_min,
|
||||
const ImVec2& rect_max) const;
|
||||
void ResetDropState();
|
||||
std::optional<PalettePanel> ResolvePanelById(const std::string& panel_id) const;
|
||||
void AddPanelToTarget(const PalettePanel& panel);
|
||||
|
||||
// Properties
|
||||
void DrawPanelProperties(LayoutPanel* panel);
|
||||
void DrawNodeProperties(DockNode* node);
|
||||
|
||||
// Code generation
|
||||
std::string GenerateDockBuilderCode() const;
|
||||
std::string GenerateLayoutPresetCode() const;
|
||||
|
||||
// Tree View
|
||||
void DrawDockNodeTree(DockNode* node, int& node_index);
|
||||
|
||||
// Edit operations
|
||||
void DeleteNode(DockNode* node);
|
||||
void DeletePanel(LayoutPanel* panel);
|
||||
|
||||
// Undo/Redo
|
||||
void PushUndoState();
|
||||
void Undo();
|
||||
void Redo();
|
||||
|
||||
// Undo/Redo stacks
|
||||
std::vector<std::unique_ptr<LayoutDefinition>> undo_stack_;
|
||||
std::vector<std::unique_ptr<LayoutDefinition>> redo_stack_;
|
||||
static constexpr size_t kMaxUndoSteps = 50;
|
||||
|
||||
std::vector<PalettePanel> GetAvailablePanels() const;
|
||||
void RefreshPanelCache();
|
||||
bool MatchesSearchFilter(const PalettePanel& panel) const;
|
||||
|
||||
// State
|
||||
bool is_open_ = false;
|
||||
bool show_code_preview_ = false;
|
||||
bool show_tree_view_ = true;
|
||||
|
||||
// Design mode
|
||||
DesignMode design_mode_ = DesignMode::PanelLayout;
|
||||
|
||||
// Current layout being edited
|
||||
std::unique_ptr<LayoutDefinition> current_layout_;
|
||||
|
||||
// Widget design state
|
||||
std::unique_ptr<PanelDesign> current_panel_design_;
|
||||
std::string selected_panel_for_design_; // Panel ID to design widgets for
|
||||
|
||||
// Theme properties
|
||||
ThemeProperties theme_properties_;
|
||||
ThemePropertiesPanel theme_panel_;
|
||||
bool show_theme_panel_ = false;
|
||||
|
||||
// Selection state (Panel Layout Mode)
|
||||
LayoutPanel* selected_panel_ = nullptr;
|
||||
DockNode* selected_node_ = nullptr;
|
||||
DockNode* last_drop_node_for_preview_ = nullptr;
|
||||
|
||||
// Selection state (Widget Design Mode)
|
||||
WidgetDefinition* selected_widget_ = nullptr;
|
||||
|
||||
// Drag and drop state
|
||||
bool is_dragging_panel_ = false;
|
||||
PalettePanel dragging_panel_;
|
||||
ImVec2 drop_zone_pos_;
|
||||
ImVec2 drop_zone_size_;
|
||||
ImGuiDir drop_direction_ = ImGuiDir_None;
|
||||
DockNode* drop_target_node_ = nullptr;
|
||||
|
||||
// Preview/application hooks
|
||||
yaze::editor::LayoutManager* layout_manager_ = nullptr;
|
||||
yaze::editor::EditorManager* editor_manager_ = nullptr;
|
||||
// Panel manager reference (for importing panels)
|
||||
PanelManager* panel_manager_ = nullptr;
|
||||
|
||||
// Search filter for palette
|
||||
char search_filter_[256] = "";
|
||||
std::string selected_category_filter_ = "All";
|
||||
|
||||
// Widget palette search
|
||||
char widget_search_filter_[256] = "";
|
||||
std::string selected_widget_category_ = "All";
|
||||
|
||||
// Panel cache
|
||||
mutable std::vector<PalettePanel> panel_cache_;
|
||||
mutable bool panel_cache_dirty_ = true;
|
||||
|
||||
// Canvas state
|
||||
ImVec2 canvas_scroll_ = ImVec2(0, 0);
|
||||
float canvas_zoom_ = 1.0f;
|
||||
};
|
||||
|
||||
} // namespace layout_designer
|
||||
} // namespace editor
|
||||
} // namespace yaze
|
||||
|
||||
#endif // YAZE_APP_EDITOR_LAYOUT_DESIGNER_LAYOUT_DESIGNER_WINDOW_H_
|
||||
@@ -1,201 +0,0 @@
|
||||
#include "app/editor/layout_designer/layout_serialization.h"
|
||||
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
|
||||
#include "absl/status/status.h"
|
||||
#include "absl/strings/str_format.h"
|
||||
#include "util/log.h"
|
||||
|
||||
// Simple JSON-like serialization (can be replaced with nlohmann/json later)
|
||||
namespace yaze {
|
||||
namespace editor {
|
||||
namespace layout_designer {
|
||||
|
||||
namespace {
|
||||
|
||||
// Helper to escape JSON strings
|
||||
std::string EscapeJson(const std::string& str) {
|
||||
std::string result;
|
||||
for (char chr : str) {
|
||||
switch (chr) {
|
||||
case '"': result += "\\\""; break;
|
||||
case '\\': result += "\\\\"; break;
|
||||
case '\n': result += "\\n"; break;
|
||||
case '\r': result += "\\r"; break;
|
||||
case '\t': result += "\\t"; break;
|
||||
default: result += chr; break;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string DirToString(ImGuiDir dir) {
|
||||
switch (dir) {
|
||||
case ImGuiDir_None: return "none";
|
||||
case ImGuiDir_Left: return "left";
|
||||
case ImGuiDir_Right: return "right";
|
||||
case ImGuiDir_Up: return "up";
|
||||
case ImGuiDir_Down: return "down";
|
||||
case ImGuiDir_COUNT: return "none"; // Unused, but handle for completeness
|
||||
default: return "none";
|
||||
}
|
||||
}
|
||||
|
||||
ImGuiDir StringToDir(const std::string& str) {
|
||||
if (str == "left") return ImGuiDir_Left;
|
||||
if (str == "right") return ImGuiDir_Right;
|
||||
if (str == "up") return ImGuiDir_Up;
|
||||
if (str == "down") return ImGuiDir_Down;
|
||||
return ImGuiDir_None;
|
||||
}
|
||||
|
||||
std::string NodeTypeToString(DockNodeType type) {
|
||||
switch (type) {
|
||||
case DockNodeType::Root: return "root";
|
||||
case DockNodeType::Split: return "split";
|
||||
case DockNodeType::Leaf: return "leaf";
|
||||
default: return "leaf";
|
||||
}
|
||||
}
|
||||
|
||||
DockNodeType StringToNodeType(const std::string& str) {
|
||||
if (str == "root") return DockNodeType::Root;
|
||||
if (str == "split") return DockNodeType::Split;
|
||||
if (str == "leaf") return DockNodeType::Leaf;
|
||||
return DockNodeType::Leaf;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
std::string LayoutSerializer::ToJson(const LayoutDefinition& layout) {
|
||||
std::ostringstream json;
|
||||
|
||||
json << "{\n";
|
||||
json << " \"layout\": {\n";
|
||||
json << " \"name\": \"" << EscapeJson(layout.name) << "\",\n";
|
||||
json << " \"description\": \"" << EscapeJson(layout.description) << "\",\n";
|
||||
json << " \"version\": \"" << EscapeJson(layout.version) << "\",\n";
|
||||
json << " \"author\": \"" << EscapeJson(layout.author) << "\",\n";
|
||||
json << " \"created_timestamp\": " << layout.created_timestamp << ",\n";
|
||||
json << " \"modified_timestamp\": " << layout.modified_timestamp << ",\n";
|
||||
json << " \"canvas_size\": [" << layout.canvas_size.x << ", "
|
||||
<< layout.canvas_size.y << "],\n";
|
||||
|
||||
if (layout.root) {
|
||||
json << " \"root_node\": " << SerializeDockNode(*layout.root) << "\n";
|
||||
} else {
|
||||
json << " \"root_node\": null\n";
|
||||
}
|
||||
|
||||
json << " }\n";
|
||||
json << "}\n";
|
||||
|
||||
return json.str();
|
||||
}
|
||||
|
||||
std::string LayoutSerializer::SerializeDockNode(const DockNode& node) {
|
||||
std::ostringstream json;
|
||||
|
||||
json << "{\n";
|
||||
json << " \"type\": \"" << NodeTypeToString(node.type) << "\",\n";
|
||||
|
||||
if (node.IsSplit()) {
|
||||
json << " \"split_dir\": \"" << DirToString(node.split_dir) << "\",\n";
|
||||
json << " \"split_ratio\": " << node.split_ratio << ",\n";
|
||||
|
||||
json << " \"left_child\": ";
|
||||
if (node.child_left) {
|
||||
json << SerializeDockNode(*node.child_left);
|
||||
} else {
|
||||
json << "null";
|
||||
}
|
||||
json << ",\n";
|
||||
|
||||
json << " \"right_child\": ";
|
||||
if (node.child_right) {
|
||||
json << SerializeDockNode(*node.child_right);
|
||||
} else {
|
||||
json << "null";
|
||||
}
|
||||
json << "\n";
|
||||
|
||||
} else if (node.IsLeaf()) {
|
||||
json << " \"panels\": [\n";
|
||||
for (size_t idx = 0; idx < node.panels.size(); ++idx) {
|
||||
json << " " << SerializePanel(node.panels[idx]);
|
||||
if (idx < node.panels.size() - 1) {
|
||||
json << ",";
|
||||
}
|
||||
json << "\n";
|
||||
}
|
||||
json << " ]\n";
|
||||
}
|
||||
|
||||
json << " }";
|
||||
|
||||
return json.str();
|
||||
}
|
||||
|
||||
std::string LayoutSerializer::SerializePanel(const LayoutPanel& panel) {
|
||||
return absl::StrFormat(
|
||||
"{\"id\":\"%s\",\"name\":\"%s\",\"icon\":\"%s\","
|
||||
"\"priority\":%d,\"visible\":%s,\"closable\":%s,\"pinnable\":%s}",
|
||||
EscapeJson(panel.panel_id),
|
||||
EscapeJson(panel.display_name),
|
||||
EscapeJson(panel.icon),
|
||||
panel.priority,
|
||||
panel.visible_by_default ? "true" : "false",
|
||||
panel.closable ? "true" : "false",
|
||||
panel.pinnable ? "true" : "false");
|
||||
}
|
||||
|
||||
absl::StatusOr<LayoutDefinition> LayoutSerializer::FromJson(
|
||||
const std::string& json_str) {
|
||||
// TODO(scawful): Implement full JSON parsing
|
||||
// For now, this is a placeholder that returns an error
|
||||
return absl::UnimplementedError(
|
||||
"JSON deserialization not yet implemented. "
|
||||
"This requires integrating nlohmann/json library.");
|
||||
}
|
||||
|
||||
absl::Status LayoutSerializer::SaveToFile(const LayoutDefinition& layout,
|
||||
const std::string& filepath) {
|
||||
std::ofstream file(filepath);
|
||||
if (!file.is_open()) {
|
||||
return absl::InternalError(
|
||||
absl::StrFormat("Failed to open file for writing: %s", filepath));
|
||||
}
|
||||
|
||||
std::string json = ToJson(layout);
|
||||
file << json;
|
||||
file.close();
|
||||
|
||||
if (file.fail()) {
|
||||
return absl::InternalError(
|
||||
absl::StrFormat("Failed to write to file: %s", filepath));
|
||||
}
|
||||
|
||||
LOG_INFO("LayoutSerializer", "Saved layout to: %s", filepath.c_str());
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
absl::StatusOr<LayoutDefinition> LayoutSerializer::LoadFromFile(
|
||||
const std::string& filepath) {
|
||||
std::ifstream file(filepath);
|
||||
if (!file.is_open()) {
|
||||
return absl::InternalError(
|
||||
absl::StrFormat("Failed to open file for reading: %s", filepath));
|
||||
}
|
||||
|
||||
std::stringstream buffer;
|
||||
buffer << file.rdbuf();
|
||||
file.close();
|
||||
|
||||
return FromJson(buffer.str());
|
||||
}
|
||||
|
||||
} // namespace layout_designer
|
||||
} // namespace editor
|
||||
} // namespace yaze
|
||||
|
||||
@@ -1,63 +0,0 @@
|
||||
#ifndef YAZE_APP_EDITOR_LAYOUT_DESIGNER_LAYOUT_SERIALIZATION_H_
|
||||
#define YAZE_APP_EDITOR_LAYOUT_DESIGNER_LAYOUT_SERIALIZATION_H_
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "app/editor/layout_designer/layout_definition.h"
|
||||
#include "absl/status/statusor.h"
|
||||
|
||||
namespace yaze {
|
||||
namespace editor {
|
||||
namespace layout_designer {
|
||||
|
||||
/**
|
||||
* @class LayoutSerializer
|
||||
* @brief Handles JSON serialization and deserialization of layouts
|
||||
*/
|
||||
class LayoutSerializer {
|
||||
public:
|
||||
/**
|
||||
* @brief Serialize a layout to JSON string
|
||||
* @param layout The layout to serialize
|
||||
* @return JSON string representation
|
||||
*/
|
||||
static std::string ToJson(const LayoutDefinition& layout);
|
||||
|
||||
/**
|
||||
* @brief Deserialize a layout from JSON string
|
||||
* @param json_str The JSON string
|
||||
* @return LayoutDefinition or error status
|
||||
*/
|
||||
static absl::StatusOr<LayoutDefinition> FromJson(const std::string& json_str);
|
||||
|
||||
/**
|
||||
* @brief Save layout to JSON file
|
||||
* @param layout The layout to save
|
||||
* @param filepath Path to save to
|
||||
* @return Success status
|
||||
*/
|
||||
static absl::Status SaveToFile(const LayoutDefinition& layout,
|
||||
const std::string& filepath);
|
||||
|
||||
/**
|
||||
* @brief Load layout from JSON file
|
||||
* @param filepath Path to load from
|
||||
* @return LayoutDefinition or error status
|
||||
*/
|
||||
static absl::StatusOr<LayoutDefinition> LoadFromFile(const std::string& filepath);
|
||||
|
||||
private:
|
||||
// Helper methods for converting individual components
|
||||
static std::string SerializePanel(const LayoutPanel& panel);
|
||||
static std::string SerializeDockNode(const DockNode& node);
|
||||
|
||||
static LayoutPanel DeserializePanel(const std::string& json);
|
||||
static std::unique_ptr<DockNode> DeserializeDockNode(const std::string& json);
|
||||
};
|
||||
|
||||
} // namespace layout_designer
|
||||
} // namespace editor
|
||||
} // namespace yaze
|
||||
|
||||
#endif // YAZE_APP_EDITOR_LAYOUT_DESIGNER_LAYOUT_SERIALIZATION_H_
|
||||
|
||||
@@ -1,203 +0,0 @@
|
||||
#include "app/editor/layout_designer/theme_properties.h"
|
||||
|
||||
#include "absl/strings/str_format.h"
|
||||
#include "app/gui/core/icons.h"
|
||||
|
||||
namespace yaze {
|
||||
namespace editor {
|
||||
namespace layout_designer {
|
||||
|
||||
void ThemeProperties::Apply() const {
|
||||
ImGuiStyle& style = ImGui::GetStyle();
|
||||
|
||||
// Padding
|
||||
style.WindowPadding = window_padding;
|
||||
style.FramePadding = frame_padding;
|
||||
style.CellPadding = cell_padding;
|
||||
style.ItemSpacing = item_spacing;
|
||||
style.ItemInnerSpacing = item_inner_spacing;
|
||||
|
||||
// Rounding
|
||||
style.WindowRounding = window_rounding;
|
||||
style.ChildRounding = child_rounding;
|
||||
style.FrameRounding = frame_rounding;
|
||||
style.PopupRounding = popup_rounding;
|
||||
style.ScrollbarRounding = scrollbar_rounding;
|
||||
style.GrabRounding = grab_rounding;
|
||||
style.TabRounding = tab_rounding;
|
||||
|
||||
// Borders
|
||||
style.WindowBorderSize = window_border_size;
|
||||
style.ChildBorderSize = child_border_size;
|
||||
style.PopupBorderSize = popup_border_size;
|
||||
style.FrameBorderSize = frame_border_size;
|
||||
style.TabBorderSize = tab_border_size;
|
||||
|
||||
// Sizes
|
||||
style.IndentSpacing = indent_spacing;
|
||||
style.ScrollbarSize = scrollbar_size;
|
||||
style.GrabMinSize = grab_min_size;
|
||||
}
|
||||
|
||||
void ThemeProperties::LoadFromCurrent() {
|
||||
const ImGuiStyle& style = ImGui::GetStyle();
|
||||
|
||||
window_padding = style.WindowPadding;
|
||||
frame_padding = style.FramePadding;
|
||||
cell_padding = style.CellPadding;
|
||||
item_spacing = style.ItemSpacing;
|
||||
item_inner_spacing = style.ItemInnerSpacing;
|
||||
|
||||
window_rounding = style.WindowRounding;
|
||||
child_rounding = style.ChildRounding;
|
||||
frame_rounding = style.FrameRounding;
|
||||
popup_rounding = style.PopupRounding;
|
||||
scrollbar_rounding = style.ScrollbarRounding;
|
||||
grab_rounding = style.GrabRounding;
|
||||
tab_rounding = style.TabRounding;
|
||||
|
||||
window_border_size = style.WindowBorderSize;
|
||||
child_border_size = style.ChildBorderSize;
|
||||
popup_border_size = style.PopupBorderSize;
|
||||
frame_border_size = style.FrameBorderSize;
|
||||
tab_border_size = style.TabBorderSize;
|
||||
|
||||
indent_spacing = style.IndentSpacing;
|
||||
scrollbar_size = style.ScrollbarSize;
|
||||
grab_min_size = style.GrabMinSize;
|
||||
}
|
||||
|
||||
void ThemeProperties::Reset() {
|
||||
*this = ThemeProperties(); // Reset to default values
|
||||
}
|
||||
|
||||
std::string ThemeProperties::GenerateStyleCode() const {
|
||||
std::string code;
|
||||
|
||||
code += "// Theme Configuration - Generated by Layout Designer\n";
|
||||
code += "void ApplyTheme() {\n";
|
||||
code += " ImGuiStyle& style = ImGui::GetStyle();\n\n";
|
||||
|
||||
code += " // Padding\n";
|
||||
code += absl::StrFormat(" style.WindowPadding = ImVec2(%.1ff, %.1ff);\n",
|
||||
window_padding.x, window_padding.y);
|
||||
code += absl::StrFormat(" style.FramePadding = ImVec2(%.1ff, %.1ff);\n",
|
||||
frame_padding.x, frame_padding.y);
|
||||
code += absl::StrFormat(" style.CellPadding = ImVec2(%.1ff, %.1ff);\n",
|
||||
cell_padding.x, cell_padding.y);
|
||||
code += absl::StrFormat(" style.ItemSpacing = ImVec2(%.1ff, %.1ff);\n",
|
||||
item_spacing.x, item_spacing.y);
|
||||
code += absl::StrFormat(" style.ItemInnerSpacing = ImVec2(%.1ff, %.1ff);\n\n",
|
||||
item_inner_spacing.x, item_inner_spacing.y);
|
||||
|
||||
code += " // Rounding\n";
|
||||
code += absl::StrFormat(" style.WindowRounding = %.1ff;\n", window_rounding);
|
||||
code += absl::StrFormat(" style.ChildRounding = %.1ff;\n", child_rounding);
|
||||
code += absl::StrFormat(" style.FrameRounding = %.1ff;\n", frame_rounding);
|
||||
code += absl::StrFormat(" style.PopupRounding = %.1ff;\n", popup_rounding);
|
||||
code += absl::StrFormat(" style.ScrollbarRounding = %.1ff;\n", scrollbar_rounding);
|
||||
code += absl::StrFormat(" style.GrabRounding = %.1ff;\n", grab_rounding);
|
||||
code += absl::StrFormat(" style.TabRounding = %.1ff;\n\n", tab_rounding);
|
||||
|
||||
code += " // Borders\n";
|
||||
code += absl::StrFormat(" style.WindowBorderSize = %.1ff;\n", window_border_size);
|
||||
code += absl::StrFormat(" style.ChildBorderSize = %.1ff;\n", child_border_size);
|
||||
code += absl::StrFormat(" style.PopupBorderSize = %.1ff;\n", popup_border_size);
|
||||
code += absl::StrFormat(" style.FrameBorderSize = %.1ff;\n", frame_border_size);
|
||||
code += absl::StrFormat(" style.TabBorderSize = %.1ff;\n\n", tab_border_size);
|
||||
|
||||
code += " // Sizes\n";
|
||||
code += absl::StrFormat(" style.IndentSpacing = %.1ff;\n", indent_spacing);
|
||||
code += absl::StrFormat(" style.ScrollbarSize = %.1ff;\n", scrollbar_size);
|
||||
code += absl::StrFormat(" style.GrabMinSize = %.1ff;\n", grab_min_size);
|
||||
|
||||
code += "}\n";
|
||||
|
||||
return code;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// ThemePropertiesPanel Implementation
|
||||
// ============================================================================
|
||||
|
||||
bool ThemePropertiesPanel::Draw(ThemeProperties& properties) {
|
||||
bool modified = false;
|
||||
|
||||
ImGui::Text(ICON_MD_PALETTE " Theme Properties");
|
||||
ImGui::TextDisabled("Configure visual styling");
|
||||
ImGui::Separator();
|
||||
|
||||
if (ImGui::Button("Load from Current")) {
|
||||
properties.LoadFromCurrent();
|
||||
modified = true;
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Apply to App")) {
|
||||
properties.Apply();
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Reset")) {
|
||||
properties.Reset();
|
||||
modified = true;
|
||||
}
|
||||
|
||||
ImGui::Spacing();
|
||||
ImGui::Separator();
|
||||
|
||||
// Sections
|
||||
DrawPaddingSection(properties);
|
||||
DrawRoundingSection(properties);
|
||||
DrawBordersSection(properties);
|
||||
DrawSizesSection(properties);
|
||||
|
||||
return modified;
|
||||
}
|
||||
|
||||
void ThemePropertiesPanel::DrawPaddingSection(ThemeProperties& properties) {
|
||||
if (ImGui::CollapsingHeader(ICON_MD_SPACE_BAR " Padding",
|
||||
show_padding_ ? ImGuiTreeNodeFlags_DefaultOpen : 0)) {
|
||||
ImGui::SliderFloat2("Window Padding", &properties.window_padding.x, 0.0f, 20.0f, "%.0f");
|
||||
ImGui::SliderFloat2("Frame Padding", &properties.frame_padding.x, 0.0f, 20.0f, "%.0f");
|
||||
ImGui::SliderFloat2("Cell Padding", &properties.cell_padding.x, 0.0f, 20.0f, "%.0f");
|
||||
ImGui::SliderFloat2("Item Spacing", &properties.item_spacing.x, 0.0f, 20.0f, "%.0f");
|
||||
ImGui::SliderFloat2("Item Inner Spacing", &properties.item_inner_spacing.x, 0.0f, 20.0f, "%.0f");
|
||||
}
|
||||
}
|
||||
|
||||
void ThemePropertiesPanel::DrawRoundingSection(ThemeProperties& properties) {
|
||||
if (ImGui::CollapsingHeader(ICON_MD_ROUNDED_CORNER " Rounding",
|
||||
show_rounding_ ? ImGuiTreeNodeFlags_DefaultOpen : 0)) {
|
||||
ImGui::SliderFloat("Window Rounding", &properties.window_rounding, 0.0f, 12.0f, "%.0f");
|
||||
ImGui::SliderFloat("Child Rounding", &properties.child_rounding, 0.0f, 12.0f, "%.0f");
|
||||
ImGui::SliderFloat("Frame Rounding", &properties.frame_rounding, 0.0f, 12.0f, "%.0f");
|
||||
ImGui::SliderFloat("Popup Rounding", &properties.popup_rounding, 0.0f, 12.0f, "%.0f");
|
||||
ImGui::SliderFloat("Scrollbar Rounding", &properties.scrollbar_rounding, 0.0f, 12.0f, "%.0f");
|
||||
ImGui::SliderFloat("Grab Rounding", &properties.grab_rounding, 0.0f, 12.0f, "%.0f");
|
||||
ImGui::SliderFloat("Tab Rounding", &properties.tab_rounding, 0.0f, 12.0f, "%.0f");
|
||||
}
|
||||
}
|
||||
|
||||
void ThemePropertiesPanel::DrawBordersSection(ThemeProperties& properties) {
|
||||
if (ImGui::CollapsingHeader(ICON_MD_BORDER_ALL " Borders",
|
||||
show_borders_ ? ImGuiTreeNodeFlags_DefaultOpen : 0)) {
|
||||
ImGui::SliderFloat("Window Border", &properties.window_border_size, 0.0f, 2.0f, "%.0f");
|
||||
ImGui::SliderFloat("Child Border", &properties.child_border_size, 0.0f, 2.0f, "%.0f");
|
||||
ImGui::SliderFloat("Popup Border", &properties.popup_border_size, 0.0f, 2.0f, "%.0f");
|
||||
ImGui::SliderFloat("Frame Border", &properties.frame_border_size, 0.0f, 2.0f, "%.0f");
|
||||
ImGui::SliderFloat("Tab Border", &properties.tab_border_size, 0.0f, 2.0f, "%.0f");
|
||||
}
|
||||
}
|
||||
|
||||
void ThemePropertiesPanel::DrawSizesSection(ThemeProperties& properties) {
|
||||
if (ImGui::CollapsingHeader(ICON_MD_STRAIGHTEN " Sizes",
|
||||
show_sizes_ ? ImGuiTreeNodeFlags_DefaultOpen : 0)) {
|
||||
ImGui::SliderFloat("Indent Spacing", &properties.indent_spacing, 0.0f, 30.0f, "%.0f");
|
||||
ImGui::SliderFloat("Scrollbar Size", &properties.scrollbar_size, 1.0f, 20.0f, "%.0f");
|
||||
ImGui::SliderFloat("Grab Min Size", &properties.grab_min_size, 1.0f, 20.0f, "%.0f");
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace layout_designer
|
||||
} // namespace editor
|
||||
} // namespace yaze
|
||||
|
||||
@@ -1,92 +0,0 @@
|
||||
#ifndef YAZE_APP_EDITOR_LAYOUT_DESIGNER_THEME_PROPERTIES_H_
|
||||
#define YAZE_APP_EDITOR_LAYOUT_DESIGNER_THEME_PROPERTIES_H_
|
||||
|
||||
#include <string>
|
||||
#include "imgui/imgui.h"
|
||||
|
||||
namespace yaze {
|
||||
namespace editor {
|
||||
namespace layout_designer {
|
||||
|
||||
/**
|
||||
* @struct ThemeProperties
|
||||
* @brief Encapsulates ImGui style properties for visual design
|
||||
*
|
||||
* This allows layout designer to expose theming properties directly,
|
||||
* eliminating the need for complex Display Settings menus.
|
||||
*/
|
||||
struct ThemeProperties {
|
||||
// Padding
|
||||
ImVec2 window_padding = ImVec2(10, 10);
|
||||
ImVec2 frame_padding = ImVec2(10, 2);
|
||||
ImVec2 cell_padding = ImVec2(4, 5);
|
||||
ImVec2 item_spacing = ImVec2(10, 5);
|
||||
ImVec2 item_inner_spacing = ImVec2(5, 5);
|
||||
|
||||
// Rounding
|
||||
float window_rounding = 0.0f;
|
||||
float child_rounding = 0.0f;
|
||||
float frame_rounding = 5.0f;
|
||||
float popup_rounding = 0.0f;
|
||||
float scrollbar_rounding = 5.0f;
|
||||
float grab_rounding = 0.0f;
|
||||
float tab_rounding = 0.0f;
|
||||
|
||||
// Borders
|
||||
float window_border_size = 0.0f;
|
||||
float child_border_size = 1.0f;
|
||||
float popup_border_size = 1.0f;
|
||||
float frame_border_size = 0.0f;
|
||||
float tab_border_size = 0.0f;
|
||||
|
||||
// Sizes
|
||||
float indent_spacing = 20.0f;
|
||||
float scrollbar_size = 14.0f;
|
||||
float grab_min_size = 15.0f;
|
||||
|
||||
// Apply these properties to ImGui style
|
||||
void Apply() const;
|
||||
|
||||
// Load from current ImGui style
|
||||
void LoadFromCurrent();
|
||||
|
||||
// Reset to defaults
|
||||
void Reset();
|
||||
|
||||
// Export as code
|
||||
std::string GenerateStyleCode() const;
|
||||
};
|
||||
|
||||
/**
|
||||
* @class ThemePropertiesPanel
|
||||
* @brief UI panel for editing theme properties in the layout designer
|
||||
*/
|
||||
class ThemePropertiesPanel {
|
||||
public:
|
||||
ThemePropertiesPanel() = default;
|
||||
|
||||
/**
|
||||
* @brief Draw the theme properties editor
|
||||
* @param properties The theme properties to edit
|
||||
* @return true if any property was modified
|
||||
*/
|
||||
bool Draw(ThemeProperties& properties);
|
||||
|
||||
private:
|
||||
void DrawPaddingSection(ThemeProperties& properties);
|
||||
void DrawRoundingSection(ThemeProperties& properties);
|
||||
void DrawBordersSection(ThemeProperties& properties);
|
||||
void DrawSizesSection(ThemeProperties& properties);
|
||||
|
||||
bool show_padding_ = true;
|
||||
bool show_rounding_ = true;
|
||||
bool show_borders_ = true;
|
||||
bool show_sizes_ = true;
|
||||
};
|
||||
|
||||
} // namespace layout_designer
|
||||
} // namespace editor
|
||||
} // namespace yaze
|
||||
|
||||
#endif // YAZE_APP_EDITOR_LAYOUT_DESIGNER_THEME_PROPERTIES_H_
|
||||
|
||||
@@ -1,387 +0,0 @@
|
||||
#include "app/editor/layout_designer/widget_code_generator.h"
|
||||
|
||||
#include "absl/strings/str_format.h"
|
||||
#include "absl/strings/str_replace.h"
|
||||
|
||||
namespace yaze {
|
||||
namespace editor {
|
||||
namespace layout_designer {
|
||||
|
||||
std::string WidgetCodeGenerator::GeneratePanelDrawMethod(const PanelDesign& design) {
|
||||
std::string code;
|
||||
|
||||
code += absl::StrFormat("// Generated by YAZE Layout Designer\n");
|
||||
code += absl::StrFormat("// Panel: %s\n", design.panel_name);
|
||||
code += absl::StrFormat("// Generated: <timestamp>\n\n");
|
||||
|
||||
code += absl::StrFormat("void %sPanel::Draw(bool* p_open) {\n", design.panel_id);
|
||||
|
||||
// Generate code for all root widgets
|
||||
for (const auto& widget : design.widgets) {
|
||||
code += GenerateWidgetCode(*widget, 1);
|
||||
}
|
||||
|
||||
code += "}\n";
|
||||
|
||||
return code;
|
||||
}
|
||||
|
||||
std::string WidgetCodeGenerator::GenerateWidgetCode(const WidgetDefinition& widget,
|
||||
int indent_level) {
|
||||
std::string code;
|
||||
std::string indent = GetIndent(indent_level);
|
||||
|
||||
// Add comment with widget ID
|
||||
code += indent + absl::StrFormat("// Widget: %s\n", widget.id);
|
||||
|
||||
// Generate code based on widget type
|
||||
switch (widget.type) {
|
||||
case WidgetType::Text:
|
||||
case WidgetType::TextWrapped:
|
||||
case WidgetType::TextColored:
|
||||
case WidgetType::BulletText:
|
||||
code += GenerateTextCode(widget, indent_level);
|
||||
break;
|
||||
|
||||
case WidgetType::Button:
|
||||
case WidgetType::SmallButton:
|
||||
code += GenerateButtonCode(widget, indent_level);
|
||||
break;
|
||||
|
||||
case WidgetType::Checkbox:
|
||||
case WidgetType::RadioButton:
|
||||
case WidgetType::InputText:
|
||||
case WidgetType::InputInt:
|
||||
case WidgetType::InputFloat:
|
||||
case WidgetType::SliderInt:
|
||||
case WidgetType::SliderFloat:
|
||||
code += GenerateInputCode(widget, indent_level);
|
||||
break;
|
||||
|
||||
case WidgetType::BeginTable:
|
||||
case WidgetType::EndTable:
|
||||
case WidgetType::TableNextRow:
|
||||
case WidgetType::TableNextColumn:
|
||||
code += GenerateTableCode(widget, indent_level);
|
||||
break;
|
||||
|
||||
case WidgetType::Canvas:
|
||||
code += GenerateCanvasCode(widget, indent_level);
|
||||
break;
|
||||
|
||||
case WidgetType::Separator:
|
||||
code += indent + "ImGui::Separator();\n";
|
||||
break;
|
||||
|
||||
case WidgetType::SameLine:
|
||||
code += indent + "ImGui::SameLine();\n";
|
||||
break;
|
||||
|
||||
case WidgetType::Spacing:
|
||||
code += indent + "ImGui::Spacing();\n";
|
||||
break;
|
||||
|
||||
case WidgetType::NewLine:
|
||||
code += indent + "ImGui::NewLine();\n";
|
||||
break;
|
||||
|
||||
case WidgetType::BeginGroup:
|
||||
case WidgetType::BeginChild:
|
||||
case WidgetType::CollapsingHeader:
|
||||
case WidgetType::TreeNode:
|
||||
case WidgetType::TabBar:
|
||||
code += GenerateContainerCode(widget, indent_level);
|
||||
break;
|
||||
|
||||
default:
|
||||
code += indent + absl::StrFormat("// TODO: Generate code for %s\n",
|
||||
GetWidgetTypeName(widget.type));
|
||||
break;
|
||||
}
|
||||
|
||||
// Add same line directive if needed
|
||||
if (widget.same_line) {
|
||||
code += indent + "ImGui::SameLine();\n";
|
||||
}
|
||||
|
||||
code += "\n";
|
||||
return code;
|
||||
}
|
||||
|
||||
std::string WidgetCodeGenerator::GenerateMemberVariables(const PanelDesign& design) {
|
||||
std::string code;
|
||||
code += " // Widget state variables\n";
|
||||
|
||||
for (const auto& widget : design.widgets) {
|
||||
std::string var_name = GetVariableName(*widget);
|
||||
|
||||
switch (widget->type) {
|
||||
case WidgetType::Checkbox: {
|
||||
code += absl::StrFormat(" bool %s = false;\n", var_name);
|
||||
break;
|
||||
}
|
||||
case WidgetType::InputText: {
|
||||
auto* buffer_size_prop = const_cast<WidgetDefinition&>(*widget)
|
||||
.GetProperty("buffer_size");
|
||||
int size = buffer_size_prop ? buffer_size_prop->int_value : 256;
|
||||
code += absl::StrFormat(" char %s[%d] = {};\n", var_name, size);
|
||||
break;
|
||||
}
|
||||
case WidgetType::InputInt:
|
||||
case WidgetType::SliderInt: {
|
||||
code += absl::StrFormat(" int %s = 0;\n", var_name);
|
||||
break;
|
||||
}
|
||||
case WidgetType::InputFloat:
|
||||
case WidgetType::SliderFloat: {
|
||||
code += absl::StrFormat(" float %s = 0.0f;\n", var_name);
|
||||
break;
|
||||
}
|
||||
case WidgetType::ColorEdit:
|
||||
case WidgetType::ColorPicker: {
|
||||
code += absl::StrFormat(" ImVec4 %s = ImVec4(1,1,1,1);\n", var_name);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return code;
|
||||
}
|
||||
|
||||
std::string WidgetCodeGenerator::GenerateInitializationCode(const PanelDesign& design) {
|
||||
std::string code;
|
||||
|
||||
// Generate initialization for input text buffers, etc.
|
||||
for (const auto& widget : design.widgets) {
|
||||
if (widget->type == WidgetType::InputText) {
|
||||
auto* hint_prop = const_cast<WidgetDefinition&>(*widget).GetProperty("hint");
|
||||
if (hint_prop && !hint_prop->string_value.empty()) {
|
||||
std::string var_name = GetVariableName(*widget);
|
||||
code += absl::StrFormat(" // Initialize %s hint\n", var_name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return code;
|
||||
}
|
||||
|
||||
// Private helper methods
|
||||
|
||||
std::string WidgetCodeGenerator::GetIndent(int level) {
|
||||
return std::string(level * 2, ' ');
|
||||
}
|
||||
|
||||
std::string WidgetCodeGenerator::EscapeString(const std::string& str) {
|
||||
return absl::StrReplaceAll(str, {{"\\", "\\\\"}, {"\"", "\\\""}});
|
||||
}
|
||||
|
||||
std::string WidgetCodeGenerator::GenerateButtonCode(const WidgetDefinition& widget,
|
||||
int indent) {
|
||||
std::string code;
|
||||
std::string ind = GetIndent(indent);
|
||||
|
||||
auto* label_prop = const_cast<WidgetDefinition&>(widget).GetProperty("label");
|
||||
std::string label = label_prop ? label_prop->string_value : "Button";
|
||||
|
||||
auto* size_prop = const_cast<WidgetDefinition&>(widget).GetProperty("size");
|
||||
ImVec2 size = size_prop ? size_prop->vec2_value : ImVec2(0, 0);
|
||||
|
||||
if (widget.type == WidgetType::SmallButton) {
|
||||
code += ind + absl::StrFormat("if (ImGui::SmallButton(\"%s\")) {\n",
|
||||
EscapeString(label));
|
||||
} else if (size.x != 0 || size.y != 0) {
|
||||
code += ind + absl::StrFormat(
|
||||
"if (ImGui::Button(\"%s\", ImVec2(%.1f, %.1f))) {\n",
|
||||
EscapeString(label), size.x, size.y);
|
||||
} else {
|
||||
code += ind + absl::StrFormat("if (ImGui::Button(\"%s\")) {\n",
|
||||
EscapeString(label));
|
||||
}
|
||||
|
||||
code += ind + " // TODO: Button callback\n";
|
||||
if (!widget.callback_name.empty()) {
|
||||
code += ind + absl::StrFormat(" %s();\n", widget.callback_name);
|
||||
}
|
||||
code += ind + "}\n";
|
||||
|
||||
if (!widget.tooltip.empty()) {
|
||||
code += ind + "if (ImGui::IsItemHovered()) {\n";
|
||||
code += ind + absl::StrFormat(" ImGui::SetTooltip(\"%s\");\n",
|
||||
EscapeString(widget.tooltip));
|
||||
code += ind + "}\n";
|
||||
}
|
||||
|
||||
return code;
|
||||
}
|
||||
|
||||
std::string WidgetCodeGenerator::GenerateTextCode(const WidgetDefinition& widget,
|
||||
int indent) {
|
||||
std::string code;
|
||||
std::string ind = GetIndent(indent);
|
||||
|
||||
auto* text_prop = const_cast<WidgetDefinition&>(widget).GetProperty("text");
|
||||
std::string text = text_prop ? text_prop->string_value : "Text";
|
||||
|
||||
switch (widget.type) {
|
||||
case WidgetType::Text:
|
||||
code += ind + absl::StrFormat("ImGui::Text(\"%s\");\n", EscapeString(text));
|
||||
break;
|
||||
case WidgetType::TextWrapped:
|
||||
code += ind + absl::StrFormat("ImGui::TextWrapped(\"%s\");\n", EscapeString(text));
|
||||
break;
|
||||
case WidgetType::BulletText:
|
||||
code += ind + absl::StrFormat("ImGui::BulletText(\"%s\");\n", EscapeString(text));
|
||||
break;
|
||||
case WidgetType::TextColored: {
|
||||
auto* color_prop = const_cast<WidgetDefinition&>(widget).GetProperty("color");
|
||||
ImVec4 color = color_prop ? color_prop->color_value : ImVec4(1, 1, 1, 1);
|
||||
code += ind + absl::StrFormat(
|
||||
"ImGui::TextColored(ImVec4(%.2f, %.2f, %.2f, %.2f), \"%s\");\n",
|
||||
color.x, color.y, color.z, color.w, EscapeString(text));
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return code;
|
||||
}
|
||||
|
||||
std::string WidgetCodeGenerator::GenerateInputCode(const WidgetDefinition& widget,
|
||||
int indent) {
|
||||
std::string code;
|
||||
std::string ind = GetIndent(indent);
|
||||
std::string var_name = GetVariableName(widget);
|
||||
|
||||
auto* label_prop = const_cast<WidgetDefinition&>(widget).GetProperty("label");
|
||||
std::string label = label_prop ? label_prop->string_value : "Input";
|
||||
|
||||
switch (widget.type) {
|
||||
case WidgetType::Checkbox:
|
||||
code += ind + absl::StrFormat("ImGui::Checkbox(\"%s\", &%s);\n",
|
||||
EscapeString(label), var_name);
|
||||
break;
|
||||
|
||||
case WidgetType::InputText: {
|
||||
auto* hint_prop = const_cast<WidgetDefinition&>(widget).GetProperty("hint");
|
||||
if (hint_prop && !hint_prop->string_value.empty()) {
|
||||
code += ind + absl::StrFormat(
|
||||
"ImGui::InputTextWithHint(\"%s\", \"%s\", %s, sizeof(%s));\n",
|
||||
EscapeString(label), EscapeString(hint_prop->string_value),
|
||||
var_name, var_name);
|
||||
} else {
|
||||
code += ind + absl::StrFormat("ImGui::InputText(\"%s\", %s, sizeof(%s));\n",
|
||||
EscapeString(label), var_name, var_name);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case WidgetType::InputInt:
|
||||
code += ind + absl::StrFormat("ImGui::InputInt(\"%s\", &%s);\n",
|
||||
EscapeString(label), var_name);
|
||||
break;
|
||||
|
||||
case WidgetType::SliderInt: {
|
||||
auto* min_prop = const_cast<WidgetDefinition&>(widget).GetProperty("min");
|
||||
auto* max_prop = const_cast<WidgetDefinition&>(widget).GetProperty("max");
|
||||
int min_val = min_prop ? min_prop->int_value : 0;
|
||||
int max_val = max_prop ? max_prop->int_value : 100;
|
||||
code += ind + absl::StrFormat("ImGui::SliderInt(\"%s\", &%s, %d, %d);\n",
|
||||
EscapeString(label), var_name, min_val, max_val);
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return code;
|
||||
}
|
||||
|
||||
std::string WidgetCodeGenerator::GenerateTableCode(const WidgetDefinition& widget,
|
||||
int indent) {
|
||||
std::string code;
|
||||
std::string ind = GetIndent(indent);
|
||||
|
||||
switch (widget.type) {
|
||||
case WidgetType::BeginTable: {
|
||||
auto* id_prop = const_cast<WidgetDefinition&>(widget).GetProperty("id");
|
||||
auto* columns_prop = const_cast<WidgetDefinition&>(widget).GetProperty("columns");
|
||||
std::string id = id_prop ? id_prop->string_value : "table";
|
||||
int columns = columns_prop ? columns_prop->int_value : 2;
|
||||
|
||||
code += ind + absl::StrFormat("if (ImGui::BeginTable(\"%s\", %d)) {\n",
|
||||
id, columns);
|
||||
|
||||
// Generate children
|
||||
for (const auto& child : widget.children) {
|
||||
code += GenerateWidgetCode(*child, indent + 1);
|
||||
}
|
||||
|
||||
code += ind + " ImGui::EndTable();\n";
|
||||
code += ind + "}\n";
|
||||
break;
|
||||
}
|
||||
|
||||
case WidgetType::TableNextRow:
|
||||
code += ind + "ImGui::TableNextRow();\n";
|
||||
break;
|
||||
|
||||
case WidgetType::TableNextColumn:
|
||||
code += ind + "ImGui::TableNextColumn();\n";
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return code;
|
||||
}
|
||||
|
||||
std::string WidgetCodeGenerator::GenerateCanvasCode(const WidgetDefinition& widget,
|
||||
int indent) {
|
||||
std::string code;
|
||||
std::string ind = GetIndent(indent);
|
||||
|
||||
auto* size_prop = const_cast<WidgetDefinition&>(widget).GetProperty("size");
|
||||
ImVec2 size = size_prop ? size_prop->vec2_value : ImVec2(300, 200);
|
||||
|
||||
code += ind + absl::StrFormat("// Custom canvas - size: %.0fx%.0f\n",
|
||||
size.x, size.y);
|
||||
code += ind + "ImVec2 canvas_pos = ImGui::GetCursorScreenPos();\n";
|
||||
code += ind + absl::StrFormat("ImVec2 canvas_size = ImVec2(%.0ff, %.0ff);\n",
|
||||
size.x, size.y);
|
||||
code += ind + "ImDrawList* draw_list = ImGui::GetWindowDrawList();\n";
|
||||
code += ind + "// TODO: Add custom drawing code here\n";
|
||||
code += ind + "ImGui::Dummy(canvas_size);\n";
|
||||
|
||||
return code;
|
||||
}
|
||||
|
||||
std::string WidgetCodeGenerator::GenerateContainerCode(const WidgetDefinition& widget,
|
||||
int indent) {
|
||||
std::string code;
|
||||
std::string ind = GetIndent(indent);
|
||||
|
||||
// TODO: Implement container code generation
|
||||
code += ind + absl::StrFormat("// TODO: Container widget: %s\n",
|
||||
GetWidgetTypeName(widget.type));
|
||||
|
||||
return code;
|
||||
}
|
||||
|
||||
std::string WidgetCodeGenerator::GetVariableName(const WidgetDefinition& widget) {
|
||||
// Convert widget ID to valid C++ variable name
|
||||
std::string var_name = widget.id;
|
||||
std::replace(var_name.begin(), var_name.end(), '.', '_');
|
||||
std::replace(var_name.begin(), var_name.end(), '-', '_');
|
||||
var_name += "_";
|
||||
return var_name;
|
||||
}
|
||||
|
||||
} // namespace layout_designer
|
||||
} // namespace editor
|
||||
} // namespace yaze
|
||||
|
||||
@@ -1,68 +0,0 @@
|
||||
#ifndef YAZE_APP_EDITOR_LAYOUT_DESIGNER_WIDGET_CODE_GENERATOR_H_
|
||||
#define YAZE_APP_EDITOR_LAYOUT_DESIGNER_WIDGET_CODE_GENERATOR_H_
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "app/editor/layout_designer/widget_definition.h"
|
||||
|
||||
namespace yaze {
|
||||
namespace editor {
|
||||
namespace layout_designer {
|
||||
|
||||
/**
|
||||
* @class WidgetCodeGenerator
|
||||
* @brief Generates C++ ImGui code from widget definitions
|
||||
*/
|
||||
class WidgetCodeGenerator {
|
||||
public:
|
||||
/**
|
||||
* @brief Generate complete panel Draw() method code
|
||||
* @param design The panel design
|
||||
* @return C++ code string
|
||||
*/
|
||||
static std::string GeneratePanelDrawMethod(const PanelDesign& design);
|
||||
|
||||
/**
|
||||
* @brief Generate code for a single widget
|
||||
* @param widget The widget definition
|
||||
* @param indent_level Indentation level for formatting
|
||||
* @return C++ code string
|
||||
*/
|
||||
static std::string GenerateWidgetCode(const WidgetDefinition& widget,
|
||||
int indent_level = 0);
|
||||
|
||||
/**
|
||||
* @brief Generate member variable declarations for panel
|
||||
* @param design The panel design
|
||||
* @return C++ code for private members
|
||||
*/
|
||||
static std::string GenerateMemberVariables(const PanelDesign& design);
|
||||
|
||||
/**
|
||||
* @brief Generate initialization code for panel constructor
|
||||
* @param design The panel design
|
||||
* @return C++ initialization code
|
||||
*/
|
||||
static std::string GenerateInitializationCode(const PanelDesign& design);
|
||||
|
||||
private:
|
||||
static std::string GetIndent(int level);
|
||||
static std::string EscapeString(const std::string& str);
|
||||
static std::string GenerateButtonCode(const WidgetDefinition& widget, int indent);
|
||||
static std::string GenerateTextCode(const WidgetDefinition& widget, int indent);
|
||||
static std::string GenerateInputCode(const WidgetDefinition& widget, int indent);
|
||||
static std::string GenerateTableCode(const WidgetDefinition& widget, int indent);
|
||||
static std::string GenerateCanvasCode(const WidgetDefinition& widget, int indent);
|
||||
static std::string GenerateContainerCode(const WidgetDefinition& widget, int indent);
|
||||
|
||||
// Get variable name for widget (e.g., button_clicked_, input_text_buffer_)
|
||||
static std::string GetVariableName(const WidgetDefinition& widget);
|
||||
};
|
||||
|
||||
} // namespace layout_designer
|
||||
} // namespace editor
|
||||
} // namespace yaze
|
||||
|
||||
#endif // YAZE_APP_EDITOR_LAYOUT_DESIGNER_WIDGET_CODE_GENERATOR_H_
|
||||
|
||||
@@ -1,421 +0,0 @@
|
||||
#include "app/editor/layout_designer/widget_definition.h"
|
||||
|
||||
#include <chrono>
|
||||
|
||||
#include "app/gui/core/icons.h"
|
||||
|
||||
namespace yaze {
|
||||
namespace editor {
|
||||
namespace layout_designer {
|
||||
|
||||
// ============================================================================
|
||||
// WidgetProperty Implementation
|
||||
// ============================================================================
|
||||
|
||||
void WidgetDefinition::AddProperty(const std::string& name,
|
||||
WidgetProperty::Type type) {
|
||||
WidgetProperty prop;
|
||||
prop.name = name;
|
||||
prop.type = type;
|
||||
properties.push_back(prop);
|
||||
}
|
||||
|
||||
WidgetProperty* WidgetDefinition::GetProperty(const std::string& name) {
|
||||
for (auto& prop : properties) {
|
||||
if (prop.name == name) {
|
||||
return ∝
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void WidgetDefinition::AddChild(std::unique_ptr<WidgetDefinition> child) {
|
||||
if (CanHaveChildren()) {
|
||||
children.push_back(std::move(child));
|
||||
}
|
||||
}
|
||||
|
||||
bool WidgetDefinition::IsContainer() const {
|
||||
return IsContainerWidget(type);
|
||||
}
|
||||
|
||||
bool WidgetDefinition::CanHaveChildren() const {
|
||||
return IsContainerWidget(type);
|
||||
}
|
||||
|
||||
bool WidgetDefinition::RequiresEnd() const {
|
||||
return RequiresEndCall(type);
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// PanelDesign Implementation
|
||||
// ============================================================================
|
||||
|
||||
void PanelDesign::AddWidget(std::unique_ptr<WidgetDefinition> widget) {
|
||||
widgets.push_back(std::move(widget));
|
||||
Touch();
|
||||
}
|
||||
|
||||
WidgetDefinition* PanelDesign::FindWidget(const std::string& id) {
|
||||
// Recursive search through widget tree
|
||||
std::function<WidgetDefinition*(WidgetDefinition*)> search =
|
||||
[&](WidgetDefinition* widget) -> WidgetDefinition* {
|
||||
if (widget->id == id) {
|
||||
return widget;
|
||||
}
|
||||
for (auto& child : widget->children) {
|
||||
if (auto* found = search(child.get())) {
|
||||
return found;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
};
|
||||
|
||||
for (auto& widget : widgets) {
|
||||
if (auto* found = search(widget.get())) {
|
||||
return found;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::vector<WidgetDefinition*> PanelDesign::GetAllWidgets() {
|
||||
std::vector<WidgetDefinition*> result;
|
||||
|
||||
std::function<void(WidgetDefinition*)> collect =
|
||||
[&](WidgetDefinition* widget) {
|
||||
result.push_back(widget);
|
||||
for (auto& child : widget->children) {
|
||||
collect(child.get());
|
||||
}
|
||||
};
|
||||
|
||||
for (auto& widget : widgets) {
|
||||
collect(widget.get());
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool PanelDesign::Validate(std::string* error_message) const {
|
||||
if (panel_id.empty()) {
|
||||
if (error_message) *error_message = "Panel ID cannot be empty";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Validate widget IDs are unique
|
||||
std::set<std::string> ids;
|
||||
for (const auto& widget : widgets) {
|
||||
if (ids.count(widget->id)) {
|
||||
if (error_message) *error_message = "Duplicate widget ID: " + widget->id;
|
||||
return false;
|
||||
}
|
||||
ids.insert(widget->id);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void PanelDesign::Touch() {
|
||||
auto now = std::chrono::system_clock::now();
|
||||
modified_timestamp = std::chrono::duration_cast<std::chrono::seconds>(
|
||||
now.time_since_epoch()).count();
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Utility Functions
|
||||
// ============================================================================
|
||||
|
||||
const char* GetWidgetTypeName(WidgetType type) {
|
||||
switch (type) {
|
||||
case WidgetType::Text: return "Text";
|
||||
case WidgetType::TextWrapped: return "Text (Wrapped)";
|
||||
case WidgetType::TextColored: return "Colored Text";
|
||||
case WidgetType::Button: return "Button";
|
||||
case WidgetType::SmallButton: return "Small Button";
|
||||
case WidgetType::Checkbox: return "Checkbox";
|
||||
case WidgetType::RadioButton: return "Radio Button";
|
||||
case WidgetType::InputText: return "Text Input";
|
||||
case WidgetType::InputInt: return "Integer Input";
|
||||
case WidgetType::InputFloat: return "Float Input";
|
||||
case WidgetType::SliderInt: return "Integer Slider";
|
||||
case WidgetType::SliderFloat: return "Float Slider";
|
||||
case WidgetType::ColorEdit: return "Color Editor";
|
||||
case WidgetType::ColorPicker: return "Color Picker";
|
||||
case WidgetType::Separator: return "Separator";
|
||||
case WidgetType::SameLine: return "Same Line";
|
||||
case WidgetType::Spacing: return "Spacing";
|
||||
case WidgetType::Dummy: return "Dummy Space";
|
||||
case WidgetType::NewLine: return "New Line";
|
||||
case WidgetType::Indent: return "Indent";
|
||||
case WidgetType::Unindent: return "Unindent";
|
||||
case WidgetType::BeginGroup: return "Group (Begin)";
|
||||
case WidgetType::EndGroup: return "Group (End)";
|
||||
case WidgetType::BeginChild: return "Child Window (Begin)";
|
||||
case WidgetType::EndChild: return "Child Window (End)";
|
||||
case WidgetType::CollapsingHeader: return "Collapsing Header";
|
||||
case WidgetType::TreeNode: return "Tree Node";
|
||||
case WidgetType::TabBar: return "Tab Bar";
|
||||
case WidgetType::TabItem: return "Tab Item";
|
||||
case WidgetType::BeginTable: return "Table (Begin)";
|
||||
case WidgetType::EndTable: return "Table (End)";
|
||||
case WidgetType::TableNextRow: return "Table Next Row";
|
||||
case WidgetType::TableNextColumn: return "Table Next Column";
|
||||
case WidgetType::TableSetupColumn: return "Table Setup Column";
|
||||
case WidgetType::Canvas: return "Custom Canvas";
|
||||
case WidgetType::Image: return "Image";
|
||||
case WidgetType::ImageButton: return "Image Button";
|
||||
case WidgetType::ProgressBar: return "Progress Bar";
|
||||
case WidgetType::BulletText: return "Bullet Text";
|
||||
case WidgetType::BeginMenu: return "Menu (Begin)";
|
||||
case WidgetType::EndMenu: return "Menu (End)";
|
||||
case WidgetType::MenuItem: return "Menu Item";
|
||||
case WidgetType::BeginCombo: return "Combo (Begin)";
|
||||
case WidgetType::EndCombo: return "Combo (End)";
|
||||
case WidgetType::Selectable: return "Selectable";
|
||||
case WidgetType::ListBox: return "List Box";
|
||||
default: return "Unknown";
|
||||
}
|
||||
}
|
||||
|
||||
const char* GetWidgetTypeIcon(WidgetType type) {
|
||||
switch (type) {
|
||||
case WidgetType::Text:
|
||||
case WidgetType::TextWrapped:
|
||||
case WidgetType::TextColored:
|
||||
case WidgetType::BulletText:
|
||||
return ICON_MD_TEXT_FIELDS;
|
||||
|
||||
case WidgetType::Button:
|
||||
case WidgetType::SmallButton:
|
||||
case WidgetType::ImageButton:
|
||||
return ICON_MD_SMART_BUTTON;
|
||||
|
||||
case WidgetType::Checkbox:
|
||||
return ICON_MD_CHECK_BOX;
|
||||
|
||||
case WidgetType::RadioButton:
|
||||
return ICON_MD_RADIO_BUTTON_CHECKED;
|
||||
|
||||
case WidgetType::InputText:
|
||||
case WidgetType::InputInt:
|
||||
case WidgetType::InputFloat:
|
||||
return ICON_MD_INPUT;
|
||||
|
||||
case WidgetType::SliderInt:
|
||||
case WidgetType::SliderFloat:
|
||||
return ICON_MD_TUNE;
|
||||
|
||||
case WidgetType::ColorEdit:
|
||||
case WidgetType::ColorPicker:
|
||||
return ICON_MD_PALETTE;
|
||||
|
||||
case WidgetType::Separator:
|
||||
return ICON_MD_HORIZONTAL_RULE;
|
||||
|
||||
case WidgetType::BeginTable:
|
||||
case WidgetType::EndTable:
|
||||
return ICON_MD_TABLE_CHART;
|
||||
|
||||
case WidgetType::CollapsingHeader:
|
||||
case WidgetType::TreeNode:
|
||||
return ICON_MD_ACCOUNT_TREE;
|
||||
|
||||
case WidgetType::TabBar:
|
||||
case WidgetType::TabItem:
|
||||
return ICON_MD_TAB;
|
||||
|
||||
case WidgetType::Canvas:
|
||||
return ICON_MD_DRAW;
|
||||
|
||||
case WidgetType::Image:
|
||||
return ICON_MD_IMAGE;
|
||||
|
||||
case WidgetType::ProgressBar:
|
||||
return ICON_MD_LINEAR_SCALE;
|
||||
|
||||
case WidgetType::BeginMenu:
|
||||
case WidgetType::EndMenu:
|
||||
case WidgetType::MenuItem:
|
||||
return ICON_MD_MENU;
|
||||
|
||||
case WidgetType::BeginCombo:
|
||||
case WidgetType::EndCombo:
|
||||
case WidgetType::ListBox:
|
||||
return ICON_MD_ARROW_DROP_DOWN;
|
||||
|
||||
case WidgetType::Selectable:
|
||||
return ICON_MD_CHECK_CIRCLE;
|
||||
|
||||
default:
|
||||
return ICON_MD_WIDGETS;
|
||||
}
|
||||
}
|
||||
|
||||
bool IsContainerWidget(WidgetType type) {
|
||||
switch (type) {
|
||||
case WidgetType::BeginGroup:
|
||||
case WidgetType::BeginChild:
|
||||
case WidgetType::CollapsingHeader:
|
||||
case WidgetType::TreeNode:
|
||||
case WidgetType::TabBar:
|
||||
case WidgetType::TabItem:
|
||||
case WidgetType::BeginTable:
|
||||
case WidgetType::BeginMenu:
|
||||
case WidgetType::BeginCombo:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool RequiresEndCall(WidgetType type) {
|
||||
switch (type) {
|
||||
case WidgetType::BeginGroup:
|
||||
case WidgetType::BeginChild:
|
||||
case WidgetType::TreeNode:
|
||||
case WidgetType::TabBar:
|
||||
case WidgetType::BeginTable:
|
||||
case WidgetType::BeginMenu:
|
||||
case WidgetType::BeginCombo:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<WidgetProperty> GetDefaultProperties(WidgetType type) {
|
||||
std::vector<WidgetProperty> props;
|
||||
|
||||
switch (type) {
|
||||
case WidgetType::Text:
|
||||
case WidgetType::TextWrapped:
|
||||
case WidgetType::BulletText: {
|
||||
WidgetProperty prop;
|
||||
prop.name = "text";
|
||||
prop.type = WidgetProperty::Type::String;
|
||||
prop.string_value = "Sample Text";
|
||||
props.push_back(prop);
|
||||
break;
|
||||
}
|
||||
|
||||
case WidgetType::Button:
|
||||
case WidgetType::SmallButton: {
|
||||
WidgetProperty label;
|
||||
label.name = "label";
|
||||
label.type = WidgetProperty::Type::String;
|
||||
label.string_value = "Button";
|
||||
props.push_back(label);
|
||||
|
||||
WidgetProperty size;
|
||||
size.name = "size";
|
||||
size.type = WidgetProperty::Type::Vec2;
|
||||
size.vec2_value = ImVec2(0, 0); // Auto size
|
||||
props.push_back(size);
|
||||
break;
|
||||
}
|
||||
|
||||
case WidgetType::Checkbox: {
|
||||
WidgetProperty label;
|
||||
label.name = "label";
|
||||
label.type = WidgetProperty::Type::String;
|
||||
label.string_value = "Checkbox";
|
||||
props.push_back(label);
|
||||
|
||||
WidgetProperty checked;
|
||||
checked.name = "checked";
|
||||
checked.type = WidgetProperty::Type::Bool;
|
||||
checked.bool_value = false;
|
||||
props.push_back(checked);
|
||||
break;
|
||||
}
|
||||
|
||||
case WidgetType::InputText: {
|
||||
WidgetProperty label;
|
||||
label.name = "label";
|
||||
label.type = WidgetProperty::Type::String;
|
||||
label.string_value = "Input";
|
||||
props.push_back(label);
|
||||
|
||||
WidgetProperty hint;
|
||||
hint.name = "hint";
|
||||
hint.type = WidgetProperty::Type::String;
|
||||
hint.string_value = "Enter text...";
|
||||
props.push_back(hint);
|
||||
|
||||
WidgetProperty buffer_size;
|
||||
buffer_size.name = "buffer_size";
|
||||
buffer_size.type = WidgetProperty::Type::Int;
|
||||
buffer_size.int_value = 256;
|
||||
props.push_back(buffer_size);
|
||||
break;
|
||||
}
|
||||
|
||||
case WidgetType::SliderInt: {
|
||||
WidgetProperty label;
|
||||
label.name = "label";
|
||||
label.type = WidgetProperty::Type::String;
|
||||
label.string_value = "Slider";
|
||||
props.push_back(label);
|
||||
|
||||
WidgetProperty min_val;
|
||||
min_val.name = "min";
|
||||
min_val.type = WidgetProperty::Type::Int;
|
||||
min_val.int_value = 0;
|
||||
props.push_back(min_val);
|
||||
|
||||
WidgetProperty max_val;
|
||||
max_val.name = "max";
|
||||
max_val.type = WidgetProperty::Type::Int;
|
||||
max_val.int_value = 100;
|
||||
props.push_back(max_val);
|
||||
break;
|
||||
}
|
||||
|
||||
case WidgetType::BeginTable: {
|
||||
WidgetProperty id;
|
||||
id.name = "id";
|
||||
id.type = WidgetProperty::Type::String;
|
||||
id.string_value = "table";
|
||||
props.push_back(id);
|
||||
|
||||
WidgetProperty columns;
|
||||
columns.name = "columns";
|
||||
columns.type = WidgetProperty::Type::Int;
|
||||
columns.int_value = 2;
|
||||
props.push_back(columns);
|
||||
|
||||
WidgetProperty flags;
|
||||
flags.name = "flags";
|
||||
flags.type = WidgetProperty::Type::Flags;
|
||||
flags.flags_value = 0; // ImGuiTableFlags
|
||||
props.push_back(flags);
|
||||
break;
|
||||
}
|
||||
|
||||
case WidgetType::Canvas: {
|
||||
WidgetProperty size;
|
||||
size.name = "size";
|
||||
size.type = WidgetProperty::Type::Vec2;
|
||||
size.vec2_value = ImVec2(300, 200);
|
||||
props.push_back(size);
|
||||
|
||||
WidgetProperty bg_color;
|
||||
bg_color.name = "background";
|
||||
bg_color.type = WidgetProperty::Type::Color;
|
||||
bg_color.color_value = ImVec4(0.2f, 0.2f, 0.2f, 1.0f);
|
||||
props.push_back(bg_color);
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return props;
|
||||
}
|
||||
|
||||
} // namespace layout_designer
|
||||
} // namespace editor
|
||||
} // namespace yaze
|
||||
|
||||
@@ -1,200 +0,0 @@
|
||||
#ifndef YAZE_APP_EDITOR_LAYOUT_DESIGNER_WIDGET_DEFINITION_H_
|
||||
#define YAZE_APP_EDITOR_LAYOUT_DESIGNER_WIDGET_DEFINITION_H_
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "imgui/imgui.h"
|
||||
|
||||
namespace yaze {
|
||||
namespace editor {
|
||||
namespace layout_designer {
|
||||
|
||||
/**
|
||||
* @enum WidgetType
|
||||
* @brief Types of ImGui widgets available in the designer
|
||||
*/
|
||||
enum class WidgetType {
|
||||
// Basic Widgets
|
||||
Text,
|
||||
TextWrapped,
|
||||
TextColored,
|
||||
Button,
|
||||
SmallButton,
|
||||
Checkbox,
|
||||
RadioButton,
|
||||
InputText,
|
||||
InputInt,
|
||||
InputFloat,
|
||||
SliderInt,
|
||||
SliderFloat,
|
||||
ColorEdit,
|
||||
ColorPicker,
|
||||
|
||||
// Layout Widgets
|
||||
Separator,
|
||||
SameLine,
|
||||
Spacing,
|
||||
Dummy, // Invisible spacing
|
||||
NewLine,
|
||||
Indent,
|
||||
Unindent,
|
||||
|
||||
// Container Widgets
|
||||
BeginGroup,
|
||||
EndGroup,
|
||||
BeginChild,
|
||||
EndChild,
|
||||
CollapsingHeader,
|
||||
TreeNode,
|
||||
TabBar,
|
||||
TabItem,
|
||||
|
||||
// Table Widgets
|
||||
BeginTable,
|
||||
EndTable,
|
||||
TableNextRow,
|
||||
TableNextColumn,
|
||||
TableSetupColumn,
|
||||
|
||||
// Custom Widgets
|
||||
Canvas, // Custom drawing area
|
||||
Image,
|
||||
ImageButton,
|
||||
ProgressBar,
|
||||
BulletText,
|
||||
|
||||
// Menu Widgets
|
||||
BeginMenu,
|
||||
EndMenu,
|
||||
MenuItem,
|
||||
|
||||
// Combo/Dropdown
|
||||
BeginCombo,
|
||||
EndCombo,
|
||||
Selectable,
|
||||
ListBox,
|
||||
};
|
||||
|
||||
/**
|
||||
* @struct WidgetProperty
|
||||
* @brief Represents a configurable property of a widget
|
||||
*/
|
||||
struct WidgetProperty {
|
||||
std::string name;
|
||||
enum class Type {
|
||||
String,
|
||||
Int,
|
||||
Float,
|
||||
Bool,
|
||||
Color,
|
||||
Vec2,
|
||||
Flags
|
||||
} type;
|
||||
|
||||
// Value storage (union-like)
|
||||
std::string string_value;
|
||||
int int_value = 0;
|
||||
float float_value = 0.0f;
|
||||
bool bool_value = false;
|
||||
ImVec4 color_value = ImVec4(1, 1, 1, 1);
|
||||
ImVec2 vec2_value = ImVec2(0, 0);
|
||||
int flags_value = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* @struct WidgetDefinition
|
||||
* @brief Defines a widget instance in a panel layout
|
||||
*/
|
||||
struct WidgetDefinition {
|
||||
std::string id; // Unique widget ID
|
||||
WidgetType type; // Widget type
|
||||
std::string label; // Display label
|
||||
ImVec2 position = ImVec2(0, 0); // Position in panel (for absolute positioning)
|
||||
ImVec2 size = ImVec2(-1, 0); // Size (-1 = auto-width)
|
||||
|
||||
// Properties specific to widget type
|
||||
std::vector<WidgetProperty> properties;
|
||||
|
||||
// Hierarchy
|
||||
std::vector<std::unique_ptr<WidgetDefinition>> children;
|
||||
|
||||
// Code generation hints
|
||||
std::string callback_name; // For buttons, menu items, etc.
|
||||
std::string tooltip; // Hover tooltip
|
||||
bool same_line = false; // Should this widget be on same line as previous?
|
||||
|
||||
// Visual hints for designer
|
||||
bool selected = false;
|
||||
ImVec4 border_color = ImVec4(0.5f, 0.5f, 0.5f, 1.0f);
|
||||
|
||||
// Helper methods
|
||||
void AddProperty(const std::string& name, WidgetProperty::Type type);
|
||||
WidgetProperty* GetProperty(const std::string& name);
|
||||
void AddChild(std::unique_ptr<WidgetDefinition> child);
|
||||
|
||||
// Validation
|
||||
bool IsContainer() const;
|
||||
bool CanHaveChildren() const;
|
||||
bool RequiresEnd() const; // Needs End*() call
|
||||
};
|
||||
|
||||
/**
|
||||
* @struct PanelDesign
|
||||
* @brief Complete design definition for a panel's internal layout
|
||||
*/
|
||||
struct PanelDesign {
|
||||
std::string panel_id; // e.g., "dungeon.room_selector"
|
||||
std::string panel_name; // Human-readable name
|
||||
ImVec2 design_size = ImVec2(400, 600); // Design canvas size
|
||||
|
||||
// Widget tree (root level widgets)
|
||||
std::vector<std::unique_ptr<WidgetDefinition>> widgets;
|
||||
|
||||
// Metadata
|
||||
std::string author;
|
||||
std::string version = "1.0.0";
|
||||
int64_t created_timestamp = 0;
|
||||
int64_t modified_timestamp = 0;
|
||||
|
||||
// Helper methods
|
||||
void AddWidget(std::unique_ptr<WidgetDefinition> widget);
|
||||
WidgetDefinition* FindWidget(const std::string& id);
|
||||
std::vector<WidgetDefinition*> GetAllWidgets();
|
||||
bool Validate(std::string* error_message = nullptr) const;
|
||||
void Touch(); // Update modified timestamp
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Get human-readable name for widget type
|
||||
*/
|
||||
const char* GetWidgetTypeName(WidgetType type);
|
||||
|
||||
/**
|
||||
* @brief Get icon for widget type
|
||||
*/
|
||||
const char* GetWidgetTypeIcon(WidgetType type);
|
||||
|
||||
/**
|
||||
* @brief Check if widget type is a container
|
||||
*/
|
||||
bool IsContainerWidget(WidgetType type);
|
||||
|
||||
/**
|
||||
* @brief Check if widget type requires an End*() call
|
||||
*/
|
||||
bool RequiresEndCall(WidgetType type);
|
||||
|
||||
/**
|
||||
* @brief Get default properties for a widget type
|
||||
*/
|
||||
std::vector<WidgetProperty> GetDefaultProperties(WidgetType type);
|
||||
|
||||
} // namespace layout_designer
|
||||
} // namespace editor
|
||||
} // namespace yaze
|
||||
|
||||
#endif // YAZE_APP_EDITOR_LAYOUT_DESIGNER_WIDGET_DEFINITION_H_
|
||||
|
||||
@@ -1,283 +0,0 @@
|
||||
#include "app/editor/layout_designer/yaze_widgets.h"
|
||||
|
||||
#include "absl/strings/str_format.h"
|
||||
#include "app/gui/core/icons.h"
|
||||
|
||||
namespace yaze {
|
||||
namespace editor {
|
||||
namespace layout_designer {
|
||||
|
||||
const char* GetYazeWidgetTypeName(YazeWidgetType type) {
|
||||
switch (type) {
|
||||
case YazeWidgetType::ThemedButton: return "Themed Button";
|
||||
case YazeWidgetType::PrimaryButton: return "Primary Button";
|
||||
case YazeWidgetType::DangerButton: return "Danger Button";
|
||||
case YazeWidgetType::ThemedIconButton: return "Themed Icon Button";
|
||||
case YazeWidgetType::TransparentIconButton: return "Transparent Icon Button";
|
||||
case YazeWidgetType::BeginField: return "Begin Field";
|
||||
case YazeWidgetType::EndField: return "End Field";
|
||||
case YazeWidgetType::PropertyTable: return "Property Table";
|
||||
case YazeWidgetType::PropertyRow: return "Property Row";
|
||||
case YazeWidgetType::SectionHeader: return "Section Header";
|
||||
case YazeWidgetType::PaddedPanel: return "Padded Panel";
|
||||
case YazeWidgetType::TableWithTheming: return "Themed Table";
|
||||
case YazeWidgetType::CanvasPanel: return "Canvas Panel";
|
||||
case YazeWidgetType::AutoInputField: return "Auto Input Field";
|
||||
case YazeWidgetType::AutoButton: return "Auto Button";
|
||||
case YazeWidgetType::AutoCheckbox: return "Auto Checkbox";
|
||||
case YazeWidgetType::AutoInputText: return "Auto Input Text";
|
||||
case YazeWidgetType::PaletteColorButton: return "Palette Color Button";
|
||||
case YazeWidgetType::PanelHeader: return "Panel Header";
|
||||
default: return "Unknown Yaze Widget";
|
||||
}
|
||||
}
|
||||
|
||||
const char* GetYazeWidgetTypeIcon(YazeWidgetType type) {
|
||||
switch (type) {
|
||||
case YazeWidgetType::ThemedButton:
|
||||
case YazeWidgetType::PrimaryButton:
|
||||
case YazeWidgetType::DangerButton:
|
||||
case YazeWidgetType::AutoButton:
|
||||
return ICON_MD_SMART_BUTTON;
|
||||
|
||||
case YazeWidgetType::ThemedIconButton:
|
||||
case YazeWidgetType::TransparentIconButton:
|
||||
return ICON_MD_RADIO_BUTTON_UNCHECKED;
|
||||
|
||||
case YazeWidgetType::PropertyTable:
|
||||
case YazeWidgetType::PropertyRow:
|
||||
case YazeWidgetType::TableWithTheming:
|
||||
return ICON_MD_TABLE_CHART;
|
||||
|
||||
case YazeWidgetType::SectionHeader:
|
||||
case YazeWidgetType::PanelHeader:
|
||||
return ICON_MD_TITLE;
|
||||
|
||||
case YazeWidgetType::CanvasPanel:
|
||||
return ICON_MD_DRAW;
|
||||
|
||||
case YazeWidgetType::BeginField:
|
||||
case YazeWidgetType::EndField:
|
||||
case YazeWidgetType::AutoInputField:
|
||||
case YazeWidgetType::AutoInputText:
|
||||
return ICON_MD_INPUT;
|
||||
|
||||
case YazeWidgetType::PaletteColorButton:
|
||||
return ICON_MD_PALETTE;
|
||||
|
||||
default:
|
||||
return ICON_MD_WIDGETS;
|
||||
}
|
||||
}
|
||||
|
||||
std::string GenerateYazeWidgetCode(YazeWidgetType yaze_type,
|
||||
const WidgetDefinition& widget,
|
||||
int indent_level) {
|
||||
std::string indent(indent_level * 2, ' ');
|
||||
std::string code;
|
||||
|
||||
auto* label_prop = const_cast<WidgetDefinition&>(widget).GetProperty("label");
|
||||
std::string label = label_prop ? label_prop->string_value : "Widget";
|
||||
|
||||
switch (yaze_type) {
|
||||
case YazeWidgetType::ThemedButton:
|
||||
code += indent + absl::StrFormat("if (gui::ThemedButton(\"%s\")) {\n", label);
|
||||
if (!widget.callback_name.empty()) {
|
||||
code += indent + absl::StrFormat(" %s();\n", widget.callback_name);
|
||||
}
|
||||
code += indent + "}\n";
|
||||
break;
|
||||
|
||||
case YazeWidgetType::PrimaryButton:
|
||||
code += indent + absl::StrFormat("if (gui::PrimaryButton(\"%s\")) {\n", label);
|
||||
if (!widget.callback_name.empty()) {
|
||||
code += indent + absl::StrFormat(" %s();\n", widget.callback_name);
|
||||
}
|
||||
code += indent + "}\n";
|
||||
break;
|
||||
|
||||
case YazeWidgetType::DangerButton:
|
||||
code += indent + absl::StrFormat("if (gui::DangerButton(\"%s\")) {\n", label);
|
||||
if (!widget.callback_name.empty()) {
|
||||
code += indent + absl::StrFormat(" %s();\n", widget.callback_name);
|
||||
}
|
||||
code += indent + "}\n";
|
||||
break;
|
||||
|
||||
case YazeWidgetType::SectionHeader: {
|
||||
auto* icon_prop = const_cast<WidgetDefinition&>(widget).GetProperty("icon");
|
||||
std::string icon = icon_prop ? icon_prop->string_value : ICON_MD_LABEL;
|
||||
code += indent + absl::StrFormat("gui::SectionHeader(\"%s\", \"%s\");\n",
|
||||
icon, label);
|
||||
break;
|
||||
}
|
||||
|
||||
case YazeWidgetType::PropertyTable:
|
||||
code += indent + "if (gui::BeginPropertyTable(\"props\")) {\n";
|
||||
code += indent + " // Add property rows here\n";
|
||||
code += indent + " gui::EndPropertyTable();\n";
|
||||
code += indent + "}\n";
|
||||
break;
|
||||
|
||||
case YazeWidgetType::PropertyRow: {
|
||||
auto* value_prop = const_cast<WidgetDefinition&>(widget).GetProperty("value");
|
||||
std::string value = value_prop ? value_prop->string_value : "Value";
|
||||
code += indent + absl::StrFormat("gui::PropertyRow(\"%s\", \"%s\");\n",
|
||||
label, value);
|
||||
break;
|
||||
}
|
||||
|
||||
case YazeWidgetType::TableWithTheming: {
|
||||
auto* columns_prop = const_cast<WidgetDefinition&>(widget).GetProperty("columns");
|
||||
int columns = columns_prop ? columns_prop->int_value : 2;
|
||||
code += indent + absl::StrFormat(
|
||||
"if (gui::LayoutHelpers::BeginTableWithTheming(\"table\", %d)) {\n",
|
||||
columns);
|
||||
code += indent + " // Table contents\n";
|
||||
code += indent + " gui::LayoutHelpers::EndTableWithTheming();\n";
|
||||
code += indent + "}\n";
|
||||
break;
|
||||
}
|
||||
|
||||
case YazeWidgetType::AutoButton:
|
||||
code += indent + absl::StrFormat("if (gui::AutoButton(\"%s\")) {\n", label);
|
||||
if (!widget.callback_name.empty()) {
|
||||
code += indent + absl::StrFormat(" %s();\n", widget.callback_name);
|
||||
}
|
||||
code += indent + "}\n";
|
||||
break;
|
||||
|
||||
case YazeWidgetType::CanvasPanel: {
|
||||
auto* size_prop = const_cast<WidgetDefinition&>(widget).GetProperty("size");
|
||||
ImVec2 size = size_prop ? size_prop->vec2_value : ImVec2(300, 200);
|
||||
code += indent + "ImVec2 canvas_size;\n";
|
||||
code += indent + "gui::LayoutHelpers::BeginCanvasPanel(\"canvas\", &canvas_size);\n";
|
||||
code += indent + "// Custom drawing code here\n";
|
||||
code += indent + "gui::LayoutHelpers::EndCanvasPanel();\n";
|
||||
break;
|
||||
}
|
||||
|
||||
case YazeWidgetType::PanelHeader: {
|
||||
auto* icon_prop = const_cast<WidgetDefinition&>(widget).GetProperty("icon");
|
||||
std::string icon = icon_prop ? icon_prop->string_value : ICON_MD_WINDOW;
|
||||
code += indent + absl::StrFormat("gui::PanelHeader(\"%s\", \"%s\");\n",
|
||||
label, icon);
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
code += indent + absl::StrFormat("// TODO: Yaze widget: %s\n",
|
||||
GetYazeWidgetTypeName(yaze_type));
|
||||
break;
|
||||
}
|
||||
|
||||
return code;
|
||||
}
|
||||
|
||||
std::vector<std::string> GetRequiredIncludes(YazeWidgetType type) {
|
||||
std::vector<std::string> includes;
|
||||
|
||||
switch (type) {
|
||||
case YazeWidgetType::ThemedButton:
|
||||
case YazeWidgetType::PrimaryButton:
|
||||
case YazeWidgetType::DangerButton:
|
||||
case YazeWidgetType::ThemedIconButton:
|
||||
case YazeWidgetType::TransparentIconButton:
|
||||
case YazeWidgetType::PaletteColorButton:
|
||||
case YazeWidgetType::PanelHeader:
|
||||
includes.push_back("app/gui/widgets/themed_widgets.h");
|
||||
break;
|
||||
|
||||
case YazeWidgetType::BeginField:
|
||||
case YazeWidgetType::EndField:
|
||||
case YazeWidgetType::PropertyTable:
|
||||
case YazeWidgetType::PropertyRow:
|
||||
case YazeWidgetType::SectionHeader:
|
||||
includes.push_back("app/gui/core/ui_helpers.h");
|
||||
break;
|
||||
|
||||
case YazeWidgetType::PaddedPanel:
|
||||
case YazeWidgetType::TableWithTheming:
|
||||
case YazeWidgetType::CanvasPanel:
|
||||
case YazeWidgetType::AutoInputField:
|
||||
includes.push_back("app/gui/core/layout_helpers.h");
|
||||
break;
|
||||
|
||||
case YazeWidgetType::AutoButton:
|
||||
case YazeWidgetType::AutoCheckbox:
|
||||
case YazeWidgetType::AutoInputText:
|
||||
includes.push_back("app/gui/automation/widget_auto_register.h");
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return includes;
|
||||
}
|
||||
|
||||
std::vector<WidgetProperty> GetYazeDefaultProperties(YazeWidgetType type) {
|
||||
std::vector<WidgetProperty> props;
|
||||
|
||||
switch (type) {
|
||||
case YazeWidgetType::ThemedButton:
|
||||
case YazeWidgetType::PrimaryButton:
|
||||
case YazeWidgetType::DangerButton:
|
||||
case YazeWidgetType::AutoButton: {
|
||||
WidgetProperty label;
|
||||
label.name = "label";
|
||||
label.type = WidgetProperty::Type::String;
|
||||
label.string_value = "Button";
|
||||
props.push_back(label);
|
||||
break;
|
||||
}
|
||||
|
||||
case YazeWidgetType::SectionHeader: {
|
||||
WidgetProperty label;
|
||||
label.name = "label";
|
||||
label.type = WidgetProperty::Type::String;
|
||||
label.string_value = "Section";
|
||||
props.push_back(label);
|
||||
|
||||
WidgetProperty icon;
|
||||
icon.name = "icon";
|
||||
icon.type = WidgetProperty::Type::String;
|
||||
icon.string_value = ICON_MD_LABEL;
|
||||
props.push_back(icon);
|
||||
break;
|
||||
}
|
||||
|
||||
case YazeWidgetType::PropertyTable: {
|
||||
WidgetProperty columns;
|
||||
columns.name = "columns";
|
||||
columns.type = WidgetProperty::Type::Int;
|
||||
columns.int_value = 2;
|
||||
props.push_back(columns);
|
||||
break;
|
||||
}
|
||||
|
||||
case YazeWidgetType::PropertyRow: {
|
||||
WidgetProperty label;
|
||||
label.name = "label";
|
||||
label.type = WidgetProperty::Type::String;
|
||||
label.string_value = "Property";
|
||||
props.push_back(label);
|
||||
|
||||
WidgetProperty value;
|
||||
value.name = "value";
|
||||
value.type = WidgetProperty::Type::String;
|
||||
value.string_value = "Value";
|
||||
props.push_back(value);
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return props;
|
||||
}
|
||||
|
||||
} // namespace layout_designer
|
||||
} // namespace editor
|
||||
} // namespace yaze
|
||||
@@ -1,86 +0,0 @@
|
||||
#ifndef YAZE_APP_EDITOR_LAYOUT_DESIGNER_YAZE_WIDGETS_H_
|
||||
#define YAZE_APP_EDITOR_LAYOUT_DESIGNER_YAZE_WIDGETS_H_
|
||||
|
||||
#include "app/editor/layout_designer/widget_definition.h"
|
||||
|
||||
namespace yaze {
|
||||
namespace editor {
|
||||
namespace layout_designer {
|
||||
|
||||
/**
|
||||
* @enum YazeWidgetType
|
||||
* @brief Extended widget types using yaze GUI abstractions
|
||||
*/
|
||||
enum class YazeWidgetType {
|
||||
// Themed buttons (from themed_widgets.h)
|
||||
ThemedButton,
|
||||
PrimaryButton,
|
||||
DangerButton,
|
||||
ThemedIconButton,
|
||||
TransparentIconButton,
|
||||
|
||||
// Layout helpers (from ui_helpers.h)
|
||||
BeginField, // Label + widget field pattern
|
||||
EndField,
|
||||
PropertyTable, // Table for properties
|
||||
PropertyRow, // Property row in table
|
||||
SectionHeader, // Section header with icon
|
||||
|
||||
// Layout helpers (from layout_helpers.h)
|
||||
PaddedPanel, // Panel with standard padding
|
||||
TableWithTheming, // Table with theme awareness
|
||||
CanvasPanel, // Canvas with toolbar
|
||||
AutoInputField, // Auto-sized input field
|
||||
|
||||
// Widget automation (from widget_auto_register.h)
|
||||
AutoButton, // Auto-registered button
|
||||
AutoCheckbox, // Auto-registered checkbox
|
||||
AutoInputText, // Auto-registered input
|
||||
|
||||
// Custom yaze widgets
|
||||
PaletteColorButton, // Color button for palettes
|
||||
PanelHeader, // Panel header with icon
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Convert YazeWidgetType to WidgetType (for base widget system)
|
||||
*/
|
||||
WidgetType ToWidgetType(YazeWidgetType type);
|
||||
|
||||
/**
|
||||
* @brief Get human-readable name for yaze widget type
|
||||
*/
|
||||
const char* GetYazeWidgetTypeName(YazeWidgetType type);
|
||||
|
||||
/**
|
||||
* @brief Get icon for yaze widget type
|
||||
*/
|
||||
const char* GetYazeWidgetTypeIcon(YazeWidgetType type);
|
||||
|
||||
/**
|
||||
* @brief Generate code for yaze widget (uses yaze abstractions)
|
||||
* @param yaze_type The yaze widget type
|
||||
* @param widget The widget definition (for properties)
|
||||
* @param indent_level Indentation level
|
||||
* @return Generated C++ code using yaze helpers
|
||||
*/
|
||||
std::string GenerateYazeWidgetCode(YazeWidgetType yaze_type,
|
||||
const WidgetDefinition& widget,
|
||||
int indent_level = 0);
|
||||
|
||||
/**
|
||||
* @brief Get default properties for yaze widget type
|
||||
*/
|
||||
std::vector<WidgetProperty> GetYazeDefaultProperties(YazeWidgetType type);
|
||||
|
||||
/**
|
||||
* @brief Check if yaze widget requires specific includes
|
||||
*/
|
||||
std::vector<std::string> GetRequiredIncludes(YazeWidgetType type);
|
||||
|
||||
} // namespace layout_designer
|
||||
} // namespace editor
|
||||
} // namespace yaze
|
||||
|
||||
#endif // YAZE_APP_EDITOR_LAYOUT_DESIGNER_YAZE_WIDGETS_H_
|
||||
|
||||
@@ -286,8 +286,6 @@ void MenuOrchestrator::AddToolsMenuItems() {
|
||||
[this]() { OnShowCommandPalette(); }, SHORTCUT_CTRL_SHIFT(P))
|
||||
.Item("Resource Label Manager", ICON_MD_LABEL,
|
||||
[this]() { OnShowResourceLabelManager(); })
|
||||
.Item("Layout Designer", ICON_MD_DASHBOARD,
|
||||
[this]() { OnShowLayoutDesigner(); }, SHORTCUT_CTRL(L))
|
||||
.Separator();
|
||||
|
||||
// ROM Analysis (moved from Debug menu)
|
||||
@@ -750,13 +748,6 @@ void MenuOrchestrator::OnShowWelcomeScreen() {
|
||||
}
|
||||
}
|
||||
|
||||
void MenuOrchestrator::OnShowLayoutDesigner() {
|
||||
// Open the WYSIWYG layout designer
|
||||
if (editor_manager_) {
|
||||
editor_manager_->OpenLayoutDesigner();
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef YAZE_BUILD_AGENT_UI
|
||||
void MenuOrchestrator::OnShowAIAgent() {
|
||||
if (editor_manager_) {
|
||||
|
||||
@@ -98,7 +98,6 @@ class MenuOrchestrator {
|
||||
void OnShowEmulator();
|
||||
void OnShowPanelBrowser();
|
||||
void OnShowWelcomeScreen();
|
||||
void OnShowLayoutDesigner();
|
||||
|
||||
#ifdef YAZE_BUILD_AGENT_UI
|
||||
void OnShowAIAgent();
|
||||
|
||||
Reference in New Issue
Block a user