mirror of
				https://github.com/Dummi26/musicdb.git
				synced 2025-10-30 03:25:26 +01:00 
			
		
		
		
	feat: use better animations (these now work on low fps too)
This commit is contained in:
		
							parent
							
								
									c23d487a02
								
							
						
					
					
						commit
						ee7c74f3a8
					
				| @ -1,130 +1,42 @@ | |||||||
| use std::{ | use std::time::Instant; | ||||||
|     ops::{Add, AddAssign, Mul, MulAssign, Sub}, |  | ||||||
|     time::{Duration, Instant}, |  | ||||||
| }; |  | ||||||
| 
 | 
 | ||||||
| pub struct AnimationController<F> { | use uianimator::{default_animator_f64_quadratic::DefaultAnimatorF64Quadratic, Animator}; | ||||||
|     pub last_updated: Instant, | 
 | ||||||
|     pub value: F, | pub struct AnimationController { | ||||||
|     pub speed: F, |     speed: f64, | ||||||
|     pub max_speed: F, |     anim: DefaultAnimatorF64Quadratic, | ||||||
|     pub target: F, |  | ||||||
|     /// while the time remaining to finish the animation is above this, we accelerate (higher -> stop acceleration earlier)
 |  | ||||||
|     pub accel_until: F, |  | ||||||
|     /// if the time remaining to finish the animation drops below this, we decelerate (higher -> start decelerating earlier)
 |  | ||||||
|     pub decel_while: F, |  | ||||||
|     pub acceleration: F, |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| pub trait Float: | impl AnimationController { | ||||||
|     Sized |     pub fn new(value: f64, target: f64, speed: f64) -> Self { | ||||||
|     + Clone |         let mut anim = DefaultAnimatorF64Quadratic::new(value, speed); | ||||||
|     + Copy |         if value != target { | ||||||
|     + Add<Self, Output = Self> |             anim.set_target(target, Instant::now()); | ||||||
|     + Sub<Self, Output = Self> |  | ||||||
|     + std::ops::Neg<Output = Self> |  | ||||||
|     + Mul<Self, Output = Self> |  | ||||||
|     + MulAssign<Self> |  | ||||||
|     + AddAssign<Self> |  | ||||||
|     + PartialOrd<Self> |  | ||||||
| { |  | ||||||
|     fn zero() -> Self; |  | ||||||
|     /// 1/1000
 |  | ||||||
|     fn milli() -> Self; |  | ||||||
|     fn duration_secs(d: Duration) -> Self; |  | ||||||
|     fn abs(self) -> Self { |  | ||||||
|         if self < Self::zero() { |  | ||||||
|             -self |  | ||||||
|         } else { |  | ||||||
|             self |  | ||||||
|         } |         } | ||||||
|  |         AnimationController { speed, anim } | ||||||
|     } |     } | ||||||
|  |     pub fn target(&self) -> f64 { | ||||||
|  |         self.anim.target() | ||||||
|     } |     } | ||||||
| 
 |     pub fn set_target(&mut self, now: Instant, target: f64) { | ||||||
| impl<F: Float> AnimationController<F> { |         self.anim.set_target(target, now); | ||||||
|     pub fn new( |  | ||||||
|         value: F, |  | ||||||
|         target: F, |  | ||||||
|         acceleration: F, |  | ||||||
|         max_speed: F, |  | ||||||
|         accel_until: F, |  | ||||||
|         decel_while: F, |  | ||||||
|         now: Instant, |  | ||||||
|     ) -> Self { |  | ||||||
|         AnimationController { |  | ||||||
|             last_updated: now, |  | ||||||
|             value, |  | ||||||
|             speed: F::zero(), |  | ||||||
|             max_speed, |  | ||||||
|             target, |  | ||||||
|             accel_until, |  | ||||||
|             decel_while, |  | ||||||
|             acceleration, |  | ||||||
|     } |     } | ||||||
|     } |     pub fn update(&mut self, now: Instant, instant: bool) -> Result<f64, f64> { | ||||||
|     pub fn ignore_elapsed_time(&mut self, now: Instant) { |         if self.anim.target() != self.anim.get_value(now) { | ||||||
|         self.last_updated = now; |  | ||||||
|     } |  | ||||||
|     pub fn update(&mut self, now: Instant, instant: bool) -> bool { |  | ||||||
|         let changed = if self.target != self.value { |  | ||||||
|             if instant { |             if instant { | ||||||
|                 self.value = self.target; |                 let target = self.anim.target(); | ||||||
|  |                 self.anim = DefaultAnimatorF64Quadratic::new(target, self.speed); | ||||||
|  |                 Ok(target) | ||||||
|             } else { |             } else { | ||||||
|                 let inc = self.target > self.value; |                 Ok(self.anim.get_value(now)) | ||||||
|                 let seconds = F::duration_secs(now.duration_since(self.last_updated)); |             } | ||||||
|                 let ref1 = self.value + self.speed * self.accel_until; |  | ||||||
|                 let ref2 = self.value + self.speed * self.decel_while; |  | ||||||
|                 let speed_diff = match (ref1 < self.target, ref2 > self.target) { |  | ||||||
|                     (true, false) => self.acceleration, |  | ||||||
|                     (false, true) => -self.acceleration, |  | ||||||
|                     (true, true) | (false, false) => F::zero(), |  | ||||||
|                 }; |  | ||||||
|                 self.speed += speed_diff; |  | ||||||
|                 if self.speed.abs() > self.max_speed { |  | ||||||
|                     if self.speed < F::zero() { |  | ||||||
|                         self.speed = -self.max_speed; |  | ||||||
|         } else { |         } else { | ||||||
|                         self.speed = self.max_speed; |             Err(self.anim.target()) | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|                 self.value += self.speed * seconds; |     pub fn value(&mut self, now: Instant) -> f64 { | ||||||
|                 self.speed += speed_diff; |         match self.update(now, false) { | ||||||
|                 if (self.target - self.value).abs() < self.speed * F::milli() |             Ok(v) | Err(v) => v, | ||||||
|                     || inc != (self.target > self.value) |  | ||||||
|                 { |  | ||||||
|                     // overshoot or target reached
 |  | ||||||
|                     self.value = self.target; |  | ||||||
|                     self.speed = F::zero(); |  | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|             true |  | ||||||
|         } else { |  | ||||||
|             false |  | ||||||
|         }; |  | ||||||
|         self.last_updated = now; |  | ||||||
|         changed |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| impl Float for f32 { |  | ||||||
|     fn zero() -> Self { |  | ||||||
|         0.0 |  | ||||||
|     } |  | ||||||
|     fn milli() -> Self { |  | ||||||
|         0.001 |  | ||||||
|     } |  | ||||||
|     fn duration_secs(d: Duration) -> Self { |  | ||||||
|         d.as_secs_f32().min(0.1) |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| impl Float for f64 { |  | ||||||
|     fn zero() -> Self { |  | ||||||
|         0.0 |  | ||||||
|     } |  | ||||||
|     fn milli() -> Self { |  | ||||||
|         0.001 |  | ||||||
|     } |  | ||||||
|     fn duration_secs(d: Duration) -> Self { |  | ||||||
|         d.as_secs_f64().min(0.1) |  | ||||||
|     } |  | ||||||
| } | } | ||||||
|  | |||||||
| @ -604,7 +604,7 @@ impl GuiElem for Slider { | |||||||
|     fn draw(&mut self, info: &mut DrawInfo, g: &mut speedy2d::Graphics2D) { |     fn draw(&mut self, info: &mut DrawInfo, g: &mut speedy2d::Graphics2D) { | ||||||
|         if self.display != (self.config.mouse_down.0 || info.pos.contains(info.mouse_pos)) { |         if self.display != (self.config.mouse_down.0 || info.pos.contains(info.mouse_pos)) { | ||||||
|             self.display = !self.display; |             self.display = !self.display; | ||||||
|             self.display_since = Some(Instant::now()); |             self.display_since = Some(info.time); | ||||||
|             self.config.redraw = true; |             self.config.redraw = true; | ||||||
|         } |         } | ||||||
|         let dot_size = (info.pos.height() * 0.9).min(info.pos.width() * 0.25); |         let dot_size = (info.pos.height() * 0.9).min(info.pos.width() * 0.25); | ||||||
|  | |||||||
| @ -198,7 +198,11 @@ impl GuiElem for EditorForSongs { | |||||||
|                     Event::SetArtist(name, id) => { |                     Event::SetArtist(name, id) => { | ||||||
|                         self.c_scrollbox.children.c_artist.chosen_id = id; |                         self.c_scrollbox.children.c_artist.chosen_id = id; | ||||||
|                         self.c_scrollbox.children.c_artist.last_search = name.to_lowercase(); |                         self.c_scrollbox.children.c_artist.last_search = name.to_lowercase(); | ||||||
|                         self.c_scrollbox.children.c_artist.open_prog.target = 1.0; |                         self.c_scrollbox | ||||||
|  |                             .children | ||||||
|  |                             .c_artist | ||||||
|  |                             .open_prog | ||||||
|  |                             .set_target(info.time, 1.0); | ||||||
|                         *self |                         *self | ||||||
|                             .c_scrollbox |                             .c_scrollbox | ||||||
|                             .children |                             .children | ||||||
| @ -232,15 +236,15 @@ impl GuiElem for EditorForSongs { | |||||||
|             } |             } | ||||||
|         } |         } | ||||||
|         // artist sel
 |         // artist sel
 | ||||||
|         if self |         if let Ok(val) = self | ||||||
|             .c_scrollbox |             .c_scrollbox | ||||||
|             .children |             .children | ||||||
|             .c_artist |             .c_artist | ||||||
|             .open_prog |             .open_prog | ||||||
|             .update(Instant::now(), false) |             .update(info.time, false) | ||||||
|         { |         { | ||||||
|             if let Some(v) = self.c_scrollbox.children_heights.get_mut(1) { |             if let Some(v) = self.c_scrollbox.children_heights.get_mut(1) { | ||||||
|                 *v = ELEM_HEIGHT * self.c_scrollbox.children.c_artist.open_prog.value; |                 *v = ELEM_HEIGHT * val as f32; | ||||||
|                 self.c_scrollbox.config_mut().redraw = true; |                 self.c_scrollbox.config_mut().redraw = true; | ||||||
|             } |             } | ||||||
|             if let Some(h) = &info.helper { |             if let Some(h) = &info.helper { | ||||||
| @ -272,7 +276,7 @@ pub struct EditorForSongArtistChooser { | |||||||
|     config: GuiElemCfg, |     config: GuiElemCfg, | ||||||
|     event_sender: std::sync::mpsc::Sender<Event>, |     event_sender: std::sync::mpsc::Sender<Event>, | ||||||
|     /// `1.0` = collapsed, `self.expand_to` = expanded (shows `c_picker` of height 7-1=6)
 |     /// `1.0` = collapsed, `self.expand_to` = expanded (shows `c_picker` of height 7-1=6)
 | ||||||
|     open_prog: AnimationController<f32>, |     open_prog: AnimationController, | ||||||
|     expand_to: f32, |     expand_to: f32, | ||||||
|     chosen_id: Option<ArtistId>, |     chosen_id: Option<ArtistId>, | ||||||
|     c_name: TextField, |     c_name: TextField, | ||||||
| @ -285,7 +289,7 @@ impl EditorForSongArtistChooser { | |||||||
|         Self { |         Self { | ||||||
|             config: GuiElemCfg::default(), |             config: GuiElemCfg::default(), | ||||||
|             event_sender, |             event_sender, | ||||||
|             open_prog: AnimationController::new(1.0, 1.0, 0.3, 8.0, 0.5, 0.6, Instant::now()), |             open_prog: AnimationController::new(1.0, 1.0, 4.0), | ||||||
|             expand_to, |             expand_to, | ||||||
|             chosen_id: None, |             chosen_id: None, | ||||||
|             c_name: TextField::new( |             c_name: TextField::new( | ||||||
| @ -307,10 +311,10 @@ impl EditorForSongArtistChooser { | |||||||
| } | } | ||||||
| impl GuiElem for EditorForSongArtistChooser { | impl GuiElem for EditorForSongArtistChooser { | ||||||
|     fn draw(&mut self, info: &mut crate::gui::DrawInfo, _g: &mut speedy2d::Graphics2D) { |     fn draw(&mut self, info: &mut crate::gui::DrawInfo, _g: &mut speedy2d::Graphics2D) { | ||||||
|         let picker_enabled = self.open_prog.value > 1.0; |         let picker_enabled = self.open_prog.value(info.time) > 1.0; | ||||||
|         self.c_picker.config_mut().enabled = picker_enabled; |         self.c_picker.config_mut().enabled = picker_enabled; | ||||||
|         if picker_enabled { |         if picker_enabled { | ||||||
|             let split = 1.0 / self.open_prog.value; |             let split = 1.0 / self.open_prog.value(info.time) as f32; | ||||||
|             self.c_name.config_mut().pos = Rectangle::from_tuples((0.0, 0.0), (1.0, split)); |             self.c_name.config_mut().pos = Rectangle::from_tuples((0.0, 0.0), (1.0, split)); | ||||||
|             self.c_picker.config_mut().pos = Rectangle::from_tuples((0.0, split), (1.0, 1.0)); |             self.c_picker.config_mut().pos = Rectangle::from_tuples((0.0, split), (1.0, 1.0)); | ||||||
|         } else { |         } else { | ||||||
| @ -327,9 +331,10 @@ impl GuiElem for EditorForSongArtistChooser { | |||||||
|             }; |             }; | ||||||
|             if search_changed { |             if search_changed { | ||||||
|                 self.chosen_id = None; |                 self.chosen_id = None; | ||||||
|                 self.open_prog.target = self.expand_to; |  | ||||||
|                 if search.is_empty() { |                 if search.is_empty() { | ||||||
|                     self.open_prog.target = 1.0; |                     self.open_prog.set_target(info.time, 1.0); | ||||||
|  |                 } else { | ||||||
|  |                     self.open_prog.set_target(info.time, self.expand_to as f64); | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|             let artists = info |             let artists = info | ||||||
|  | |||||||
| @ -1,7 +1,4 @@ | |||||||
| use std::{ | use std::sync::{atomic::AtomicBool, Arc}; | ||||||
|     sync::{atomic::AtomicBool, Arc}, |  | ||||||
|     time::Instant, |  | ||||||
| }; |  | ||||||
| 
 | 
 | ||||||
| use musicdb_lib::data::ArtistId; | use musicdb_lib::data::ArtistId; | ||||||
| use speedy2d::{color::Color, dimen::Vec2, image::ImageHandle, shape::Rectangle}; | use speedy2d::{color::Color, dimen::Vec2, image::ImageHandle, shape::Rectangle}; | ||||||
| @ -27,8 +24,8 @@ pub struct IdleDisplay { | |||||||
|     pub c_buttons: PlayPause, |     pub c_buttons: PlayPause, | ||||||
|     pub c_buttons_custom_pos: bool, |     pub c_buttons_custom_pos: bool, | ||||||
| 
 | 
 | ||||||
|     pub cover_aspect_ratio: AnimationController<f32>, |     pub cover_aspect_ratio: AnimationController, | ||||||
|     pub artist_image_aspect_ratio: AnimationController<f32>, |     pub artist_image_aspect_ratio: AnimationController, | ||||||
| 
 | 
 | ||||||
|     pub cover_pos: Option<Rectangle>, |     pub cover_pos: Option<Rectangle>, | ||||||
|     pub cover_left: f32, |     pub cover_left: f32, | ||||||
| @ -76,24 +73,8 @@ impl IdleDisplay { | |||||||
|             is_fav: (false, Arc::clone(&is_fav)), |             is_fav: (false, Arc::clone(&is_fav)), | ||||||
|             c_buttons: PlayPause::new(GuiElemCfg::default(), is_fav), |             c_buttons: PlayPause::new(GuiElemCfg::default(), is_fav), | ||||||
|             c_buttons_custom_pos: false, |             c_buttons_custom_pos: false, | ||||||
|             cover_aspect_ratio: AnimationController::new( |             cover_aspect_ratio: AnimationController::new(1.0, 1.0, 1.0), | ||||||
|                 1.0, |             artist_image_aspect_ratio: AnimationController::new(0.0, 0.0, 1.0), | ||||||
|                 1.0, |  | ||||||
|                 0.01, |  | ||||||
|                 1.0, |  | ||||||
|                 0.8, |  | ||||||
|                 0.6, |  | ||||||
|                 Instant::now(), |  | ||||||
|             ), |  | ||||||
|             artist_image_aspect_ratio: AnimationController::new( |  | ||||||
|                 0.0, |  | ||||||
|                 0.0, |  | ||||||
|                 0.01, |  | ||||||
|                 1.0, |  | ||||||
|                 0.8, |  | ||||||
|                 0.6, |  | ||||||
|                 Instant::now(), |  | ||||||
|             ), |  | ||||||
|             cover_pos: None, |             cover_pos: None, | ||||||
|             cover_left: 0.01, |             cover_left: 0.01, | ||||||
|             cover_top: 0.21, |             cover_top: 0.21, | ||||||
| @ -181,7 +162,7 @@ impl GuiElem for IdleDisplay { | |||||||
|                         .is_some_and(|(a, _)| *a != artist_id) |                         .is_some_and(|(a, _)| *a != artist_id) | ||||||
|                 { |                 { | ||||||
|                     self.current_artist_image = Some((artist_id, None)); |                     self.current_artist_image = Some((artist_id, None)); | ||||||
|                     self.artist_image_aspect_ratio.target = 0.0; |                     self.artist_image_aspect_ratio.set_target(info.time, 0.0); | ||||||
|                     if let Some(artist) = info.database.artists().get(&artist_id) { |                     if let Some(artist) = info.database.artists().get(&artist_id) { | ||||||
|                         for tag in &artist.general.tags { |                         for tag in &artist.general.tags { | ||||||
|                             if tag.starts_with("ImageExt=") { |                             if tag.starts_with("ImageExt=") { | ||||||
| @ -205,7 +186,7 @@ impl GuiElem for IdleDisplay { | |||||||
|             } else { |             } else { | ||||||
|                 if self.current_artist_image.is_some() { |                 if self.current_artist_image.is_some() { | ||||||
|                     self.current_artist_image = None; |                     self.current_artist_image = None; | ||||||
|                     self.artist_image_aspect_ratio.target = 0.0; |                     self.artist_image_aspect_ratio.set_target(info.time, 0.0); | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
| @ -213,7 +194,7 @@ impl GuiElem for IdleDisplay { | |||||||
|             self.current_info.new_cover = false; |             self.current_info.new_cover = false; | ||||||
|             match self.current_info.current_cover { |             match self.current_info.current_cover { | ||||||
|                 None | Some((_, Some(None))) => { |                 None | Some((_, Some(None))) => { | ||||||
|                     self.cover_aspect_ratio.target = 0.0; |                     self.cover_aspect_ratio.set_target(info.time, 0.0); | ||||||
|                 } |                 } | ||||||
|                 Some((_, None)) | Some((_, Some(Some(_)))) => {} |                 Some((_, None)) | Some((_, Some(Some(_)))) => {} | ||||||
|             } |             } | ||||||
| @ -243,6 +224,7 @@ impl GuiElem for IdleDisplay { | |||||||
|                 info.pos.top_left().x + info.pos.height() * self.cover_left, |                 info.pos.top_left().x + info.pos.height() * self.cover_left, | ||||||
|                 info.pos.top_left().y + info.pos.height() * self.cover_top, |                 info.pos.top_left().y + info.pos.height() * self.cover_top, | ||||||
|                 info.pos.top_left().y + info.pos.height() * self.cover_bottom, |                 info.pos.top_left().y + info.pos.height() * self.cover_bottom, | ||||||
|  |                 info.time, | ||||||
|                 &mut self.cover_aspect_ratio, |                 &mut self.cover_aspect_ratio, | ||||||
|             ); |             ); | ||||||
|         } |         } | ||||||
| @ -260,20 +242,23 @@ impl GuiElem for IdleDisplay { | |||||||
|                     info.pos.top_left().x + info.pos.height() * self.cover_left, |                     info.pos.top_left().x + info.pos.height() * self.cover_left, | ||||||
|                     top, |                     top, | ||||||
|                     bottom, |                     bottom, | ||||||
|                     self.cover_aspect_ratio.value, |                     self.cover_aspect_ratio.value(info.time) as f32, | ||||||
|                 ) + info.pos.height() * self.artist_image_to_cover_margin, |                 ) + info.pos.height() * self.artist_image_to_cover_margin, | ||||||
|                 top + (bottom - top) * self.artist_image_top, |                 top + (bottom - top) * self.artist_image_top, | ||||||
|                 bottom, |                 bottom, | ||||||
|  |                 info.time, | ||||||
|                 &mut self.artist_image_aspect_ratio, |                 &mut self.artist_image_aspect_ratio, | ||||||
|             ); |             ); | ||||||
|         } |         } | ||||||
|         // move children to make space for cover
 |         // move children to make space for cover
 | ||||||
|         let ar_updated = self |         let ar_updated = self | ||||||
|             .cover_aspect_ratio |             .cover_aspect_ratio | ||||||
|             .update(info.time.clone(), info.high_performance) |             .update(info.time, info.high_performance) | ||||||
|  |             .is_ok() | ||||||
|             | self |             | self | ||||||
|                 .artist_image_aspect_ratio |                 .artist_image_aspect_ratio | ||||||
|                 .update(info.time.clone(), info.high_performance); |                 .update(info.time, info.high_performance) | ||||||
|  |                 .is_ok(); | ||||||
|         if ar_updated || info.pos.size() != self.config.pixel_pos.size() { |         if ar_updated || info.pos.size() != self.config.pixel_pos.size() { | ||||||
|             if let Some(h) = &info.helper { |             if let Some(h) = &info.helper { | ||||||
|                 h.request_redraw(); |                 h.request_redraw(); | ||||||
| @ -281,8 +266,12 @@ impl GuiElem for IdleDisplay { | |||||||
|             // make thing be relative to width instead of to height by multiplying with this
 |             // make thing be relative to width instead of to height by multiplying with this
 | ||||||
|             let top = self.cover_top; |             let top = self.cover_top; | ||||||
|             let bottom = self.cover_bottom; |             let bottom = self.cover_bottom; | ||||||
|             let left = (get_right_x(self.cover_left, top, bottom, self.cover_aspect_ratio.value) |             let left = (get_right_x( | ||||||
|                 + self.artist_image_to_cover_margin) |                 self.cover_left, | ||||||
|  |                 top, | ||||||
|  |                 bottom, | ||||||
|  |                 self.cover_aspect_ratio.value(info.time) as f32, | ||||||
|  |             ) + self.artist_image_to_cover_margin) | ||||||
|                 * info.pos.height() |                 * info.pos.height() | ||||||
|                 / info.pos.width(); |                 / info.pos.width(); | ||||||
|             let ai_top = top + (bottom - top) * self.artist_image_top; |             let ai_top = top + (bottom - top) * self.artist_image_top; | ||||||
| @ -293,7 +282,7 @@ impl GuiElem for IdleDisplay { | |||||||
|                 left, |                 left, | ||||||
|                 ai_top * info.pos.height() / info.pos.width(), |                 ai_top * info.pos.height() / info.pos.width(), | ||||||
|                 bottom * info.pos.height() / info.pos.width(), |                 bottom * info.pos.height() / info.pos.width(), | ||||||
|                 self.artist_image_aspect_ratio.value, |                 self.artist_image_aspect_ratio.value(info.time) as f32, | ||||||
|             ); |             ); | ||||||
|             self.c_side2_label.config_mut().pos = |             self.c_side2_label.config_mut().pos = | ||||||
|                 Rectangle::from_tuples((left, ai_top), (max_right, bottom)); |                 Rectangle::from_tuples((left, ai_top), (max_right, bottom)); | ||||||
|  | |||||||
| @ -6,7 +6,6 @@ use std::{ | |||||||
|         atomic::{AtomicBool, AtomicUsize}, |         atomic::{AtomicBool, AtomicUsize}, | ||||||
|         mpsc, Mutex, |         mpsc, Mutex, | ||||||
|     }, |     }, | ||||||
|     time::Instant, |  | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| use musicdb_lib::data::{ | use musicdb_lib::data::{ | ||||||
| @ -70,7 +69,7 @@ pub struct LibraryBrowser { | |||||||
|     search_song: String, |     search_song: String, | ||||||
|     search_song_regex: Option<Regex>, |     search_song_regex: Option<Regex>, | ||||||
|     filter_target_state: Arc<AtomicBool>, |     filter_target_state: Arc<AtomicBool>, | ||||||
|     filter_state: AnimationController<f32>, |     filter_state: AnimationController, | ||||||
|     library_updated: bool, |     library_updated: bool, | ||||||
|     search_settings_changed: Arc<AtomicBool>, |     search_settings_changed: Arc<AtomicBool>, | ||||||
|     search_is_case_sensitive: Arc<AtomicBool>, |     search_is_case_sensitive: Arc<AtomicBool>, | ||||||
| @ -203,7 +202,7 @@ impl LibraryBrowser { | |||||||
|             search_song: String::new(), |             search_song: String::new(), | ||||||
|             search_song_regex: None, |             search_song_regex: None, | ||||||
|             filter_target_state, |             filter_target_state, | ||||||
|             filter_state: AnimationController::new(0.0, 0.0, 0.25, 25.0, 0.1, 0.2, Instant::now()), |             filter_state: AnimationController::new(0.0, 0.0, 4.0), | ||||||
|             library_updated: true, |             library_updated: true, | ||||||
|             search_settings_changed, |             search_settings_changed, | ||||||
|             search_is_case_sensitive, |             search_is_case_sensitive, | ||||||
| @ -380,18 +379,19 @@ impl GuiElem for LibraryBrowser { | |||||||
|         let filter_target_state = self |         let filter_target_state = self | ||||||
|             .filter_target_state |             .filter_target_state | ||||||
|             .load(std::sync::atomic::Ordering::Relaxed); |             .load(std::sync::atomic::Ordering::Relaxed); | ||||||
|         self.filter_state.target = if filter_target_state { 1.0 } else { 0.0 }; |         self.filter_state | ||||||
|         if self.filter_state.update(info.time, info.high_performance) { |             .set_target(info.time, if filter_target_state { 1.0 } else { 0.0 }); | ||||||
|  |         if let Ok(val) = self.filter_state.update(info.time, info.high_performance) { | ||||||
|             if let Some(h) = &info.helper { |             if let Some(h) = &info.helper { | ||||||
|                 h.request_redraw(); |                 h.request_redraw(); | ||||||
|             } |             } | ||||||
|             let y = LP_LIB1 + (LP_LIB1S - LP_LIB1) * self.filter_state.value; |             let y = LP_LIB1 + (LP_LIB1S - LP_LIB1) * val as f32; | ||||||
|             self.c_scroll_box.config_mut().pos = |             self.c_scroll_box.config_mut().pos = | ||||||
|                 Rectangle::new(Vec2::new(0.0, y), Vec2::new(1.0, LP_LIB2)); |                 Rectangle::new(Vec2::new(0.0, y), Vec2::new(1.0, LP_LIB2)); | ||||||
|             let filter_panel = &mut self.c_filter_panel; |             let filter_panel = &mut self.c_filter_panel; | ||||||
|             filter_panel.config_mut().pos = |             filter_panel.config_mut().pos = | ||||||
|                 Rectangle::new(Vec2::new(0.0, LP_LIB1), Vec2::new(1.0, y)); |                 Rectangle::new(Vec2::new(0.0, LP_LIB1), Vec2::new(1.0, y)); | ||||||
|             filter_panel.config.enabled = self.filter_state.value > 0.0; |             filter_panel.config.enabled = val > 0.0; | ||||||
|         } |         } | ||||||
|         // -
 |         // -
 | ||||||
|         if self.library_updated { |         if self.library_updated { | ||||||
|  | |||||||
| @ -1,4 +1,7 @@ | |||||||
| use std::{sync::Arc, time::Duration}; | use std::{ | ||||||
|  |     sync::Arc, | ||||||
|  |     time::{Duration, Instant}, | ||||||
|  | }; | ||||||
| 
 | 
 | ||||||
| use musicdb_lib::data::{CoverId, SongId}; | use musicdb_lib::data::{CoverId, SongId}; | ||||||
| use speedy2d::{color::Color, dimen::Vec2, image::ImageHandle, shape::Rectangle}; | use speedy2d::{color::Color, dimen::Vec2, image::ImageHandle, shape::Rectangle}; | ||||||
| @ -151,25 +154,29 @@ pub fn image_display( | |||||||
|     left: f32, |     left: f32, | ||||||
|     top: f32, |     top: f32, | ||||||
|     bottom: f32, |     bottom: f32, | ||||||
|     aspect_ratio: &mut AnimationController<f32>, |     now: Instant, | ||||||
|  |     aspect_ratio: &mut AnimationController, | ||||||
| ) { | ) { | ||||||
|     if let Some(cover) = &img { |     if let Some(cover) = &img { | ||||||
|         let cover_size = cover.size(); |         let cover_size = cover.size(); | ||||||
|         aspect_ratio.target = if cover_size.x > 0 && cover_size.y > 0 { |  | ||||||
|         let pos = if let Some(pos) = pos { |         let pos = if let Some(pos) = pos { | ||||||
|             pos |             pos | ||||||
|         } else { |         } else { | ||||||
|                 let right_x = get_right_x(left, top, bottom, aspect_ratio.value); |             let right_x = get_right_x(left, top, bottom, aspect_ratio.value(now) as f32); | ||||||
|             Rectangle::from_tuples((left, top), (right_x, bottom)) |             Rectangle::from_tuples((left, top), (right_x, bottom)) | ||||||
|         }; |         }; | ||||||
|             let aspect_ratio = cover_size.x as f32 / cover_size.y as f32; |  | ||||||
|         g.draw_rectangle_image(pos, cover); |         g.draw_rectangle_image(pos, cover); | ||||||
|             aspect_ratio |         aspect_ratio.set_target( | ||||||
|  |             now, | ||||||
|  |             if cover_size.x > 0 && cover_size.y > 0 { | ||||||
|  |                 let aspect_ratio = cover_size.x as f32 / cover_size.y as f32; | ||||||
|  |                 aspect_ratio as f64 | ||||||
|             } else { |             } else { | ||||||
|                 0.0 |                 0.0 | ||||||
|         }; |             }, | ||||||
|  |         ); | ||||||
|     } else { |     } else { | ||||||
|         aspect_ratio.target = 0.0; |         aspect_ratio.set_target(now, 0.0); | ||||||
|     } |     } | ||||||
| } | } | ||||||
| pub fn get_right_x(left: f32, top: f32, bottom: f32, aspect_ratio: f32) -> f32 { | pub fn get_right_x(left: f32, top: f32, bottom: f32, aspect_ratio: f32) -> f32 { | ||||||
|  | |||||||
| @ -403,7 +403,7 @@ impl GuiElem for GuiScreen { | |||||||
|             false |             false | ||||||
|         }; |         }; | ||||||
|         // request_redraw for animations
 |         // request_redraw for animations
 | ||||||
|         let idle_value = self.idle.get_value(Instant::now()) as f32; |         let idle_value = self.idle.get_value(info.time) as f32; | ||||||
|         let idle_changed = self.idle_prev_val != idle_value; |         let idle_changed = self.idle_prev_val != idle_value; | ||||||
|         if idle_changed || idle_exit_anim || self.settings.1.is_some() { |         if idle_changed || idle_exit_anim || self.settings.1.is_some() { | ||||||
|             self.idle_prev_val = idle_value; |             self.idle_prev_val = idle_value; | ||||||
|  | |||||||
| @ -1,7 +1,4 @@ | |||||||
| use std::{ | use std::sync::{atomic::AtomicBool, Arc}; | ||||||
|     sync::{atomic::AtomicBool, Arc}, |  | ||||||
|     time::Instant, |  | ||||||
| }; |  | ||||||
| 
 | 
 | ||||||
| use speedy2d::{dimen::Vec2, shape::Rectangle}; | use speedy2d::{dimen::Vec2, shape::Rectangle}; | ||||||
| 
 | 
 | ||||||
| @ -17,7 +14,7 @@ pub struct StatusBar { | |||||||
|     config: GuiElemCfg, |     config: GuiElemCfg, | ||||||
|     pub idle_mode: f32, |     pub idle_mode: f32, | ||||||
|     current_info: CurrentInfo, |     current_info: CurrentInfo, | ||||||
|     cover_aspect_ratio: AnimationController<f32>, |     cover_aspect_ratio: AnimationController, | ||||||
|     c_song_label: AdvancedLabel, |     c_song_label: AdvancedLabel, | ||||||
|     pub force_reset_texts: bool, |     pub force_reset_texts: bool, | ||||||
|     c_buttons: PlayPause, |     c_buttons: PlayPause, | ||||||
| @ -31,15 +28,7 @@ impl StatusBar { | |||||||
|             config, |             config, | ||||||
|             idle_mode: 0.0, |             idle_mode: 0.0, | ||||||
|             current_info: CurrentInfo::new(), |             current_info: CurrentInfo::new(), | ||||||
|             cover_aspect_ratio: AnimationController::new( |             cover_aspect_ratio: AnimationController::new(0.0, 0.0, 1.0), | ||||||
|                 0.0, |  | ||||||
|                 0.0, |  | ||||||
|                 0.01, |  | ||||||
|                 1.0, |  | ||||||
|                 0.8, |  | ||||||
|                 0.6, |  | ||||||
|                 Instant::now(), |  | ||||||
|             ), |  | ||||||
|             c_song_label: AdvancedLabel::new(GuiElemCfg::default(), Vec2::new(0.0, 0.5), vec![]), |             c_song_label: AdvancedLabel::new(GuiElemCfg::default(), Vec2::new(0.0, 0.5), vec![]), | ||||||
|             force_reset_texts: false, |             force_reset_texts: false, | ||||||
|             is_fav: (false, Arc::clone(&is_fav)), |             is_fav: (false, Arc::clone(&is_fav)), | ||||||
| @ -82,7 +71,7 @@ impl GuiElem for StatusBar { | |||||||
|             self.current_info.new_cover = false; |             self.current_info.new_cover = false; | ||||||
|             match self.current_info.current_cover { |             match self.current_info.current_cover { | ||||||
|                 None | Some((_, Some(None))) => { |                 None | Some((_, Some(None))) => { | ||||||
|                     self.cover_aspect_ratio.target = 0.0; |                     self.cover_aspect_ratio.set_target(info.time, 0.0); | ||||||
|                 } |                 } | ||||||
|                 Some((_, None)) | Some((_, Some(Some(_)))) => {} |                 Some((_, None)) | Some((_, Some(Some(_)))) => {} | ||||||
|             } |             } | ||||||
| @ -90,7 +79,8 @@ impl GuiElem for StatusBar { | |||||||
|         // move children to make space for cover
 |         // move children to make space for cover
 | ||||||
|         let ar_updated = self |         let ar_updated = self | ||||||
|             .cover_aspect_ratio |             .cover_aspect_ratio | ||||||
|             .update(info.time.clone(), info.high_performance); |             .update(info.time, info.high_performance) | ||||||
|  |             .is_ok(); | ||||||
|         if ar_updated || info.pos.size() != self.config.pixel_pos.size() { |         if ar_updated || info.pos.size() != self.config.pixel_pos.size() { | ||||||
|             if let Some(h) = &info.helper { |             if let Some(h) = &info.helper { | ||||||
|                 h.request_redraw(); |                 h.request_redraw(); | ||||||
| @ -105,7 +95,8 @@ impl GuiElem for StatusBar { | |||||||
|             ); |             ); | ||||||
|             self.c_song_label.config_mut().pos = Rectangle::from_tuples( |             self.c_song_label.config_mut().pos = Rectangle::from_tuples( | ||||||
|                 ( |                 ( | ||||||
|                     self.cover_aspect_ratio.value * info.pos.height() / info.pos.width(), |                     self.cover_aspect_ratio.value(info.time) as f32 * info.pos.height() | ||||||
|  |                         / info.pos.width(), | ||||||
|                     0.0, |                     0.0, | ||||||
|                 ), |                 ), | ||||||
|                 (buttons_right_pos - buttons_width, 1.0), |                 (buttons_right_pos - buttons_width, 1.0), | ||||||
| @ -125,6 +116,7 @@ impl GuiElem for StatusBar { | |||||||
|                 info.pos.top_left().x + info.pos.height() * 0.05, |                 info.pos.top_left().x + info.pos.height() * 0.05, | ||||||
|                 info.pos.top_left().y + info.pos.height() * 0.05, |                 info.pos.top_left().y + info.pos.height() * 0.05, | ||||||
|                 info.pos.top_left().y + info.pos.height() * 0.95, |                 info.pos.top_left().y + info.pos.height() * 0.95, | ||||||
|  |                 info.time, | ||||||
|                 &mut self.cover_aspect_ratio, |                 &mut self.cover_aspect_ratio, | ||||||
|             ); |             ); | ||||||
|         } |         } | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 Mark
						Mark