Skip to main content

runmat_plot/styling/
theme.rs

1//! Modern dark theme system with professional styling
2//!
3//! Provides a sleek, modern dark theme with excellent contrast,
4//! readability, and visual hierarchy.
5
6use glam::Vec4;
7
8/// Professional color palette for the modern dark theme
9#[derive(Debug, Clone)]
10pub struct ModernDarkTheme {
11    // Background colors
12    pub background_primary: Vec4,
13    pub background_secondary: Vec4,
14    pub background_tertiary: Vec4,
15
16    // Text colors
17    pub text_primary: Vec4,
18    pub text_secondary: Vec4,
19    pub text_accent: Vec4,
20
21    // Accent colors (inspired by the green theme in the image)
22    pub accent_primary: Vec4,
23    pub accent_secondary: Vec4,
24    pub accent_success: Vec4,
25    pub accent_warning: Vec4,
26    pub accent_error: Vec4,
27
28    // Plot colors
29    pub plot_background: Vec4,
30    pub grid_major: Vec4,
31    pub grid_minor: Vec4,
32    pub axis_color: Vec4,
33
34    // Data colors (beautiful palette for multiple series)
35    pub data_colors: Vec<Vec4>,
36}
37
38impl Default for ModernDarkTheme {
39    fn default() -> Self {
40        Self {
41            // Rich dark backgrounds with subtle gradients
42            background_primary: Vec4::new(0.08, 0.09, 0.11, 1.0), // Deep charcoal
43            background_secondary: Vec4::new(0.12, 0.14, 0.16, 1.0), // Lighter charcoal
44            background_tertiary: Vec4::new(0.16, 0.18, 0.21, 1.0), // Card backgrounds
45
46            // High contrast text for excellent readability
47            text_primary: Vec4::new(0.95, 0.96, 0.97, 1.0), // Almost white
48            text_secondary: Vec4::new(0.75, 0.78, 0.82, 1.0), // Light grey
49            text_accent: Vec4::new(0.40, 0.85, 0.55, 1.0),  // Beautiful green accent
50
51            // Modern accent colors inspired by the provided image
52            accent_primary: Vec4::new(0.35, 0.78, 0.48, 1.0), // Primary green
53            accent_secondary: Vec4::new(0.28, 0.65, 0.40, 1.0), // Darker green
54            accent_success: Vec4::new(0.42, 0.85, 0.55, 1.0), // Success green
55            accent_warning: Vec4::new(0.95, 0.75, 0.25, 1.0), // Warm amber
56            accent_error: Vec4::new(0.92, 0.35, 0.35, 1.0),   // Soft red
57
58            // Professional plot styling
59            plot_background: Vec4::new(0.10, 0.11, 0.13, 1.0), // Slightly lighter than main bg
60            grid_major: Vec4::new(0.25, 0.27, 0.30, 0.6),      // Subtle grid lines
61            grid_minor: Vec4::new(0.20, 0.22, 0.25, 0.3),      // Very subtle minor grid
62            axis_color: Vec4::new(0.65, 0.68, 0.72, 1.0),      // Clear axis lines
63
64            // Beautiful data color palette (carefully chosen for contrast and aesthetics)
65            data_colors: vec![
66                Vec4::new(0.35, 0.78, 0.48, 1.0), // Primary green
67                Vec4::new(0.25, 0.65, 0.85, 1.0), // Beautiful blue
68                Vec4::new(0.95, 0.55, 0.25, 1.0), // Warm orange
69                Vec4::new(0.75, 0.35, 0.85, 1.0), // Royal purple
70                Vec4::new(0.95, 0.75, 0.25, 1.0), // Golden yellow
71                Vec4::new(0.85, 0.35, 0.55, 1.0), // Rose pink
72                Vec4::new(0.25, 0.85, 0.75, 1.0), // Turquoise
73                Vec4::new(0.65, 0.75, 0.35, 1.0), // Lime green
74            ],
75        }
76    }
77}
78
79impl ModernDarkTheme {
80    /// Get data color by index (cycles through palette)
81    pub fn get_data_color(&self, index: usize) -> Vec4 {
82        self.data_colors[index % self.data_colors.len()]
83    }
84
85    /// Create a lighter variant of a color (for highlights)
86    pub fn lighten_color(color: Vec4, factor: f32) -> Vec4 {
87        Vec4::new(
88            (color.x + factor).min(1.0),
89            (color.y + factor).min(1.0),
90            (color.z + factor).min(1.0),
91            color.w,
92        )
93    }
94
95    /// Create a darker variant of a color (for shadows)
96    pub fn darken_color(color: Vec4, factor: f32) -> Vec4 {
97        Vec4::new(
98            (color.x - factor).max(0.0),
99            (color.y - factor).max(0.0),
100            (color.z - factor).max(0.0),
101            color.w,
102        )
103    }
104
105    /// Apply this theme to egui context
106    #[cfg(any(feature = "gui", feature = "egui-overlay"))]
107    pub fn apply_to_egui(&self, ctx: &egui::Context) {
108        let mut visuals = egui::Visuals::dark();
109
110        // Main UI colors
111        visuals.window_fill = egui::Color32::from_rgba_unmultiplied(
112            (self.background_secondary.x * 255.0) as u8,
113            (self.background_secondary.y * 255.0) as u8,
114            (self.background_secondary.z * 255.0) as u8,
115            (self.background_secondary.w * 255.0) as u8,
116        );
117
118        visuals.panel_fill = egui::Color32::from_rgba_unmultiplied(
119            (self.background_tertiary.x * 255.0) as u8,
120            (self.background_tertiary.y * 255.0) as u8,
121            (self.background_tertiary.z * 255.0) as u8,
122            (self.background_tertiary.w * 255.0) as u8,
123        );
124
125        // Text colors
126        visuals.widgets.noninteractive.fg_stroke.color = egui::Color32::from_rgba_unmultiplied(
127            (self.text_primary.x * 255.0) as u8,
128            (self.text_primary.y * 255.0) as u8,
129            (self.text_primary.z * 255.0) as u8,
130            (self.text_primary.w * 255.0) as u8,
131        );
132
133        // Accent colors
134        visuals.selection.bg_fill = egui::Color32::from_rgba_unmultiplied(
135            (self.accent_primary.x * 255.0) as u8,
136            (self.accent_primary.y * 255.0) as u8,
137            (self.accent_primary.z * 255.0) as u8,
138            64, // Semi-transparent
139        );
140
141        // Apply the theme
142        ctx.set_visuals(visuals);
143    }
144}
145
146/// Typography system for professional text rendering
147#[derive(Debug, Clone)]
148pub struct Typography {
149    pub title_font_size: f32,
150    pub subtitle_font_size: f32,
151    pub axis_label_font_size: f32,
152    pub tick_label_font_size: f32,
153    pub legend_font_size: f32,
154
155    pub title_font_family: String,
156    pub body_font_family: String,
157    pub monospace_font_family: String,
158}
159
160impl Default for Typography {
161    fn default() -> Self {
162        Self {
163            title_font_size: 18.0,
164            subtitle_font_size: 14.0,
165            axis_label_font_size: 12.0,
166            tick_label_font_size: 10.0,
167            legend_font_size: 11.0,
168
169            title_font_family: "SF Pro Display".to_string(), // Modern, clean
170            body_font_family: "SF Pro Text".to_string(),     // Readable
171            monospace_font_family: "SF Mono".to_string(),    // For numbers
172        }
173    }
174}
175
176/// Professional spacing and layout constants
177#[derive(Debug, Clone)]
178pub struct Layout {
179    pub plot_padding: f32,
180    pub title_margin: f32,
181    pub axis_margin: f32,
182    pub legend_margin: f32,
183    pub grid_line_width: f32,
184    pub axis_line_width: f32,
185    pub data_line_width: f32,
186    pub point_size: f32,
187}
188
189impl Default for Layout {
190    fn default() -> Self {
191        Self {
192            plot_padding: 20.0,
193            title_margin: 15.0,
194            axis_margin: 10.0,
195            legend_margin: 8.0,
196            grid_line_width: 0.5,
197            axis_line_width: 1.0,
198            data_line_width: 2.0,
199            point_size: 4.0,
200        }
201    }
202}