mirror of
https://github.com/LGUG2Z/komorebi.git
synced 2026-01-13 14:23:24 +01:00
Compare commits
6 Commits
feature/ni
...
animate-wi
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
23cc38b1d3 | ||
|
|
a8aad69ecb | ||
|
|
e24835a4ae | ||
|
|
50b12431fa | ||
|
|
95df970860 | ||
|
|
9d1c0ad790 |
43
komorebi-core/src/animation.rs
Normal file
43
komorebi-core/src/animation.rs
Normal file
@@ -0,0 +1,43 @@
|
||||
use clap::ValueEnum;
|
||||
use schemars::JsonSchema;
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
use strum::Display;
|
||||
use strum::EnumString;
|
||||
|
||||
#[derive(
|
||||
Copy, Clone, Debug, Serialize, Deserialize, Display, EnumString, ValueEnum, JsonSchema,
|
||||
)]
|
||||
#[strum(serialize_all = "snake_case")]
|
||||
pub enum EaseEnum {
|
||||
Linear,
|
||||
EaseInSine,
|
||||
EaseOutSine,
|
||||
EaseInOutSine,
|
||||
EaseInQuad,
|
||||
EaseOutQuad,
|
||||
EaseInOutQuad,
|
||||
EaseInCubic,
|
||||
EaseInOutCubic,
|
||||
EaseInQuart,
|
||||
EaseOutQuart,
|
||||
EaseInOutQuart,
|
||||
EaseInQuint,
|
||||
EaseOutQuint,
|
||||
EaseInOutQuint,
|
||||
EaseInExpo,
|
||||
EaseOutExpo,
|
||||
EaseInOutExpo,
|
||||
EaseInCirc,
|
||||
EaseOutCirc,
|
||||
EaseInOutCirc,
|
||||
EaseInBack,
|
||||
EaseOutBack,
|
||||
EaseInOutBack,
|
||||
EaseInElastic,
|
||||
EaseOutElastic,
|
||||
EaseInOutElastic,
|
||||
EaseInBounce,
|
||||
EaseOutBounce,
|
||||
EaseInOutBounce,
|
||||
}
|
||||
@@ -14,6 +14,7 @@ use serde::Serialize;
|
||||
use strum::Display;
|
||||
use strum::EnumString;
|
||||
|
||||
pub use animation::EaseEnum;
|
||||
pub use arrangement::Arrangement;
|
||||
pub use arrangement::Axis;
|
||||
pub use custom_layout::CustomLayout;
|
||||
@@ -24,6 +25,7 @@ pub use layout::Layout;
|
||||
pub use operation_direction::OperationDirection;
|
||||
pub use rect::Rect;
|
||||
|
||||
pub mod animation;
|
||||
pub mod arrangement;
|
||||
pub mod config_generation;
|
||||
pub mod custom_layout;
|
||||
@@ -127,6 +129,9 @@ pub enum SocketMessage {
|
||||
WatchConfiguration(bool),
|
||||
CompleteConfiguration,
|
||||
AltFocusHack(bool),
|
||||
Animation(bool),
|
||||
AnimationDuration(u64),
|
||||
AnimationEase(EaseEnum),
|
||||
ActiveWindowBorder(bool),
|
||||
ActiveWindowBorderColour(WindowKind, u32, u32, u32),
|
||||
ActiveWindowBorderWidth(i32),
|
||||
|
||||
510
komorebi/src/animation.rs
Normal file
510
komorebi/src/animation.rs
Normal file
@@ -0,0 +1,510 @@
|
||||
use color_eyre::Result;
|
||||
use komorebi_core::EaseEnum;
|
||||
use komorebi_core::Rect;
|
||||
|
||||
use schemars::JsonSchema;
|
||||
|
||||
use std::f64::consts::PI;
|
||||
use std::time::Duration;
|
||||
use std::time::Instant;
|
||||
|
||||
use crate::ANIMATION_EASE;
|
||||
|
||||
pub trait Ease {
|
||||
fn evaluate(t: f64) -> f64;
|
||||
}
|
||||
|
||||
pub struct Linear;
|
||||
|
||||
impl Ease for Linear {
|
||||
fn evaluate(t: f64) -> f64 {
|
||||
t
|
||||
}
|
||||
}
|
||||
|
||||
pub struct EaseInSine;
|
||||
|
||||
impl Ease for EaseInSine {
|
||||
fn evaluate(t: f64) -> f64 {
|
||||
1.0 - f64::cos((t * PI) / 2.0)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct EaseOutSine;
|
||||
|
||||
impl Ease for EaseOutSine {
|
||||
fn evaluate(t: f64) -> f64 {
|
||||
f64::sin((t * PI) / 2.0)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct EaseInOutSine;
|
||||
|
||||
impl Ease for EaseInOutSine {
|
||||
fn evaluate(t: f64) -> f64 {
|
||||
-(f64::cos(PI * t) - 1.0) / 2.0
|
||||
}
|
||||
}
|
||||
|
||||
pub struct EaseInQuad;
|
||||
|
||||
impl Ease for EaseInQuad {
|
||||
fn evaluate(t: f64) -> f64 {
|
||||
t * t
|
||||
}
|
||||
}
|
||||
|
||||
pub struct EaseOutQuad;
|
||||
|
||||
impl Ease for EaseOutQuad {
|
||||
fn evaluate(t: f64) -> f64 {
|
||||
(1.0 - t).mul_add(-1.0 - t, 1.0)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct EaseInOutQuad;
|
||||
|
||||
impl Ease for EaseInOutQuad {
|
||||
fn evaluate(t: f64) -> f64 {
|
||||
if t < 0.5 {
|
||||
2.0 * t * t
|
||||
} else {
|
||||
1.0 - (-2.0f64).mul_add(t, 2.0).powi(2) / 2.0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct EaseInCubic;
|
||||
|
||||
impl Ease for EaseInCubic {
|
||||
fn evaluate(t: f64) -> f64 {
|
||||
t * t * t
|
||||
}
|
||||
}
|
||||
|
||||
pub struct EaseOutCubic;
|
||||
|
||||
impl Ease for EaseOutCubic {
|
||||
fn evaluate(t: f64) -> f64 {
|
||||
1.0 - (1.0 - t).powi(3)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct EaseInOutCubic;
|
||||
|
||||
impl Ease for EaseInOutCubic {
|
||||
fn evaluate(t: f64) -> f64 {
|
||||
if t < 0.5 {
|
||||
4.0 * t * t * t
|
||||
} else {
|
||||
1.0 - (-2.0f64).mul_add(t, 2.0).powi(3) / 2.0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct EaseInQuart;
|
||||
|
||||
impl Ease for EaseInQuart {
|
||||
fn evaluate(t: f64) -> f64 {
|
||||
t * t * t * t
|
||||
}
|
||||
}
|
||||
|
||||
pub struct EaseOutQuart;
|
||||
|
||||
impl Ease for EaseOutQuart {
|
||||
fn evaluate(t: f64) -> f64 {
|
||||
1.0 - (1.0 - t).powi(4)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct EaseInOutQuart;
|
||||
|
||||
impl Ease for EaseInOutQuart {
|
||||
fn evaluate(t: f64) -> f64 {
|
||||
if t < 0.5 {
|
||||
8.0 * t * t * t * t
|
||||
} else {
|
||||
1.0 - (-2.0f64).mul_add(t, 2.0).powi(4) / 2.0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct EaseInQuint;
|
||||
|
||||
impl Ease for EaseInQuint {
|
||||
fn evaluate(t: f64) -> f64 {
|
||||
t * t * t * t * t
|
||||
}
|
||||
}
|
||||
|
||||
pub struct EaseOutQuint;
|
||||
|
||||
impl Ease for EaseOutQuint {
|
||||
fn evaluate(t: f64) -> f64 {
|
||||
1.0 - (1.0 - t).powi(5)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct EaseInOutQuint;
|
||||
|
||||
impl Ease for EaseInOutQuint {
|
||||
fn evaluate(t: f64) -> f64 {
|
||||
if t < 0.5 {
|
||||
16.0 * t * t * t * t
|
||||
} else {
|
||||
1.0 - (-2.0f64).mul_add(t, 2.0).powi(5) / 2.0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct EaseInExpo;
|
||||
|
||||
impl Ease for EaseInExpo {
|
||||
fn evaluate(t: f64) -> f64 {
|
||||
if t == 0.0 {
|
||||
return t;
|
||||
}
|
||||
|
||||
10.0f64.mul_add(t, -10.0).exp2()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct EaseOutExpo;
|
||||
|
||||
impl Ease for EaseOutExpo {
|
||||
fn evaluate(t: f64) -> f64 {
|
||||
if (t - 1.0).abs() < f64::EPSILON {
|
||||
return t;
|
||||
}
|
||||
|
||||
1.0 - (-10.0 * t).exp2()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct EaseInOutExpo;
|
||||
|
||||
impl Ease for EaseInOutExpo {
|
||||
fn evaluate(t: f64) -> f64 {
|
||||
if t == 0.0 || (t - 1.0).abs() < f64::EPSILON {
|
||||
return t;
|
||||
}
|
||||
|
||||
if t < 0.5 {
|
||||
20.0f64.mul_add(t, -10.0).exp2() / 2.0
|
||||
} else {
|
||||
(2.0 - (-20.0f64).mul_add(t, 10.0).exp2()) / 2.0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct EaseInCirc;
|
||||
|
||||
impl Ease for EaseInCirc {
|
||||
fn evaluate(t: f64) -> f64 {
|
||||
1.0 - f64::sqrt(t.mul_add(-t, 1.0))
|
||||
}
|
||||
}
|
||||
|
||||
pub struct EaseOutCirc;
|
||||
|
||||
impl Ease for EaseOutCirc {
|
||||
fn evaluate(t: f64) -> f64 {
|
||||
f64::sqrt((t - 1.0).mul_add(-(t - 1.0), 1.0))
|
||||
}
|
||||
}
|
||||
|
||||
pub struct EaseInOutCirc;
|
||||
|
||||
impl Ease for EaseInOutCirc {
|
||||
fn evaluate(t: f64) -> f64 {
|
||||
if t < 0.5 {
|
||||
(1.0 - f64::sqrt((2.0 * t).mul_add(-(2.0 * t), 1.0))) / 2.0
|
||||
} else {
|
||||
(f64::sqrt(
|
||||
(-2.0f64)
|
||||
.mul_add(t, 2.0)
|
||||
.mul_add(-(-2.0f64).mul_add(t, 2.0), 1.0),
|
||||
) + 1.0)
|
||||
/ 2.0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct EaseInBack;
|
||||
|
||||
impl Ease for EaseInBack {
|
||||
fn evaluate(t: f64) -> f64 {
|
||||
let c1 = 1.70158;
|
||||
let c3 = c1 + 1.0;
|
||||
|
||||
(c3 * t * t).mul_add(t, -c1 * t * t)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct EaseOutBack;
|
||||
|
||||
impl Ease for EaseOutBack {
|
||||
fn evaluate(t: f64) -> f64 {
|
||||
let c1: f64 = 1.70158;
|
||||
let c3: f64 = c1 + 1.0;
|
||||
|
||||
c1.mul_add((t - 1.0).powi(2), c3.mul_add((t - 1.0).powi(3), 1.0))
|
||||
}
|
||||
}
|
||||
|
||||
pub struct EaseInOutBack;
|
||||
|
||||
impl Ease for EaseInOutBack {
|
||||
fn evaluate(t: f64) -> f64 {
|
||||
let c1: f64 = 1.70158;
|
||||
let c2: f64 = c1 * 1.525;
|
||||
|
||||
if t < 0.5 {
|
||||
((2.0 * t).powi(2) * ((c2 + 1.0) * 2.0).mul_add(t, -c2)) / 2.0
|
||||
} else {
|
||||
((2.0f64.mul_add(t, -2.0))
|
||||
.powi(2)
|
||||
.mul_add((c2 + 1.0).mul_add(t.mul_add(2.0, -2.0), c2), 2.0))
|
||||
/ 2.0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct EaseInElastic;
|
||||
|
||||
impl Ease for EaseInElastic {
|
||||
fn evaluate(t: f64) -> f64 {
|
||||
if (t - 1.0).abs() < f64::EPSILON || t == 0.0 {
|
||||
return t;
|
||||
}
|
||||
|
||||
let c4 = (2.0 * PI) / 3.0;
|
||||
|
||||
-(10.0f64.mul_add(t, -10.0).exp2()) * f64::sin(t.mul_add(10.0, -10.75) * c4)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct EaseOutElastic;
|
||||
|
||||
impl Ease for EaseOutElastic {
|
||||
fn evaluate(t: f64) -> f64 {
|
||||
if (t - 1.0).abs() < f64::EPSILON || t == 0.0 {
|
||||
return t;
|
||||
}
|
||||
|
||||
let c4 = (2.0 * PI) / 3.0;
|
||||
|
||||
(-10.0 * t)
|
||||
.exp2()
|
||||
.mul_add(f64::sin(t.mul_add(10.0, -0.75) * c4), 1.0)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct EaseInOutElastic;
|
||||
|
||||
impl Ease for EaseInOutElastic {
|
||||
fn evaluate(t: f64) -> f64 {
|
||||
if (t - 1.0).abs() < f64::EPSILON || t == 0.0 {
|
||||
return t;
|
||||
}
|
||||
|
||||
let c5 = (2.0 * PI) / 4.5;
|
||||
|
||||
if t < 0.5 {
|
||||
-(20.0f64.mul_add(t, -10.0).exp2() * f64::sin(20.0f64.mul_add(t, -11.125) * c5)) / 2.0
|
||||
} else {
|
||||
((-20.0f64).mul_add(t, 10.0).exp2() * f64::sin(20.0f64.mul_add(t, -11.125) * c5)) / 2.0
|
||||
+ 1.0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct EaseInBounce;
|
||||
|
||||
impl Ease for EaseInBounce {
|
||||
fn evaluate(t: f64) -> f64 {
|
||||
1.0 - EaseOutBounce::evaluate(1.0 - t)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct EaseOutBounce;
|
||||
|
||||
impl Ease for EaseOutBounce {
|
||||
fn evaluate(t: f64) -> f64 {
|
||||
let mut time = t;
|
||||
let n1 = 7.5625;
|
||||
let d1 = 2.75;
|
||||
|
||||
if t < 1.0 / d1 {
|
||||
n1 * time * time
|
||||
} else if time < 2.0 / d1 {
|
||||
time -= 1.5 / d1;
|
||||
(n1 * time).mul_add(time, 0.75)
|
||||
} else if time < 2.5 / d1 {
|
||||
time -= 2.25 / d1;
|
||||
(n1 * time).mul_add(time, 0.9375)
|
||||
} else {
|
||||
time -= 2.625 / d1;
|
||||
(n1 * time).mul_add(time, 0.984_375)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct EaseInOutBounce;
|
||||
|
||||
impl Ease for EaseInOutBounce {
|
||||
fn evaluate(t: f64) -> f64 {
|
||||
if t < 0.5 {
|
||||
(1.0 - EaseOutBounce::evaluate(2.0f64.mul_add(-t, 1.0))) / 2.0
|
||||
} else {
|
||||
(1.0 + EaseOutBounce::evaluate(2.0f64.mul_add(t, -1.0))) / 2.0
|
||||
}
|
||||
}
|
||||
}
|
||||
fn apply_ease_func(t: f64) -> f64 {
|
||||
let ease = *ANIMATION_EASE.lock();
|
||||
|
||||
match ease {
|
||||
EaseEnum::Linear => Linear::evaluate(t),
|
||||
EaseEnum::EaseInSine => EaseInSine::evaluate(t),
|
||||
EaseEnum::EaseOutSine => EaseOutSine::evaluate(t),
|
||||
EaseEnum::EaseInOutSine => EaseInOutSine::evaluate(t),
|
||||
EaseEnum::EaseInQuad => EaseInQuad::evaluate(t),
|
||||
EaseEnum::EaseOutQuad => EaseOutQuad::evaluate(t),
|
||||
EaseEnum::EaseInOutQuad => EaseInOutQuad::evaluate(t),
|
||||
EaseEnum::EaseInCubic => EaseInCubic::evaluate(t),
|
||||
EaseEnum::EaseInOutCubic => EaseInOutCubic::evaluate(t),
|
||||
EaseEnum::EaseInQuart => EaseInQuart::evaluate(t),
|
||||
EaseEnum::EaseOutQuart => EaseOutQuart::evaluate(t),
|
||||
EaseEnum::EaseInOutQuart => EaseInOutQuart::evaluate(t),
|
||||
EaseEnum::EaseInQuint => EaseInQuint::evaluate(t),
|
||||
EaseEnum::EaseOutQuint => EaseOutQuint::evaluate(t),
|
||||
EaseEnum::EaseInOutQuint => EaseInOutQuint::evaluate(t),
|
||||
EaseEnum::EaseInExpo => EaseInExpo::evaluate(t),
|
||||
EaseEnum::EaseOutExpo => EaseOutExpo::evaluate(t),
|
||||
EaseEnum::EaseInOutExpo => EaseInOutExpo::evaluate(t),
|
||||
EaseEnum::EaseInCirc => EaseInCirc::evaluate(t),
|
||||
EaseEnum::EaseOutCirc => EaseOutCirc::evaluate(t),
|
||||
EaseEnum::EaseInOutCirc => EaseInOutCirc::evaluate(t),
|
||||
EaseEnum::EaseInBack => EaseInBack::evaluate(t),
|
||||
EaseEnum::EaseOutBack => EaseOutBack::evaluate(t),
|
||||
EaseEnum::EaseInOutBack => EaseInOutBack::evaluate(t),
|
||||
EaseEnum::EaseInElastic => EaseInElastic::evaluate(t),
|
||||
EaseEnum::EaseOutElastic => EaseOutElastic::evaluate(t),
|
||||
EaseEnum::EaseInOutElastic => EaseInOutElastic::evaluate(t),
|
||||
EaseEnum::EaseInBounce => EaseInBounce::evaluate(t),
|
||||
EaseEnum::EaseOutBounce => EaseOutBounce::evaluate(t),
|
||||
EaseEnum::EaseInOutBounce => EaseInOutBounce::evaluate(t),
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Clone, Copy, JsonSchema)]
|
||||
pub struct Animation {
|
||||
// is_cancel: AtomicBool,
|
||||
// pub in_progress: AtomicBool,
|
||||
is_cancel: bool,
|
||||
pub in_progress: bool,
|
||||
}
|
||||
|
||||
// impl Default for Animation {
|
||||
// fn default() -> Self {
|
||||
// Animation {
|
||||
// // I'm not sure if this is the right way to do it
|
||||
// // I've tried to use Arc<Mutex<bool>> but it dooes not implement Copy trait
|
||||
// // and I dont want to rewrite everything cause I'm not experienced with rust
|
||||
// // Down here you can see the idea I've tried to achive like in any other OOP language
|
||||
// // My thought is that in order to prevent Google Chrome breaking render window
|
||||
// // I need to cancel animation if user starting new window movement. So window stops
|
||||
// // moving at one point and then fires new animation.
|
||||
// // But my approach does not work because of rust borrowing rules and wired pointers
|
||||
// // lifetime annotation that I dont know how to use.
|
||||
// is_cancel: false,
|
||||
// in_progress: false,
|
||||
// // is_cancel: AtomicBool::new(false),
|
||||
// // in_progress: AtomicBool::new(false),
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
impl Animation {
|
||||
pub fn cancel(&mut self) {
|
||||
if !self.in_progress {
|
||||
return;
|
||||
}
|
||||
|
||||
self.is_cancel = true;
|
||||
let max_duration = Duration::from_secs(1);
|
||||
let spent_duration = Instant::now();
|
||||
|
||||
while self.in_progress {
|
||||
if spent_duration.elapsed() >= max_duration {
|
||||
self.in_progress = false;
|
||||
}
|
||||
|
||||
std::thread::sleep(Duration::from_millis(16));
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::cast_possible_truncation)]
|
||||
pub fn lerp(x: i32, new_x: i32, t: f64) -> i32 {
|
||||
let time = apply_ease_func(t);
|
||||
f64::from(new_x - x).mul_add(time, f64::from(x)) as i32
|
||||
}
|
||||
|
||||
pub fn lerp_rect(original_rect: &Rect, new_rect: &Rect, t: f64) -> Rect {
|
||||
Rect {
|
||||
left: Self::lerp(original_rect.left, new_rect.left, t),
|
||||
top: Self::lerp(original_rect.top, new_rect.top, t),
|
||||
right: Self::lerp(original_rect.right, new_rect.right, t),
|
||||
bottom: Self::lerp(original_rect.bottom, new_rect.bottom, t),
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::cast_precision_loss)]
|
||||
pub fn animate(
|
||||
&mut self,
|
||||
duration: Duration,
|
||||
mut f: impl FnMut(f64) -> Result<()>,
|
||||
) -> Result<()> {
|
||||
self.in_progress = true;
|
||||
// set target frame time to match 240 fps (my max refresh rate of monitor)
|
||||
// probably not the best way to do it is take actual monitor refresh rate
|
||||
// or make it configurable
|
||||
let target_frame_time = Duration::from_millis(1000 / 240);
|
||||
let mut progress = 0.0;
|
||||
let animation_start = Instant::now();
|
||||
|
||||
// start animation
|
||||
while progress < 1.0 {
|
||||
// check if animation is cancelled
|
||||
if self.is_cancel {
|
||||
// cancel animation
|
||||
// set all flags
|
||||
self.is_cancel = !self.is_cancel;
|
||||
self.in_progress = false;
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let tick_start = Instant::now();
|
||||
// calculate progress
|
||||
progress = animation_start.elapsed().as_millis() as f64 / duration.as_millis() as f64;
|
||||
f(progress).ok();
|
||||
|
||||
// sleep until next frame
|
||||
while tick_start.elapsed() < target_frame_time {
|
||||
std::thread::sleep(target_frame_time - tick_start.elapsed());
|
||||
}
|
||||
}
|
||||
|
||||
self.in_progress = false;
|
||||
|
||||
// limit progress to 1.0 if animation took longer
|
||||
if progress > 1.0 {
|
||||
progress = 1.0;
|
||||
}
|
||||
|
||||
// process animation for 1.0 to set target position
|
||||
f(progress)
|
||||
}
|
||||
}
|
||||
@@ -16,6 +16,7 @@ use std::sync::atomic::AtomicBool;
|
||||
use std::sync::atomic::AtomicI32;
|
||||
use std::sync::atomic::AtomicIsize;
|
||||
use std::sync::atomic::AtomicU32;
|
||||
use std::sync::atomic::AtomicU64;
|
||||
use std::sync::atomic::Ordering;
|
||||
use std::sync::Arc;
|
||||
#[cfg(feature = "deadlock_detection")]
|
||||
@@ -26,6 +27,7 @@ use color_eyre::Result;
|
||||
use crossbeam_channel::Receiver;
|
||||
use crossbeam_channel::Sender;
|
||||
use crossbeam_utils::Backoff;
|
||||
use komorebi_core::EaseEnum;
|
||||
use lazy_static::lazy_static;
|
||||
use os_info::Version;
|
||||
#[cfg(feature = "deadlock_detection")]
|
||||
@@ -65,6 +67,7 @@ use crate::windows_api::WindowsApi;
|
||||
#[macro_use]
|
||||
mod ring;
|
||||
|
||||
mod animation;
|
||||
mod border;
|
||||
mod com;
|
||||
mod container;
|
||||
@@ -214,6 +217,8 @@ lazy_static! {
|
||||
static ref BORDER_OFFSET: Arc<Mutex<Option<Rect>>> =
|
||||
Arc::new(Mutex::new(None));
|
||||
|
||||
static ref ANIMATION_EASE: Arc<Mutex<EaseEnum>> = Arc::new(Mutex::new(EaseEnum::Linear));
|
||||
|
||||
// Use app-specific titlebar removal options where possible
|
||||
// eg. Windows Terminal, IntelliJ IDEA, Firefox
|
||||
static ref NO_TITLEBAR: Arc<Mutex<Vec<String>>> = Arc::new(Mutex::new(vec![]));
|
||||
@@ -237,6 +242,8 @@ pub static BORDER_WIDTH: AtomicI32 = AtomicI32::new(20);
|
||||
// 0 0 0 aka pure black, I doubt anyone will want this as a border colour
|
||||
pub const TRANSPARENCY_COLOUR: u32 = 0;
|
||||
pub static REMOVE_TITLEBARS: AtomicBool = AtomicBool::new(false);
|
||||
pub static ANIMATION_ENABLED: AtomicBool = AtomicBool::new(false);
|
||||
pub static ANIMATION_DURATION: AtomicU64 = AtomicU64::new(250);
|
||||
|
||||
pub static HIDDEN_HWND: AtomicIsize = AtomicIsize::new(0);
|
||||
|
||||
|
||||
@@ -47,6 +47,9 @@ use crate::windows_api::WindowsApi;
|
||||
use crate::Notification;
|
||||
use crate::NotificationEvent;
|
||||
use crate::ALT_FOCUS_HACK;
|
||||
use crate::ANIMATION_DURATION;
|
||||
use crate::ANIMATION_EASE;
|
||||
use crate::ANIMATION_ENABLED;
|
||||
use crate::BORDER_COLOUR_CURRENT;
|
||||
use crate::BORDER_COLOUR_MONOCLE;
|
||||
use crate::BORDER_COLOUR_SINGLE;
|
||||
@@ -1140,6 +1143,15 @@ impl WindowManager {
|
||||
self.hide_border()?;
|
||||
}
|
||||
}
|
||||
SocketMessage::Animation(enable) => {
|
||||
ANIMATION_ENABLED.store(enable, Ordering::SeqCst);
|
||||
}
|
||||
SocketMessage::AnimationDuration(duration) => {
|
||||
ANIMATION_DURATION.store(duration, Ordering::SeqCst);
|
||||
}
|
||||
SocketMessage::AnimationEase(ease) => {
|
||||
*ANIMATION_EASE.lock() = ease;
|
||||
}
|
||||
SocketMessage::ActiveWindowBorderColour(kind, r, g, b) => {
|
||||
match kind {
|
||||
WindowKind::Single => {
|
||||
@@ -1295,7 +1307,7 @@ impl WindowManager {
|
||||
| SocketMessage::CycleMoveWindow(_)
|
||||
| SocketMessage::MoveWindow(_) => {
|
||||
let foreground = WindowsApi::foreground_window()?;
|
||||
let foreground_window = Window { hwnd: foreground };
|
||||
let foreground_window = Window::new(foreground);
|
||||
let mut rect = WindowsApi::window_rect(foreground_window.hwnd())?;
|
||||
rect.top -= self.invisible_borders.bottom;
|
||||
rect.bottom += self.invisible_borders.bottom;
|
||||
|
||||
@@ -515,7 +515,8 @@ impl WindowManager {
|
||||
WindowManagerEvent::DisplayChange(..)
|
||||
| WindowManagerEvent::MouseCapture(..)
|
||||
| WindowManagerEvent::Cloak(..)
|
||||
| WindowManagerEvent::Uncloak(..) => {}
|
||||
| WindowManagerEvent::Uncloak(..)
|
||||
| WindowManagerEvent::UpdateFocusedWindowBorder(..) => {}
|
||||
};
|
||||
|
||||
if *self.focused_workspace()?.tile() && BORDER_ENABLED.load(Ordering::SeqCst) {
|
||||
@@ -529,7 +530,8 @@ impl WindowManager {
|
||||
| WindowManagerEvent::Show(_, window)
|
||||
| WindowManagerEvent::FocusChange(_, window)
|
||||
| WindowManagerEvent::Hide(_, window)
|
||||
| WindowManagerEvent::Minimize(_, window) => {
|
||||
| WindowManagerEvent::Minimize(_, window)
|
||||
| WindowManagerEvent::UpdateFocusedWindowBorder(window) => {
|
||||
let border = Border::from(BORDER_HWND.load(Ordering::SeqCst));
|
||||
let mut target_window = None;
|
||||
let mut target_window_is_monocle = false;
|
||||
@@ -595,6 +597,10 @@ impl WindowManager {
|
||||
WindowsApi::invalidate_border_rect()?;
|
||||
border.set_position(target_window, &self.invisible_borders, activate)?;
|
||||
|
||||
if matches!(event, WindowManagerEvent::UpdateFocusedWindowBorder(_)) {
|
||||
window.focus(self.mouse_follows_focus)?;
|
||||
}
|
||||
|
||||
if activate {
|
||||
BORDER_HIDDEN.store(false, Ordering::SeqCst);
|
||||
}
|
||||
|
||||
@@ -7,6 +7,9 @@ use crate::window_manager_event::WindowManagerEvent;
|
||||
use crate::windows_api::WindowsApi;
|
||||
use crate::workspace::Workspace;
|
||||
use crate::ALT_FOCUS_HACK;
|
||||
use crate::ANIMATION_DURATION;
|
||||
use crate::ANIMATION_EASE;
|
||||
use crate::ANIMATION_ENABLED;
|
||||
use crate::BORDER_COLOUR_CURRENT;
|
||||
use crate::BORDER_COLOUR_MONOCLE;
|
||||
use crate::BORDER_COLOUR_SINGLE;
|
||||
@@ -39,6 +42,7 @@ use komorebi_core::config_generation::MatchingStrategy;
|
||||
use komorebi_core::resolve_home_path;
|
||||
use komorebi_core::ApplicationIdentifier;
|
||||
use komorebi_core::DefaultLayout;
|
||||
use komorebi_core::EaseEnum;
|
||||
use komorebi_core::FocusFollowsMouseImplementation;
|
||||
use komorebi_core::HidingBehaviour;
|
||||
use komorebi_core::Layout;
|
||||
@@ -312,6 +316,15 @@ pub struct StaticConfig {
|
||||
/// Set monitor index preferences
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub monitor_index_preferences: Option<HashMap<usize, Rect>>,
|
||||
/// Enable or disable animations (default: false)
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub animation: Option<bool>,
|
||||
/// Set the animation ease function (default: Linear)
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub animation_ease: Option<EaseEnum>,
|
||||
/// Set the animation duration in ms (default: 250)
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub animation_duration: Option<u64>,
|
||||
}
|
||||
|
||||
impl From<&WindowManager> for StaticConfig {
|
||||
@@ -432,6 +445,9 @@ impl From<&WindowManager> for StaticConfig {
|
||||
layered_applications: None,
|
||||
object_name_change_applications: None,
|
||||
monitor_index_preferences: Option::from(MONITOR_INDEX_PREFERENCES.lock().clone()),
|
||||
animation: Option::from(ANIMATION_ENABLED.load(Ordering::SeqCst)),
|
||||
animation_duration: Option::from(ANIMATION_DURATION.load(Ordering::SeqCst)),
|
||||
animation_ease: Option::from(*ANIMATION_EASE.lock()),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -449,6 +465,19 @@ impl StaticConfig {
|
||||
*window_hiding_behaviour = behaviour;
|
||||
}
|
||||
|
||||
if let Some(animation) = self.animation {
|
||||
ANIMATION_ENABLED.store(animation, Ordering::SeqCst);
|
||||
}
|
||||
|
||||
if let Some(duration) = self.animation_duration {
|
||||
ANIMATION_DURATION.store(duration, Ordering::SeqCst);
|
||||
}
|
||||
|
||||
if let Some(ease) = self.animation_ease {
|
||||
let mut animation_ease = ANIMATION_EASE.lock();
|
||||
*animation_ease = ease;
|
||||
}
|
||||
|
||||
if let Some(hack) = self.alt_focus_hack {
|
||||
ALT_FOCUS_HACK.store(hack, Ordering::SeqCst);
|
||||
}
|
||||
|
||||
@@ -1,10 +1,14 @@
|
||||
use crate::com::SetCloak;
|
||||
use crate::winevent_listener::WINEVENT_CALLBACK_CHANNEL;
|
||||
use crate::ANIMATION_DURATION;
|
||||
use crate::ANIMATION_ENABLED;
|
||||
use std::collections::HashMap;
|
||||
use std::convert::TryFrom;
|
||||
use std::fmt::Display;
|
||||
use std::fmt::Formatter;
|
||||
use std::fmt::Write as _;
|
||||
use std::sync::atomic::Ordering;
|
||||
use std::time::Duration;
|
||||
|
||||
use color_eyre::eyre::anyhow;
|
||||
use color_eyre::Result;
|
||||
@@ -25,6 +29,7 @@ use komorebi_core::ApplicationIdentifier;
|
||||
use komorebi_core::HidingBehaviour;
|
||||
use komorebi_core::Rect;
|
||||
|
||||
use crate::animation::Animation;
|
||||
use crate::styles::ExtendedWindowStyle;
|
||||
use crate::styles::WindowStyle;
|
||||
use crate::window_manager_event::WindowManagerEvent;
|
||||
@@ -44,6 +49,7 @@ use crate::WSL2_UI_PROCESSES;
|
||||
#[derive(Debug, Clone, Copy, JsonSchema)]
|
||||
pub struct Window {
|
||||
pub(crate) hwnd: isize,
|
||||
animation: Animation,
|
||||
}
|
||||
|
||||
impl Display for Window {
|
||||
@@ -103,6 +109,14 @@ impl Serialize for Window {
|
||||
}
|
||||
|
||||
impl Window {
|
||||
// for instantiation of animation struct
|
||||
pub fn new(hwnd: isize) -> Self {
|
||||
Self {
|
||||
hwnd,
|
||||
animation: Animation::default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub const fn hwnd(self) -> HWND {
|
||||
HWND(self.hwnd)
|
||||
}
|
||||
@@ -122,6 +136,47 @@ impl Window {
|
||||
true,
|
||||
)
|
||||
}
|
||||
pub fn animate_position(&mut self, layout: &Rect, top: bool) -> Result<()> {
|
||||
let hwnd = self.hwnd();
|
||||
let curr_rect = WindowsApi::window_rect(hwnd).unwrap();
|
||||
|
||||
if curr_rect.left == layout.left
|
||||
&& curr_rect.top == layout.top
|
||||
&& curr_rect.bottom == layout.bottom
|
||||
&& curr_rect.right == layout.right
|
||||
{
|
||||
WindowsApi::position_window(hwnd, layout, top)
|
||||
} else {
|
||||
let target_rect = *layout;
|
||||
let duration = Duration::from_millis(ANIMATION_DURATION.load(Ordering::SeqCst));
|
||||
let mut animation = self.animation;
|
||||
|
||||
let self_copied = *self;
|
||||
std::thread::spawn(move || {
|
||||
animation.animate(duration, |progress: f64| {
|
||||
let new_rect = Animation::lerp_rect(&curr_rect, &target_rect, progress);
|
||||
if progress < 1.0 {
|
||||
// using MoveWindow because it runs faster than SetWindowPos
|
||||
// so animation have more fps and feel smoother
|
||||
WindowsApi::move_window(hwnd, &new_rect, true)?;
|
||||
} else {
|
||||
WindowsApi::position_window(hwnd, &new_rect, top)?;
|
||||
|
||||
if WindowsApi::foreground_window()? == self_copied.hwnd {
|
||||
WINEVENT_CALLBACK_CHANNEL
|
||||
.lock()
|
||||
.0
|
||||
.send(WindowManagerEvent::UpdateFocusedWindowBorder(self_copied))?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
})
|
||||
});
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_position(
|
||||
&mut self,
|
||||
@@ -154,7 +209,17 @@ impl Window {
|
||||
rect.bottom += invisible_borders.bottom;
|
||||
}
|
||||
|
||||
WindowsApi::position_window(self.hwnd(), &rect, top)
|
||||
if ANIMATION_ENABLED.load(Ordering::SeqCst) {
|
||||
// check if animation is in progress
|
||||
if self.animation.in_progress {
|
||||
// wait for cancel animation
|
||||
self.animation.cancel();
|
||||
}
|
||||
|
||||
self.animate_position(&rect, top)
|
||||
} else {
|
||||
WindowsApi::position_window(self.hwnd(), &rect, top)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn hide(self) {
|
||||
|
||||
@@ -217,7 +217,7 @@ impl WindowManager {
|
||||
#[tracing::instrument(skip(self))]
|
||||
pub fn show_border(&self) -> Result<()> {
|
||||
let foreground = WindowsApi::foreground_window()?;
|
||||
let foreground_window = Window { hwnd: foreground };
|
||||
let foreground_window = Window::new(foreground);
|
||||
let mut rect = WindowsApi::window_rect(foreground_window.hwnd())?;
|
||||
rect.top -= self.invisible_borders.bottom;
|
||||
rect.bottom += self.invisible_borders.bottom;
|
||||
@@ -589,7 +589,7 @@ impl WindowManager {
|
||||
|
||||
// Hide the window we are about to remove if it is on the currently focused workspace
|
||||
if op.is_origin(focused_monitor_idx, focused_workspace_idx) {
|
||||
Window { hwnd: op.hwnd }.hide();
|
||||
Window::new(op.hwnd).hide();
|
||||
should_update_focused_workspace = true;
|
||||
}
|
||||
|
||||
@@ -619,7 +619,7 @@ impl WindowManager {
|
||||
.get_mut(op.target_workspace_idx)
|
||||
.ok_or_else(|| anyhow!("there is no workspace with that index"))?;
|
||||
|
||||
target_workspace.new_container_for_window(Window { hwnd: op.hwnd });
|
||||
target_workspace.new_container_for_window(Window::new(op.hwnd));
|
||||
}
|
||||
|
||||
// Only re-tile the focused workspace if we need to
|
||||
@@ -663,14 +663,14 @@ impl WindowManager {
|
||||
#[tracing::instrument(skip(self))]
|
||||
pub fn manage_focused_window(&mut self) -> Result<()> {
|
||||
let hwnd = WindowsApi::foreground_window()?;
|
||||
let event = WindowManagerEvent::Manage(Window { hwnd });
|
||||
let event = WindowManagerEvent::Manage(Window::new(hwnd));
|
||||
Ok(WINEVENT_CALLBACK_CHANNEL.lock().0.send(event)?)
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip(self))]
|
||||
pub fn unmanage_focused_window(&mut self) -> Result<()> {
|
||||
let hwnd = WindowsApi::foreground_window()?;
|
||||
let event = WindowManagerEvent::Unmanage(Window { hwnd });
|
||||
let event = WindowManagerEvent::Unmanage(Window::new(hwnd));
|
||||
Ok(WINEVENT_CALLBACK_CHANNEL.lock().0.send(event)?)
|
||||
}
|
||||
|
||||
@@ -708,13 +708,14 @@ impl WindowManager {
|
||||
];
|
||||
|
||||
if !known_hwnd {
|
||||
let class = Window { hwnd }.class()?;
|
||||
let class = Window::new(hwnd).class()?;
|
||||
// Some applications (Electron/Chromium-based, explorer) have (invisible?) overlays
|
||||
// windows that we need to look beyond to find the actual window to raise
|
||||
if overlay_classes.contains(&class) {
|
||||
for monitor in self.monitors() {
|
||||
for workspace in monitor.workspaces() {
|
||||
if let Some(exe_hwnd) = workspace.hwnd_from_exe(&Window { hwnd }.exe()?)
|
||||
if let Some(exe_hwnd) =
|
||||
workspace.hwnd_from_exe(&Window::new(hwnd).exe()?)
|
||||
{
|
||||
hwnd = exe_hwnd;
|
||||
known_hwnd = true;
|
||||
@@ -725,11 +726,11 @@ impl WindowManager {
|
||||
}
|
||||
|
||||
if known_hwnd {
|
||||
let event = WindowManagerEvent::Raise(Window { hwnd });
|
||||
let event = WindowManagerEvent::Raise(Window::new(hwnd));
|
||||
self.has_pending_raise_op = true;
|
||||
Ok(WINEVENT_CALLBACK_CHANNEL.lock().0.send(event)?)
|
||||
} else {
|
||||
tracing::debug!("not raising unknown window: {}", Window { hwnd });
|
||||
tracing::debug!("not raising unknown window: {}", Window::new(hwnd,));
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -843,9 +844,7 @@ impl WindowManager {
|
||||
} else if let Ok(window) = self.focused_window_mut() {
|
||||
window.focus(self.mouse_follows_focus)?;
|
||||
} else {
|
||||
let desktop_window = Window {
|
||||
hwnd: WindowsApi::desktop_window()?,
|
||||
};
|
||||
let desktop_window = Window::new(WindowsApi::desktop_window()?);
|
||||
|
||||
let rect = self.focused_monitor_size()?;
|
||||
WindowsApi::center_cursor_in_rect(&rect)?;
|
||||
|
||||
@@ -27,6 +27,7 @@ pub enum WindowManagerEvent {
|
||||
Unmanage(Window),
|
||||
Raise(Window),
|
||||
DisplayChange(Window),
|
||||
UpdateFocusedWindowBorder(Window),
|
||||
}
|
||||
|
||||
impl Display for WindowManagerEvent {
|
||||
@@ -77,6 +78,9 @@ impl Display for WindowManagerEvent {
|
||||
Self::DisplayChange(window) => {
|
||||
write!(f, "DisplayChange (Window: {window})")
|
||||
}
|
||||
Self::UpdateFocusedWindowBorder(window) => {
|
||||
write!(f, "UpdateFocusedBorderWindow (Window: {window})")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -97,7 +101,8 @@ impl WindowManagerEvent {
|
||||
| Self::Raise(window)
|
||||
| Self::Manage(window)
|
||||
| Self::DisplayChange(window)
|
||||
| Self::Unmanage(window) => window,
|
||||
| Self::Unmanage(window)
|
||||
| Self::UpdateFocusedWindowBorder(window) => window,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -81,6 +81,7 @@ use windows::Win32::UI::WindowsAndMessaging::GetWindowThreadProcessId;
|
||||
use windows::Win32::UI::WindowsAndMessaging::IsIconic;
|
||||
use windows::Win32::UI::WindowsAndMessaging::IsWindow;
|
||||
use windows::Win32::UI::WindowsAndMessaging::IsWindowVisible;
|
||||
use windows::Win32::UI::WindowsAndMessaging::MoveWindow;
|
||||
use windows::Win32::UI::WindowsAndMessaging::PostMessageW;
|
||||
use windows::Win32::UI::WindowsAndMessaging::RealGetWindowClassW;
|
||||
use windows::Win32::UI::WindowsAndMessaging::RegisterClassA;
|
||||
@@ -354,6 +355,20 @@ impl WindowsApi {
|
||||
.process()
|
||||
}
|
||||
|
||||
pub fn move_window(hwnd: HWND, layout: &Rect, repaint: bool) -> Result<()> {
|
||||
unsafe {
|
||||
MoveWindow(
|
||||
hwnd,
|
||||
layout.left,
|
||||
layout.top,
|
||||
layout.right,
|
||||
layout.bottom,
|
||||
repaint,
|
||||
)
|
||||
}
|
||||
.process()
|
||||
}
|
||||
|
||||
fn show_window(hwnd: HWND, command: SHOW_WINDOW_CMD) {
|
||||
// BOOL is returned but does not signify whether or not the operation was succesful
|
||||
// https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-showwindow
|
||||
|
||||
@@ -104,7 +104,7 @@ pub extern "system" fn enum_window(hwnd: HWND, lparam: LPARAM) -> BOOL {
|
||||
let is_minimized = WindowsApi::is_iconic(hwnd);
|
||||
|
||||
if is_visible && is_window && !is_minimized {
|
||||
let window = Window { hwnd: hwnd.0 };
|
||||
let window = Window::new(hwnd.0);
|
||||
|
||||
if let Ok(should_manage) = window.should_manage(None) {
|
||||
if should_manage {
|
||||
@@ -132,7 +132,7 @@ pub extern "system" fn win_event_hook(
|
||||
return;
|
||||
}
|
||||
|
||||
let window = Window { hwnd: hwnd.0 };
|
||||
let window = Window::new(hwnd.0);
|
||||
|
||||
let winevent = unsafe { ::std::mem::transmute(event) };
|
||||
let event_type = match WindowManagerEvent::from_win_event(winevent, window) {
|
||||
@@ -196,7 +196,7 @@ pub extern "system" fn hidden_window(
|
||||
unsafe {
|
||||
match message {
|
||||
WM_DISPLAYCHANGE => {
|
||||
let event_type = WindowManagerEvent::DisplayChange(Window { hwnd: window.0 });
|
||||
let event_type = WindowManagerEvent::DisplayChange(Window::new(window.0));
|
||||
WINEVENT_CALLBACK_CHANNEL
|
||||
.lock()
|
||||
.0
|
||||
@@ -211,7 +211,7 @@ pub extern "system" fn hidden_window(
|
||||
if wparam.0 as u32 == SPI_SETWORKAREA.0
|
||||
|| wparam.0 as u32 == SPI_ICONVERTICALSPACING.0
|
||||
{
|
||||
let event_type = WindowManagerEvent::DisplayChange(Window { hwnd: window.0 });
|
||||
let event_type = WindowManagerEvent::DisplayChange(Window::new(window.0));
|
||||
WINEVENT_CALLBACK_CHANNEL
|
||||
.lock()
|
||||
.0
|
||||
@@ -224,7 +224,7 @@ pub extern "system" fn hidden_window(
|
||||
WM_DEVICECHANGE => {
|
||||
#[allow(clippy::cast_possible_truncation)]
|
||||
if wparam.0 as u32 == DBT_DEVNODES_CHANGED {
|
||||
let event_type = WindowManagerEvent::DisplayChange(Window { hwnd: window.0 });
|
||||
let event_type = WindowManagerEvent::DisplayChange(Window::new(window.0));
|
||||
WINEVENT_CALLBACK_CHANNEL
|
||||
.lock()
|
||||
.0
|
||||
|
||||
@@ -19,11 +19,15 @@ use komorebi_core::Layout;
|
||||
use komorebi_core::OperationDirection;
|
||||
use komorebi_core::Rect;
|
||||
|
||||
use crate::border::Border;
|
||||
use crate::container::Container;
|
||||
use crate::ring::Ring;
|
||||
use crate::static_config::WorkspaceConfig;
|
||||
use crate::window::Window;
|
||||
use crate::windows_api::WindowsApi;
|
||||
use crate::ANIMATION_ENABLED;
|
||||
use crate::BORDER_HIDDEN;
|
||||
use crate::BORDER_HWND;
|
||||
use crate::DEFAULT_CONTAINER_PADDING;
|
||||
use crate::DEFAULT_WORKSPACE_PADDING;
|
||||
use crate::INITIAL_CONFIGURATION_LOADED;
|
||||
@@ -244,6 +248,12 @@ impl Workspace {
|
||||
}
|
||||
|
||||
if *self.tile() {
|
||||
if ANIMATION_ENABLED.load(Ordering::SeqCst) {
|
||||
let border = Border::from(BORDER_HWND.load(Ordering::SeqCst));
|
||||
border.hide()?;
|
||||
BORDER_HIDDEN.store(true, Ordering::SeqCst);
|
||||
}
|
||||
|
||||
if let Some(container) = self.monocle_container_mut() {
|
||||
if let Some(window) = container.focused_window_mut() {
|
||||
adjusted_work_area.add_padding(container_padding);
|
||||
|
||||
@@ -21,6 +21,7 @@ use color_eyre::Result;
|
||||
use fs_tail::TailedFile;
|
||||
use heck::ToKebabCase;
|
||||
use komorebi_core::resolve_home_path;
|
||||
use komorebi_core::EaseEnum;
|
||||
use lazy_static::lazy_static;
|
||||
use paste::paste;
|
||||
use sysinfo::SystemExt;
|
||||
@@ -627,6 +628,25 @@ struct ActiveWindowBorderOffset {
|
||||
offset: i32,
|
||||
}
|
||||
|
||||
#[derive(Parser, AhkFunction)]
|
||||
struct Animation {
|
||||
#[clap(value_enum)]
|
||||
boolean_state: BooleanState,
|
||||
}
|
||||
|
||||
#[derive(Parser, AhkFunction)]
|
||||
struct AnimationDuration {
|
||||
/// Desired animation durations in ms
|
||||
duration: u64,
|
||||
}
|
||||
|
||||
#[derive(Parser, AhkFunction)]
|
||||
struct AnimationEase {
|
||||
/// Desired ease function for animation
|
||||
#[clap(value_enum, short, long, default_value = "linear")]
|
||||
ease_func: EaseEnum,
|
||||
}
|
||||
|
||||
#[derive(Parser, AhkFunction)]
|
||||
#[allow(clippy::struct_excessive_bools)]
|
||||
struct Start {
|
||||
@@ -1059,6 +1079,15 @@ enum SubCommand {
|
||||
/// Set the offset for the active window border
|
||||
#[clap(arg_required_else_help = true)]
|
||||
ActiveWindowBorderOffset(ActiveWindowBorderOffset),
|
||||
/// Enable or disable the window move animation
|
||||
#[clap(arg_required_else_help = true)]
|
||||
Animation(Animation),
|
||||
/// Set the duration for the window move animation in ms
|
||||
#[clap(arg_required_else_help = true)]
|
||||
AnimationDuration(AnimationDuration),
|
||||
/// Set the ease function for the window move animation
|
||||
#[clap(arg_required_else_help = true)]
|
||||
AnimationEase(AnimationEase),
|
||||
/// Enable or disable focus follows mouse for the operating system
|
||||
#[clap(arg_required_else_help = true)]
|
||||
FocusFollowsMouse(FocusFollowsMouse),
|
||||
@@ -1986,6 +2015,15 @@ Stop-Process -Name:whkd -ErrorAction SilentlyContinue
|
||||
SubCommand::ActiveWindowBorderOffset(arg) => {
|
||||
send_message(&SocketMessage::ActiveWindowBorderOffset(arg.offset).as_bytes()?)?;
|
||||
}
|
||||
SubCommand::Animation(arg) => {
|
||||
send_message(&SocketMessage::Animation(arg.boolean_state.into()).as_bytes()?)?;
|
||||
}
|
||||
SubCommand::AnimationDuration(arg) => {
|
||||
send_message(&SocketMessage::AnimationDuration(arg.duration).as_bytes()?)?;
|
||||
}
|
||||
SubCommand::AnimationEase(arg) => {
|
||||
send_message(&SocketMessage::AnimationEase(arg.ease_func).as_bytes()?)?;
|
||||
}
|
||||
SubCommand::ResizeDelta(arg) => {
|
||||
send_message(&SocketMessage::ResizeDelta(arg.pixels).as_bytes()?)?;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user