refactor(animation): generalized ANIMATION_MANAGER

instead of isize key for ANIMATION_MANAGER hashmap, now we use string
key. For window move animation key wouldbe `window_move:{hwnd}`.
That allows to use single manager for more types of animations.
This commit is contained in:
thearturca
2024-10-14 20:47:30 +03:00
parent 7b08afa891
commit e449861c10
4 changed files with 76 additions and 79 deletions
+15 -21
View File
@@ -13,29 +13,23 @@ use super::ANIMATION_FPS;
use super::ANIMATION_MANAGER; use super::ANIMATION_MANAGER;
#[derive(Debug, Default, Clone, Copy, Serialize, Deserialize, JsonSchema, PartialEq)] #[derive(Debug, Default, Clone, Copy, Serialize, Deserialize, JsonSchema, PartialEq)]
pub struct Animation { pub struct Animation {}
pub hwnd: isize,
}
impl Animation { impl Animation {
pub fn new(hwnd: isize) -> Self {
Self { hwnd }
}
/// Returns true if the animation needs to continue /// Returns true if the animation needs to continue
pub fn cancel(&mut self) -> bool { pub fn cancel(animation_key: &str) -> bool {
if !ANIMATION_MANAGER.lock().in_progress(self.hwnd) { if !ANIMATION_MANAGER.lock().in_progress(animation_key) {
return true; return true;
} }
// should be more than 0 // should be more than 0
let cancel_idx = ANIMATION_MANAGER.lock().init_cancel(self.hwnd); let cancel_idx = ANIMATION_MANAGER.lock().init_cancel(animation_key);
let max_duration = Duration::from_secs(1); let max_duration = Duration::from_secs(1);
let spent_duration = Instant::now(); let spent_duration = Instant::now();
while ANIMATION_MANAGER.lock().in_progress(self.hwnd) { while ANIMATION_MANAGER.lock().in_progress(animation_key) {
if spent_duration.elapsed() >= max_duration { if spent_duration.elapsed() >= max_duration {
ANIMATION_MANAGER.lock().end(self.hwnd); ANIMATION_MANAGER.lock().end(animation_key);
} }
std::thread::sleep(Duration::from_millis( std::thread::sleep(Duration::from_millis(
@@ -43,28 +37,28 @@ impl Animation {
)); ));
} }
let latest_cancel_idx = ANIMATION_MANAGER.lock().latest_cancel_idx(self.hwnd); let latest_cancel_idx = ANIMATION_MANAGER.lock().latest_cancel_idx(animation_key);
ANIMATION_MANAGER.lock().end_cancel(self.hwnd); ANIMATION_MANAGER.lock().end_cancel(animation_key);
latest_cancel_idx == cancel_idx latest_cancel_idx == cancel_idx
} }
#[allow(clippy::cast_precision_loss)] #[allow(clippy::cast_precision_loss)]
pub fn animate( pub fn animate(
&mut self, animation_key: &str,
duration: Duration, duration: Duration,
mut render_callback: impl FnMut(f64) -> Result<()>, mut render_callback: impl FnMut(f64) -> Result<()>,
) -> Result<()> { ) -> Result<()> {
if ANIMATION_MANAGER.lock().in_progress(self.hwnd) { if ANIMATION_MANAGER.lock().in_progress(animation_key) {
let should_animate = self.cancel(); let should_animate = Self::cancel(animation_key);
if !should_animate { if !should_animate {
return Ok(()); return Ok(());
} }
} }
ANIMATION_MANAGER.lock().start(self.hwnd); ANIMATION_MANAGER.lock().start(animation_key);
let target_frame_time = Duration::from_millis(1000 / ANIMATION_FPS.load(Ordering::Relaxed)); let target_frame_time = Duration::from_millis(1000 / ANIMATION_FPS.load(Ordering::Relaxed));
let mut progress = 0.0; let mut progress = 0.0;
@@ -73,9 +67,9 @@ impl Animation {
// start animation // start animation
while progress < 1.0 { while progress < 1.0 {
// check if animation is cancelled // check if animation is cancelled
if ANIMATION_MANAGER.lock().is_cancelled(self.hwnd) { if ANIMATION_MANAGER.lock().is_cancelled(animation_key) {
// cancel animation // cancel animation
ANIMATION_MANAGER.lock().cancel(self.hwnd); ANIMATION_MANAGER.lock().cancel(animation_key);
return Ok(()); return Ok(());
} }
@@ -92,7 +86,7 @@ impl Animation {
} }
} }
ANIMATION_MANAGER.lock().end(self.hwnd); ANIMATION_MANAGER.lock().end(animation_key);
// limit progress to 1.0 if animation took longer // limit progress to 1.0 if animation took longer
if progress > 1.0 { if progress > 1.0 {
+26 -23
View File
@@ -1,7 +1,5 @@
use super::ANIMATIONS_IN_PROGRESS;
use std::collections::hash_map::Entry; use std::collections::hash_map::Entry;
use std::collections::HashMap; use std::collections::HashMap;
use std::sync::atomic::Ordering;
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy)]
struct AnimationState { struct AnimationState {
@@ -12,7 +10,7 @@ struct AnimationState {
#[derive(Debug)] #[derive(Debug)]
pub struct AnimationManager { pub struct AnimationManager {
animations: HashMap<isize, AnimationState>, animations: HashMap<String, AnimationState>,
} }
impl Default for AnimationManager { impl Default for AnimationManager {
@@ -28,24 +26,24 @@ impl AnimationManager {
} }
} }
pub fn is_cancelled(&self, hwnd: isize) -> bool { pub fn is_cancelled(&self, animation_key: &str) -> bool {
if let Some(animation_state) = self.animations.get(&hwnd) { if let Some(animation_state) = self.animations.get(animation_key) {
animation_state.pending_cancel_count > 0 animation_state.pending_cancel_count > 0
} else { } else {
false false
} }
} }
pub fn in_progress(&self, hwnd: isize) -> bool { pub fn in_progress(&self, animation_key: &str) -> bool {
if let Some(animation_state) = self.animations.get(&hwnd) { if let Some(animation_state) = self.animations.get(animation_key) {
animation_state.in_progress animation_state.in_progress
} else { } else {
false false
} }
} }
pub fn init_cancel(&mut self, hwnd: isize) -> usize { pub fn init_cancel(&mut self, animation_key: &str) -> usize {
if let Some(animation_state) = self.animations.get_mut(&hwnd) { if let Some(animation_state) = self.animations.get_mut(animation_key) {
animation_state.pending_cancel_count += 1; animation_state.pending_cancel_count += 1;
animation_state.cancel_idx_counter += 1; animation_state.cancel_idx_counter += 1;
@@ -56,51 +54,56 @@ impl AnimationManager {
} }
} }
pub fn latest_cancel_idx(&mut self, hwnd: isize) -> usize { pub fn latest_cancel_idx(&mut self, animation_key: &str) -> usize {
if let Some(animation_state) = self.animations.get_mut(&hwnd) { if let Some(animation_state) = self.animations.get_mut(animation_key) {
animation_state.cancel_idx_counter animation_state.cancel_idx_counter
} else { } else {
0 0
} }
} }
pub fn end_cancel(&mut self, hwnd: isize) { pub fn end_cancel(&mut self, animation_key: &str) {
if let Some(animation_state) = self.animations.get_mut(&hwnd) { if let Some(animation_state) = self.animations.get_mut(animation_key) {
animation_state.pending_cancel_count -= 1; animation_state.pending_cancel_count -= 1;
} }
} }
pub fn cancel(&mut self, hwnd: isize) { pub fn cancel(&mut self, animation_key: &str) {
if let Some(animation_state) = self.animations.get_mut(&hwnd) { if let Some(animation_state) = self.animations.get_mut(animation_key) {
animation_state.in_progress = false; animation_state.in_progress = false;
} }
} }
pub fn start(&mut self, hwnd: isize) { pub fn start(&mut self, animation_key: &str) {
if let Entry::Vacant(e) = self.animations.entry(hwnd) { if let Entry::Vacant(e) = self.animations.entry(animation_key.to_string()) {
e.insert(AnimationState { e.insert(AnimationState {
in_progress: true, in_progress: true,
cancel_idx_counter: 0, cancel_idx_counter: 0,
pending_cancel_count: 0, pending_cancel_count: 0,
}); });
ANIMATIONS_IN_PROGRESS.store(self.animations.len(), Ordering::Release);
return; return;
} }
if let Some(animation_state) = self.animations.get_mut(&hwnd) { if let Some(animation_state) = self.animations.get_mut(animation_key) {
animation_state.in_progress = true; animation_state.in_progress = true;
} }
} }
pub fn end(&mut self, hwnd: isize) { pub fn end(&mut self, animation_key: &str) {
if let Some(animation_state) = self.animations.get_mut(&hwnd) { if let Some(animation_state) = self.animations.get_mut(animation_key) {
animation_state.in_progress = false; animation_state.in_progress = false;
if animation_state.pending_cancel_count == 0 { if animation_state.pending_cancel_count == 0 {
self.animations.remove(&hwnd); self.animations.remove(animation_key);
ANIMATIONS_IN_PROGRESS.store(self.animations.len(), Ordering::Release);
} }
} }
} }
pub fn animations_in_progress(&self, animation_key_prefix: &str) -> usize {
self.animations
.keys()
.filter(|key| key.starts_with(animation_key_prefix))
.count()
}
} }
-2
View File
@@ -4,7 +4,6 @@ use crate::core::animation::AnimationStyle;
use lazy_static::lazy_static; use lazy_static::lazy_static;
use std::sync::atomic::AtomicBool; use std::sync::atomic::AtomicBool;
use std::sync::atomic::AtomicU64; use std::sync::atomic::AtomicU64;
use std::sync::atomic::AtomicUsize;
use std::sync::Arc; use std::sync::Arc;
use parking_lot::Mutex; use parking_lot::Mutex;
@@ -25,4 +24,3 @@ lazy_static! {
pub static ANIMATION_ENABLED: AtomicBool = AtomicBool::new(false); pub static ANIMATION_ENABLED: AtomicBool = AtomicBool::new(false);
pub static ANIMATION_DURATION: AtomicU64 = AtomicU64::new(250); pub static ANIMATION_DURATION: AtomicU64 = AtomicU64::new(250);
pub static ANIMATION_FPS: AtomicU64 = AtomicU64::new(60); pub static ANIMATION_FPS: AtomicU64 = AtomicU64::new(60);
pub static ANIMATIONS_IN_PROGRESS: AtomicUsize = AtomicUsize::new(0);
+35 -33
View File
@@ -1,7 +1,7 @@
use crate::animation::lerp::Lerp; use crate::animation::lerp::Lerp;
use crate::animation::ANIMATIONS_IN_PROGRESS;
use crate::animation::ANIMATION_DURATION; use crate::animation::ANIMATION_DURATION;
use crate::animation::ANIMATION_ENABLED; use crate::animation::ANIMATION_ENABLED;
use crate::animation::ANIMATION_MANAGER;
use crate::animation::ANIMATION_STYLE; use crate::animation::ANIMATION_STYLE;
use crate::border_manager; use crate::border_manager;
use crate::com::SetCloak; use crate::com::SetCloak;
@@ -60,16 +60,11 @@ pub static MINIMUM_HEIGHT: AtomicI32 = AtomicI32::new(0);
#[derive(Debug, Default, Clone, Copy, Deserialize, JsonSchema, PartialEq)] #[derive(Debug, Default, Clone, Copy, Deserialize, JsonSchema, PartialEq)]
pub struct Window { pub struct Window {
pub hwnd: isize, pub hwnd: isize,
#[serde(skip)]
animation: Animation,
} }
impl From<isize> for Window { impl From<isize> for Window {
fn from(value: isize) -> Self { fn from(value: isize) -> Self {
Self { Self { hwnd: value }
hwnd: value,
animation: Animation::new(value),
}
} }
} }
@@ -77,7 +72,6 @@ impl From<HWND> for Window {
fn from(value: HWND) -> Self { fn from(value: HWND) -> Self {
Self { Self {
hwnd: value.0 as isize, hwnd: value.0 as isize,
animation: Animation::new(value.0 as isize),
} }
} }
} }
@@ -205,7 +199,6 @@ impl Window {
pub fn animate_position(&self, start_rect: &Rect, target_rect: &Rect, top: bool) -> Result<()> { pub fn animate_position(&self, start_rect: &Rect, target_rect: &Rect, top: bool) -> Result<()> {
let start_rect = *start_rect; let start_rect = *start_rect;
let target_rect = *target_rect; let target_rect = *target_rect;
let mut animation = self.animation;
let duration = Duration::from_millis(ANIMATION_DURATION.load(Ordering::SeqCst)); let duration = Duration::from_millis(ANIMATION_DURATION.load(Ordering::SeqCst));
let style = *ANIMATION_STYLE.lock(); let style = *ANIMATION_STYLE.lock();
@@ -218,33 +211,42 @@ impl Window {
let hwnd = self.hwnd; let hwnd = self.hwnd;
std::thread::spawn(move || { std::thread::spawn(move || {
animation.animate(duration, |progress: f64| { Animation::animate(
let new_rect = start_rect.lerp(target_rect, progress, style); format!("window_move:{}", hwnd).as_str(),
duration,
|progress: f64| {
let new_rect = start_rect.lerp(target_rect, progress, style);
if progress == 1.0 { if progress == 1.0 {
WindowsApi::position_window(hwnd, &new_rect, top)?; WindowsApi::position_window(hwnd, &new_rect, top)?;
if WindowsApi::foreground_window().unwrap_or_default() == hwnd { if WindowsApi::foreground_window().unwrap_or_default() == hwnd {
focus_manager::send_notification(hwnd) focus_manager::send_notification(hwnd)
}
if ANIMATION_MANAGER
.lock()
.animations_in_progress("window_move")
== 0
{
border_manager::BORDER_TEMPORARILY_DISABLED
.store(false, Ordering::SeqCst);
stackbar_manager::STACKBAR_TEMPORARILY_DISABLED
.store(false, Ordering::SeqCst);
border_manager::send_notification(Some(hwnd));
stackbar_manager::send_notification();
transparency_manager::send_notification();
}
} else {
// using MoveWindow because it runs faster than SetWindowPos
// so animation have more fps and feel smoother
WindowsApi::move_window(hwnd, &new_rect, false)?;
WindowsApi::invalidate_rect(hwnd, None, false);
} }
if ANIMATIONS_IN_PROGRESS.load(Ordering::Acquire) == 0 { Ok(())
border_manager::BORDER_TEMPORARILY_DISABLED.store(false, Ordering::SeqCst); },
stackbar_manager::STACKBAR_TEMPORARILY_DISABLED )
.store(false, Ordering::SeqCst);
border_manager::send_notification(Some(hwnd));
stackbar_manager::send_notification();
transparency_manager::send_notification();
}
} else {
// using MoveWindow because it runs faster than SetWindowPos
// so animation have more fps and feel smoother
WindowsApi::move_window(hwnd, &new_rect, false)?;
WindowsApi::invalidate_rect(hwnd, None, false);
}
Ok(())
})
}); });
Ok(()) Ok(())