mirror of
https://github.com/Dummi26/musicdb.git
synced 2025-09-13 15:16:14 +02: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