imgui-frontend-engineer: move layout designer into lab target

This commit is contained in:
scawful
2025-12-28 09:56:41 -06:00
parent d002f5556d
commit b78e167105
27 changed files with 557 additions and 114 deletions

View File

@@ -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()

View File

@@ -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();
}

View File

@@ -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);

View File

@@ -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! 🎨

View File

@@ -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.

View File

@@ -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!

View File

@@ -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

View File

@@ -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

View File

@@ -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_

View File

@@ -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

View File

@@ -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_

View File

@@ -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

View File

@@ -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_

View File

@@ -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

View File

@@ -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_

View File

@@ -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 &prop;
}
}
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

View File

@@ -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_

View File

@@ -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

View File

@@ -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_

View File

@@ -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_) {

View File

@@ -98,7 +98,6 @@ class MenuOrchestrator {
void OnShowEmulator();
void OnShowPanelBrowser();
void OnShowWelcomeScreen();
void OnShowLayoutDesigner();
#ifdef YAZE_BUILD_AGENT_UI
void OnShowAIAgent();