diff --git a/src/app/gui/editor_layout.cc b/src/app/gui/editor_layout.cc new file mode 100644 index 00000000..b6058830 --- /dev/null +++ b/src/app/gui/editor_layout.cc @@ -0,0 +1,316 @@ +#define IMGUI_DEFINE_MATH_OPERATORS + +#include "app/gui/editor_layout.h" + +#include "absl/strings/str_format.h" +#include "app/gui/icons.h" +#include "app/gui/input.h" +#include "app/gui/ui_helpers.h" +#include "app/gui/widgets/widget_id_registry.h" +#include "imgui/imgui.h" +#include "imgui/imgui_internal.h" + +namespace yaze { +namespace gui { + +// ============================================================================ +// CompactToolbar Implementation +// ============================================================================ + +void CompactToolbar::Begin() { + // Ultra-compact toolbar with no padding waste + ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(6, 3)); + ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(4, 4)); + ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(8, 6)); + + ImGui::BeginGroup(); + in_toolbar_ = true; + button_count_ = 0; +} + +void CompactToolbar::End() { + ImGui::EndGroup(); + ImGui::PopStyleVar(3); + ImGui::Separator(); + in_toolbar_ = false; +} + +void CompactToolbar::BeginModeGroup() { + // Visual grouping with subtle background - taller for better button visibility + ImGui::PushStyleColor(ImGuiCol_ChildBg, ImVec4(0.15f, 0.15f, 0.17f, 0.5f)); + ImGui::BeginChild("##ModeGroup", ImVec2(0, 40), true, + ImGuiWindowFlags_NoScrollbar); +} + +bool CompactToolbar::ModeButton(const char* icon, bool selected, const char* tooltip) { + if (selected) { + ImGui::PushStyleColor(ImGuiCol_Button, GetAccentColor()); + } + + bool clicked = ImGui::Button(icon, ImVec2(32, 32)); // Larger buttons for better usability + + if (selected) { + ImGui::PopStyleColor(); + } + + // Register for test automation + if (ImGui::GetItemID() != 0 && tooltip) { + std::string button_path = absl::StrFormat("ModeButton:%s", tooltip); + WidgetIdRegistry::Instance().RegisterWidget( + button_path, "button", ImGui::GetItemID(), tooltip); + } + + if (tooltip && ImGui::IsItemHovered()) { + ImGui::SetTooltip("%s", tooltip); + } + + ImGui::SameLine(); + button_count_++; + + return clicked; +} + +void CompactToolbar::EndModeGroup() { + ImGui::EndChild(); + ImGui::PopStyleColor(); + ImGui::SameLine(); + AddSeparator(); +} + +void CompactToolbar::AddSeparator() { + ImGui::TextDisabled("|"); + ImGui::SameLine(); +} + +void CompactToolbar::AddRomBadge(uint8_t version, std::function on_upgrade) { + RomVersionBadge(version == 0xFF ? "Vanilla" : + absl::StrFormat("v%d", version).c_str(), + version == 0xFF); + + if (on_upgrade && (version == 0xFF || version < 3)) { + ImGui::SameLine(); + if (ImGui::SmallButton(ICON_MD_UPGRADE)) { + on_upgrade(); + } + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("Upgrade to ZSCustomOverworld v3"); + } + } + + ImGui::SameLine(); + AddSeparator(); +} + +bool CompactToolbar::AddProperty(const char* icon, const char* label, + uint8_t* value, + std::function on_change) { + ImGui::Text("%s", icon); + ImGui::SameLine(); + ImGui::SetNextItemWidth(55); + + bool changed = InputHexByte(label, value); + if (changed && on_change) { + on_change(); + } + + ImGui::SameLine(); + return changed; +} + +bool CompactToolbar::AddProperty(const char* icon, const char* label, + uint16_t* value, + std::function on_change) { + ImGui::Text("%s", icon); + ImGui::SameLine(); + ImGui::SetNextItemWidth(70); + + bool changed = InputHexWord(label, value); + if (changed && on_change) { + on_change(); + } + + ImGui::SameLine(); + return changed; +} + +bool CompactToolbar::AddCombo(const char* icon, int* current, + const char* const items[], int count) { + ImGui::Text("%s", icon); + ImGui::SameLine(); + ImGui::SetNextItemWidth(110); + + bool changed = ImGui::Combo("##combo", current, items, count); + ImGui::SameLine(); + + return changed; +} + +bool CompactToolbar::AddToggle(const char* icon, bool* state, const char* tooltip) { + bool result = ToggleIconButton(icon, icon, state, tooltip); + ImGui::SameLine(); + return result; +} + +bool CompactToolbar::AddAction(const char* icon, const char* tooltip) { + bool clicked = ImGui::SmallButton(icon); + + // Register for test automation + if (ImGui::GetItemID() != 0 && tooltip) { + std::string button_path = absl::StrFormat("ToolbarAction:%s", tooltip); + WidgetIdRegistry::Instance().RegisterWidget( + button_path, "button", ImGui::GetItemID(), tooltip); + } + + if (tooltip && ImGui::IsItemHovered()) { + ImGui::SetTooltip("%s", tooltip); + } + + ImGui::SameLine(); + return clicked; +} + +bool CompactToolbar::BeginCollapsibleSection(const char* label, bool* p_open) { + ImGui::NewLine(); // Start on new line + bool is_open = ImGui::CollapsingHeader(label, ImGuiTreeNodeFlags_None); + if (p_open) *p_open = is_open; + in_section_ = is_open; + return is_open; +} + +void CompactToolbar::EndCollapsibleSection() { + in_section_ = false; +} + +void CompactToolbar::AddV3StatusBadge(uint8_t version, std::function on_settings) { + if (version >= 3 && version != 0xFF) { + StatusBadge("v3 Active", ButtonType::Success); + ImGui::SameLine(); + if (ImGui::SmallButton(ICON_MD_TUNE " Settings") && on_settings) { + on_settings(); + } + } else { + StatusBadge("v3 Available", ButtonType::Default); + ImGui::SameLine(); + if (ImGui::SmallButton(ICON_MD_UPGRADE " Upgrade")) { + ImGui::OpenPopup("UpgradeROMVersion"); + } + } + ImGui::SameLine(); +} + +bool CompactToolbar::AddUsageStatsButton(const char* tooltip) { + bool clicked = ImGui::SmallButton(ICON_MD_ANALYTICS " Usage"); + if (tooltip && ImGui::IsItemHovered()) { + ImGui::SetTooltip("%s", tooltip); + } + ImGui::SameLine(); + return clicked; +} + +// ============================================================================ +// EditorCard Implementation +// ============================================================================ + +EditorCard::EditorCard(const char* title, const char* icon) + : title_(title), icon_(icon ? icon : ""), default_size_(400, 300) {} + +void EditorCard::SetDefaultSize(float width, float height) { + default_size_ = ImVec2(width, height); +} + +void EditorCard::SetPosition(Position pos) { + position_ = pos; +} + +bool EditorCard::Begin(bool* p_open) { + ImGuiWindowFlags flags = ImGuiWindowFlags_None; + + // Set initial position based on position enum + if (first_draw_) { + float display_width = ImGui::GetIO().DisplaySize.x; + float display_height = ImGui::GetIO().DisplaySize.y; + + switch (position_) { + case Position::Right: + ImGui::SetNextWindowPos(ImVec2(display_width - default_size_.x - 10, 30), + ImGuiCond_FirstUseEver); + break; + case Position::Left: + ImGui::SetNextWindowPos(ImVec2(10, 30), ImGuiCond_FirstUseEver); + break; + case Position::Bottom: + ImGui::SetNextWindowPos( + ImVec2(10, display_height - default_size_.y - 10), + ImGuiCond_FirstUseEver); + break; + case Position::Floating: + case Position::Free: + ImGui::SetNextWindowPos( + ImVec2(display_width * 0.5f - default_size_.x * 0.5f, + display_height * 0.3f), + ImGuiCond_FirstUseEver); + break; + } + + ImGui::SetNextWindowSize(default_size_, ImGuiCond_FirstUseEver); + first_draw_ = false; + } + + // Create window title with icon + std::string window_title = icon_.empty() ? title_ : icon_ + " " + title_; + + // Modern card styling + ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 8.0f); + ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(10, 10)); + ImGui::PushStyleColor(ImGuiCol_TitleBg, GetThemeColor(ImGuiCol_TitleBg)); + ImGui::PushStyleColor(ImGuiCol_TitleBgActive, GetAccentColor()); + + bool visible = ImGui::Begin(window_title.c_str(), p_open, flags); + + // Register card window for test automation + if (ImGui::GetCurrentWindow() && ImGui::GetCurrentWindow()->ID != 0) { + std::string card_path = absl::StrFormat("EditorCard:%s", title_.c_str()); + WidgetIdRegistry::Instance().RegisterWidget( + card_path, "window", ImGui::GetCurrentWindow()->ID, + absl::StrFormat("Editor card: %s", title_.c_str())); + } + + return visible; +} + +void EditorCard::End() { + ImGui::End(); + ImGui::PopStyleColor(2); + ImGui::PopStyleVar(2); +} + +// ============================================================================ +// EditorLayout Implementation +// ============================================================================ + +void EditorLayout::Begin() { + toolbar_.Begin(); + in_layout_ = true; +} + +void EditorLayout::End() { + toolbar_.End(); + in_layout_ = false; +} + +void EditorLayout::BeginMainCanvas() { + // Main canvas takes remaining space + ImGui::BeginChild("##MainCanvas", ImVec2(0, 0), false); +} + +void EditorLayout::EndMainCanvas() { + ImGui::EndChild(); +} + +void EditorLayout::RegisterCard(EditorCard* card) { + cards_.push_back(card); +} + +} // namespace gui +} // namespace yaze + diff --git a/src/app/gui/editor_layout.h b/src/app/gui/editor_layout.h new file mode 100644 index 00000000..c79b46d7 --- /dev/null +++ b/src/app/gui/editor_layout.h @@ -0,0 +1,184 @@ +#ifndef YAZE_APP_GUI_EDITOR_LAYOUT_H +#define YAZE_APP_GUI_EDITOR_LAYOUT_H + +#include +#include +#include + +#include "imgui/imgui.h" + +namespace yaze { +namespace gui { + +/** + * @class CompactToolbar + * @brief Ultra-compact toolbar that merges mode buttons with settings + * + * Design Philosophy: + * - Single horizontal bar with everything inline + * - Small icon-only buttons for modes + * - Inline property editing (InputHex with scroll) + * - No wasted vertical space + * - Beautiful, modern appearance + * + * Layout: [Mode Icons] | [ROM Badge] [World] [GFX] [Pal] [Spr] ... | [Quick Actions] + */ +class CompactToolbar { + public: + CompactToolbar() = default; + + // Begin the toolbar + void Begin(); + + // End the toolbar + void End(); + + // Add mode button group + void BeginModeGroup(); + bool ModeButton(const char* icon, bool selected, const char* tooltip); + void EndModeGroup(); + + // Add separator + void AddSeparator(); + + // Add ROM version badge + void AddRomBadge(uint8_t version, std::function on_upgrade = nullptr); + + // Add quick property (inline hex editing) + bool AddProperty(const char* icon, const char* label, uint8_t* value, + std::function on_change = nullptr); + bool AddProperty(const char* icon, const char* label, uint16_t* value, + std::function on_change = nullptr); + + // Add combo selector + bool AddCombo(const char* icon, int* current, const char* const items[], int count); + + // Add toggle button + bool AddToggle(const char* icon, bool* state, const char* tooltip); + + // Add action button + bool AddAction(const char* icon, const char* tooltip); + + // Add collapsible settings section + bool BeginCollapsibleSection(const char* label, bool* p_open); + void EndCollapsibleSection(); + + // Add v3 settings indicator + void AddV3StatusBadge(uint8_t version, std::function on_settings); + + // Add usage statistics button + bool AddUsageStatsButton(const char* tooltip); + + // Get button count for widget registration + int GetButtonCount() const { return button_count_; } + + private: + bool in_toolbar_ = false; + bool in_section_ = false; + int button_count_ = 0; +}; + +/** + * @class EditorCard + * @brief Draggable, dockable card for editor sub-windows + * + * Replaces traditional child windows with modern cards that can be: + * - Dragged and positioned freely + * - Docked to edges (optional) + * - Minimized to title bar + * - Resized responsively + * - Themed beautifully + * + * Usage: + * ```cpp + * EditorCard tile_card("Tile Selector", ICON_MD_GRID_VIEW); + * tile_card.SetDefaultSize(300, 400); + * tile_card.SetPosition(CardPosition::Right); + * + * if (tile_card.Begin()) { + * // Draw tile selector content + * tile_card.End(); + * } + * ``` + */ +class EditorCard { + public: + enum class Position { + Free, // Floating window + Right, // Docked to right side + Left, // Docked to left side + Bottom, // Docked to bottom + Floating, // Floating but position saved + }; + + EditorCard(const char* title, const char* icon = nullptr); + + // Set card properties + void SetDefaultSize(float width, float height); + void SetPosition(Position pos); + void SetMinimizable(bool minimizable) { minimizable_ = minimizable; } + void SetClosable(bool closable) { closable_ = closable; } + + // Begin drawing the card + bool Begin(bool* p_open = nullptr); + + // End drawing + void End(); + + // Minimize/maximize + void SetMinimized(bool minimized) { minimized_ = minimized; } + bool IsMinimized() const { return minimized_; } + + private: + std::string title_; + std::string icon_; + ImVec2 default_size_; + Position position_ = Position::Free; + bool minimizable_ = true; + bool closable_ = true; + bool minimized_ = false; + bool first_draw_ = true; +}; + +/** + * @class EditorLayout + * @brief Modern layout manager for editor components + * + * Manages the overall editor layout with: + * - Compact toolbar at top + * - Main canvas in center + * - Floating/docked cards for tools + * - No redundant headers + * - Responsive sizing + */ +class EditorLayout { + public: + EditorLayout() = default; + + // Begin the editor layout + void Begin(); + + // End the editor layout + void End(); + + // Get toolbar reference + CompactToolbar& GetToolbar() { return toolbar_; } + + // Begin main canvas area + void BeginMainCanvas(); + void EndMainCanvas(); + + // Register a card (for layout management) + void RegisterCard(EditorCard* card); + + private: + CompactToolbar toolbar_; + std::vector cards_; + bool in_layout_ = false; +}; + +} // namespace gui +} // namespace yaze + +#endif // YAZE_APP_GUI_EDITOR_LAYOUT_H + diff --git a/src/app/gui/gui_library.cmake b/src/app/gui/gui_library.cmake index 019a5a88..74ed1a8c 100644 --- a/src/app/gui/gui_library.cmake +++ b/src/app/gui/gui_library.cmake @@ -18,6 +18,7 @@ set( app/gui/ui_helpers.cc app/gui/toolset.cc app/gui/settings_bar.cc + app/gui/editor_layout.cc # Canvas system components app/gui/canvas/canvas_modals.cc app/gui/canvas/canvas_context_menu.cc diff --git a/src/app/gui/ui_helpers.cc b/src/app/gui/ui_helpers.cc index 1ce5e817..d2bb5d26 100644 --- a/src/app/gui/ui_helpers.cc +++ b/src/app/gui/ui_helpers.cc @@ -1,6 +1,5 @@ #include "app/gui/ui_helpers.h" -#include "absl/strings/str_format.h" #include "app/gui/color.h" #include "app/gui/icons.h" #include "app/gui/theme_manager.h" @@ -43,21 +42,25 @@ ImVec4 GetAccentColor() { return ConvertColorToImVec4(theme.accent); } -// Entity/Map marker colors (normalized RGB + proper alpha) +// Entity/Map marker colors (vibrant with good visibility) ImVec4 GetEntranceColor() { - return ImVec4(1.0f, 1.0f, 0.0f, 0.39f); // Yellow, 100/255 alpha + // Bright yellow with strong visibility + return ImVec4(1.0f, 0.9f, 0.0f, 0.85f); // Yellow-gold, high visibility } ImVec4 GetExitColor() { - return ImVec4(1.0f, 1.0f, 1.0f, 0.59f); // White, 150/255 alpha + // Bright cyan-white for contrast + return ImVec4(0.9f, 1.0f, 1.0f, 0.85f); // Cyan-white, high visibility } ImVec4 GetItemColor() { - return ImVec4(1.0f, 0.0f, 0.0f, 0.59f); // Red, 150/255 alpha + // Vibrant red for items + return ImVec4(1.0f, 0.2f, 0.2f, 0.85f); // Bright red, high visibility } ImVec4 GetSpriteColor() { - return ImVec4(1.0f, 0.0f, 1.0f, 0.59f); // Magenta, 150/255 alpha + // Bright magenta for sprites + return ImVec4(1.0f, 0.3f, 1.0f, 0.85f); // Bright magenta, high visibility } ImVec4 GetSelectedColor() {