From 951e7891b46d2be5ff125e7784bbc11323b487f5 Mon Sep 17 00:00:00 2001 From: scawful Date: Sun, 5 Oct 2025 16:24:29 -0400 Subject: [PATCH] refactor: Update File Paths and Introduce New GUI Components - Modified file paths in verify-build-environment scripts to reflect the new directory structure for agent-related files, enhancing clarity and organization. - Added new SettingsBar and Toolset classes to improve the editor's UI, providing a modern, compact interface for managing settings and tools. - Implemented helper functions for animations and responsive layouts in ui_helpers, enhancing the overall user experience and visual appeal. - Updated CMake configuration to include new source files, ensuring a seamless build process. --- scripts/verify-build-environment.ps1 | 4 +- scripts/verify-build-environment.sh | 4 +- src/app/gui/gui_library.cmake | 2 + 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/ui_helpers.cc | 124 ++++++++++++++++- src/app/gui/ui_helpers.h | 32 +++++ 9 files changed, 735 insertions(+), 9 deletions(-) create mode 100644 src/app/gui/settings_bar.cc create mode 100644 src/app/gui/settings_bar.h create mode 100644 src/app/gui/toolset.cc create mode 100644 src/app/gui/toolset.h diff --git a/scripts/verify-build-environment.ps1 b/scripts/verify-build-environment.ps1 index 48ad5104..3e050c2a 100644 --- a/scripts/verify-build-environment.ps1 +++ b/scripts/verify-build-environment.ps1 @@ -182,8 +182,8 @@ function Test-AgentFolderStructure { ) $oldSystemFiles = @( - "src/app/editor/system/agent_chat_widget.h", - "src/app/editor/system/agent_collaboration_coordinator.h" + "src/app/editor/agent/agent_chat_widget.h", + "src/app/editor/agent/agent_collaboration_coordinator.h" ) $allPresent = $true diff --git a/scripts/verify-build-environment.sh b/scripts/verify-build-environment.sh index 7c62aacd..81a56f5d 100755 --- a/scripts/verify-build-environment.sh +++ b/scripts/verify-build-environment.sh @@ -148,8 +148,8 @@ function test_agent_folder_structure() { ) local old_system_files=( - "src/app/editor/system/agent_chat_widget.h" - "src/app/editor/system/agent_collaboration_coordinator.h" + "src/app/editor/agent/agent_chat_widget.h" + "src/app/editor/agent/agent_collaboration_coordinator.h" ) local all_present=1 diff --git a/src/app/gui/gui_library.cmake b/src/app/gui/gui_library.cmake index 9c4fc616..019a5a88 100644 --- a/src/app/gui/gui_library.cmake +++ b/src/app/gui/gui_library.cmake @@ -16,6 +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 # 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 new file mode 100644 index 00000000..0f221e44 --- /dev/null +++ b/src/app/gui/settings_bar.cc @@ -0,0 +1,157 @@ +#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 new file mode 100644 index 00000000..f94157e9 --- /dev/null +++ b/src/app/gui/settings_bar.h @@ -0,0 +1,111 @@ +#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 new file mode 100644 index 00000000..186f6eff --- /dev/null +++ b/src/app/gui/toolset.cc @@ -0,0 +1,199 @@ +#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 new file mode 100644 index 00000000..372223cb --- /dev/null +++ b/src/app/gui/toolset.h @@ -0,0 +1,111 @@ +#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/ui_helpers.cc b/src/app/gui/ui_helpers.cc index 08be1fb8..1ce5e817 100644 --- a/src/app/gui/ui_helpers.cc +++ b/src/app/gui/ui_helpers.cc @@ -43,21 +43,21 @@ ImVec4 GetAccentColor() { return ConvertColorToImVec4(theme.accent); } -// Entity/Map marker colors +// Entity/Map marker colors (normalized RGB + proper alpha) ImVec4 GetEntranceColor() { - return ImVec4(1.0f, 1.0f, 0.0f, 0.6f); // Yellow with transparency + return ImVec4(1.0f, 1.0f, 0.0f, 0.39f); // Yellow, 100/255 alpha } ImVec4 GetExitColor() { - return ImVec4(1.0f, 1.0f, 1.0f, 0.6f); // White with transparency + return ImVec4(1.0f, 1.0f, 1.0f, 0.59f); // White, 150/255 alpha } ImVec4 GetItemColor() { - return ImVec4(1.0f, 0.0f, 0.0f, 0.6f); // Red with transparency + return ImVec4(1.0f, 0.0f, 0.0f, 0.59f); // Red, 150/255 alpha } ImVec4 GetSpriteColor() { - return ImVec4(1.0f, 0.0f, 1.0f, 0.6f); // Magenta with transparency + return ImVec4(1.0f, 0.0f, 1.0f, 0.59f); // Magenta, 150/255 alpha } ImVec4 GetSelectedColor() { @@ -378,6 +378,120 @@ void RightAlign(float width) { ImGui::GetContentRegionAvail().x - width); } +// ============================================================================ +// Animation Helpers +// ============================================================================ + +float GetPulseAlpha(float speed) { + return 0.5f + 0.5f * sinf(static_cast(ImGui::GetTime()) * speed * 2.0f); +} + +float GetFadeIn(float duration) { + static double start_time = 0.0; + double current_time = ImGui::GetTime(); + + if (start_time == 0.0) { + start_time = current_time; + } + + float elapsed = static_cast(current_time - start_time); + float alpha = ImClamp(elapsed / duration, 0.0f, 1.0f); + + // Reset after complete + if (alpha >= 1.0f) { + start_time = 0.0; + } + + return alpha; +} + +void PushPulseEffect(float speed) { + ImGui::PushStyleVar(ImGuiStyleVar_Alpha, + ImGui::GetStyle().Alpha * GetPulseAlpha(speed)); +} + +void PopPulseEffect() { + ImGui::PopStyleVar(); +} + +void LoadingSpinner(const char* label, float radius) { + ImDrawList* draw_list = ImGui::GetWindowDrawList(); + ImVec2 pos = ImGui::GetCursorScreenPos(); + pos.x += radius + 4; + pos.y += radius + 4; + + const float rotation = static_cast(ImGui::GetTime()) * 3.0f; + const int segments = 16; + const float thickness = 3.0f; + + const float start_angle = rotation; + const float end_angle = rotation + IM_PI * 1.5f; + + draw_list->PathArcTo(pos, radius, start_angle, end_angle, segments); + draw_list->PathStroke(ImGui::GetColorU32(GetAccentColor()), 0, thickness); + + if (label) { + ImGui::SetCursorPosX(ImGui::GetCursorPosX() + radius * 2 + 8); + ImGui::Text("%s", label); + } else { + ImGui::Dummy(ImVec2(radius * 2 + 8, radius * 2 + 8)); + } +} + +// ============================================================================ +// Responsive Layout Helpers +// ============================================================================ + +float GetResponsiveWidth(float min_width, float max_width, float ratio) { + float available = ImGui::GetContentRegionAvail().x; + float target = available * ratio; + + if (target < min_width) return min_width; + if (target > max_width) return max_width; + return target; +} + +void SetupResponsiveColumns(int count, float min_col_width) { + float available = ImGui::GetContentRegionAvail().x; + float col_width = available / count; + + if (col_width < min_col_width) { + col_width = min_col_width; + } + + for (int i = 0; i < count; ++i) { + ImGui::TableSetupColumn("##col", ImGuiTableColumnFlags_WidthFixed, col_width); + } +} + +static int g_two_col_table_active = 0; + +void BeginTwoColumns(const char* id, float split_ratio) { + ImGuiTableFlags flags = ImGuiTableFlags_Resizable | + ImGuiTableFlags_BordersInnerV | + ImGuiTableFlags_SizingStretchProp; + + if (ImGui::BeginTable(id, 2, flags)) { + float available = ImGui::GetContentRegionAvail().x; + ImGui::TableSetupColumn("##left", ImGuiTableColumnFlags_None, + available * split_ratio); + ImGui::TableSetupColumn("##right", ImGuiTableColumnFlags_None, + available * (1.0f - split_ratio)); + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + g_two_col_table_active++; + } +} + +void SwitchColumn() { + ImGui::TableNextColumn(); +} + +void EndTwoColumns() { + ImGui::EndTable(); + g_two_col_table_active--; +} + // ============================================================================ // Input Helpers // ============================================================================ diff --git a/src/app/gui/ui_helpers.h b/src/app/gui/ui_helpers.h index f4e14499..c6091010 100644 --- a/src/app/gui/ui_helpers.h +++ b/src/app/gui/ui_helpers.h @@ -132,6 +132,38 @@ void HorizontalSpacing(float pixels = 8.0f); void CenterText(const char* text); void RightAlign(float width); +// ============================================================================ +// Animation Helpers +// ============================================================================ + +// Pulsing alpha animation (for loading indicators, etc.) +float GetPulseAlpha(float speed = 1.0f); + +// Fade-in animation (for panel transitions) +float GetFadeIn(float duration = 0.3f); + +// Apply pulsing effect to next widget +void PushPulseEffect(float speed = 1.0f); +void PopPulseEffect(); + +// Loading spinner (animated circle) +void LoadingSpinner(const char* label = nullptr, float radius = 10.0f); + +// ============================================================================ +// Responsive Layout Helpers +// ============================================================================ + +// Get responsive width based on available space +float GetResponsiveWidth(float min_width, float max_width, float ratio = 0.5f); + +// Auto-fit table columns +void SetupResponsiveColumns(int count, float min_col_width = 100.0f); + +// Responsive two-column layout +void BeginTwoColumns(const char* id, float split_ratio = 0.6f); +void SwitchColumn(); +void EndTwoColumns(); + // ============================================================================ // Input Helpers (complement existing gui::InputHex functions) // ============================================================================