mirror of
https://github.com/LGUG2Z/komorebi.git
synced 2026-03-21 08:59:11 +01:00
feat(animation): introduce RenderDispatcher trait
This commit is contained in:
@@ -8,23 +8,20 @@ use std::sync::atomic::Ordering;
|
||||
use std::time::Duration;
|
||||
use std::time::Instant;
|
||||
|
||||
use super::RenderDispatcher;
|
||||
use super::ANIMATION_DURATION;
|
||||
use super::ANIMATION_FPS;
|
||||
use super::ANIMATION_MANAGER;
|
||||
|
||||
#[derive(Debug, Default, Clone, Copy, Serialize, Deserialize, JsonSchema, PartialEq)]
|
||||
pub struct Animation {}
|
||||
pub struct Animation;
|
||||
|
||||
impl Animation {
|
||||
/// Returns true if the animation needs to continue
|
||||
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(animation_key);
|
||||
let max_duration = Duration::from_secs(1);
|
||||
let max_duration = Duration::from_secs(5);
|
||||
let spent_duration = Instant::now();
|
||||
|
||||
while ANIMATION_MANAGER.lock().in_progress(animation_key) {
|
||||
@@ -46,54 +43,67 @@ impl Animation {
|
||||
|
||||
#[allow(clippy::cast_precision_loss)]
|
||||
pub fn animate(
|
||||
animation_key: &str,
|
||||
animation_key: String,
|
||||
duration: Duration,
|
||||
mut render_callback: impl FnMut(f64) -> Result<()>,
|
||||
render_dispatcher: (impl RenderDispatcher + Send + 'static),
|
||||
) -> Result<()> {
|
||||
if ANIMATION_MANAGER.lock().in_progress(animation_key) {
|
||||
let should_animate = Self::cancel(animation_key);
|
||||
std::thread::spawn(move || {
|
||||
if ANIMATION_MANAGER.lock().in_progress(animation_key.as_str()) {
|
||||
let should_animate = Self::cancel(animation_key.as_str());
|
||||
|
||||
if !should_animate {
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
|
||||
ANIMATION_MANAGER.lock().start(animation_key);
|
||||
|
||||
let target_frame_time = Duration::from_millis(1000 / ANIMATION_FPS.load(Ordering::Relaxed));
|
||||
let mut progress = 0.0;
|
||||
let animation_start = Instant::now();
|
||||
|
||||
// start animation
|
||||
while progress < 1.0 {
|
||||
// check if animation is cancelled
|
||||
if ANIMATION_MANAGER.lock().is_cancelled(animation_key) {
|
||||
// cancel animation
|
||||
ANIMATION_MANAGER.lock().cancel(animation_key);
|
||||
return Ok(());
|
||||
if !should_animate {
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
|
||||
let frame_start = Instant::now();
|
||||
// calculate progress
|
||||
progress = animation_start.elapsed().as_millis() as f64 / duration.as_millis() as f64;
|
||||
render_callback(progress).ok();
|
||||
render_dispatcher.pre_render()?;
|
||||
|
||||
// sleep until next frame
|
||||
let frame_time_elapsed = frame_start.elapsed();
|
||||
ANIMATION_MANAGER.lock().start(animation_key.as_str());
|
||||
|
||||
if frame_time_elapsed < target_frame_time {
|
||||
std::thread::sleep(target_frame_time - frame_time_elapsed);
|
||||
let target_frame_time =
|
||||
Duration::from_millis(1000 / ANIMATION_FPS.load(Ordering::Relaxed));
|
||||
let mut progress = 0.0;
|
||||
let animation_start = Instant::now();
|
||||
|
||||
// start animation
|
||||
while progress < 1.0 {
|
||||
// check if animation is cancelled
|
||||
if ANIMATION_MANAGER
|
||||
.lock()
|
||||
.is_cancelled(animation_key.as_str())
|
||||
{
|
||||
// cancel animation
|
||||
ANIMATION_MANAGER.lock().cancel(animation_key.as_str());
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let frame_start = Instant::now();
|
||||
// calculate progress
|
||||
progress =
|
||||
animation_start.elapsed().as_millis() as f64 / duration.as_millis() as f64;
|
||||
render_dispatcher.render(progress).ok();
|
||||
|
||||
// sleep until next frame
|
||||
let frame_time_elapsed = frame_start.elapsed();
|
||||
|
||||
if frame_time_elapsed < target_frame_time {
|
||||
std::thread::sleep(target_frame_time - frame_time_elapsed);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ANIMATION_MANAGER.lock().end(animation_key);
|
||||
ANIMATION_MANAGER.lock().end(animation_key.as_str());
|
||||
|
||||
// limit progress to 1.0 if animation took longer
|
||||
if progress > 1.0 {
|
||||
progress = 1.0;
|
||||
}
|
||||
// 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
|
||||
render_callback(progress)
|
||||
// process animation for 1.0 to set target position
|
||||
render_dispatcher.render(progress).ok();
|
||||
}
|
||||
|
||||
render_dispatcher.post_render()
|
||||
});
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
use std::collections::hash_map::Entry;
|
||||
use std::collections::HashMap;
|
||||
|
||||
use super::prefix::AnimationPrefix;
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
struct AnimationState {
|
||||
pub in_progress: bool,
|
||||
@@ -100,10 +102,10 @@ impl AnimationManager {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn animations_in_progress(&self, animation_key_prefix: &str) -> usize {
|
||||
pub fn count_in_progress(&self, animation_key_prefix: AnimationPrefix) -> usize {
|
||||
self.animations
|
||||
.keys()
|
||||
.filter(|key| key.starts_with(animation_key_prefix))
|
||||
.filter(|key| key.starts_with(animation_key_prefix.to_string().as_str()))
|
||||
.count()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,6 +13,8 @@ pub use animation::Animation;
|
||||
pub mod animation_manager;
|
||||
pub mod lerp;
|
||||
pub mod prefix;
|
||||
pub mod render_dispatcher;
|
||||
pub use render_dispatcher::RenderDispatcher;
|
||||
pub mod style;
|
||||
|
||||
lazy_static! {
|
||||
|
||||
@@ -13,7 +13,5 @@ pub enum AnimationPrefix {
|
||||
}
|
||||
|
||||
pub fn new_animation_key(prefix: AnimationPrefix, key: String) -> String {
|
||||
match prefix {
|
||||
AnimationPrefix::WindowMove => format!("window_move:{}", key),
|
||||
}
|
||||
format!("{}:{}", prefix, key)
|
||||
}
|
||||
|
||||
7
komorebi/src/animation/render_dispatcher.rs
Normal file
7
komorebi/src/animation/render_dispatcher.rs
Normal file
@@ -0,0 +1,7 @@
|
||||
use color_eyre::Result;
|
||||
|
||||
pub trait RenderDispatcher {
|
||||
fn pre_render(&self) -> Result<()>;
|
||||
fn render(&self, delta: f64) -> Result<()>;
|
||||
fn post_render(&self) -> Result<()>;
|
||||
}
|
||||
@@ -1,6 +1,8 @@
|
||||
use crate::animation::lerp::Lerp;
|
||||
use crate::animation::prefix::new_animation_key;
|
||||
use crate::animation::prefix::AnimationPrefix;
|
||||
use crate::animation::RenderDispatcher;
|
||||
// use crate::animation::renderer::Renderer;
|
||||
use crate::animation::ANIMATION_DURATION;
|
||||
use crate::animation::ANIMATION_ENABLED;
|
||||
use crate::animation::ANIMATION_MANAGER;
|
||||
@@ -10,6 +12,7 @@ use crate::com::SetCloak;
|
||||
use crate::focus_manager;
|
||||
use crate::stackbar_manager;
|
||||
use crate::windows_api;
|
||||
use crate::AnimationStyle;
|
||||
use crate::SLOW_APPLICATION_COMPENSATION_TIME;
|
||||
use crate::SLOW_APPLICATION_IDENTIFIERS;
|
||||
use std::collections::HashMap;
|
||||
@@ -153,6 +156,76 @@ impl Serialize for Window {
|
||||
}
|
||||
}
|
||||
|
||||
struct WindowMoveRenderDispatcher {
|
||||
prefix: AnimationPrefix,
|
||||
hwnd: isize,
|
||||
start_rect: Rect,
|
||||
target_rect: Rect,
|
||||
top: bool,
|
||||
style: AnimationStyle,
|
||||
}
|
||||
|
||||
impl WindowMoveRenderDispatcher {
|
||||
pub fn new(
|
||||
prefix: AnimationPrefix,
|
||||
hwnd: isize,
|
||||
start_rect: Rect,
|
||||
target_rect: Rect,
|
||||
top: bool,
|
||||
style: AnimationStyle,
|
||||
) -> Self {
|
||||
Self {
|
||||
prefix,
|
||||
hwnd,
|
||||
start_rect,
|
||||
target_rect,
|
||||
top,
|
||||
style,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl RenderDispatcher for WindowMoveRenderDispatcher {
|
||||
fn pre_render(&self) -> Result<()> {
|
||||
border_manager::BORDER_TEMPORARILY_DISABLED.store(true, Ordering::SeqCst);
|
||||
border_manager::send_notification(Some(self.hwnd));
|
||||
|
||||
stackbar_manager::STACKBAR_TEMPORARILY_DISABLED.store(true, Ordering::SeqCst);
|
||||
stackbar_manager::send_notification();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn render(&self, progress: f64) -> Result<()> {
|
||||
let new_rect = self.start_rect.lerp(self.target_rect, progress, self.style);
|
||||
|
||||
// using MoveWindow because it runs faster than SetWindowPos
|
||||
// so animation have more fps and feel smoother
|
||||
WindowsApi::move_window(self.hwnd, &new_rect, false)?;
|
||||
WindowsApi::invalidate_rect(self.hwnd, None, false);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn post_render(&self) -> Result<()> {
|
||||
WindowsApi::position_window(self.hwnd, &self.target_rect, self.top)?;
|
||||
if ANIMATION_MANAGER.lock().count_in_progress(self.prefix) == 0 {
|
||||
if WindowsApi::foreground_window().unwrap_or_default() == self.hwnd {
|
||||
focus_manager::send_notification(self.hwnd)
|
||||
}
|
||||
|
||||
border_manager::BORDER_TEMPORARILY_DISABLED.store(false, Ordering::SeqCst);
|
||||
stackbar_manager::STACKBAR_TEMPORARILY_DISABLED.store(false, Ordering::SeqCst);
|
||||
|
||||
border_manager::send_notification(Some(self.hwnd));
|
||||
stackbar_manager::send_notification();
|
||||
transparency_manager::send_notification();
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Window {
|
||||
pub const fn hwnd(self) -> HWND {
|
||||
HWND(windows_api::as_ptr!(self.hwnd))
|
||||
@@ -198,62 +271,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 duration = Duration::from_millis(ANIMATION_DURATION.load(Ordering::SeqCst));
|
||||
let style = *ANIMATION_STYLE.lock();
|
||||
|
||||
border_manager::BORDER_TEMPORARILY_DISABLED.store(true, Ordering::SeqCst);
|
||||
border_manager::send_notification(Some(self.hwnd));
|
||||
|
||||
stackbar_manager::STACKBAR_TEMPORARILY_DISABLED.store(true, Ordering::SeqCst);
|
||||
stackbar_manager::send_notification();
|
||||
|
||||
let hwnd = self.hwnd;
|
||||
|
||||
std::thread::spawn(move || {
|
||||
Animation::animate(
|
||||
new_animation_key(AnimationPrefix::WindowMove, hwnd.to_string()).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 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);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
},
|
||||
)
|
||||
});
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn set_position(&self, layout: &Rect, top: bool) -> Result<()> {
|
||||
let window_rect = WindowsApi::window_rect(self.hwnd)?;
|
||||
|
||||
@@ -262,7 +279,23 @@ impl Window {
|
||||
}
|
||||
|
||||
if ANIMATION_ENABLED.load(Ordering::SeqCst) {
|
||||
self.animate_position(&window_rect, layout, top)
|
||||
let duration = Duration::from_millis(ANIMATION_DURATION.load(Ordering::SeqCst));
|
||||
let style = *ANIMATION_STYLE.lock();
|
||||
|
||||
let render_dispatcher = WindowMoveRenderDispatcher::new(
|
||||
AnimationPrefix::WindowMove,
|
||||
self.hwnd,
|
||||
window_rect,
|
||||
*layout,
|
||||
top,
|
||||
style,
|
||||
);
|
||||
|
||||
Animation::animate(
|
||||
new_animation_key(AnimationPrefix::WindowMove, self.hwnd.to_string()),
|
||||
duration,
|
||||
render_dispatcher,
|
||||
)
|
||||
} else {
|
||||
WindowsApi::position_window(self.hwnd, layout, top)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user