Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 59 additions & 0 deletions capi/src/current_segment_component.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
//! The Current Segment Component is a component that shows how much time will
//! be saved or lost during the current segment based on the chosen comparison.
//! It displays the difference between the current segment time
//! and the chosen comparison segment time.

use super::{output_vec, Json};
use crate::component::OwnedComponent;
use crate::key_value_component_state::OwnedKeyValueComponentState;
use livesplit_core::component::current_segment::Component as CurrentSegmentComponent;
use livesplit_core::{GeneralLayoutSettings, Timer};

/// type
pub type OwnedCurrentSegmentComponent = Box<CurrentSegmentComponent>;

/// Creates a new Current Segment Component.
#[unsafe(no_mangle)]
pub extern "C" fn CurrentSegmentComponent_new() -> OwnedCurrentSegmentComponent {
Box::new(CurrentSegmentComponent::new())
}

/// drop
#[unsafe(no_mangle)]
pub extern "C" fn CurrentSegmentComponent_drop(this: OwnedCurrentSegmentComponent) {
drop(this);
}

/// Converts the component into a generic component suitable for using with a
/// layout.
#[unsafe(no_mangle)]
pub extern "C" fn CurrentSegmentComponent_into_generic(
this: OwnedCurrentSegmentComponent,
) -> OwnedComponent {
Box::new((*this).into())
}

/// Encodes the component's state information as JSON.
#[unsafe(no_mangle)]
pub extern "C" fn CurrentSegmentComponent_state_as_json(
this: &CurrentSegmentComponent,
timer: &Timer,
layout_settings: &GeneralLayoutSettings,
) -> Json {
output_vec(|o| {
this.state(&timer.snapshot(), layout_settings)
.write_json(o)
.unwrap();
})
}

/// Calculates the component's state based on the timer and the layout
/// settings provided.
#[unsafe(no_mangle)]
pub extern "C" fn CurrentSegmentComponent_state(
this: &CurrentSegmentComponent,
timer: &Timer,
layout_settings: &GeneralLayoutSettings,
) -> OwnedKeyValueComponentState {
Box::new(this.state(&timer.snapshot(), layout_settings))
}
1 change: 1 addition & 0 deletions capi/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ pub mod command_sink;
pub mod component;
pub mod current_comparison_component;
pub mod current_pace_component;
pub mod current_segment_component;
pub mod delta_component;
pub mod detailed_timer_component;
pub mod detailed_timer_component_state;
Expand Down
261 changes: 261 additions & 0 deletions src/component/current_segment.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,261 @@
//! Provides the Current Segment Component and relevant types for using it. The
//! Current Segment Component is a component that shows how much time will be saved
//! or lost during the current [`Segment`](crate::run::Segment) based on the
//! chosen comparison. It displays the difference between the current segment time
//! and the chosen comparison segment time. Additionally, the potential time save for the current
//! [`Segment`](crate::run::Segment) can be displayed.

use super::key_value;
use crate::{
analysis, comparison, platform::prelude::*, settings::{Color, Field, Gradient, SemanticColor, SettingsDescription, Value}, timing::{
formatter::{Accuracy, Delta, SegmentTime, TimeFormatter}, Snapshot
}, GeneralLayoutSettings
};
use alloc::borrow::Cow;
use core::fmt::Write as FmtWrite;
use serde_derive::{Deserialize, Serialize};

/// Provides the Current Segment Component and relevant types for using it. The
/// Current Segment Component is a component that shows how much time will be saved
/// or lost during the current [`Segment`](crate::run::Segment) based on the
/// chosen comparison. It displays the difference between the current segment time
/// and the chosen comparison segment time. Additionally, the potential time save for the current
/// [`Segment`](crate::run::Segment) can be displayed.
#[derive(Default, Clone)]
pub struct Component {
settings: Settings,
}

/// The Settings for this component.
#[derive(Clone, Serialize, Deserialize)]
#[serde(default)]
pub struct Settings {
/// The background shown behind the component.
pub background: Gradient,
/// The comparison chosen. Uses the Timer's current comparison if set to
/// `None`.
pub comparison_override: Option<String>,
/// Specifies whether to display the name of the component and its value in
/// two separate rows.
pub display_two_rows: bool,
/// The color of the label. If `None` is specified, the color is taken from
/// the layout.
pub label_color: Option<Color>,
/// Specifies if the decimals should not be shown anymore when the
/// visualized delta is above one minute.
pub drop_decimals: bool,
/// The accuracy of the time shown.
pub accuracy: Accuracy,
/// Determines if the time save that could've been saved is shown in
/// addition to the previous segment.
pub show_possible_time_save: bool,
}

impl Default for Settings {
fn default() -> Self {
Self {
background: key_value::DEFAULT_GRADIENT,
comparison_override: None,
display_two_rows: false,
label_color: None,
drop_decimals: true,
accuracy: Accuracy::Tenths,
show_possible_time_save: false,
}
}
}

impl Component {
/// Creates a new Current Segment Component.
pub fn new() -> Self {
Default::default()
}

/// Creates a new Current Segment Component with the given settings.
pub const fn with_settings(settings: Settings) -> Self {
Self { settings }
}

/// Accesses the settings of the component.
pub const fn settings(&self) -> &Settings {
&self.settings
}

/// Grants mutable access to the settings of the component.
pub const fn settings_mut(&mut self) -> &mut Settings {
&mut self.settings
}

/// Accesses the name of the component.
pub fn name(&self) -> Cow<'static, str> {
self.text(
self.settings
.comparison_override
.as_ref()
.map(String::as_ref),
)
}

fn text(&self, comparison: Option<&str>) -> Cow<'static, str> {
let text = "Current Segment";
let mut text = Cow::from(text);
if let Some(comparison) = comparison {
write!(text.to_mut(), " ({})", comparison::shorten(comparison)).unwrap();
}
text
}

/// Updates the component's state based on the timer and layout settings
/// provided.
pub fn update_state(
&self,
state: &mut key_value::State,
timer: &Snapshot<'_>,
layout_settings: &GeneralLayoutSettings,
) {
let resolved_comparison = comparison::resolve(&self.settings.comparison_override, timer);
let comparison = comparison::or_current(resolved_comparison, timer);
let method = timer.current_timing_method();
let phase = timer.current_phase();
let mut time_change = None;
let mut possible_save = None;
let mut semantic_color = SemanticColor::Default;
if phase.is_running() || phase.is_paused() {
if let Some(split_index) = timer.current_split_index() {
time_change = analysis::live_segment_delta(
timer,
split_index,
comparison,
method
);
if self.settings.show_possible_time_save {
possible_save = analysis::possible_time_save::calculate(
timer,
split_index,
comparison,
false,
)
.0
};
semantic_color = analysis::split_color(
timer,
time_change,
split_index,
false,
false,
comparison,
method,
);
};
};

let value_color = Some(semantic_color.visualize(layout_settings));

let text = self.text(resolved_comparison);

state.background = self.settings.background;
state.key_color = self.settings.label_color;
state.value_color = value_color;
state.semantic_color = semantic_color;

state.key.clear();
state.key.push_str(&text); // FIXME: Uncow

state.value.clear();
let _ = write!(
state.value,
"{}",
Delta::custom(self.settings.drop_decimals, self.settings.accuracy).format(time_change),
);

if self.settings.show_possible_time_save {
let _ = write!(
state.value,
" / {}",
SegmentTime::with_accuracy(self.settings.accuracy).format(possible_save),
);
}

state.key_abbreviations.clear();
state.key_abbreviations.push("Current Segment".into());
state.key_abbreviations.push("Curr. Segment".into());
state.key_abbreviations.push("Curr. Seg.".into());

state.display_two_rows = self.settings.display_two_rows;
state.updates_frequently = phase.updates_frequently(method);
}

/// Calculates the component's state based on the timer and the layout
/// settings provided.
pub fn state(
&self,
timer: &Snapshot<'_>,
layout_settings: &GeneralLayoutSettings,
) -> key_value::State {
let mut state = Default::default();
self.update_state(&mut state, timer, layout_settings);
state
}

/// Accesses a generic description of the settings available for this
/// component and their current values.
pub fn settings_description(&self) -> SettingsDescription {
SettingsDescription::with_fields(vec![
Field::new(
"Background".into(),
"The background shown behind the component.".into(),
self.settings.background.into(),
),
Field::new(
"Comparison".into(),
"The comparison used for calculating how much time was saved or lost. If not specified, the current comparison is used.".into(),
self.settings.comparison_override.clone().into(),
),
Field::new(
"Display 2 Rows".into(),
"Specifies whether to display the name of the component and how much time was saved or lost in two separate rows.".into(),
self.settings.display_two_rows.into(),
),
Field::new(
"Label Color".into(),
"The color of the component's name. If not specified, the color is taken from the layout.".into(),
self.settings.label_color.into(),
),
Field::new(
"Drop Decimals".into(),
"Specifies whether to drop the decimals from the time when the time shown is over a minute.".into(),
self.settings.drop_decimals.into(),
),
Field::new(
"Accuracy".into(),
"The accuracy of the time shown.".into(),
self.settings.accuracy.into(),
),
Field::new(
"Show Possible Time Save".into(),
"Specifies whether to show how much time could be saved for the currrent segment in addition to the current delta.".into(),
self.settings.show_possible_time_save.into(),
),
])
}

/// Sets a setting's value by its index to the given value.
///
/// # Panics
///
/// This panics if the type of the value to be set is not compatible with
/// the type of the setting's value. A panic can also occur if the index of
/// the setting provided is out of bounds.
pub fn set_value(&mut self, index: usize, value: Value) {
match index {
0 => self.settings.background = value.into(),
1 => self.settings.comparison_override = value.into(),
2 => self.settings.display_two_rows = value.into(),
3 => self.settings.label_color = value.into(),
4 => self.settings.drop_decimals = value.into(),
5 => self.settings.accuracy = value.into(),
6 => self.settings.show_possible_time_save = value.into(),
_ => panic!("Unsupported Setting Index"),
}
}
}
2 changes: 2 additions & 0 deletions src/component/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
pub mod blank_space;
pub mod current_comparison;
pub mod current_pace;
pub mod current_segment;
pub mod delta;
pub mod detailed_timer;
pub mod graph;
Expand All @@ -26,6 +27,7 @@ pub mod key_value;
pub use blank_space::Component as BlankSpace;
pub use current_comparison::Component as CurrentComparison;
pub use current_pace::Component as CurrentPace;
pub use current_segment::Component as CurrentSegment;
pub use delta::Component as Delta;
pub use detailed_timer::Component as DetailedTimer;
pub use graph::Component as Graph;
Expand Down
Loading