From d15f178f2713fed771f0f965933773cda222afed Mon Sep 17 00:00:00 2001 From: Alexander Mohr Date: Mon, 5 May 2025 15:24:50 +0200 Subject: [PATCH] fix multi monitor --- README.md | 1 - src/lib/config.rs | 19 ------- src/lib/gui.rs | 141 ++++++++-------------------------------------- 3 files changed, 24 insertions(+), 137 deletions(-) diff --git a/README.md b/README.md index 29e26e6..3c66e01 100644 --- a/README.md +++ b/README.md @@ -44,7 +44,6 @@ The standard view will show `ssh` and `drun`, for other modes the following pref ### New config / command line options * fuzzy-length: Defines how long a string must be to be considered for fuzzy match * row-box-orientation: Allows aligning values vertically to place the label below the icon -* configurable animation time ### New Styling options diff --git a/src/lib/config.rs b/src/lib/config.rs index 2584856..901b3bb 100644 --- a/src/lib/config.rs +++ b/src/lib/config.rs @@ -325,16 +325,6 @@ pub struct Config { #[clap(long = "row-box-orientation")] row_bow_orientation: Option, - /// Defines how long it takes for the show animation to finish - /// Defaults to 70ms - #[clap(long = "show-animation-time")] - show_animation_time: Option, - - /// Defines how long it takes for the hide animation to finish - /// Defaults to 100ms - #[clap(long = "hide-animation-time")] - hide_animation_time: Option, - #[clap(long = "line-wrap")] line_wrap: Option, } @@ -436,15 +426,6 @@ impl Config { self.width.clone().unwrap_or("50%".to_owned()) } - #[must_use] - pub fn show_animation_time(&self) -> u64 { - self.show_animation_time.unwrap_or(10) - } - - #[must_use] - pub fn hide_animation_time(&self) -> u64 { - self.hide_animation_time.unwrap_or(10) - } #[must_use] pub fn row_bow_orientation(&self) -> Orientation { self.row_bow_orientation.unwrap_or(Orientation::Horizontal) diff --git a/src/lib/gui.rs b/src/lib/gui.rs index 6c0ceca..40d9a3b 100644 --- a/src/lib/gui.rs +++ b/src/lib/gui.rs @@ -8,7 +8,7 @@ use crossbeam::channel; use crossbeam::channel::Sender; use gdk4::gio::File; use gdk4::glib::{Propagation, timeout_add_local}; -use gdk4::prelude::{Cast, DisplayExt, MonitorExt}; +use gdk4::prelude::{Cast, DisplayExt, MonitorExt, SurfaceExt}; use gdk4::{Display, Key}; use gtk4::glib::ControlFlow; use gtk4::prelude::{ @@ -296,11 +296,23 @@ fn build_ui( log::debug!("got items after {:?}", wait_for_items.elapsed()); build_ui_from_menu_items(&ui_elements, &meta, provider_elements); + let animate_cfg = config.clone(); + let animate_window = ui_elements.window.clone(); + timeout_add_local(Duration::from_millis(5), move || { + if !animate_window.is_active() { + return ControlFlow::Continue; + } + animate_window.set_opacity(1.0); + window_show_resize(&animate_cfg.clone(), &animate_window); + ControlFlow::Break + }); + + // hide the fact that we are starting with a small window + ui_elements.window.set_opacity(0.01); let window_start = Instant::now(); - ui_elements.window.show(); + ui_elements.window.present(); log::debug!("window show took {:?}", window_start.elapsed()); - animate_window_show(config, ui_elements.window.clone()); log::debug!("Building UI took {:?}", start.elapsed(),); } @@ -473,7 +485,7 @@ fn handle_key_press( if let Err(e) = meta.selected_sender.send(Err(Error::NoSelection)) { log::error!("failed to send message {e}"); } - close_gui(ui.app.clone(), ui.window.clone(), &meta.config); + close_gui(&ui.app); } Key::Return => { let search_lock = ui.search_text.lock().unwrap(); @@ -580,11 +592,9 @@ fn sort_menu_items_by_score( } } -fn animate_window_show(config: &Config, window: ApplicationWindow) { +fn window_show_resize(config: &Config, window: &ApplicationWindow) { if let Some(surface) = window.surface() { - let display = window.display(); - - // todo this does not work for multi monitor systems + let display = surface.display(); let monitor = display.monitor_at_surface(&surface); if let Some(monitor) = monitor { let geometry = monitor.geometry(); @@ -601,117 +611,14 @@ fn animate_window_show(config: &Config, window: ApplicationWindow) { "monitor geometry: {geometry:?}, target_height {target_height}, target_width {target_width}" ); - let animation_start = Instant::now(); - animate_window( - window, - config.show_animation_time(), - target_height, - target_width, - move || { - log::debug!("animation done after {:?}", animation_start.elapsed()); - }, - ); - } - } -} -fn animate_window_close(config: &Config, window: ApplicationWindow, on_done_func: Func) -where - Func: Fn() + 'static, -{ - // todo the target size might not work for higher dpi displays or bigger resolutions - window.set_child(Widget::NONE); - - animate_window(window, config.hide_animation_time(), 10, 10, on_done_func); -} - -fn ease_in_out_cubic(t: f32) -> f32 { - if t < 0.7 { - 10.0 * t * t * t - } else { - 1.0 - (-2.0 * t + 2.0).powi(3) - } -} - -#[allow(clippy::cast_possible_truncation)] -#[allow(clippy::cast_precision_loss)] -fn animate_window( - window: ApplicationWindow, - animation_time: u64, - target_height: i32, - target_width: i32, - on_done_func: Func, -) where - Func: Fn() + 'static, -{ - if animation_time == 0 { - window.set_width_request(target_width); - window.set_height_request(target_height); - on_done_func(); - return; - } - - let allocation = window.allocation(); - - // Define animation parameters - let animation_step_length = Duration::from_millis(20); - - // Start positions (initial window dimensions) - let start_width = allocation.width() as f32; - let start_height = allocation.height() as f32; - - // Calculate the change in width and height - let delta_width = target_width as f32 - start_width; - let delta_height = target_height as f32 - start_height; - - // Animation time starts when the timeout is ran for the first time - let mut start_time: Option = None; - - let mut last_t = 0.0; - - let before_animation = Instant::now(); - timeout_add_local(animation_step_length, move || { - if !window.is_visible() { - return ControlFlow::Continue; - } - let start_time = start_time.unwrap_or_else(|| { - let now = Instant::now(); - start_time = Some(now); - log::debug!("animation started after {:?}", before_animation.elapsed()); - now - }); - - let elapsed_us = start_time.elapsed().as_micros() as f32; - let t = (elapsed_us / (animation_time * 1000) as f32).min(1.0); - - // Skip if there's no meaningful change in progress - if (t - last_t).abs() < 0.001 && t < 1.0 { - return ControlFlow::Continue; - } - last_t = t; - - let eased_t = ease_in_out_cubic(t); - - let current_width = start_width + delta_width * eased_t; - let current_height = start_height + delta_height * eased_t; - - let rounded_width = current_width.round() as i32; - let rounded_height = current_height.round() as i32; - - if t >= 1.0 || rounded_height > target_height || rounded_width > target_width { window.set_width_request(target_width); window.set_height_request(target_height); - on_done_func(); - ControlFlow::Break - } else { - window.set_width_request(rounded_width); - window.set_height_request(rounded_height); - ControlFlow::Continue } - }); + } } -fn close_gui(app: Application, window: ApplicationWindow, config: &Config) { - animate_window_close(config, window, move || app.quit()); +fn close_gui(app: &Application) { + app.quit(); } fn handle_selected_item( @@ -729,7 +636,7 @@ where log::error!("failed to send message {e}"); } - close_gui(ui.app.clone(), ui.window.clone(), &meta.config); + close_gui(&ui.app); return Ok(()); } else if let Some(s) = ui.main_box.selected_children().into_iter().next() { let list_items = ui.menu_rows.lock().unwrap(); @@ -738,7 +645,7 @@ where if let Err(e) = meta.selected_sender.send(Ok(item.clone())) { log::error!("failed to send message {e}"); } - close_gui(ui.app.clone(), ui.window.clone(), &meta.config); + close_gui(&ui.app); return Ok(()); } } @@ -759,7 +666,7 @@ where if let Err(e) = meta.selected_sender.send(Ok(item.clone())) { log::error!("failed to send message {e}"); } - close_gui(ui.app.clone(), ui.window.clone(), &meta.config); + close_gui(&ui.app); Ok(()) } else { Err("selected item cannot be resolved".to_owned())