Skip to main content

egui_charts/ui/replay/
config.rs

1//! Replay configuration
2//!
3//! Visual and behavioral configuration for replay mode.
4
5use super::actions::ReplaySpeed;
6use crate::theme::Theme;
7use crate::tokens::DESIGN_TOKENS;
8use egui::Color32;
9
10/// Colors for replay UI elements
11#[derive(Debug, Clone, PartialEq)]
12pub struct ReplayColors {
13    /// Play button color
14    pub play_color: Color32,
15    /// Pause button color
16    pub pause_color: Color32,
17    /// Stop button color
18    pub stop_color: Color32,
19    /// Progress bar background
20    pub progress_bg: Color32,
21    /// Progress bar filled portion
22    pub progress_fill: Color32,
23    /// Progress bar cursor/handle
24    pub progress_cursor: Color32,
25    /// Marker default color
26    pub marker_color: Color32,
27    /// Speed indicator color
28    pub speed_color: Color32,
29    /// Replay active indicator
30    pub active_indicator: Color32,
31    /// Disabled button color
32    pub disabled_color: Color32,
33}
34
35impl ReplayColors {
36    /// Colours from a theme's UI palette.
37    ///
38    /// The replay widgets paint with the active theme's colours while the configured colours
39    /// equal [`ReplayColors::default`]; set any other colours with [`ReplayConfig::with_colors`].
40    pub fn from_theme(theme: &Theme) -> Self {
41        let ui = &theme.ui;
42        Self {
43            play_color: ui.success,
44            pause_color: ui.warning,
45            stop_color: ui.error,
46            progress_bg: ui.btn_bg_hover,
47            progress_fill: ui.accent,
48            progress_cursor: ui.text,
49            marker_color: ui.warning,
50            speed_color: ui.text,
51            active_indicator: ui.success,
52            disabled_color: ui.text_muted,
53        }
54    }
55}
56
57impl Default for ReplayColors {
58    fn default() -> Self {
59        Self::from_theme(&Theme::dark())
60    }
61}
62
63/// Layout configuration for replay controls
64#[derive(Debug, Clone)]
65pub struct ReplayLayout {
66    /// Height of the replay control bar
67    pub control_bar_height: f32,
68    /// Button size
69    pub btn_size: f32,
70    /// Small button size (for step btns)
71    pub small_button_size: f32,
72    /// Spacing between btns
73    pub btn_spacing: f32,
74    /// Progress bar height
75    pub progress_bar_height: f32,
76    /// Progress bar width (if fixed, None for flexible)
77    pub progress_bar_width: Option<f32>,
78    /// Padding inside the control bar
79    pub padding: f32,
80    /// Speed dropdown width
81    pub speed_dropdown_width: f32,
82    /// Date display width
83    pub date_display_width: f32,
84}
85
86impl ReplayLayout {
87    pub(super) fn resolved(&self, theme: &Theme) -> Self {
88        let defaults = Self::default();
89        let mut layout = self.clone();
90        for (value, default, themed) in [
91            (
92                &mut layout.btn_spacing,
93                defaults.btn_spacing,
94                theme.spacing.sm,
95            ),
96            (
97                &mut layout.progress_bar_height,
98                defaults.progress_bar_height,
99                theme.spacing.lg,
100            ),
101            (&mut layout.padding, defaults.padding, theme.spacing.lg),
102        ] {
103            if *value == default {
104                *value = themed;
105            }
106        }
107        layout
108    }
109}
110
111impl Default for ReplayLayout {
112    fn default() -> Self {
113        Self {
114            control_bar_height: DESIGN_TOKENS.sizing.replay.control_bar_height,
115            btn_size: DESIGN_TOKENS.sizing.replay.button_size,
116            small_button_size: DESIGN_TOKENS.sizing.button_sm,
117            btn_spacing: crate::theme::Theme::default().spacing.sm,
118            progress_bar_height: crate::theme::Theme::default().spacing.lg,
119            progress_bar_width: None,
120            padding: crate::theme::Theme::default().spacing.lg,
121            speed_dropdown_width: DESIGN_TOKENS.sizing.replay.speed_dropdown_width,
122            date_display_width: DESIGN_TOKENS.sizing.replay.date_display_width,
123        }
124    }
125}
126
127/// Behavior configuration for replay
128#[derive(Debug, Clone)]
129pub struct ReplayBehavior {
130    /// Default playback speed
131    pub default_speed: ReplaySpeed,
132    /// Whether to loop when reaching end
133    pub loop_playback: bool,
134    /// Whether to auto-scroll chart during playback
135    pub auto_scroll: bool,
136    /// Number of bars to keep visible ahead of current position
137    pub lookahead_bars: usize,
138    /// Whether to pause on user interaction (e.g., clicking chart)
139    pub pause_on_interaction: bool,
140    /// Min time between bar updates (in seconds)
141    pub min_bar_duration: f64,
142    /// Max time between bar updates (in seconds)
143    pub max_bar_duration: f64,
144    /// Whether to show bar counter
145    pub show_bar_counter: bool,
146    /// Whether to show elapsed time
147    pub show_elapsed_time: bool,
148    /// Whether to show remaining time
149    pub show_remaining_time: bool,
150}
151
152impl Default for ReplayBehavior {
153    fn default() -> Self {
154        Self {
155            default_speed: ReplaySpeed::Normal,
156            loop_playback: false,
157            auto_scroll: true,
158            lookahead_bars: 10,
159            pause_on_interaction: false,
160            min_bar_duration: 0.01,
161            max_bar_duration: 10.0,
162            show_bar_counter: true,
163            show_elapsed_time: true,
164            show_remaining_time: true,
165        }
166    }
167}
168
169/// Complete replay configuration
170#[derive(Debug, Clone, Default)]
171pub struct ReplayConfig {
172    /// Color configuration
173    pub colors: ReplayColors,
174    /// Layout configuration
175    pub layout: ReplayLayout,
176    /// Behavior configuration
177    pub behavior: ReplayBehavior,
178}
179
180impl ReplayConfig {
181    /// Create new configuration with defaults
182    pub fn new() -> Self {
183        Self::default()
184    }
185
186    /// Set colors
187    pub fn with_colors(mut self, colors: ReplayColors) -> Self {
188        self.colors = colors;
189        self
190    }
191
192    /// Set layout
193    pub fn with_layout(mut self, layout: ReplayLayout) -> Self {
194        self.layout = layout;
195        self
196    }
197
198    /// Set behavior
199    pub fn with_behavior(mut self, behavior: ReplayBehavior) -> Self {
200        self.behavior = behavior;
201        self
202    }
203
204    /// Create a compact layout variant
205    pub fn compact() -> Self {
206        Self {
207            layout: ReplayLayout {
208                control_bar_height: DESIGN_TOKENS.sizing.icon_xxl,
209                btn_size: DESIGN_TOKENS.sizing.icon_lg,
210                small_button_size: DESIGN_TOKENS.sizing.technical_labels.elliott_label_size,
211                btn_spacing: crate::theme::Theme::default().spacing.xs,
212                progress_bar_height: crate::theme::Theme::default().spacing.md,
213                padding: crate::theme::Theme::default().spacing.sm,
214                speed_dropdown_width: DESIGN_TOKENS.sizing.technical_labels.pattern_label_width
215                    - crate::theme::Theme::default().spacing.xxxl,
216                date_display_width: DESIGN_TOKENS.sizing.replay.date_display_width
217                    - (crate::theme::Theme::default().spacing.xxxl
218                        + crate::theme::Theme::default().spacing.xxl),
219                ..Default::default()
220            },
221            ..Default::default()
222        }
223    }
224
225    /// Create a minimal layout (just play/pause and progress)
226    pub fn minimal() -> Self {
227        Self {
228            layout: ReplayLayout {
229                control_bar_height: DESIGN_TOKENS.sizing.replay.button_size,
230                btn_size: DESIGN_TOKENS.sizing.button_sm,
231                small_button_size: DESIGN_TOKENS.sizing.icon_md,
232                btn_spacing: crate::theme::Theme::default().spacing.xs,
233                progress_bar_height: crate::theme::Theme::default().spacing.sm,
234                padding: crate::theme::Theme::default().spacing.sm,
235                speed_dropdown_width: DESIGN_TOKENS.sizing.technical_labels.line_label_width,
236                date_display_width: DESIGN_TOKENS.sizing.replay.date_display_width
237                    - DESIGN_TOKENS.sizing.technical_labels.pattern_label_width
238                    + crate::theme::Theme::default().spacing.xxxl
239                    + crate::theme::Theme::default().spacing.xxl,
240                ..Default::default()
241            },
242            behavior: ReplayBehavior {
243                show_bar_counter: false,
244                show_elapsed_time: false,
245                show_remaining_time: false,
246                ..Default::default()
247            },
248            ..Default::default()
249        }
250    }
251}