use super::timer;
use crate::{
analysis::comparison_single_segment_time,
comparison::{self, best_segments, none},
platform::prelude::*,
settings::{CachedImageId, Color, Field, Gradient, ImageData, SettingsDescription, Value},
timing::{
formatter::{Accuracy, DigitsFormat, SegmentTime, TimeFormatter},
Snapshot,
},
GeneralLayoutSettings, Segment, TimeSpan, TimerPhase,
};
use core::fmt::Write;
use serde::{Deserialize, Serialize};
#[cfg(test)]
mod tests;
#[derive(Default, Clone)]
pub struct Component {
icon_id: CachedImageId,
timer: timer::Component,
segment_timer: timer::Component,
settings: Settings,
}
#[derive(Clone, Serialize, Deserialize)]
#[serde(default)]
pub struct Settings {
pub background: timer::DeltaGradient,
pub comparison1: Option<String>,
pub comparison2: Option<String>,
pub hide_second_comparison: bool,
pub timer: timer::Settings,
pub segment_timer: timer::Settings,
pub display_icon: bool,
pub show_segment_name: bool,
}
#[derive(Default, Serialize, Deserialize)]
pub struct State {
pub background: Gradient,
pub timer: timer::State,
pub segment_timer: timer::State,
pub comparison1: Option<ComparisonState>,
pub comparison2: Option<ComparisonState>,
pub segment_name: Option<String>,
pub icon_change: Option<ImageData>,
}
#[derive(Serialize, Deserialize)]
pub struct ComparisonState {
pub name: String,
pub time: String,
}
fn update_comparison(
state: &mut Option<ComparisonState>,
new_state: Option<(&str, Option<TimeSpan>)>,
) {
if let Some((name, time)) = new_state {
let state = state.get_or_insert_with(|| ComparisonState {
name: String::new(),
time: String::new(),
});
state.name.clear();
state.name.push_str(name);
state.time.clear();
let _ = write!(state.time, "{}", SegmentTime::new().format(time));
} else {
*state = None;
}
}
impl Default for Settings {
fn default() -> Self {
Settings {
background: timer::DeltaGradient::from(Gradient::Transparent),
comparison1: None,
comparison2: Some(String::from(best_segments::NAME)),
hide_second_comparison: false,
timer: timer::Settings {
height: 40,
..Default::default()
},
segment_timer: timer::Settings {
height: 25,
is_segment_timer: true,
color_override: Some(Color::rgba(
170.0 / 255.0,
170.0 / 255.0,
170.0 / 255.0,
1.0,
)),
..Default::default()
},
display_icon: false,
show_segment_name: false,
}
}
}
#[cfg(feature = "std")]
impl State {
pub fn write_json<W>(&self, writer: W) -> serde_json::Result<()>
where
W: std::io::Write,
{
serde_json::to_writer(writer, self)
}
}
impl Component {
pub fn new() -> Self {
Self::with_settings(Default::default())
}
pub fn with_settings(settings: Settings) -> Self {
let timer = timer::Component::with_settings(settings.timer.clone());
let segment_timer = timer::Component::with_settings(settings.segment_timer.clone());
Self {
timer,
segment_timer,
settings,
..Default::default()
}
}
pub const fn settings(&self) -> &Settings {
&self.settings
}
pub fn set_settings(&mut self, settings: Settings) {
self.settings = settings;
*self.timer.settings_mut() = self.settings.timer.clone();
*self.segment_timer.settings_mut() = self.settings.segment_timer.clone();
}
pub const fn name(&self) -> &'static str {
"Detailed Timer"
}
pub fn update_state(
&mut self,
state: &mut State,
timer: &Snapshot<'_>,
layout_settings: &GeneralLayoutSettings,
) {
let current_phase = timer.current_phase();
let timing_method = self
.settings
.timer
.timing_method
.unwrap_or_else(|| timer.current_timing_method());
let run = timer.run();
let (current_split, last_split_index) = if current_phase == TimerPhase::Ended {
(run.segments().last(), run.len() - 1)
} else {
(
timer.current_split(),
timer.current_split_index().unwrap_or(0),
)
};
let (comparison1, comparison2) = if current_phase != TimerPhase::NotRunning {
let mut comparison1 = self
.settings
.comparison1
.as_deref()
.unwrap_or_else(|| timer.current_comparison());
let comparison2 = self
.settings
.comparison2
.as_deref()
.unwrap_or_else(|| timer.current_comparison());
let mut hide_comparison = self.settings.hide_second_comparison;
if hide_comparison
|| !run.comparisons().any(|c| c == comparison2)
|| comparison2 == none::NAME
{
hide_comparison = true;
if !run.comparisons().any(|c| c == comparison1) || comparison1 == none::NAME {
comparison1 = timer.current_comparison();
}
} else if !run.comparisons().any(|c| c == comparison1) || comparison1 == none::NAME {
hide_comparison = true;
comparison1 = comparison2;
} else if comparison1 == comparison2 {
hide_comparison = true;
}
let comparison1 = Some((
comparison::shorten(comparison1),
comparison_single_segment_time(run, last_split_index, comparison1, timing_method),
));
let comparison2 = if !hide_comparison {
Some((
comparison::shorten(comparison2),
comparison_single_segment_time(
run,
last_split_index,
comparison2,
timing_method,
),
))
} else {
None
};
(comparison1, comparison2)
} else {
Default::default()
};
let display_icon = self.settings.display_icon;
let icon_change = self
.icon_id
.update_with(current_split.filter(|_| display_icon).map(Segment::icon))
.map(Into::into);
self.timer
.update_state(&mut state.timer, timer, layout_settings);
self.segment_timer
.update_state(&mut state.segment_timer, timer, layout_settings);
state.background = self
.settings
.background
.gradient(state.timer.semantic_color.visualize(layout_settings));
update_comparison(&mut state.comparison1, comparison1);
update_comparison(&mut state.comparison2, comparison2);
match current_split.filter(|_| self.settings.show_segment_name) {
Some(segment) => {
let segment_name = state.segment_name.get_or_insert_with(String::new);
segment_name.clear();
segment_name.push_str(segment.name());
}
None => state.segment_name = None,
}
state.icon_change = icon_change;
}
pub fn state(
&mut self,
timer: &Snapshot<'_>,
layout_settings: &GeneralLayoutSettings,
) -> State {
let mut state = Default::default();
self.update_state(&mut state, timer, layout_settings);
state
}
pub fn remount(&mut self) {
self.icon_id.reset();
}
pub fn settings_description(&self) -> SettingsDescription {
SettingsDescription::with_fields(vec![
Field::new("Background".into(), self.settings.background.into()),
Field::new(
"Timing Method".into(),
self.settings.timer.timing_method.into(),
),
Field::new(
"Comparison 1".into(),
self.settings.comparison1.clone().into(),
),
Field::new(
"Comparison 2".into(),
self.settings.comparison2.clone().into(),
),
Field::new(
"Hide Second Comparison".into(),
self.settings.hide_second_comparison.into(),
),
Field::new(
"Timer Height".into(),
u64::from(self.settings.timer.height).into(),
),
Field::new(
"Segment Timer Height".into(),
u64::from(self.settings.segment_timer.height).into(),
),
Field::new(
"Timer Digits Format".into(),
self.settings.timer.digits_format.into(),
),
Field::new("Timer Accuracy".into(), self.settings.timer.accuracy.into()),
Field::new(
"Segment Timer Digits Format".into(),
self.settings.segment_timer.digits_format.into(),
),
Field::new(
"Segment Timer Accuracy".into(),
self.settings.segment_timer.accuracy.into(),
),
Field::new(
"Show Segment Name".into(),
self.settings.show_segment_name.into(),
),
Field::new("Display Icon".into(), self.settings.display_icon.into()),
])
}
pub fn set_value(&mut self, index: usize, value: Value) {
match index {
0 => self.settings.background = value.into(),
1 => {
let value = value.into();
self.settings.timer.timing_method = value;
self.settings.segment_timer.timing_method = value;
}
2 => self.settings.comparison1 = value.into(),
3 => self.settings.comparison2 = value.into(),
4 => self.settings.hide_second_comparison = value.into(),
5 => {
let value = value.into_uint().unwrap() as _;
self.settings.timer.height = value;
self.timer.settings_mut().height = value;
}
6 => {
let value = value.into_uint().unwrap() as _;
self.settings.segment_timer.height = value;
self.segment_timer.settings_mut().height = value;
}
7 => {
let value: DigitsFormat = value.into();
self.settings.timer.digits_format = value;
self.timer.settings_mut().digits_format = value;
}
8 => {
let value: Accuracy = value.into();
self.settings.timer.accuracy = value;
self.timer.settings_mut().accuracy = value;
}
9 => {
let value: DigitsFormat = value.into();
self.settings.segment_timer.digits_format = value;
self.segment_timer.settings_mut().digits_format = value;
}
10 => {
let value: Accuracy = value.into();
self.settings.segment_timer.accuracy = value;
self.segment_timer.settings_mut().accuracy = value;
}
11 => self.settings.show_segment_name = value.into(),
12 => self.settings.display_icon = value.into(),
_ => panic!("Unsupported Setting Index"),
}
}
}