diff --git a/frontend/src-tauri/Cargo.toml b/frontend/src-tauri/Cargo.toml index 5918f4b..ea39eb3 100644 --- a/frontend/src-tauri/Cargo.toml +++ b/frontend/src-tauri/Cargo.toml @@ -27,6 +27,7 @@ rand = "0.9.1" tokio = { version = "1.45.1", features = ["macros", "parking_lot", "rt-multi-thread", "time"] } zip = { version = "4.0.0", features = ["zstd"] } anyhow = "1.0.98" +futures = "0.3.31" [features] # this feature is used for production builds or when `devPath` points to the filesystem diff --git a/frontend/src-tauri/src/animator.rs b/frontend/src-tauri/src/animator.rs index eebf3da..0a193f5 100644 --- a/frontend/src-tauri/src/animator.rs +++ b/frontend/src-tauri/src/animator.rs @@ -1,11 +1,9 @@ use anyhow::Result; +use futures::FutureExt; use image::{ImageFormat, RgbaImage}; -use std::{env, fs, future::pending, sync::Arc, time::Duration}; +use std::{env, fs, sync::Arc, time::Duration}; use tokio::time::interval; -use tokio::{ - select, - time::{Instant, Interval}, -}; +use tokio::{select, time::Interval}; use zip::ZipArchive; use crate::{ @@ -16,27 +14,20 @@ use crate::{ #[derive(Debug, Default)] struct AnimationState { cursor_index: Option, - loaded_image: Option, - loaded_particles: Option>, -} - -async fn pend_optional_interval(t: &mut Option) -> Instant { - if let Some(t) = t { - t.tick().await - } else { - pending::().await - } + frame_timer: Option, + frame_index: usize, + loaded_image: Vec, + loaded_particles: Vec>, } #[tokio::main] pub async fn animate_loop(cursors: Arc>) -> Result<()> { let mut cmds = CURSOR_CMD.subscribe(); let mut selected: Option = None; - let start = Instant::now(); let mut animation_state = AnimationState::default(); - let mut animation_interval: Option = None; + let mut animation_interval = interval(Duration::from_secs_f32(1.0 / 30.0)); loop { select! { @@ -48,11 +39,6 @@ pub async fn animate_loop(cursors: Arc>) -> Result<()> { if i < cursors.len() { selected = Some(i); } - if cursors[i].animation_speed > 0.0 { - animation_interval = Some(interval(Duration::from_secs_f32(1.0/cursors[i].animation_speed))); - }else{ - animation_interval = None; - } } crate::CursorCmd::Next => { selected = Some(selected.map(|x| (x + 1) % cursors.len()).unwrap_or(0)) @@ -67,9 +53,7 @@ pub async fn animate_loop(cursors: Arc>) -> Result<()> { CURSOR_CHANGED.send(selected.unwrap_or(0)).ok(); } - _ = pend_optional_interval(&mut animation_interval) => { - println!("interval {:?}", start.elapsed()); - } + _ = animation_interval.tick() => {} } update_cursor(&mut animation_state, selected, &cursors).await?; @@ -94,6 +78,10 @@ fn load(path: &str) -> Option { ) } +fn load_frames(frames: &Vec) -> Vec { + frames.iter().map(|x| load(x)).flatten().collect() +} + async fn update_cursor( animation_state: &mut AnimationState, selected: Option, @@ -104,19 +92,38 @@ async fn update_cursor( animation_state.cursor_index = selected; if let Some(selected) = selected { - animation_state.loaded_image = load(&cursors[selected].path); + if cursors[selected].animation_speed > 0.0 { + animation_state.frame_timer = Some(interval(Duration::from_secs_f32( + 1.0 / cursors[selected].animation_speed, + ))) + } + animation_state.loaded_image = load_frames(&cursors[selected].frames); animation_state.loaded_particles = cursors[selected] .particles .iter() - .map(|x| load(&x.path)) + .map(|x| load_frames(&x.frames)) .collect(); - if let Some(i) = &animation_state.loaded_image { + if let Some(i) = animation_state.loaded_image.get(0) { set_system_cursor(i)?; } } else { restore_default_cursor()?; } + } else { + if let Some(frame_timer) = &mut animation_state.frame_timer { + if frame_timer.tick().now_or_never().is_some() && animation_state.loaded_image.len() > 0 + { + animation_state.frame_index += 1; + animation_state.frame_index %= animation_state.loaded_image.len(); + if let Some(i) = animation_state + .loaded_image + .get(animation_state.frame_index) + { + set_system_cursor(i)?; + } + } + } } Ok(()) diff --git a/frontend/src-tauri/src/cursor_def.json b/frontend/src-tauri/src/cursor_def.json index 5269f1f..9123907 100644 --- a/frontend/src-tauri/src/cursor_def.json +++ b/frontend/src-tauri/src/cursor_def.json @@ -1,14 +1,21 @@ [ { "name": "white", - "path": "icon.png", + + "frames": ["icon.png"], "animation_speed": 0.0, "particles": [] }, { "name": "red", - "path": "red.png", + "frames": ["red.png"], "animation_speed": 0.0, "particles": [] + }, + { + "name": "flashing", + "frames": ["red.png", "icon.png"], + "animation_speed": 30.0, + "particles": [] } ] \ No newline at end of file diff --git a/frontend/src-tauri/src/lib.rs b/frontend/src-tauri/src/lib.rs index 7b8ddab..303053f 100644 --- a/frontend/src-tauri/src/lib.rs +++ b/frontend/src-tauri/src/lib.rs @@ -47,7 +47,7 @@ impl RandomRange { #[derive(Debug, Clone, Serialize, Deserialize)] pub struct ParticleConfig { - path: String, + frames: Vec, animation_speed: f32, gravity_x: RandomRange, gravity_y: RandomRange, @@ -61,7 +61,7 @@ pub struct ParticleConfig { #[derive(Debug, Clone, Serialize, Deserialize)] pub struct Cursor { name: String, - path: String, + frames: Vec, animation_speed: f32, particles: Vec, }