⚠️ VeridianOS Kernel Documentation - This is low-level kernel code. All functions are unsafe unless explicitly marked otherwise. no_std

veridian_kernel/desktop/desktop_ext/
theme.rs

1//! Theme Engine
2//!
3//! Color schemes (light/dark/solarized/nord/dracula) with runtime switching.
4
5#[cfg(feature = "alloc")]
6use alloc::collections::BTreeMap;
7
8/// Named color schemes.
9#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
10pub enum ThemePreset {
11    /// Light theme with white backgrounds.
12    Light,
13    /// Dark theme with dark backgrounds.
14    #[default]
15    Dark,
16    /// Solarized Dark.
17    SolarizedDark,
18    /// Solarized Light.
19    SolarizedLight,
20    /// Nord theme.
21    Nord,
22    /// Dracula theme.
23    Dracula,
24    /// Custom (user-defined).
25    Custom,
26}
27
28/// ARGB color (alpha in high byte).
29#[derive(Debug, Clone, Copy, PartialEq, Eq)]
30pub struct ThemeColor(pub u32);
31
32impl ThemeColor {
33    /// Create a color from ARGB components.
34    pub const fn from_argb(a: u8, r: u8, g: u8, b: u8) -> Self {
35        Self(((a as u32) << 24) | ((r as u32) << 16) | ((g as u32) << 8) | (b as u32))
36    }
37
38    /// Create a fully opaque color from RGB.
39    pub const fn from_rgb(r: u8, g: u8, b: u8) -> Self {
40        Self::from_argb(0xFF, r, g, b)
41    }
42
43    /// Get the alpha component.
44    pub const fn alpha(self) -> u8 {
45        (self.0 >> 24) as u8
46    }
47
48    /// Get the red component.
49    pub const fn red(self) -> u8 {
50        (self.0 >> 16) as u8
51    }
52
53    /// Get the green component.
54    pub const fn green(self) -> u8 {
55        (self.0 >> 8) as u8
56    }
57
58    /// Get the blue component.
59    pub const fn blue(self) -> u8 {
60        self.0 as u8
61    }
62
63    /// Blend two colors using integer alpha blending.
64    /// `alpha_256` is 0-256 (not 0-255) for shift-based division.
65    pub fn blend(self, other: Self, alpha_256: u32) -> Self {
66        let inv = 256 - alpha_256;
67        let r = ((self.red() as u32 * inv) + (other.red() as u32 * alpha_256)) >> 8;
68        let g = ((self.green() as u32 * inv) + (other.green() as u32 * alpha_256)) >> 8;
69        let b = ((self.blue() as u32 * inv) + (other.blue() as u32 * alpha_256)) >> 8;
70        Self::from_rgb(r as u8, g as u8, b as u8)
71    }
72
73    /// Darken a color by a percentage (0-100).
74    pub fn darken(self, percent: u32) -> Self {
75        let factor = 100u32.saturating_sub(percent);
76        let r = (self.red() as u32 * factor) / 100;
77        let g = (self.green() as u32 * factor) / 100;
78        let b = (self.blue() as u32 * factor) / 100;
79        Self::from_argb(self.alpha(), r as u8, g as u8, b as u8)
80    }
81
82    /// Lighten a color by a percentage (0-100).
83    pub fn lighten(self, percent: u32) -> Self {
84        let factor = percent;
85        let r = self.red() as u32 + ((255 - self.red() as u32) * factor) / 100;
86        let g = self.green() as u32 + ((255 - self.green() as u32) * factor) / 100;
87        let b = self.blue() as u32 + ((255 - self.blue() as u32) * factor) / 100;
88        Self::from_argb(
89            self.alpha(),
90            r.min(255) as u8,
91            g.min(255) as u8,
92            b.min(255) as u8,
93        )
94    }
95}
96
97/// Color slots in the theme.
98#[derive(Debug, Clone, Copy, PartialEq, Eq)]
99pub struct ThemeColors {
100    // Window
101    pub window_background: ThemeColor,
102    pub window_foreground: ThemeColor,
103    pub window_border: ThemeColor,
104    pub window_border_focused: ThemeColor,
105
106    // Title bar
107    pub titlebar_background: ThemeColor,
108    pub titlebar_foreground: ThemeColor,
109    pub titlebar_background_inactive: ThemeColor,
110    pub titlebar_foreground_inactive: ThemeColor,
111
112    // Buttons
113    pub button_background: ThemeColor,
114    pub button_foreground: ThemeColor,
115    pub button_hover: ThemeColor,
116    pub button_pressed: ThemeColor,
117
118    // Accent / selection
119    pub accent: ThemeColor,
120    pub selection_background: ThemeColor,
121    pub selection_foreground: ThemeColor,
122
123    // Desktop
124    pub desktop_background: ThemeColor,
125    pub panel_background: ThemeColor,
126    pub panel_foreground: ThemeColor,
127
128    // Text
129    pub text_primary: ThemeColor,
130    pub text_secondary: ThemeColor,
131    pub text_disabled: ThemeColor,
132
133    // Status colors
134    pub error: ThemeColor,
135    pub warning: ThemeColor,
136    pub success: ThemeColor,
137    pub info: ThemeColor,
138
139    // Scrollbar
140    pub scrollbar_track: ThemeColor,
141    pub scrollbar_thumb: ThemeColor,
142
143    // Tooltip
144    pub tooltip_background: ThemeColor,
145    pub tooltip_foreground: ThemeColor,
146}
147
148impl ThemeColors {
149    /// Create the default dark theme.
150    pub const fn dark() -> Self {
151        Self {
152            window_background: ThemeColor::from_rgb(0x2D, 0x2D, 0x2D),
153            window_foreground: ThemeColor::from_rgb(0xE0, 0xE0, 0xE0),
154            window_border: ThemeColor::from_rgb(0x44, 0x44, 0x44),
155            window_border_focused: ThemeColor::from_rgb(0x5A, 0x9F, 0xD4),
156            titlebar_background: ThemeColor::from_rgb(0x38, 0x38, 0x38),
157            titlebar_foreground: ThemeColor::from_rgb(0xE0, 0xE0, 0xE0),
158            titlebar_background_inactive: ThemeColor::from_rgb(0x30, 0x30, 0x30),
159            titlebar_foreground_inactive: ThemeColor::from_rgb(0x80, 0x80, 0x80),
160            button_background: ThemeColor::from_rgb(0x45, 0x45, 0x45),
161            button_foreground: ThemeColor::from_rgb(0xE0, 0xE0, 0xE0),
162            button_hover: ThemeColor::from_rgb(0x55, 0x55, 0x55),
163            button_pressed: ThemeColor::from_rgb(0x35, 0x35, 0x35),
164            accent: ThemeColor::from_rgb(0x5A, 0x9F, 0xD4),
165            selection_background: ThemeColor::from_rgb(0x26, 0x4F, 0x78),
166            selection_foreground: ThemeColor::from_rgb(0xFF, 0xFF, 0xFF),
167            desktop_background: ThemeColor::from_rgb(0x1A, 0x1A, 0x2E),
168            panel_background: ThemeColor::from_rgb(0x20, 0x20, 0x20),
169            panel_foreground: ThemeColor::from_rgb(0xD0, 0xD0, 0xD0),
170            text_primary: ThemeColor::from_rgb(0xE0, 0xE0, 0xE0),
171            text_secondary: ThemeColor::from_rgb(0xA0, 0xA0, 0xA0),
172            text_disabled: ThemeColor::from_rgb(0x60, 0x60, 0x60),
173            error: ThemeColor::from_rgb(0xE0, 0x50, 0x50),
174            warning: ThemeColor::from_rgb(0xE0, 0xA0, 0x30),
175            success: ThemeColor::from_rgb(0x50, 0xC8, 0x78),
176            info: ThemeColor::from_rgb(0x5A, 0x9F, 0xD4),
177            scrollbar_track: ThemeColor::from_rgb(0x30, 0x30, 0x30),
178            scrollbar_thumb: ThemeColor::from_rgb(0x55, 0x55, 0x55),
179            tooltip_background: ThemeColor::from_rgb(0x40, 0x40, 0x40),
180            tooltip_foreground: ThemeColor::from_rgb(0xE0, 0xE0, 0xE0),
181        }
182    }
183
184    /// Create the light theme.
185    pub const fn light() -> Self {
186        Self {
187            window_background: ThemeColor::from_rgb(0xF5, 0xF5, 0xF5),
188            window_foreground: ThemeColor::from_rgb(0x20, 0x20, 0x20),
189            window_border: ThemeColor::from_rgb(0xCC, 0xCC, 0xCC),
190            window_border_focused: ThemeColor::from_rgb(0x33, 0x7A, 0xB7),
191            titlebar_background: ThemeColor::from_rgb(0xE8, 0xE8, 0xE8),
192            titlebar_foreground: ThemeColor::from_rgb(0x20, 0x20, 0x20),
193            titlebar_background_inactive: ThemeColor::from_rgb(0xF0, 0xF0, 0xF0),
194            titlebar_foreground_inactive: ThemeColor::from_rgb(0x80, 0x80, 0x80),
195            button_background: ThemeColor::from_rgb(0xE0, 0xE0, 0xE0),
196            button_foreground: ThemeColor::from_rgb(0x20, 0x20, 0x20),
197            button_hover: ThemeColor::from_rgb(0xD0, 0xD0, 0xD0),
198            button_pressed: ThemeColor::from_rgb(0xC0, 0xC0, 0xC0),
199            accent: ThemeColor::from_rgb(0x33, 0x7A, 0xB7),
200            selection_background: ThemeColor::from_rgb(0xB3, 0xD4, 0xFC),
201            selection_foreground: ThemeColor::from_rgb(0x00, 0x00, 0x00),
202            desktop_background: ThemeColor::from_rgb(0xDE, 0xDE, 0xE8),
203            panel_background: ThemeColor::from_rgb(0xF0, 0xF0, 0xF0),
204            panel_foreground: ThemeColor::from_rgb(0x30, 0x30, 0x30),
205            text_primary: ThemeColor::from_rgb(0x20, 0x20, 0x20),
206            text_secondary: ThemeColor::from_rgb(0x60, 0x60, 0x60),
207            text_disabled: ThemeColor::from_rgb(0xA0, 0xA0, 0xA0),
208            error: ThemeColor::from_rgb(0xD3, 0x2F, 0x2F),
209            warning: ThemeColor::from_rgb(0xF5, 0x7C, 0x00),
210            success: ThemeColor::from_rgb(0x38, 0x8E, 0x3C),
211            info: ThemeColor::from_rgb(0x19, 0x76, 0xD2),
212            scrollbar_track: ThemeColor::from_rgb(0xE8, 0xE8, 0xE8),
213            scrollbar_thumb: ThemeColor::from_rgb(0xB0, 0xB0, 0xB0),
214            tooltip_background: ThemeColor::from_rgb(0x30, 0x30, 0x30),
215            tooltip_foreground: ThemeColor::from_rgb(0xF0, 0xF0, 0xF0),
216        }
217    }
218
219    /// Create the Solarized Dark theme.
220    pub const fn solarized_dark() -> Self {
221        Self {
222            window_background: ThemeColor::from_rgb(0x00, 0x2B, 0x36),
223            window_foreground: ThemeColor::from_rgb(0x83, 0x94, 0x96),
224            window_border: ThemeColor::from_rgb(0x07, 0x36, 0x42),
225            window_border_focused: ThemeColor::from_rgb(0x26, 0x8B, 0xD2),
226            titlebar_background: ThemeColor::from_rgb(0x07, 0x36, 0x42),
227            titlebar_foreground: ThemeColor::from_rgb(0x93, 0xA1, 0xA1),
228            titlebar_background_inactive: ThemeColor::from_rgb(0x00, 0x2B, 0x36),
229            titlebar_foreground_inactive: ThemeColor::from_rgb(0x58, 0x6E, 0x75),
230            button_background: ThemeColor::from_rgb(0x07, 0x36, 0x42),
231            button_foreground: ThemeColor::from_rgb(0x93, 0xA1, 0xA1),
232            button_hover: ThemeColor::from_rgb(0x0A, 0x43, 0x50),
233            button_pressed: ThemeColor::from_rgb(0x05, 0x2A, 0x33),
234            accent: ThemeColor::from_rgb(0x26, 0x8B, 0xD2),
235            selection_background: ThemeColor::from_rgb(0x07, 0x36, 0x42),
236            selection_foreground: ThemeColor::from_rgb(0xFD, 0xF6, 0xE3),
237            desktop_background: ThemeColor::from_rgb(0x00, 0x2B, 0x36),
238            panel_background: ThemeColor::from_rgb(0x07, 0x36, 0x42),
239            panel_foreground: ThemeColor::from_rgb(0x83, 0x94, 0x96),
240            text_primary: ThemeColor::from_rgb(0x83, 0x94, 0x96),
241            text_secondary: ThemeColor::from_rgb(0x58, 0x6E, 0x75),
242            text_disabled: ThemeColor::from_rgb(0x3B, 0x51, 0x50),
243            error: ThemeColor::from_rgb(0xDC, 0x32, 0x2F),
244            warning: ThemeColor::from_rgb(0xCB, 0x4B, 0x16),
245            success: ThemeColor::from_rgb(0x85, 0x99, 0x00),
246            info: ThemeColor::from_rgb(0x26, 0x8B, 0xD2),
247            scrollbar_track: ThemeColor::from_rgb(0x00, 0x2B, 0x36),
248            scrollbar_thumb: ThemeColor::from_rgb(0x07, 0x36, 0x42),
249            tooltip_background: ThemeColor::from_rgb(0x07, 0x36, 0x42),
250            tooltip_foreground: ThemeColor::from_rgb(0xFD, 0xF6, 0xE3),
251        }
252    }
253
254    /// Create the Solarized Light theme.
255    pub const fn solarized_light() -> Self {
256        Self {
257            window_background: ThemeColor::from_rgb(0xFD, 0xF6, 0xE3),
258            window_foreground: ThemeColor::from_rgb(0x65, 0x7B, 0x83),
259            window_border: ThemeColor::from_rgb(0xEE, 0xE8, 0xD5),
260            window_border_focused: ThemeColor::from_rgb(0x26, 0x8B, 0xD2),
261            titlebar_background: ThemeColor::from_rgb(0xEE, 0xE8, 0xD5),
262            titlebar_foreground: ThemeColor::from_rgb(0x58, 0x6E, 0x75),
263            titlebar_background_inactive: ThemeColor::from_rgb(0xFD, 0xF6, 0xE3),
264            titlebar_foreground_inactive: ThemeColor::from_rgb(0x93, 0xA1, 0xA1),
265            button_background: ThemeColor::from_rgb(0xEE, 0xE8, 0xD5),
266            button_foreground: ThemeColor::from_rgb(0x58, 0x6E, 0x75),
267            button_hover: ThemeColor::from_rgb(0xE0, 0xDA, 0xC7),
268            button_pressed: ThemeColor::from_rgb(0xD3, 0xCD, 0xBB),
269            accent: ThemeColor::from_rgb(0x26, 0x8B, 0xD2),
270            selection_background: ThemeColor::from_rgb(0xEE, 0xE8, 0xD5),
271            selection_foreground: ThemeColor::from_rgb(0x00, 0x2B, 0x36),
272            desktop_background: ThemeColor::from_rgb(0xFD, 0xF6, 0xE3),
273            panel_background: ThemeColor::from_rgb(0xEE, 0xE8, 0xD5),
274            panel_foreground: ThemeColor::from_rgb(0x65, 0x7B, 0x83),
275            text_primary: ThemeColor::from_rgb(0x65, 0x7B, 0x83),
276            text_secondary: ThemeColor::from_rgb(0x93, 0xA1, 0xA1),
277            text_disabled: ThemeColor::from_rgb(0xC0, 0xBB, 0xAA),
278            error: ThemeColor::from_rgb(0xDC, 0x32, 0x2F),
279            warning: ThemeColor::from_rgb(0xCB, 0x4B, 0x16),
280            success: ThemeColor::from_rgb(0x85, 0x99, 0x00),
281            info: ThemeColor::from_rgb(0x26, 0x8B, 0xD2),
282            scrollbar_track: ThemeColor::from_rgb(0xFD, 0xF6, 0xE3),
283            scrollbar_thumb: ThemeColor::from_rgb(0xEE, 0xE8, 0xD5),
284            tooltip_background: ThemeColor::from_rgb(0x07, 0x36, 0x42),
285            tooltip_foreground: ThemeColor::from_rgb(0xFD, 0xF6, 0xE3),
286        }
287    }
288
289    /// Create the Nord theme.
290    pub const fn nord() -> Self {
291        Self {
292            window_background: ThemeColor::from_rgb(0x2E, 0x34, 0x40),
293            window_foreground: ThemeColor::from_rgb(0xD8, 0xDE, 0xE9),
294            window_border: ThemeColor::from_rgb(0x3B, 0x42, 0x52),
295            window_border_focused: ThemeColor::from_rgb(0x88, 0xC0, 0xD0),
296            titlebar_background: ThemeColor::from_rgb(0x3B, 0x42, 0x52),
297            titlebar_foreground: ThemeColor::from_rgb(0xEC, 0xEF, 0xF4),
298            titlebar_background_inactive: ThemeColor::from_rgb(0x2E, 0x34, 0x40),
299            titlebar_foreground_inactive: ThemeColor::from_rgb(0x4C, 0x56, 0x6A),
300            button_background: ThemeColor::from_rgb(0x43, 0x4C, 0x5E),
301            button_foreground: ThemeColor::from_rgb(0xEC, 0xEF, 0xF4),
302            button_hover: ThemeColor::from_rgb(0x4C, 0x56, 0x6A),
303            button_pressed: ThemeColor::from_rgb(0x3B, 0x42, 0x52),
304            accent: ThemeColor::from_rgb(0x88, 0xC0, 0xD0),
305            selection_background: ThemeColor::from_rgb(0x43, 0x4C, 0x5E),
306            selection_foreground: ThemeColor::from_rgb(0xEC, 0xEF, 0xF4),
307            desktop_background: ThemeColor::from_rgb(0x2E, 0x34, 0x40),
308            panel_background: ThemeColor::from_rgb(0x3B, 0x42, 0x52),
309            panel_foreground: ThemeColor::from_rgb(0xD8, 0xDE, 0xE9),
310            text_primary: ThemeColor::from_rgb(0xD8, 0xDE, 0xE9),
311            text_secondary: ThemeColor::from_rgb(0x81, 0xA1, 0xC1),
312            text_disabled: ThemeColor::from_rgb(0x4C, 0x56, 0x6A),
313            error: ThemeColor::from_rgb(0xBF, 0x61, 0x6A),
314            warning: ThemeColor::from_rgb(0xEB, 0xCB, 0x8B),
315            success: ThemeColor::from_rgb(0xA3, 0xBE, 0x8C),
316            info: ThemeColor::from_rgb(0x88, 0xC0, 0xD0),
317            scrollbar_track: ThemeColor::from_rgb(0x2E, 0x34, 0x40),
318            scrollbar_thumb: ThemeColor::from_rgb(0x4C, 0x56, 0x6A),
319            tooltip_background: ThemeColor::from_rgb(0x3B, 0x42, 0x52),
320            tooltip_foreground: ThemeColor::from_rgb(0xEC, 0xEF, 0xF4),
321        }
322    }
323
324    /// Create the Dracula theme.
325    pub const fn dracula() -> Self {
326        Self {
327            window_background: ThemeColor::from_rgb(0x28, 0x2A, 0x36),
328            window_foreground: ThemeColor::from_rgb(0xF8, 0xF8, 0xF2),
329            window_border: ThemeColor::from_rgb(0x44, 0x47, 0x5A),
330            window_border_focused: ThemeColor::from_rgb(0xBD, 0x93, 0xF9),
331            titlebar_background: ThemeColor::from_rgb(0x44, 0x47, 0x5A),
332            titlebar_foreground: ThemeColor::from_rgb(0xF8, 0xF8, 0xF2),
333            titlebar_background_inactive: ThemeColor::from_rgb(0x28, 0x2A, 0x36),
334            titlebar_foreground_inactive: ThemeColor::from_rgb(0x62, 0x72, 0xA4),
335            button_background: ThemeColor::from_rgb(0x44, 0x47, 0x5A),
336            button_foreground: ThemeColor::from_rgb(0xF8, 0xF8, 0xF2),
337            button_hover: ThemeColor::from_rgb(0x55, 0x58, 0x6E),
338            button_pressed: ThemeColor::from_rgb(0x38, 0x3A, 0x4A),
339            accent: ThemeColor::from_rgb(0xBD, 0x93, 0xF9),
340            selection_background: ThemeColor::from_rgb(0x44, 0x47, 0x5A),
341            selection_foreground: ThemeColor::from_rgb(0xF8, 0xF8, 0xF2),
342            desktop_background: ThemeColor::from_rgb(0x28, 0x2A, 0x36),
343            panel_background: ThemeColor::from_rgb(0x21, 0x22, 0x2C),
344            panel_foreground: ThemeColor::from_rgb(0xF8, 0xF8, 0xF2),
345            text_primary: ThemeColor::from_rgb(0xF8, 0xF8, 0xF2),
346            text_secondary: ThemeColor::from_rgb(0x62, 0x72, 0xA4),
347            text_disabled: ThemeColor::from_rgb(0x44, 0x47, 0x5A),
348            error: ThemeColor::from_rgb(0xFF, 0x55, 0x55),
349            warning: ThemeColor::from_rgb(0xFF, 0xB8, 0x6C),
350            success: ThemeColor::from_rgb(0x50, 0xFA, 0x7B),
351            info: ThemeColor::from_rgb(0x8B, 0xE9, 0xFD),
352            scrollbar_track: ThemeColor::from_rgb(0x28, 0x2A, 0x36),
353            scrollbar_thumb: ThemeColor::from_rgb(0x44, 0x47, 0x5A),
354            tooltip_background: ThemeColor::from_rgb(0x28, 0x2A, 0x36),
355            tooltip_foreground: ThemeColor::from_rgb(0xF8, 0xF8, 0xF2),
356        }
357    }
358}
359
360/// GTK/Qt-style property key for theme mapping stubs.
361#[derive(Debug, Clone, Copy, PartialEq, Eq)]
362pub enum StyleProperty {
363    BackgroundColor,
364    ForegroundColor,
365    BorderColor,
366    BorderWidth,
367    BorderRadius,
368    FontSize,
369    FontWeight,
370    Padding,
371    Margin,
372    Opacity,
373}
374
375/// Icon theme name stub.
376#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
377pub enum IconTheme {
378    #[default]
379    Adwaita,
380    Breeze,
381    Papirus,
382    Custom,
383}
384
385/// Theme manager with runtime switching.
386#[derive(Debug)]
387pub struct ThemeManager {
388    /// Current active theme preset.
389    current_preset: ThemePreset,
390    /// Resolved colors for the current theme.
391    colors: ThemeColors,
392    /// Current icon theme.
393    icon_theme: IconTheme,
394    /// Whether animations should follow theme (affects durations).
395    animate_transitions: bool,
396    /// Custom color overrides (slot index -> color).
397    #[cfg(feature = "alloc")]
398    custom_overrides: BTreeMap<u8, ThemeColor>,
399}
400
401impl Default for ThemeManager {
402    fn default() -> Self {
403        Self::new()
404    }
405}
406
407impl ThemeManager {
408    /// Create a new theme manager with the default dark theme.
409    pub fn new() -> Self {
410        Self {
411            current_preset: ThemePreset::Dark,
412            colors: ThemeColors::dark(),
413            icon_theme: IconTheme::Adwaita,
414            animate_transitions: true,
415            #[cfg(feature = "alloc")]
416            custom_overrides: BTreeMap::new(),
417        }
418    }
419
420    /// Switch to a named theme preset.
421    pub fn set_theme(&mut self, preset: ThemePreset) {
422        self.current_preset = preset;
423        self.colors = match preset {
424            ThemePreset::Light => ThemeColors::light(),
425            ThemePreset::Dark => ThemeColors::dark(),
426            ThemePreset::SolarizedDark => ThemeColors::solarized_dark(),
427            ThemePreset::SolarizedLight => ThemeColors::solarized_light(),
428            ThemePreset::Nord => ThemeColors::nord(),
429            ThemePreset::Dracula => ThemeColors::dracula(),
430            ThemePreset::Custom => self.colors, // Keep current
431        };
432    }
433
434    /// Get current theme colors.
435    pub fn colors(&self) -> &ThemeColors {
436        &self.colors
437    }
438
439    /// Get current theme preset.
440    pub fn current_preset(&self) -> ThemePreset {
441        self.current_preset
442    }
443
444    /// Set custom colors directly.
445    pub fn set_colors(&mut self, colors: ThemeColors) {
446        self.current_preset = ThemePreset::Custom;
447        self.colors = colors;
448    }
449
450    /// Set icon theme.
451    pub fn set_icon_theme(&mut self, theme: IconTheme) {
452        self.icon_theme = theme;
453    }
454
455    /// Get icon theme.
456    pub fn icon_theme(&self) -> IconTheme {
457        self.icon_theme
458    }
459
460    /// Set whether to animate theme transitions.
461    pub fn set_animate_transitions(&mut self, animate: bool) {
462        self.animate_transitions = animate;
463    }
464
465    /// Check if theme transitions should be animated.
466    pub fn animate_transitions(&self) -> bool {
467        self.animate_transitions
468    }
469
470    /// Map a GTK/Qt-style property to the current theme (stub).
471    /// Returns the u32 color or size value for the property.
472    pub fn map_style_property(&self, property: StyleProperty) -> u32 {
473        match property {
474            StyleProperty::BackgroundColor => self.colors.window_background.0,
475            StyleProperty::ForegroundColor => self.colors.window_foreground.0,
476            StyleProperty::BorderColor => self.colors.window_border.0,
477            StyleProperty::BorderWidth => 1,
478            StyleProperty::BorderRadius => 4,
479            StyleProperty::FontSize => 14,
480            StyleProperty::FontWeight => 400,
481            StyleProperty::Padding => 8,
482            StyleProperty::Margin => 4,
483            StyleProperty::Opacity => 255,
484        }
485    }
486
487    /// Get the GTK theme name string for this preset (stub for GTK
488    /// integration).
489    pub fn gtk_theme_name(&self) -> &'static str {
490        match self.current_preset {
491            ThemePreset::Light => "Adwaita",
492            ThemePreset::Dark => "Adwaita-dark",
493            ThemePreset::SolarizedDark | ThemePreset::SolarizedLight => "Solarized",
494            ThemePreset::Nord => "Nordic",
495            ThemePreset::Dracula => "Dracula",
496            ThemePreset::Custom => "Custom",
497        }
498    }
499
500    /// Get the Qt theme variant for this preset (stub for Qt integration).
501    pub fn qt_style_hint(&self) -> u32 {
502        match self.current_preset {
503            ThemePreset::Light | ThemePreset::SolarizedLight => 0, // Light
504            _ => 1,                                                // Dark
505        }
506    }
507}