use std::time::Duration;
use crate::animation::AnimatorConfig;
use crate::animation::config::PositionAnimation;
use crate::animation::easing::EasingStyle;
use crate::config::types::ConfigEasing;
use crate::config::types::FlowConfig;
use crate::layout::types::{MonitorInfo, Padding as LayoutPadding};
use super::types::LayoutConfig;
pub(super) fn derive_layout_config(app_config: &FlowConfig, monitor: &MonitorInfo) -> LayoutConfig {
let gap = app_config.padding.window_gap;
let column_width = match app_config.column_width {
Some(cw) => {
log::debug!(
"column_width: using explicit override of {}px (ignoring columns_per_screen={})",
cw,
app_config.columns_per_screen,
);
cw
}
None => {
let monitor_width = monitor.work_area.width;
let n = app_config.columns_per_screen as i32;
let total_gap = (n + 1) * gap;
let computed = (monitor_width - total_gap) / n;
log::debug!(
"column_width: auto-computed from columns_per_screen={}, monitor_width={}px, window_gap={}px → {}px",
app_config.columns_per_screen,
monitor_width,
gap,
computed,
);
computed as u32
}
};
LayoutConfig {
column_width,
min_column_width_px: app_config.min_column_width_px,
min_window_height_px: app_config.min_window_height_px,
padding: LayoutPadding {
window_gap: app_config.padding.window_gap,
up: app_config.padding.up,
down: app_config.padding.down,
},
}
}
fn config_easing_to_style(easing: &ConfigEasing) -> EasingStyle {
match easing {
ConfigEasing::Linear => EasingStyle::Linear,
ConfigEasing::EaseInSine => EasingStyle::EaseInSine,
ConfigEasing::EaseOutSine => EasingStyle::EaseOutSine,
ConfigEasing::EaseInOutSine => EasingStyle::EaseInOutSine,
ConfigEasing::EaseInQuad => EasingStyle::EaseInQuad,
ConfigEasing::EaseOutQuad => EasingStyle::EaseOutQuad,
ConfigEasing::EaseInOutQuad => EasingStyle::EaseInOutQuad,
ConfigEasing::EaseInCubic => EasingStyle::EaseInCubic,
ConfigEasing::EaseOutCubic => EasingStyle::EaseOutCubic,
ConfigEasing::EaseInOutCubic => EasingStyle::EaseInOutCubic,
ConfigEasing::EaseInQuart => EasingStyle::EaseInQuart,
ConfigEasing::EaseOutQuart => EasingStyle::EaseOutQuart,
ConfigEasing::EaseInOutQuart => EasingStyle::EaseInOutQuart,
ConfigEasing::EaseInQuint => EasingStyle::EaseInQuint,
ConfigEasing::EaseOutQuint => EasingStyle::EaseOutQuint,
ConfigEasing::EaseInOutQuint => EasingStyle::EaseInOutQuint,
ConfigEasing::EaseInExpo => EasingStyle::EaseInExpo,
ConfigEasing::EaseOutExpo => EasingStyle::EaseOutExpo,
ConfigEasing::EaseInOutExpo => EasingStyle::EaseInOutExpo,
ConfigEasing::EaseInCirc => EasingStyle::EaseInCirc,
ConfigEasing::EaseOutCirc => EasingStyle::EaseOutCirc,
ConfigEasing::EaseInOutCirc => EasingStyle::EaseInOutCirc,
ConfigEasing::EaseInBack => EasingStyle::EaseInBack,
ConfigEasing::EaseOutBack => EasingStyle::EaseOutBack,
ConfigEasing::EaseInOutBack => EasingStyle::EaseInOutBack,
ConfigEasing::EaseInElastic => EasingStyle::EaseInElastic,
ConfigEasing::EaseOutElastic => EasingStyle::EaseOutElastic,
ConfigEasing::EaseInOutElastic => EasingStyle::EaseInOutElastic,
ConfigEasing::EaseInBounce => EasingStyle::EaseInBounce,
ConfigEasing::EaseOutBounce => EasingStyle::EaseOutBounce,
ConfigEasing::EaseInOutBounce => EasingStyle::EaseInOutBounce,
}
}
pub(super) fn derive_animator_config(
app_config: &FlowConfig,
override_duration: Duration,
) -> AnimatorConfig {
let duration = if override_duration != Duration::ZERO {
override_duration
} else if app_config.animation.enabled {
Duration::from_millis(app_config.animation.duration_ms as u64)
} else {
Duration::ZERO
};
let position_animation =
PositionAnimation::Custom(config_easing_to_style(&app_config.animation.easing));
AnimatorConfig {
duration,
position_animation,
..AnimatorConfig::default()
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::animation::config::PositionAnimation;
use crate::animation::easing::EasingStyle;
use crate::config::types::AnimationConfig;
use crate::config::types::ConfigEasing;
fn make_enabled_config(duration_ms: u32) -> FlowConfig {
FlowConfig {
animation: AnimationConfig {
enabled: true,
duration_ms,
..AnimationConfig::default()
},
..FlowConfig::default()
}
}
fn make_disabled_config() -> FlowConfig {
FlowConfig {
animation: AnimationConfig {
enabled: false,
..AnimationConfig::default()
},
..FlowConfig::default()
}
}
#[test]
fn derive_animator_config_zero_sentinel_uses_user_settings() {
let cfg = make_enabled_config(250);
let result = derive_animator_config(&cfg, Duration::ZERO);
assert_eq!(
result.duration,
Duration::from_millis(250),
"Duration::ZERO sentinel with animation enabled should use user's 250ms"
);
let cfg = make_disabled_config();
let result = derive_animator_config(&cfg, Duration::ZERO);
assert_eq!(
result.duration,
Duration::ZERO,
"Duration::ZERO sentinel with animation disabled should produce zero duration"
);
}
#[test]
fn derive_animator_config_nonzero_override_overrides_user() {
let cfg = make_enabled_config(250);
let result = derive_animator_config(&cfg, Duration::from_millis(50));
assert_eq!(
result.duration,
Duration::from_millis(50),
"non-zero override should take precedence over user's 250ms"
);
let cfg = make_disabled_config();
let result = derive_animator_config(&cfg, Duration::from_millis(100));
assert_eq!(
result.duration,
Duration::from_millis(100),
"non-zero override should take precedence even when animation is disabled"
);
}
#[test]
fn derive_animator_config_maps_easing_to_custom() {
let cfg = make_enabled_config(250);
let result = derive_animator_config(&cfg, Duration::ZERO);
match &result.position_animation {
PositionAnimation::Custom(style) => {
assert_eq!(*style, EasingStyle::EaseOutExpo);
}
other => panic!("expected Custom, got {other:?}"),
}
}
#[test]
fn derive_animator_config_maps_linear_easing() {
let mut cfg = make_enabled_config(250);
cfg.animation.easing = ConfigEasing::Linear;
let result = derive_animator_config(&cfg, Duration::ZERO);
match &result.position_animation {
PositionAnimation::Custom(style) => {
assert_eq!(*style, EasingStyle::Linear);
}
other => panic!("expected Custom(Linear), got {other:?}"),
}
}
#[test]
fn config_easing_to_style_all_variants() {
for (config_easing, expected_style) in [
(ConfigEasing::Linear, EasingStyle::Linear),
(ConfigEasing::EaseInSine, EasingStyle::EaseInSine),
(ConfigEasing::EaseOutSine, EasingStyle::EaseOutSine),
(ConfigEasing::EaseInOutSine, EasingStyle::EaseInOutSine),
(ConfigEasing::EaseInQuad, EasingStyle::EaseInQuad),
(ConfigEasing::EaseOutQuad, EasingStyle::EaseOutQuad),
(ConfigEasing::EaseInOutQuad, EasingStyle::EaseInOutQuad),
(ConfigEasing::EaseInCubic, EasingStyle::EaseInCubic),
(ConfigEasing::EaseOutCubic, EasingStyle::EaseOutCubic),
(ConfigEasing::EaseInOutCubic, EasingStyle::EaseInOutCubic),
(ConfigEasing::EaseInQuart, EasingStyle::EaseInQuart),
(ConfigEasing::EaseOutQuart, EasingStyle::EaseOutQuart),
(ConfigEasing::EaseInOutQuart, EasingStyle::EaseInOutQuart),
(ConfigEasing::EaseInQuint, EasingStyle::EaseInQuint),
(ConfigEasing::EaseOutQuint, EasingStyle::EaseOutQuint),
(ConfigEasing::EaseInOutQuint, EasingStyle::EaseInOutQuint),
(ConfigEasing::EaseInExpo, EasingStyle::EaseInExpo),
(ConfigEasing::EaseOutExpo, EasingStyle::EaseOutExpo),
(ConfigEasing::EaseInOutExpo, EasingStyle::EaseInOutExpo),
(ConfigEasing::EaseInCirc, EasingStyle::EaseInCirc),
(ConfigEasing::EaseOutCirc, EasingStyle::EaseOutCirc),
(ConfigEasing::EaseInOutCirc, EasingStyle::EaseInOutCirc),
(ConfigEasing::EaseInBack, EasingStyle::EaseInBack),
(ConfigEasing::EaseOutBack, EasingStyle::EaseOutBack),
(ConfigEasing::EaseInOutBack, EasingStyle::EaseInOutBack),
(ConfigEasing::EaseInElastic, EasingStyle::EaseInElastic),
(ConfigEasing::EaseOutElastic, EasingStyle::EaseOutElastic),
(
ConfigEasing::EaseInOutElastic,
EasingStyle::EaseInOutElastic,
),
(ConfigEasing::EaseInBounce, EasingStyle::EaseInBounce),
(ConfigEasing::EaseOutBounce, EasingStyle::EaseOutBounce),
(ConfigEasing::EaseInOutBounce, EasingStyle::EaseInOutBounce),
] {
assert_eq!(
config_easing_to_style(&config_easing),
expected_style,
"mismatch for {config_easing:?}"
);
}
}
}