livesplit_core/layout/
component.rs

1use super::{ComponentSettings, ComponentState, GeneralSettings};
2use crate::{
3    component::{
4        blank_space, current_comparison, current_pace, delta, detailed_timer, graph, pb_chance,
5        possible_time_save, previous_segment, segment_time, separator, splits, sum_of_best, text,
6        timer, title, total_playtime,
7    },
8    platform::prelude::*,
9    settings::{SettingsDescription, Value},
10    timing::Snapshot,
11};
12use alloc::borrow::Cow;
13
14/// A `Component` provides information about a run in a way that is easy to
15/// visualize. This type can store any of the components provided by this crate.
16#[derive(Clone)]
17pub enum Component {
18    /// The Blank Space Component.
19    BlankSpace(blank_space::Component),
20    /// The Current Comparison Component.
21    CurrentComparison(current_comparison::Component),
22    /// The Current Pace Component.
23    CurrentPace(current_pace::Component),
24    /// The Delta Component.
25    Delta(delta::Component),
26    /// The Detailed Timer Component.
27    DetailedTimer(Box<detailed_timer::Component>),
28    /// The Graph Component.
29    Graph(graph::Component),
30    /// The PB Chance Component.
31    PbChance(pb_chance::Component),
32    /// The Possible Time Save Component.
33    PossibleTimeSave(possible_time_save::Component),
34    /// The Previous Segment Component.
35    PreviousSegment(previous_segment::Component),
36    /// The Segment Time Component.
37    SegmentTime(segment_time::Component),
38    /// The Separator Component.
39    Separator(separator::Component),
40    /// The Splits Component.
41    Splits(splits::Component),
42    /// The Sum of Best Component.
43    SumOfBest(sum_of_best::Component),
44    /// The Text Component.
45    Text(text::Component),
46    /// The Timer Component.
47    Timer(timer::Component),
48    /// The Title Component.
49    Title(title::Component),
50    /// The Total Playtime Component.
51    TotalPlaytime(total_playtime::Component),
52}
53
54impl From<blank_space::Component> for Component {
55    fn from(component: blank_space::Component) -> Self {
56        Self::BlankSpace(component)
57    }
58}
59
60impl From<current_comparison::Component> for Component {
61    fn from(component: current_comparison::Component) -> Self {
62        Self::CurrentComparison(component)
63    }
64}
65
66impl From<current_pace::Component> for Component {
67    fn from(component: current_pace::Component) -> Self {
68        Self::CurrentPace(component)
69    }
70}
71
72impl From<delta::Component> for Component {
73    fn from(component: delta::Component) -> Self {
74        Self::Delta(component)
75    }
76}
77
78impl From<Box<detailed_timer::Component>> for Component {
79    fn from(component: Box<detailed_timer::Component>) -> Self {
80        Self::DetailedTimer(component)
81    }
82}
83
84impl From<graph::Component> for Component {
85    fn from(component: graph::Component) -> Self {
86        Self::Graph(component)
87    }
88}
89
90impl From<pb_chance::Component> for Component {
91    fn from(component: pb_chance::Component) -> Self {
92        Self::PbChance(component)
93    }
94}
95
96impl From<possible_time_save::Component> for Component {
97    fn from(component: possible_time_save::Component) -> Self {
98        Self::PossibleTimeSave(component)
99    }
100}
101
102impl From<previous_segment::Component> for Component {
103    fn from(component: previous_segment::Component) -> Self {
104        Self::PreviousSegment(component)
105    }
106}
107
108impl From<segment_time::Component> for Component {
109    fn from(component: segment_time::Component) -> Self {
110        Self::SegmentTime(component)
111    }
112}
113
114impl From<separator::Component> for Component {
115    fn from(component: separator::Component) -> Self {
116        Self::Separator(component)
117    }
118}
119
120impl From<splits::Component> for Component {
121    fn from(component: splits::Component) -> Self {
122        Self::Splits(component)
123    }
124}
125
126impl From<sum_of_best::Component> for Component {
127    fn from(component: sum_of_best::Component) -> Self {
128        Self::SumOfBest(component)
129    }
130}
131
132impl From<text::Component> for Component {
133    fn from(component: text::Component) -> Self {
134        Self::Text(component)
135    }
136}
137
138impl From<timer::Component> for Component {
139    fn from(component: timer::Component) -> Self {
140        Self::Timer(component)
141    }
142}
143
144impl From<title::Component> for Component {
145    fn from(component: title::Component) -> Self {
146        Self::Title(component)
147    }
148}
149
150impl From<total_playtime::Component> for Component {
151    fn from(component: total_playtime::Component) -> Self {
152        Self::TotalPlaytime(component)
153    }
154}
155
156impl Component {
157    /// Updates the component's state based on the timer and settings provided.
158    /// The timer provides the information to visualize and the layout settings
159    /// provide general information about how to expose that information in the
160    /// state.
161    pub fn update_state(
162        &mut self,
163        state: &mut ComponentState,
164        timer: &Snapshot<'_>,
165        layout_settings: &GeneralSettings,
166    ) {
167        match (state, self) {
168            (ComponentState::BlankSpace(state), Component::BlankSpace(component)) => {
169                component.update_state(state)
170            }
171            (ComponentState::KeyValue(state), Component::CurrentComparison(component)) => {
172                component.update_state(state, timer)
173            }
174            (ComponentState::KeyValue(state), Component::CurrentPace(component)) => {
175                component.update_state(state, timer)
176            }
177            (ComponentState::KeyValue(state), Component::Delta(component)) => {
178                component.update_state(state, timer, layout_settings)
179            }
180            (ComponentState::DetailedTimer(state), Component::DetailedTimer(component)) => {
181                component.update_state(&mut *state, timer, layout_settings)
182            }
183            (ComponentState::Graph(state), Component::Graph(component)) => {
184                component.update_state(state, timer, layout_settings)
185            }
186            (ComponentState::KeyValue(state), Component::PbChance(component)) => {
187                component.update_state(state, timer)
188            }
189            (ComponentState::KeyValue(state), Component::PossibleTimeSave(component)) => {
190                component.update_state(state, timer)
191            }
192            (ComponentState::KeyValue(state), Component::PreviousSegment(component)) => {
193                component.update_state(state, timer, layout_settings)
194            }
195            (ComponentState::KeyValue(state), Component::SegmentTime(component)) => {
196                component.update_state(state, timer)
197            }
198            (ComponentState::Separator(state), Component::Separator(component)) => {
199                component.update_state(state)
200            }
201            (ComponentState::Splits(state), Component::Splits(component)) => {
202                component.update_state(state, timer, layout_settings)
203            }
204            (ComponentState::KeyValue(state), Component::SumOfBest(component)) => {
205                component.update_state(state, timer)
206            }
207            (ComponentState::Text(state), Component::Text(component)) => {
208                component.update_state(state, timer)
209            }
210            (ComponentState::Timer(state), Component::Timer(component)) => {
211                component.update_state(state, timer, layout_settings)
212            }
213            (ComponentState::Title(state), Component::Title(component)) => {
214                component.update_state(state, timer)
215            }
216            (ComponentState::KeyValue(state), Component::TotalPlaytime(component)) => {
217                component.update_state(state, timer)
218            }
219            (state, component) => *state = component.state(timer, layout_settings),
220        }
221    }
222
223    /// Calculates the component's state based on the timer and settings
224    /// provided. The timer provides the information to visualize and the layout
225    /// settings provide general information about how to expose that
226    /// information in the state.
227    pub fn state(
228        &mut self,
229        timer: &Snapshot<'_>,
230        layout_settings: &GeneralSettings,
231    ) -> ComponentState {
232        match self {
233            Component::BlankSpace(component) => ComponentState::BlankSpace(component.state()),
234            Component::CurrentComparison(component) => {
235                ComponentState::KeyValue(component.state(timer))
236            }
237            Component::CurrentPace(component) => ComponentState::KeyValue(component.state(timer)),
238            Component::Delta(component) => {
239                ComponentState::KeyValue(component.state(timer, layout_settings))
240            }
241            Component::DetailedTimer(component) => {
242                ComponentState::DetailedTimer(Box::new(component.state(timer, layout_settings)))
243            }
244            Component::Graph(component) => {
245                ComponentState::Graph(component.state(timer, layout_settings))
246            }
247            Component::PbChance(component) => ComponentState::KeyValue(component.state(timer)),
248            Component::PossibleTimeSave(component) => {
249                ComponentState::KeyValue(component.state(timer))
250            }
251            Component::PreviousSegment(component) => {
252                ComponentState::KeyValue(component.state(timer, layout_settings))
253            }
254            Component::SegmentTime(component) => ComponentState::KeyValue(component.state(timer)),
255            Component::Separator(component) => ComponentState::Separator(component.state()),
256            Component::Splits(component) => {
257                ComponentState::Splits(component.state(timer, layout_settings))
258            }
259            Component::SumOfBest(component) => ComponentState::KeyValue(component.state(timer)),
260            Component::Text(component) => ComponentState::Text(component.state(timer)),
261            Component::Timer(component) => {
262                ComponentState::Timer(component.state(timer, layout_settings))
263            }
264            Component::Title(component) => ComponentState::Title(component.state(timer)),
265            Component::TotalPlaytime(component) => ComponentState::KeyValue(component.state(timer)),
266        }
267    }
268
269    /// Accesses the settings of the component. Each component has different
270    /// settings, so you need to handle them on a case by case basis. If you
271    /// want to access a more general description of the settings, access the
272    /// Settings Description instead.
273    pub fn settings(&self) -> ComponentSettings {
274        match self {
275            Component::BlankSpace(component) => {
276                ComponentSettings::BlankSpace(component.settings().clone())
277            }
278            Component::CurrentComparison(component) => {
279                ComponentSettings::CurrentComparison(component.settings().clone())
280            }
281            Component::CurrentPace(component) => {
282                ComponentSettings::CurrentPace(component.settings().clone())
283            }
284            Component::Delta(component) => ComponentSettings::Delta(component.settings().clone()),
285            Component::DetailedTimer(component) => {
286                ComponentSettings::DetailedTimer(Box::new(component.settings().clone()))
287            }
288            Component::Graph(component) => ComponentSettings::Graph(component.settings().clone()),
289            Component::PbChance(component) => {
290                ComponentSettings::PbChance(component.settings().clone())
291            }
292            Component::PossibleTimeSave(component) => {
293                ComponentSettings::PossibleTimeSave(component.settings().clone())
294            }
295            Component::PreviousSegment(component) => {
296                ComponentSettings::PreviousSegment(component.settings().clone())
297            }
298            Component::SegmentTime(component) => {
299                ComponentSettings::SegmentTime(component.settings().clone())
300            }
301            Component::Separator(_) => ComponentSettings::Separator,
302            Component::Splits(component) => ComponentSettings::Splits(component.settings().clone()),
303            Component::SumOfBest(component) => {
304                ComponentSettings::SumOfBest(component.settings().clone())
305            }
306            Component::Text(component) => ComponentSettings::Text(component.settings().clone()),
307            Component::Timer(component) => ComponentSettings::Timer(component.settings().clone()),
308            Component::Title(component) => ComponentSettings::Title(component.settings().clone()),
309            Component::TotalPlaytime(component) => {
310                ComponentSettings::TotalPlaytime(component.settings().clone())
311            }
312        }
313    }
314
315    /// Accesses the name of the component.
316    pub fn name(&self) -> Cow<'_, str> {
317        match self {
318            Component::BlankSpace(component) => component.name().into(),
319            Component::CurrentComparison(component) => component.name().into(),
320            Component::CurrentPace(component) => component.name(),
321            Component::Delta(component) => component.name(),
322            Component::DetailedTimer(component) => component.name().into(),
323            Component::Graph(component) => component.name(),
324            Component::PbChance(component) => component.name().into(),
325            Component::PossibleTimeSave(component) => component.name(),
326            Component::PreviousSegment(component) => component.name(),
327            Component::SegmentTime(component) => component.name(),
328            Component::Separator(component) => component.name().into(),
329            Component::Splits(component) => component.name().into(),
330            Component::SumOfBest(component) => component.name().into(),
331            Component::Text(component) => component.name(),
332            Component::Timer(component) => component.name().into(),
333            Component::Title(component) => component.name().into(),
334            Component::TotalPlaytime(component) => component.name().into(),
335        }
336    }
337
338    /// Tells the component to scroll up. This may be interpreted differently
339    /// based on the kind of component. Most components will ignore this.
340    pub fn scroll_up(&mut self) {
341        if let Component::Splits(component) = self {
342            component.scroll_up();
343        }
344    }
345
346    /// Tells the component to scroll down. This may be interpreted differently
347    /// based on the kind of component. Most components will ignore this.
348    pub fn scroll_down(&mut self) {
349        if let Component::Splits(component) = self {
350            component.scroll_down();
351        }
352    }
353
354    /// Some component states provide relative information based on information
355    /// they already provided. Remounting forces the components to provide
356    /// absolute information again, as if they provide the state for the first
357    /// time.
358    pub fn remount(&mut self) {
359        match self {
360            Component::DetailedTimer(component) => component.remount(),
361            Component::Splits(component) => component.remount(),
362            Component::Title(component) => component.remount(),
363            _ => {}
364        }
365    }
366
367    /// Provides a general description of the settings. Such a Settings
368    /// Description entirely describes all the settings that are available, what
369    /// type they are and what value they currently have. This provides a user
370    /// interface independent way of changing the settings.
371    pub fn settings_description(&self) -> SettingsDescription {
372        match self {
373            Component::BlankSpace(component) => component.settings_description(),
374            Component::CurrentComparison(component) => component.settings_description(),
375            Component::CurrentPace(component) => component.settings_description(),
376            Component::Delta(component) => component.settings_description(),
377            Component::DetailedTimer(component) => component.settings_description(),
378            Component::Graph(component) => component.settings_description(),
379            Component::PbChance(component) => component.settings_description(),
380            Component::PossibleTimeSave(component) => component.settings_description(),
381            Component::PreviousSegment(component) => component.settings_description(),
382            Component::SegmentTime(component) => component.settings_description(),
383            Component::Separator(component) => component.settings_description(),
384            Component::Splits(component) => component.settings_description(),
385            Component::SumOfBest(component) => component.settings_description(),
386            Component::Text(component) => component.settings_description(),
387            Component::Timer(component) => component.settings_description(),
388            Component::Title(component) => component.settings_description(),
389            Component::TotalPlaytime(component) => component.settings_description(),
390        }
391    }
392
393    /// Changes a setting of the component based on its Settings Description
394    /// index.
395    ///
396    /// # Panics
397    ///
398    /// This may panic if the index doesn't match any setting provided by the
399    /// Settings Description of this component. Additionally, the value needs to
400    /// have a compatible type.
401    pub fn set_value(&mut self, index: usize, value: Value) {
402        match self {
403            Component::BlankSpace(component) => component.set_value(index, value),
404            Component::CurrentComparison(component) => component.set_value(index, value),
405            Component::CurrentPace(component) => component.set_value(index, value),
406            Component::Delta(component) => component.set_value(index, value),
407            Component::DetailedTimer(component) => component.set_value(index, value),
408            Component::Graph(component) => component.set_value(index, value),
409            Component::PbChance(component) => component.set_value(index, value),
410            Component::PossibleTimeSave(component) => component.set_value(index, value),
411            Component::PreviousSegment(component) => component.set_value(index, value),
412            Component::SegmentTime(component) => component.set_value(index, value),
413            Component::Separator(component) => component.set_value(index, value),
414            Component::Splits(component) => component.set_value(index, value),
415            Component::SumOfBest(component) => component.set_value(index, value),
416            Component::Text(component) => component.set_value(index, value),
417            Component::Timer(component) => component.set_value(index, value),
418            Component::Title(component) => component.set_value(index, value),
419            Component::TotalPlaytime(component) => component.set_value(index, value),
420        }
421    }
422}