diff --git a/docs/cli/animation-duration.md b/docs/cli/animation-duration.md index dd6f98ed..e0ec8d34 100644 --- a/docs/cli/animation-duration.md +++ b/docs/cli/animation-duration.md @@ -13,7 +13,7 @@ Options: -a, --animation-type Animation type to apply the duration to. If not specified, sets global duration - [possible values: movement, transparency] + [possible values: movement, transparency, workspace] -h, --help Print help diff --git a/docs/cli/animation-style.md b/docs/cli/animation-style.md index f5f9efe9..11cc3eae 100644 --- a/docs/cli/animation-style.md +++ b/docs/cli/animation-style.md @@ -16,7 +16,7 @@ Options: -a, --animation-type Animation type to apply the style to. If not specified, sets global style - [possible values: movement, transparency] + [possible values: movement, transparency, workspace] -h, --help Print help diff --git a/docs/cli/animation.md b/docs/cli/animation.md index 135e57be..530bcaa8 100644 --- a/docs/cli/animation.md +++ b/docs/cli/animation.md @@ -13,7 +13,7 @@ Options: -a, --animation-type Animation type to apply the state to. If not specified, sets global state - [possible values: movement, transparency] + [possible values: movement, transparency, workspace] -h, --help Print help diff --git a/docs/common-workflows/animations.md b/docs/common-workflows/animations.md index 52792091..e6740583 100644 --- a/docs/common-workflows/animations.md +++ b/docs/common-workflows/animations.md @@ -25,4 +25,67 @@ It is important to note that higher `fps` and a longer `duration` settings will in increased CPU usage. This feature is not considered stable, and you may encounter visual artifacts -from time to time. \ No newline at end of file +from time to time. + +## Workspace transitions + +Workspaces can slide in and out of the monitor when you switch between them: the +windows of the workspace you leave slide out of the monitor, and the windows of the +workspace you switch to slide in from the other side, from the right when you go to +a workspace with a higher index (or move right across the edge of a workspace when +`cross_boundary_behaviour` is `Workspace`) and from the left when you go to one with +a lower index (or move left). + +It is on when animations are enabled for everything (`"enabled": true`) and ghost +movement is on, and can be turned off in particular with its own animation kind. It +can also be turned on by itself, without the other kinds: + +```json +{ + "animation": { + "enabled": { + "movement": true, + "workspace": true + }, + "ghost_movement": true, + "duration": { + "movement": 250, + "workspace": 350 + }, + "style": { + "movement": "EaseOutSine", + "workspace": "EaseOutExpo" + } + } +} +``` + +To turn it off and keep the rest: + +```json +{ + "animation": { + "enabled": true, + "ghost_movement": true + } +} +``` + +becomes + +```json +{ + "animation": { + "enabled": { "movement": true, "workspace": false }, + "ghost_movement": true + } +} +``` + +Without `ghost_movement` workspaces switch instantly, as they do without this setting. +The windows never leave the monitor they are on, so no part of them is shown on a +monitor next to it. + +The same can be set with `komorebic animation enable --animation-type workspace`, +`komorebic animation-duration 350 --animation-type workspace` and +`komorebic animation-style --style ease-out-expo --animation-type workspace`. \ No newline at end of file diff --git a/komorebi/src/animation/mod.rs b/komorebi/src/animation/mod.rs index f22ad148..86578fac 100644 --- a/komorebi/src/animation/mod.rs +++ b/komorebi/src/animation/mod.rs @@ -7,6 +7,8 @@ use std::collections::HashMap; use std::sync::Arc; use std::sync::atomic::AtomicBool; use std::sync::atomic::AtomicU64; +use std::sync::atomic::Ordering; +use std::time::Duration; use parking_lot::Mutex; @@ -82,5 +84,139 @@ lazy_static! { Arc::new(Mutex::new(HashMap::new())); } +/// Whether the windows of a workspace slide out of a monitor and those of the +/// workspace that is switched to slide in. Animations being enabled for all +/// kinds enables this one too, unless it is turned off for this kind in +/// particular. The windows are slid as ghosts, so ghosts are needed. +fn transitions_enabled_by( + ghost_movement: bool, + enabled_globally: bool, + enabled: &HashMap, +) -> bool { + ghost_movement + && enabled + .get(&AnimationPrefix::Workspace) + .copied() + .unwrap_or(enabled_globally) +} + +/// How long the animation of a kind takes, which is what it is set to or else +/// what animations take +fn duration_for( + prefix: AnimationPrefix, + per_animation: &HashMap, + global: u64, +) -> Duration { + Duration::from_millis(per_animation.get(&prefix).copied().unwrap_or(global)) +} + +/// How the animation of a kind eases, which is what it is set to or else what +/// animations do +fn style_for( + prefix: AnimationPrefix, + per_animation: &HashMap, + global: AnimationStyle, +) -> AnimationStyle { + per_animation.get(&prefix).copied().unwrap_or(global) +} + +/// Whether workspaces slide in and out of the monitor when they are switched +pub fn workspace_transitions_enabled() -> bool { + transitions_enabled_by( + GHOST_MOVEMENT_ENABLED.load(Ordering::Relaxed), + ANIMATION_ENABLED_GLOBAL.load(Ordering::SeqCst), + &ANIMATION_ENABLED_PER_ANIMATION.lock(), + ) +} + +/// How long a workspace takes to slide in and out of a monitor +pub fn workspace_transition_duration() -> Duration { + duration_for( + AnimationPrefix::Workspace, + &ANIMATION_DURATION_PER_ANIMATION.lock(), + ANIMATION_DURATION_GLOBAL.load(Ordering::SeqCst), + ) +} + +/// How a workspace eases as it slides in and out of a monitor +pub fn workspace_transition_style() -> AnimationStyle { + style_for( + AnimationPrefix::Workspace, + &ANIMATION_STYLE_PER_ANIMATION.lock(), + *ANIMATION_STYLE_GLOBAL.lock(), + ) +} + pub static ANIMATION_FPS: AtomicU64 = AtomicU64::new(DEFAULT_ANIMATION_FPS); pub static GHOST_MOVEMENT_ENABLED: AtomicBool = AtomicBool::new(DEFAULT_GHOST_MOVEMENT); + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn workspaces_slide_when_animations_are_on_unless_turned_off_for_them() { + let none = HashMap::new(); + let workspace = HashMap::from([(AnimationPrefix::Workspace, true)]); + let movement = HashMap::from([(AnimationPrefix::Movement, true)]); + let off = HashMap::from([(AnimationPrefix::Workspace, false)]); + + // all animations on is this one on + assert!(transitions_enabled_by(true, true, &none)); + assert!(transitions_enabled_by(true, true, &movement)); + + // unless it is turned off in particular + assert!(!transitions_enabled_by(true, true, &off)); + + // or on in particular, without the rest + assert!(transitions_enabled_by(true, false, &workspace)); + assert!(!transitions_enabled_by(true, false, &none)); + assert!(!transitions_enabled_by(true, false, &movement)); + + // and it needs ghosts + assert!(!transitions_enabled_by(false, true, &none)); + assert!(!transitions_enabled_by(false, false, &workspace)); + } + + #[test] + fn the_duration_and_style_are_the_ones_of_the_kind_or_else_the_global_ones() { + let durations = HashMap::from([(AnimationPrefix::Workspace, 400)]); + + assert_eq!( + duration_for(AnimationPrefix::Workspace, &durations, 250), + Duration::from_millis(400) + ); + assert_eq!( + duration_for(AnimationPrefix::Movement, &durations, 250), + Duration::from_millis(250) + ); + assert_eq!( + duration_for(AnimationPrefix::Workspace, &HashMap::new(), 250), + Duration::from_millis(250) + ); + + let styles = HashMap::from([(AnimationPrefix::Workspace, AnimationStyle::EaseOutSine)]); + + assert_eq!( + style_for(AnimationPrefix::Workspace, &styles, AnimationStyle::Linear), + AnimationStyle::EaseOutSine + ); + assert_eq!( + style_for(AnimationPrefix::Movement, &styles, AnimationStyle::Linear), + AnimationStyle::Linear + ); + } + + #[test] + fn the_workspace_kind_is_named_in_the_configuration() { + assert_eq!(AnimationPrefix::Workspace.to_string(), "workspace"); + assert_eq!( + "workspace".parse::().unwrap(), + AnimationPrefix::Workspace + ); + assert_eq!( + serde_json::from_str::("\"workspace\"").unwrap(), + AnimationPrefix::Workspace + ); + } +} diff --git a/komorebi/src/animation/prefix.rs b/komorebi/src/animation/prefix.rs index 6e91d176..47132db7 100644 --- a/komorebi/src/animation/prefix.rs +++ b/komorebi/src/animation/prefix.rs @@ -14,6 +14,8 @@ use strum::EnumString; pub enum AnimationPrefix { Movement, Transparency, + /// A workspace sliding in and out of a monitor when it is switched to + Workspace, } pub fn new_animation_key(prefix: AnimationPrefix, key: String) -> String { diff --git a/schema.bar.json b/schema.bar.json index 2058c8a0..63bac232 100644 --- a/schema.bar.json +++ b/schema.bar.json @@ -187,10 +187,19 @@ ], "$defs": { "AnimationPrefix": { - "type": "string", - "enum": [ - "movement", - "transparency" + "oneOf": [ + { + "type": "string", + "enum": [ + "movement", + "transparency" + ] + }, + { + "description": "A workspace sliding in and out of a monitor when it is switched to", + "type": "string", + "const": "workspace" + } ] }, "AnimationStyle": { diff --git a/schema.json b/schema.json index de0d6070..3c9b20cb 100644 --- a/schema.json +++ b/schema.json @@ -3653,15 +3653,9 @@ { "description": "Animation configuration prefixed for a specific animation kind", "type": "object", - "properties": { - "movement": { - "type": "boolean" - }, - "transparency": { - "type": "boolean" - } - }, - "additionalProperties": false + "additionalProperties": { + "type": "boolean" + } }, { "description": "Animation configuration for all animation kinds", @@ -3675,19 +3669,11 @@ { "description": "Animation configuration prefixed for a specific animation kind", "type": "object", - "properties": { - "movement": { - "type": "integer", - "format": "uint64", - "minimum": 0 - }, - "transparency": { - "type": "integer", - "format": "uint64", - "minimum": 0 - } - }, - "additionalProperties": false + "additionalProperties": { + "type": "integer", + "format": "uint64", + "minimum": 0 + } }, { "description": "Animation configuration for all animation kinds", @@ -3703,15 +3689,9 @@ { "description": "Animation configuration prefixed for a specific animation kind", "type": "object", - "properties": { - "movement": { - "$ref": "#/$defs/AnimationStyle" - }, - "transparency": { - "$ref": "#/$defs/AnimationStyle" - } - }, - "additionalProperties": false + "additionalProperties": { + "$ref": "#/$defs/AnimationStyle" + } }, { "description": "Animation configuration for all animation kinds",