From 6daf0adf8422b729563904a1b42accede2851c88 Mon Sep 17 00:00:00 2001 From: scawful Date: Sun, 5 Oct 2025 17:19:02 -0400 Subject: [PATCH] refactor: Rename CompactToolbar to Toolset and Enhance Toolbar Functionality - Renamed CompactToolbar class to Toolset for improved clarity and consistency in naming. - Introduced WidgetMeasurement for tracking widget dimensions, enabling debugging and test automation. - Updated toolbar methods to include measurement functionality, logging overflow warnings, and improved layout handling. - Removed the deprecated SettingsBar and Toolset classes to streamline the codebase. - Adjusted CMake configuration to reflect the new file structure and included widget measurement source files. --- src/app/gui/editor_layout.cc | 92 +++++++++----- src/app/gui/editor_layout.h | 11 +- src/app/gui/gui_library.cmake | 3 +- src/app/gui/settings_bar.cc | 157 ----------------------- src/app/gui/settings_bar.h | 111 ----------------- src/app/gui/toolset.cc | 199 ------------------------------ src/app/gui/toolset.h | 111 ----------------- src/app/gui/widget_measurement.cc | 140 +++++++++++++++++++++ src/app/gui/widget_measurement.h | 119 ++++++++++++++++++ 9 files changed, 329 insertions(+), 614 deletions(-) delete mode 100644 src/app/gui/settings_bar.cc delete mode 100644 src/app/gui/settings_bar.h delete mode 100644 src/app/gui/toolset.cc delete mode 100644 src/app/gui/toolset.h create mode 100644 src/app/gui/widget_measurement.cc create mode 100644 src/app/gui/widget_measurement.h diff --git a/src/app/gui/editor_layout.cc b/src/app/gui/editor_layout.cc index b6058830..d3beb095 100644 --- a/src/app/gui/editor_layout.cc +++ b/src/app/gui/editor_layout.cc @@ -6,6 +6,7 @@ #include "app/gui/icons.h" #include "app/gui/input.h" #include "app/gui/ui_helpers.h" +#include "app/gui/widget_measurement.h" #include "app/gui/widgets/widget_id_registry.h" #include "imgui/imgui.h" #include "imgui/imgui_internal.h" @@ -14,35 +15,60 @@ namespace yaze { namespace gui { // ============================================================================ -// CompactToolbar Implementation +// Toolset Implementation // ============================================================================ -void CompactToolbar::Begin() { +void Toolset::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(); + // Don't use BeginGroup - it causes stretching. Just use direct layout. in_toolbar_ = true; button_count_ = 0; + current_line_width_ = 0.0f; + + // Begin measurement for debugging and test automation + WidgetMeasurement::Instance().BeginToolbarMeasurement("OverworldToolbar"); } -void CompactToolbar::End() { - ImGui::EndGroup(); +void Toolset::End() { + // End the current line + ImGui::NewLine(); + + // End measurement and check for overflow + WidgetMeasurement::Instance().EndToolbarMeasurement(); + + float toolbar_width = WidgetMeasurement::Instance().GetToolbarWidth("OverworldToolbar"); + float available_width = ImGui::GetContentRegionAvail().x; + + // Log warning if toolbar overflows (for debugging) + if (toolbar_width > available_width && toolbar_width > 0) { + // Only log once per second to avoid spam + static float last_warning_time = 0.0f; + float current_time = ImGui::GetTime(); + if (current_time - last_warning_time > 1.0f) { + ImGui::SetTooltip("⚠️ Toolbar overflow: %.0fpx / %.0fpx available", + toolbar_width, available_width); + last_warning_time = current_time; + } + } + ImGui::PopStyleVar(3); ImGui::Separator(); in_toolbar_ = false; + current_line_width_ = 0.0f; } -void CompactToolbar::BeginModeGroup() { +void Toolset::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) { +bool Toolset::ModeButton(const char* icon, bool selected, const char* tooltip) { if (selected) { ImGui::PushStyleColor(ImGuiCol_Button, GetAccentColor()); } @@ -53,6 +79,10 @@ bool CompactToolbar::ModeButton(const char* icon, bool selected, const char* too ImGui::PopStyleColor(); } + // Measure this widget + std::string widget_id = tooltip ? tooltip : absl::StrFormat("Button_%d", button_count_); + WidgetMeasurement::Instance().MeasureLastItem(widget_id, "mode_button"); + // Register for test automation if (ImGui::GetItemID() != 0 && tooltip) { std::string button_path = absl::StrFormat("ModeButton:%s", tooltip); @@ -70,25 +100,26 @@ bool CompactToolbar::ModeButton(const char* icon, bool selected, const char* too return clicked; } -void CompactToolbar::EndModeGroup() { +void Toolset::EndModeGroup() { ImGui::EndChild(); ImGui::PopStyleColor(); ImGui::SameLine(); AddSeparator(); } -void CompactToolbar::AddSeparator() { - ImGui::TextDisabled("|"); +void Toolset::AddSeparator() { + // Use a proper separator that doesn't stretch + ImGui::SeparatorEx(ImGuiSeparatorFlags_Vertical); ImGui::SameLine(); } -void CompactToolbar::AddRomBadge(uint8_t version, std::function on_upgrade) { +void Toolset::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(); + ImGui::SameLine(0, 2); // Tighter spacing if (ImGui::SmallButton(ICON_MD_UPGRADE)) { on_upgrade(); } @@ -98,12 +129,11 @@ void CompactToolbar::AddRomBadge(uint8_t version, std::function on_upgra } ImGui::SameLine(); - AddSeparator(); } -bool CompactToolbar::AddProperty(const char* icon, const char* label, - uint8_t* value, - std::function on_change) { +bool Toolset::AddProperty(const char* icon, const char* label, + uint8_t* value, + std::function on_change) { ImGui::Text("%s", icon); ImGui::SameLine(); ImGui::SetNextItemWidth(55); @@ -117,9 +147,9 @@ bool CompactToolbar::AddProperty(const char* icon, const char* label, return changed; } -bool CompactToolbar::AddProperty(const char* icon, const char* label, - uint16_t* value, - std::function on_change) { +bool Toolset::AddProperty(const char* icon, const char* label, + uint16_t* value, + std::function on_change) { ImGui::Text("%s", icon); ImGui::SameLine(); ImGui::SetNextItemWidth(70); @@ -133,25 +163,29 @@ bool CompactToolbar::AddProperty(const char* icon, const char* label, return changed; } -bool CompactToolbar::AddCombo(const char* icon, int* current, - const char* const items[], int count) { +bool Toolset::AddCombo(const char* icon, int* current, + const char* const items[], int count) { ImGui::Text("%s", icon); - ImGui::SameLine(); - ImGui::SetNextItemWidth(110); + WidgetMeasurement::Instance().MeasureLastItem("combo_icon", "text"); + + ImGui::SameLine(0, 2); // Reduce spacing between icon and combo + ImGui::SetNextItemWidth(100); // Slightly narrower for better fit bool changed = ImGui::Combo("##combo", current, items, count); + WidgetMeasurement::Instance().MeasureLastItem("combo_selector", "combo"); + ImGui::SameLine(); return changed; } -bool CompactToolbar::AddToggle(const char* icon, bool* state, const char* tooltip) { +bool Toolset::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 Toolset::AddAction(const char* icon, const char* tooltip) { bool clicked = ImGui::SmallButton(icon); // Register for test automation @@ -169,7 +203,7 @@ bool CompactToolbar::AddAction(const char* icon, const char* tooltip) { return clicked; } -bool CompactToolbar::BeginCollapsibleSection(const char* label, bool* p_open) { +bool Toolset::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; @@ -177,11 +211,11 @@ bool CompactToolbar::BeginCollapsibleSection(const char* label, bool* p_open) { return is_open; } -void CompactToolbar::EndCollapsibleSection() { +void Toolset::EndCollapsibleSection() { in_section_ = false; } -void CompactToolbar::AddV3StatusBadge(uint8_t version, std::function on_settings) { +void Toolset::AddV3StatusBadge(uint8_t version, std::function on_settings) { if (version >= 3 && version != 0xFF) { StatusBadge("v3 Active", ButtonType::Success); ImGui::SameLine(); @@ -198,7 +232,7 @@ void CompactToolbar::AddV3StatusBadge(uint8_t version, std::function on_ ImGui::SameLine(); } -bool CompactToolbar::AddUsageStatsButton(const char* tooltip) { +bool Toolset::AddUsageStatsButton(const char* tooltip) { bool clicked = ImGui::SmallButton(ICON_MD_ANALYTICS " Usage"); if (tooltip && ImGui::IsItemHovered()) { ImGui::SetTooltip("%s", tooltip); diff --git a/src/app/gui/editor_layout.h b/src/app/gui/editor_layout.h index c79b46d7..50051944 100644 --- a/src/app/gui/editor_layout.h +++ b/src/app/gui/editor_layout.h @@ -11,7 +11,7 @@ namespace yaze { namespace gui { /** - * @class CompactToolbar + * @class Toolset * @brief Ultra-compact toolbar that merges mode buttons with settings * * Design Philosophy: @@ -23,9 +23,9 @@ namespace gui { * * Layout: [Mode Icons] | [ROM Badge] [World] [GFX] [Pal] [Spr] ... | [Quick Actions] */ -class CompactToolbar { +class Toolset { public: - CompactToolbar() = default; + Toolset() = default; // Begin the toolbar void Begin(); @@ -76,6 +76,7 @@ class CompactToolbar { bool in_toolbar_ = false; bool in_section_ = false; int button_count_ = 0; + float current_line_width_ = 0.0f; }; /** @@ -162,7 +163,7 @@ class EditorLayout { void End(); // Get toolbar reference - CompactToolbar& GetToolbar() { return toolbar_; } + Toolset& GetToolbar() { return toolbar_; } // Begin main canvas area void BeginMainCanvas(); @@ -172,7 +173,7 @@ class EditorLayout { void RegisterCard(EditorCard* card); private: - CompactToolbar toolbar_; + Toolset toolbar_; std::vector cards_; bool in_layout_ = false; }; diff --git a/src/app/gui/gui_library.cmake b/src/app/gui/gui_library.cmake index 74ed1a8c..d25e1f61 100644 --- a/src/app/gui/gui_library.cmake +++ b/src/app/gui/gui_library.cmake @@ -16,9 +16,8 @@ set( app/gui/widgets/widget_auto_register.cc app/gui/widgets/widget_state_capture.cc app/gui/ui_helpers.cc - app/gui/toolset.cc - app/gui/settings_bar.cc app/gui/editor_layout.cc + app/gui/widget_measurement.cc # Canvas system components app/gui/canvas/canvas_modals.cc app/gui/canvas/canvas_context_menu.cc diff --git a/src/app/gui/settings_bar.cc b/src/app/gui/settings_bar.cc deleted file mode 100644 index 0f221e44..00000000 --- a/src/app/gui/settings_bar.cc +++ /dev/null @@ -1,157 +0,0 @@ -#include "app/gui/settings_bar.h" - -#include "absl/strings/str_format.h" -#include "app/gui/icons.h" -#include "app/gui/input.h" -#include "app/gui/ui_helpers.h" -#include "imgui/imgui.h" - -namespace yaze { -namespace gui { - -void SettingsBar::SetRomVersion(uint8_t version) { - rom_version_ = version; -} - -void SettingsBar::BeginDraw() { - // Compact, modern settings bar - ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(12, 6)); - ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(8, 4)); - - // Single row layout using columns for alignment - ImGui::BeginGroup(); - in_settings_bar_ = true; - current_column_ = 0; -} - -void SettingsBar::EndDraw() { - ImGui::EndGroup(); - ImGui::PopStyleVar(2); - ImGui::Separator(); - in_settings_bar_ = false; -} - -void SettingsBar::AddVersionBadge(std::function on_upgrade) { - if (IsVanilla()) { - RomVersionBadge("Vanilla ROM", true); - - if (on_upgrade) { - ImGui::SameLine(); - if (IconButton(ICON_MD_UPGRADE, "Upgrade to v3", ImVec2(0, 0))) { - on_upgrade(); - } - } - } else { - std::string version_text = absl::StrFormat("v%d", rom_version_); - RomVersionBadge(version_text.c_str(), false); - - if (rom_version_ < 3 && on_upgrade) { - ImGui::SameLine(); - if (IconButton(ICON_MD_UPGRADE, "Upgrade to v3", ImVec2(0, 0))) { - on_upgrade(); - } - } - } - - ImGui::SameLine(); - AddSeparator(); -} - -void SettingsBar::AddWorldSelector(int* current_world) { - ImGui::Text(ICON_MD_PUBLIC); - ImGui::SameLine(); - ImGui::SetNextItemWidth(120); - - const char* worlds[] = {"Light World", "Dark World", "Extra World"}; - ImGui::Combo("##world", current_world, worlds, 3); - - ImGui::SameLine(); -} - -void SettingsBar::AddHexByteInput(const char* icon, const char* label, - uint8_t* value, - std::function on_change) { - ImGui::Text("%s", icon); - ImGui::SameLine(); - ImGui::SetNextItemWidth(60); - - if (InputHexByte(label, value)) { - if (on_change) on_change(); - } - - ImGui::SameLine(); -} - -void SettingsBar::AddHexWordInput(const char* icon, const char* label, - uint16_t* value, - std::function on_change) { - ImGui::Text("%s", icon); - ImGui::SameLine(); - ImGui::SetNextItemWidth(80); - - if (InputHexWord(label, value)) { - if (on_change) on_change(); - } - - ImGui::SameLine(); -} - -void SettingsBar::AddCheckbox(const char* icon, const char* label, bool* value, - std::function on_change) { - ImGui::Text("%s", icon); - ImGui::SameLine(); - - if (ImGui::Checkbox(label, value)) { - if (on_change) on_change(); - } - - ImGui::SameLine(); -} - -void SettingsBar::AddCombo(const char* icon, const char* label, int* current, - const char* const items[], int count, - std::function on_change) { - ImGui::Text("%s", icon); - ImGui::SameLine(); - ImGui::SetNextItemWidth(120); - - if (ImGui::Combo(label, current, items, count)) { - if (on_change) on_change(); - } - - ImGui::SameLine(); -} - -void SettingsBar::AddAreaSizeSelector(int* area_size, - std::function on_change) { - if (!IsV3()) return; - - const char* sizes[] = {"Small (1x1)", "Large (2x2)", "Wide (2x1)", "Tall (1x2)"}; - - ImGui::Text(ICON_MD_ASPECT_RATIO); - ImGui::SameLine(); - ImGui::SetNextItemWidth(110); - - if (ImGui::Combo("##areasize", area_size, sizes, 4)) { - if (on_change) on_change(); - } - - ImGui::SameLine(); -} - -void SettingsBar::AddButton(const char* icon, const char* label, - std::function callback) { - if (IconButton(icon, label)) { - if (callback) callback(); - } - - ImGui::SameLine(); -} - -void SettingsBar::AddSeparator() { - ImGui::TextDisabled("|"); - ImGui::SameLine(); -} - -} // namespace gui -} // namespace yaze diff --git a/src/app/gui/settings_bar.h b/src/app/gui/settings_bar.h deleted file mode 100644 index f94157e9..00000000 --- a/src/app/gui/settings_bar.h +++ /dev/null @@ -1,111 +0,0 @@ -#ifndef YAZE_APP_GUI_SETTINGS_BAR_H -#define YAZE_APP_GUI_SETTINGS_BAR_H - -#include -#include -#include - -#include "imgui/imgui.h" - -namespace yaze { -namespace gui { - -/** - * @class SettingsBar - * @brief Modern, compact settings bar for editor properties - * - * A horizontal bar that consolidates mode selection, property editing, - * and quick actions into a single, space-efficient component. - * - * Design Philosophy: - * - Inline property editing with InputHex widgets - * - Compact layout saves vertical space - * - Visual ROM version indicator - * - Theme-aware styling - * - Responsive to different ROM versions - * - * Features: - * - World selector - * - Map properties (GFX, Palette, Sprites, Message ID) - * - Version-specific properties (v2: Main Palette, v3: Animated GFX, Area Size) - * - Visual ROM version badge - * - Upgrade prompts for vanilla ROMs - * - Mosaic effect toggle - * - * Usage: - * ```cpp - * SettingsBar settings; - * settings.SetRomVersion(asm_version); - * settings.BeginDraw(); - * - * settings.AddWorldSelector(¤t_world); - * settings.AddHexInput("Graphics", &map->area_graphics); - * settings.AddHexInput("Palette", &map->area_palette); - * - * if (settings.IsV3()) { - * settings.AddAreaSizeSelector(&map->area_size); - * } - * - * settings.EndDraw(); - * ``` - */ -class SettingsBar { - public: - SettingsBar() = default; - - // Set ROM version to enable version-specific features - void SetRomVersion(uint8_t version); - - // Check ROM version - bool IsVanilla() const { return rom_version_ == 0xFF; } - bool IsV2() const { return rom_version_ >= 2 && rom_version_ != 0xFF; } - bool IsV3() const { return rom_version_ >= 3 && rom_version_ != 0xFF; } - - // Begin drawing the settings bar - void BeginDraw(); - - // End drawing the settings bar - void EndDraw(); - - // Add ROM version badge - void AddVersionBadge(std::function on_upgrade = nullptr); - - // Add world selector dropdown - void AddWorldSelector(int* current_world); - - // Add hex input for a property - void AddHexByteInput(const char* icon, const char* label, uint8_t* value, - std::function on_change = nullptr); - void AddHexWordInput(const char* icon, const char* label, uint16_t* value, - std::function on_change = nullptr); - - // Add checkbox - void AddCheckbox(const char* icon, const char* label, bool* value, - std::function on_change = nullptr); - - // Add combo box - void AddCombo(const char* icon, const char* label, int* current, - const char* const items[], int count, - std::function on_change = nullptr); - - // Add area size selector (v3+ only) - void AddAreaSizeSelector(int* area_size, - std::function on_change = nullptr); - - // Add custom button - void AddButton(const char* icon, const char* label, - std::function callback); - - // Add spacing - void AddSeparator(); - - private: - uint8_t rom_version_ = 0xFF; - int current_column_ = 0; - bool in_settings_bar_ = false; -}; - -} // namespace gui -} // namespace yaze - -#endif // YAZE_APP_GUI_SETTINGS_BAR_H diff --git a/src/app/gui/toolset.cc b/src/app/gui/toolset.cc deleted file mode 100644 index 186f6eff..00000000 --- a/src/app/gui/toolset.cc +++ /dev/null @@ -1,199 +0,0 @@ -#include "app/gui/toolset.h" - -#include "absl/strings/str_format.h" -#include "app/gui/icons.h" -#include "app/gui/ui_helpers.h" -#include "imgui/imgui.h" - -namespace yaze { -namespace gui { - -void Toolset::AddTool(const std::string& id, const char* icon, - const char* shortcut, std::function callback, - const char* tooltip) { - Tool tool; - tool.id = id; - tool.icon = icon; - tool.shortcut = shortcut; - tool.callback = callback; - - if (tooltip) { - tool.tooltip = std::string(icon) + " " + tooltip; - if (shortcut && strlen(shortcut) > 0) { - tool.tooltip += " (" + std::string(shortcut) + ")"; - } - } else { - tool.tooltip = id; - if (shortcut && strlen(shortcut) > 0) { - tool.tooltip += " (" + std::string(shortcut) + ")"; - } - } - - tools_.push_back(tool); -} - -void Toolset::AddSeparator() { - if (!tools_.empty()) { - tools_.back().separator_after = true; - } -} - -void Toolset::SetSelected(const std::string& id) { - selected_ = id; -} - -bool Toolset::Draw() { - bool clicked = false; - - // Calculate columns based on mode - int columns = max_columns_; - if (columns == 0) { - // Auto-fit to available width - float button_width = compact_mode_ ? 32.0f : 80.0f; - columns = static_cast(ImGui::GetContentRegionAvail().x / button_width); - if (columns < 1) columns = 1; - if (columns > static_cast(tools_.size())) - columns = static_cast(tools_.size()); - } - - // Modern toolset with reduced padding - ImGui::PushStyleVar(ImGuiStyleVar_CellPadding, ImVec2(2, 2)); - ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(4, 4)); - - if (ImGui::BeginTable("##Toolset", columns, - ImGuiTableFlags_SizingFixedFit | - ImGuiTableFlags_BordersInnerV)) { - - for (const auto& tool : tools_) { - if (!tool.enabled) continue; - - ImGui::TableNextColumn(); - - bool is_selected = (tool.id == selected_); - DrawTool(tool, is_selected); - - if (tool.separator_after) { - ImGui::TableNextColumn(); - ImGui::Dummy(ImVec2(1, 1)); // Small separator space - } - } - - ImGui::EndTable(); - } - - ImGui::PopStyleVar(2); - - return clicked; -} - -void Toolset::DrawTool(const Tool& tool, bool is_selected) { - ImVec2 button_size = compact_mode_ ? ImVec2(28, 28) : ImVec2(0, 0); - - if (is_selected) { - ImGui::PushStyleColor(ImGuiCol_Button, GetAccentColor()); - ImGui::PushStyleColor(ImGuiCol_ButtonHovered, - ImVec4(GetAccentColor().x * 1.1f, GetAccentColor().y * 1.1f, - GetAccentColor().z * 1.1f, GetAccentColor().w)); - } - - bool clicked = false; - if (compact_mode_) { - clicked = ImGui::Button(tool.icon.c_str(), button_size); - } else { - std::string label = tool.icon + " " + tool.id; - clicked = ImGui::Button(label.c_str(), button_size); - } - - if (is_selected) { - ImGui::PopStyleColor(2); - } - - if (clicked && tool.callback) { - tool.callback(); - selected_ = tool.id; - } - - if (ImGui::IsItemHovered() && !tool.tooltip.empty()) { - ImGui::SetTooltip("%s", tool.tooltip.c_str()); - } -} - -void Toolset::Clear() { - tools_.clear(); - selected_.clear(); -} - -// ============================================================================ -// Editor Toolset Factories -// ============================================================================ - -namespace EditorToolset { - -Toolset CreateOverworldToolset() { - Toolset toolset; - toolset.SetCompactMode(true); - - // Editing tools - toolset.AddTool("Pan", ICON_MD_PAN_TOOL_ALT, "1", nullptr, - "Pan - Middle click and drag"); - toolset.AddTool("Draw", ICON_MD_DRAW, "2", nullptr, - "Draw Tile"); - toolset.AddTool("Entrances", ICON_MD_DOOR_FRONT, "3", nullptr, - "Entrances"); - toolset.AddTool("Exits", ICON_MD_DOOR_BACK, "4", nullptr, - "Exits"); - toolset.AddTool("Items", ICON_MD_GRASS, "5", nullptr, - "Items"); - toolset.AddTool("Sprites", ICON_MD_PEST_CONTROL_RODENT, "6", nullptr, - "Sprites"); - toolset.AddTool("Transports", ICON_MD_ADD_LOCATION, "7", nullptr, - "Transports"); - toolset.AddTool("Music", ICON_MD_MUSIC_NOTE, "8", nullptr, - "Music"); - - toolset.AddSeparator(); - - // View controls - toolset.AddTool("ZoomOut", ICON_MD_ZOOM_OUT, "", nullptr, - "Zoom Out"); - toolset.AddTool("ZoomIn", ICON_MD_ZOOM_IN, "", nullptr, - "Zoom In"); - toolset.AddTool("Fullscreen", ICON_MD_OPEN_IN_FULL, "F11", nullptr, - "Fullscreen Canvas"); - - toolset.AddSeparator(); - - // Quick access - toolset.AddTool("Tile16", ICON_MD_GRID_VIEW, "Ctrl+T", nullptr, - "Tile16 Editor"); - toolset.AddTool("Copy", ICON_MD_CONTENT_COPY, "", nullptr, - "Copy Map"); - - return toolset; -} - -Toolset CreateDungeonToolset() { - Toolset toolset; - toolset.SetCompactMode(true); - - toolset.AddTool("Pan", ICON_MD_PAN_TOOL_ALT, "1", nullptr, - "Pan"); - toolset.AddTool("Draw", ICON_MD_DRAW, "2", nullptr, - "Draw Object"); - toolset.AddTool("Select", ICON_MD_SELECT_ALL, "3", nullptr, - "Select"); - - toolset.AddSeparator(); - - toolset.AddTool("ZoomOut", ICON_MD_ZOOM_OUT, "", nullptr, - "Zoom Out"); - toolset.AddTool("ZoomIn", ICON_MD_ZOOM_IN, "", nullptr, - "Zoom In"); - - return toolset; -} - -} // namespace EditorToolset - -} // namespace gui -} // namespace yaze diff --git a/src/app/gui/toolset.h b/src/app/gui/toolset.h deleted file mode 100644 index 372223cb..00000000 --- a/src/app/gui/toolset.h +++ /dev/null @@ -1,111 +0,0 @@ -#ifndef YAZE_APP_GUI_TOOLSET_H -#define YAZE_APP_GUI_TOOLSET_H - -#include -#include -#include - -#include "imgui/imgui.h" - -namespace yaze { -namespace gui { - -/** - * @class Toolset - * @brief Modern, space-efficient toolset for editor mode switching - * - * A toolset is a horizontal bar of icon buttons for switching between - * editor modes (Pan, Draw, Select, etc.). This provides a consistent, - * beautiful interface across all editors. - * - * Features: - * - Compact, icon-based design - * - Visual selection indicator - * - Keyboard shortcut hints - * - Automatic tooltips - * - Theme-aware styling - * - Separators for logical grouping - * - * Usage: - * ```cpp - * Toolset toolset; - * toolset.AddTool("Pan", ICON_MD_PAN_TOOL, "1", []() { mode = PAN; }); - * toolset.AddTool("Draw", ICON_MD_DRAW, "2", []() { mode = DRAW; }); - * toolset.AddSeparator(); - * toolset.AddTool("Zoom+", ICON_MD_ZOOM_IN, "", []() { ZoomIn(); }); - * - * if (toolset.Draw()) { - * // A tool was clicked - * } - * ``` - */ -class Toolset { - public: - struct Tool { - std::string id; - std::string icon; - std::string tooltip; - std::string shortcut; - std::function callback; - bool enabled = true; - bool separator_after = false; - }; - - Toolset() = default; - - // Add a tool to the toolset - void AddTool(const std::string& id, const char* icon, - const char* shortcut, std::function callback, - const char* tooltip = nullptr); - - // Add a separator for visual grouping - void AddSeparator(); - - // Set the currently selected tool - void SetSelected(const std::string& id); - - // Get the currently selected tool ID - const std::string& GetSelected() const { return selected_; } - - // Draw the toolset (returns true if a tool was clicked) - bool Draw(); - - // Compact mode (smaller buttons, no labels) - void SetCompactMode(bool compact) { compact_mode_ = compact; } - - // Set the number of columns (0 = auto-fit to window width) - void SetMaxColumns(int columns) { max_columns_ = columns; } - - // Clear all tools - void Clear(); - - private: - void DrawTool(const Tool& tool, bool is_selected); - - std::vector tools_; - std::string selected_; - bool compact_mode_ = true; - int max_columns_ = 0; // 0 = auto-fit -}; - -/** - * @namespace EditorToolset - * @brief Helper functions for creating common editor toolsets - */ -namespace EditorToolset { - -// Create standard editing mode toolset -Toolset CreateStandardToolset(); - -// Create overworld-specific toolset -Toolset CreateOverworldToolset(); - -// Create dungeon-specific toolset -Toolset CreateDungeonToolset(); - -} // namespace EditorToolset - -} // namespace gui -} // namespace yaze - -#endif // YAZE_APP_GUI_TOOLSET_H diff --git a/src/app/gui/widget_measurement.cc b/src/app/gui/widget_measurement.cc new file mode 100644 index 00000000..55300869 --- /dev/null +++ b/src/app/gui/widget_measurement.cc @@ -0,0 +1,140 @@ +#include "app/gui/widget_measurement.h" + +#include "absl/strings/str_format.h" +#include "imgui/imgui.h" +#include "imgui/imgui_internal.h" + +namespace yaze { +namespace gui { + +WidgetMetrics WidgetMeasurement::MeasureLastItem(const std::string& widget_id, + const std::string& type) { + if (!enabled_) { + return WidgetMetrics{}; + } + + WidgetMetrics metrics; + metrics.widget_id = widget_id; + metrics.type = type; + + // Get item rect (bounding box) + metrics.rect_min = ImGui::GetItemRectMin(); + metrics.rect_max = ImGui::GetItemRectMax(); + + // Calculate size + metrics.size = ImVec2(metrics.rect_max.x - metrics.rect_min.x, + metrics.rect_max.y - metrics.rect_min.y); + + // Store position + metrics.position = metrics.rect_min; + + // Get content region + metrics.content_size = ImGui::GetContentRegionAvail(); + + // Get cursor position after item + metrics.cursor_pos_x = ImGui::GetCursorPosX(); + + // Store in current toolbar if active + if (!current_toolbar_id_.empty()) { + toolbar_metrics_[current_toolbar_id_].push_back(metrics); + } + + // Store in frame metrics + frame_metrics_.push_back(metrics); + + return metrics; +} + +void WidgetMeasurement::BeginToolbarMeasurement(const std::string& toolbar_id) { + current_toolbar_id_ = toolbar_id; + current_toolbar_start_x_ = ImGui::GetCursorPosX(); + current_toolbar_width_ = 0.0f; + + // Clear previous measurements for this toolbar + toolbar_metrics_[toolbar_id].clear(); +} + +void WidgetMeasurement::EndToolbarMeasurement() { + if (current_toolbar_id_.empty()) return; + + // Calculate total width from cursor movement + float end_x = ImGui::GetCursorPosX(); + current_toolbar_width_ = end_x - current_toolbar_start_x_; + + // Store the width + toolbar_widths_[current_toolbar_id_] = current_toolbar_width_; + + // Reset state + current_toolbar_id_.clear(); + current_toolbar_width_ = 0.0f; + current_toolbar_start_x_ = 0.0f; +} + +float WidgetMeasurement::GetToolbarWidth(const std::string& toolbar_id) const { + auto it = toolbar_widths_.find(toolbar_id); + if (it != toolbar_widths_.end()) { + return it->second; + } + return 0.0f; +} + +bool WidgetMeasurement::WouldToolbarOverflow(const std::string& toolbar_id, + float available_width) const { + float toolbar_width = GetToolbarWidth(toolbar_id); + return toolbar_width > available_width; +} + +const std::vector& WidgetMeasurement::GetToolbarMetrics( + const std::string& toolbar_id) const { + static const std::vector empty; + auto it = toolbar_metrics_.find(toolbar_id); + if (it != toolbar_metrics_.end()) { + return it->second; + } + return empty; +} + +void WidgetMeasurement::ClearFrame() { + frame_metrics_.clear(); + // Don't clear toolbar metrics - they persist across frames for debugging +} + +std::string WidgetMeasurement::ExportMetricsJSON() const { + std::string json = "{\n \"toolbars\": {\n"; + + bool first_toolbar = true; + for (const auto& [toolbar_id, metrics] : toolbar_metrics_) { + if (!first_toolbar) json += ",\n"; + first_toolbar = false; + + json += absl::StrFormat(" \"%s\": {\n", toolbar_id); + json += absl::StrFormat(" \"total_width\": %.1f,\n", + GetToolbarWidth(toolbar_id)); + json += " \"widgets\": [\n"; + + bool first_widget = true; + for (const auto& metric : metrics) { + if (!first_widget) json += ",\n"; + first_widget = false; + + json += " {\n"; + json += absl::StrFormat(" \"id\": \"%s\",\n", metric.widget_id); + json += absl::StrFormat(" \"type\": \"%s\",\n", metric.type); + json += absl::StrFormat(" \"width\": %.1f,\n", metric.size.x); + json += absl::StrFormat(" \"height\": %.1f,\n", metric.size.y); + json += absl::StrFormat(" \"x\": %.1f,\n", metric.position.x); + json += absl::StrFormat(" \"y\": %.1f\n", metric.position.y); + json += " }"; + } + + json += "\n ]\n"; + json += " }"; + } + + json += "\n }\n}\n"; + return json; +} + +} // namespace gui +} // namespace yaze + diff --git a/src/app/gui/widget_measurement.h b/src/app/gui/widget_measurement.h new file mode 100644 index 00000000..8ccfec31 --- /dev/null +++ b/src/app/gui/widget_measurement.h @@ -0,0 +1,119 @@ +#ifndef YAZE_APP_GUI_WIDGET_MEASUREMENT_H +#define YAZE_APP_GUI_WIDGET_MEASUREMENT_H + +#include +#include +#include + +#include "absl/strings/str_format.h" +#include "imgui/imgui.h" + +namespace yaze { +namespace gui { + +/** + * @class WidgetMeasurement + * @brief Tracks widget dimensions for debugging and test automation + * + * Integrates with ImGui Test Engine to provide accurate measurements + * of UI elements, helping prevent layout issues and enabling automated + * testing of widget sizes and positions. + */ +struct WidgetMetrics { + ImVec2 size; // Width and height + ImVec2 position; // Screen position + ImVec2 content_size; // Available content region + ImVec2 rect_min; // Bounding box min + ImVec2 rect_max; // Bounding box max + float cursor_pos_x; // Cursor X after rendering + std::string widget_id; // Widget identifier + std::string type; // Widget type (button, input, combo, etc.) + + std::string ToString() const { + return absl::StrFormat( + "Widget '%s' (%s): size=(%.1f,%.1f) pos=(%.1f,%.1f) content=(%.1f,%.1f) cursor_x=%.1f", + widget_id, type, size.x, size.y, position.x, position.y, + content_size.x, content_size.y, cursor_pos_x); + } +}; + +class WidgetMeasurement { + public: + static WidgetMeasurement& Instance() { + static WidgetMeasurement instance; + return instance; + } + + /** + * @brief Measure the last rendered ImGui item + * @param widget_id Unique identifier for this widget + * @param type Widget type (button, input, etc.) + * @return WidgetMetrics containing all measurements + */ + WidgetMetrics MeasureLastItem(const std::string& widget_id, + const std::string& type = "unknown"); + + /** + * @brief Begin measuring a toolbar section + */ + void BeginToolbarMeasurement(const std::string& toolbar_id); + + /** + * @brief End measuring a toolbar section and store total width + */ + void EndToolbarMeasurement(); + + /** + * @brief Get total measured width of a toolbar + */ + float GetToolbarWidth(const std::string& toolbar_id) const; + + /** + * @brief Check if toolbar would overflow given window width + */ + bool WouldToolbarOverflow(const std::string& toolbar_id, + float available_width) const; + + /** + * @brief Get all measurements for a specific toolbar + */ + const std::vector& GetToolbarMetrics( + const std::string& toolbar_id) const; + + /** + * @brief Clear all measurements (call once per frame) + */ + void ClearFrame(); + + /** + * @brief Export measurements for test automation + */ + std::string ExportMetricsJSON() const; + + /** + * @brief Enable/disable measurement (performance option) + */ + void SetEnabled(bool enabled) { enabled_ = enabled; } + bool IsEnabled() const { return enabled_; } + + private: + WidgetMeasurement() = default; + + bool enabled_ = true; + std::string current_toolbar_id_; + float current_toolbar_width_ = 0.0f; + float current_toolbar_start_x_ = 0.0f; + + // Store measurements per toolbar + std::unordered_map> toolbar_metrics_; + std::unordered_map toolbar_widths_; + + // All measurements from current frame + std::vector frame_metrics_; +}; + +} // namespace gui +} // namespace yaze + +#endif // YAZE_APP_GUI_WIDGET_MEASUREMENT_H +