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

View File

@@ -13,29 +13,23 @@ use super::ANIMATION_FPS;
use super::ANIMATION_MANAGER;
#[derive(Debug, Default, Clone, Copy, Serialize, Deserialize, JsonSchema, PartialEq)]
pub struct Animation {
pub hwnd: isize,
}
pub struct Animation {}
impl Animation {
pub fn new(hwnd: isize) -> Self {
Self { hwnd }
}
/// Returns true if the animation needs to continue
pub fn cancel(&mut self) -> bool {
if !ANIMATION_MANAGER.lock().in_progress(self.hwnd) {
pub fn cancel(animation_key: &str) -> bool {
if !ANIMATION_MANAGER.lock().in_progress(animation_key) {
return true;
}
// 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 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 {
ANIMATION_MANAGER.lock().end(self.hwnd);
ANIMATION_MANAGER.lock().end(animation_key);
}
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
}
#[allow(clippy::cast_precision_loss)]
pub fn animate(
&mut self,
animation_key: &str,
duration: Duration,
mut render_callback: impl FnMut(f64) -> Result<()>,
) -> Result<()> {
if ANIMATION_MANAGER.lock().in_progress(self.hwnd) {
let should_animate = self.cancel();
if ANIMATION_MANAGER.lock().in_progress(animation_key) {
let should_animate = Self::cancel(animation_key);
if !should_animate {
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 mut progress = 0.0;
@@ -73,9 +67,9 @@ impl Animation {
// start animation
while progress < 1.0 {
// check if animation is cancelled
if ANIMATION_MANAGER.lock().is_cancelled(self.hwnd) {
if ANIMATION_MANAGER.lock().is_cancelled(animation_key) {
// cancel animation
ANIMATION_MANAGER.lock().cancel(self.hwnd);
ANIMATION_MANAGER.lock().cancel(animation_key);
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
if progress > 1.0 {

View File

@@ -1,7 +1,5 @@
use super::ANIMATIONS_IN_PROGRESS;
use std::collections::hash_map::Entry;
use std::collections::HashMap;
use std::sync::atomic::Ordering;
#[derive(Debug, Clone, Copy)]
struct AnimationState {
@@ -12,7 +10,7 @@ struct AnimationState {
#[derive(Debug)]
pub struct AnimationManager {
animations: HashMap<isize, AnimationState>,
animations: HashMap<String, AnimationState>,
}
impl Default for AnimationManager {
@@ -28,24 +26,24 @@ impl AnimationManager {
}
}
pub fn is_cancelled(&self, hwnd: isize) -> bool {
if let Some(animation_state) = self.animations.get(&hwnd) {
pub fn is_cancelled(&self, animation_key: &str) -> bool {
if let Some(animation_state) = self.animations.get(animation_key) {
animation_state.pending_cancel_count > 0
} else {
false
}
}
pub fn in_progress(&self, hwnd: isize) -> bool {
if let Some(animation_state) = self.animations.get(&hwnd) {
pub fn in_progress(&self, animation_key: &str) -> bool {
if let Some(animation_state) = self.animations.get(animation_key) {
animation_state.in_progress
} else {
false
}
}
pub fn init_cancel(&mut self, hwnd: isize) -> usize {
if let Some(animation_state) = self.animations.get_mut(&hwnd) {
pub fn init_cancel(&mut self, animation_key: &str) -> usize {
if let Some(animation_state) = self.animations.get_mut(animation_key) {
animation_state.pending_cancel_count += 1;
animation_state.cancel_idx_counter += 1;
@@ -56,51 +54,56 @@ impl AnimationManager {
}
}
pub fn latest_cancel_idx(&mut self, hwnd: isize) -> usize {
if let Some(animation_state) = self.animations.get_mut(&hwnd) {
pub fn latest_cancel_idx(&mut self, animation_key: &str) -> usize {
if let Some(animation_state) = self.animations.get_mut(animation_key) {
animation_state.cancel_idx_counter
} else {
0
}
}
pub fn end_cancel(&mut self, hwnd: isize) {
if let Some(animation_state) = self.animations.get_mut(&hwnd) {
pub fn end_cancel(&mut self, animation_key: &str) {
if let Some(animation_state) = self.animations.get_mut(animation_key) {
animation_state.pending_cancel_count -= 1;
}
}
pub fn cancel(&mut self, hwnd: isize) {
if let Some(animation_state) = self.animations.get_mut(&hwnd) {
pub fn cancel(&mut self, animation_key: &str) {
if let Some(animation_state) = self.animations.get_mut(animation_key) {
animation_state.in_progress = false;
}
}
pub fn start(&mut self, hwnd: isize) {
if let Entry::Vacant(e) = self.animations.entry(hwnd) {
pub fn start(&mut self, animation_key: &str) {
if let Entry::Vacant(e) = self.animations.entry(animation_key.to_string()) {
e.insert(AnimationState {
in_progress: true,
cancel_idx_counter: 0,
pending_cancel_count: 0,
});
ANIMATIONS_IN_PROGRESS.store(self.animations.len(), Ordering::Release);
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;
}
}
pub fn end(&mut self, hwnd: isize) {
if let Some(animation_state) = self.animations.get_mut(&hwnd) {
pub fn end(&mut self, animation_key: &str) {
if let Some(animation_state) = self.animations.get_mut(animation_key) {
animation_state.in_progress = false;
if animation_state.pending_cancel_count == 0 {
self.animations.remove(&hwnd);
ANIMATIONS_IN_PROGRESS.store(self.animations.len(), Ordering::Release);
self.animations.remove(animation_key);
}
}
}
pub fn animations_in_progress(&self, animation_key_prefix: &str) -> usize {
self.animations
.keys()
.filter(|key| key.starts_with(animation_key_prefix))
.count()
}
}

View File

@@ -4,7 +4,6 @@ use crate::core::animation::AnimationStyle;
use lazy_static::lazy_static;
use std::sync::atomic::AtomicBool;
use std::sync::atomic::AtomicU64;
use std::sync::atomic::AtomicUsize;
use std::sync::Arc;
use parking_lot::Mutex;
@@ -25,4 +24,3 @@ lazy_static! {
pub static ANIMATION_ENABLED: AtomicBool = AtomicBool::new(false);
pub static ANIMATION_DURATION: AtomicU64 = AtomicU64::new(250);
pub static ANIMATION_FPS: AtomicU64 = AtomicU64::new(60);
pub static ANIMATIONS_IN_PROGRESS: AtomicUsize = AtomicUsize::new(0);

View File

@@ -1,7 +1,7 @@
use crate::animation::lerp::Lerp;
use crate::animation::ANIMATIONS_IN_PROGRESS;
use crate::animation::ANIMATION_DURATION;
use crate::animation::ANIMATION_ENABLED;
use crate::animation::ANIMATION_MANAGER;
use crate::animation::ANIMATION_STYLE;
use crate::border_manager;
use crate::com::SetCloak;
@@ -60,16 +60,11 @@ pub static MINIMUM_HEIGHT: AtomicI32 = AtomicI32::new(0);
#[derive(Debug, Default, Clone, Copy, Deserialize, JsonSchema, PartialEq)]
pub struct Window {
pub hwnd: isize,
#[serde(skip)]
animation: Animation,
}
impl From<isize> for Window {
fn from(value: isize) -> Self {
Self {
hwnd: value,
animation: Animation::new(value),
}
Self { hwnd: value }
}
}
@@ -77,7 +72,6 @@ impl From<HWND> for Window {
fn from(value: HWND) -> Self {
Self {
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<()> {
let start_rect = *start_rect;
let target_rect = *target_rect;
let mut animation = self.animation;
let duration = Duration::from_millis(ANIMATION_DURATION.load(Ordering::SeqCst));
let style = *ANIMATION_STYLE.lock();
@@ -218,33 +211,42 @@ impl Window {
let hwnd = self.hwnd;
std::thread::spawn(move || {
animation.animate(duration, |progress: f64| {
let new_rect = start_rect.lerp(target_rect, progress, style);
Animation::animate(
format!("window_move:{}", hwnd).as_str(),
duration,
|progress: f64| {
let new_rect = start_rect.lerp(target_rect, progress, style);
if progress == 1.0 {
WindowsApi::position_window(hwnd, &new_rect, top)?;
if WindowsApi::foreground_window().unwrap_or_default() == hwnd {
focus_manager::send_notification(hwnd)
if progress == 1.0 {
WindowsApi::position_window(hwnd, &new_rect, top)?;
if WindowsApi::foreground_window().unwrap_or_default() == 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 {
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(())