From e29fccf20e5e65760eef60731668742f240b7ebd Mon Sep 17 00:00:00 2001 From: Mark Date: Tue, 14 Nov 2023 18:38:12 +0100 Subject: [PATCH] fix bug and add power saver power saver will be enabled on bad performance --- musicdb-client/src/gui.rs | 72 +++++++++++++++++++------- musicdb-client/src/gui_base.rs | 8 ++- musicdb-client/src/gui_idle_display.rs | 22 ++++++-- musicdb-client/src/gui_library.rs | 2 +- musicdb-client/src/gui_screen.rs | 48 +++++++++++++++-- musicdb-client/src/gui_settings.rs | 34 ++++++------ musicdb-client/src/gui_statusbar.rs | 2 +- 7 files changed, 140 insertions(+), 48 deletions(-) diff --git a/musicdb-client/src/gui.rs b/musicdb-client/src/gui.rs index f5ecbe7..5c737ab 100755 --- a/musicdb-client/src/gui.rs +++ b/musicdb-client/src/gui.rs @@ -281,19 +281,21 @@ pub struct Gui { pub font: Font, pub covers: Option>, pub custom_images: Option>, - pub last_draw: Instant, pub modifiers: ModifiersState, pub dragging: Option<( Dragging, Option>, )>, - pub no_animations: bool, + pub high_performance: bool, pub line_height: f32, pub last_height: f32, pub scroll_pixels_multiplier: f64, pub scroll_lines_multiplier: f64, pub scroll_pages_multiplier: f64, pub gui_config: Option, + last_performance_check: Instant, + average_frame_time_ms: u32, + frames_drawn: u32, } impl Gui { fn new( @@ -399,16 +401,18 @@ impl Gui { covers: Some(HashMap::new()), custom_images: Some(HashMap::new()), // font: Font::new(include_bytes!("/usr/share/fonts/TTF/FiraSans-Regular.ttf")).unwrap(), - last_draw: Instant::now(), modifiers: ModifiersState::default(), dragging: None, - no_animations, + high_performance: no_animations, line_height, last_height: 720.0, scroll_pixels_multiplier, scroll_lines_multiplier, scroll_pages_multiplier, gui_config: Some(gui_config), + last_performance_check: Instant::now(), + average_frame_time_ms: 0, + frames_drawn: 0, } } } @@ -974,8 +978,9 @@ impl Default for GuiElemCfg { #[allow(unused)] pub enum GuiAction { OpenMain, - SetIdle(bool), - SetAnimationsDisabled(bool), + /// false -> prevent idling, true -> end idling even if already idle + EndIdle(bool), + SetHighPerformance(bool), OpenSettings(bool), ShowNotification(Box (Box, NotifInfo) + Send>), /// Build the GuiAction(s) later, when we have access to the Database (can turn an AlbumId into a QueueContent::Folder, etc) @@ -1029,7 +1034,7 @@ pub struct DrawInfo<'a> { )>, pub context_menu: Option>, pub gui_config: &'a mut GuiConfig, - pub no_animations: bool, + pub high_performance: bool, } pub fn adjust_area(outer: &Rectangle, rel_area: &Rectangle) -> Rectangle { @@ -1062,7 +1067,7 @@ impl Gui { GuiAction::ShowNotification(func) => _ = self.notif_sender.send(func), GuiAction::ResetKeyboardFocus => _ = self.gui._keyboard_reset_focus(), GuiAction::SetDragging(d) => self.dragging = d, - GuiAction::SetAnimationsDisabled(d) => self.no_animations = d, + GuiAction::SetHighPerformance(d) => self.high_performance = d, GuiAction::ContextMenu(m) => self.gui.c_context_menu = m, GuiAction::SetLineHeight(h) => { self.line_height = h; @@ -1077,8 +1082,12 @@ impl Gui { } GuiAction::Do(mut f) => f(self), GuiAction::Exit => _ = self.event_sender.send_event(GuiEvent::Exit), - GuiAction::SetIdle(v) => { - self.gui.idle.target = if v { 1.0 } else { 0.0 }; + GuiAction::EndIdle(v) => { + if v { + self.gui.unidle(); + } else { + self.gui.not_idle(); + } } GuiAction::OpenSettings(v) => { self.gui.idle.target = 0.0; @@ -1099,7 +1108,7 @@ impl Gui { } impl WindowHandler for Gui { fn on_draw(&mut self, helper: &mut WindowHelper, graphics: &mut Graphics2D) { - let start = Instant::now(); + let draw_start_time = Instant::now(); graphics.draw_rectangle( Rectangle::new(Vec2::ZERO, self.size.into_f32()), Color::BLACK, @@ -1109,7 +1118,7 @@ impl WindowHandler for Gui { let mut custom_images = self.custom_images.take().unwrap(); let mut cfg = self.gui_config.take().unwrap(); let mut info = DrawInfo { - time: Instant::now(), + time: draw_start_time, actions: Vec::with_capacity(0), pos: Rectangle::new(Vec2::ZERO, self.size.into_f32()), database: &mut *dblock, @@ -1122,7 +1131,7 @@ impl WindowHandler for Gui { has_keyboard_focus: false, child_has_keyboard_focus: true, line_height: self.line_height, - no_animations: self.no_animations, + high_performance: self.high_performance, dragging: self.dragging.take(), context_menu: self.gui.c_context_menu.take(), gui_config: &mut cfg, @@ -1173,11 +1182,38 @@ impl WindowHandler for Gui { for a in actions { self.exec_gui_action(a); } - // eprintln!( - // "fps <= {}", - // 1000 / self.last_draw.elapsed().as_millis().max(1) - // ); - self.last_draw = start; + let ft = draw_start_time.elapsed().as_millis() as u32; + self.average_frame_time_ms = (self.average_frame_time_ms * 7 + ft) / 8; + if !self.high_performance && self.average_frame_time_ms > 50 { + self.high_performance = true; + *self + .gui + .c_settings + .c_scroll_box + .children + .performance_toggle + .children + .1 + .children[0] + .content + .text() = "On due to\nbad performance".to_string(); + } + // #[cfg(debug_assertions)] + { + self.frames_drawn += 1; + if draw_start_time + .duration_since(self.last_performance_check) + .as_secs() + >= 1 + { + self.last_performance_check = draw_start_time; + eprintln!( + "[performance] {} fps | {}ms", + self.frames_drawn, self.average_frame_time_ms + ); + self.frames_drawn = 0; + } + } } fn on_mouse_button_down(&mut self, helper: &mut WindowHelper, button: MouseButton) { if let Some(a) = self.gui._mouse_button(button, true, self.mouse_pos.clone()) { diff --git a/musicdb-client/src/gui_base.rs b/musicdb-client/src/gui_base.rs index 85221aa..517e5bf 100755 --- a/musicdb-client/src/gui_base.rs +++ b/musicdb-client/src/gui_base.rs @@ -200,7 +200,7 @@ impl GuiElem for ScrollBox { } if self.scroll_target != self.scroll_display { self.config.redraw = true; - if info.no_animations { + if info.high_performance { self.scroll_display = self.scroll_target; } else { self.scroll_display = 0.2 * self.scroll_target + 0.8 * self.scroll_display; @@ -233,7 +233,11 @@ impl GuiElem for ScrollBox { let y_rel = self.size_unit.to_rel(y_pos, info.pos.height()); if y_rel + h_rel >= 0.0 && y_rel <= 1.0 { let cfg = e.config_mut(); - cfg.enabled = true; + cfg.enabled = if info.high_performance { + y_rel >= 0.0 && y_rel + h_rel <= 1.0 + } else { + true + }; cfg.pos = Rectangle::new( Vec2::new(cfg.pos.top_left().x, 0.0f32.max(y_rel)), Vec2::new( diff --git a/musicdb-client/src/gui_idle_display.rs b/musicdb-client/src/gui_idle_display.rs index a1c14c4..9b00d7c 100644 --- a/musicdb-client/src/gui_idle_display.rs +++ b/musicdb-client/src/gui_idle_display.rs @@ -4,10 +4,11 @@ use musicdb_lib::data::ArtistId; use speedy2d::{color::Color, dimen::Vec2, image::ImageHandle, shape::Rectangle}; use crate::{ - gui::{DrawInfo, GuiElem, GuiElemCfg, GuiServerImage}, + gui::{DrawInfo, GuiAction, GuiElem, GuiElemCfg, GuiServerImage}, gui_anim::AnimationController, + gui_base::Button, gui_playback::{get_right_x, image_display, CurrentInfo}, - gui_text::AdvancedLabel, + gui_text::{AdvancedLabel, Label}, }; pub struct IdleDisplay { @@ -15,6 +16,7 @@ pub struct IdleDisplay { pub idle_mode: f32, current_info: CurrentInfo, current_artist_image: Option<(ArtistId, Option<(String, Option>)>)>, + pub c_idle_exit_hint: Button<[Label; 1]>, c_top_label: AdvancedLabel, c_side1_label: AdvancedLabel, c_side2_label: AdvancedLabel, @@ -36,6 +38,17 @@ impl IdleDisplay { idle_mode: 0.0, current_info: CurrentInfo::new(), current_artist_image: None, + c_idle_exit_hint: Button::new( + GuiElemCfg::default().disabled(), + |_| vec![GuiAction::EndIdle(true)], + [Label::new( + GuiElemCfg::default(), + "Back".to_owned(), + Color::GRAY, + None, + Vec2::new(0.5, 0.5), + )], + ), c_top_label: AdvancedLabel::new( GuiElemCfg::at(Rectangle::from_tuples((0.05, 0.02), (0.95, 0.18))), Vec2::new(0.5, 0.5), @@ -74,6 +87,7 @@ impl GuiElem for IdleDisplay { fn children(&mut self) -> Box + '_> { Box::new( [ + self.c_idle_exit_hint.elem_mut(), self.c_top_label.elem_mut(), self.c_side1_label.elem_mut(), self.c_side2_label.elem_mut(), @@ -215,10 +229,10 @@ impl GuiElem for IdleDisplay { // move children to make space for cover let ar_updated = self .cover_aspect_ratio - .update(info.time.clone(), info.no_animations) + .update(info.time.clone(), info.high_performance) | self .artist_image_aspect_ratio - .update(info.time.clone(), info.no_animations); + .update(info.time.clone(), info.high_performance); if ar_updated || info.pos.size() != self.config.pixel_pos.size() { if let Some(h) = &info.helper { h.request_redraw(); diff --git a/musicdb-client/src/gui_library.rs b/musicdb-client/src/gui_library.rs index a6a3130..a35f01f 100755 --- a/musicdb-client/src/gui_library.rs +++ b/musicdb-client/src/gui_library.rs @@ -380,7 +380,7 @@ impl GuiElem for LibraryBrowser { .filter_target_state .load(std::sync::atomic::Ordering::Relaxed); self.filter_state.target = if filter_target_state { 1.0 } else { 0.0 }; - if self.filter_state.update(info.time, info.no_animations) { + if self.filter_state.update(info.time, info.high_performance) { if let Some(h) = &info.helper { h.request_redraw(); } diff --git a/musicdb-client/src/gui_screen.rs b/musicdb-client/src/gui_screen.rs index 9b51e4e..ddf28e9 100755 --- a/musicdb-client/src/gui_screen.rs +++ b/musicdb-client/src/gui_screen.rs @@ -40,7 +40,7 @@ pub struct GuiScreen { c_notif_overlay: NotifOverlay, c_idle_display: IdleDisplay, c_status_bar: StatusBar, - c_settings: Settings, + pub c_settings: Settings, pub c_main_view: Panel<( Button<[Label; 1]>, Button<[Label; 1]>, @@ -173,8 +173,19 @@ impl GuiScreen { 0.0 } } - fn not_idle(&mut self) { + pub fn not_idle(&mut self) { self.last_interaction = Instant::now(); + if self.idle.target > 0.0 { + if self.idle.value < 1.0 { + self.idle.target = 0.0; + } else { + self.c_idle_display.c_idle_exit_hint.config_mut().enabled = true; + } + } + } + pub fn unidle(&mut self) { + self.not_idle(); + self.c_idle_display.c_idle_exit_hint.config_mut().enabled = false; self.idle.target = 0.0; } fn idle_check(&mut self) { @@ -253,9 +264,36 @@ impl GuiElem for GuiScreen { // skip idle_check if paused or queue is empty self.idle_check(); } + // show/hide idle_exit_hint + let idle_exit_anim = if self.c_idle_display.c_idle_exit_hint.config().enabled { + let hide = info + .time + .duration_since(self.last_interaction) + .as_secs_f32() + / 3.0; + let cv = if hide >= 1.0 { + self.c_idle_display.c_idle_exit_hint.config_mut().enabled = false; + false + } else { + let v = hide * hide; + let w = 0.15; + let h = 0.05; + let dx = w * v; + let dy = h * v; + self.c_idle_display.c_idle_exit_hint.config_mut().pos = + Rectangle::from_tuples((-dx, -dy), (w - dx, h - dy)); + true + }; + if let Some(h) = &info.helper { + h.set_cursor_visible(cv); + } + cv + } else { + false + }; // request_redraw for animations - let idle_changed = self.idle.update(info.time, info.no_animations); - if idle_changed || self.settings.1.is_some() { + let idle_changed = self.idle.update(info.time, info.high_performance); + if idle_changed || idle_exit_anim || self.settings.1.is_some() { if let Some(h) = &info.helper { h.request_redraw() } @@ -264,7 +302,7 @@ impl GuiElem for GuiScreen { if idle_changed { let enable_normal_ui = self.idle.value < 1.0; self.c_main_view.config_mut().enabled = enable_normal_ui; - self.c_settings.config_mut().enabled = enable_normal_ui; + // self.c_settings.config_mut().enabled = enable_normal_ui; self.c_status_bar.config_mut().enabled = enable_normal_ui; if let Some(h) = &info.helper { h.set_cursor_visible(enable_normal_ui); diff --git a/musicdb-client/src/gui_settings.rs b/musicdb-client/src/gui_settings.rs index 6c25563..77d3182 100755 --- a/musicdb-client/src/gui_settings.rs +++ b/musicdb-client/src/gui_settings.rs @@ -8,7 +8,7 @@ use crate::{ pub struct Settings { pub config: GuiElemCfg, - c_scroll_box: ScrollBox, + pub c_scroll_box: ScrollBox, c_background: Panel<()>, } impl Settings { @@ -47,13 +47,13 @@ impl Settings { } } } -struct SettingsContent { - back_button: Button<[Label; 1]>, - opacity: Panel<(Label, Slider)>, - animations_toggle: Panel<(Label, Button<[Label; 1]>)>, - line_height: Panel<(Label, Slider)>, - scroll_sensitivity: Panel<(Label, Slider)>, - idle_time: Panel<(Label, Slider)>, +pub struct SettingsContent { + pub back_button: Button<[Label; 1]>, + pub opacity: Panel<(Label, Slider)>, + pub performance_toggle: Panel<(Label, Button<[Label; 1]>)>, + pub line_height: Panel<(Label, Slider)>, + pub scroll_sensitivity: Panel<(Label, Slider)>, + pub idle_time: Panel<(Label, Slider)>, } impl GuiElemChildren for SettingsContent { fn iter(&mut self) -> Box + '_> { @@ -61,7 +61,7 @@ impl GuiElemChildren for SettingsContent { [ self.back_button.elem_mut(), self.opacity.elem_mut(), - self.animations_toggle.elem_mut(), + self.performance_toggle.elem_mut(), self.line_height.elem_mut(), self.scroll_sensitivity.elem_mut(), self.idle_time.elem_mut(), @@ -75,7 +75,7 @@ impl GuiElemChildren for SettingsContent { } impl SettingsContent { pub fn new( - no_animations: bool, + high_performance: bool, line_height: f32, _scroll_sensitivity_pixels: f64, scroll_sensitivity_lines: f64, @@ -120,12 +120,12 @@ impl SettingsContent { }, ), ), - animations_toggle: Panel::new( + performance_toggle: Panel::new( GuiElemCfg::default(), ( Label::new( GuiElemCfg::at(Rectangle::from_tuples((0.0, 0.0), (0.33, 1.0))), - "Animations".to_string(), + "Power Saver".to_string(), Color::WHITE, None, Vec2::new(1.0, 0.5), @@ -134,18 +134,18 @@ impl SettingsContent { GuiElemCfg::at(Rectangle::from_tuples((0.75, 0.0), (1.0, 1.0))), |b| { let text = b.children[0].content.text(); - let ad = if text == "On" { + let ad = if text.starts_with("On") { *text = "Off".to_string(); - true + false } else { *text = "On".to_string(); - false + true }; - vec![GuiAction::SetAnimationsDisabled(ad)] + vec![GuiAction::SetHighPerformance(ad)] }, [Label::new( GuiElemCfg::default(), - if no_animations { "Off" } else { "On" }.to_string(), + if high_performance { "On" } else { "Off" }.to_string(), Color::WHITE, None, Vec2::new(0.5, 0.5), diff --git a/musicdb-client/src/gui_statusbar.rs b/musicdb-client/src/gui_statusbar.rs index 9d25c32..7b3755b 100644 --- a/musicdb-client/src/gui_statusbar.rs +++ b/musicdb-client/src/gui_statusbar.rs @@ -66,7 +66,7 @@ impl GuiElem for StatusBar { // move children to make space for cover let ar_updated = self .cover_aspect_ratio - .update(info.time.clone(), info.no_animations); + .update(info.time.clone(), info.high_performance); if ar_updated || info.pos.size() != self.config.pixel_pos.size() { if let Some(h) = &info.helper { h.request_redraw();