diff --git a/docs/modules/Launcher.md b/docs/modules/Launcher.md index 14040d29..91fd6104 100644 --- a/docs/modules/Launcher.md +++ b/docs/modules/Launcher.md @@ -12,13 +12,15 @@ Optionally displays a launchable set of favourites. > Type: `launcher` -| | Type | Default | Description | -|--------------|------------|---------|-----------------------------------------------------------------------------------------------------| -| `favorites` | `string[]` | `[]` | List of app IDs (or classes) to always show at the start of the launcher | -| `show_names` | `boolean` | `false` | Whether to show app names on the button label. Names will still show on tooltips when set to false. | -| `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 | +| | Type | Default | Description | +|----------------------|------------|---------|-----------------------------------------------------------------------------------------------------| +| `favorites` | `string[]` | `[]` | List of app IDs (or classes) to always show at the start of the launcher | +| `show_names` | `boolean` | `false` | Whether to show app names on the button label. Names will still show on tooltips when set to false. | +| `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. | +
JSON 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 07d7fb38..aab8b406 100644 --- a/src/modules/launcher/item.rs +++ b/src/modules/launcher/item.rs @@ -195,24 +195,31 @@ impl ItemButton { style_context.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 70e4b56f..4fb6ce87 100644 --- a/src/modules/launcher/mod.rs +++ b/src/modules/launcher/mod.rs @@ -54,6 +54,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, + /// See [common options](module-level-options#common-options). #[serde(flatten)] pub common: Option, @@ -86,6 +92,7 @@ pub enum ItemEvent { FocusItem(String), FocusWindow(usize), OpenItem(String), + MinimizeItem(String), } enum ItemOrWindow { @@ -265,6 +272,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 { @@ -293,8 +301,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() @@ -313,7 +323,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); + } } } }