yaze 0.3.2
Link to the Past ROM Editor
 
Loading...
Searching...
No Matches
theme_manager.h
Go to the documentation of this file.
1#ifndef YAZE_APP_GUI_THEME_MANAGER_H
2#define YAZE_APP_GUI_THEME_MANAGER_H
3
4#include <functional>
5#include <map>
6#include <optional>
7#include <set>
8#include <string>
9#include <utility>
10#include <vector>
11
12#include "absl/status/status.h"
13#include "absl/status/statusor.h"
14#include "app/gui/core/color.h"
15#include "imgui/imgui.h"
16
17namespace yaze {
18namespace gui {
19
24enum class DensityPreset {
25 kCompact, // 0.75x - Dense UI, more content visible
26 kNormal, // 1.0x - Default balanced spacing
27 kComfortable // 1.25x - Spacious, easier to click
28};
29
34// Default overworld entity marker colors, used when a theme does not declare
35// its own. They follow docs/public/developer/architecture.md section 3.4
36// (entrances yellow-gold, exits cyan-white, items red, sprites magenta) and
37// are the same for every theme: the markers sit on the map's own pixels, not
38// on theme surfaces, so they need to read on light and dark map areas alike.
39// The boxes are opaque; labels get an outline (CanvasUtils::DrawCanvasText).
41 static Color Entrance() { return {1.0f, 0.8f, 0.0f, 1.0f}; } // 255,204,0
42 static Color Hole() {
43 return {1.0f, 150.0f / 255.0f, 0.0f, 1.0f}; // 255,150,0 amber
44 }
45 static Color Exit() {
46 return {150.0f / 255.0f, 235.0f / 255.0f, 1.0f, 1.0f}; // 150,235,255
47 }
48 static Color Item() {
49 return {235.0f / 255.0f, 45.0f / 255.0f, 45.0f / 255.0f, 1.0f};
50 }
51 static Color Sprite() {
52 return {235.0f / 255.0f, 60.0f / 255.0f, 235.0f / 255.0f, 1.0f};
53 }
54};
55
56struct Theme {
57 std::string name;
58 std::string description;
59 std::string author;
60
61 // Primary colors
71
72 // Text colors
76
77 // Window colors
82
83 // Interactive elements
90
91 // Navigation and selection
102
103 // Borders and separators
109
110 // Scrollbars and controls
115
116 // Special elements
122
123 // Complete ImGui color support
145
146 // Additional ImGui colors for complete coverage
153
154 // Enhanced theme system - semantic colors
155 Color text_highlight; // For selected text, highlighted items
156 Color link_hover; // For hover state of links
157 Color code_background; // For code blocks, monospace text backgrounds
158 Color success_light; // Lighter variant of success color
159 Color warning_light; // Lighter variant of warning color
160 Color error_light; // Lighter variant of error color
161 Color info_light; // Lighter variant of info color
162
163 // UI state colors
164 Color active_selection; // For active/selected UI elements
165 Color hover_highlight; // General hover state
166 Color focus_border; // For focused input elements
167 Color disabled_overlay; // Semi-transparent overlay for disabled elements
168
169 // Editor-specific colors
170 Color editor_background; // Main editor canvas background
171 Color editor_grid; // Grid lines in editors
172 Color editor_cursor; // Cursor/selection in editors
173 Color editor_selection; // Selected area in editors
174
175 // Unified selection and interaction colors (replacing hardcoded values)
176 Color selection_primary; // Primary selection (typically gold/yellow)
177 Color selection_secondary; // Secondary selection (typically cyan/blue)
178 Color selection_hover; // Hover highlight color
179 Color selection_pulsing; // Pulsing animation color
180 Color selection_handle; // Corner handles for resizing/moving
181 Color drag_preview; // Ghost preview when dragging
182 Color drag_preview_outline; // Outline for drag preview
183
184 // Map entity marker colors. Each is its own token, keyed in .theme files by
185 // the field name. The overworld marker defaults are fixed values (see
186 // EntityMarkerDefaults), not the status colors: an exit is not an error.
194
195 // Nested struct for dungeon editor colors
224
225 // Nested struct for chat/agent colors
264
265 // Style parameters
266 float window_rounding = 2.0f;
267 float frame_rounding = 4.0f;
268 float scrollbar_rounding = 4.0f;
269 float grab_rounding = 3.0f;
270 float tab_rounding = 3.0f;
271 float window_border_size = 1.0f;
272 float frame_border_size = 1.0f;
273
274 // Animation and effects
275 bool enable_animations = true;
276 float animation_speed = 1.0f;
278
279 // Theme-aware sizing system (relative to font size)
280 // compact_factor: 0.8 = very compact, 1.0 = normal, 1.2 = spacious
281 float compact_factor = 1.0f;
283
284 // Semantic sizing multipliers (applied on top of compact_factor)
285 float widget_height_multiplier = 1.0f; // Standard widget height
286 float spacing_multiplier = 1.0f; // Padding/margins between elements
287 float toolbar_height_multiplier = 0.8f; // Compact toolbars
288 float panel_padding_multiplier = 1.0f; // Panel interior padding
289 float input_width_multiplier = 1.0f; // Standard input field width
290 float button_padding_multiplier = 1.0f; // Button interior padding
291 float table_row_height_multiplier = 1.0f; // Table row height
292 float canvas_toolbar_multiplier = 0.75f; // Canvas overlay toolbars
293
294 // Helper methods
295 void ApplyToImGui() const;
296 // Writes only the density-derived padding/spacing scalars. ApplyToImGui
297 // calls it; ApplyClassicYazeTheme calls it separately because ColorsYaze()
298 // hardcodes those scalars at the Normal scale.
299 void ApplyDensitySizingToImGui() const;
301};
302
308 public:
309 static ThemeManager& Get();
310
311 // Theme management
312 absl::Status LoadTheme(const std::string& theme_name);
313 absl::Status SaveTheme(const Theme& theme, const std::string& filename);
314 absl::Status LoadThemeFromFile(const std::string& filepath);
315 // Non-const: records the chosen filepath in theme_file_paths_ on success so
316 // subsequent GetCurrentThemeFilePath calls round-trip for themes created or
317 // renamed via the save dialog (not just ones that were loaded from disk).
318 absl::Status SaveThemeToFile(const Theme& theme, const std::string& filepath);
319
320 // Dynamic theme discovery - replaces hardcoded theme lists with automatic
321 // discovery This works across development builds, macOS app bundles, and
322 // other deployment scenarios
323 std::vector<std::string> DiscoverAvailableThemeFiles() const;
324 absl::Status LoadAllAvailableThemes();
325 absl::Status RefreshAvailableThemes(); // Public method to refresh at runtime
326
327 // Built-in themes
329 std::vector<std::string> GetAvailableThemes() const;
330 const Theme* GetTheme(const std::string& name) const;
331 const Theme& GetCurrentTheme() const { return current_theme_; }
332 const std::string& GetCurrentThemeName() const { return current_theme_name_; }
333
334 // Filesystem path the current theme was loaded from (or last saved to via
335 // SaveThemeToFile). Returns empty string for Classic YAZE (no file) or when
336 // no file association exists; "Save Over Current" uses this to decide
337 // whether the overwrite action is available.
338 std::string GetCurrentThemeFilePath() const;
339
340 // Theme application
341 void ApplyTheme(const std::string& theme_name);
342 void ApplyTheme(const Theme& theme);
343 // Re-apply `theme` as the canonical version of the theme it names. Unlike
344 // ApplyTheme(const Theme&), which paints whatever struct it is handed (the
345 // theme editor's in-progress edits go through that), this routes Classic
346 // YAZE back to ApplyClassicYazeTheme() because BuildClassicYazeTheme's
347 // struct cannot reproduce ColorsYaze(). Use it on restore/re-apply paths:
348 // EndPreview, live-preview cancel, and the Display Density combo.
349 void ReapplyTheme(const Theme& theme);
350 // Apply the original ColorsYaze() function. `density` overrides the density
351 // carried over from the outgoing theme — ApplyTheme(const Theme&) passes the
352 // incoming struct's preset so the Display Density combo still works while
353 // Classic YAZE is active.
355 std::optional<DensityPreset> density = std::nullopt);
356
357 // Theme-changed callback. Fired after any successful theme application with
358 // the current theme name. Keeps persistence decoupled from ThemeManager —
359 // the editor wires this to UserSettings so the selection survives restart.
360 using ThemeChangedCallback = std::function<void(const std::string&)>;
362 on_theme_changed_ = std::move(callback);
363 }
364
365 // Theme preview (for hover preview in selector)
366 void StartPreview(const std::string& theme_name);
367 void EndPreview();
368 bool IsPreviewActive() const;
369
370 // Smooth theme transitions
371 void UpdateTransition();
372 bool IsTransitioning() const { return transitioning_; }
373
374 // Theme creation and editing
375 Theme CreateCustomTheme(const std::string& name);
376 void ShowThemeEditor(bool* p_open);
377 void ShowThemeSelector(bool* p_open);
378 void ShowSimpleThemeEditor(bool* p_open);
379
380 // Accent color derivation - generate harmonious theme from single color
381 Theme GenerateThemeFromAccent(const Color& accent, bool dark_mode = true);
382 void ApplyAccentColor(const Color& accent, bool dark_mode = true);
383
384 // Integration with welcome screen
388
389 // Export current theme as JSON string for Web/WASM sync
390 std::string ExportCurrentThemeJson() const;
391
392 // Convenient theme color access interface
393 Color GetThemeColor(const std::string& color_name) const;
394 ImVec4 GetThemeColorVec4(const std::string& color_name) const;
395
396 // Material Design color accessors
415
416 private:
418
419 std::map<std::string, Theme> themes_;
420 // Display name → filesystem path captured at LoadThemeFromFile time. Lets
421 // GetCurrentThemeFilePath return a path that survives display names with
422 // spaces/apostrophes (e.g., "Majora's Moon" → majoras_moon.theme).
423 std::map<std::string, std::string> theme_file_paths_;
425 std::string current_theme_name_ = "Classic YAZE";
426
427 // Preview state for hover preview in theme selector
428 bool preview_active_ = false;
431
432 // True while InitializeBuiltInThemes is constructing the singleton. Guards
433 // ApplyClassicYazeTheme from calling AgentUI::RefreshTheme, which would
434 // recursively re-enter ThemeManager::Get() and abort on __cxa_guard_acquire.
436
437 // Smooth transition state (lerps ImGui colors per-frame)
438 bool transitioning_ = false;
440 ImVec4 transition_from_[ImGuiCol_COUNT] = {};
441 ImVec4 transition_to_[ImGuiCol_COUNT] = {};
442
443 // Fires on_theme_changed_ with current_theme_name_. Safe to call when no
444 // callback is set (becomes a no-op).
445 void NotifyThemeChanged();
446
448
450 // `declared_keys`, when non-null, receives every recognised key the file
451 // actually contained. ApplySmartDefaults needs that to tell "the author
452 // omitted this field" from "the author chose black": a default-constructed
453 // Color is opaque black, so the two are identical by value.
454 absl::Status ParseThemeFile(const std::string& content, Theme& theme,
455 std::set<std::string>* declared_keys = nullptr);
456 // Fill missing properties from primary colors. Fields named in
457 // `declared_keys` are left exactly as the file set them.
458 void ApplySmartDefaults(Theme& theme,
459 const std::set<std::string>& declared_keys = {});
460 Color ParseColorFromString(const std::string& color_str) const;
461 std::string SerializeTheme(const Theme& theme) const;
462
463 // Helper methods for path resolution
464 std::vector<std::string> GetThemeSearchPaths() const;
465 std::string GetThemesDirectory() const;
466 std::string GetUserThemesDirectory() const; // Returns ~/.yaze/themes/
467};
468
469// Global convenience functions for easy theme color access
470// Material Design color accessors - global convenience functions
471inline Color GetThemeColor(const std::string& color_name) {
472 return ThemeManager::Get().GetThemeColor(color_name);
473}
474
475inline ImVec4 GetThemeColorVec4(const std::string& color_name) {
476 return ThemeManager::Get().GetThemeColorVec4(color_name);
477}
478
479// Material Design color accessors
481 return ThemeManager::Get().GetPrimary();
482}
491}
493 return ThemeManager::Get().GetSurface();
494}
509}
515}
517 return ThemeManager::Get().GetOutline();
518}
525inline Color GetShadow() {
526 return ThemeManager::Get().GetShadow();
527}
528
529// ImVec4 versions for direct ImGui usage
530inline ImVec4 GetPrimaryVec4() {
532}
533inline ImVec4 GetPrimaryHoverVec4() {
535}
536inline ImVec4 GetPrimaryActiveVec4() {
538}
539inline ImVec4 GetSurfaceVec4() {
541}
554inline ImVec4 GetOnSurfaceVec4() {
556}
560inline ImVec4 GetOnPrimaryVec4() {
562}
563inline ImVec4 GetOutlineVec4() {
565}
566inline ImVec4 GetTextSecondaryVec4() {
568}
569inline ImVec4 GetTextDisabledVec4() {
571}
572inline ImVec4 GetTextPrimaryVec4() {
574 ThemeManager::Get().GetCurrentTheme().text_primary);
575}
576inline ImVec4 GetSuccessVec4() {
577 return ConvertColorToImVec4(ThemeManager::Get().GetCurrentTheme().success);
578}
579inline ImVec4 GetWarningVec4() {
580 return ConvertColorToImVec4(ThemeManager::Get().GetCurrentTheme().warning);
581}
582inline ImVec4 GetErrorVec4() {
583 return ConvertColorToImVec4(ThemeManager::Get().GetCurrentTheme().error);
584}
585inline ImVec4 GetShadowVec4() {
587}
588} // namespace gui
589
590} // namespace yaze
591
592#endif // YAZE_APP_GUI_THEME_MANAGER_H
Manages themes, loading, saving, and switching.
Color GetSurfaceContainerHigh() const
absl::Status ParseThemeFile(const std::string &content, Theme &theme, std::set< std::string > *declared_keys=nullptr)
Color ParseColorFromString(const std::string &color_str) const
Color GetSurfaceVariant() const
std::string preview_original_name_
Color GetTextSecondary() const
void ShowThemeEditor(bool *p_open)
Color GetWelcomeScreenBackground() const
ImVec4 GetThemeColorVec4(const std::string &color_name) const
void ReapplyTheme(const Theme &theme)
void StartPreview(const std::string &theme_name)
ThemeChangedCallback on_theme_changed_
absl::Status SaveTheme(const Theme &theme, const std::string &filename)
std::string GetCurrentThemeFilePath() const
std::function< void(const std::string &)> ThemeChangedCallback
std::map< std::string, Theme > themes_
absl::Status LoadTheme(const std::string &theme_name)
const Theme & GetCurrentTheme() const
Color GetPrimaryHover() const
absl::Status LoadThemeFromFile(const std::string &filepath)
std::map< std::string, std::string > theme_file_paths_
void ApplyTheme(const std::string &theme_name)
std::string GetThemesDirectory() const
std::string current_theme_name_
ImVec4 transition_to_[ImGuiCol_COUNT]
Color GetWelcomeScreenAccent() const
void ApplyAccentColor(const Color &accent, bool dark_mode=true)
std::vector< std::string > DiscoverAvailableThemeFiles() const
std::string GetUserThemesDirectory() const
Theme CreateCustomTheme(const std::string &name)
absl::Status LoadAllAvailableThemes()
Color GetSurfaceContainer() const
Color GetOnSurfaceVariant() const
std::vector< std::string > GetThemeSearchPaths() const
void SetOnThemeChangedCallback(ThemeChangedCallback callback)
static ThemeManager & Get()
Color GetThemeColor(const std::string &color_name) const
void ApplyClassicYazeTheme(std::optional< DensityPreset > density=std::nullopt)
const std::string & GetCurrentThemeName() const
void ShowThemeSelector(bool *p_open)
Theme GenerateThemeFromAccent(const Color &accent, bool dark_mode=true)
const Theme * GetTheme(const std::string &name) const
std::string SerializeTheme(const Theme &theme) const
absl::Status RefreshAvailableThemes()
Color GetTextDisabled() const
std::string ExportCurrentThemeJson() const
absl::Status SaveThemeToFile(const Theme &theme, const std::string &filepath)
void ApplySmartDefaults(Theme &theme, const std::set< std::string > &declared_keys={})
Color GetSurfaceContainerHighest() const
Color GetWelcomeScreenBorder() const
void ShowSimpleThemeEditor(bool *p_open)
Color GetPrimaryActive() const
ImVec4 transition_from_[ImGuiCol_COUNT]
std::vector< std::string > GetAvailableThemes() const
ImVec4 ConvertColorToImVec4(const Color &color)
Definition color.h:140
ImVec4 GetErrorVec4()
ImVec4 GetSurfaceContainerHighestVec4()
Color GetPrimaryHover()
Color GetOnPrimary()
ImVec4 GetThemeColorVec4(const std::string &color_name)
Color GetOnSurfaceVariant()
ImVec4 GetOnPrimaryVec4()
Color GetTextDisabled()
ImVec4 GetPrimaryActiveVec4()
Color GetSurfaceContainerHighest()
Color GetOnSurface()
ImVec4 GetPrimaryVec4()
ImVec4 GetTextPrimaryVec4()
Color GetSecondary()
Color GetSurfaceContainer()
ImVec4 GetSurfaceVariantVec4()
ImVec4 GetSurfaceVec4()
DensityPreset
Typography and spacing density presets.
ImVec4 GetTextDisabledVec4()
Color GetTextSecondary()
Color GetOutline()
Color GetShadow()
ImVec4 GetTextSecondaryVec4()
ImVec4 GetShadowVec4()
ImVec4 GetSurfaceContainerHighVec4()
ImVec4 GetSuccessVec4()
ImVec4 GetPrimaryHoverVec4()
Color GetPrimaryActive()
Color GetPrimary()
Color GetSurface()
ImVec4 GetOutlineVec4()
Color GetThemeColor(const std::string &color_name)
Color GetSurfaceContainerHigh()
ImVec4 GetOnSurfaceVariantVec4()
ImVec4 GetOnSurfaceVec4()
ImVec4 GetSurfaceContainerVec4()
Color GetSurfaceVariant()
ImVec4 GetWarningVec4()
Comprehensive theme structure for YAZE.
Color tab_dimmed_selected_overline
std::string description
std::string name
Color nav_windowing_highlight
float panel_padding_multiplier
float widget_height_multiplier
struct yaze::gui::Theme::DungeonColors dungeon
struct yaze::gui::Theme::AgentTheme agent
void ApplyDensitySizingToImGui() const
void ApplyToImGui() const
float button_padding_multiplier
float table_row_height_multiplier
float toolbar_height_multiplier
DensityPreset density_preset
void ApplyDensityPreset(DensityPreset preset)
std::string author
float canvas_toolbar_multiplier