From b85818cb9718d74b9994fefa9012a3b385f9a12f Mon Sep 17 00:00:00 2001 From: William Calliari Date: Mon, 16 Sep 2024 22:34:59 +0200 Subject: [PATCH] Add configuration option for close_tab_on_middle_mouse --- cosmic-comp-config/src/lib.rs | 15 +++++++++++++++ src/config/mod.rs | 9 ++++++++- src/shell/element/mod.rs | 4 +++- src/shell/element/stack.rs | 19 +++++++++++++------ src/shell/element/stack/tab.rs | 16 +++++++++++++--- src/shell/layout/floating/mod.rs | 7 +++++-- src/shell/layout/tiling/mod.rs | 15 ++++++++++----- src/shell/mod.rs | 29 ++++++++++++++++++++--------- src/shell/workspace.rs | 4 +++- 9 files changed, 90 insertions(+), 28 deletions(-) diff --git a/cosmic-comp-config/src/lib.rs b/cosmic-comp-config/src/lib.rs index 5a656e52..870b98b4 100644 --- a/cosmic-comp-config/src/lib.rs +++ b/cosmic-comp-config/src/lib.rs @@ -21,6 +21,8 @@ pub struct CosmicCompConfig { /// If set to Global, autotile applies to all windows in all workspaces /// If set to PerWorkspace, autotile only applies to new windows, and new workspaces pub autotile_behavior: TileBehavior, + /// Configure behavior of the stack layout. + pub stack_behavior: StackBehavior, /// Active hint enabled pub active_hint: bool, /// Enables changing keyboard focus to windows when the cursor passes into them @@ -55,6 +57,7 @@ impl Default for CosmicCompConfig { xkb_config: Default::default(), autotile: Default::default(), autotile_behavior: Default::default(), + stack_behavior: StackBehavior::default(), active_hint: true, focus_follows_cursor: false, cursor_follows_focus: false, @@ -105,3 +108,15 @@ fn default_repeat_rate() -> u32 { fn default_repeat_delay() -> u32 { 600 } + + +#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)] +pub struct StackBehavior { + pub close_tab_on_middle_click: bool, +} + +impl Default for StackBehavior { + fn default() -> Self { + Self { close_tab_on_middle_click: true } + } +} \ No newline at end of file diff --git a/src/config/mod.rs b/src/config/mod.rs index 77db2951..4eb1273d 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -41,7 +41,7 @@ mod types; pub use self::types::*; use cosmic::config::CosmicTk; use cosmic_comp_config::{ - input::InputConfig, workspace::WorkspaceConfig, CosmicCompConfig, TileBehavior, XkbConfig, + input::InputConfig, workspace::WorkspaceConfig, CosmicCompConfig, StackBehavior, TileBehavior, XkbConfig }; #[derive(Debug)] @@ -690,6 +690,13 @@ fn config_changed(config: cosmic_config::Config, keys: Vec, state: &mut ); } } + "stack_behavior" => { + let new = get_config::(&config, "stack_behavior"); + if new != state.common.config.cosmic_conf.stack_behavior { + state.common.config.cosmic_conf.stack_behavior = new; + state.common.update_config(); + } + } "active_hint" => { let new = get_config::(&config, "active_hint"); if new != state.common.config.cosmic_conf.active_hint { diff --git a/src/shell/element/mod.rs b/src/shell/element/mod.rs index 4a2cfb67..c7ab218c 100644 --- a/src/shell/element/mod.rs +++ b/src/shell/element/mod.rs @@ -7,6 +7,7 @@ use crate::{ utils::{iced::IcedElementInternal, prelude::*}, }; use calloop::LoopHandle; +use cosmic_comp_config::StackBehavior; use id_tree::NodeId; use smithay::{ backend::{ @@ -592,6 +593,7 @@ impl CosmicMapped { &mut self, (output, overlap): (&Output, Rectangle), theme: cosmic::Theme, + config: StackBehavior, ) { match &self.element { CosmicMappedInternal::Window(window) => { @@ -599,7 +601,7 @@ impl CosmicMapped { let activated = surface.is_activated(true); let handle = window.loop_handle(); - let stack = CosmicStack::new(std::iter::once(surface), handle, theme); + let stack = CosmicStack::new(std::iter::once(surface), handle, theme, config); if let Some(geo) = self.last_geometry.lock().unwrap().clone() { stack.set_geometry(geo.to_global(&output)); } diff --git a/src/shell/element/stack.rs b/src/shell/element/stack.rs index 8b5f94ed..ff033c7f 100644 --- a/src/shell/element/stack.rs +++ b/src/shell/element/stack.rs @@ -20,6 +20,7 @@ use cosmic::{ iced_widget::scrollable::AbsoluteOffset, theme, widget as cosmic_widget, Apply, Element as CosmicElement, Theme, }; +use cosmic_comp_config::StackBehavior; use cosmic_settings_config::shortcuts; use once_cell::sync::Lazy; use shortcuts::action::{Direction, FocusDirection}; @@ -102,6 +103,8 @@ pub struct CosmicStackInternal { last_seat: Arc, Serial)>>>, geometry: Arc>>>, mask: Arc>>, + + behavior: StackBehavior, } impl CosmicStackInternal { @@ -129,6 +132,7 @@ impl CosmicStack { windows: impl Iterator, handle: LoopHandle<'static, crate::state::State>, theme: cosmic::Theme, + behavior: StackBehavior, ) -> CosmicStack { let windows = windows.map(Into::into).collect::>(); assert!(!windows.is_empty()); @@ -155,6 +159,7 @@ impl CosmicStack { last_seat: Arc::new(Mutex::new(None)), geometry: Arc::new(Mutex::new(None)), mask: Arc::new(Mutex::new(None)), + behavior, }, (width, TAB_HEIGHT), handle, @@ -680,10 +685,6 @@ impl TabMessage for Message { fn scrolled() -> Self { Message::Scrolled } - - fn close(idx: usize) -> Self { - Message::Close(idx) - } } impl Program for CosmicStackInternal { @@ -894,14 +895,20 @@ impl Program for CosmicStackInternal { windows.iter().enumerate().map(|(i, w)| { let user_data = w.user_data(); user_data.insert_if_missing(Id::unique); - Tab::new( + let mut tab = Tab::new( w.title(), w.app_id(), user_data.get::().unwrap().clone(), ) .on_press(Message::PotentialTabDragStart(i)) .on_right_click(Message::TabMenu(i)) - .on_close(Message::Close(i)) + .on_close(Message::Close(i)); + + if self.behavior.close_tab_on_middle_click { + tab = tab.on_middle_click(Message::Close(i)); + } + + tab }), active, windows[active].is_activated(false), diff --git a/src/shell/element/stack/tab.rs b/src/shell/element/stack/tab.rs index 90c6530e..6304de7d 100644 --- a/src/shell/element/stack/tab.rs +++ b/src/shell/element/stack/tab.rs @@ -132,7 +132,6 @@ impl From for theme::Container { pub trait TabMessage: Clone { fn activate(idx: usize) -> Self; - fn close(idx: usize) -> Self; fn scroll_further() -> Self; fn scroll_back() -> Self; @@ -148,6 +147,7 @@ pub struct Tab { close_message: Option, press_message: Option, right_click_message: Option, + middle_click_message: Option, rule_theme: TabRuleTheme, background_theme: TabBackgroundTheme, active: bool, @@ -163,6 +163,7 @@ impl Tab { close_message: None, press_message: None, right_click_message: None, + middle_click_message: None, rule_theme: TabRuleTheme::Default, background_theme: TabBackgroundTheme::Default, active: false, @@ -179,6 +180,11 @@ impl Tab { self } + pub fn on_middle_click(mut self, message: Message) -> Self { + self.middle_click_message = Some(message); + self + } + pub fn on_close(mut self, message: Message) -> Self { self.close_message = Some(message); self @@ -255,6 +261,7 @@ impl Tab { elements: items, press_message: self.press_message, right_click_message: self.right_click_message, + middle_click_message: self.middle_click_message, } } } @@ -274,6 +281,7 @@ pub(super) struct TabInternal<'a, Message: TabMessage> { elements: Vec>, press_message: Option, right_click_message: Option, + middle_click_message: Option, } impl<'a, Message> Widget for TabInternal<'a, Message> @@ -424,8 +432,10 @@ where event, event::Event::Mouse(mouse::Event::ButtonReleased(mouse::Button::Middle)) ) { - shell.publish(Message::close(self.idx)); - return event::Status::Captured; + if let Some(message) = self.middle_click_message.clone() { + shell.publish(message); + return event::Status::Captured; + } } } diff --git a/src/shell/layout/floating/mod.rs b/src/shell/layout/floating/mod.rs index 140fb5d5..da47ab15 100644 --- a/src/shell/layout/floating/mod.rs +++ b/src/shell/layout/floating/mod.rs @@ -6,6 +6,7 @@ use std::{ time::{Duration, Instant}, }; +use cosmic_comp_config::StackBehavior; use cosmic_settings_config::shortcuts::action::ResizeDirection; use keyframe::{ease, functions::EaseInOutCubic}; use smithay::{ @@ -886,6 +887,7 @@ impl FloatingLayout { &mut self, mapped: &CosmicMapped, mut focus_stack: FocusStackMut, + stack_behavior: &StackBehavior, ) -> Option { if !self.space.elements().any(|m| m == mapped) { return None; @@ -899,7 +901,7 @@ impl FloatingLayout { if mapped.is_window() { // if it is just a window self.space.unmap_elem(&mapped); - mapped.convert_to_stack((&output, mapped.bbox()), self.theme.clone()); + mapped.convert_to_stack((&output, mapped.bbox()), self.theme.clone(), stack_behavior.clone()); self.map_internal( mapped.clone(), Some(location.as_local()), @@ -957,13 +959,14 @@ impl FloatingLayout { &mut self, seat: &Seat, focus_stack: FocusStackMut, + stack_behavior: &StackBehavior, ) -> Option { let Some(KeyboardFocusTarget::Element(elem)) = seat.get_keyboard().unwrap().current_focus() else { return None; }; - self.toggle_stacking(&elem, focus_stack) + self.toggle_stacking(&elem, focus_stack, stack_behavior) } pub fn move_element( diff --git a/src/shell/layout/tiling/mod.rs b/src/shell/layout/tiling/mod.rs index feeebcd8..1cdd24ff 100644 --- a/src/shell/layout/tiling/mod.rs +++ b/src/shell/layout/tiling/mod.rs @@ -38,6 +38,7 @@ use crate::{ }, }; +use cosmic_comp_config::StackBehavior; use cosmic_settings_config::shortcuts::action::{FocusDirection, ResizeDirection}; use id_tree::{InsertBehavior, MoveBehavior, Node, NodeId, NodeIdError, RemoveBehavior, Tree}; use keyframe::{ @@ -135,6 +136,7 @@ pub struct TilingLayout { swapping_stack_surface_id: Id, last_overview_hover: Option<(Option, TargetZone)>, pub theme: cosmic::Theme, + stack_behavior: StackBehavior, } #[derive(Debug, Clone, PartialEq)] @@ -342,7 +344,7 @@ pub struct MinimizedTilingState { } impl TilingLayout { - pub fn new(theme: cosmic::Theme, output: &Output) -> TilingLayout { + pub fn new(theme: cosmic::Theme, output: &Output, stack_behavior: StackBehavior) -> TilingLayout { TilingLayout { queue: TreeQueue { trees: { @@ -358,6 +360,7 @@ impl TilingLayout { swapping_stack_surface_id: Id::new(), last_overview_hover: None, theme, + stack_behavior, } } @@ -2121,6 +2124,7 @@ impl TilingLayout { &mut self, mapped: &CosmicMapped, mut focus_stack: FocusStackMut, + stack_behavior: &StackBehavior ) -> Option { let gaps = self.gaps(); @@ -2137,7 +2141,7 @@ impl TilingLayout { // if it is just a window match tree.get_mut(&node_id).unwrap().data_mut() { Data::Mapped { mapped, .. } => { - mapped.convert_to_stack((&self.output, mapped.bbox()), self.theme.clone()); + mapped.convert_to_stack((&self.output, mapped.bbox()), self.theme.clone(), stack_behavior.clone()); focus_stack.append(&mapped); KeyboardFocusTarget::Element(mapped.clone()) } @@ -2238,6 +2242,7 @@ impl TilingLayout { &mut self, seat: &Seat, mut focus_stack: FocusStackMut, + stack_behavior: &StackBehavior, ) -> Option { let gaps = self.gaps(); @@ -2251,7 +2256,7 @@ impl TilingLayout { { match last_active_data { FocusedNodeData::Window(mapped) => { - return self.toggle_stacking(&mapped, focus_stack); + return self.toggle_stacking(&mapped, focus_stack, stack_behavior); } FocusedNodeData::Group(_, _) => { let mut handle = None; @@ -2274,7 +2279,7 @@ impl TilingLayout { return None; } let handle = handle.unwrap(); - let stack = CosmicStack::new(surfaces.into_iter(), handle, self.theme.clone()); + let stack = CosmicStack::new(surfaces.into_iter(), handle, self.theme.clone(), stack_behavior.clone()); for child in tree .children_ids(&last_active) @@ -2741,7 +2746,7 @@ impl TilingLayout { Some(TargetZone::WindowStack(window_id, _)) if tree.get(&window_id).is_ok() => { match tree.get_mut(window_id).unwrap().data_mut() { Data::Mapped { mapped, .. } => { - mapped.convert_to_stack((&self.output, mapped.bbox()), self.theme.clone()); + mapped.convert_to_stack((&self.output, mapped.bbox()), self.theme.clone(), self.stack_behavior.clone()); let Some(stack) = mapped.stack_ref_mut() else { unreachable!() }; diff --git a/src/shell/mod.rs b/src/shell/mod.rs index 07acdee0..d36d9db8 100644 --- a/src/shell/mod.rs +++ b/src/shell/mod.rs @@ -12,8 +12,7 @@ use wayland_backend::server::ClientId; use crate::wayland::{handlers::data_device, protocols::workspace::WorkspaceCapabilities}; use cosmic_comp_config::{ - workspace::{WorkspaceLayout, WorkspaceMode}, - TileBehavior, + workspace::{WorkspaceLayout, WorkspaceMode}, StackBehavior, TileBehavior }; use cosmic_protocols::workspace::v1::server::zcosmic_workspace_handle_v1::{ State as WState, TilingState, @@ -319,6 +318,7 @@ pub struct WorkspaceSet { pub sticky_layer: FloatingLayout, pub minimized_windows: Vec, pub workspaces: Vec, + stack_behavior: StackBehavior, } fn create_workspace( @@ -328,6 +328,7 @@ fn create_workspace( active: bool, tiling: bool, theme: cosmic::Theme, + stack_behavior: StackBehavior, ) -> Workspace { let workspace_handle = state .create_workspace( @@ -346,7 +347,7 @@ fn create_workspace( &workspace_handle, [WorkspaceCapabilities::Activate].into_iter(), ); - Workspace::new(workspace_handle, output.clone(), tiling, theme.clone()) + Workspace::new(workspace_handle, output.clone(), tiling, theme.clone(), stack_behavior) } fn move_workspace_to_group( @@ -417,6 +418,7 @@ impl WorkspaceSet { idx: usize, tiling_enabled: bool, theme: cosmic::Theme, + stack_behavior: StackBehavior, ) -> WorkspaceSet { let group_handle = state.create_workspace_group(); let workspaces = { @@ -427,6 +429,7 @@ impl WorkspaceSet { true, tiling_enabled, theme.clone(), + stack_behavior.clone() ); workspace_set_idx(state, 1, idx, &workspace.handle); state.set_workspace_capabilities( @@ -448,6 +451,7 @@ impl WorkspaceSet { minimized_windows: Vec::new(), workspaces, output: output.clone(), + stack_behavior, } } @@ -558,6 +562,7 @@ impl WorkspaceSet { false, self.tiling_enabled, self.theme.clone(), + self.stack_behavior.clone(), ); workspace_set_idx( state, @@ -621,6 +626,7 @@ pub struct Workspaces { mode: WorkspaceMode, autotile: bool, autotile_behavior: TileBehavior, + stack_behavior: StackBehavior, theme: cosmic::Theme, } @@ -633,6 +639,7 @@ impl Workspaces { mode: config.cosmic_conf.workspaces.workspace_mode, autotile: config.cosmic_conf.autotile, autotile_behavior: config.cosmic_conf.autotile_behavior, + stack_behavior: config.cosmic_conf.stack_behavior.clone(), theme, } } @@ -661,6 +668,7 @@ impl Workspaces { self.sets.len(), self.autotile, self.theme.clone(), + self.stack_behavior.clone(), ) }); workspace_state.add_group_output(&set.group, &output); @@ -850,6 +858,7 @@ impl Workspaces { false, config.cosmic_conf.autotile, self.theme.clone(), + self.stack_behavior.clone() ), ); } @@ -3558,6 +3567,8 @@ impl Shell { seat: &Seat, window: &CosmicMapped, ) -> Option { + let behavior = self.workspaces.stack_behavior.clone(); + if let Some(set) = self .workspaces .sets @@ -3566,16 +3577,16 @@ impl Shell { { let workspace = &mut set.workspaces[set.active]; set.sticky_layer - .toggle_stacking(window, workspace.focus_stack.get_mut(seat)) + .toggle_stacking(window, workspace.focus_stack.get_mut(seat), &behavior) } else if let Some(workspace) = self.space_for_mut(window) { if workspace.tiling_layer.mapped().any(|(m, _)| m == window) { workspace .tiling_layer - .toggle_stacking(window, workspace.focus_stack.get_mut(seat)) + .toggle_stacking(window, workspace.focus_stack.get_mut(seat), &behavior) } else if workspace.floating_layer.mapped().any(|w| w == window) { workspace .floating_layer - .toggle_stacking(window, workspace.focus_stack.get_mut(seat)) + .toggle_stacking(window, workspace.focus_stack.get_mut(seat), &behavior) } else { None } @@ -3595,15 +3606,15 @@ impl Shell { if let Some(window) = maybe_window { if set.sticky_layer.mapped().any(|m| m == &window) { set.sticky_layer - .toggle_stacking_focused(seat, workspace.focus_stack.get_mut(seat)) + .toggle_stacking_focused(seat, workspace.focus_stack.get_mut(seat), &self.workspaces.stack_behavior) } else if workspace.tiling_layer.mapped().any(|(m, _)| m == &window) { workspace .tiling_layer - .toggle_stacking_focused(seat, workspace.focus_stack.get_mut(seat)) + .toggle_stacking_focused(seat, workspace.focus_stack.get_mut(seat), &self.workspaces.stack_behavior) } else if workspace.floating_layer.mapped().any(|w| w == &window) { workspace .floating_layer - .toggle_stacking_focused(seat, workspace.focus_stack.get_mut(seat)) + .toggle_stacking_focused(seat, workspace.focus_stack.get_mut(seat), &self.workspaces.stack_behavior) } else { None } diff --git a/src/shell/workspace.rs b/src/shell/workspace.rs index 44af522e..9ebfe975 100644 --- a/src/shell/workspace.rs +++ b/src/shell/workspace.rs @@ -19,6 +19,7 @@ use crate::{ }; use cosmic::theme::CosmicTheme; +use cosmic_comp_config::StackBehavior; use cosmic_protocols::workspace::v1::server::zcosmic_workspace_handle_v1::TilingState; use id_tree::Tree; use indexmap::IndexSet; @@ -238,8 +239,9 @@ impl Workspace { output: Output, tiling_enabled: bool, theme: cosmic::Theme, + stack_behavior: StackBehavior, ) -> Workspace { - let tiling_layer = TilingLayout::new(theme.clone(), &output); + let tiling_layer = TilingLayout::new(theme.clone(), &output, stack_behavior); let floating_layer = FloatingLayout::new(theme, &output); let output_name = output.name();