Skip to main content

egui_charts/widget/settings/
data.rs

1//! Data structures for settings state - Canvas style
2
3use super::types::{BackgroundType, ButtonVisibility, GridLinesMode, PrecisionMode, WatermarkMode};
4use crate::chart::{PnlUnit, TradingLabelAlignment};
5use crate::model::LineStyle;
6use egui::Color32;
7
8// ============================================================================
9// Settings State
10// ============================================================================
11
12/// Candle color configuration (body, border, wick for up/down candles) and which parts are drawn.
13/// Saved settings without a field take its default.
14#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
15#[serde(default)]
16pub struct CandleColorConfig {
17    /// Colour each candle by its close against the previous close instead of its open.
18    pub color_by_previous_close: bool,
19    /// Whether candle bodies are filled.
20    pub show_body: bool,
21    /// Whether candle borders are drawn.
22    pub show_border: bool,
23    /// Whether candle wicks are drawn.
24    pub show_wick: bool,
25    /// Body color of rising candles.
26    pub body_up: Color32,
27    /// Body color of falling candles.
28    pub body_down: Color32,
29    /// Border color of rising candles.
30    pub border_up: Color32,
31    /// Border color of falling candles.
32    pub border_down: Color32,
33    /// Wick color of rising candles.
34    pub wick_up: Color32,
35    /// Wick color of falling candles.
36    pub wick_down: Color32,
37}
38
39impl CandleColorConfig {
40    /// Candle body, border and wick colours from `theme`.
41    pub fn from_theme(theme: &crate::theme::Theme) -> Self {
42        let chart = &theme.chart;
43        Self {
44            color_by_previous_close: false,
45            show_body: true,
46            show_border: false,
47            show_wick: true,
48            body_up: chart.candle_up,
49            body_down: chart.candle_down,
50            border_up: chart.candle_up,
51            border_down: chart.candle_down,
52            wick_up: chart.candle_up,
53            wick_down: chart.candle_down,
54        }
55    }
56}
57
58impl Default for CandleColorConfig {
59    fn default() -> Self {
60        Self::from_theme(&crate::theme::Theme::default())
61    }
62}
63
64/// Status line display options. Saved settings without a field take its default.
65#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
66#[serde(default)]
67pub struct StatusLineOptions {
68    /// Whether the legend shows the symbol's logo before the title, or its initial on a neutral
69    /// disc without one.
70    pub show_logo: bool,
71    /// Whether the legend shows the instrument title.
72    pub show_symbol: bool,
73    /// Display the description instead of the ticker.
74    pub title_as_description: bool,
75    /// Whether the legend shows the market status after the title: the session's state and
76    /// delayed or end-of-day data.
77    pub show_market_status: bool,
78    /// Display current bid and ask controls.
79    pub show_quote_boxes: bool,
80    /// Display study names.
81    pub show_indicator_titles: bool,
82    /// Display host-supplied study inputs.
83    pub show_indicator_inputs: bool,
84    /// Display historical study output values.
85    pub show_indicator_values: bool,
86    /// Whether the legend shows open, high, low and close.
87    pub show_ohlc: bool,
88    /// Whether the legend shows the absolute price change.
89    pub show_change: bool,
90    /// Whether the legend shows the percentage price change.
91    pub show_change_percent: bool,
92    /// Whether the legend shows volume.
93    pub show_volume: bool,
94    /// Whether the legend shows the change of the last price from the previous session's close.
95    pub show_last_day_change: bool,
96    /// Opacity of the study rows' backdrop, from 0 to 100 percent.
97    pub indicator_background_opacity: u8,
98}
99
100impl Default for StatusLineOptions {
101    fn default() -> Self {
102        Self {
103            show_logo: true,
104            show_symbol: true,
105            title_as_description: true,
106            show_market_status: true,
107            show_quote_boxes: true,
108            show_indicator_titles: true,
109            show_indicator_inputs: true,
110            show_indicator_values: true,
111            show_ohlc: true,
112            show_change: true,
113            show_change_percent: true,
114            show_volume: true,
115            show_last_day_change: false,
116            indicator_background_opacity: 60,
117        }
118    }
119}
120
121/// Canvas tab: Chart basic styles settings
122#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
123pub struct ChartBasicStylesSettings {
124    /// Solid or gradient chart background.
125    pub background_type: BackgroundType,
126    /// `Color32::PLACEHOLDER` follows the active chart background until a colour is chosen.
127    pub background_color: Color32,
128    /// First gradient endpoint color.
129    pub background_gradient_top: Color32,
130    /// Second gradient endpoint color.
131    pub background_gradient_bottom: Color32,
132}
133
134impl Default for ChartBasicStylesSettings {
135    fn default() -> Self {
136        Self {
137            background_type: BackgroundType::Solid,
138            background_color: Color32::PLACEHOLDER,
139            background_gradient_top: Color32::PLACEHOLDER,
140            background_gradient_bottom: Color32::PLACEHOLDER,
141        }
142    }
143}
144
145/// Canvas tab: Grid lines settings
146#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
147pub struct GridLinesSettings {
148    /// Display mode.
149    pub mode: GridLinesMode,
150    /// Color of horizontal grid lines.
151    pub horizontal_color: Color32,
152    /// Color of vertical grid lines.
153    pub vertical_color: Color32,
154}
155
156impl Default for GridLinesSettings {
157    fn default() -> Self {
158        Self {
159            mode: GridLinesMode::Both,
160            horizontal_color: crate::theme::Theme::default().chart.grid_line,
161            vertical_color: crate::theme::Theme::default().chart.grid_line,
162        }
163    }
164}
165
166/// Canvas tab: Crosshair settings
167#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
168pub struct CrosshairSettings {
169    /// Element color.
170    pub color: Color32,
171    /// Stroke width in logical pixels.
172    pub line_width: f32,
173    /// Stroke pattern.
174    pub line_style: LineStyle,
175}
176
177impl Default for CrosshairSettings {
178    fn default() -> Self {
179        Self {
180            color: crate::theme::Theme::default().chart.axis_text_secondary,
181            line_width: 1.0,
182            line_style: LineStyle::Dashed,
183        }
184    }
185}
186
187/// Canvas tab: Watermark settings
188#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
189pub struct WatermarkSettings {
190    /// Display mode.
191    pub mode: WatermarkMode,
192    /// Element color.
193    pub color: Color32,
194}
195
196impl Default for WatermarkSettings {
197    fn default() -> Self {
198        Self {
199            mode: WatermarkMode::ReplayMode,
200            color: crate::theme::Theme::default()
201                .ui
202                .text_muted
203                .gamma_multiply(0.2), // ~20% opacity
204        }
205    }
206}
207
208/// Canvas tab: Scales appearance settings
209#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
210pub struct ScalesAppearanceSettings {
211    /// Text color.
212    pub text_color: Color32,
213    /// Font size in logical pixels.
214    pub font_size: u8,
215    /// Axis line color.
216    pub lines_color: Color32,
217}
218
219impl Default for ScalesAppearanceSettings {
220    fn default() -> Self {
221        Self {
222            text_color: crate::theme::Theme::default().ui.text_secondary,
223            font_size: 12,
224            lines_color: Color32::TRANSPARENT,
225        }
226    }
227}
228
229/// Canvas tab: Buttons visibility settings
230#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
231pub struct ButtonsSettings {
232    /// Visibility of chart navigation controls.
233    pub navigation: ButtonVisibility,
234    /// Visibility of pane controls.
235    pub pane: ButtonVisibility,
236}
237
238impl Default for ButtonsSettings {
239    fn default() -> Self {
240        Self {
241            navigation: ButtonVisibility::Hidden,
242            pane: ButtonVisibility::VisibleOnMouseOver,
243        }
244    }
245}
246
247/// Canvas tab: Margins settings
248#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
249pub struct MarginsSettings {
250    /// Empty space above prices, as a percentage of pane height.
251    pub top_percent: f32,
252    /// Empty space below prices, as a percentage of pane height.
253    pub bottom_percent: f32,
254    /// Empty space after the latest bar, in bars.
255    pub right_bars: u32,
256}
257
258impl Default for MarginsSettings {
259    fn default() -> Self {
260        Self {
261            top_percent: 10.0,
262            bottom_percent: 8.0,
263            right_bars: 10,
264        }
265    }
266}
267
268/// Scales and lines tab settings
269#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
270#[serde(default)]
271pub struct ScalesAndLinesSettings {
272    /// Display host-supplied currency metadata.
273    pub show_currency: bool,
274    /// Display host-supplied price unit metadata.
275    pub show_unit: bool,
276    /// Display Auto, Log and Percent navigation buttons.
277    pub show_scale_mode_buttons: bool,
278    /// Display the price-axis host action button.
279    pub show_plus_button: bool,
280    /// Let value tags overlap instead of moving them apart.
281    pub allow_label_overlap: bool,
282    /// Lock scale units per bar to `price_to_bar_ratio`.
283    pub lock_price_to_bar_ratio: bool,
284    /// Positive scale units per bar while locked; log scales use natural logarithms of prices.
285    pub price_to_bar_ratio: f64,
286    /// Last price tag and line colour, or the candle direction colour when unset.
287    pub symbol_price_color: Option<Color32>,
288    /// Display the last values of studies on their price axes.
289    pub show_study_values: bool,
290    /// Study value tag colour, or each plot's colour when unset.
291    pub study_value_color: Option<Color32>,
292    /// Write the symbol before its value tags on the price scale, independently of its last
293    /// value and legend title.
294    pub show_symbol_name: bool,
295    /// Write each study's name before its value tags, independently of the values and legend
296    /// titles.
297    pub show_study_names: bool,
298    /// Display the host-supplied extended-session price tag.
299    pub show_extended_session_price: bool,
300    /// Extended-session price tag colour.
301    pub extended_session_price_color: Color32,
302    /// Whether visible prices determine the scale range.
303    pub auto_scale: bool,
304    /// Whether prices use a logarithmic scale.
305    pub log_scale: bool,
306    /// Whether prices are shown as percentage changes.
307    pub percentage_scale: bool,
308    /// Whether increasing prices run downward.
309    pub invert_scale: bool,
310    /// Show the price scale on the left instead of the right.
311    pub price_scale_left: bool,
312    /// Display the current close tag independently of its line.
313    pub show_last_price_label: bool,
314    /// Display the previous session close tag independently of its line.
315    pub show_prev_close_label: bool,
316    /// Whether to show price line.
317    pub show_price_line: bool,
318    /// Whether to show countdown.
319    pub show_countdown: bool,
320    /// Whether to show bid ask.
321    pub show_bid_ask: bool,
322    /// Whether to show high low.
323    pub show_high_low: bool,
324    /// Whether to show prev close.
325    pub show_prev_close: bool,
326}
327
328impl Default for ScalesAndLinesSettings {
329    fn default() -> Self {
330        Self {
331            show_currency: true,
332            show_unit: true,
333            show_scale_mode_buttons: true,
334            show_plus_button: true,
335            allow_label_overlap: false,
336            lock_price_to_bar_ratio: false,
337            price_to_bar_ratio: 1.0,
338            symbol_price_color: None,
339            show_study_values: true,
340            study_value_color: None,
341            show_symbol_name: false,
342            show_study_names: false,
343            show_extended_session_price: true,
344            extended_session_price_color: Color32::from_rgb(251, 142, 1),
345            auto_scale: true,
346            log_scale: false,
347            percentage_scale: false,
348            invert_scale: false,
349            price_scale_left: false,
350            show_last_price_label: true,
351            show_prev_close_label: false,
352            show_price_line: true,
353            show_countdown: true,
354            show_bid_ask: false,
355            show_high_low: false,
356            show_prev_close: false,
357        }
358    }
359}
360
361/// Trading tab settings: the Buy and Sell buttons' behaviour, notifications and the layers
362/// `ui::trading::Trading::show` draws through `Trading::display` with the `ui` feature.
363/// The Buy and Sell buttons themselves are [`StatusLineOptions::show_quote_boxes`].
364/// Saved settings without a field take its default, unless the field says otherwise.
365#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
366#[serde(default)]
367pub struct TradingSettings {
368    /// Buy and Sell place a market order of the order quantity at once instead of opening the
369    /// order ticket.
370    pub instant_orders: bool,
371    /// Show current Bid and Ask tags supplied by the quote source.
372    pub bid_ask_labels: bool,
373    /// Ask the host to play [`execution_sound`](Self::execution_sound) for every fill, through
374    /// `ui::trading::Trading::take_execution_sound` with the `ui` feature.
375    pub play_execution_sound: bool,
376    /// The name of the sound the host plays for fills, one of the
377    /// `SettingsDialog::execution_sounds` with the `ui` feature; empty for the host's first.
378    pub execution_sound: String,
379    /// Local execution sound gain, from zero (muted) to one.
380    pub execution_sound_volume: f32,
381    /// Which trading events raise a notification.
382    pub notifications: TradingNotifications,
383    /// Draw the open positions.
384    pub show_positions: bool,
385    /// Give each position's profit or loss on its label.
386    pub position_pnl: bool,
387    /// The unit of the positions' profit or loss.
388    pub position_pnl_unit: PnlUnit,
389    /// Show a position's reverse button while its label is hovered.
390    pub reverse_button: bool,
391    /// Extend trading lines to the left edge of the pane instead of starting at their labels.
392    pub extend_lines: bool,
393    /// Horizontal placement of order and position labels.
394    pub label_alignment: TradingLabelAlignment,
395    /// Include the visible trading overlays in images exported through `ui::trading::Trading`.
396    pub include_in_image: bool,
397    /// Draw the working orders and their brackets.
398    pub show_orders: bool,
399    /// Give on each take profit and stop loss the profit or loss of reaching it.
400    pub bracket_pnl: bool,
401    /// The unit of the brackets' profit or loss.
402    pub bracket_pnl_unit: PnlUnit,
403    /// Draw the fills on their bars.
404    pub show_executions: bool,
405    /// Write the side and total quantity beside each fill's mark. On for new settings; settings
406    /// saved without this field keep labels off, as they were drawn when saved.
407    #[serde(default)]
408    pub show_execution_labels: bool,
409}
410
411impl Default for TradingSettings {
412    fn default() -> Self {
413        Self {
414            instant_orders: false,
415            bid_ask_labels: false,
416            play_execution_sound: false,
417            execution_sound: String::new(),
418            execution_sound_volume: 1.0,
419            notifications: TradingNotifications::All,
420            show_positions: true,
421            position_pnl: true,
422            position_pnl_unit: PnlUnit::Money,
423            reverse_button: true,
424            extend_lines: true,
425            label_alignment: TradingLabelAlignment::Right,
426            include_in_image: false,
427            show_orders: true,
428            bracket_pnl: true,
429            bracket_pnl_unit: PnlUnit::Money,
430            show_executions: true,
431            show_execution_labels: true,
432        }
433    }
434}
435
436/// Which trading events raise a notification: the levels of the toasts they use.
437#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
438pub enum TradingNotifications {
439    /// Fills, partial fills, rejections and refused orders.
440    #[default]
441    All,
442    /// Rejections and refused orders only.
443    Errors,
444    /// None.
445    Off,
446}
447
448impl TradingNotifications {
449    /// Every level, in the order a menu lists them.
450    pub const ALL: [Self; 3] = [Self::All, Self::Errors, Self::Off];
451
452    /// The level's name in menus.
453    pub fn label(self) -> &'static str {
454        match self {
455            Self::All => "All events",
456            Self::Errors => "Errors only",
457            Self::Off => "Off",
458        }
459    }
460}
461
462/// Alerts tab settings
463#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
464#[serde(default)]
465pub struct AlertsSettings {
466    /// Ask the host to play a local sound when an alert fires.
467    pub play_sound: bool,
468    /// Host sound name; empty selects its first available sound.
469    pub sound: String,
470    /// Local alert sound gain, from zero (muted) to one.
471    pub sound_volume: f32,
472    /// Whether to show alerts.
473    pub show_alerts: bool,
474    /// Whether to show alert labels.
475    pub show_alert_labels: bool,
476    /// Alert color.
477    pub alert_color: Color32,
478    /// Draw only active alerts; otherwise triggered, disabled and expired alerts are drawn dimmed.
479    pub show_active_only: bool,
480    /// Seconds an alert notification stays on screen.
481    pub notification_seconds: u32,
482}
483
484impl Default for AlertsSettings {
485    fn default() -> Self {
486        Self {
487            play_sound: false,
488            sound: String::new(),
489            sound_volume: 1.0,
490            show_alerts: true,
491            show_alert_labels: true,
492            alert_color: crate::studies::palette::WARM_ORANGE,
493            show_active_only: true,
494            notification_seconds: 5,
495        }
496    }
497}
498
499/// Independent order-flow layers and their appearance. Missing saved fields keep layers off.
500#[derive(Debug, Clone, Default, PartialEq, serde::Serialize, serde::Deserialize)]
501#[serde(default)]
502pub struct OrderFlowSettings {
503    /// Draw sampled resting liquidity behind the price series.
504    pub show_heatmap: bool,
505    /// Brightness and resting-size range, retained while the heatmap is off.
506    pub heatmap: crate::chart::overlays::LiquidityHeatmapConfig,
507    /// Draw the live book's side profile independently of the heatmap.
508    pub show_order_book: bool,
509    /// Live book measure, order filter and size highlights.
510    pub order_book: crate::chart::overlays::OrderBookView,
511    /// Draw the supplied trades as volume bubbles.
512    pub show_volume_bubbles: bool,
513    /// Bubble grouping, colours and sizing, retained while bubbles are off.
514    pub volume_bubbles: crate::chart::overlays::VolumeBubblesConfig,
515}
516
517/// Serializable display settings shared by the chart and its settings dialog.
518#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
519pub struct ChartSettingsState {
520    // Symbol tab
521    /// Active representation used to select the Symbol controls. Applying settings keeps the chart type.
522    #[serde(default)]
523    pub chart_type: crate::model::ChartType,
524    /// Styles and construction inputs; absent in settings saved before these controls existed.
525    #[serde(default)]
526    pub chart_type_params: Option<crate::model::ChartTypeParams>,
527    /// Source for line and area representations; absent in older saved settings.
528    #[serde(default)]
529    pub price_source: Option<crate::model::PriceSource>,
530    /// Candle body, border and wick colors.
531    pub candle_colors: CandleColorConfig,
532    /// Price display precision.
533    pub precision: PrecisionMode,
534    /// UTC, Local, or a supported zone name.
535    pub timezone: String,
536    /// Show analyst estimates answered by the host feed.
537    #[serde(default)]
538    pub show_price_targets: bool,
539    /// Ask the host for history adjusted for dividends. The host owns the data, so
540    /// [`apply_to_chart`](Self::apply_to_chart) leaves it to the host, which reloads the
541    /// adjusted history; [`from_chart`](Self::from_chart) leaves it off.
542    #[serde(default)]
543    pub adjust_for_dividends: bool,
544
545    // Order flow tab
546    /// Independent heatmap, live book and trade bubble settings; older documents keep them off.
547    #[serde(default)]
548    pub order_flow: OrderFlowSettings,
549
550    // Status line tab
551    /// Legend content and quote controls.
552    pub status_line: StatusLineOptions,
553
554    // Scales and lines tab
555    /// Price scale and reference line settings.
556    pub scales_and_lines: ScalesAndLinesSettings,
557
558    // Canvas tab - grouped settings
559    /// Chart background settings.
560    pub chart_basic_styles: ChartBasicStylesSettings,
561    /// Grid directions and colors.
562    pub grid_lines: GridLinesSettings,
563    /// Crosshair line appearance.
564    pub crosshair: CrosshairSettings,
565    /// Watermark appearance.
566    pub watermark: WatermarkSettings,
567    /// Axis appearance.
568    pub scales_appearance: ScalesAppearanceSettings,
569    /// Chart and pane control visibility.
570    pub btns: ButtonsSettings,
571    /// Space around the visible prices.
572    pub margins: MarginsSettings,
573
574    // Trading tab
575    /// Host trading controls and overlays.
576    pub trading: TradingSettings,
577
578    // Alerts tab
579    /// Alert marks and labels.
580    pub alerts: AlertsSettings,
581
582    // Events tab
583    /// Visible event categories.
584    pub events: crate::model::EventVisibility,
585}
586
587impl Default for ChartSettingsState {
588    fn default() -> Self {
589        let chart_basic_styles = ChartBasicStylesSettings::default();
590        let grid_lines = GridLinesSettings::default();
591
592        Self {
593            chart_type: crate::model::ChartType::default(),
594            chart_type_params: None,
595            price_source: None,
596            candle_colors: CandleColorConfig::default(),
597            precision: PrecisionMode::Default,
598            timezone: "UTC".to_string(),
599            show_price_targets: false,
600            adjust_for_dividends: false,
601
602            order_flow: OrderFlowSettings::default(),
603
604            status_line: StatusLineOptions::default(),
605
606            scales_and_lines: ScalesAndLinesSettings::default(),
607
608            chart_basic_styles: chart_basic_styles.clone(),
609            grid_lines: grid_lines.clone(),
610            crosshair: CrosshairSettings::default(),
611            watermark: WatermarkSettings::default(),
612            scales_appearance: ScalesAppearanceSettings::default(),
613            btns: ButtonsSettings::default(),
614            margins: MarginsSettings::default(),
615
616            trading: TradingSettings::default(),
617
618            alerts: AlertsSettings::default(),
619
620            events: crate::model::EventVisibility::default(),
621        }
622    }
623}