From 64b953ce5ed8c328e9f78aa676ec2937adeb1c62 Mon Sep 17 00:00:00 2001 From: pachliopta <111627835+pachliopta@users.noreply.github.com> Date: Sun, 10 Nov 2024 16:20:28 +1100 Subject: [PATCH] feat(launcher): add option to minimize window if focused --- docs/modules/Launcher.md | 1 + src/clients/wayland/mod.rs | 15 +++++++++++++++ .../wayland/wlr_foreign_toplevel/handle.rs | 5 +++++ .../wayland/wlr_foreign_toplevel/mod.rs | 9 +++++++++ src/modules/launcher/item.rs | 17 ++++++++++++----- src/modules/launcher/mod.rs | 18 ++++++++++++++++-- 6 files changed, 58 insertions(+), 7 deletions(-) diff --git a/docs/modules/Launcher.md b/docs/modules/Launcher.md index f5bba433..c6faabad 100644 --- a/docs/modules/Launcher.md +++ b/docs/modules/Launcher.md @@ -19,6 +19,7 @@ Optionally displays a launchable set of favourites. | `show_icons` | `boolean` | `true` | Whether to show app icons on the button. | | `icon_size` | `integer` | `32` | Size to render icon at (image icons only). | | `reversed` | `boolean` | `false` | Whether to reverse the order of favorites/items | +| `minimize_focused` | `boolean` | `true` | Whether to minimize a focused window when its icon is clicked. Only minimizes single windows. | | `truncate.mode` | `'start'` or `'middle'` or `'end'` or `off` | `end` | The location of the ellipses and where to truncate text from. Applies to application names when `show_names` is enabled. | | `truncate.length` | `integer` | `null` | The fixed width (in chars) of the widget. Leave blank to let GTK automatically handle. | | `truncate.max_length` | `integer` | `null` | The maximum number of characters before truncating. Leave blank to let GTK automatically handle. | diff --git a/src/clients/wayland/mod.rs b/src/clients/wayland/mod.rs index ca7c367b..dc201f51 100644 --- a/src/clients/wayland/mod.rs +++ b/src/clients/wayland/mod.rs @@ -76,6 +76,8 @@ pub enum Request { ToplevelInfoAll, #[cfg(feature = "launcher")] ToplevelFocus(usize), + #[cfg(feature = "launcher")] + ToplevelMinimize(usize), #[cfg(feature = "clipboard")] CopyToClipboard(ClipboardItem), @@ -350,6 +352,19 @@ impl Environment { send!(env.response_tx, Response::Ok); } + #[cfg(feature = "launcher")] + Msg(Request::ToplevelMinimize(id)) => { + let handle = env + .handles + .iter() + .find(|handle| handle.info().map_or(false, |info| info.id == id)); + + if let Some(handle) = handle { + handle.minimize(); + } + + send!(env.response_tx, Response::Ok); + } #[cfg(feature = "clipboard")] Msg(Request::CopyToClipboard(item)) => { env.copy_to_clipboard(item); diff --git a/src/clients/wayland/wlr_foreign_toplevel/handle.rs b/src/clients/wayland/wlr_foreign_toplevel/handle.rs index 30ec99da..779d2aee 100644 --- a/src/clients/wayland/wlr_foreign_toplevel/handle.rs +++ b/src/clients/wayland/wlr_foreign_toplevel/handle.rs @@ -33,6 +33,11 @@ impl ToplevelHandle { trace!("Activating handle"); self.handle.activate(seat); } + + pub fn minimize(&self) { + trace!("Minimizing handle"); + self.handle.set_minimized(); + } } #[derive(Debug, Default)] diff --git a/src/clients/wayland/wlr_foreign_toplevel/mod.rs b/src/clients/wayland/wlr_foreign_toplevel/mod.rs index 107ca700..c9baf0fe 100644 --- a/src/clients/wayland/wlr_foreign_toplevel/mod.rs +++ b/src/clients/wayland/wlr_foreign_toplevel/mod.rs @@ -36,6 +36,15 @@ impl Client { } } + /// Minimizes the toplevel with the provided ID. + #[cfg(feature = "launcher")] + pub fn toplevel_minimize(&self, handle_id: usize) { + match self.send_request(Request::ToplevelMinimize(handle_id)) { + Response::Ok => (), + _ => unreachable!(), + } + } + /// Subscribes to events from toplevels. pub fn subscribe_toplevels(&self) -> broadcast::Receiver { self.toplevel_channel.0.subscribe() diff --git a/src/modules/launcher/item.rs b/src/modules/launcher/item.rs index 8fa200bc..1e2203bf 100644 --- a/src/modules/launcher/item.rs +++ b/src/modules/launcher/item.rs @@ -193,24 +193,31 @@ impl ItemButton { button.add_class("focused"); } + let menu_state = Rc::new(RwLock::new(MenuState { + num_windows: item.windows.len(), + })); + { let app_id = item.app_id.clone(); let tx = controller_tx.clone(); + let menu_state = menu_state.clone(); button.connect_clicked(move |button| { // lazy check :| TODO: Improve this let style_context = button.style_context(); if style_context.has_class("open") { - try_send!(tx, ItemEvent::FocusItem(app_id.clone())); + let menu_state = read_lock!(menu_state); + + if style_context.has_class("focused") && menu_state.num_windows == 1 { + try_send!(tx, ItemEvent::MinimizeItem(app_id.clone())); + } else { + try_send!(tx, ItemEvent::FocusItem(app_id.clone())); + } } else { try_send!(tx, ItemEvent::OpenItem(app_id.clone())); } }); } - let menu_state = Rc::new(RwLock::new(MenuState { - num_windows: item.windows.len(), - })); - { let app_id = item.app_id.clone(); let tx = tx.clone(); diff --git a/src/modules/launcher/mod.rs b/src/modules/launcher/mod.rs index 0ed1c35a..2359aeb6 100644 --- a/src/modules/launcher/mod.rs +++ b/src/modules/launcher/mod.rs @@ -56,6 +56,12 @@ pub struct LauncherModule { #[serde(default = "crate::config::default_false")] reversed: bool, + /// Whether to minimize a window if it is focused when clicked. + /// + /// **Default**: `true` + #[serde(default = "crate::config::default_true")] + minimize_focused: bool, + // -- common -- /// Truncate application names on the bar if they get too long. /// See [truncate options](module-level-options#truncate-mode). @@ -111,6 +117,7 @@ pub enum ItemEvent { FocusItem(String), FocusWindow(usize), OpenItem(String), + MinimizeItem(String), } enum ItemOrWindow { @@ -290,6 +297,7 @@ impl Module for LauncherModule { }); // listen to ui events + let minimize_focused = self.minimize_focused; let wl = context.client::(); spawn(async move { while let Some(event) = rx.recv().await { @@ -318,8 +326,10 @@ impl Module for LauncherModule { } else { send_async!(tx, ModuleUpdateEvent::ClosePopup); + let minimize_window = matches!(event, ItemEvent::MinimizeItem(_)); + let id = match event { - ItemEvent::FocusItem(app_id) => { + ItemEvent::FocusItem(app_id) | ItemEvent::MinimizeItem(app_id) => { lock!(items).get(&app_id).and_then(|item| { item.windows .iter() @@ -338,7 +348,11 @@ impl Module for LauncherModule { .find_map(|(_, item)| item.windows.get(&id)) { debug!("Focusing window {id}: {}", window.name); - wl.toplevel_focus(window.id); + if minimize_window && minimize_focused { + wl.toplevel_minimize(window.id); + } else { + wl.toplevel_focus(window.id); + } } } }