egui_charts/ui/timeframe_toolbar/
state.rs1use super::DateRange;
6use crate::model::Timeframe;
7
8#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
10pub enum SessionType {
11 #[default]
13 RTH,
14 ETH,
16 Full24x7,
18}
19
20impl SessionType {
21 pub fn label(&self) -> &'static str {
23 match self {
24 SessionType::RTH => "RTH",
25 SessionType::ETH => "ETH",
26 SessionType::Full24x7 => "24/7",
27 }
28 }
29
30 pub fn tooltip(&self) -> &'static str {
32 match self {
33 SessionType::RTH => "Regular trading hours: on",
34 SessionType::ETH => "Regular trading hours: off (extended hours)",
35 SessionType::Full24x7 => "Regular trading hours: off (24/7 trading)",
36 }
37 }
38}
39
40#[derive(Clone, Debug)]
42pub struct TimeframeToolbarState {
43 pub timeframe: Timeframe,
45 pub sel_range: DateRange,
47 pub date_picker_open: bool,
49 pub range_collapsed: bool,
51 pub timezone: String,
53 pub timezone_menu_open: bool,
55 pub session_type: SessionType,
57 pub session_menu_open: bool,
59 pub adjust_for_dividends: bool,
61 pub curr_time: Option<chrono::DateTime<chrono::Utc>>,
63}
64
65impl Default for TimeframeToolbarState {
66 fn default() -> Self {
67 Self {
68 timeframe: Timeframe::Min1,
69 sel_range: DateRange::Month1,
70 date_picker_open: false,
71 range_collapsed: false,
72 timezone: "UTC".to_string(),
73 timezone_menu_open: false,
74 session_type: SessionType::RTH,
75 session_menu_open: false,
76 adjust_for_dividends: false,
77 curr_time: None,
78 }
79 }
80}
81
82impl TimeframeToolbarState {
83 pub fn new() -> Self {
85 Self::default()
86 }
87
88 pub fn set_timeframe(&mut self, timeframe: Timeframe) {
90 self.timeframe = timeframe;
91 }
92
93 pub fn set_date_range(&mut self, range: DateRange) {
95 self.sel_range = range;
96 }
97
98 pub fn toggle_date_picker(&mut self) {
100 self.date_picker_open = !self.date_picker_open;
101 }
102
103 pub fn toggle_range_collapsed(&mut self) {
105 self.range_collapsed = !self.range_collapsed;
106 }
107
108 pub fn set_timezone(&mut self, timezone: String) {
110 self.timezone = timezone;
111 }
112
113 pub fn toggle_timezone_menu(&mut self) {
115 self.timezone_menu_open = !self.timezone_menu_open;
116 }
117
118 pub fn set_session_type(&mut self, session: SessionType) {
120 self.session_type = session;
121 }
122
123 pub fn toggle_session_menu(&mut self) {
125 self.session_menu_open = !self.session_menu_open;
126 }
127
128 pub fn toggle_adjust_for_dividends(&mut self) {
130 self.adjust_for_dividends = !self.adjust_for_dividends;
131 }
132
133 pub fn update_time(&mut self) {
135 self.curr_time = Some(chrono::Utc::now());
136 }
137
138 pub fn formatted_time(&self) -> String {
140 let zone = self
141 .timezone
142 .parse::<crate::model::TimezoneId>()
143 .unwrap_or(crate::model::TimezoneId::utc());
144 match &self.curr_time {
145 Some(time) => format!("{} {zone}", time.with_timezone(&zone).format("%H:%M:%S")),
146 None => format!("--:--:-- {zone}"),
147 }
148 }
149
150 pub fn sync_from_app_state(&mut self, app_state: &dyn crate::ui::app_state::ChartAppState) {
152 self.timeframe = *app_state.active_timeframe();
153 }
154}
155
156#[cfg(test)]
157mod tests {
158 use super::*;
159
160 #[test]
161 fn clock_converts_the_named_zone_and_falls_back_to_utc() {
162 for (instant, zone, expected) in [
163 ("2026-01-15T13:00:00Z", "UTC", "13:00:00 UTC"),
164 ("2026-01-15T13:00:00Z", "+05:45", "18:45:00 +05:45"),
165 #[cfg(feature = "timezones")]
166 (
167 "2026-01-15T13:00:00Z",
168 "America/New_York",
169 "08:00:00 America/New_York",
170 ),
171 #[cfg(feature = "timezones")]
172 (
173 "2026-07-15T13:00:00Z",
174 "America/New_York",
175 "09:00:00 America/New_York",
176 ),
177 ("2026-01-15T13:00:00Z", "unknown", "13:00:00 UTC"),
178 ] {
179 let state = TimeframeToolbarState {
180 curr_time: Some(instant.parse().unwrap()),
181 timezone: zone.to_owned(),
182 ..Default::default()
183 };
184 assert_eq!(state.formatted_time(), expected);
185 }
186 let state = TimeframeToolbarState {
187 timezone: "unknown".to_owned(),
188 ..Default::default()
189 };
190 assert_eq!(state.formatted_time(), "--:--:-- UTC");
191 }
192}