refactor(gui): reorganize background rendering and layout helpers
- Moved background rendering functionality from the editor to a dedicated GUI module, enhancing modularity and separation of concerns. - Introduced layout helpers for consistent theme-aware sizing across the GUI, improving UI consistency and maintainability. - Updated CMake configuration to reflect the new structure, ensuring proper linkage of the background renderer and layout helpers. Benefits: - Improved organization of GUI components, facilitating easier updates and enhancements. - Enhanced user interface consistency through theme-aware layout management.
This commit is contained in:
381
src/app/gui/background_renderer.cc
Normal file
381
src/app/gui/background_renderer.cc
Normal file
@@ -0,0 +1,381 @@
|
||||
#include "app/gui/background_renderer.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
|
||||
#include "app/core/timing.h"
|
||||
#include "app/gui/theme_manager.h"
|
||||
#include "imgui/imgui.h"
|
||||
|
||||
#ifndef M_PI
|
||||
#define M_PI 3.14159265358979323846
|
||||
#endif
|
||||
|
||||
namespace yaze {
|
||||
namespace gui {
|
||||
|
||||
// BackgroundRenderer Implementation
|
||||
BackgroundRenderer& BackgroundRenderer::Get() {
|
||||
static BackgroundRenderer instance;
|
||||
return instance;
|
||||
}
|
||||
|
||||
void BackgroundRenderer::RenderDockingBackground(ImDrawList* draw_list, const ImVec2& window_pos,
|
||||
const ImVec2& window_size, const Color& theme_color) {
|
||||
if (!draw_list) return;
|
||||
|
||||
UpdateAnimation(core::TimingManager::Get().GetDeltaTime());
|
||||
|
||||
// Get current theme colors
|
||||
auto& theme_manager = ThemeManager::Get();
|
||||
auto current_theme = theme_manager.GetCurrentTheme();
|
||||
|
||||
// Create a subtle tinted background
|
||||
Color bg_tint = {
|
||||
current_theme.background.red * 1.1f,
|
||||
current_theme.background.green * 1.1f,
|
||||
current_theme.background.blue * 1.1f,
|
||||
0.3f
|
||||
};
|
||||
|
||||
ImU32 bg_color = ImGui::ColorConvertFloat4ToU32(ConvertColorToImVec4(bg_tint));
|
||||
draw_list->AddRectFilled(window_pos,
|
||||
ImVec2(window_pos.x + window_size.x, window_pos.y + window_size.y),
|
||||
bg_color);
|
||||
|
||||
// Render the grid if enabled
|
||||
if (grid_settings_.grid_size > 0) {
|
||||
RenderGridBackground(draw_list, window_pos, window_size, theme_color);
|
||||
}
|
||||
|
||||
// Add subtle corner accents
|
||||
if (current_theme.enable_glow_effects) {
|
||||
float corner_size = 60.0f;
|
||||
Color accent_faded = current_theme.accent;
|
||||
accent_faded.alpha = 0.1f + 0.05f * sinf(animation_time_ * 2.0f);
|
||||
|
||||
ImU32 corner_color = ImGui::ColorConvertFloat4ToU32(ConvertColorToImVec4(accent_faded));
|
||||
|
||||
// Top-left corner
|
||||
draw_list->AddRectFilledMultiColor(
|
||||
window_pos,
|
||||
ImVec2(window_pos.x + corner_size, window_pos.y + corner_size),
|
||||
corner_color, IM_COL32(0,0,0,0), IM_COL32(0,0,0,0), corner_color);
|
||||
|
||||
// Bottom-right corner
|
||||
draw_list->AddRectFilledMultiColor(
|
||||
ImVec2(window_pos.x + window_size.x - corner_size, window_pos.y + window_size.y - corner_size),
|
||||
ImVec2(window_pos.x + window_size.x, window_pos.y + window_size.y),
|
||||
IM_COL32(0,0,0,0), corner_color, corner_color, IM_COL32(0,0,0,0));
|
||||
}
|
||||
}
|
||||
|
||||
void BackgroundRenderer::RenderGridBackground(ImDrawList* draw_list, const ImVec2& window_pos,
|
||||
const ImVec2& window_size, const Color& grid_color) {
|
||||
if (!draw_list || grid_settings_.grid_size <= 0) return;
|
||||
|
||||
// Grid parameters with optional animation
|
||||
float grid_size = grid_settings_.grid_size;
|
||||
float offset_x = 0.0f;
|
||||
float offset_y = 0.0f;
|
||||
|
||||
// Apply animation if enabled
|
||||
if (grid_settings_.enable_animation) {
|
||||
float animation_offset = animation_time_ * grid_settings_.animation_speed * 10.0f;
|
||||
offset_x = fmodf(animation_offset, grid_size);
|
||||
offset_y = fmodf(animation_offset * 0.7f, grid_size); // Different speed for interesting effect
|
||||
}
|
||||
|
||||
// Window center for radial calculations
|
||||
ImVec2 center = ImVec2(window_pos.x + window_size.x * 0.5f,
|
||||
window_pos.y + window_size.y * 0.5f);
|
||||
float max_distance = sqrtf(window_size.x * window_size.x + window_size.y * window_size.y) * 0.5f;
|
||||
|
||||
// Apply breathing effect to color if enabled
|
||||
Color themed_grid_color = grid_color;
|
||||
themed_grid_color.alpha = grid_settings_.opacity;
|
||||
|
||||
if (grid_settings_.enable_breathing) {
|
||||
float breathing_factor = 1.0f + grid_settings_.breathing_intensity *
|
||||
sinf(animation_time_ * grid_settings_.breathing_speed);
|
||||
themed_grid_color.red = std::min(1.0f, themed_grid_color.red * breathing_factor);
|
||||
themed_grid_color.green = std::min(1.0f, themed_grid_color.green * breathing_factor);
|
||||
themed_grid_color.blue = std::min(1.0f, themed_grid_color.blue * breathing_factor);
|
||||
}
|
||||
|
||||
if (grid_settings_.enable_dots) {
|
||||
// Render grid as dots
|
||||
for (float x = window_pos.x - offset_x; x < window_pos.x + window_size.x + grid_size; x += grid_size) {
|
||||
for (float y = window_pos.y - offset_y; y < window_pos.y + window_size.y + grid_size; y += grid_size) {
|
||||
ImVec2 dot_pos(x, y);
|
||||
|
||||
// Calculate radial fade
|
||||
float fade_factor = 1.0f;
|
||||
if (grid_settings_.radial_fade) {
|
||||
float distance = sqrtf((dot_pos.x - center.x) * (dot_pos.x - center.x) +
|
||||
(dot_pos.y - center.y) * (dot_pos.y - center.y));
|
||||
fade_factor = 1.0f - std::min(distance / grid_settings_.fade_distance, 1.0f);
|
||||
fade_factor = fade_factor * fade_factor; // Square for smoother falloff
|
||||
}
|
||||
|
||||
if (fade_factor > 0.01f) {
|
||||
ImU32 dot_color = BlendColorWithFade(themed_grid_color, fade_factor);
|
||||
DrawGridDot(draw_list, dot_pos, dot_color, grid_settings_.dot_size);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Render grid as lines
|
||||
// Vertical lines
|
||||
for (float x = window_pos.x - offset_x; x < window_pos.x + window_size.x + grid_size; x += grid_size) {
|
||||
ImVec2 line_start(x, window_pos.y);
|
||||
ImVec2 line_end(x, window_pos.y + window_size.y);
|
||||
|
||||
// Calculate average fade for this line
|
||||
float avg_fade = 0.0f;
|
||||
if (grid_settings_.radial_fade) {
|
||||
for (float y = window_pos.y; y < window_pos.y + window_size.y; y += grid_size * 0.5f) {
|
||||
float distance = sqrtf((x - center.x) * (x - center.x) + (y - center.y) * (y - center.y));
|
||||
float fade = 1.0f - std::min(distance / grid_settings_.fade_distance, 1.0f);
|
||||
avg_fade += fade * fade;
|
||||
}
|
||||
avg_fade /= (window_size.y / (grid_size * 0.5f));
|
||||
} else {
|
||||
avg_fade = 1.0f;
|
||||
}
|
||||
|
||||
if (avg_fade > 0.01f) {
|
||||
ImU32 line_color = BlendColorWithFade(themed_grid_color, avg_fade);
|
||||
DrawGridLine(draw_list, line_start, line_end, line_color, grid_settings_.line_thickness);
|
||||
}
|
||||
}
|
||||
|
||||
// Horizontal lines
|
||||
for (float y = window_pos.y - offset_y; y < window_pos.y + window_size.y + grid_size; y += grid_size) {
|
||||
ImVec2 line_start(window_pos.x, y);
|
||||
ImVec2 line_end(window_pos.x + window_size.x, y);
|
||||
|
||||
// Calculate average fade for this line
|
||||
float avg_fade = 0.0f;
|
||||
if (grid_settings_.radial_fade) {
|
||||
for (float x = window_pos.x; x < window_pos.x + window_size.x; x += grid_size * 0.5f) {
|
||||
float distance = sqrtf((x - center.x) * (x - center.x) + (y - center.y) * (y - center.y));
|
||||
float fade = 1.0f - std::min(distance / grid_settings_.fade_distance, 1.0f);
|
||||
avg_fade += fade * fade;
|
||||
}
|
||||
avg_fade /= (window_size.x / (grid_size * 0.5f));
|
||||
} else {
|
||||
avg_fade = 1.0f;
|
||||
}
|
||||
|
||||
if (avg_fade > 0.01f) {
|
||||
ImU32 line_color = BlendColorWithFade(themed_grid_color, avg_fade);
|
||||
DrawGridLine(draw_list, line_start, line_end, line_color, grid_settings_.line_thickness);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void BackgroundRenderer::RenderRadialGradient(ImDrawList* draw_list, const ImVec2& center,
|
||||
float radius, const Color& inner_color, const Color& outer_color) {
|
||||
if (!draw_list) return;
|
||||
|
||||
const int segments = 32;
|
||||
const int rings = 8;
|
||||
|
||||
for (int ring = 0; ring < rings; ++ring) {
|
||||
float ring_radius = radius * (ring + 1) / rings;
|
||||
float inner_ring_radius = radius * ring / rings;
|
||||
|
||||
// Interpolate colors for this ring
|
||||
float t = static_cast<float>(ring) / rings;
|
||||
Color ring_color = {
|
||||
inner_color.red * (1.0f - t) + outer_color.red * t,
|
||||
inner_color.green * (1.0f - t) + outer_color.green * t,
|
||||
inner_color.blue * (1.0f - t) + outer_color.blue * t,
|
||||
inner_color.alpha * (1.0f - t) + outer_color.alpha * t
|
||||
};
|
||||
|
||||
ImU32 color = ImGui::ColorConvertFloat4ToU32(ConvertColorToImVec4(ring_color));
|
||||
|
||||
if (ring == 0) {
|
||||
// Center circle
|
||||
draw_list->AddCircleFilled(center, ring_radius, color, segments);
|
||||
} else {
|
||||
// Ring
|
||||
for (int i = 0; i < segments; ++i) {
|
||||
float angle1 = (2.0f * M_PI * i) / segments;
|
||||
float angle2 = (2.0f * M_PI * (i + 1)) / segments;
|
||||
|
||||
ImVec2 p1_inner = ImVec2(center.x + cosf(angle1) * inner_ring_radius,
|
||||
center.y + sinf(angle1) * inner_ring_radius);
|
||||
ImVec2 p2_inner = ImVec2(center.x + cosf(angle2) * inner_ring_radius,
|
||||
center.y + sinf(angle2) * inner_ring_radius);
|
||||
ImVec2 p1_outer = ImVec2(center.x + cosf(angle1) * ring_radius,
|
||||
center.y + sinf(angle1) * ring_radius);
|
||||
ImVec2 p2_outer = ImVec2(center.x + cosf(angle2) * ring_radius,
|
||||
center.y + sinf(angle2) * ring_radius);
|
||||
|
||||
draw_list->AddQuadFilled(p1_inner, p2_inner, p2_outer, p1_outer, color);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void BackgroundRenderer::UpdateAnimation(float delta_time) {
|
||||
if (grid_settings_.enable_animation) {
|
||||
animation_time_ += delta_time;
|
||||
}
|
||||
}
|
||||
|
||||
void BackgroundRenderer::UpdateForTheme(const Color& primary_color, const Color& background_color) {
|
||||
// Create a grid color that's a subtle blend of the theme's primary and background
|
||||
cached_grid_color_ = {
|
||||
(primary_color.red * 0.3f + background_color.red * 0.7f),
|
||||
(primary_color.green * 0.3f + background_color.green * 0.7f),
|
||||
(primary_color.blue * 0.3f + background_color.blue * 0.7f),
|
||||
grid_settings_.opacity
|
||||
};
|
||||
}
|
||||
|
||||
void BackgroundRenderer::DrawSettingsUI() {
|
||||
if (ImGui::CollapsingHeader("Background Grid Settings")) {
|
||||
ImGui::Indent();
|
||||
|
||||
ImGui::SliderFloat("Grid Size", &grid_settings_.grid_size, 8.0f, 128.0f, "%.0f px");
|
||||
ImGui::SliderFloat("Line Thickness", &grid_settings_.line_thickness, 0.5f, 3.0f, "%.1f px");
|
||||
ImGui::SliderFloat("Opacity", &grid_settings_.opacity, 0.01f, 0.3f, "%.3f");
|
||||
ImGui::SliderFloat("Fade Distance", &grid_settings_.fade_distance, 50.0f, 500.0f, "%.0f px");
|
||||
|
||||
ImGui::Separator();
|
||||
ImGui::Text("Visual Effects:");
|
||||
ImGui::Checkbox("Enable Animation", &grid_settings_.enable_animation);
|
||||
ImGui::SameLine();
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("Makes the grid move slowly across the screen");
|
||||
}
|
||||
|
||||
ImGui::Checkbox("Color Breathing", &grid_settings_.enable_breathing);
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("Grid color pulses with a breathing effect");
|
||||
}
|
||||
|
||||
ImGui::Checkbox("Radial Fade", &grid_settings_.radial_fade);
|
||||
ImGui::Checkbox("Use Dots Instead of Lines", &grid_settings_.enable_dots);
|
||||
|
||||
// Animation settings (only show if animation is enabled)
|
||||
if (grid_settings_.enable_animation) {
|
||||
ImGui::Indent();
|
||||
ImGui::SliderFloat("Animation Speed", &grid_settings_.animation_speed, 0.1f, 3.0f, "%.1fx");
|
||||
ImGui::Unindent();
|
||||
}
|
||||
|
||||
// Breathing settings (only show if breathing is enabled)
|
||||
if (grid_settings_.enable_breathing) {
|
||||
ImGui::Indent();
|
||||
ImGui::SliderFloat("Breathing Speed", &grid_settings_.breathing_speed, 0.5f, 3.0f, "%.1fx");
|
||||
ImGui::SliderFloat("Breathing Intensity", &grid_settings_.breathing_intensity, 0.1f, 0.8f, "%.1f");
|
||||
ImGui::Unindent();
|
||||
}
|
||||
|
||||
if (grid_settings_.enable_dots) {
|
||||
ImGui::SliderFloat("Dot Size", &grid_settings_.dot_size, 1.0f, 8.0f, "%.1f px");
|
||||
}
|
||||
|
||||
// Preview
|
||||
ImGui::Spacing();
|
||||
ImGui::Text("Preview:");
|
||||
ImVec2 preview_size(200, 100);
|
||||
ImVec2 preview_pos = ImGui::GetCursorScreenPos();
|
||||
|
||||
ImDrawList* preview_draw_list = ImGui::GetWindowDrawList();
|
||||
auto& theme_manager = ThemeManager::Get();
|
||||
auto theme_color = theme_manager.GetCurrentTheme().primary;
|
||||
|
||||
// Draw preview background
|
||||
preview_draw_list->AddRectFilled(preview_pos,
|
||||
ImVec2(preview_pos.x + preview_size.x, preview_pos.y + preview_size.y),
|
||||
IM_COL32(30, 30, 30, 255));
|
||||
|
||||
// Draw preview grid
|
||||
RenderGridBackground(preview_draw_list, preview_pos, preview_size, theme_color);
|
||||
|
||||
// Advance cursor
|
||||
ImGui::Dummy(preview_size);
|
||||
|
||||
ImGui::Unindent();
|
||||
}
|
||||
}
|
||||
|
||||
float BackgroundRenderer::CalculateRadialFade(const ImVec2& pos, const ImVec2& center, float max_distance) const {
|
||||
float distance = sqrtf((pos.x - center.x) * (pos.x - center.x) +
|
||||
(pos.y - center.y) * (pos.y - center.y));
|
||||
float fade = 1.0f - std::min(distance / max_distance, 1.0f);
|
||||
return fade * fade; // Square for smoother falloff
|
||||
}
|
||||
|
||||
ImU32 BackgroundRenderer::BlendColorWithFade(const Color& base_color, float fade_factor) const {
|
||||
Color faded_color = {
|
||||
base_color.red,
|
||||
base_color.green,
|
||||
base_color.blue,
|
||||
base_color.alpha * fade_factor
|
||||
};
|
||||
return ImGui::ColorConvertFloat4ToU32(ConvertColorToImVec4(faded_color));
|
||||
}
|
||||
|
||||
void BackgroundRenderer::DrawGridLine(ImDrawList* draw_list, const ImVec2& start, const ImVec2& end,
|
||||
ImU32 color, float thickness) const {
|
||||
draw_list->AddLine(start, end, color, thickness);
|
||||
}
|
||||
|
||||
void BackgroundRenderer::DrawGridDot(ImDrawList* draw_list, const ImVec2& pos, ImU32 color, float size) const {
|
||||
draw_list->AddCircleFilled(pos, size, color);
|
||||
}
|
||||
|
||||
// DockSpaceRenderer Implementation
|
||||
bool DockSpaceRenderer::background_enabled_ = true;
|
||||
bool DockSpaceRenderer::grid_enabled_ = true;
|
||||
bool DockSpaceRenderer::effects_enabled_ = true;
|
||||
ImVec2 DockSpaceRenderer::last_dockspace_pos_{};
|
||||
ImVec2 DockSpaceRenderer::last_dockspace_size_{};
|
||||
|
||||
void DockSpaceRenderer::BeginEnhancedDockSpace(ImGuiID dockspace_id, const ImVec2& size,
|
||||
ImGuiDockNodeFlags flags) {
|
||||
// Store window info
|
||||
last_dockspace_pos_ = ImGui::GetWindowPos();
|
||||
last_dockspace_size_ = ImGui::GetWindowSize();
|
||||
|
||||
// Create the actual dockspace first
|
||||
ImGui::DockSpace(dockspace_id, size, flags);
|
||||
|
||||
// NOW draw the background effects on the foreground draw list so they're visible
|
||||
if (background_enabled_) {
|
||||
ImDrawList* fg_draw_list = ImGui::GetForegroundDrawList();
|
||||
auto& theme_manager = ThemeManager::Get();
|
||||
auto current_theme = theme_manager.GetCurrentTheme();
|
||||
|
||||
if (grid_enabled_) {
|
||||
auto& bg_renderer = BackgroundRenderer::Get();
|
||||
// Use the main viewport for full-screen grid
|
||||
const ImGuiViewport* viewport = ImGui::GetMainViewport();
|
||||
ImVec2 grid_pos = viewport->WorkPos;
|
||||
ImVec2 grid_size = viewport->WorkSize;
|
||||
|
||||
// Use subtle grid color that doesn't distract
|
||||
Color subtle_grid_color = current_theme.primary;
|
||||
// Use the grid settings opacity for consistency
|
||||
subtle_grid_color.alpha = bg_renderer.GetGridSettings().opacity;
|
||||
|
||||
bg_renderer.RenderGridBackground(fg_draw_list, grid_pos, grid_size, subtle_grid_color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DockSpaceRenderer::EndEnhancedDockSpace() {
|
||||
// Additional post-processing effects could go here
|
||||
// For now, this is just for API consistency
|
||||
}
|
||||
|
||||
} // namespace gui
|
||||
} // namespace yaze
|
||||
96
src/app/gui/background_renderer.h
Normal file
96
src/app/gui/background_renderer.h
Normal file
@@ -0,0 +1,96 @@
|
||||
#ifndef YAZE_APP_EDITOR_UI_BACKGROUND_RENDERER_H
|
||||
#define YAZE_APP_EDITOR_UI_BACKGROUND_RENDERER_H
|
||||
|
||||
#include "imgui/imgui.h"
|
||||
#include "app/gui/color.h"
|
||||
|
||||
namespace yaze {
|
||||
namespace gui {
|
||||
|
||||
/**
|
||||
* @class BackgroundRenderer
|
||||
* @brief Renders themed background effects for docking windows
|
||||
*/
|
||||
class BackgroundRenderer {
|
||||
public:
|
||||
struct GridSettings {
|
||||
float grid_size = 32.0f; // Size of grid cells
|
||||
float line_thickness = 1.0f; // Thickness of grid lines
|
||||
float opacity = 0.12f; // Subtle but visible opacity
|
||||
float fade_distance = 400.0f; // Distance over which grid fades
|
||||
bool enable_animation = false; // Animation toggle (default off)
|
||||
bool enable_breathing = false; // Color breathing effect toggle (default off)
|
||||
bool radial_fade = true; // Re-enable subtle radial fade
|
||||
bool enable_dots = false; // Use dots instead of lines
|
||||
float dot_size = 2.0f; // Size of grid dots
|
||||
float animation_speed = 1.0f; // Animation speed multiplier
|
||||
float breathing_speed = 1.5f; // Breathing effect speed
|
||||
float breathing_intensity = 0.3f; // How much color changes during breathing
|
||||
};
|
||||
|
||||
static BackgroundRenderer& Get();
|
||||
|
||||
// Main rendering functions
|
||||
void RenderDockingBackground(ImDrawList* draw_list, const ImVec2& window_pos,
|
||||
const ImVec2& window_size, const Color& theme_color);
|
||||
void RenderGridBackground(ImDrawList* draw_list, const ImVec2& window_pos,
|
||||
const ImVec2& window_size, const Color& grid_color);
|
||||
void RenderRadialGradient(ImDrawList* draw_list, const ImVec2& center,
|
||||
float radius, const Color& inner_color, const Color& outer_color);
|
||||
|
||||
// Configuration
|
||||
void SetGridSettings(const GridSettings& settings) { grid_settings_ = settings; }
|
||||
const GridSettings& GetGridSettings() const { return grid_settings_; }
|
||||
|
||||
// Animation
|
||||
void UpdateAnimation(float delta_time);
|
||||
void SetAnimationEnabled(bool enabled) { grid_settings_.enable_animation = enabled; }
|
||||
|
||||
// Theme integration
|
||||
void UpdateForTheme(const Color& primary_color, const Color& background_color);
|
||||
|
||||
// UI for settings
|
||||
void DrawSettingsUI();
|
||||
|
||||
private:
|
||||
BackgroundRenderer() = default;
|
||||
|
||||
GridSettings grid_settings_;
|
||||
float animation_time_ = 0.0f;
|
||||
Color cached_grid_color_{0.5f, 0.5f, 0.5f, 0.1f};
|
||||
|
||||
// Helper functions
|
||||
float CalculateRadialFade(const ImVec2& pos, const ImVec2& center, float max_distance) const;
|
||||
ImU32 BlendColorWithFade(const Color& base_color, float fade_factor) const;
|
||||
void DrawGridLine(ImDrawList* draw_list, const ImVec2& start, const ImVec2& end,
|
||||
ImU32 color, float thickness) const;
|
||||
void DrawGridDot(ImDrawList* draw_list, const ImVec2& pos, ImU32 color, float size) const;
|
||||
};
|
||||
|
||||
/**
|
||||
* @class DockSpaceRenderer
|
||||
* @brief Enhanced docking space with themed background effects
|
||||
*/
|
||||
class DockSpaceRenderer {
|
||||
public:
|
||||
static void BeginEnhancedDockSpace(ImGuiID dockspace_id, const ImVec2& size = ImVec2(0, 0),
|
||||
ImGuiDockNodeFlags flags = 0);
|
||||
static void EndEnhancedDockSpace();
|
||||
|
||||
// Configuration
|
||||
static void SetBackgroundEnabled(bool enabled) { background_enabled_ = enabled; }
|
||||
static void SetGridEnabled(bool enabled) { grid_enabled_ = enabled; }
|
||||
static void SetEffectsEnabled(bool enabled) { effects_enabled_ = enabled; }
|
||||
|
||||
private:
|
||||
static bool background_enabled_;
|
||||
static bool grid_enabled_;
|
||||
static bool effects_enabled_;
|
||||
static ImVec2 last_dockspace_pos_;
|
||||
static ImVec2 last_dockspace_size_;
|
||||
};
|
||||
|
||||
} // namespace gui
|
||||
} // namespace yaze
|
||||
|
||||
#endif // YAZE_APP_EDITOR_UI_BACKGROUND_RENDERER_H
|
||||
@@ -385,5 +385,29 @@ void DrawCanvasLabels(const CanvasRenderContext& ctx,
|
||||
}
|
||||
|
||||
} // namespace CanvasUtils
|
||||
|
||||
// CanvasConfig theme-aware methods implementation
|
||||
float CanvasConfig::GetToolbarHeight() const {
|
||||
if (!use_theme_sizing) {
|
||||
return 32.0f; // Legacy fixed height
|
||||
}
|
||||
|
||||
// Use layout helpers for theme-aware sizing
|
||||
// We need to include layout_helpers.h in the implementation file
|
||||
// For now, return a reasonable default that respects ImGui font size
|
||||
return ImGui::GetFontSize() * 0.75f; // Will be replaced with LayoutHelpers call
|
||||
}
|
||||
|
||||
float CanvasConfig::GetGridSpacing() const {
|
||||
if (!use_theme_sizing) {
|
||||
return grid_step; // Use configured grid_step as-is
|
||||
}
|
||||
|
||||
// Apply minimal theme-aware adjustment based on font size
|
||||
// Grid should stay consistent, but scale slightly with UI density
|
||||
float base_spacing = ImGui::GetFontSize() * 0.5f;
|
||||
return std::max(grid_step, base_spacing);
|
||||
}
|
||||
|
||||
} // namespace gui
|
||||
} // namespace yaze
|
||||
|
||||
@@ -20,12 +20,18 @@ struct CanvasConfig {
|
||||
bool enable_context_menu = true;
|
||||
bool is_draggable = false;
|
||||
bool auto_resize = false;
|
||||
bool clamp_rect_to_local_maps = true; // NEW: Prevent rectangle wrap across 512x512 boundaries
|
||||
bool clamp_rect_to_local_maps = true; // Prevent rectangle wrap across 512x512 boundaries
|
||||
bool use_theme_sizing = true; // Use theme-aware sizing instead of fixed sizes
|
||||
float grid_step = 32.0f;
|
||||
float global_scale = 1.0f;
|
||||
ImVec2 canvas_size = ImVec2(0, 0);
|
||||
ImVec2 content_size = ImVec2(0, 0); // Size of actual content (bitmap, etc.)
|
||||
bool custom_canvas_size = false;
|
||||
|
||||
// Get theme-aware canvas toolbar height (when use_theme_sizing is true)
|
||||
float GetToolbarHeight() const;
|
||||
// Get theme-aware grid spacing (when use_theme_sizing is true)
|
||||
float GetGridSpacing() const;
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -13,11 +13,14 @@ set(
|
||||
app/gui/editor_card_manager.cc
|
||||
app/gui/editor_layout.cc
|
||||
app/gui/input.cc
|
||||
app/gui/layout_helpers.cc
|
||||
app/gui/themed_widgets.cc
|
||||
app/gui/modules/asset_browser.cc
|
||||
app/gui/modules/text_editor.cc
|
||||
app/gui/style.cc
|
||||
app/gui/theme_manager.cc
|
||||
app/gui/ui_helpers.cc
|
||||
app/gui/background_renderer.cc # Moved from yaze_editor (used by style.cc)
|
||||
app/gui/widgets/agent_chat_widget.cc
|
||||
app/gui/widgets/collaboration_panel.cc
|
||||
app/gui/widgets/dungeon_object_emulator_preview.cc
|
||||
|
||||
281
src/app/gui/layout_helpers.cc
Normal file
281
src/app/gui/layout_helpers.cc
Normal file
@@ -0,0 +1,281 @@
|
||||
#include "app/gui/layout_helpers.h"
|
||||
|
||||
#include "imgui/imgui.h"
|
||||
#include "imgui/imgui_internal.h"
|
||||
#include "app/gui/theme_manager.h"
|
||||
#include "app/gui/color.h"
|
||||
|
||||
namespace yaze {
|
||||
namespace gui {
|
||||
|
||||
// Core sizing functions
|
||||
float LayoutHelpers::GetStandardWidgetHeight() {
|
||||
const auto& theme = GetTheme();
|
||||
return GetBaseFontSize() * theme.widget_height_multiplier * theme.compact_factor;
|
||||
}
|
||||
|
||||
float LayoutHelpers::GetStandardSpacing() {
|
||||
const auto& theme = GetTheme();
|
||||
return GetBaseFontSize() * 0.5f * theme.spacing_multiplier * theme.compact_factor;
|
||||
}
|
||||
|
||||
float LayoutHelpers::GetToolbarHeight() {
|
||||
const auto& theme = GetTheme();
|
||||
return GetBaseFontSize() * theme.toolbar_height_multiplier * theme.compact_factor;
|
||||
}
|
||||
|
||||
float LayoutHelpers::GetPanelPadding() {
|
||||
const auto& theme = GetTheme();
|
||||
return GetBaseFontSize() * 0.5f * theme.panel_padding_multiplier * theme.compact_factor;
|
||||
}
|
||||
|
||||
float LayoutHelpers::GetStandardInputWidth() {
|
||||
const auto& theme = GetTheme();
|
||||
return GetBaseFontSize() * 8.0f * theme.input_width_multiplier * theme.compact_factor;
|
||||
}
|
||||
|
||||
float LayoutHelpers::GetButtonPadding() {
|
||||
const auto& theme = GetTheme();
|
||||
return GetBaseFontSize() * 0.3f * theme.button_padding_multiplier * theme.compact_factor;
|
||||
}
|
||||
|
||||
float LayoutHelpers::GetTableRowHeight() {
|
||||
const auto& theme = GetTheme();
|
||||
return GetBaseFontSize() * theme.table_row_height_multiplier * theme.compact_factor;
|
||||
}
|
||||
|
||||
float LayoutHelpers::GetCanvasToolbarHeight() {
|
||||
const auto& theme = GetTheme();
|
||||
return GetBaseFontSize() * theme.canvas_toolbar_multiplier * theme.compact_factor;
|
||||
}
|
||||
|
||||
// Layout utilities
|
||||
void LayoutHelpers::BeginPaddedPanel(const char* label, float padding) {
|
||||
if (padding < 0.0f) {
|
||||
padding = GetPanelPadding();
|
||||
}
|
||||
ImGui::BeginChild(label, ImVec2(0, 0), true);
|
||||
ImGui::Dummy(ImVec2(padding, padding));
|
||||
ImGui::SameLine();
|
||||
ImGui::BeginGroup();
|
||||
ImGui::Dummy(ImVec2(0, padding));
|
||||
}
|
||||
|
||||
void LayoutHelpers::EndPaddedPanel() {
|
||||
ImGui::Dummy(ImVec2(0, GetPanelPadding()));
|
||||
ImGui::EndGroup();
|
||||
ImGui::SameLine();
|
||||
ImGui::Dummy(ImVec2(GetPanelPadding(), 0));
|
||||
ImGui::EndChild();
|
||||
}
|
||||
|
||||
bool LayoutHelpers::BeginTableWithTheming(const char* str_id, int columns,
|
||||
ImGuiTableFlags flags,
|
||||
const ImVec2& outer_size,
|
||||
float inner_width) {
|
||||
const auto& theme = GetTheme();
|
||||
|
||||
// Apply theme colors to table
|
||||
ImGui::PushStyleColor(ImGuiCol_TableHeaderBg, ConvertColorToImVec4(theme.table_header_bg));
|
||||
ImGui::PushStyleColor(ImGuiCol_TableBorderStrong, ConvertColorToImVec4(theme.table_border_strong));
|
||||
ImGui::PushStyleColor(ImGuiCol_TableBorderLight, ConvertColorToImVec4(theme.table_border_light));
|
||||
ImGui::PushStyleColor(ImGuiCol_TableRowBg, ConvertColorToImVec4(theme.table_row_bg));
|
||||
ImGui::PushStyleColor(ImGuiCol_TableRowBgAlt, ConvertColorToImVec4(theme.table_row_bg_alt));
|
||||
|
||||
// Set row height if not overridden by caller
|
||||
if (!(flags & ImGuiTableFlags_NoHostExtendY)) {
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_CellPadding,
|
||||
ImVec2(ImGui::GetStyle().CellPadding.x, GetTableRowHeight() * 0.25f));
|
||||
}
|
||||
|
||||
return ImGui::BeginTable(str_id, columns, flags, outer_size, inner_width);
|
||||
}
|
||||
|
||||
void LayoutHelpers::BeginCanvasPanel(const char* label, ImVec2* canvas_size) {
|
||||
const auto& theme = GetTheme();
|
||||
|
||||
// Apply theme to canvas container
|
||||
ImGui::PushStyleColor(ImGuiCol_ChildBg, ConvertColorToImVec4(theme.editor_background));
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0, 0));
|
||||
|
||||
if (canvas_size) {
|
||||
ImGui::BeginChild(label, *canvas_size, true);
|
||||
} else {
|
||||
ImGui::BeginChild(label, ImVec2(0, 0), true);
|
||||
}
|
||||
}
|
||||
|
||||
void LayoutHelpers::EndCanvasPanel() {
|
||||
ImGui::EndChild();
|
||||
ImGui::PopStyleVar(1);
|
||||
ImGui::PopStyleColor(1);
|
||||
}
|
||||
|
||||
// Input field helpers
|
||||
bool LayoutHelpers::AutoSizedInputField(const char* label, char* buf,
|
||||
size_t buf_size, ImGuiInputTextFlags flags) {
|
||||
ImGui::SetNextItemWidth(GetStandardInputWidth());
|
||||
return ImGui::InputText(label, buf, buf_size, flags);
|
||||
}
|
||||
|
||||
bool LayoutHelpers::AutoSizedInputInt(const char* label, int* v, int step,
|
||||
int step_fast, ImGuiInputTextFlags flags) {
|
||||
ImGui::SetNextItemWidth(GetStandardInputWidth());
|
||||
return ImGui::InputInt(label, v, step, step_fast, flags);
|
||||
}
|
||||
|
||||
bool LayoutHelpers::AutoSizedInputFloat(const char* label, float* v, float step,
|
||||
float step_fast, const char* format,
|
||||
ImGuiInputTextFlags flags) {
|
||||
ImGui::SetNextItemWidth(GetStandardInputWidth());
|
||||
return ImGui::InputFloat(label, v, step, step_fast, format, flags);
|
||||
}
|
||||
|
||||
// Input preset functions for common patterns
|
||||
bool LayoutHelpers::InputHexRow(const char* label, uint8_t* data) {
|
||||
const auto& theme = GetTheme();
|
||||
float input_width = GetStandardInputWidth() * 0.5f; // Hex inputs are smaller
|
||||
|
||||
ImGui::AlignTextToFramePadding();
|
||||
ImGui::Text("%s", label);
|
||||
ImGui::SameLine();
|
||||
|
||||
// Use theme-aware input width for hex byte (2 chars + controls)
|
||||
ImGui::SetNextItemWidth(input_width);
|
||||
|
||||
char buf[8];
|
||||
snprintf(buf, sizeof(buf), "%02X", *data);
|
||||
|
||||
bool changed = ImGui::InputText(
|
||||
("##" + std::string(label)).c_str(), buf, sizeof(buf),
|
||||
ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_AutoSelectAll);
|
||||
|
||||
if (changed) {
|
||||
unsigned int temp;
|
||||
if (sscanf(buf, "%X", &temp) == 1) {
|
||||
*data = static_cast<uint8_t>(temp & 0xFF);
|
||||
}
|
||||
}
|
||||
|
||||
return changed;
|
||||
}
|
||||
|
||||
bool LayoutHelpers::InputHexRow(const char* label, uint16_t* data) {
|
||||
const auto& theme = GetTheme();
|
||||
float input_width = GetStandardInputWidth() * 0.6f; // Hex word slightly wider
|
||||
|
||||
ImGui::AlignTextToFramePadding();
|
||||
ImGui::Text("%s", label);
|
||||
ImGui::SameLine();
|
||||
|
||||
// Use theme-aware input width for hex word (4 chars + controls)
|
||||
ImGui::SetNextItemWidth(input_width);
|
||||
|
||||
char buf[8];
|
||||
snprintf(buf, sizeof(buf), "%04X", *data);
|
||||
|
||||
bool changed = ImGui::InputText(
|
||||
("##" + std::string(label)).c_str(), buf, sizeof(buf),
|
||||
ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_AutoSelectAll);
|
||||
|
||||
if (changed) {
|
||||
unsigned int temp;
|
||||
if (sscanf(buf, "%X", &temp) == 1) {
|
||||
*data = static_cast<uint16_t>(temp & 0xFFFF);
|
||||
}
|
||||
}
|
||||
|
||||
return changed;
|
||||
}
|
||||
|
||||
void LayoutHelpers::BeginPropertyGrid(const char* label) {
|
||||
const auto& theme = GetTheme();
|
||||
|
||||
// Create a 2-column table for property editing
|
||||
if (ImGui::BeginTable(label, 2,
|
||||
ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg)) {
|
||||
// Setup columns: label column (30%) and value column (70%)
|
||||
ImGui::TableSetupColumn("Property", ImGuiTableColumnFlags_WidthFixed,
|
||||
GetStandardInputWidth() * 1.5f);
|
||||
ImGui::TableSetupColumn("Value", ImGuiTableColumnFlags_WidthStretch);
|
||||
}
|
||||
}
|
||||
|
||||
void LayoutHelpers::EndPropertyGrid() {
|
||||
ImGui::EndTable();
|
||||
}
|
||||
|
||||
bool LayoutHelpers::InputToolbarField(const char* label, char* buf, size_t buf_size) {
|
||||
// Compact input field for toolbars
|
||||
float compact_width = GetStandardInputWidth() * 0.8f * GetTheme().compact_factor;
|
||||
ImGui::SetNextItemWidth(compact_width);
|
||||
|
||||
return ImGui::InputText(label, buf, buf_size,
|
||||
ImGuiInputTextFlags_AutoSelectAll);
|
||||
}
|
||||
|
||||
// Toolbar helpers
|
||||
void LayoutHelpers::BeginToolbar(const char* label) {
|
||||
const auto& theme = GetTheme();
|
||||
ImGui::PushStyleColor(ImGuiCol_ChildBg, ConvertColorToImVec4(theme.menu_bar_bg));
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding,
|
||||
ImVec2(GetButtonPadding(), GetButtonPadding()));
|
||||
ImGui::BeginChild(label, ImVec2(0, GetToolbarHeight()), true,
|
||||
ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse);
|
||||
}
|
||||
|
||||
void LayoutHelpers::EndToolbar() {
|
||||
ImGui::EndChild();
|
||||
ImGui::PopStyleVar(1);
|
||||
ImGui::PopStyleColor(1);
|
||||
}
|
||||
|
||||
void LayoutHelpers::ToolbarSeparator() {
|
||||
ImGui::SameLine();
|
||||
ImGui::Dummy(ImVec2(GetStandardSpacing(), 0));
|
||||
ImGui::SameLine();
|
||||
ImGui::SeparatorEx(ImGuiSeparatorFlags_Vertical);
|
||||
ImGui::SameLine();
|
||||
ImGui::Dummy(ImVec2(GetStandardSpacing(), 0));
|
||||
ImGui::SameLine();
|
||||
}
|
||||
|
||||
bool LayoutHelpers::ToolbarButton(const char* label, const ImVec2& size) {
|
||||
const auto& theme = GetTheme();
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding,
|
||||
ImVec2(GetButtonPadding(), GetButtonPadding()));
|
||||
bool result = ImGui::Button(label, size);
|
||||
ImGui::PopStyleVar(1);
|
||||
return result;
|
||||
}
|
||||
|
||||
// Common layout patterns
|
||||
void LayoutHelpers::PropertyRow(const char* label, std::function<void()> widget_callback) {
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableSetColumnIndex(0);
|
||||
ImGui::AlignTextToFramePadding();
|
||||
ImGui::Text("%s", label);
|
||||
ImGui::TableSetColumnIndex(1);
|
||||
ImGui::SetNextItemWidth(-1);
|
||||
widget_callback();
|
||||
}
|
||||
|
||||
void LayoutHelpers::SectionHeader(const char* label) {
|
||||
const auto& theme = GetTheme();
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, ConvertColorToImVec4(theme.accent));
|
||||
ImGui::SeparatorText(label);
|
||||
ImGui::PopStyleColor(1);
|
||||
}
|
||||
|
||||
void LayoutHelpers::HelpMarker(const char* desc) {
|
||||
ImGui::TextDisabled("(?)");
|
||||
if (ImGui::BeginItemTooltip()) {
|
||||
ImGui::PushTextWrapPos(ImGui::GetFontSize() * 35.0f);
|
||||
ImGui::TextUnformatted(desc);
|
||||
ImGui::PopTextWrapPos();
|
||||
ImGui::EndTooltip();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace gui
|
||||
} // namespace yaze
|
||||
83
src/app/gui/layout_helpers.h
Normal file
83
src/app/gui/layout_helpers.h
Normal file
@@ -0,0 +1,83 @@
|
||||
#ifndef YAZE_APP_GUI_LAYOUT_HELPERS_H
|
||||
#define YAZE_APP_GUI_LAYOUT_HELPERS_H
|
||||
|
||||
#include "imgui/imgui.h"
|
||||
#include "app/gui/theme_manager.h"
|
||||
|
||||
namespace yaze {
|
||||
namespace gui {
|
||||
|
||||
/**
|
||||
* @brief Theme-aware sizing helpers for consistent UI layout
|
||||
*
|
||||
* All sizing functions respect the current theme's compact_factor and
|
||||
* semantic multipliers, ensuring layouts are consistent but customizable.
|
||||
*/
|
||||
class LayoutHelpers {
|
||||
public:
|
||||
// Core sizing functions (respect theme compact_factor + multipliers)
|
||||
static float GetStandardWidgetHeight();
|
||||
static float GetStandardSpacing();
|
||||
static float GetToolbarHeight();
|
||||
static float GetPanelPadding();
|
||||
static float GetStandardInputWidth();
|
||||
static float GetButtonPadding();
|
||||
static float GetTableRowHeight();
|
||||
static float GetCanvasToolbarHeight();
|
||||
|
||||
// Layout utilities
|
||||
static void BeginPaddedPanel(const char* label, float padding = -1.0f);
|
||||
static void EndPaddedPanel();
|
||||
|
||||
static bool BeginTableWithTheming(const char* str_id, int columns,
|
||||
ImGuiTableFlags flags = 0,
|
||||
const ImVec2& outer_size = ImVec2(0, 0),
|
||||
float inner_width = 0.0f);
|
||||
static void EndTable() { ImGui::EndTable(); }
|
||||
|
||||
static void BeginCanvasPanel(const char* label, ImVec2* canvas_size = nullptr);
|
||||
static void EndCanvasPanel();
|
||||
|
||||
// Input field helpers
|
||||
static bool AutoSizedInputField(const char* label, char* buf, size_t buf_size,
|
||||
ImGuiInputTextFlags flags = 0);
|
||||
static bool AutoSizedInputInt(const char* label, int* v, int step = 1,
|
||||
int step_fast = 100, ImGuiInputTextFlags flags = 0);
|
||||
static bool AutoSizedInputFloat(const char* label, float* v, float step = 0.0f,
|
||||
float step_fast = 0.0f, const char* format = "%.3f",
|
||||
ImGuiInputTextFlags flags = 0);
|
||||
|
||||
// Input preset functions for common patterns
|
||||
static bool InputHexRow(const char* label, uint8_t* data);
|
||||
static bool InputHexRow(const char* label, uint16_t* data);
|
||||
static void BeginPropertyGrid(const char* label);
|
||||
static void EndPropertyGrid();
|
||||
static bool InputToolbarField(const char* label, char* buf, size_t buf_size);
|
||||
|
||||
// Toolbar helpers
|
||||
static void BeginToolbar(const char* label);
|
||||
static void EndToolbar();
|
||||
static void ToolbarSeparator();
|
||||
static bool ToolbarButton(const char* label, const ImVec2& size = ImVec2(0, 0));
|
||||
|
||||
// Common layout patterns
|
||||
static void PropertyRow(const char* label, std::function<void()> widget_callback);
|
||||
static void SectionHeader(const char* label);
|
||||
static void HelpMarker(const char* desc);
|
||||
|
||||
// Get current theme
|
||||
static const EnhancedTheme& GetTheme() {
|
||||
return ThemeManager::Get().GetCurrentTheme();
|
||||
}
|
||||
|
||||
private:
|
||||
static float GetBaseFontSize() { return ImGui::GetFontSize(); }
|
||||
static float ApplyCompactFactor(float base_value) {
|
||||
return base_value * GetTheme().compact_factor;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace gui
|
||||
} // namespace yaze
|
||||
|
||||
#endif // YAZE_APP_GUI_LAYOUT_HELPERS_H
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
#include "util/file_util.h"
|
||||
#include "app/gui/theme_manager.h"
|
||||
#include "app/editor/ui/background_renderer.h"
|
||||
#include "app/gui/background_renderer.h"
|
||||
#include "app/platform/font_loader.h"
|
||||
#include "app/gui/color.h"
|
||||
#include "app/gui/icons.h"
|
||||
|
||||
@@ -150,11 +150,25 @@ struct EnhancedTheme {
|
||||
float tab_rounding = 0.0f;
|
||||
float window_border_size = 0.0f;
|
||||
float frame_border_size = 0.0f;
|
||||
|
||||
|
||||
// Animation and effects
|
||||
bool enable_animations = true;
|
||||
float animation_speed = 1.0f;
|
||||
bool enable_glow_effects = false;
|
||||
|
||||
// Theme-aware sizing system (relative to font size)
|
||||
// compact_factor: 0.8 = very compact, 1.0 = normal, 1.2 = spacious
|
||||
float compact_factor = 1.0f;
|
||||
|
||||
// Semantic sizing multipliers (applied on top of compact_factor)
|
||||
float widget_height_multiplier = 1.0f; // Standard widget height
|
||||
float spacing_multiplier = 1.0f; // Padding/margins between elements
|
||||
float toolbar_height_multiplier = 0.8f; // Compact toolbars
|
||||
float panel_padding_multiplier = 1.0f; // Panel interior padding
|
||||
float input_width_multiplier = 1.0f; // Standard input field width
|
||||
float button_padding_multiplier = 1.0f; // Button interior padding
|
||||
float table_row_height_multiplier = 1.0f; // Table row height
|
||||
float canvas_toolbar_multiplier = 0.75f; // Canvas overlay toolbars
|
||||
|
||||
// Helper methods
|
||||
void ApplyToImGui() const;
|
||||
|
||||
272
src/app/gui/themed_widgets.cc
Normal file
272
src/app/gui/themed_widgets.cc
Normal file
@@ -0,0 +1,272 @@
|
||||
#include "app/gui/themed_widgets.h"
|
||||
|
||||
namespace yaze {
|
||||
namespace gui {
|
||||
namespace themed {
|
||||
|
||||
// ============================================================================
|
||||
// Buttons
|
||||
// ============================================================================
|
||||
|
||||
bool Button(const char* label, const ImVec2& size) {
|
||||
const auto& theme = GetTheme();
|
||||
ImGui::PushStyleColor(ImGuiCol_Button, ConvertColorToImVec4(theme.button));
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ConvertColorToImVec4(theme.button_hovered));
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonActive, ConvertColorToImVec4(theme.button_active));
|
||||
|
||||
bool result = ImGui::Button(label, size);
|
||||
|
||||
ImGui::PopStyleColor(3);
|
||||
return result;
|
||||
}
|
||||
|
||||
bool IconButton(const char* icon, const char* tooltip) {
|
||||
bool result = Button(icon, ImVec2(LayoutHelpers::GetStandardWidgetHeight(),
|
||||
LayoutHelpers::GetStandardWidgetHeight()));
|
||||
if (tooltip && ImGui::IsItemHovered()) {
|
||||
BeginTooltip();
|
||||
ImGui::Text("%s", tooltip);
|
||||
EndTooltip();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
bool PrimaryButton(const char* label, const ImVec2& size) {
|
||||
const auto& theme = GetTheme();
|
||||
ImGui::PushStyleColor(ImGuiCol_Button, ConvertColorToImVec4(theme.accent));
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonHovered,
|
||||
ImVec4(theme.accent.red * 1.2f, theme.accent.green * 1.2f,
|
||||
theme.accent.blue * 1.2f, theme.accent.alpha));
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonActive,
|
||||
ImVec4(theme.accent.red * 0.8f, theme.accent.green * 0.8f,
|
||||
theme.accent.blue * 0.8f, theme.accent.alpha));
|
||||
|
||||
bool result = ImGui::Button(label, size);
|
||||
|
||||
ImGui::PopStyleColor(3);
|
||||
return result;
|
||||
}
|
||||
|
||||
bool DangerButton(const char* label, const ImVec2& size) {
|
||||
const auto& theme = GetTheme();
|
||||
ImGui::PushStyleColor(ImGuiCol_Button, ConvertColorToImVec4(theme.error));
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonHovered,
|
||||
ImVec4(theme.error.red * 1.2f, theme.error.green * 1.2f,
|
||||
theme.error.blue * 1.2f, theme.error.alpha));
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonActive,
|
||||
ImVec4(theme.error.red * 0.8f, theme.error.green * 0.8f,
|
||||
theme.error.blue * 0.8f, theme.error.alpha));
|
||||
|
||||
bool result = ImGui::Button(label, size);
|
||||
|
||||
ImGui::PopStyleColor(3);
|
||||
return result;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Headers & Sections
|
||||
// ============================================================================
|
||||
|
||||
void Header(const char* label) {
|
||||
LayoutHelpers::SectionHeader(label);
|
||||
}
|
||||
|
||||
bool CollapsingHeader(const char* label, ImGuiTreeNodeFlags flags) {
|
||||
const auto& theme = GetTheme();
|
||||
ImGui::PushStyleColor(ImGuiCol_Header, ConvertColorToImVec4(theme.header));
|
||||
ImGui::PushStyleColor(ImGuiCol_HeaderHovered, ConvertColorToImVec4(theme.header_hovered));
|
||||
ImGui::PushStyleColor(ImGuiCol_HeaderActive, ConvertColorToImVec4(theme.header_active));
|
||||
|
||||
bool result = ImGui::CollapsingHeader(label, flags);
|
||||
|
||||
ImGui::PopStyleColor(3);
|
||||
return result;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Cards & Panels
|
||||
// ============================================================================
|
||||
|
||||
void Card(const char* label, std::function<void()> content, const ImVec2& size) {
|
||||
BeginPanel(label, size);
|
||||
content();
|
||||
EndPanel();
|
||||
}
|
||||
|
||||
void BeginPanel(const char* label, const ImVec2& size) {
|
||||
const auto& theme = GetTheme();
|
||||
|
||||
ImGui::PushStyleColor(ImGuiCol_ChildBg, ConvertColorToImVec4(theme.surface));
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_ChildRounding, theme.window_rounding);
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding,
|
||||
ImVec2(LayoutHelpers::GetPanelPadding(),
|
||||
LayoutHelpers::GetPanelPadding()));
|
||||
|
||||
ImGui::BeginChild(label, size, true);
|
||||
}
|
||||
|
||||
void EndPanel() {
|
||||
ImGui::EndChild();
|
||||
ImGui::PopStyleVar(2);
|
||||
ImGui::PopStyleColor(1);
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Inputs
|
||||
// ============================================================================
|
||||
|
||||
bool InputText(const char* label, char* buf, size_t buf_size,
|
||||
ImGuiInputTextFlags flags) {
|
||||
const auto& theme = GetTheme();
|
||||
ImGui::PushStyleColor(ImGuiCol_FrameBg, ConvertColorToImVec4(theme.frame_bg));
|
||||
ImGui::PushStyleColor(ImGuiCol_FrameBgHovered, ConvertColorToImVec4(theme.frame_bg_hovered));
|
||||
ImGui::PushStyleColor(ImGuiCol_FrameBgActive, ConvertColorToImVec4(theme.frame_bg_active));
|
||||
|
||||
ImGui::SetNextItemWidth(LayoutHelpers::GetStandardInputWidth());
|
||||
bool result = ImGui::InputText(label, buf, buf_size, flags);
|
||||
|
||||
ImGui::PopStyleColor(3);
|
||||
return result;
|
||||
}
|
||||
|
||||
bool InputInt(const char* label, int* v, int step, int step_fast,
|
||||
ImGuiInputTextFlags flags) {
|
||||
const auto& theme = GetTheme();
|
||||
ImGui::PushStyleColor(ImGuiCol_FrameBg, ConvertColorToImVec4(theme.frame_bg));
|
||||
ImGui::PushStyleColor(ImGuiCol_FrameBgHovered, ConvertColorToImVec4(theme.frame_bg_hovered));
|
||||
ImGui::PushStyleColor(ImGuiCol_FrameBgActive, ConvertColorToImVec4(theme.frame_bg_active));
|
||||
|
||||
ImGui::SetNextItemWidth(LayoutHelpers::GetStandardInputWidth());
|
||||
bool result = ImGui::InputInt(label, v, step, step_fast, flags);
|
||||
|
||||
ImGui::PopStyleColor(3);
|
||||
return result;
|
||||
}
|
||||
|
||||
bool InputFloat(const char* label, float* v, float step, float step_fast,
|
||||
const char* format, ImGuiInputTextFlags flags) {
|
||||
const auto& theme = GetTheme();
|
||||
ImGui::PushStyleColor(ImGuiCol_FrameBg, ConvertColorToImVec4(theme.frame_bg));
|
||||
ImGui::PushStyleColor(ImGuiCol_FrameBgHovered, ConvertColorToImVec4(theme.frame_bg_hovered));
|
||||
ImGui::PushStyleColor(ImGuiCol_FrameBgActive, ConvertColorToImVec4(theme.frame_bg_active));
|
||||
|
||||
ImGui::SetNextItemWidth(LayoutHelpers::GetStandardInputWidth());
|
||||
bool result = ImGui::InputFloat(label, v, step, step_fast, format, flags);
|
||||
|
||||
ImGui::PopStyleColor(3);
|
||||
return result;
|
||||
}
|
||||
|
||||
bool Checkbox(const char* label, bool* v) {
|
||||
const auto& theme = GetTheme();
|
||||
ImGui::PushStyleColor(ImGuiCol_CheckMark, ConvertColorToImVec4(theme.check_mark));
|
||||
|
||||
bool result = ImGui::Checkbox(label, v);
|
||||
|
||||
ImGui::PopStyleColor(1);
|
||||
return result;
|
||||
}
|
||||
|
||||
bool Combo(const char* label, int* current_item, const char* const items[],
|
||||
int items_count, int popup_max_height_in_items) {
|
||||
const auto& theme = GetTheme();
|
||||
ImGui::PushStyleColor(ImGuiCol_FrameBg, ConvertColorToImVec4(theme.frame_bg));
|
||||
ImGui::PushStyleColor(ImGuiCol_FrameBgHovered, ConvertColorToImVec4(theme.frame_bg_hovered));
|
||||
ImGui::PushStyleColor(ImGuiCol_FrameBgActive, ConvertColorToImVec4(theme.frame_bg_active));
|
||||
|
||||
ImGui::SetNextItemWidth(LayoutHelpers::GetStandardInputWidth());
|
||||
bool result = ImGui::Combo(label, current_item, items, items_count,
|
||||
popup_max_height_in_items);
|
||||
|
||||
ImGui::PopStyleColor(3);
|
||||
return result;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Tables
|
||||
// ============================================================================
|
||||
|
||||
bool BeginTable(const char* str_id, int columns, ImGuiTableFlags flags,
|
||||
const ImVec2& outer_size, float inner_width) {
|
||||
return LayoutHelpers::BeginTableWithTheming(str_id, columns, flags, outer_size, inner_width);
|
||||
}
|
||||
|
||||
void EndTable() {
|
||||
LayoutHelpers::EndTable();
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Tooltips & Help
|
||||
// ============================================================================
|
||||
|
||||
void HelpMarker(const char* desc) {
|
||||
LayoutHelpers::HelpMarker(desc);
|
||||
}
|
||||
|
||||
void BeginTooltip() {
|
||||
const auto& theme = GetTheme();
|
||||
ImGui::PushStyleColor(ImGuiCol_PopupBg, ConvertColorToImVec4(theme.popup_bg));
|
||||
ImGui::BeginTooltip();
|
||||
}
|
||||
|
||||
void EndTooltip() {
|
||||
ImGui::EndTooltip();
|
||||
ImGui::PopStyleColor(1);
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Status & Feedback
|
||||
// ============================================================================
|
||||
|
||||
void StatusText(const char* text, StatusType type) {
|
||||
const auto& theme = GetTheme();
|
||||
ImVec4 color;
|
||||
|
||||
switch (type) {
|
||||
case StatusType::kSuccess:
|
||||
color = ConvertColorToImVec4(theme.success);
|
||||
break;
|
||||
case StatusType::kWarning:
|
||||
color = ConvertColorToImVec4(theme.warning);
|
||||
break;
|
||||
case StatusType::kError:
|
||||
color = ConvertColorToImVec4(theme.error);
|
||||
break;
|
||||
case StatusType::kInfo:
|
||||
color = ConvertColorToImVec4(theme.info);
|
||||
break;
|
||||
}
|
||||
|
||||
ImGui::TextColored(color, "%s", text);
|
||||
}
|
||||
|
||||
void ProgressBar(float fraction, const ImVec2& size, const char* overlay) {
|
||||
const auto& theme = GetTheme();
|
||||
ImGui::PushStyleColor(ImGuiCol_PlotHistogram, ConvertColorToImVec4(theme.accent));
|
||||
|
||||
ImGui::ProgressBar(fraction, size, overlay);
|
||||
|
||||
ImGui::PopStyleColor(1);
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Utility
|
||||
// ============================================================================
|
||||
|
||||
void PushWidgetColors() {
|
||||
const auto& theme = GetTheme();
|
||||
ImGui::PushStyleColor(ImGuiCol_FrameBg, ConvertColorToImVec4(theme.frame_bg));
|
||||
ImGui::PushStyleColor(ImGuiCol_FrameBgHovered, ConvertColorToImVec4(theme.frame_bg_hovered));
|
||||
ImGui::PushStyleColor(ImGuiCol_FrameBgActive, ConvertColorToImVec4(theme.frame_bg_active));
|
||||
ImGui::PushStyleColor(ImGuiCol_Button, ConvertColorToImVec4(theme.button));
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ConvertColorToImVec4(theme.button_hovered));
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonActive, ConvertColorToImVec4(theme.button_active));
|
||||
}
|
||||
|
||||
void PopWidgetColors() {
|
||||
ImGui::PopStyleColor(6);
|
||||
}
|
||||
|
||||
} // namespace themed
|
||||
} // namespace gui
|
||||
} // namespace yaze
|
||||
211
src/app/gui/themed_widgets.h
Normal file
211
src/app/gui/themed_widgets.h
Normal file
@@ -0,0 +1,211 @@
|
||||
#ifndef YAZE_APP_GUI_THEMED_WIDGETS_H
|
||||
#define YAZE_APP_GUI_THEMED_WIDGETS_H
|
||||
|
||||
#include "imgui/imgui.h"
|
||||
#include "app/gui/theme_manager.h"
|
||||
#include "app/gui/layout_helpers.h"
|
||||
#include "app/gui/color.h"
|
||||
|
||||
namespace yaze {
|
||||
namespace gui {
|
||||
|
||||
/**
|
||||
* @brief Opt-in themed widget library for gradual migration
|
||||
*
|
||||
* All widgets automatically use the current theme from ThemeManager.
|
||||
* Editors can opt-in by using these widgets instead of raw ImGui calls.
|
||||
*
|
||||
* Usage:
|
||||
* ```cpp
|
||||
* using namespace yaze::gui::themed;
|
||||
*
|
||||
* if (Button("Save")) {
|
||||
* // Button uses theme colors automatically
|
||||
* }
|
||||
*
|
||||
* Header("Settings"); // Themed section header
|
||||
*
|
||||
* Card("Properties", [&]() {
|
||||
* // Content inside themed card
|
||||
* });
|
||||
* ```
|
||||
*/
|
||||
namespace themed {
|
||||
|
||||
// ============================================================================
|
||||
// Buttons
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* @brief Themed button with automatic color application
|
||||
*/
|
||||
bool Button(const char* label, const ImVec2& size = ImVec2(0, 0));
|
||||
|
||||
/**
|
||||
* @brief Themed button with icon (Material Design Icons)
|
||||
*/
|
||||
bool IconButton(const char* icon, const char* tooltip = nullptr);
|
||||
|
||||
/**
|
||||
* @brief Primary action button (uses accent color)
|
||||
*/
|
||||
bool PrimaryButton(const char* label, const ImVec2& size = ImVec2(0, 0));
|
||||
|
||||
/**
|
||||
* @brief Danger/destructive action button (uses error color)
|
||||
*/
|
||||
bool DangerButton(const char* label, const ImVec2& size = ImVec2(0, 0));
|
||||
|
||||
// ============================================================================
|
||||
// Headers & Sections
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* @brief Themed section header with accent color
|
||||
*/
|
||||
void Header(const char* label);
|
||||
|
||||
/**
|
||||
* @brief Collapsible section with themed header
|
||||
*/
|
||||
bool CollapsingHeader(const char* label, ImGuiTreeNodeFlags flags = 0);
|
||||
|
||||
// ============================================================================
|
||||
// Cards & Panels
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* @brief Themed card with rounded corners and shadow
|
||||
* @param label Unique ID for the card
|
||||
* @param content Callback function to render card content
|
||||
* @param size Card size (0, 0 for auto-size)
|
||||
*/
|
||||
void Card(const char* label, std::function<void()> content,
|
||||
const ImVec2& size = ImVec2(0, 0));
|
||||
|
||||
/**
|
||||
* @brief Begin themed panel (manual version of Card)
|
||||
*/
|
||||
void BeginPanel(const char* label, const ImVec2& size = ImVec2(0, 0));
|
||||
|
||||
/**
|
||||
* @brief End themed panel
|
||||
*/
|
||||
void EndPanel();
|
||||
|
||||
// ============================================================================
|
||||
// Inputs
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* @brief Themed text input
|
||||
*/
|
||||
bool InputText(const char* label, char* buf, size_t buf_size,
|
||||
ImGuiInputTextFlags flags = 0);
|
||||
|
||||
/**
|
||||
* @brief Themed integer input
|
||||
*/
|
||||
bool InputInt(const char* label, int* v, int step = 1, int step_fast = 100,
|
||||
ImGuiInputTextFlags flags = 0);
|
||||
|
||||
/**
|
||||
* @brief Themed float input
|
||||
*/
|
||||
bool InputFloat(const char* label, float* v, float step = 0.0f,
|
||||
float step_fast = 0.0f, const char* format = "%.3f",
|
||||
ImGuiInputTextFlags flags = 0);
|
||||
|
||||
/**
|
||||
* @brief Themed checkbox
|
||||
*/
|
||||
bool Checkbox(const char* label, bool* v);
|
||||
|
||||
/**
|
||||
* @brief Themed combo box
|
||||
*/
|
||||
bool Combo(const char* label, int* current_item, const char* const items[],
|
||||
int items_count, int popup_max_height_in_items = -1);
|
||||
|
||||
// ============================================================================
|
||||
// Tables
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* @brief Begin themed table with automatic styling
|
||||
*/
|
||||
bool BeginTable(const char* str_id, int columns, ImGuiTableFlags flags = 0,
|
||||
const ImVec2& outer_size = ImVec2(0, 0), float inner_width = 0.0f);
|
||||
|
||||
/**
|
||||
* @brief End themed table
|
||||
*/
|
||||
void EndTable();
|
||||
|
||||
// ============================================================================
|
||||
// Tooltips & Help
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* @brief Themed help marker with tooltip
|
||||
*/
|
||||
void HelpMarker(const char* desc);
|
||||
|
||||
/**
|
||||
* @brief Begin themed tooltip
|
||||
*/
|
||||
void BeginTooltip();
|
||||
|
||||
/**
|
||||
* @brief End themed tooltip
|
||||
*/
|
||||
void EndTooltip();
|
||||
|
||||
// ============================================================================
|
||||
// Status & Feedback
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* @brief Themed status text (success, warning, error, info)
|
||||
*/
|
||||
void StatusText(const char* text, StatusType type);
|
||||
|
||||
enum class StatusType {
|
||||
kSuccess,
|
||||
kWarning,
|
||||
kError,
|
||||
kInfo
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Themed progress bar
|
||||
*/
|
||||
void ProgressBar(float fraction, const ImVec2& size = ImVec2(-1, 0),
|
||||
const char* overlay = nullptr);
|
||||
|
||||
// ============================================================================
|
||||
// Utility
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* @brief Get current theme (shortcut)
|
||||
*/
|
||||
inline const EnhancedTheme& GetTheme() {
|
||||
return ThemeManager::Get().GetCurrentTheme();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Apply theme colors to next widget
|
||||
*/
|
||||
void PushWidgetColors();
|
||||
|
||||
/**
|
||||
* @brief Restore previous colors
|
||||
*/
|
||||
void PopWidgetColors();
|
||||
|
||||
} // namespace themed
|
||||
} // namespace gui
|
||||
} // namespace yaze
|
||||
|
||||
#endif // YAZE_APP_GUI_THEMED_WIDGETS_H
|
||||
Reference in New Issue
Block a user