feat: use better animations (these now work on low fps too)

This commit is contained in:
Mark 2025-08-23 12:26:13 +02:00
parent c23d487a02
commit ee7c74f3a8
8 changed files with 105 additions and 200 deletions

View File

@ -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()
impl<F: Float> AnimationController<F> {
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 ignore_elapsed_time(&mut self, now: Instant) { pub fn set_target(&mut self, now: Instant, target: f64) {
self.last_updated = now; self.anim.set_target(target, now);
} }
pub fn update(&mut self, now: Instant, instant: bool) -> bool { pub fn update(&mut self, now: Instant, instant: bool) -> Result<f64, f64> {
let changed = if self.target != self.value { if self.anim.target() != self.anim.get_value(now) {
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 {
self.speed = self.max_speed;
}
}
self.value += self.speed * seconds;
self.speed += speed_diff;
if (self.target - self.value).abs() < self.speed * F::milli()
|| inc != (self.target > self.value)
{
// overshoot or target reached
self.value = self.target;
self.speed = F::zero();
}
} }
true
} else { } else {
false Err(self.anim.target())
}; }
self.last_updated = now; }
changed pub fn value(&mut self, now: Instant) -> f64 {
} match self.update(now, false) {
} Ok(v) | Err(v) => v,
}
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)
} }
} }

View File

@ -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);

View File

@ -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

View File

@ -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));

View File

@ -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 {

View File

@ -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 {
let right_x = get_right_x(left, top, bottom, aspect_ratio.value);
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);
aspect_ratio
} else { } else {
0.0 let right_x = get_right_x(left, top, bottom, aspect_ratio.value(now) as f32);
Rectangle::from_tuples((left, top), (right_x, bottom))
}; };
g.draw_rectangle_image(pos, cover);
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 {
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 {

View File

@ -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;

View File

@ -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,
); );
} }