diff --git a/src/animation.rs b/src/animation.rs index ea5d019..bc3298c 100644 --- a/src/animation.rs +++ b/src/animation.rs @@ -1,11 +1,13 @@ -use chrono::{DateTime, Utc}; use keyframe::{ease_with_scaled_time, functions::EaseInOut}; -use std::{borrow::Cow, collections::HashMap, time::Duration}; +use std::{ + collections::HashMap, + time::{Duration, Instant}, +}; #[derive(Default)] pub struct Animator { - animations: HashMap, - remove_later: Vec, + animations: HashMap, + remove_later: Vec, } #[derive(Debug)] @@ -14,15 +16,17 @@ struct AnimationInner { end: f64, current: f64, - start_time: DateTime, + start_time: Instant, duration: Duration, - t: AnimationType, } -#[derive(PartialEq, Eq, Debug, Copy, Clone)] -pub enum AnimationType { - Single, - Repeat, +#[derive(Eq, PartialEq, Debug, Hash, Clone)] +pub enum Animation { + Height, +} + +pub struct AnimationConfig { + pub height: Duration, } impl Animator { @@ -30,71 +34,37 @@ impl Animator { Self::default() } - pub fn add_animation( - &mut self, - name: Cow<'_, str>, - start: f64, - end: f64, - duration: Duration, - t: AnimationType, - ) { - match self.animations.entry(name.into_owned()) { + pub fn add_animation(&mut self, name: Animation, start: f64, end: f64, duration: Duration) { + match self.animations.entry(name) { std::collections::hash_map::Entry::Occupied(o) => - panic!("Add already existed animation with {}. It is a bug. Please submit a bug to https://github.com/l4l/yofi/issues", o.key()), + panic!("Add already existed animation with {:?}. It is a bug. Please submit a bug to https://github.com/l4l/yofi/issues", o), std::collections::hash_map::Entry::Vacant(v) => { - v.insert(AnimationInner::new(start, end, duration, t)); + v.insert(AnimationInner::new(start, end, duration)); } } } - pub fn cancel_animation(&mut self, name: &str) { - self.animations.remove(name); + pub fn cancel_animation(&mut self, name: Animation) { + self.animations.remove(&name); } - pub fn add_step_animation( - &mut self, - name: Cow<'_, str>, - start: f64, - end: f64, - step_duration: Duration, - t: AnimationType, - ) { - let full_duration = Duration::from_millis( - ((end - start) as u128 * step_duration.as_millis()) - .try_into() - .unwrap_or(u64::MAX), - ); - self.add_animation(name, start, end, full_duration, t); + pub fn contains(&self, name: Animation) -> bool { + self.animations.contains_key(&name) } - pub fn contains(&self, name: &str) -> bool { - self.animations.contains_key(name) - } - - pub fn get_value(&mut self, name: &str) -> Option { - Some(self.animations.get(name)?.current) + pub fn get_value(&mut self, name: Animation) -> Option { + Some(self.animations.get(&name)?.current) } pub fn proceed(&mut self) -> bool { - let current_time = Utc::now(); + let current_time = Instant::now(); for name in std::mem::take(&mut self.remove_later).into_iter() { - let animation = self.animations.remove(&name).unwrap(); - if animation.t == AnimationType::Repeat { - self.animations.insert( - name.clone(), - AnimationInner::new( - animation.start, - animation.end, - animation.duration, - animation.t, - ), - ); - } + self.animations.remove(&name).unwrap(); } for (name, animation) in self.animations.iter_mut() { - let time = (current_time - animation.start_time).num_milliseconds() as f64; + let time = (current_time - animation.start_time).as_millis() as f64; let mut start = animation.start; let mut end = animation.end; @@ -140,14 +110,13 @@ impl Animator { } impl AnimationInner { - fn new(start: f64, end: f64, duration: Duration, t: AnimationType) -> Self { + fn new(start: f64, end: f64, duration: Duration) -> Self { Self { start, end, current: start, - start_time: Utc::now(), + start_time: Instant::now(), duration, - t, } } } diff --git a/src/config.rs b/src/config.rs index 1d45e3e..a39159e 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,5 +1,6 @@ use std::ffi::CString; use std::path::PathBuf; +use std::time::Duration; use defaults::Defaults; use serde::Deserialize; @@ -32,8 +33,6 @@ pub struct Config { #[def = "false"] auto_height: bool, #[def = "false"] - animations: bool, - #[def = "false"] force_window: bool, window_offsets: Option<(i32, i32)>, scale: Option, @@ -48,6 +47,7 @@ pub struct Config { corner_radius: Radius, icon: Option, + animation: Option, input_text: InputText, list_items: ListItems, @@ -58,8 +58,8 @@ impl Config { self.icon = None; } - pub fn animations_enabled(&self) -> bool { - self.animations + pub fn animation(&self) -> &Option { + &self.animation } pub fn set_prompt(&mut self, prompt: String) { @@ -120,6 +120,13 @@ struct Icon { fallback_icon_path: Option, } +#[derive(Defaults, Deserialize)] +#[serde(default)] +pub struct Animation { + #[def = "500"] + height_duration_ms: u64, +} + fn config_path() -> PathBuf { xdg::BaseDirectories::with_prefix(crate::prog_name!()) .expect("failed to get xdg dirs") diff --git a/src/config/params.rs b/src/config/params.rs index 2e31f47..75d5ebc 100644 --- a/src/config/params.rs +++ b/src/config/params.rs @@ -6,6 +6,7 @@ use std::rc::Rc; use once_cell::unsync::Lazy; use super::*; +use crate::animation::AnimationConfig; use crate::desktop::IconConfig; use crate::draw::{BgParams, InputTextParams, ListParams}; use crate::font::{Font, FontBackend, InnerFont}; @@ -126,6 +127,14 @@ impl<'a> From<&'a Config> for Option { } } +impl<'a> From<&'a Config> for Option { + fn from(config: &'a Config) -> Option { + config.animation.as_ref().map(|c| AnimationConfig { + height: Duration::from_millis(c.height_duration_ms), + }) + } +} + fn default_font() -> Font { std::thread_local! { static DEFAULT_FONT: Lazy = Lazy::new(|| Rc::new(InnerFont::default())); diff --git a/src/draw.rs b/src/draw.rs index 7329d2e..a54b9d0 100644 --- a/src/draw.rs +++ b/src/draw.rs @@ -10,6 +10,8 @@ pub use list_view::{ListItem, Params as ListParams, ADDITIONAL_CAP}; use crate::{style::Radius, Color}; +pub use self::list_view::ListViewInfo; + pub type DrawTarget<'a> = raqote::DrawTarget<&'a mut [u32]>; mod background; @@ -34,11 +36,6 @@ pub enum Widget<'a, It = std::iter::Empty>> { Background(background::Background), } -pub struct ListViewInfo { - pub new_skip: usize, - pub new_y: u32, -} - impl<'a, It> Widget<'a, It> { pub fn input_text(text: &'a str, params: &'a InputTextParams<'a>) -> Self { Self::InputText(Box::new(input_text::InputText::new(text, params))) diff --git a/src/draw/list_view.rs b/src/draw/list_view.rs index cd36c98..3008eb9 100644 --- a/src/draw/list_view.rs +++ b/src/draw/list_view.rs @@ -7,7 +7,7 @@ use super::{DrawTarget, Drawable, Space}; use crate::font::{Font, FontBackend, FontColor}; use crate::state::ContinuousMatch; use crate::style::Margin; -use crate::{Color, ListViewInfo}; +use crate::Color; use unicode_segmentation::UnicodeSegmentation; pub const ADDITIONAL_CAP: u32 = 10; @@ -44,6 +44,11 @@ pub struct ListView<'a, It> { _tparam: PhantomData<&'a ()>, } +pub struct ListViewInfo { + pub new_skip: usize, + pub new_y: u32, +} + impl<'a, It> ListView<'a, It> { pub fn new( items: It, diff --git a/src/main.rs b/src/main.rs index a9f9056..d202ea3 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,6 +1,7 @@ +use std::collections::HashSet; use std::path::PathBuf; -use std::{collections::HashSet, time::Duration}; +use animation::AnimationConfig; use log::LevelFilter; use sctk::{ environment::SimpleGlobal, @@ -12,7 +13,7 @@ use sctk::{ }; use structopt::{clap::ArgGroup, StructOpt}; -pub use animation::Animator; +pub use animation::{Animation, Animator}; pub use color::Color; pub use desktop::Entry as DesktopEntry; pub use draw::{DrawTarget, ListViewInfo}; @@ -218,7 +219,7 @@ fn main_inner() { let background_config = config.param(); let input_config = config.param(); let list_config = config.param(); - let animations = config.animations_enabled(); + let animation_config = config.param(); if !env.get_shell().unwrap().needs_configure() { draw( @@ -228,7 +229,7 @@ fn main_inner() { &input_config, &list_config, &mut surface, - animations, + &animation_config, ); } @@ -239,8 +240,8 @@ fn main_inner() { loop { let mut should_redraw = false; for event in key_stream.try_iter() { - if animations { - animator.cancel_animation("HeightAnimation"); + if animation_config.is_some() { + animator.cancel_animation(Animation::Height); } should_redraw = true; @@ -256,7 +257,7 @@ fn main_inner() { surface::EventStatus::Idle => {} }; - if animations && animator.proceed() { + if animation_config.is_some() && animator.proceed() { should_redraw = true } @@ -268,13 +269,13 @@ fn main_inner() { &input_config, &list_config, &mut surface, - animations, + &animation_config, ); } display.flush().unwrap(); - let timeout = if animations { + let timeout = if animation_config.is_some() { animator.proceed_step() } else { None @@ -317,7 +318,7 @@ fn draw( input_config: &draw::InputTextParams, list_config: &draw::ListParams, surface: &mut surface::Surface, - animations: bool, + animation_config: &Option, ) { use std::iter::once; @@ -327,7 +328,7 @@ fn draw( let old_height = surface.get_height(); - match animator.get_value("HeightAnimation") { + match animator.get_value(Animation::Height) { Some(value) => surface.update_height(value as u32), None => surface.update_height(background_config.height), } @@ -357,11 +358,13 @@ fn draw( + list_config.margin.bottom as u32 + ADDITIONAL_CAP; - if animator.contains("HeightAnimation") { + if animator.contains(Animation::Height) { surface.commit(); return; } + let animations = animation_config.is_some(); + if full_height != old_height { if full_height > background_config.height { full_height = background_config.height; @@ -372,11 +375,10 @@ fn draw( // ListItem::show_default) if animations && state.input_changed() { animator.add_animation( - "HeightAnimation".into(), + Animation::Height, old_height as f64, full_height as f64, - Duration::from_millis(500), - animation::AnimationType::Single, + animation_config.as_ref().unwrap().height, ); } } diff --git a/src/surface.rs b/src/surface.rs index c7f0904..158aef1 100644 --- a/src/surface.rs +++ b/src/surface.rs @@ -260,8 +260,6 @@ impl Surface { space_left.height -= occupied.height; } } - - // Create a new buffer from the pool } pub fn commit(&mut self) {