diff options
| author | DawidPietrykowski <53954695+DawidPietrykowski@users.noreply.github.com> | 2026-06-25 18:12:51 +0200 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2026-06-25 18:12:51 +0200 |
| commit | ebd731b0ff12737a6e2160a255280534ef3991bd (patch) | |
| tree | 4313963a269205391aa519fd090ea1de2b7cc4af /zellij-utils | |
| parent | tests: add more integration tests (#5308) (diff) | |
| download | sl0ppij-ebd731b0ff12737a6e2160a255280534ef3991bd.tar.gz sl0ppij-ebd731b0ff12737a6e2160a255280534ef3991bd.tar.xz sl0ppij-ebd731b0ff12737a6e2160a255280534ef3991bd.zip | |
feat(panes): `FocusLastPane` action (#4241)
* feat(panes): focus last pane (#4142)
* add tests
* add client id parameter to `add_floating_pane`
* add action for floating panes
* add tests for floating panes
* add keybinds to configs
* rebase fixes
* simplify focus logic when adding stacked pane
* formatting fix
* formatting fix 2
* fix: make sure expanding pane always focuses it for all clients in stack
* add pr
* fix rebase
---------
Co-authored-by: Aram Drevekenin <aram@poor.dev>
Diffstat (limited to 'zellij-utils')
| -rw-r--r-- | zellij-utils/assets/prost/api.action.rs | 3 | ||||
| -rw-r--r-- | zellij-utils/assets/prost/api.plugin_command.rs | 3 | ||||
| -rw-r--r-- | zellij-utils/assets/prost_ipc/client_server_contract.rs | 8 | ||||
| -rw-r--r-- | zellij-utils/src/cli.rs | 2 | ||||
| -rw-r--r-- | zellij-utils/src/client_server_contract/common_types.proto | 2 | ||||
| -rw-r--r-- | zellij-utils/src/data.rs | 1 | ||||
| -rw-r--r-- | zellij-utils/src/errors.rs | 1 | ||||
| -rw-r--r-- | zellij-utils/src/input/actions.rs | 3 | ||||
| -rw-r--r-- | zellij-utils/src/ipc/protobuf_conversion.rs | 5 | ||||
| -rw-r--r-- | zellij-utils/src/kdl/mod.rs | 6 | ||||
| -rw-r--r-- | zellij-utils/src/kdl/snapshots/zellij_utils__kdl__keybinds_to_string_with_all_actions.snap | 3 | ||||
| -rw-r--r-- | zellij-utils/src/plugin_api/action.proto | 1 | ||||
| -rw-r--r-- | zellij-utils/src/plugin_api/action.rs | 8 | ||||
| -rw-r--r-- | zellij-utils/src/plugin_api/plugin_command.proto | 1 | ||||
| -rw-r--r-- | zellij-utils/src/plugin_api/plugin_command.rs | 10 |
15 files changed, 54 insertions, 3 deletions
diff --git a/zellij-utils/assets/prost/api.action.rs b/zellij-utils/assets/prost/api.action.rs index 8d331b60..31325096 100644 --- a/zellij-utils/assets/prost/api.action.rs +++ b/zellij-utils/assets/prost/api.action.rs @@ -1124,6 +1124,7 @@ pub enum ActionName { SetDarkTheme = 101, SetLightTheme = 102, ToggleTheme = 103, + FocusLastPane = 104, } impl ActionName { /// String value of the enum field names used in the ProtoBuf definition. @@ -1233,6 +1234,7 @@ impl ActionName { ActionName::SetDarkTheme => "SetDarkTheme", ActionName::SetLightTheme => "SetLightTheme", ActionName::ToggleTheme => "ToggleTheme", + ActionName::FocusLastPane => "FocusLastPane", } } /// Creates an enum from field names used in the ProtoBuf definition. @@ -1339,6 +1341,7 @@ impl ActionName { "SetDarkTheme" => Some(Self::SetDarkTheme), "SetLightTheme" => Some(Self::SetLightTheme), "ToggleTheme" => Some(Self::ToggleTheme), + "FocusLastPane" => Some(Self::FocusLastPane), _ => None, } } diff --git a/zellij-utils/assets/prost/api.plugin_command.rs b/zellij-utils/assets/prost/api.plugin_command.rs index b43bd004..4cef814a 100644 --- a/zellij-utils/assets/prost/api.plugin_command.rs +++ b/zellij-utils/assets/prost/api.plugin_command.rs @@ -2229,6 +2229,7 @@ pub enum CommandName { NewTabUnfocused = 220, NewTiledPaneInTab = 221, ExitMobileMode = 222, + FocusLastPane = 223, } impl CommandName { /// String value of the enum field names used in the ProtoBuf definition. @@ -2437,6 +2438,7 @@ impl CommandName { CommandName::NewTabUnfocused => "NewTabUnfocused", CommandName::NewTiledPaneInTab => "NewTiledPaneInTab", CommandName::ExitMobileMode => "ExitMobileMode", + CommandName::FocusLastPane => "FocusLastPane", } } /// Creates an enum from field names used in the ProtoBuf definition. @@ -2642,6 +2644,7 @@ impl CommandName { "NewTabUnfocused" => Some(Self::NewTabUnfocused), "NewTiledPaneInTab" => Some(Self::NewTiledPaneInTab), "ExitMobileMode" => Some(Self::ExitMobileMode), + "FocusLastPane" => Some(Self::FocusLastPane), _ => None, } } diff --git a/zellij-utils/assets/prost_ipc/client_server_contract.rs b/zellij-utils/assets/prost_ipc/client_server_contract.rs index 4ef94968..e08496e7 100644 --- a/zellij-utils/assets/prost_ipc/client_server_contract.rs +++ b/zellij-utils/assets/prost_ipc/client_server_contract.rs @@ -119,7 +119,7 @@ pub struct RgbColor { #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct Action { - #[prost(oneof="action::ActionType", tags="1, 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, 51, 52, 53, 54, 55, 56, 57, 58, 59, 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, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140")] + #[prost(oneof="action::ActionType", tags="1, 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, 51, 52, 53, 54, 55, 56, 57, 58, 59, 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, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141")] pub action_type: ::core::option::Option<action::ActionType>, } /// Nested message and enum types in `Action`. @@ -409,6 +409,8 @@ pub mod action { ToggleTheme(super::ToggleThemeAction), #[prost(message, tag="140")] ToggleMobileMode(super::ToggleMobileModeAction), + #[prost(message, tag="141")] + FocusLastPane(super::FocusLastPaneAction), } } // Action message definitions (all 92 variants) @@ -428,6 +430,10 @@ pub struct FocusPreviousPaneAction { } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] +pub struct FocusLastPaneAction { +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] pub struct SwitchFocusAction { } #[allow(clippy::derive_partial_eq_without_eq)] diff --git a/zellij-utils/src/cli.rs b/zellij-utils/src/cli.rs index 5b169170..a00c4d05 100644 --- a/zellij-utils/src/cli.rs +++ b/zellij-utils/src/cli.rs @@ -765,6 +765,8 @@ pub enum CliAction { /// The pane_id of the pane, eg. terminal_1, plugin_2 or 3 pane_id: String, }, + /// Change focus to the last focused frame + FocusLastPane, /// Move the focused pane in the specified direction. [right|left|up|down] MoveFocus { direction: Direction, diff --git a/zellij-utils/src/client_server_contract/common_types.proto b/zellij-utils/src/client_server_contract/common_types.proto index 1dfc3994..718cc279 100644 --- a/zellij-utils/src/client_server_contract/common_types.proto +++ b/zellij-utils/src/client_server_contract/common_types.proto @@ -278,6 +278,7 @@ message Action { SetLightThemeAction set_light_theme = 138; ToggleThemeAction toggle_theme = 139; ToggleMobileModeAction toggle_mobile_mode = 140; + FocusLastPaneAction focus_last_pane = 141; } } @@ -287,6 +288,7 @@ message Action { message QuitAction {} message FocusNextPaneAction {} message FocusPreviousPaneAction {} +message FocusLastPaneAction {} message SwitchFocusAction {} message MovePaneBackwardsAction {} message ClearScreenAction {} diff --git a/zellij-utils/src/data.rs b/zellij-utils/src/data.rs index 16ff560e..c11f777f 100644 --- a/zellij-utils/src/data.rs +++ b/zellij-utils/src/data.rs @@ -3384,6 +3384,7 @@ pub enum PluginCommand { ResizeWithDirection(ResizeStrategy), FocusNextPane, FocusPreviousPane, + FocusLastPane, MoveFocus(Direction), MoveFocusOrTab(Direction), Detach, diff --git a/zellij-utils/src/errors.rs b/zellij-utils/src/errors.rs index 8e4bc66c..c9e8e25f 100644 --- a/zellij-utils/src/errors.rs +++ b/zellij-utils/src/errors.rs @@ -246,6 +246,7 @@ pub enum ScreenContext { SwitchFocus, FocusNextPane, FocusPreviousPane, + FocusLastPane, FocusPaneAt, MoveFocusLeft, MoveFocusLeftOrPreviousTab, diff --git a/zellij-utils/src/input/actions.rs b/zellij-utils/src/input/actions.rs index cf5fc448..40158b8b 100644 --- a/zellij-utils/src/input/actions.rs +++ b/zellij-utils/src/input/actions.rs @@ -158,6 +158,8 @@ pub enum Action { /// Switch focus to next pane in specified direction. FocusNextPane, FocusPreviousPane, + /// Switch focus to the last focused pane. + FocusLastPane, /// Move the focus pane in specified direction. SwitchFocus, MoveFocus { @@ -828,6 +830,7 @@ impl Action { ))?; Ok(vec![Action::FocusPaneByPaneId { pane_id }]) }, + CliAction::FocusLastPane => Ok(vec![Action::FocusLastPane]), CliAction::MoveFocus { direction } => Ok(vec![Action::MoveFocus { direction }]), CliAction::MoveFocusOrTab { direction } => { Ok(vec![Action::MoveFocusOrTab { direction }]) diff --git a/zellij-utils/src/ipc/protobuf_conversion.rs b/zellij-utils/src/ipc/protobuf_conversion.rs index d1bfb7d4..c4941676 100644 --- a/zellij-utils/src/ipc/protobuf_conversion.rs +++ b/zellij-utils/src/ipc/protobuf_conversion.rs @@ -915,6 +915,7 @@ impl From<crate::input::actions::Action> EditFileAction, EditScrollbackAction, EditScrollbackByPaneIdAction, + FocusLastPaneAction, FocusNextPaneAction, FocusPaneByPaneIdAction, FocusPluginPaneWithIdAction, @@ -1090,6 +1091,9 @@ impl From<crate::input::actions::Action> crate::input::actions::Action::FocusPreviousPane => { ActionType::FocusPreviousPane(FocusPreviousPaneAction {}) }, + crate::input::actions::Action::FocusLastPane => { + ActionType::FocusLastPane(FocusLastPaneAction {}) + }, crate::input::actions::Action::SwitchFocus => { ActionType::SwitchFocus(SwitchFocusAction {}) }, @@ -1961,6 +1965,7 @@ impl TryFrom<crate::client_server_contract::client_server_contract::Action> ActionType::FocusPreviousPane(_) => { Ok(crate::input::actions::Action::FocusPreviousPane) }, + ActionType::FocusLastPane(_) => Ok(crate::input::actions::Action::FocusLastPane), ActionType::SwitchFocus(_) => Ok(crate::input::actions::Action::SwitchFocus), ActionType::MoveFocus(move_focus_action) => { Ok(crate::input::actions::Action::MoveFocus { diff --git a/zellij-utils/src/kdl/mod.rs b/zellij-utils/src/kdl/mod.rs index 0164ef92..14695f68 100644 --- a/zellij-utils/src/kdl/mod.rs +++ b/zellij-utils/src/kdl/mod.rs @@ -48,6 +48,7 @@ macro_rules! parse_kdl_action_arguments { "Quit" => Ok(Action::Quit), "FocusNextPane" => Ok(Action::FocusNextPane), "FocusPreviousPane" => Ok(Action::FocusPreviousPane), + "FocusLastPane" => Ok(Action::FocusLastPane), "SwitchFocus" => Ok(Action::SwitchFocus), "EditScrollback" => Ok(Action::EditScrollback { ansi: false }), "ScrollUp" => Ok(Action::ScrollUp), @@ -660,6 +661,7 @@ impl Action { }, Action::FocusNextPane => Some(KdlNode::new("FocusNextPane")), Action::FocusPreviousPane => Some(KdlNode::new("FocusPreviousPane")), + Action::FocusLastPane => Some(KdlNode::new("FocusLastPane")), Action::SwitchFocus => Some(KdlNode::new("SwitchFocus")), Action::MoveFocus { direction } => { let mut node = KdlNode::new("MoveFocus"); @@ -1512,6 +1514,9 @@ impl TryFrom<(&KdlNode, &Options)> for Action { "FocusPreviousPane" => { parse_kdl_action_arguments!(action_name, action_arguments, kdl_action) }, + "FocusLastPane" => { + parse_kdl_action_arguments!(action_name, action_arguments, kdl_action) + }, "SwitchFocus" => parse_kdl_action_arguments!(action_name, action_arguments, kdl_action), "EditScrollback" => { let ansi = crate::kdl_get_bool_property_or_child_value!(kdl_action, "ansi") @@ -6729,6 +6734,7 @@ fn keybinds_to_string_with_all_actions() { config_key_2 "config_value_2"; }; } + bind "Ctrl Alt k" { FocusLastPane; } } }"#; let document: KdlDocument = fake_config.parse().unwrap(); diff --git a/zellij-utils/src/kdl/snapshots/zellij_utils__kdl__keybinds_to_string_with_all_actions.snap b/zellij-utils/src/kdl/snapshots/zellij_utils__kdl__keybinds_to_string_with_all_actions.snap index 43e85faa..b0119a63 100644 --- a/zellij-utils/src/kdl/snapshots/zellij_utils__kdl__keybinds_to_string_with_all_actions.snap +++ b/zellij-utils/src/kdl/snapshots/zellij_utils__kdl__keybinds_to_string_with_all_actions.snap @@ -1,6 +1,5 @@ --- source: zellij-utils/src/kdl/mod.rs -assertion_line: 2731 expression: serialized.to_string() --- keybinds clear-defaults=true { @@ -47,6 +46,7 @@ keybinds clear-defaults=true { } bind "Alt j" { GoToPreviousTab; } bind "Ctrl k" { MovePane "right"; } + bind "Ctrl Alt k" { FocusLastPane; } bind "Alt k" { CloseTab; } bind "Ctrl l" { MovePaneBackwards; } bind "Alt l" { GoToTab 1; } @@ -127,4 +127,3 @@ keybinds clear-defaults=true { bind "Alt z" { SearchInput 0; } } } - diff --git a/zellij-utils/src/plugin_api/action.proto b/zellij-utils/src/plugin_api/action.proto index 7eedf393..68c859b8 100644 --- a/zellij-utils/src/plugin_api/action.proto +++ b/zellij-utils/src/plugin_api/action.proto @@ -493,6 +493,7 @@ enum ActionName { SetDarkTheme = 101; SetLightTheme = 102; ToggleTheme = 103; + FocusLastPane = 104; } message Position { diff --git a/zellij-utils/src/plugin_api/action.rs b/zellij-utils/src/plugin_api/action.rs index 34b3adce..f4e35d84 100644 --- a/zellij-utils/src/plugin_api/action.rs +++ b/zellij-utils/src/plugin_api/action.rs @@ -168,6 +168,10 @@ impl TryFrom<ProtobufAction> for Action { Some(_) => Err("FocusPreviousPane should not have a payload"), None => Ok(Action::FocusPreviousPane), }, + Some(ProtobufActionName::FocusLastPane) => match protobuf_action.optional_payload { + Some(_) => Err("FocusLastPane should not have a payload"), + None => Ok(Action::FocusLastPane), + }, Some(ProtobufActionName::SwitchFocus) => match protobuf_action.optional_payload { Some(_) => Err("SwitchFocus should not have a payload"), None => Ok(Action::SwitchFocus), @@ -1173,6 +1177,10 @@ impl TryFrom<Action> for ProtobufAction { name: ProtobufActionName::FocusPreviousPane as i32, optional_payload: None, }), + Action::FocusLastPane => Ok(ProtobufAction { + name: ProtobufActionName::FocusLastPane as i32, + optional_payload: None, + }), Action::SwitchFocus => Ok(ProtobufAction { name: ProtobufActionName::SwitchFocus as i32, optional_payload: None, diff --git a/zellij-utils/src/plugin_api/plugin_command.proto b/zellij-utils/src/plugin_api/plugin_command.proto index c65bcbfd..82f4d835 100644 --- a/zellij-utils/src/plugin_api/plugin_command.proto +++ b/zellij-utils/src/plugin_api/plugin_command.proto @@ -213,6 +213,7 @@ enum CommandName { NewTabUnfocused = 220; NewTiledPaneInTab = 221; ExitMobileMode = 222; + FocusLastPane = 223; } message PluginCommand { diff --git a/zellij-utils/src/plugin_api/plugin_command.rs b/zellij-utils/src/plugin_api/plugin_command.rs index 42b0774f..546c3586 100644 --- a/zellij-utils/src/plugin_api/plugin_command.rs +++ b/zellij-utils/src/plugin_api/plugin_command.rs @@ -980,6 +980,12 @@ impl TryFrom<ProtobufPluginCommand> for PluginCommand { } Ok(PluginCommand::FocusPreviousPane) }, + Some(CommandName::FocusLastPane) => { + if protobuf_plugin_command.payload.is_some() { + return Err("FocusLastPane should not have a payload"); + } + Ok(PluginCommand::FocusLastPane) + }, Some(CommandName::MoveFocus) => match protobuf_plugin_command.payload { Some(Payload::MoveFocusPayload(move_payload)) => match move_payload.direction { Some(direction) => Ok(PluginCommand::MoveFocus(direction.try_into()?)), @@ -3005,6 +3011,10 @@ impl TryFrom<PluginCommand> for ProtobufPluginCommand { name: CommandName::FocusPreviousPane as i32, payload: None, }), + PluginCommand::FocusLastPane => Ok(ProtobufPluginCommand { + name: CommandName::FocusLastPane as i32, + payload: None, + }), PluginCommand::MoveFocus(direction) => Ok(ProtobufPluginCommand { name: CommandName::MoveFocus as i32, payload: Some(Payload::MoveFocusPayload(MovePayload { |
