backend-infra-engineer: Post v0.3.9-hotfix7 snapshot (build cleanup)
This commit is contained in:
993
docs/internal/plans/ui_ux_improvement.md
Normal file
993
docs/internal/plans/ui_ux_improvement.md
Normal file
@@ -0,0 +1,993 @@
|
||||
# UI/UX Improvement Plan: IDE-like Interface
|
||||
|
||||
> Terminology update: ongoing refactor renames Card → Panel. References to
|
||||
> `CardInfo`/`EditorCardRegistry` should be read as
|
||||
> `PanelDescriptor`/`PanelManager` going forward.
|
||||
|
||||
**Created**: 2025-01-25
|
||||
**Status**: Design Phase
|
||||
**Priority**: High
|
||||
**Target**: Make yaze feel like VSCode/JetBrains IDEs
|
||||
|
||||
---
|
||||
|
||||
## Executive Summary
|
||||
|
||||
This document outlines comprehensive UI/UX improvements to transform yaze into a professional, IDE-like application with consistent theming, responsive layouts, proper disabled states, and Material Design principles throughout.
|
||||
|
||||
**Key Improvements**:
|
||||
1. Enhanced sidebar system with responsive sizing, badges, and disabled states
|
||||
2. Improved menu bar with proper precondition checks and consistent spacing
|
||||
3. Responsive panel system that adapts to window size
|
||||
4. Extended AgentUITheme for application-wide consistency
|
||||
5. Status cluster enhancements for better visual feedback
|
||||
|
||||
---
|
||||
|
||||
## 1. Sidebar System Improvements
|
||||
|
||||
### Current Issues
|
||||
- No visual feedback for disabled cards (when ROM not loaded)
|
||||
- Fixed sizing doesn't respond to window height changes
|
||||
- No badge indicators for notifications/counts
|
||||
- Hover effects are basic (only tooltip)
|
||||
- Missing selection state visual feedback
|
||||
- No category headers/separators for large card lists
|
||||
|
||||
### Proposed Solutions
|
||||
|
||||
#### 1.1 Disabled State System
|
||||
|
||||
**File**: `src/app/editor/system/editor_card_registry.h`
|
||||
|
||||
```cpp
|
||||
struct CardInfo {
|
||||
// ... existing fields ...
|
||||
std::function<bool()> enabled_condition; // NEW: Card enable condition
|
||||
int notification_count = 0; // NEW: Badge count (0 = no badge)
|
||||
};
|
||||
```
|
||||
|
||||
**File**: `src/app/editor/system/editor_card_registry.cc` (DrawSidebar)
|
||||
|
||||
```cpp
|
||||
// In the card drawing loop:
|
||||
bool is_active = card.visibility_flag && *card.visibility_flag;
|
||||
bool is_enabled = card.enabled_condition ? card.enabled_condition() : true;
|
||||
|
||||
if (!is_enabled) {
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_Alpha, 0.4f); // Dim disabled cards
|
||||
}
|
||||
|
||||
// ... draw button ...
|
||||
|
||||
if (!is_enabled) {
|
||||
ImGui::PopStyleVar();
|
||||
}
|
||||
|
||||
if (ImGui::IsItemHovered()) {
|
||||
if (!is_enabled) {
|
||||
ImGui::SetTooltip("%s\nRequires ROM to be loaded", card.display_name.c_str());
|
||||
} else {
|
||||
// Normal tooltip
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### 1.2 Notification Badges
|
||||
|
||||
**New Helper Function**: `src/app/editor/system/editor_card_registry.cc`
|
||||
|
||||
```cpp
|
||||
void EditorCardRegistry::DrawCardWithBadge(const CardInfo& card, bool is_active) {
|
||||
const auto& theme = gui::ThemeManager::Get().GetCurrentTheme();
|
||||
|
||||
// Draw the main button
|
||||
// ... existing button code ...
|
||||
|
||||
// Draw notification badge if count > 0
|
||||
if (card.notification_count > 0) {
|
||||
ImDrawList* draw_list = ImGui::GetWindowDrawList();
|
||||
ImVec2 button_max = ImGui::GetItemRectMax();
|
||||
|
||||
// Badge position (top-right corner of button)
|
||||
float badge_radius = 8.0f;
|
||||
ImVec2 badge_pos(button_max.x - badge_radius, button_max.y - 32.0f + badge_radius);
|
||||
|
||||
// Draw badge circle
|
||||
ImVec4 error_color = gui::ConvertColorToImVec4(theme.error);
|
||||
draw_list->AddCircleFilled(badge_pos, badge_radius, ImGui::GetColorU32(error_color));
|
||||
|
||||
// Draw count text
|
||||
if (card.notification_count < 100) {
|
||||
char badge_text[4];
|
||||
snprintf(badge_text, sizeof(badge_text), "%d", card.notification_count);
|
||||
|
||||
ImVec2 text_size = ImGui::CalcTextSize(badge_text);
|
||||
ImVec2 text_pos(badge_pos.x - text_size.x * 0.5f,
|
||||
badge_pos.y - text_size.y * 0.5f);
|
||||
|
||||
draw_list->AddText(text_pos, IM_COL32(255, 255, 255, 255), badge_text);
|
||||
} else {
|
||||
draw_list->AddText(
|
||||
ImVec2(badge_pos.x - 6, badge_pos.y - 6),
|
||||
IM_COL32(255, 255, 255, 255), "!");
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### 1.3 Enhanced Hover Effects
|
||||
|
||||
**Style Improvements**:
|
||||
|
||||
```cpp
|
||||
// In DrawSidebar, replace existing button style:
|
||||
if (is_active) {
|
||||
ImGui::PushStyleColor(ImGuiCol_Button,
|
||||
ImVec4(accent_color.x, accent_color.y, accent_color.z, 0.6f));
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonHovered,
|
||||
ImVec4(accent_color.x, accent_color.y, accent_color.z, 0.8f));
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonActive, accent_color);
|
||||
|
||||
// Add glow effect
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 8.0f);
|
||||
} else {
|
||||
ImGui::PushStyleColor(ImGuiCol_Button, button_bg);
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonHovered,
|
||||
ImVec4(accent_color.x, accent_color.y, accent_color.z, 0.3f));
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonActive,
|
||||
gui::ConvertColorToImVec4(theme.button_active));
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 4.0f);
|
||||
}
|
||||
```
|
||||
|
||||
#### 1.4 Responsive Sizing
|
||||
|
||||
**Current code calculates fixed heights**. Improve with dynamic calculations:
|
||||
|
||||
```cpp
|
||||
// Replace fixed utility_section_height calculation:
|
||||
const float utility_section_height = 4 * 40.0f + 80.0f;
|
||||
|
||||
// With dynamic calculation:
|
||||
const float button_height = 40.0f * theme.widget_height_multiplier;
|
||||
const float spacing = ImGui::GetStyle().ItemSpacing.y;
|
||||
const float utility_button_count = 4;
|
||||
const float utility_section_height =
|
||||
(utility_button_count * (button_height + spacing)) +
|
||||
80.0f * theme.panel_padding_multiplier;
|
||||
|
||||
const float current_y = ImGui::GetCursorPosY();
|
||||
const float available_height = viewport_height - current_y - utility_section_height;
|
||||
```
|
||||
|
||||
#### 1.5 Category Headers/Separators
|
||||
|
||||
**For editors with many cards**, add collapsible sections:
|
||||
|
||||
```cpp
|
||||
void EditorCardRegistry::DrawSidebarWithSections(
|
||||
size_t session_id,
|
||||
const std::string& category,
|
||||
const std::map<std::string, std::vector<CardInfo>>& card_sections) {
|
||||
|
||||
for (const auto& [section_name, cards] : card_sections) {
|
||||
if (ImGui::CollapsingHeader(section_name.c_str(),
|
||||
ImGuiTreeNodeFlags_DefaultOpen)) {
|
||||
for (const auto& card : cards) {
|
||||
DrawCardWithBadge(card, /* is_active */ ...);
|
||||
}
|
||||
}
|
||||
ImGui::Spacing();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2. Menu Bar System Improvements
|
||||
|
||||
### Current Issues
|
||||
- Many menu items don't gray out when preconditions not met
|
||||
- Inconsistent spacing/padding between menu items
|
||||
- Status cluster feels cramped
|
||||
- No visual separation between menu groups
|
||||
|
||||
### Proposed Solutions
|
||||
|
||||
#### 2.1 Comprehensive Enabled Conditions
|
||||
|
||||
**File**: `src/app/editor/system/menu_orchestrator.cc`
|
||||
|
||||
Add helper methods:
|
||||
|
||||
```cpp
|
||||
// Existing helpers (keep these):
|
||||
bool MenuOrchestrator::HasActiveRom() const;
|
||||
bool MenuOrchestrator::CanSaveRom() const;
|
||||
bool MenuOrchestrator::HasCurrentEditor() const;
|
||||
bool MenuOrchestrator::HasMultipleSessions() const;
|
||||
|
||||
// NEW helpers:
|
||||
bool MenuOrchestrator::HasActiveSelection() const {
|
||||
// Check if current editor has selected entities/objects
|
||||
// Delegate to EditorManager to query active editor
|
||||
}
|
||||
|
||||
bool MenuOrchestrator::CanUndo() const {
|
||||
// Check if undo stack has entries
|
||||
return HasCurrentEditor() && editor_manager_->CanUndo();
|
||||
}
|
||||
|
||||
bool MenuOrchestrator::CanRedo() const {
|
||||
return HasCurrentEditor() && editor_manager_->CanRedo();
|
||||
}
|
||||
|
||||
bool MenuOrchestrator::HasClipboardData() const {
|
||||
// Check if clipboard has paste-able data
|
||||
return HasCurrentEditor() && editor_manager_->HasClipboardData();
|
||||
}
|
||||
|
||||
bool MenuOrchestrator::IsRomModified() const {
|
||||
return HasActiveRom() && rom_manager_.GetCurrentRom()->dirty();
|
||||
}
|
||||
```
|
||||
|
||||
#### 2.2 Apply Conditions to All Menu Items
|
||||
|
||||
**File**: `src/app/editor/system/menu_orchestrator.cc`
|
||||
|
||||
```cpp
|
||||
void MenuOrchestrator::BuildFileMenu() {
|
||||
menu_builder_.BeginMenu("File", ICON_MD_FOLDER);
|
||||
|
||||
menu_builder_.Item(
|
||||
"Open ROM", ICON_MD_FILE_OPEN,
|
||||
[this]() { OnOpenRom(); },
|
||||
"Ctrl+O",
|
||||
[]() { return true; } // Always enabled
|
||||
);
|
||||
|
||||
menu_builder_.Item(
|
||||
"Save ROM", ICON_MD_SAVE,
|
||||
[this]() { OnSaveRom(); },
|
||||
"Ctrl+S",
|
||||
[this]() { return CanSaveRom(); } // EXISTING
|
||||
);
|
||||
|
||||
menu_builder_.Item(
|
||||
"Save ROM As...", ICON_MD_SAVE_AS,
|
||||
[this]() { OnSaveRomAs(); },
|
||||
"Ctrl+Shift+S",
|
||||
[this]() { return HasActiveRom(); } // NEW
|
||||
);
|
||||
|
||||
menu_builder_.Separator();
|
||||
|
||||
menu_builder_.Item(
|
||||
"Create Project", ICON_MD_CREATE_NEW_FOLDER,
|
||||
[this]() { OnCreateProject(); },
|
||||
"Ctrl+Shift+N",
|
||||
[this]() { return HasActiveRom(); } // NEW - requires ROM first
|
||||
);
|
||||
|
||||
// ... continue for all items
|
||||
|
||||
menu_builder_.EndMenu();
|
||||
}
|
||||
|
||||
void MenuOrchestrator::BuildEditMenu() {
|
||||
menu_builder_.BeginMenu("Edit", ICON_MD_EDIT);
|
||||
|
||||
menu_builder_.Item(
|
||||
"Undo", ICON_MD_UNDO,
|
||||
[this]() { OnUndo(); },
|
||||
"Ctrl+Z",
|
||||
[this]() { return CanUndo(); } // NEW
|
||||
);
|
||||
|
||||
menu_builder_.Item(
|
||||
"Redo", ICON_MD_REDO,
|
||||
[this]() { OnRedo(); },
|
||||
"Ctrl+Y",
|
||||
[this]() { return CanRedo(); } // NEW
|
||||
);
|
||||
|
||||
menu_builder_.Separator();
|
||||
|
||||
menu_builder_.Item(
|
||||
"Cut", ICON_MD_CONTENT_CUT,
|
||||
[this]() { OnCut(); },
|
||||
"Ctrl+X",
|
||||
[this]() { return HasActiveSelection(); } // NEW
|
||||
);
|
||||
|
||||
menu_builder_.Item(
|
||||
"Copy", ICON_MD_CONTENT_COPY,
|
||||
[this]() { OnCopy(); },
|
||||
"Ctrl+C",
|
||||
[this]() { return HasActiveSelection(); } // NEW
|
||||
);
|
||||
|
||||
menu_builder_.Item(
|
||||
"Paste", ICON_MD_CONTENT_PASTE,
|
||||
[this]() { OnPaste(); },
|
||||
"Ctrl+V",
|
||||
[this]() { return HasClipboardData(); } // NEW
|
||||
);
|
||||
|
||||
menu_builder_.EndMenu();
|
||||
}
|
||||
```
|
||||
|
||||
#### 2.3 Menu Spacing and Visual Grouping
|
||||
|
||||
**File**: `src/app/editor/ui/menu_builder.h`
|
||||
|
||||
Add spacing control methods:
|
||||
|
||||
```cpp
|
||||
class MenuBuilder {
|
||||
public:
|
||||
// ... existing methods ...
|
||||
|
||||
// NEW: Visual spacing
|
||||
void SeparatorWithSpacing(float height = 4.0f) {
|
||||
ImGui::Spacing();
|
||||
ImGui::Separator();
|
||||
ImGui::Dummy(ImVec2(0, height));
|
||||
}
|
||||
|
||||
// NEW: Menu section header (for long menus)
|
||||
void SectionHeader(const char* label) {
|
||||
const auto& theme = gui::ThemeManager::Get().GetCurrentTheme();
|
||||
ImGui::PushStyleColor(ImGuiCol_Text,
|
||||
gui::ConvertColorToImVec4(theme.text_disabled));
|
||||
ImGui::TextUnformatted(label);
|
||||
ImGui::PopStyleColor();
|
||||
ImGui::Spacing();
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
**Usage**:
|
||||
|
||||
```cpp
|
||||
void MenuOrchestrator::BuildToolsMenu() {
|
||||
menu_builder_.BeginMenu("Tools", ICON_MD_BUILD);
|
||||
|
||||
menu_builder_.SectionHeader("ROM Analysis");
|
||||
menu_builder_.Item("ROM Info", ICON_MD_INFO, ...);
|
||||
menu_builder_.Item("Validate ROM", ICON_MD_CHECK_CIRCLE, ...);
|
||||
|
||||
menu_builder_.SeparatorWithSpacing();
|
||||
|
||||
menu_builder_.SectionHeader("Utilities");
|
||||
menu_builder_.Item("Global Search", ICON_MD_SEARCH, ...);
|
||||
menu_builder_.Item("Command Palette", ICON_MD_TERMINAL, ...);
|
||||
|
||||
menu_builder_.EndMenu();
|
||||
}
|
||||
```
|
||||
|
||||
#### 2.4 Status Cluster Improvements
|
||||
|
||||
**File**: `src/app/editor/ui/ui_coordinator.cc`
|
||||
|
||||
```cpp
|
||||
void UICoordinator::DrawMenuBarExtras() {
|
||||
const auto& theme = gui::ThemeManager::Get().GetCurrentTheme();
|
||||
|
||||
// Calculate cluster width dynamically
|
||||
float cluster_width = 0.0f;
|
||||
cluster_width += 30.0f; // Dirty badge
|
||||
cluster_width += 30.0f; // Notification bell
|
||||
if (session_coordinator_.HasMultipleSessions()) {
|
||||
cluster_width += 80.0f; // Session button with name
|
||||
}
|
||||
cluster_width += 60.0f; // Version
|
||||
cluster_width += 20.0f; // Padding
|
||||
|
||||
// Right-align with proper spacing
|
||||
ImGui::SameLine(ImGui::GetWindowWidth() - cluster_width);
|
||||
|
||||
// Add subtle separator before status cluster
|
||||
ImGui::PushStyleColor(ImGuiCol_Separator,
|
||||
gui::ConvertColorToImVec4(theme.border));
|
||||
ImGui::SeparatorEx(ImGuiSeparatorFlags_Vertical);
|
||||
ImGui::PopStyleColor();
|
||||
|
||||
ImGui::SameLine();
|
||||
ImGui::Dummy(ImVec2(8.0f, 0)); // Spacing
|
||||
ImGui::SameLine();
|
||||
|
||||
// 1. Dirty badge (with animation)
|
||||
Rom* current_rom = rom_manager_.GetCurrentRom();
|
||||
if (current_rom && current_rom->dirty()) {
|
||||
DrawDirtyBadge();
|
||||
}
|
||||
|
||||
// 2. Notification bell
|
||||
DrawNotificationBell();
|
||||
|
||||
// 3. Session button (if multiple)
|
||||
if (session_coordinator_.HasMultipleSessions()) {
|
||||
DrawSessionButton();
|
||||
}
|
||||
|
||||
// 4. Version
|
||||
ImGui::SameLine();
|
||||
ImGui::PushStyleColor(ImGuiCol_Text,
|
||||
gui::ConvertColorToImVec4(theme.text_disabled));
|
||||
ImGui::TextUnformatted(ICON_MD_INFO " v0.1.0");
|
||||
ImGui::PopStyleColor();
|
||||
}
|
||||
|
||||
void UICoordinator::DrawDirtyBadge() {
|
||||
const auto& theme = gui::ThemeManager::Get().GetCurrentTheme();
|
||||
|
||||
// Pulsing animation
|
||||
static float pulse_time = 0.0f;
|
||||
pulse_time += ImGui::GetIO().DeltaTime;
|
||||
float pulse_alpha = 0.7f + 0.3f * sin(pulse_time * 3.0f);
|
||||
|
||||
ImVec4 warning_color = gui::ConvertColorToImVec4(theme.warning);
|
||||
warning_color.w = pulse_alpha;
|
||||
|
||||
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0, 0, 0, 0));
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonHovered,
|
||||
ImVec4(warning_color.x, warning_color.y, warning_color.z, 0.3f));
|
||||
|
||||
if (ImGui::SmallButton(ICON_MD_CIRCLE)) {
|
||||
// Quick save on click
|
||||
OnSaveRom();
|
||||
}
|
||||
|
||||
ImGui::PopStyleColor(2);
|
||||
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("Unsaved changes\nClick to save (Ctrl+S)");
|
||||
}
|
||||
|
||||
ImGui::SameLine();
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. AgentUITheme Extensions
|
||||
|
||||
### New Theme Properties Needed
|
||||
|
||||
**File**: `src/app/editor/agent/agent_ui_theme.h`
|
||||
|
||||
```cpp
|
||||
struct AgentUITheme {
|
||||
// ... existing fields ...
|
||||
|
||||
// NEW: Sidebar specific colors
|
||||
ImVec4 sidebar_bg;
|
||||
ImVec4 sidebar_border;
|
||||
ImVec4 sidebar_icon_active;
|
||||
ImVec4 sidebar_icon_inactive;
|
||||
ImVec4 sidebar_icon_disabled;
|
||||
ImVec4 sidebar_icon_hover;
|
||||
ImVec4 sidebar_badge_bg;
|
||||
ImVec4 sidebar_badge_text;
|
||||
|
||||
// NEW: Menu bar specific colors
|
||||
ImVec4 menu_separator;
|
||||
ImVec4 menu_section_header;
|
||||
ImVec4 menu_item_disabled;
|
||||
|
||||
// NEW: Panel sizing (responsive)
|
||||
float sidebar_width_base = 48.0f;
|
||||
float sidebar_icon_size = 40.0f;
|
||||
float sidebar_icon_spacing = 6.0f;
|
||||
float panel_padding = 8.0f;
|
||||
float status_cluster_spacing = 8.0f;
|
||||
|
||||
// NEW: Animation timing
|
||||
float hover_transition_speed = 0.15f;
|
||||
float badge_pulse_speed = 3.0f;
|
||||
|
||||
static AgentUITheme FromCurrentTheme();
|
||||
};
|
||||
```
|
||||
|
||||
**File**: `src/app/editor/agent/agent_ui_theme.cc`
|
||||
|
||||
```cpp
|
||||
AgentUITheme AgentUITheme::FromCurrentTheme() {
|
||||
AgentUITheme theme;
|
||||
const auto& current = gui::ThemeManager::Get().GetCurrentTheme();
|
||||
|
||||
// ... existing code ...
|
||||
|
||||
// NEW: Sidebar colors
|
||||
theme.sidebar_bg = ConvertColorToImVec4(current.surface);
|
||||
theme.sidebar_border = ConvertColorToImVec4(current.border);
|
||||
theme.sidebar_icon_active = ConvertColorToImVec4(current.accent);
|
||||
theme.sidebar_icon_inactive = ConvertColorToImVec4(current.button);
|
||||
theme.sidebar_icon_disabled = ImVec4(0.3f, 0.3f, 0.3f, 1.0f);
|
||||
theme.sidebar_icon_hover = ConvertColorToImVec4(current.button_hovered);
|
||||
theme.sidebar_badge_bg = ConvertColorToImVec4(current.error);
|
||||
theme.sidebar_badge_text = ImVec4(1.0f, 1.0f, 1.0f, 1.0f);
|
||||
|
||||
// NEW: Menu bar colors
|
||||
theme.menu_separator = ConvertColorToImVec4(current.border);
|
||||
theme.menu_section_header = ConvertColorToImVec4(current.text_disabled);
|
||||
theme.menu_item_disabled = ImVec4(0.4f, 0.4f, 0.4f, 1.0f);
|
||||
|
||||
// NEW: Responsive sizing from theme
|
||||
theme.sidebar_width_base = 48.0f * current.compact_factor;
|
||||
theme.sidebar_icon_size = 40.0f * current.widget_height_multiplier;
|
||||
theme.sidebar_icon_spacing = 6.0f * current.spacing_multiplier;
|
||||
theme.panel_padding = 8.0f * current.panel_padding_multiplier;
|
||||
|
||||
return theme;
|
||||
}
|
||||
```
|
||||
|
||||
### Helper Functions
|
||||
|
||||
**File**: `src/app/editor/agent/agent_ui_theme.h`
|
||||
|
||||
```cpp
|
||||
namespace AgentUI {
|
||||
|
||||
// ... existing helpers ...
|
||||
|
||||
// NEW: Sidebar helpers
|
||||
void PushSidebarStyle();
|
||||
void PopSidebarStyle();
|
||||
|
||||
void RenderSidebarButton(const char* icon, bool is_active, bool is_enabled,
|
||||
int notification_count = 0);
|
||||
|
||||
// NEW: Menu helpers
|
||||
void PushMenuSectionStyle();
|
||||
void PopMenuSectionStyle();
|
||||
|
||||
// NEW: Status cluster helpers
|
||||
void RenderDirtyIndicator(bool dirty);
|
||||
void RenderNotificationBadge(int count);
|
||||
void RenderSessionIndicator(const char* session_name, bool is_active);
|
||||
|
||||
} // namespace AgentUI
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. Right Panel System Integration
|
||||
|
||||
### Current State
|
||||
- `RightPanelManager` exists and handles agent chat, proposals, settings, help
|
||||
- Uses fixed widths per panel type
|
||||
- No animation (animating_ flag exists but unused)
|
||||
- Good theme integration already
|
||||
|
||||
### Improvements Needed
|
||||
|
||||
#### 4.1 Slide Animation
|
||||
|
||||
**File**: `src/app/editor/ui/right_panel_manager.cc`
|
||||
|
||||
```cpp
|
||||
void RightPanelManager::Draw() {
|
||||
if (active_panel_ == PanelType::kNone && panel_animation_ <= 0.0f) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto& theme = gui::ThemeManager::Get().GetCurrentTheme();
|
||||
const ImGuiViewport* viewport = ImGui::GetMainViewport();
|
||||
const float viewport_height = viewport->WorkSize.y;
|
||||
const float viewport_width = viewport->WorkSize.x;
|
||||
|
||||
// Animate panel expansion/collapse
|
||||
if (animating_) {
|
||||
const float animation_speed = 6.0f * ImGui::GetIO().DeltaTime;
|
||||
|
||||
if (active_panel_ != PanelType::kNone) {
|
||||
// Expanding
|
||||
panel_animation_ = std::min(1.0f, panel_animation_ + animation_speed);
|
||||
if (panel_animation_ >= 1.0f) {
|
||||
animating_ = false;
|
||||
}
|
||||
} else {
|
||||
// Collapsing
|
||||
panel_animation_ = std::max(0.0f, panel_animation_ - animation_speed);
|
||||
if (panel_animation_ <= 0.0f) {
|
||||
animating_ = false;
|
||||
return; // Fully collapsed, don't draw
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Eased animation curve (smooth in/out)
|
||||
float eased_progress = panel_animation_ * panel_animation_ * (3.0f - 2.0f * panel_animation_);
|
||||
|
||||
const float target_width = GetPanelWidth();
|
||||
const float current_width = target_width * eased_progress;
|
||||
|
||||
// ... rest of drawing code, use current_width instead of GetPanelWidth()
|
||||
}
|
||||
```
|
||||
|
||||
#### 4.2 Responsive Width Calculation
|
||||
|
||||
```cpp
|
||||
float RightPanelManager::GetPanelWidth() const {
|
||||
const ImGuiViewport* viewport = ImGui::GetMainViewport();
|
||||
const float viewport_width = viewport->WorkSize.x;
|
||||
|
||||
// Calculate max panel width (30% of viewport, clamped)
|
||||
const float max_panel_width = std::min(600.0f, viewport_width * 0.3f);
|
||||
|
||||
float base_width = 0.0f;
|
||||
switch (active_panel_) {
|
||||
case PanelType::kAgentChat:
|
||||
base_width = agent_chat_width_;
|
||||
break;
|
||||
case PanelType::kProposals:
|
||||
base_width = proposals_width_;
|
||||
break;
|
||||
case PanelType::kSettings:
|
||||
base_width = settings_width_;
|
||||
break;
|
||||
case PanelType::kHelp:
|
||||
base_width = help_width_;
|
||||
break;
|
||||
default:
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
return std::min(base_width, max_panel_width);
|
||||
}
|
||||
```
|
||||
|
||||
#### 4.3 Docking Space Adjustment
|
||||
|
||||
**File**: `src/app/editor/editor_manager.cc`
|
||||
|
||||
When drawing the main docking space, reserve space for the right panel:
|
||||
|
||||
```cpp
|
||||
void EditorManager::UpdateMainDockingSpace() {
|
||||
const ImGuiViewport* viewport = ImGui::GetMainViewport();
|
||||
|
||||
float sidebar_width = 0.0f;
|
||||
if (ui_coordinator_->IsCardSidebarVisible() &&
|
||||
!card_registry_.IsSidebarCollapsed()) {
|
||||
sidebar_width = EditorCardRegistry::GetSidebarWidth();
|
||||
}
|
||||
|
||||
// NEW: Reserve space for right panel
|
||||
float right_panel_width = right_panel_manager_->GetPanelWidth();
|
||||
if (right_panel_manager_->IsPanelExpanded()) {
|
||||
right_panel_width *= right_panel_manager_->GetAnimationProgress();
|
||||
}
|
||||
|
||||
// Docking space occupies the remaining space
|
||||
ImVec2 dockspace_pos(viewport->WorkPos.x + sidebar_width, viewport->WorkPos.y);
|
||||
ImVec2 dockspace_size(
|
||||
viewport->WorkSize.x - sidebar_width - right_panel_width,
|
||||
viewport->WorkSize.y
|
||||
);
|
||||
|
||||
ImGui::SetNextWindowPos(dockspace_pos);
|
||||
ImGui::SetNextWindowSize(dockspace_size);
|
||||
|
||||
// ... rest of docking space setup
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. Overall Layout System
|
||||
|
||||
### 5.1 Layout Zones
|
||||
|
||||
```
|
||||
┌───────────────────────────────────────────────────────────────┐
|
||||
│ Menu Bar (Full Width) [●][🔔][📄][v]│
|
||||
├─┬─────────────────────────────────────────────────────────┬───┤
|
||||
│S│ │ R │
|
||||
│i│ │ i │
|
||||
│d│ Main Docking Space │ g │
|
||||
│e│ │ h │
|
||||
│b│ (Editor Cards) │ t │
|
||||
│a│ │ │
|
||||
│r│ │ P │
|
||||
│ │ │ a │
|
||||
│ │ │ n │
|
||||
│ │ │ e │
|
||||
│ │ │ l │
|
||||
│ │ │ │
|
||||
└─┴─────────────────────────────────────────────────────────┴───┘
|
||||
```
|
||||
|
||||
### 5.2 Responsive Breakpoints
|
||||
|
||||
**Add to EditorManager or new LayoutManager class**:
|
||||
|
||||
```cpp
|
||||
enum class LayoutMode {
|
||||
kCompact, // < 1280px width
|
||||
kNormal, // 1280-1920px
|
||||
kWide // > 1920px
|
||||
};
|
||||
|
||||
LayoutMode GetLayoutMode() const {
|
||||
const ImGuiViewport* viewport = ImGui::GetMainViewport();
|
||||
float width = viewport->WorkSize.x;
|
||||
|
||||
if (width < 1280.0f) return LayoutMode::kCompact;
|
||||
if (width < 1920.0f) return LayoutMode::kNormal;
|
||||
return LayoutMode::kWide;
|
||||
}
|
||||
|
||||
// Adjust UI based on mode:
|
||||
void ApplyLayoutMode(LayoutMode mode) {
|
||||
switch (mode) {
|
||||
case LayoutMode::kCompact:
|
||||
// Auto-collapse sidebar on small screens?
|
||||
// Reduce panel widths
|
||||
right_panel_manager_->SetPanelWidth(PanelType::kAgentChat, 300.0f);
|
||||
break;
|
||||
case LayoutMode::kNormal:
|
||||
right_panel_manager_->SetPanelWidth(PanelType::kAgentChat, 380.0f);
|
||||
break;
|
||||
case LayoutMode::kWide:
|
||||
right_panel_manager_->SetPanelWidth(PanelType::kAgentChat, 480.0f);
|
||||
break;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. Implementation Priority Order
|
||||
|
||||
### Phase 1: Critical Foundation (Week 1)
|
||||
**Priority: P0 - Immediate**
|
||||
|
||||
1. **Extend AgentUITheme** with sidebar/menu colors and sizing
|
||||
- Files: `agent_ui_theme.h`, `agent_ui_theme.cc`
|
||||
- Effort: 2 hours
|
||||
- Benefit: Foundation for all other improvements
|
||||
|
||||
2. **Add CardInfo enabled_condition field**
|
||||
- Files: `editor_card_registry.h`, `editor_card_registry.cc`
|
||||
- Effort: 1 hour
|
||||
- Benefit: Enables disabled state system
|
||||
|
||||
3. **Implement disabled state rendering in sidebar**
|
||||
- Files: `editor_card_registry.cc` (DrawSidebar)
|
||||
- Effort: 3 hours
|
||||
- Benefit: Immediate visual feedback improvement
|
||||
|
||||
### Phase 2: Menu Bar Improvements (Week 1-2)
|
||||
**Priority: P1 - High**
|
||||
|
||||
4. **Add helper methods to MenuOrchestrator**
|
||||
- Files: `menu_orchestrator.h`, `menu_orchestrator.cc`
|
||||
- Methods: `CanUndo()`, `CanRedo()`, `HasActiveSelection()`, etc.
|
||||
- Effort: 4 hours
|
||||
- Benefit: Foundation for proper menu item states
|
||||
|
||||
5. **Apply enabled conditions to all menu items**
|
||||
- Files: `menu_orchestrator.cc` (all Build*Menu methods)
|
||||
- Effort: 6 hours
|
||||
- Benefit: Professional menu behavior
|
||||
|
||||
6. **Improve status cluster layout and animations**
|
||||
- Files: `ui_coordinator.cc` (DrawMenuBarExtras)
|
||||
- Effort: 4 hours
|
||||
- Benefit: Polished status area
|
||||
|
||||
### Phase 3: Sidebar Enhancements (Week 2)
|
||||
**Priority: P1 - High**
|
||||
|
||||
7. **Add notification badge system**
|
||||
- Files: `editor_card_registry.h`, `editor_card_registry.cc`
|
||||
- Effort: 4 hours
|
||||
- Benefit: Visual notification system
|
||||
|
||||
8. **Enhance hover effects and animations**
|
||||
- Files: `editor_card_registry.cc` (DrawSidebar)
|
||||
- Effort: 3 hours
|
||||
- Benefit: More polished interactions
|
||||
|
||||
9. **Implement responsive sidebar sizing**
|
||||
- Files: `editor_card_registry.cc` (DrawSidebar)
|
||||
- Effort: 2 hours
|
||||
- Benefit: Better UX on different screen sizes
|
||||
|
||||
### Phase 4: Right Panel System (Week 2-3)
|
||||
**Priority: P2 - Medium**
|
||||
|
||||
10. **Add slide-in/out animation**
|
||||
- Files: `right_panel_manager.cc`
|
||||
- Effort: 4 hours
|
||||
- Benefit: Smooth panel transitions
|
||||
|
||||
11. **Implement responsive panel widths**
|
||||
- Files: `right_panel_manager.cc`
|
||||
- Effort: 2 hours
|
||||
- Benefit: Adapts to screen size
|
||||
|
||||
12. **Integrate panel with docking space**
|
||||
- Files: `editor_manager.cc`
|
||||
- Effort: 3 hours
|
||||
- Benefit: Proper space reservation
|
||||
|
||||
### Phase 5: Polish and Refinement (Week 3-4)
|
||||
**Priority: P3 - Nice to Have**
|
||||
|
||||
13. **Add category headers/separators to sidebar**
|
||||
- Files: `editor_card_registry.h`, `editor_card_registry.cc`
|
||||
- Effort: 4 hours
|
||||
- Benefit: Better organization for large card lists
|
||||
|
||||
14. **Implement layout breakpoints**
|
||||
- Files: New `layout_manager.h/cc` or in `editor_manager.cc`
|
||||
- Effort: 6 hours
|
||||
- Benefit: Professional responsive design
|
||||
|
||||
15. **Add menu section headers**
|
||||
- Files: `menu_builder.h`, `menu_orchestrator.cc`
|
||||
- Effort: 3 hours
|
||||
- Benefit: Better menu organization
|
||||
|
||||
---
|
||||
|
||||
## 7. New Classes/Methods Summary
|
||||
|
||||
### New Classes
|
||||
- None required (all improvements extend existing classes)
|
||||
|
||||
### New Methods
|
||||
|
||||
**EditorCardRegistry**:
|
||||
- `void DrawCardWithBadge(const CardInfo& card, bool is_active)`
|
||||
- `void DrawSidebarWithSections(size_t session_id, const std::string& category, const std::map<std::string, std::vector<CardInfo>>& card_sections)`
|
||||
|
||||
**MenuOrchestrator**:
|
||||
- `bool HasActiveSelection() const`
|
||||
- `bool CanUndo() const`
|
||||
- `bool CanRedo() const`
|
||||
- `bool HasClipboardData() const`
|
||||
- `bool IsRomModified() const`
|
||||
|
||||
**MenuBuilder**:
|
||||
- `void SeparatorWithSpacing(float height = 4.0f)`
|
||||
- `void SectionHeader(const char* label)`
|
||||
|
||||
**UICoordinator**:
|
||||
- `void DrawDirtyBadge()`
|
||||
|
||||
**RightPanelManager**:
|
||||
- `float GetAnimationProgress() const { return panel_animation_; }`
|
||||
|
||||
**AgentUI namespace**:
|
||||
- `void PushSidebarStyle()`
|
||||
- `void PopSidebarStyle()`
|
||||
- `void RenderSidebarButton(const char* icon, bool is_active, bool is_enabled, int notification_count = 0)`
|
||||
- `void PushMenuSectionStyle()`
|
||||
- `void PopMenuSectionStyle()`
|
||||
- `void RenderDirtyIndicator(bool dirty)`
|
||||
- `void RenderNotificationBadge(int count)`
|
||||
- `void RenderSessionIndicator(const char* session_name, bool is_active)`
|
||||
|
||||
---
|
||||
|
||||
## 8. Testing Strategy
|
||||
|
||||
### Unit Tests
|
||||
1. **CardInfo enabled_condition**: Test cards with/without conditions
|
||||
2. **Badge rendering**: Test badge count display (0, 1-99, 100+)
|
||||
3. **Menu item enabling**: Test all enabled condition helpers
|
||||
4. **Panel animation**: Test animation state transitions
|
||||
|
||||
### Integration Tests
|
||||
1. **Sidebar disabled states**: Open/close ROM, verify card states
|
||||
2. **Menu bar preconditions**: Test menu items with various editor states
|
||||
3. **Right panel animation**: Test panel open/close/switch
|
||||
4. **Responsive layout**: Test at different window sizes (1024, 1920, 2560)
|
||||
|
||||
### Visual Regression Tests
|
||||
1. **Sidebar appearance**: Screenshot tests for active/inactive/disabled states
|
||||
2. **Menu bar appearance**: Screenshot tests for enabled/disabled items
|
||||
3. **Status cluster**: Screenshot tests for dirty/clean, notifications
|
||||
4. **Panel transitions**: Record video of animations for smoothness verification
|
||||
|
||||
---
|
||||
|
||||
## 9. Migration Notes
|
||||
|
||||
### Backward Compatibility
|
||||
- All changes are additive (new fields/methods)
|
||||
- Existing code continues to work
|
||||
- `enabled_condition` is optional (defaults to always enabled)
|
||||
|
||||
### Editor Migration
|
||||
Editors should register cards with enabled conditions:
|
||||
|
||||
```cpp
|
||||
// OLD:
|
||||
card_registry.RegisterCard(session_id, {
|
||||
.card_id = "dungeon.room_selector",
|
||||
.display_name = "Room Selector",
|
||||
.icon = ICON_MD_GRID_VIEW,
|
||||
.category = "Dungeon",
|
||||
.visibility_flag = &show_room_selector_
|
||||
});
|
||||
|
||||
// NEW (with enabled condition):
|
||||
card_registry.RegisterCard(session_id, {
|
||||
.card_id = "dungeon.room_selector",
|
||||
.display_name = "Room Selector",
|
||||
.icon = ICON_MD_GRID_VIEW,
|
||||
.category = "Dungeon",
|
||||
.visibility_flag = &show_room_selector_,
|
||||
.enabled_condition = [this]() { return rom_->is_loaded(); } // NEW
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 10. Future Enhancements
|
||||
|
||||
### Beyond Initial Implementation
|
||||
1. **Command Palette Integration**: Quick access to all cards/menus
|
||||
2. **Keyboard Navigation**: Full keyboard control of sidebar/panels
|
||||
3. **Custom Layouts**: Save/restore sidebar configurations
|
||||
4. **Accessibility**: Screen reader support, high-contrast mode
|
||||
5. **Theming**: Allow users to customize sidebar colors
|
||||
6. **Gesture Support**: Swipe to open/close panels on touch devices
|
||||
|
||||
---
|
||||
|
||||
## 11. Success Metrics
|
||||
|
||||
### Measurable Goals
|
||||
1. **User feedback**: "Feels like VSCode" in user testing
|
||||
2. **Discoverability**: New users find features without documentation
|
||||
3. **Efficiency**: Power users can navigate faster with keyboard
|
||||
4. **Consistency**: Zero hardcoded colors, all theme-based
|
||||
5. **Responsiveness**: Smooth at 60fps on all supported platforms
|
||||
|
||||
### Acceptance Criteria
|
||||
- [ ] All sidebar cards show disabled state when ROM not loaded
|
||||
- [ ] All menu items properly gray out based on preconditions
|
||||
- [ ] Status cluster is visible and informative
|
||||
- [ ] Right panel animates smoothly
|
||||
- [ ] No visual glitches during window resize
|
||||
- [ ] Theme changes apply to all new UI elements
|
||||
- [ ] Keyboard shortcuts work for all major actions
|
||||
|
||||
---
|
||||
|
||||
## Appendix A: Code Snippets Repository
|
||||
|
||||
All code snippets from this document are reference implementations. Actual implementation may vary based on:
|
||||
- Performance profiling results
|
||||
- User feedback
|
||||
- Platform-specific considerations
|
||||
- Integration with existing systems
|
||||
|
||||
## Appendix B: Visual Design References
|
||||
|
||||
### IDE Inspirations
|
||||
- **VSCode**: Sidebar icon layout, status bar clusters
|
||||
- **JetBrains IDEs**: Menu organization, tool window badges
|
||||
- **Sublime Text**: Minimap, distraction-free mode
|
||||
- **Atom**: Theme system, package management UI
|
||||
|
||||
### Material Design Principles
|
||||
- **Elevation**: Use shadows/borders for depth
|
||||
- **Motion**: Meaningful animations (not decorative)
|
||||
- **Color**: Semantic color usage (error=red, success=green)
|
||||
- **Typography**: Clear hierarchy, readable at all sizes
|
||||
Reference in New Issue
Block a user