diff options
| author | Aram Drevekenin <aram@poor.dev> | 2024-12-16 16:03:20 +0100 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2024-12-16 16:03:20 +0100 |
| commit | dd291e2a1f9b4ee3eb1a947e1501a54b10663ea7 (patch) | |
| tree | 0ae41e8175d2a3fd260b3c510a30502f6386ac7f /zellij-utils | |
| parent | docs(changelog): multiplayer cursor fix (diff) | |
| download | sl0ppij-dd291e2a1f9b4ee3eb1a947e1501a54b10663ea7.tar.gz sl0ppij-dd291e2a1f9b4ee3eb1a947e1501a54b10663ea7.tar.xz sl0ppij-dd291e2a1f9b4ee3eb1a947e1501a54b10663ea7.zip | |
feat(ux): pin floating panes (#3876)
* working
* ui indication
* add keybinding
* add to plugin panes
* fix with multiple cursors
* toggle with the mouse
* fix e2e tests and add new one
* some cleanups
* add to layouts
* make mouse click more lenient
* allow setting a new floating pane as pinned
* make toggle work throughthe command line
* add to plugin api
* get tests to pass
* style(fmt): rustfmt
Diffstat (limited to 'zellij-utils')
24 files changed, 256 insertions, 23 deletions
diff --git a/zellij-utils/assets/config/default.kdl b/zellij-utils/assets/config/default.kdl index e7dfcf5d..15f1fd54 100644 --- a/zellij-utils/assets/config/default.kdl +++ b/zellij-utils/assets/config/default.kdl @@ -36,6 +36,7 @@ keybinds { bind "w" { ToggleFloatingPanes; SwitchToMode "Normal"; } bind "e" { TogglePaneEmbedOrFloating; SwitchToMode "Normal"; } bind "c" { SwitchToMode "RenamePane"; PaneNameInput 0;} + bind "i" { TogglePanePinned; SwitchToMode "Normal"; } } move { bind "Ctrl h" { SwitchToMode "Normal"; } diff --git a/zellij-utils/assets/prost/api.action.rs b/zellij-utils/assets/prost/api.action.rs index 651f59eb..87ca37dd 100644 --- a/zellij-utils/assets/prost/api.action.rs +++ b/zellij-utils/assets/prost/api.action.rs @@ -455,6 +455,7 @@ pub enum ActionName { CliPipe = 82, MoveTab = 83, KeybindPipe = 84, + TogglePanePinned = 85, } impl ActionName { /// String value of the enum field names used in the ProtoBuf definition. @@ -548,6 +549,7 @@ impl ActionName { ActionName::CliPipe => "CliPipe", ActionName::MoveTab => "MoveTab", ActionName::KeybindPipe => "KeybindPipe", + ActionName::TogglePanePinned => "TogglePanePinned", } } /// Creates an enum from field names used in the ProtoBuf definition. @@ -638,6 +640,7 @@ impl ActionName { "CliPipe" => Some(Self::CliPipe), "MoveTab" => Some(Self::MoveTab), "KeybindPipe" => Some(Self::KeybindPipe), + "TogglePanePinned" => Some(Self::TogglePanePinned), _ => None, } } diff --git a/zellij-utils/assets/prost/api.plugin_command.rs b/zellij-utils/assets/prost/api.plugin_command.rs index d4604c98..a023a68f 100644 --- a/zellij-utils/assets/prost/api.plugin_command.rs +++ b/zellij-utils/assets/prost/api.plugin_command.rs @@ -5,7 +5,7 @@ pub struct PluginCommand { pub name: i32, #[prost( oneof = "plugin_command::Payload", - tags = "2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89" + tags = "2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90" )] pub payload: ::core::option::Option<plugin_command::Payload>, } @@ -176,10 +176,20 @@ pub mod plugin_command { RebindKeysPayload(super::RebindKeysPayload), #[prost(message, tag = "89")] ChangeHostFolderPayload(super::ChangeHostFolderPayload), + #[prost(message, tag = "90")] + SetFloatingPanePinnedPayload(super::SetFloatingPanePinnedPayload), } } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] +pub struct SetFloatingPanePinnedPayload { + #[prost(message, optional, tag = "1")] + pub pane_id: ::core::option::Option<PaneId>, + #[prost(bool, tag = "2")] + pub should_be_pinned: bool, +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] pub struct ChangeHostFolderPayload { #[prost(string, tag = "1")] pub new_host_folder: ::prost::alloc::string::String, @@ -598,6 +608,8 @@ pub struct FloatingPaneCoordinates { pub width: ::core::option::Option<FixedOrPercentValue>, #[prost(message, optional, tag = "4")] pub height: ::core::option::Option<FixedOrPercentValue>, + #[prost(bool, optional, tag = "5")] + pub pinned: ::core::option::Option<bool>, } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] @@ -725,6 +737,7 @@ pub enum CommandName { RebindKeys = 112, ListClients = 113, ChangeHostFolder = 114, + SetFloatingPanePinned = 115, } impl CommandName { /// String value of the enum field names used in the ProtoBuf definition. @@ -850,6 +863,7 @@ impl CommandName { CommandName::RebindKeys => "RebindKeys", CommandName::ListClients => "ListClients", CommandName::ChangeHostFolder => "ChangeHostFolder", + CommandName::SetFloatingPanePinned => "SetFloatingPanePinned", } } /// Creates an enum from field names used in the ProtoBuf definition. @@ -972,6 +986,7 @@ impl CommandName { "RebindKeys" => Some(Self::RebindKeys), "ListClients" => Some(Self::ListClients), "ChangeHostFolder" => Some(Self::ChangeHostFolder), + "SetFloatingPanePinned" => Some(Self::SetFloatingPanePinned), _ => None, } } diff --git a/zellij-utils/src/cli.rs b/zellij-utils/src/cli.rs index 63348c42..1577be7c 100644 --- a/zellij-utils/src/cli.rs +++ b/zellij-utils/src/cli.rs @@ -246,6 +246,9 @@ pub enum Sessions { /// The height if the pane is floating as a bare integer (eg. 1) or percent (eg. 10%) #[clap(long, requires("floating"))] height: Option<String>, + /// Whether to pin a floating pane so that it is always on top + #[clap(long, requires("floating"))] + pinned: Option<bool>, }, /// Load a plugin #[clap(visible_alias = "p")] @@ -288,6 +291,9 @@ pub enum Sessions { /// The height if the pane is floating as a bare integer (eg. 1) or percent (eg. 10%) #[clap(long, requires("floating"))] height: Option<String>, + /// Whether to pin a floating pane so that it is always on top + #[clap(long, requires("floating"))] + pinned: Option<bool>, }, /// Edit file with default $EDITOR / $VISUAL #[clap(visible_alias = "e")] @@ -333,6 +339,9 @@ pub enum Sessions { /// The height if the pane is floating as a bare integer (eg. 1) or percent (eg. 10%) #[clap(long, requires("floating"))] height: Option<String>, + /// Whether to pin a floating pane so that it is always on top + #[clap(long, requires("floating"))] + pinned: Option<bool>, }, ConvertConfig { old_config_file: PathBuf, @@ -526,6 +535,9 @@ pub enum CliAction { /// The height if the pane is floating as a bare integer (eg. 1) or percent (eg. 10%) #[clap(long, requires("floating"))] height: Option<String>, + /// Whether to pin a floating pane so that it is always on top + #[clap(long, requires("floating"))] + pinned: Option<bool>, }, /// Open the specified file in a new zellij pane with your default EDITOR Edit { @@ -570,6 +582,9 @@ pub enum CliAction { /// The height if the pane is floating as a bare integer (eg. 1) or percent (eg. 10%) #[clap(long, requires("floating"))] height: Option<String>, + /// Whether to pin a floating pane so that it is always on top + #[clap(long, requires("floating"))] + pinned: Option<bool>, }, /// Switch input mode of all connected clients [locked|pane|tab|resize|move|search|session] SwitchMode { @@ -744,4 +759,5 @@ tail -f /tmp/my-live-logfile | zellij action pipe --name logs --plugin https://e plugin_title: Option<String>, }, ListClients, + TogglePanePinned, } diff --git a/zellij-utils/src/data.rs b/zellij-utils/src/data.rs index 1d0da685..1f3adf30 100644 --- a/zellij-utils/src/data.rs +++ b/zellij-utils/src/data.rs @@ -1661,6 +1661,7 @@ pub struct FloatingPaneCoordinates { pub y: Option<SplitSize>, pub width: Option<SplitSize>, pub height: Option<SplitSize>, + pub pinned: Option<bool>, } impl FloatingPaneCoordinates { @@ -1669,12 +1670,13 @@ impl FloatingPaneCoordinates { y: Option<String>, width: Option<String>, height: Option<String>, + pinned: Option<bool>, ) -> Option<Self> { let x = x.and_then(|x| SplitSize::from_str(&x).ok()); let y = y.and_then(|y| SplitSize::from_str(&y).ok()); let width = width.and_then(|width| SplitSize::from_str(&width).ok()); let height = height.and_then(|height| SplitSize::from_str(&height).ok()); - if x.is_none() && y.is_none() && width.is_none() && height.is_none() { + if x.is_none() && y.is_none() && width.is_none() && height.is_none() && pinned.is_none() { None } else { Some(FloatingPaneCoordinates { @@ -1682,6 +1684,7 @@ impl FloatingPaneCoordinates { y, width, height, + pinned, }) } } @@ -1898,4 +1901,5 @@ pub enum PluginCommand { }, ListClients, ChangeHostFolder(PathBuf), + SetFloatingPanePinned(PaneId, bool), // bool -> should be pinned } diff --git a/zellij-utils/src/errors.rs b/zellij-utils/src/errors.rs index ffabec26..f27ee06c 100644 --- a/zellij-utils/src/errors.rs +++ b/zellij-utils/src/errors.rs @@ -373,6 +373,8 @@ pub enum ScreenContext { BreakPanesToNewTab, BreakPanesToTabWithIndex, ListClientsToPlugin, + TogglePanePinned, + SetFloatingPanePinned, } /// Stack call representations corresponding to the different types of [`PtyInstruction`]s. diff --git a/zellij-utils/src/input/actions.rs b/zellij-utils/src/input/actions.rs index e19a0672..f6ecf096 100644 --- a/zellij-utils/src/input/actions.rs +++ b/zellij-utils/src/input/actions.rs @@ -300,6 +300,7 @@ pub enum Action { pane_title: Option<String>, }, ListClients, + TogglePanePinned, } impl Action { @@ -363,6 +364,7 @@ impl Action { y, width, height, + pinned, } => { let current_dir = get_current_dir(); // cwd should only be specified in a plugin alias if it was explicitly given to us, @@ -398,7 +400,7 @@ impl Action { name, skip_plugin_cache, cwd, - FloatingPaneCoordinates::new(x, y, width, height), + FloatingPaneCoordinates::new(x, y, width, height, pinned), )]) } else if in_place { Ok(vec![Action::NewInPlacePluginPane( @@ -440,7 +442,7 @@ impl Action { Ok(vec![Action::NewFloatingPane( Some(run_command_action), name, - FloatingPaneCoordinates::new(x, y, width, height), + FloatingPaneCoordinates::new(x, y, width, height, pinned), )]) } else if in_place { Ok(vec![Action::NewInPlacePane(Some(run_command_action), name)]) @@ -456,7 +458,7 @@ impl Action { Ok(vec![Action::NewFloatingPane( None, name, - FloatingPaneCoordinates::new(x, y, width, height), + FloatingPaneCoordinates::new(x, y, width, height, pinned), )]) } else if in_place { Ok(vec![Action::NewInPlacePane(None, name)]) @@ -476,6 +478,7 @@ impl Action { y, width, height, + pinned, } => { let mut file = file; let current_dir = get_current_dir(); @@ -494,7 +497,7 @@ impl Action { floating, in_place, start_suppressed, - FloatingPaneCoordinates::new(x, y, width, height), + FloatingPaneCoordinates::new(x, y, width, height, pinned), )]) }, CliAction::SwitchMode { input_mode } => { @@ -738,6 +741,7 @@ impl Action { }]) }, CliAction::ListClients => Ok(vec![Action::ListClients]), + CliAction::TogglePanePinned => Ok(vec![Action::TogglePanePinned]), } } pub fn launches_plugin(&self, plugin_url: &str) -> bool { diff --git a/zellij-utils/src/input/layout.rs b/zellij-utils/src/input/layout.rs index d4d8b261..33c8a7c1 100644 --- a/zellij-utils/src/input/layout.rs +++ b/zellij-utils/src/input/layout.rs @@ -738,6 +738,7 @@ pub struct FloatingPaneLayout { pub width: Option<PercentOrFixed>, pub x: Option<PercentOrFixed>, pub y: Option<PercentOrFixed>, + pub pinned: Option<bool>, pub run: Option<Run>, pub focus: Option<bool>, pub already_running: bool, @@ -752,6 +753,7 @@ impl FloatingPaneLayout { width: None, x: None, y: None, + pinned: None, run: None, focus: None, already_running: false, @@ -1697,6 +1699,7 @@ fn split_space( cols: split_dimension, rows: inherited_dimension, is_stacked: layout.children_are_stacked, + is_pinned: false, }, SplitDirection::Horizontal => PaneGeom { x: space_to_split.x, @@ -1704,6 +1707,7 @@ fn split_space( cols: inherited_dimension, rows: split_dimension, is_stacked: layout.children_are_stacked, + is_pinned: false, }, }; split_geom.push(geom); diff --git a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__layout_with_tabs_and_floating_panes.snap b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__layout_with_tabs_and_floating_panes.snap index 6981d9d7..69d57c30 100644 --- a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__layout_with_tabs_and_floating_panes.snap +++ b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__layout_with_tabs_and_floating_panes.snap @@ -1,6 +1,6 @@ --- source: zellij-utils/src/input/./unit/layout_test.rs -assertion_line: 275 +assertion_line: 305 expression: "format!(\"{:#?}\", layout)" --- Layout { @@ -47,6 +47,7 @@ Layout { width: None, x: None, y: None, + pinned: None, run: None, focus: None, already_running: false, @@ -96,6 +97,7 @@ Layout { width: None, x: None, y: None, + pinned: None, run: None, focus: None, already_running: false, @@ -107,6 +109,7 @@ Layout { width: None, x: None, y: None, + pinned: None, run: None, focus: None, already_running: false, diff --git a/zellij-utils/src/kdl/kdl_layout_parser.rs b/zellij-utils/src/kdl/kdl_layout_parser.rs index 81e97a7e..c38c0146 100644 --- a/zellij-utils/src/kdl/kdl_layout_parser.rs +++ b/zellij-utils/src/kdl/kdl_layout_parser.rs @@ -122,6 +122,7 @@ impl<'a> KdlLayoutParser<'a> { || property_name == "y" || property_name == "width" || property_name == "height" + || property_name == "pinned" || property_name == "contents_file" } fn is_a_valid_tab_property(&self, property_name: &str) -> bool { @@ -596,6 +597,7 @@ impl<'a> KdlLayoutParser<'a> { let width = self.parse_percent_or_fixed(kdl_node, "width", false)?; let x = self.parse_percent_or_fixed(kdl_node, "x", true)?; let y = self.parse_percent_or_fixed(kdl_node, "y", true)?; + let pinned = kdl_get_bool_property_or_child_value_with_error!(kdl_node, "pinned"); let run = self.parse_command_plugin_or_edit_block(kdl_node)?; let focus = kdl_get_bool_property_or_child_value_with_error!(kdl_node, "focus"); let name = kdl_get_string_property_or_child_value_with_error!(kdl_node, "name") @@ -619,6 +621,7 @@ impl<'a> KdlLayoutParser<'a> { y, run, focus, + pinned, pane_initial_contents, ..Default::default() }) @@ -845,7 +848,7 @@ impl<'a> KdlLayoutParser<'a> { let width = self.parse_percent_or_fixed(kdl_node, "width", false)?; let x = self.parse_percent_or_fixed(kdl_node, "x", true)?; let y = self.parse_percent_or_fixed(kdl_node, "y", true)?; - // let mut floating_pane = FloatingPaneLayout::from(&pane_template); + let pinned = kdl_get_bool_property_or_child_value_with_error!(kdl_node, "pinned"); if let Some(height) = height { pane_template.height = Some(height); } @@ -858,6 +861,9 @@ impl<'a> KdlLayoutParser<'a> { if let Some(x) = x { pane_template.x = Some(x); } + if let Some(pinned) = pinned { + pane_template.pinned = Some(pinned); + } Ok(pane_template) }, PaneOrFloatingPane::Either(mut pane_template) => { @@ -896,6 +902,7 @@ impl<'a> KdlLayoutParser<'a> { let width = self.parse_percent_or_fixed(kdl_node, "width", false)?; let x = self.parse_percent_or_fixed(kdl_node, "x", true)?; let y = self.parse_percent_or_fixed(kdl_node, "y", true)?; + let pinned = kdl_get_bool_property_or_child_value_with_error!(kdl_node, "pinned"); let mut floating_pane = FloatingPaneLayout::from(&pane_template); if let Some(height) = height { floating_pane.height = Some(height); @@ -909,6 +916,9 @@ impl<'a> KdlLayoutParser<'a> { if let Some(x) = x { floating_pane.x = Some(x); } + if let Some(pinned) = pinned { + floating_pane.pinned = Some(pinned); + } Ok(floating_pane) }, } @@ -948,6 +958,7 @@ impl<'a> KdlLayoutParser<'a> { let width = self.parse_percent_or_fixed(kdl_node, "width", false)?; let x = self.parse_percent_or_fixed(kdl_node, "x", true)?; let y = self.parse_percent_or_fixed(kdl_node, "y", true)?; + let pinned = kdl_get_string_property_or_child_value_with_error!(kdl_node, "pinned"); let has_pane_properties = borderless.is_some() || split_size.is_some() @@ -956,7 +967,7 @@ impl<'a> KdlLayoutParser<'a> { || is_expanded_in_stack.is_some() || has_children_nodes; let has_floating_pane_properties = - height.is_some() || width.is_some() || x.is_some() || y.is_some(); + height.is_some() || width.is_some() || x.is_some() || y.is_some() || pinned.is_some(); if has_pane_properties || has_floating_pane_properties { Ok(false) } else { @@ -985,6 +996,7 @@ impl<'a> KdlLayoutParser<'a> { let width = self.parse_percent_or_fixed(kdl_node, "width", false)?; let x = self.parse_percent_or_fixed(kdl_node, "x", true)?; let y = self.parse_percent_or_fixed(kdl_node, "y", true)?; + let pinned = kdl_get_bool_property_or_child_value_with_error!(kdl_node, "pinned"); let has_pane_properties = borderless.is_some() || split_size.is_some() @@ -993,7 +1005,7 @@ impl<'a> KdlLayoutParser<'a> { || is_expanded_in_stack.is_some() || has_children_nodes; let has_floating_pane_properties = - height.is_some() || width.is_some() || x.is_some() || y.is_some(); + height.is_some() || width.is_some() || x.is_some() || y.is_some() || pinned.is_some(); if has_pane_properties && has_floating_pane_properties { let mut pane_properties = vec![]; @@ -1028,6 +1040,9 @@ impl<'a> KdlLayoutParser<'a> { if y.is_some() { floating_pane_properties.push("y"); } + if pinned.is_some() { + floating_pane_properties.push("pinned"); + } Err(ConfigError::new_layout_kdl_error( format!( "A pane_template cannot have both pane ({}) and floating pane ({}) properties", @@ -1079,6 +1094,7 @@ impl<'a> KdlLayoutParser<'a> { let width = self.parse_percent_or_fixed(kdl_node, "width", false)?; let x = self.parse_percent_or_fixed(kdl_node, "x", true)?; let y = self.parse_percent_or_fixed(kdl_node, "y", true)?; + let pinned = kdl_get_bool_property_or_child_value_with_error!(kdl_node, "pinned"); self.pane_templates.insert( template_name, ( @@ -1089,6 +1105,7 @@ impl<'a> KdlLayoutParser<'a> { width, x, y, + pinned, ..Default::default() }), kdl_node.clone(), diff --git a/zellij-utils/src/kdl/mod.rs b/zellij-utils/src/kdl/mod.rs index e3726173..37f36fd8 100644 --- a/zellij-utils/src/kdl/mod.rs +++ b/zellij-utils/src/kdl/mod.rs @@ -1101,6 +1101,7 @@ impl Action { } Some(node) }, + Action::TogglePanePinned => Some(KdlNode::new("TogglePanePinned")), _ => None, } } @@ -1537,11 +1538,13 @@ impl TryFrom<(&KdlNode, &Options)> for Action { let height = command_metadata .and_then(|c_m| kdl_child_string_value_for_entry(c_m, "height")) .map(|s| s.to_owned()); + let pinned = + command_metadata.and_then(|c_m| kdl_child_bool_value_for_entry(c_m, "pinned")); if floating { Ok(Action::NewFloatingPane( Some(run_command_action), name, - FloatingPaneCoordinates::new(x, y, width, height), + FloatingPaneCoordinates::new(x, y, width, height, pinned), )) } else if in_place { Ok(Action::NewInPlacePane(Some(run_command_action), name)) @@ -1758,6 +1761,7 @@ impl TryFrom<(&KdlNode, &Options)> for Action { plugin_id, }) }, + "TogglePanePinned" => Ok(Action::TogglePanePinned), _ => Err(ConfigError::new_kdl_error( format!("Unsupported action: {}", action_name).into(), kdl_action.span().offset(), diff --git a/zellij-utils/src/kdl/snapshots/zellij_utils__kdl__bare_config_from_default_assets_to_string.snap b/zellij-utils/src/kdl/snapshots/zellij_utils__kdl__bare_config_from_default_assets_to_string.snap index 6a709ef9..bcd3ac0e 100644 --- a/zellij-utils/src/kdl/snapshots/zellij_utils__kdl__bare_config_from_default_assets_to_string.snap +++ b/zellij-utils/src/kdl/snapshots/zellij_utils__kdl__bare_config_from_default_assets_to_string.snap @@ -1,6 +1,6 @@ --- source: zellij-utils/src/kdl/mod.rs -assertion_line: 5535 +assertion_line: 5551 expression: fake_config_stringified --- keybinds clear-defaults=true { @@ -17,6 +17,7 @@ keybinds clear-defaults=true { bind "e" { TogglePaneEmbedOrFloating; SwitchToMode "normal"; } bind "f" { ToggleFocusFullscreen; SwitchToMode "normal"; } bind "h" { MoveFocus "left"; } + bind "i" { TogglePanePinned; SwitchToMode "normal"; } bind "j" { MoveFocus "down"; } bind "k" { MoveFocus "up"; } bind "l" { MoveFocus "right"; } diff --git a/zellij-utils/src/kdl/snapshots/zellij_utils__kdl__bare_config_from_default_assets_to_string_with_comments.snap b/zellij-utils/src/kdl/snapshots/zellij_utils__kdl__bare_config_from_default_assets_to_string_with_comments.snap index b4bbbefe..f31c73d3 100644 --- a/zellij-utils/src/kdl/snapshots/zellij_utils__kdl__bare_config_from_default_assets_to_string_with_comments.snap +++ b/zellij-utils/src/kdl/snapshots/zellij_utils__kdl__bare_config_from_default_assets_to_string_with_comments.snap @@ -1,6 +1,6 @@ --- source: zellij-utils/src/kdl/mod.rs -assertion_line: 5547 +assertion_line: 5563 expression: fake_config_stringified --- keybinds clear-defaults=true { @@ -17,6 +17,7 @@ keybinds clear-defaults=true { bind "e" { TogglePaneEmbedOrFloating; SwitchToMode "normal"; } bind "f" { ToggleFocusFullscreen; SwitchToMode "normal"; } bind "h" { MoveFocus "left"; } + bind "i" { TogglePanePinned; SwitchToMode "normal"; } bind "j" { MoveFocus "down"; } bind "k" { MoveFocus "up"; } bind "l" { MoveFocus "right"; } diff --git a/zellij-utils/src/pane_size.rs b/zellij-utils/src/pane_size.rs index fe56e720..aa258fd9 100644 --- a/zellij-utils/src/pane_size.rs +++ b/zellij-utils/src/pane_size.rs @@ -10,15 +10,30 @@ use crate::position::Position; /// Contains the position and size of a [`Pane`], or more generally of any terminal, measured /// in character rows and columns. -#[derive(Clone, Copy, Default, PartialEq, Debug, Serialize, Deserialize, Eq, Hash)] +#[derive(Clone, Copy, Default, Debug, Serialize, Deserialize, Hash)] pub struct PaneGeom { pub x: usize, pub y: usize, pub rows: Dimension, pub cols: Dimension, pub is_stacked: bool, + pub is_pinned: bool, // only relevant to floating panes } +impl PartialEq for PaneGeom { + fn eq(&self, other: &Self) -> bool { + // compare all except is_pinned + // TODO: add is_stacked? + self.x == other.x + && self.y == other.y + && self.rows == other.rows + && self.cols == other.cols + && self.is_stacked == other.is_stacked + } +} + +impl Eq for PaneGeom {} + #[derive(Clone, Copy, Debug, Default, Serialize, Deserialize, PartialEq, Eq)] pub struct Viewport { pub x: usize, diff --git a/zellij-utils/src/plugin_api/action.proto b/zellij-utils/src/plugin_api/action.proto index d78d5e69..df7f3b05 100644 --- a/zellij-utils/src/plugin_api/action.proto +++ b/zellij-utils/src/plugin_api/action.proto @@ -244,6 +244,7 @@ enum ActionName { CliPipe = 82; MoveTab = 83; KeybindPipe = 84; + TogglePanePinned = 85; } message Position { diff --git a/zellij-utils/src/plugin_api/action.rs b/zellij-utils/src/plugin_api/action.rs index 9bb8ac7a..96385433 100644 --- a/zellij-utils/src/plugin_api/action.rs +++ b/zellij-utils/src/plugin_api/action.rs @@ -688,6 +688,10 @@ impl TryFrom<ProtobufAction> for Action { }, _ => Err("Wrong payload for Action::RenameSession"), }, + Some(ProtobufActionName::TogglePanePinned) => match protobuf_action.optional_payload { + Some(_) => Err("TogglePanePinned should not have a payload"), + None => Ok(Action::TogglePanePinned), + }, Some(ProtobufActionName::KeybindPipe) => match protobuf_action.optional_payload { Some(_) => Err("KeybindPipe should not have a payload"), // TODO: at some point we might want to support a payload here @@ -1284,6 +1288,10 @@ impl TryFrom<Action> for ProtobufAction { name: ProtobufActionName::KeybindPipe as i32, optional_payload: None, }), + Action::TogglePanePinned { .. } => Ok(ProtobufAction { + name: ProtobufActionName::TogglePanePinned as i32, + optional_payload: None, + }), Action::NoOp | Action::Confirm | Action::NewInPlacePane(..) diff --git a/zellij-utils/src/plugin_api/plugin_command.proto b/zellij-utils/src/plugin_api/plugin_command.proto index 4cc5c10c..14e8a286 100644 --- a/zellij-utils/src/plugin_api/plugin_command.proto +++ b/zellij-utils/src/plugin_api/plugin_command.proto @@ -128,6 +128,7 @@ enum CommandName { RebindKeys = 112; ListClients = 113; ChangeHostFolder = 114; + SetFloatingPanePinned = 115; } message PluginCommand { @@ -212,9 +213,15 @@ message PluginCommand { LoadNewPluginPayload load_new_plugin_payload = 87; RebindKeysPayload rebind_keys_payload = 88; ChangeHostFolderPayload change_host_folder_payload = 89; + SetFloatingPanePinnedPayload set_floating_pane_pinned_payload = 90; } } +message SetFloatingPanePinnedPayload { + PaneId pane_id = 1; + bool should_be_pinned = 2; +} + message ChangeHostFolderPayload { string new_host_folder = 1; } @@ -485,6 +492,7 @@ message FloatingPaneCoordinates { optional FixedOrPercentValue y = 2; optional FixedOrPercentValue width = 3; optional FixedOrPercentValue height = 4; + optional bool pinned = 5; } message FixedOrPercentValue { diff --git a/zellij-utils/src/plugin_api/plugin_command.rs b/zellij-utils/src/plugin_api/plugin_command.rs index 4317a02e..9d92f260 100644 --- a/zellij-utils/src/plugin_api/plugin_command.rs +++ b/zellij-utils/src/plugin_api/plugin_command.rs @@ -18,10 +18,10 @@ pub use super::generated_api::api::{ RebindKeysPayload, ReconfigurePayload, ReloadPluginPayload, RequestPluginPermissionPayload, RerunCommandPanePayload, ResizePaneIdWithDirectionPayload, ResizePayload, RunCommandPayload, ScrollDownInPaneIdPayload, ScrollToBottomInPaneIdPayload, - ScrollToTopInPaneIdPayload, ScrollUpInPaneIdPayload, SetTimeoutPayload, - ShowPaneWithIdPayload, SubscribePayload, SwitchSessionPayload, SwitchTabToPayload, - TogglePaneEmbedOrEjectForPaneIdPayload, TogglePaneIdFullscreenPayload, UnsubscribePayload, - WebRequestPayload, WriteCharsToPaneIdPayload, WriteToPaneIdPayload, + ScrollToTopInPaneIdPayload, ScrollUpInPaneIdPayload, SetFloatingPanePinnedPayload, + SetTimeoutPayload, ShowPaneWithIdPayload, SubscribePayload, SwitchSessionPayload, + SwitchTabToPayload, TogglePaneEmbedOrEjectForPaneIdPayload, TogglePaneIdFullscreenPayload, + UnsubscribePayload, WebRequestPayload, WriteCharsToPaneIdPayload, WriteToPaneIdPayload, }, plugin_permission::PermissionType as ProtobufPermissionType, resize::ResizeAction as ProtobufResizeAction, @@ -81,6 +81,7 @@ impl Into<FloatingPaneCoordinates> for ProtobufFloatingPaneCoordinates { None => None, } }), + pinned: self.pinned, } } } @@ -132,6 +133,7 @@ impl Into<ProtobufFloatingPaneCoordinates> for FloatingPaneCoordinates { }), None => None, }, + pinned: self.pinned, } } } @@ -1311,6 +1313,21 @@ impl TryFrom<ProtobufPluginCommand> for PluginCommand { }, _ => Err("Mismatched payload for ChangeHostFolder"), }, + Some(CommandName::SetFloatingPanePinned) => match protobuf_plugin_command.payload { + Some(Payload::SetFloatingPanePinnedPayload(set_floating_pane_pinned_payload)) => { + match set_floating_pane_pinned_payload + .pane_id + .and_then(|p| p.try_into().ok()) + { + Some(pane_id) => Ok(PluginCommand::SetFloatingPanePinned( + pane_id, + set_floating_pane_pinned_payload.should_be_pinned, + )), + None => Err("PaneId not found!"), + } + }, + _ => Err("Mismatched payload for SetFloatingPanePinned"), + }, None => Err("Unrecognized plugin command"), } } @@ -2144,6 +2161,17 @@ impl TryFrom<PluginCommand> for ProtobufPluginCommand { new_host_folder: new_host_folder.display().to_string(), // TODO: not accurate? })), }), + PluginCommand::SetFloatingPanePinned(pane_id, should_be_pinned) => { + Ok(ProtobufPluginCommand { + name: CommandName::SetFloatingPanePinned as i32, + payload: Some(Payload::SetFloatingPanePinnedPayload( + SetFloatingPanePinnedPayload { + pane_id: pane_id.try_into().ok(), + should_be_pinned, + }, + )), + }) + }, } } } diff --git a/zellij-utils/src/session_serialization.rs b/zellij-utils/src/session_serialization.rs index 68a9c2da..3cb7c5eb 100644 --- a/zellij-utils/src/session_serialization.rs +++ b/zellij-utils/src/session_serialization.rs @@ -453,6 +453,14 @@ fn serialize_floating_layout_attributes( }, None => {}, } + match layout.pinned { + Some(true) => { + let mut node = KdlNode::new("pinned"); + node.entries_mut().push(KdlEntry::new(KdlValue::Bool(true))); + pane_node_children.nodes_mut().push(node); + }, + _ => {}, + } } fn serialize_start_suspended(command: &Option<String>, pane_node_children: &mut KdlDocument) { @@ -774,6 +782,7 @@ fn get_floating_panes_layout_from_panegeoms( width: Some(m.geom.cols.into()), x: Some(PercentOrFixed::Fixed(m.geom.x)), y: Some(PercentOrFixed::Fixed(m.geom.y)), + pinned: Some(m.geom.is_pinned), run, focus: Some(m.is_focused), already_running: false, @@ -1275,6 +1284,7 @@ mod tests { rows: Dimension::fixed(10), cols: Dimension::fixed(10), is_stacked: false, + is_pinned: false, }, ..Default::default() }, @@ -1286,6 +1296,7 @@ mod tests { rows: Dimension::fixed(10), cols: Dimension::fixed(10), is_stacked: false, + is_pinned: false, }, ..Default::default() }, @@ -1301,6 +1312,7 @@ mod tests { rows: Dimension::fixed(10), cols: Dimension::fixed(10), is_stacked: false, + is_pinned: false, }, ..Default::default() }, @@ -1315,6 +1327,7 @@ mod tests { rows: Dimension::fixed(10), cols: Dimension::fixed(10), is_stacked: false, + is_pinned: false, }, ..Default::default() }, @@ -1334,6 +1347,7 @@ mod tests { rows: Dimension::fixed(10), cols: Dimension::fixed(10), is_stacked: false, + is_pinned: false, }, ..Default::default() }, @@ -1347,6 +1361,7 @@ mod tests { rows: Dimension::fixed(10), cols: Dimension::fixed(10), is_stacked: false, + is_pinned: false, }, ..Default::default() }, @@ -1362,6 +1377,7 @@ mod tests { rows: Dimension::fixed(10), cols: Dimension::fixed(10), is_stacked: false, + is_pinned: false, }, ..Default::default() }, @@ -1373,6 +1389,7 @@ mod tests { rows: Dimension::fixed(10), cols: Dimension::fixed(10), is_stacked: false, + is_pinned: false, }, ..Default::default() }, @@ -1386,6 +1403,7 @@ mod tests { rows: Dimension::fixed(10), cols: Dimension::fixed(10), is_stacked: false, + is_pinned: false, }, ..Default::default() }, @@ -1415,6 +1433,7 @@ mod tests { rows: Dimension::fixed(10), cols: Dimension::fixed(10), is_stacked: false, + is_pinned: false, }, ..Default::default() }, @@ -1426,6 +1445,7 @@ mod tests { rows: Dimension::fixed(10), cols: Dimension::fixed(10), is_stacked: false, + is_pinned: false, }, ..Default::default() }, @@ -1441,6 +1461,7 @@ mod tests { rows: Dimension::fixed(10), cols: Dimension::fixed(10), is_stacked: false, + is_pinned: false, }, ..Default::default() }, @@ -1455,6 +1476,7 @@ mod tests { rows: Dimension::fixed(10), cols: Dimension::fixed(10), is_stacked: false, + is_pinned: false, }, ..Default::default() }, @@ -1474,6 +1496,7 @@ mod tests { rows: Dimension::fixed(10), cols: Dimension::fixed(10), is_stacked: false, + is_pinned: false, }, ..Default::default() }, @@ -1487,6 +1510,7 @@ mod tests { rows: Dimension::fixed(10), cols: Dimension::fixed(10), is_stacked: false, + is_pinned: false, }, ..Default::default() }, @@ -1502,6 +1526,7 @@ mod tests { rows: Dimension::fixed(10), cols: Dimension::fixed(10), is_stacked: false, + is_pinned: false, }, ..Default::default() }, @@ -1515,6 +1540,7 @@ mod tests { rows: Dimension::fixed(10), cols: Dimension::fixed(10), is_stacked: false, + is_pinned: false, }, ..Default::default() }, @@ -1528,6 +1554,7 @@ mod tests { rows: Dimension::fixed(10), cols: Dimension::fixed(10), is_stacked: false, + is_pinned: false, }, ..Default::default() }, @@ -1555,6 +1582,7 @@ mod tests { rows: Dimension::fixed(1), cols: Dimension::fixed(10), is_stacked: true, + is_pinned: false, }, ..Default::default() }, @@ -1565,6 +1593,7 @@ mod tests { rows: Dimension::fixed(10), cols: Dimension::fixed(10), is_stacked: true, + is_pinned: false, }, ..Default::default() }, @@ -1575,6 +1604,7 @@ mod tests { rows: Dimension::fixed(1), cols: Dimension::fixed(10), is_stacked: true, + is_pinned: false, }, ..Default::default() }, @@ -1598,6 +1628,7 @@ mod tests { rows: Dimension::percent(100.0), cols: Dimension::percent(100.0), is_stacked: false, + is_pinned: false, }, ..Default::default() }], @@ -1612,6 +1643,7 @@ mod tests { rows: Dimension::fixed(10), cols: Dimension::fixed(10), is_stacked: false, + is_pinned: false, }, ..Default::default() }, @@ -1622,6 +1654,7 @@ mod tests { rows: Dimension::fixed(10), cols: Dimension::fixed(10), is_stacked: false, + is_pinned: false, }, ..Default::default() }, @@ -1753,6 +1786,7 @@ mod tests { rows: get_dim(&data["rows"]), cols: get_dim(&data["cols"]), is_stacked: data["is_stacked"].to_string().parse().unwrap(), + is_pinned: false, } } diff --git a/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__default_config_with_no_cli_arguments-2.snap b/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__default_config_with_no_cli_arguments-2.snap index 8f85ae3e..4c124f27 100644 --- a/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__default_config_with_no_cli_arguments-2.snap +++ b/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__default_config_with_no_cli_arguments-2.snap @@ -1,6 +1,6 @@ --- source: zellij-utils/src/setup.rs -assertion_line: 740 +assertion_line: 756 expression: "format!(\"{:#?}\", layout)" --- Layout { @@ -1676,6 +1676,7 @@ Layout { 1, ), ), + pinned: None, run: None, focus: None, already_running: false, @@ -1703,6 +1704,7 @@ Layout { 2, ), ), + pinned: None, run: None, focus: None, already_running: false, @@ -1730,6 +1732,7 @@ Layout { 3, ), ), + pinned: None, run: None, focus: None, already_running: false, @@ -1757,6 +1760,7 @@ Layout { 4, ), ), + pinned: None, run: None, focus: None, already_running: false, @@ -1784,6 +1788,7 @@ Layout { 5, ), ), + pinned: None, run: None, focus: None, already_running: false, @@ -1811,6 +1816,7 @@ Layout { 6, ), ), + pinned: None, run: None, focus: None, already_running: false, @@ -1838,6 +1844,7 @@ Layout { 7, ), ), + pinned: None, run: None, focus: None, already_running: false, @@ -1865,6 +1872,7 @@ Layout { 8, ), ), + pinned: None, run: None, focus: None, already_running: false, @@ -1892,6 +1900,7 @@ Layout { 9, ), ), + pinned: None, run: None, focus: None, already_running: false, @@ -1919,6 +1928,7 @@ Layout { 10, ), ), + pinned: None, run: None, focus: Some( true, @@ -1951,6 +1961,7 @@ Layout { 50, ), ), + pinned: None, run: None, focus: None, already_running: false, @@ -1978,6 +1989,7 @@ Layout { 25, ), ), + pinned: None, run: None, focus: None, already_running: false, @@ -2001,6 +2013,7 @@ Layout { 25, ), ), + pinned: None, run: None, focus: None, already_running: false, @@ -2028,6 +2041,7 @@ Layout { 55, ), ), + pinned: None, run: None, focus: Some( true, @@ -2053,6 +2067,7 @@ Layout { 1, ), ), + pinned: None, run: None, focus: None, already_running: false, @@ -2076,6 +2091,7 @@ Layout { 1, ), ), + pinned: None, run: None, focus: None, already_running: false, @@ -2107,6 +2123,7 @@ Layout { 55, ), ), + pinned: None, run: None, focus: None, already_running: false, @@ -2134,6 +2151,7 @@ Layout { 55, ), ), + pinned: None, run: None, focus: Some( true, @@ -2163,6 +2181,7 @@ Layout { 1, ), ), + pinned: None, run: None, focus: None, already_running: false, @@ -2190,6 +2209,7 @@ Layout { 1, ), ), + pinned: None, run: None, focus: None, already_running: false, diff --git a/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__default_config_with_no_cli_arguments.snap b/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__default_config_with_no_cli_arguments.snap index 81ca089c..754375a8 100644 --- a/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__default_config_with_no_cli_arguments.snap +++ b/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__default_config_with_no_cli_arguments.snap @@ -1,6 +1,6 @@ --- source: zellij-utils/src/setup.rs -assertion_line: 754 +assertion_line: 755 expression: "format!(\"{:#?}\", config)" --- Config { @@ -1080,6 +1080,17 @@ Config { bare_key: Char( 'i', ), + key_modifiers: {}, + }: [ + TogglePanePinned, + SwitchToMode( + Normal, + ), + ], + KeyWithModifier { + bare_key: Char( + 'i', + ), key_modifiers: { Alt, }, diff --git a/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_env_vars_override_config_env_vars.snap b/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_env_vars_override_config_env_vars.snap index 8e9c8db3..4e79b783 100644 --- a/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_env_vars_override_config_env_vars.snap +++ b/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_env_vars_override_config_env_vars.snap @@ -1,6 +1,6 @@ --- source: zellij-utils/src/setup.rs -assertion_line: 812 +assertion_line: 813 expression: "format!(\"{:#?}\", config)" --- Config { @@ -1080,6 +1080,17 @@ Config { bare_key: Char( 'i', ), + key_modifiers: {}, + }: [ + TogglePanePinned, + SwitchToMode( + Normal, + ), + ], + KeyWithModifier { + bare_key: Char( + 'i', + ), key_modifiers: { Alt, }, diff --git a/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_themes_override_config_themes.snap b/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_themes_override_config_themes.snap index ee9f4354..6c435fa0 100644 --- a/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_themes_override_config_themes.snap +++ b/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_themes_override_config_themes.snap @@ -1,6 +1,6 @@ --- source: zellij-utils/src/setup.rs -assertion_line: 840 +assertion_line: 841 expression: "format!(\"{:#?}\", config)" --- Config { @@ -1080,6 +1080,17 @@ Config { bare_key: Char( 'i', ), + key_modifiers: {}, + }: [ + TogglePanePinned, + SwitchToMode( + Normal, + ), + ], + KeyWithModifier { + bare_key: Char( + 'i', + ), key_modifiers: { Alt, }, diff --git a/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_ui_config_overrides_config_ui_config.snap b/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_ui_config_overrides_config_ui_config.snap index b380f31e..5712f847 100644 --- a/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_ui_config_overrides_config_ui_config.snap +++ b/zellij-utils/src/snapshots/zellij_utils__setup__setup_test__layout_ui_config_overrides_config_ui_config.snap @@ -1,6 +1,6 @@ --- source: zellij-utils/src/setup.rs -assertion_line: 826 +assertion_line: 827 expression: "format!(\"{:#?}\", config)" --- Config { @@ -1080,6 +1080,17 @@ Config { bare_key: Char( 'i', ), + key_modifiers: {}, + }: [ + TogglePanePinned, + SwitchToMode( + Normal, + ), + ], + KeyWithModifier { + bare_key: Char( + 'i', + ), key_modifiers: { Alt, }, |
