diff --git a/Cargo.toml b/Cargo.toml index b7b6f734..846434ea 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -87,6 +87,7 @@ tokio = { version = "1.17.0", default-features = false, features = [ "time", ], optional = true } log = { version = "0.4.14", default-features = false, optional = true } +lazy_static = "1.4.0" [target.'cfg(all(target_arch = "wasm32", target_os = "unknown"))'.dependencies] # WebAssembly in the Web diff --git a/src/layout/general_settings.rs b/src/layout/general_settings.rs index e35dffdd..a31a5a1f 100644 --- a/src/layout/general_settings.rs +++ b/src/layout/general_settings.rs @@ -2,9 +2,12 @@ use super::LayoutDirection; use crate::{ platform::prelude::*, settings::{Color, Field, Font, Gradient, SettingsDescription, Value}, + timing::visual_cycle_timer }; use serde::{Deserialize, Serialize}; + + /// The general settings of the layout that apply to all components. #[derive(Clone, Serialize, Deserialize)] #[serde(default)] @@ -24,6 +27,8 @@ pub struct GeneralSettings { pub background: Gradient, /// The color to use for when the runner achieved a best segment. pub best_segment_color: Color, + /// Should we render best segment splits as a rainbow? + pub rainbow_for_best_segments: bool, /// The color to use for when the runner is ahead of the comparison and is /// gaining even more time. pub ahead_gaining_time_color: Color, @@ -59,6 +64,7 @@ impl Default for GeneralSettings { text_font: None, background: Gradient::Plain(Color::hsla(0.0, 0.0, 0.06, 1.0)), best_segment_color: Color::hsla(50.0, 1.0, 0.5, 1.0), + rainbow_for_best_segments: false, ahead_gaining_time_color: Color::hsla(136.0, 1.0, 0.4, 1.0), ahead_losing_time_color: Color::hsla(136.0, 0.55, 0.6, 1.0), behind_gaining_time_color: Color::hsla(0.0, 0.55, 0.6, 1.0), @@ -84,6 +90,7 @@ impl GeneralSettings { Field::new("Custom Text Font".into(), self.text_font.clone().into()), Field::new("Background".into(), self.background.into()), Field::new("Best Segment".into(), self.best_segment_color.into()), + Field::new("Use Rainbow Best Segment Color".into(), self.rainbow_for_best_segments.into()), Field::new( "Ahead (Gaining Time)".into(), self.ahead_gaining_time_color.into(), @@ -124,17 +131,33 @@ impl GeneralSettings { 3 => self.text_font = value.into(), 4 => self.background = value.into(), 5 => self.best_segment_color = value.into(), - 6 => self.ahead_gaining_time_color = value.into(), - 7 => self.ahead_losing_time_color = value.into(), - 8 => self.behind_gaining_time_color = value.into(), - 9 => self.behind_losing_time_color = value.into(), - 10 => self.not_running_color = value.into(), - 11 => self.personal_best_color = value.into(), - 12 => self.paused_color = value.into(), - 13 => self.thin_separators_color = value.into(), - 14 => self.separators_color = value.into(), - 15 => self.text_color = value.into(), + 6 => self.rainbow_for_best_segments = value.into(), + 7 => self.ahead_gaining_time_color = value.into(), + 8 => self.ahead_losing_time_color = value.into(), + 9 => self.behind_gaining_time_color = value.into(), + 10 => self.behind_losing_time_color = value.into(), + 11 => self.not_running_color = value.into(), + 12 => self.personal_best_color = value.into(), + 13 => self.paused_color = value.into(), + 14 => self.thin_separators_color = value.into(), + 15 => self.separators_color = value.into(), + 16 => self.text_color = value.into(), _ => panic!("Unsupported Setting Index"), } } + + /// Gets the best segment color. If `rainbow_for_best_segments` is false, + /// this just returns the `best_segment_color` field. otherwise, it returns + /// a color that cycles based on the current system times + pub fn get_best_segment_color(&self) -> Color { + if self.rainbow_for_best_segments { + Color::hsva( + ((visual_cycle_timer() / 100.) % 36. * 10.) as f32, + 1.0, 1.0, 1.0 + ) + } + else { + self.best_segment_color + } + } } diff --git a/src/layout/parser/mod.rs b/src/layout/parser/mod.rs index e7ecda22..e1dc2379 100644 --- a/src/layout/parser/mod.rs +++ b/src/layout/parser/mod.rs @@ -628,6 +628,9 @@ fn parse_general_settings(layout: &mut Layout, reader: &mut Reader<'_>) -> Resul "PersonalBestColor" => color(reader, |color| { settings.personal_best_color = color; }), + "UseRainbowColor" => parse_bool(reader, |color| { + settings.rainbow_for_best_segments = color; + }), "AheadGainingTimeColor" => color(reader, |color| { settings.ahead_gaining_time_color = color; }), diff --git a/src/settings/semantic_color.rs b/src/settings/semantic_color.rs index 982212bc..106e8b3c 100644 --- a/src/settings/semantic_color.rs +++ b/src/settings/semantic_color.rs @@ -48,14 +48,14 @@ impl SemanticColor { /// The General Settings store actual Color values for each of the different /// events. Using this method, you can use these to convert a Semantic Color /// to an actual Color. - pub const fn visualize(self, settings: &layout::GeneralSettings) -> Color { + pub fn visualize(self, settings: &layout::GeneralSettings) -> Color { match self { SemanticColor::Default => settings.text_color, SemanticColor::AheadGainingTime => settings.ahead_gaining_time_color, SemanticColor::AheadLosingTime => settings.ahead_losing_time_color, SemanticColor::BehindLosingTime => settings.behind_losing_time_color, SemanticColor::BehindGainingTime => settings.behind_gaining_time_color, - SemanticColor::BestSegment => settings.best_segment_color, + SemanticColor::BestSegment => settings.get_best_segment_color(), SemanticColor::NotRunning => settings.not_running_color, SemanticColor::Paused => settings.paused_color, SemanticColor::PersonalBest => settings.personal_best_color, diff --git a/src/timing/mod.rs b/src/timing/mod.rs index 2bc23713..a6efdd95 100644 --- a/src/timing/mod.rs +++ b/src/timing/mod.rs @@ -19,3 +19,19 @@ pub use self::timer::SharedTimer; pub use self::timer::{CreationError as TimerCreationError, Snapshot, Timer}; pub use self::timer_phase::TimerPhase; pub use self::timing_method::TimingMethod; + + +/// A function used for visual elements that change over time, without any +/// specific connection to an actual timer. This function returns an f64 +/// which approximately changes by 1 every millisecond. No guarantees are +/// made about the initial value of this, so you'll almost definitely want to +/// modulo this. Currently this is used to animate the rainbows on gold segments. +pub fn visual_cycle_timer() -> f64 { + use lazy_static::lazy_static; + + lazy_static! { + static ref TIME: TimeStamp = TimeStamp::now(); + } + + (TimeStamp::now() - *TIME).total_milliseconds() +} \ No newline at end of file