livesplit_core/component/
current_pace.rs

1//! Provides the Current Pace Component and relevant types for using it. The
2//! Current Pace Component is a component that shows a prediction of the current
3//! attempt's final time, if the current attempt's pace matches the chosen
4//! comparison for the remainder of the run.
5
6use super::key_value;
7use crate::{
8    analysis::current_pace,
9    comparison,
10    platform::prelude::*,
11    settings::{Color, Field, Gradient, SettingsDescription, Value},
12    timing::{
13        formatter::{Accuracy, Regular, TimeFormatter},
14        Snapshot,
15    },
16    TimerPhase,
17};
18use alloc::borrow::Cow;
19use core::fmt::Write;
20use serde::{Deserialize, Serialize};
21
22/// The Current Pace Component is a component that shows a prediction of the
23/// current attempt's final time, if the current attempt's pace matches the
24/// chosen comparison for the remainder of the run.
25#[derive(Default, Clone)]
26pub struct Component {
27    settings: Settings,
28}
29
30/// The Settings for this component.
31#[derive(Clone, Serialize, Deserialize)]
32#[serde(default)]
33pub struct Settings {
34    /// The background shown behind the component.
35    pub background: Gradient,
36    /// The comparison chosen. Uses the Timer's current comparison if set to
37    /// `None`.
38    pub comparison_override: Option<String>,
39    /// Specifies whether to display the name of the component and its value in
40    /// two separate rows.
41    pub display_two_rows: bool,
42    /// The color of the label. If `None` is specified, the color is taken from
43    /// the layout.
44    pub label_color: Option<Color>,
45    /// The color of the value. If `None` is specified, the color is taken from
46    /// the layout.
47    pub value_color: Option<Color>,
48    /// The accuracy of the time shown.
49    pub accuracy: Accuracy,
50}
51
52impl Default for Settings {
53    fn default() -> Self {
54        Self {
55            background: key_value::DEFAULT_GRADIENT,
56            comparison_override: None,
57            display_two_rows: false,
58            label_color: None,
59            value_color: None,
60            accuracy: Accuracy::Seconds,
61        }
62    }
63}
64
65impl Component {
66    /// Creates a new Current Pace Component.
67    pub fn new() -> Self {
68        Default::default()
69    }
70
71    /// Creates a new Current Pace Component with the given settings.
72    pub const fn with_settings(settings: Settings) -> Self {
73        Self { settings }
74    }
75
76    /// Accesses the settings of the component.
77    pub const fn settings(&self) -> &Settings {
78        &self.settings
79    }
80
81    /// Grants mutable access to the settings of the component.
82    pub fn settings_mut(&mut self) -> &mut Settings {
83        &mut self.settings
84    }
85
86    /// Accesses the name of the component.
87    pub fn name(&self) -> Cow<'static, str> {
88        self.text(self.settings.comparison_override.as_deref())
89    }
90
91    fn text(&self, comparison: Option<&str>) -> Cow<'static, str> {
92        if let Some(comparison) = comparison {
93            match comparison {
94                comparison::personal_best::NAME => "Current Pace".into(),
95                comparison::best_segments::NAME => "Best Possible Time".into(),
96                comparison::worst_segments::NAME => "Worst Possible Time".into(),
97                comparison::average_segments::NAME => "Predicted Time".into(),
98                comparison => format!("Current Pace ({})", comparison::shorten(comparison)).into(),
99            }
100        } else {
101            "Current Pace".into()
102        }
103    }
104
105    /// Updates the component's state based on the timer provided.
106    pub fn update_state(&self, state: &mut key_value::State, timer: &Snapshot<'_>) {
107        let comparison = comparison::resolve(&self.settings.comparison_override, timer);
108        let comparison = comparison::or_current(comparison, timer);
109        let key = self.text(Some(comparison));
110
111        let (current_pace, updates_frequently) =
112            if timer.current_phase() == TimerPhase::NotRunning && key.starts_with("Current Pace") {
113                (None, false)
114            } else {
115                current_pace::calculate(timer, comparison)
116            };
117
118        state.background = self.settings.background;
119        state.key_color = self.settings.label_color;
120        state.value_color = self.settings.value_color;
121        state.semantic_color = Default::default();
122
123        state.key.clear();
124        state.key.push_str(&key); // FIXME: Uncow this
125
126        state.value.clear();
127        let _ = write!(
128            state.value,
129            "{}",
130            Regular::with_accuracy(self.settings.accuracy).format(current_pace)
131        );
132
133        state.key_abbreviations.clear();
134        // FIXME: This &* probably is different when key is uncowed
135        match &*key {
136            "Best Possible Time" => {
137                state.key_abbreviations.push("Best Poss. Time".into());
138                state.key_abbreviations.push("Best Time".into());
139                state.key_abbreviations.push("BPT".into());
140            }
141            "Worst Possible Time" => {
142                state.key_abbreviations.push("Worst Poss. Time".into());
143                state.key_abbreviations.push("Worst Time".into());
144            }
145            "Predicted Time" => {
146                state.key_abbreviations.push("Pred. Time".into());
147            }
148            "Current Pace" => {
149                state.key_abbreviations.push("Cur. Pace".into());
150                state.key_abbreviations.push("Pace".into());
151            }
152            _ => {
153                state.key_abbreviations.push("Current Pace".into());
154                state.key_abbreviations.push("Cur. Pace".into());
155                state.key_abbreviations.push("Pace".into());
156            }
157        }
158
159        state.display_two_rows = self.settings.display_two_rows;
160        state.updates_frequently = updates_frequently;
161    }
162
163    /// Calculates the component's state based on the timer provided.
164    pub fn state(&self, timer: &Snapshot<'_>) -> key_value::State {
165        let mut state = Default::default();
166        self.update_state(&mut state, timer);
167        state
168    }
169
170    /// Accesses a generic description of the settings available for this
171    /// component and their current values.
172    pub fn settings_description(&self) -> SettingsDescription {
173        SettingsDescription::with_fields(vec![
174            Field::new("Background".into(), self.settings.background.into()),
175            Field::new(
176                "Comparison".into(),
177                self.settings.comparison_override.clone().into(),
178            ),
179            Field::new(
180                "Display 2 Rows".into(),
181                self.settings.display_two_rows.into(),
182            ),
183            Field::new("Label Color".into(), self.settings.label_color.into()),
184            Field::new("Value Color".into(), self.settings.value_color.into()),
185            Field::new("Accuracy".into(), self.settings.accuracy.into()),
186        ])
187    }
188
189    /// Sets a setting's value by its index to the given value.
190    ///
191    /// # Panics
192    ///
193    /// This panics if the type of the value to be set is not compatible with
194    /// the type of the setting's value. A panic can also occur if the index of
195    /// the setting provided is out of bounds.
196    pub fn set_value(&mut self, index: usize, value: Value) {
197        match index {
198            0 => self.settings.background = value.into(),
199            1 => self.settings.comparison_override = value.into(),
200            2 => self.settings.display_two_rows = value.into(),
201            3 => self.settings.label_color = value.into(),
202            4 => self.settings.value_color = value.into(),
203            5 => self.settings.accuracy = value.into(),
204            _ => panic!("Unsupported Setting Index"),
205        }
206    }
207}