feat(plugins): API to change floating pane coordinates (#3958)
* basic functionality through the cli * added to plugin api * add display area and viewport size to TabInfo * fix tests and add new one * some cleanups * refactor: extract pane_id parsing logic * style(fmt): rustfmt
This commit is contained in:
parent
382a0757e2
commit
c4cb9d3d81
26 changed files with 464 additions and 44 deletions
|
|
@ -1,6 +1,6 @@
|
|||
pub mod floating_pane_grid;
|
||||
use zellij_utils::{
|
||||
data::{Direction, PaneInfo, ResizeStrategy},
|
||||
data::{Direction, FloatingPaneCoordinates, PaneInfo, ResizeStrategy},
|
||||
position::Position,
|
||||
};
|
||||
|
||||
|
|
@ -728,6 +728,27 @@ impl FloatingPanes {
|
|||
let _ = self.set_pane_frames();
|
||||
}
|
||||
}
|
||||
pub fn change_pane_coordinates(
|
||||
&mut self,
|
||||
pane_id: PaneId,
|
||||
new_coordinates: FloatingPaneCoordinates,
|
||||
) -> Result<()> {
|
||||
let err_context = || format!("Failed to change_pane_coordinates");
|
||||
|
||||
{
|
||||
let viewport = { self.viewport.borrow().clone() };
|
||||
let pane = self.get_pane_mut(pane_id).with_context(err_context)?;
|
||||
let mut pane_geom = pane.position_and_size();
|
||||
if let Some(pinned) = new_coordinates.pinned.as_ref() {
|
||||
pane.set_pinned(*pinned);
|
||||
}
|
||||
pane_geom.adjust_coordinates(new_coordinates, viewport);
|
||||
pane.set_geom(pane_geom);
|
||||
pane.set_should_render(true);
|
||||
}
|
||||
let _ = self.set_pane_frames();
|
||||
Ok(())
|
||||
}
|
||||
pub fn move_clients_out_of_pane(&mut self, pane_id: PaneId) {
|
||||
let active_panes: Vec<(ClientId, PaneId)> = self
|
||||
.active_panes
|
||||
|
|
|
|||
|
|
@ -361,6 +361,15 @@ fn host_run_plugin_command(caller: Caller<'_, PluginEnv>) {
|
|||
PluginCommand::StackPanes(pane_ids) => {
|
||||
stack_panes(env, pane_ids.into_iter().map(|p_id| p_id.into()).collect())
|
||||
},
|
||||
PluginCommand::ChangeFloatingPanesCoordinates(pane_ids_and_coordinates) => {
|
||||
change_floating_panes_coordinates(
|
||||
env,
|
||||
pane_ids_and_coordinates
|
||||
.into_iter()
|
||||
.map(|(p_id, coordinates)| (p_id.into(), coordinates))
|
||||
.collect(),
|
||||
)
|
||||
},
|
||||
},
|
||||
(PermissionStatus::Denied, permission) => {
|
||||
log::error!(
|
||||
|
|
@ -1530,6 +1539,17 @@ fn stack_panes(env: &PluginEnv, pane_ids: Vec<PaneId>) {
|
|||
.send_to_screen(ScreenInstruction::StackPanes(pane_ids));
|
||||
}
|
||||
|
||||
fn change_floating_panes_coordinates(
|
||||
env: &PluginEnv,
|
||||
pane_ids_and_coordinates: Vec<(PaneId, FloatingPaneCoordinates)>,
|
||||
) {
|
||||
let _ = env
|
||||
.senders
|
||||
.send_to_screen(ScreenInstruction::ChangeFloatingPanesCoordinates(
|
||||
pane_ids_and_coordinates,
|
||||
));
|
||||
}
|
||||
|
||||
fn scan_host_folder(env: &PluginEnv, folder_to_scan: PathBuf) {
|
||||
if !folder_to_scan.starts_with("/host") {
|
||||
log::error!(
|
||||
|
|
@ -1950,6 +1970,7 @@ fn check_command_permission(
|
|||
| PluginCommand::LoadNewPlugin { .. }
|
||||
| PluginCommand::SetFloatingPanePinned(..)
|
||||
| PluginCommand::StackPanes(..)
|
||||
| PluginCommand::ChangeFloatingPanesCoordinates(..)
|
||||
| PluginCommand::KillSessions(..) => PermissionType::ChangeApplicationState,
|
||||
PluginCommand::UnblockCliPipeInput(..)
|
||||
| PluginCommand::BlockCliPipeInput(..)
|
||||
|
|
|
|||
|
|
@ -935,6 +935,14 @@ pub(crate) fn route_action(
|
|||
))
|
||||
.with_context(err_context)?;
|
||||
},
|
||||
Action::ChangeFloatingPaneCoordinates(pane_id, coordinates) => {
|
||||
senders
|
||||
.send_to_screen(ScreenInstruction::ChangeFloatingPanesCoordinates(vec![(
|
||||
pane_id.into(),
|
||||
coordinates,
|
||||
)]))
|
||||
.with_context(err_context)?;
|
||||
},
|
||||
}
|
||||
Ok(should_break)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -402,6 +402,7 @@ pub enum ScreenInstruction {
|
|||
TogglePanePinned(ClientId),
|
||||
SetFloatingPanePinned(PaneId, bool),
|
||||
StackPanes(Vec<PaneId>),
|
||||
ChangeFloatingPanesCoordinates(Vec<(PaneId, FloatingPaneCoordinates)>),
|
||||
}
|
||||
|
||||
impl From<&ScreenInstruction> for ScreenContext {
|
||||
|
|
@ -609,6 +610,9 @@ impl From<&ScreenInstruction> for ScreenContext {
|
|||
ScreenInstruction::TogglePanePinned(..) => ScreenContext::TogglePanePinned,
|
||||
ScreenInstruction::SetFloatingPanePinned(..) => ScreenContext::SetFloatingPanePinned,
|
||||
ScreenInstruction::StackPanes(..) => ScreenContext::StackPanes,
|
||||
ScreenInstruction::ChangeFloatingPanesCoordinates(..) => {
|
||||
ScreenContext::ChangeFloatingPanesCoordinates
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1528,6 +1532,8 @@ impl Screen {
|
|||
.copied()
|
||||
.collect();
|
||||
let (active_swap_layout_name, is_swap_layout_dirty) = tab.swap_layout_info();
|
||||
let tab_viewport = tab.get_viewport();
|
||||
let tab_display_area = tab.get_display_area();
|
||||
let tab_info_for_screen = TabInfo {
|
||||
position: tab.position,
|
||||
name: tab.name.clone(),
|
||||
|
|
@ -1539,6 +1545,10 @@ impl Screen {
|
|||
other_focused_clients: all_focused_clients,
|
||||
active_swap_layout_name,
|
||||
is_swap_layout_dirty,
|
||||
viewport_rows: tab_viewport.rows,
|
||||
viewport_columns: tab_viewport.cols,
|
||||
display_area_rows: tab_display_area.rows,
|
||||
display_area_columns: tab_display_area.cols,
|
||||
};
|
||||
tab_infos_for_screen_state.insert(tab.position, tab_info_for_screen);
|
||||
}
|
||||
|
|
@ -1558,6 +1568,8 @@ impl Screen {
|
|||
.collect()
|
||||
};
|
||||
let (active_swap_layout_name, is_swap_layout_dirty) = tab.swap_layout_info();
|
||||
let tab_viewport = tab.get_viewport();
|
||||
let tab_display_area = tab.get_display_area();
|
||||
let tab_info_for_plugins = TabInfo {
|
||||
position: tab.position,
|
||||
name: tab.name.clone(),
|
||||
|
|
@ -1569,6 +1581,10 @@ impl Screen {
|
|||
other_focused_clients,
|
||||
active_swap_layout_name,
|
||||
is_swap_layout_dirty,
|
||||
viewport_rows: tab_viewport.rows,
|
||||
viewport_columns: tab_viewport.cols,
|
||||
display_area_rows: tab_display_area.rows,
|
||||
display_area_columns: tab_display_area.cols,
|
||||
};
|
||||
plugin_tab_updates.push(tab_info_for_plugins);
|
||||
}
|
||||
|
|
@ -2573,6 +2589,20 @@ impl Screen {
|
|||
.get_mut(&root_tab_id)
|
||||
.map(|t| t.stack_panes(root_pane_id, panes_to_stack));
|
||||
}
|
||||
pub fn change_floating_panes_coordinates(
|
||||
&mut self,
|
||||
pane_ids_and_coordinates: Vec<(PaneId, FloatingPaneCoordinates)>,
|
||||
) {
|
||||
for (pane_id, coordinates) in pane_ids_and_coordinates {
|
||||
for (_tab_id, tab) in self.tabs.iter_mut() {
|
||||
if tab.has_pane_with_pid(&pane_id) {
|
||||
tab.change_floating_pane_coordinates(&pane_id, coordinates)
|
||||
.non_fatal();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
fn unblock_input(&self) -> Result<()> {
|
||||
self.bus
|
||||
.senders
|
||||
|
|
@ -4756,6 +4786,11 @@ pub(crate) fn screen_thread_main(
|
|||
let _ = screen.unblock_input();
|
||||
let _ = screen.render(None);
|
||||
},
|
||||
ScreenInstruction::ChangeFloatingPanesCoordinates(pane_ids_and_coordinates) => {
|
||||
screen.change_floating_panes_coordinates(pane_ids_and_coordinates);
|
||||
let _ = screen.unblock_input();
|
||||
let _ = screen.render(None);
|
||||
},
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
|
|
|
|||
|
|
@ -4495,6 +4495,23 @@ impl Tab {
|
|||
self.tiled_panes.expand_pane_in_stack(root_pane_id);
|
||||
}
|
||||
}
|
||||
pub fn change_floating_pane_coordinates(
|
||||
&mut self,
|
||||
pane_id: &PaneId,
|
||||
floating_pane_coordinates: FloatingPaneCoordinates,
|
||||
) -> Result<()> {
|
||||
self.floating_panes
|
||||
.change_pane_coordinates(*pane_id, floating_pane_coordinates)?;
|
||||
self.set_force_render();
|
||||
self.swap_layouts.set_is_floating_damaged();
|
||||
Ok(())
|
||||
}
|
||||
pub fn get_viewport(&self) -> Viewport {
|
||||
self.viewport.borrow().clone()
|
||||
}
|
||||
pub fn get_display_area(&self) -> Size {
|
||||
self.display_area.borrow().clone()
|
||||
}
|
||||
fn new_scrollback_editor_pane(&self, pid: u32) -> TerminalPane {
|
||||
let next_terminal_position = self.get_next_terminal_position();
|
||||
let mut new_pane = TerminalPane::new(
|
||||
|
|
|
|||
|
|
@ -3460,8 +3460,6 @@ fn pane_in_sgr_any_event_tracking_mouse_mode() {
|
|||
let sgr_mouse_mode_any_button = String::from("\u{1b}[?1003;1006h"); // any event tracking (1003) with SGR encoding (1006)
|
||||
tab.handle_pty_bytes(1, sgr_mouse_mode_any_button.as_bytes().to_vec())
|
||||
.unwrap();
|
||||
// TODO: CONTINUE HERE - make sure these pass, then add some button-less motions and see what
|
||||
// we track them
|
||||
tab.handle_mouse_event(
|
||||
&MouseEvent::new_left_press_event(Position::new(5, 71)),
|
||||
client_id,
|
||||
|
|
|
|||
|
|
@ -3689,3 +3689,46 @@ pub fn send_cli_stack_panes_action() {
|
|||
}
|
||||
assert_snapshot!(format!("{}", snapshot_count));
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn send_cli_change_floating_pane_coordinates_action() {
|
||||
let size = Size { cols: 80, rows: 10 };
|
||||
let client_id = 10; // fake client id should not appear in the screen's state
|
||||
let initial_tiled_layout = TiledPaneLayout::default();
|
||||
let initial_floating_panes = vec![FloatingPaneLayout::default()];
|
||||
let mut mock_screen = MockScreen::new(size);
|
||||
let session_metadata = mock_screen.clone_session_metadata();
|
||||
let screen_thread = mock_screen.run(Some(initial_tiled_layout), initial_floating_panes);
|
||||
|
||||
let received_server_instructions = Arc::new(Mutex::new(vec![]));
|
||||
let server_receiver = mock_screen.server_receiver.take().unwrap();
|
||||
let server_thread = log_actions_in_thread!(
|
||||
received_server_instructions,
|
||||
ServerInstruction::KillSession,
|
||||
server_receiver
|
||||
);
|
||||
let change_floating_pane_coordinates_action = CliAction::ChangeFloatingPaneCoordinates {
|
||||
pane_id: "0".to_owned(),
|
||||
x: Some("0".to_owned()),
|
||||
y: Some("0".to_owned()),
|
||||
width: Some("10".to_owned()),
|
||||
height: Some("10".to_owned()),
|
||||
pinned: None,
|
||||
};
|
||||
send_cli_action_to_server(
|
||||
&session_metadata,
|
||||
change_floating_pane_coordinates_action,
|
||||
client_id,
|
||||
);
|
||||
std::thread::sleep(std::time::Duration::from_millis(100));
|
||||
mock_screen.teardown(vec![server_thread, screen_thread]);
|
||||
let snapshots = take_snapshots_and_cursor_coordinates_from_render_events(
|
||||
received_server_instructions.lock().unwrap().iter(),
|
||||
size,
|
||||
);
|
||||
let snapshot_count = snapshots.len();
|
||||
for (_cursor_coordinates, snapshot) in snapshots {
|
||||
assert_snapshot!(format!("{}", snapshot));
|
||||
}
|
||||
assert_snapshot!(format!("{}", snapshot_count));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,16 @@
|
|||
---
|
||||
source: zellij-server/src/./unit/screen_tests.rs
|
||||
assertion_line: 3725
|
||||
expression: "format!(\"{}\", snapshot)"
|
||||
---
|
||||
00 (C): ┌ P[..]2 ┐─────────────────────────────────────────────────────────────────────┐
|
||||
01 (C): │ │ │
|
||||
02 (C): │ │ │
|
||||
03 (C): │ │ │
|
||||
04 (C): │ │ │
|
||||
05 (C): │ │ │
|
||||
06 (C): │ │ │
|
||||
07 (C): │ │ │
|
||||
08 (C): │ │ │
|
||||
09 (C): └────────┘─────────────────────────────────────────────────────────────────────┘
|
||||
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
---
|
||||
source: zellij-server/src/./unit/screen_tests.rs
|
||||
assertion_line: 3727
|
||||
expression: "format!(\"{}\", snapshot_count)"
|
||||
---
|
||||
2
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
---
|
||||
source: zellij-server/src/./unit/screen_tests.rs
|
||||
assertion_line: 3725
|
||||
expression: "format!(\"{}\", snapshot)"
|
||||
---
|
||||
00 (C): ┌ Pane #1 ─────────────────────────────────────────────────────────────────────┐
|
||||
01 (C): │ │
|
||||
02 (C): │ │
|
||||
03 (C): │ ┌ Pane #2 ──────────────────── PIN [ ] ┐ │
|
||||
04 (C): │ │ │ │
|
||||
05 (C): │ │ │ │
|
||||
06 (C): │ │ │ │
|
||||
07 (C): │ └──────────────────────────────────────┘ │
|
||||
08 (C): │ │
|
||||
09 (C): └──────────────────────────────────────────────────────────────────────────────┘
|
||||
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
---
|
||||
source: zellij-server/src/./unit/screen_tests.rs
|
||||
assertion_line: 2448
|
||||
assertion_line: 2926
|
||||
expression: "format!(\"{:#?}\", plugin_rename_tab_instruction)"
|
||||
---
|
||||
Some(
|
||||
|
|
@ -26,6 +26,10 @@ Some(
|
|||
"BASE",
|
||||
),
|
||||
is_swap_layout_dirty: false,
|
||||
viewport_rows: 10,
|
||||
viewport_columns: 80,
|
||||
display_area_rows: 10,
|
||||
display_area_columns: 80,
|
||||
},
|
||||
],
|
||||
),
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
---
|
||||
source: zellij-server/src/./unit/screen_tests.rs
|
||||
assertion_line: 2486
|
||||
assertion_line: 2976
|
||||
expression: "format!(\"{:#?}\", plugin_undo_rename_tab_instruction)"
|
||||
---
|
||||
Some(
|
||||
|
|
@ -26,6 +26,10 @@ Some(
|
|||
"BASE",
|
||||
),
|
||||
is_swap_layout_dirty: false,
|
||||
viewport_rows: 10,
|
||||
viewport_columns: 80,
|
||||
display_area_rows: 10,
|
||||
display_area_columns: 80,
|
||||
},
|
||||
],
|
||||
),
|
||||
|
|
|
|||
|
|
@ -1149,6 +1149,15 @@ pub fn stack_panes(pane_ids: Vec<PaneId>) {
|
|||
unsafe { host_run_plugin_command() };
|
||||
}
|
||||
|
||||
pub fn change_floating_panes_coordinates(
|
||||
pane_ids_and_coordinates: Vec<(PaneId, FloatingPaneCoordinates)>,
|
||||
) {
|
||||
let plugin_command = PluginCommand::ChangeFloatingPanesCoordinates(pane_ids_and_coordinates);
|
||||
let protobuf_plugin_command: ProtobufPluginCommand = plugin_command.try_into().unwrap();
|
||||
object_to_stdout(&protobuf_plugin_command.encode_to_vec());
|
||||
unsafe { host_run_plugin_command() };
|
||||
}
|
||||
|
||||
// Utility Functions
|
||||
|
||||
#[allow(unused)]
|
||||
|
|
|
|||
|
|
@ -406,6 +406,14 @@ pub struct TabInfo {
|
|||
pub active_swap_layout_name: ::core::option::Option<::prost::alloc::string::String>,
|
||||
#[prost(bool, tag = "10")]
|
||||
pub is_swap_layout_dirty: bool,
|
||||
#[prost(uint32, tag = "11")]
|
||||
pub viewport_rows: u32,
|
||||
#[prost(uint32, tag = "12")]
|
||||
pub viewport_columns: u32,
|
||||
#[prost(uint32, tag = "13")]
|
||||
pub display_area_rows: u32,
|
||||
#[prost(uint32, tag = "14")]
|
||||
pub display_area_columns: u32,
|
||||
}
|
||||
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
|
|
|
|||
|
|
@ -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, 90, 91"
|
||||
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, 91, 92"
|
||||
)]
|
||||
pub payload: ::core::option::Option<plugin_command::Payload>,
|
||||
}
|
||||
|
|
@ -180,10 +180,22 @@ pub mod plugin_command {
|
|||
SetFloatingPanePinnedPayload(super::SetFloatingPanePinnedPayload),
|
||||
#[prost(message, tag = "91")]
|
||||
StackPanesPayload(super::StackPanesPayload),
|
||||
#[prost(message, tag = "92")]
|
||||
ChangeFloatingPanesCoordinatesPayload(
|
||||
super::ChangeFloatingPanesCoordinatesPayload,
|
||||
),
|
||||
}
|
||||
}
|
||||
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
pub struct ChangeFloatingPanesCoordinatesPayload {
|
||||
#[prost(message, repeated, tag = "1")]
|
||||
pub pane_ids_and_floating_panes_coordinates: ::prost::alloc::vec::Vec<
|
||||
PaneIdAndFloatingPaneCoordinates,
|
||||
>,
|
||||
}
|
||||
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
pub struct StackPanesPayload {
|
||||
#[prost(message, repeated, tag = "1")]
|
||||
pub pane_ids: ::prost::alloc::vec::Vec<PaneId>,
|
||||
|
|
@ -598,6 +610,14 @@ pub struct MovePayload {
|
|||
}
|
||||
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
pub struct PaneIdAndFloatingPaneCoordinates {
|
||||
#[prost(message, optional, tag = "1")]
|
||||
pub pane_id: ::core::option::Option<PaneId>,
|
||||
#[prost(message, optional, tag = "2")]
|
||||
pub floating_pane_coordinates: ::core::option::Option<FloatingPaneCoordinates>,
|
||||
}
|
||||
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
pub struct IdAndNewName {
|
||||
/// pane id or tab index
|
||||
#[prost(uint32, tag = "1")]
|
||||
|
|
@ -747,6 +767,7 @@ pub enum CommandName {
|
|||
ChangeHostFolder = 114,
|
||||
SetFloatingPanePinned = 115,
|
||||
StackPanes = 116,
|
||||
ChangeFloatingPanesCoordinates = 117,
|
||||
}
|
||||
impl CommandName {
|
||||
/// String value of the enum field names used in the ProtoBuf definition.
|
||||
|
|
@ -874,6 +895,9 @@ impl CommandName {
|
|||
CommandName::ChangeHostFolder => "ChangeHostFolder",
|
||||
CommandName::SetFloatingPanePinned => "SetFloatingPanePinned",
|
||||
CommandName::StackPanes => "StackPanes",
|
||||
CommandName::ChangeFloatingPanesCoordinates => {
|
||||
"ChangeFloatingPanesCoordinates"
|
||||
}
|
||||
}
|
||||
}
|
||||
/// Creates an enum from field names used in the ProtoBuf definition.
|
||||
|
|
@ -998,6 +1022,9 @@ impl CommandName {
|
|||
"ChangeHostFolder" => Some(Self::ChangeHostFolder),
|
||||
"SetFloatingPanePinned" => Some(Self::SetFloatingPanePinned),
|
||||
"StackPanes" => Some(Self::StackPanes),
|
||||
"ChangeFloatingPanesCoordinates" => {
|
||||
Some(Self::ChangeFloatingPanesCoordinates)
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -771,4 +771,25 @@ tail -f /tmp/my-live-logfile | zellij action pipe --name logs --plugin https://e
|
|||
#[clap(last(true), required(true))]
|
||||
pane_ids: Vec<String>,
|
||||
},
|
||||
ChangeFloatingPaneCoordinates {
|
||||
/// The pane_id of the floating pane, eg. terminal_1, plugin_2 or 3 (equivalent to
|
||||
/// terminal_3)
|
||||
#[clap(short, long, value_parser)]
|
||||
pane_id: String,
|
||||
/// The x coordinates if the pane is floating as a bare integer (eg. 1) or percent (eg. 10%)
|
||||
#[clap(short, long)]
|
||||
x: Option<String>,
|
||||
/// The y coordinates if the pane is floating as a bare integer (eg. 1) or percent (eg. 10%)
|
||||
#[clap(short, long)]
|
||||
y: Option<String>,
|
||||
/// The width if the pane is floating as a bare integer (eg. 1) or percent (eg. 10%)
|
||||
#[clap(long)]
|
||||
width: Option<String>,
|
||||
/// The height if the pane is floating as a bare integer (eg. 1) or percent (eg. 10%)
|
||||
#[clap(long)]
|
||||
height: Option<String>,
|
||||
/// Whether to pin a floating pane so that it is always on top
|
||||
#[clap(long)]
|
||||
pinned: Option<bool>,
|
||||
},
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1294,6 +1294,16 @@ pub struct TabInfo {
|
|||
pub active_swap_layout_name: Option<String>,
|
||||
/// Whether the user manually changed the layout, moving out of the swap layout scheme
|
||||
pub is_swap_layout_dirty: bool,
|
||||
/// Row count in the viewport (including all non-ui panes, eg. will excluse the status bar)
|
||||
pub viewport_rows: usize,
|
||||
/// Column count in the viewport (including all non-ui panes, eg. will excluse the status bar)
|
||||
pub viewport_columns: usize,
|
||||
/// Row count in the display area (including all panes, will typically be larger than the
|
||||
/// viewport)
|
||||
pub display_area_rows: usize,
|
||||
/// Column count in the display area (including all panes, will typically be larger than the
|
||||
/// viewport)
|
||||
pub display_area_columns: usize,
|
||||
}
|
||||
|
||||
/// The `PaneManifest` contains a dictionary of panes, indexed by the tab position (0 indexed).
|
||||
|
|
@ -1513,6 +1523,25 @@ pub enum PaneId {
|
|||
Plugin(u32),
|
||||
}
|
||||
|
||||
impl FromStr for PaneId {
|
||||
type Err = Box<dyn std::error::Error>;
|
||||
fn from_str(stringified_pane_id: &str) -> Result<Self, Self::Err> {
|
||||
if let Some(terminal_stringified_pane_id) = stringified_pane_id.strip_prefix("terminal_") {
|
||||
u32::from_str_radix(terminal_stringified_pane_id, 10)
|
||||
.map(|id| PaneId::Terminal(id))
|
||||
.map_err(|e| e.into())
|
||||
} else if let Some(plugin_pane_id) = stringified_pane_id.strip_prefix("plugin_") {
|
||||
u32::from_str_radix(plugin_pane_id, 10)
|
||||
.map(|id| PaneId::Plugin(id))
|
||||
.map_err(|e| e.into())
|
||||
} else {
|
||||
u32::from_str_radix(&stringified_pane_id, 10)
|
||||
.map(|id| PaneId::Terminal(id))
|
||||
.map_err(|e| e.into())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl MessageToPlugin {
|
||||
pub fn new(message_name: impl Into<String>) -> Self {
|
||||
MessageToPlugin {
|
||||
|
|
@ -1903,4 +1932,5 @@ pub enum PluginCommand {
|
|||
ChangeHostFolder(PathBuf),
|
||||
SetFloatingPanePinned(PaneId, bool), // bool -> should be pinned
|
||||
StackPanes(Vec<PaneId>),
|
||||
ChangeFloatingPanesCoordinates(Vec<(PaneId, FloatingPaneCoordinates)>),
|
||||
}
|
||||
|
|
|
|||
|
|
@ -374,6 +374,7 @@ pub enum ScreenContext {
|
|||
TogglePanePinned,
|
||||
SetFloatingPanePinned,
|
||||
StackPanes,
|
||||
ChangeFloatingPanesCoordinates,
|
||||
}
|
||||
|
||||
/// Stack call representations corresponding to the different types of [`PtyInstruction`]s.
|
||||
|
|
|
|||
|
|
@ -295,6 +295,7 @@ pub enum Action {
|
|||
ListClients,
|
||||
TogglePanePinned,
|
||||
StackPanes(Vec<PaneId>),
|
||||
ChangeFloatingPaneCoordinates(PaneId, FloatingPaneCoordinates),
|
||||
}
|
||||
|
||||
impl Action {
|
||||
|
|
@ -740,37 +741,15 @@ impl Action {
|
|||
let mut malformed_ids = vec![];
|
||||
let pane_ids = pane_ids
|
||||
.iter()
|
||||
.filter_map(|stringified_pane_id| {
|
||||
if let Some(terminal_pane_id) =
|
||||
stringified_pane_id.strip_prefix("terminal_")
|
||||
{
|
||||
u32::from_str_radix(terminal_pane_id, 10)
|
||||
.ok()
|
||||
.or_else(|| {
|
||||
.filter_map(
|
||||
|stringified_pane_id| match PaneId::from_str(stringified_pane_id) {
|
||||
Ok(pane_id) => Some(pane_id),
|
||||
Err(_e) => {
|
||||
malformed_ids.push(stringified_pane_id.to_owned());
|
||||
None
|
||||
})
|
||||
.map(|id| PaneId::Terminal(id))
|
||||
} else if let Some(plugin_pane_id) =
|
||||
stringified_pane_id.strip_prefix("plugin_")
|
||||
{
|
||||
u32::from_str_radix(plugin_pane_id, 10)
|
||||
.ok()
|
||||
.or_else(|| {
|
||||
malformed_ids.push(stringified_pane_id.to_owned());
|
||||
None
|
||||
})
|
||||
.map(|id| PaneId::Plugin(id))
|
||||
} else {
|
||||
u32::from_str_radix(stringified_pane_id, 10)
|
||||
.ok()
|
||||
.or_else(|| {
|
||||
malformed_ids.push(stringified_pane_id.to_owned());
|
||||
None
|
||||
})
|
||||
.map(|id| PaneId::Terminal(id))
|
||||
}
|
||||
})
|
||||
},
|
||||
},
|
||||
)
|
||||
.collect();
|
||||
if !malformed_ids.is_empty() {
|
||||
Err(
|
||||
|
|
@ -783,6 +762,31 @@ impl Action {
|
|||
Ok(vec![Action::StackPanes(pane_ids)])
|
||||
}
|
||||
},
|
||||
CliAction::ChangeFloatingPaneCoordinates {
|
||||
pane_id,
|
||||
x,
|
||||
y,
|
||||
width,
|
||||
height,
|
||||
pinned,
|
||||
} => {
|
||||
let Some(coordinates) = FloatingPaneCoordinates::new(x, y, width, height, pinned)
|
||||
else {
|
||||
return Err(format!("Failed to parse floating pane coordinates"));
|
||||
};
|
||||
let parsed_pane_id = PaneId::from_str(&pane_id);
|
||||
match parsed_pane_id {
|
||||
Ok(parsed_pane_id) => {
|
||||
Ok(vec![Action::ChangeFloatingPaneCoordinates(parsed_pane_id, coordinates)])
|
||||
},
|
||||
Err(_e) => {
|
||||
Err(format!(
|
||||
"Malformed pane id: {}, expecting a space separated list of either a bare integer (eg. 1), a terminal pane id (eg. terminal_1) or a plugin pane id (eg. plugin_1)",
|
||||
pane_id
|
||||
))
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
pub fn launches_plugin(&self, plugin_url: &str) -> bool {
|
||||
|
|
|
|||
|
|
@ -4259,6 +4259,15 @@ impl TabInfo {
|
|||
.map(|s| s.to_owned())
|
||||
}};
|
||||
}
|
||||
macro_rules! optional_int_node {
|
||||
($name:expr, $type:ident) => {{
|
||||
kdl_document
|
||||
.get($name)
|
||||
.and_then(|n| n.entries().iter().next())
|
||||
.and_then(|e| e.value().as_i64())
|
||||
.map(|e| e as $type)
|
||||
}};
|
||||
}
|
||||
macro_rules! bool_node {
|
||||
($name:expr) => {{
|
||||
kdl_document
|
||||
|
|
@ -4288,6 +4297,10 @@ impl TabInfo {
|
|||
}
|
||||
}
|
||||
let active_swap_layout_name = optional_string_node!("active_swap_layout_name");
|
||||
let viewport_rows = optional_int_node!("viewport_rows", usize).unwrap_or(0);
|
||||
let viewport_columns = optional_int_node!("viewport_columns", usize).unwrap_or(0);
|
||||
let display_area_rows = optional_int_node!("display_area_rows", usize).unwrap_or(0);
|
||||
let display_area_columns = optional_int_node!("display_area_columns", usize).unwrap_or(0);
|
||||
let is_swap_layout_dirty = bool_node!("is_swap_layout_dirty");
|
||||
Ok(TabInfo {
|
||||
position,
|
||||
|
|
@ -4300,6 +4313,10 @@ impl TabInfo {
|
|||
other_focused_clients,
|
||||
active_swap_layout_name,
|
||||
is_swap_layout_dirty,
|
||||
viewport_rows,
|
||||
viewport_columns,
|
||||
display_area_rows,
|
||||
display_area_columns,
|
||||
})
|
||||
}
|
||||
pub fn encode_to_kdl(&self) -> KdlDocument {
|
||||
|
|
@ -4347,6 +4364,22 @@ impl TabInfo {
|
|||
kdl_doucment.nodes_mut().push(active_swap_layout);
|
||||
}
|
||||
|
||||
let mut viewport_rows = KdlNode::new("viewport_rows");
|
||||
viewport_rows.push(self.viewport_rows as i64);
|
||||
kdl_doucment.nodes_mut().push(viewport_rows);
|
||||
|
||||
let mut viewport_columns = KdlNode::new("viewport_columns");
|
||||
viewport_columns.push(self.viewport_columns as i64);
|
||||
kdl_doucment.nodes_mut().push(viewport_columns);
|
||||
|
||||
let mut display_area_columns = KdlNode::new("display_area_columns");
|
||||
display_area_columns.push(self.display_area_columns as i64);
|
||||
kdl_doucment.nodes_mut().push(display_area_columns);
|
||||
|
||||
let mut display_area_rows = KdlNode::new("display_area_rows");
|
||||
display_area_rows.push(self.display_area_rows as i64);
|
||||
kdl_doucment.nodes_mut().push(display_area_rows);
|
||||
|
||||
let mut is_swap_layout_dirty = KdlNode::new("is_swap_layout_dirty");
|
||||
is_swap_layout_dirty.push(self.is_swap_layout_dirty);
|
||||
kdl_doucment.nodes_mut().push(is_swap_layout_dirty);
|
||||
|
|
@ -4691,6 +4724,10 @@ fn serialize_and_deserialize_session_info_with_data() {
|
|||
other_focused_clients: vec![2, 3],
|
||||
active_swap_layout_name: Some("BASE".to_owned()),
|
||||
is_swap_layout_dirty: true,
|
||||
viewport_rows: 10,
|
||||
viewport_columns: 10,
|
||||
display_area_rows: 10,
|
||||
display_area_columns: 10,
|
||||
},
|
||||
TabInfo {
|
||||
position: 1,
|
||||
|
|
@ -4703,6 +4740,10 @@ fn serialize_and_deserialize_session_info_with_data() {
|
|||
other_focused_clients: vec![2, 3],
|
||||
active_swap_layout_name: None,
|
||||
is_swap_layout_dirty: false,
|
||||
viewport_rows: 10,
|
||||
viewport_columns: 10,
|
||||
display_area_rows: 10,
|
||||
display_area_columns: 10,
|
||||
},
|
||||
],
|
||||
panes: PaneManifest { panes },
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
---
|
||||
source: zellij-utils/src/kdl/mod.rs
|
||||
assertion_line: 2552
|
||||
assertion_line: 4728
|
||||
expression: serialized
|
||||
---
|
||||
name "my session name"
|
||||
|
|
@ -15,6 +15,10 @@ tabs {
|
|||
are_floating_panes_visible true
|
||||
other_focused_clients 2 3
|
||||
active_swap_layout_name "BASE"
|
||||
viewport_rows 10
|
||||
viewport_columns 10
|
||||
display_area_columns 10
|
||||
display_area_rows 10
|
||||
is_swap_layout_dirty true
|
||||
}
|
||||
tab {
|
||||
|
|
@ -26,6 +30,10 @@ tabs {
|
|||
is_sync_panes_active true
|
||||
are_floating_panes_visible true
|
||||
other_focused_clients 2 3
|
||||
viewport_rows 10
|
||||
viewport_columns 10
|
||||
display_area_columns 10
|
||||
display_area_rows 10
|
||||
is_swap_layout_dirty false
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1246,6 +1246,7 @@ impl TryFrom<Action> for ProtobufAction {
|
|||
| Action::CliPipe { .. }
|
||||
| Action::ListClients
|
||||
| Action::StackPanes(..)
|
||||
| Action::ChangeFloatingPaneCoordinates(..)
|
||||
| Action::SkipConfirm(..) => Err("Unsupported action"),
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -303,6 +303,10 @@ message TabInfo {
|
|||
repeated uint32 other_focused_clients = 8;
|
||||
optional string active_swap_layout_name = 9;
|
||||
bool is_swap_layout_dirty = 10;
|
||||
uint32 viewport_rows = 11;
|
||||
uint32 viewport_columns = 12;
|
||||
uint32 display_area_rows = 13;
|
||||
uint32 display_area_columns = 14;
|
||||
}
|
||||
|
||||
message ModeUpdatePayload {
|
||||
|
|
|
|||
|
|
@ -1070,6 +1070,10 @@ impl TryFrom<ProtobufTabInfo> for TabInfo {
|
|||
.collect(),
|
||||
active_swap_layout_name: protobuf_tab_info.active_swap_layout_name,
|
||||
is_swap_layout_dirty: protobuf_tab_info.is_swap_layout_dirty,
|
||||
viewport_rows: protobuf_tab_info.viewport_rows as usize,
|
||||
viewport_columns: protobuf_tab_info.viewport_columns as usize,
|
||||
display_area_rows: protobuf_tab_info.display_area_rows as usize,
|
||||
display_area_columns: protobuf_tab_info.display_area_columns as usize,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -1092,6 +1096,10 @@ impl TryFrom<TabInfo> for ProtobufTabInfo {
|
|||
.collect(),
|
||||
active_swap_layout_name: tab_info.active_swap_layout_name,
|
||||
is_swap_layout_dirty: tab_info.is_swap_layout_dirty,
|
||||
viewport_rows: tab_info.viewport_rows as u32,
|
||||
viewport_columns: tab_info.viewport_columns as u32,
|
||||
display_area_rows: tab_info.display_area_rows as u32,
|
||||
display_area_columns: tab_info.display_area_columns as u32,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -1475,6 +1483,10 @@ fn serialize_tab_update_event_with_non_default_values() {
|
|||
other_focused_clients: vec![2, 3, 4],
|
||||
active_swap_layout_name: Some("my cool swap layout".to_owned()),
|
||||
is_swap_layout_dirty: false,
|
||||
viewport_rows: 10,
|
||||
viewport_columns: 10,
|
||||
display_area_rows: 10,
|
||||
display_area_columns: 10,
|
||||
},
|
||||
TabInfo {
|
||||
position: 1,
|
||||
|
|
@ -1487,6 +1499,10 @@ fn serialize_tab_update_event_with_non_default_values() {
|
|||
other_focused_clients: vec![1, 5, 111],
|
||||
active_swap_layout_name: None,
|
||||
is_swap_layout_dirty: true,
|
||||
viewport_rows: 10,
|
||||
viewport_columns: 10,
|
||||
display_area_rows: 10,
|
||||
display_area_columns: 10,
|
||||
},
|
||||
TabInfo::default(),
|
||||
]);
|
||||
|
|
@ -1754,6 +1770,10 @@ fn serialize_session_update_event_with_non_default_values() {
|
|||
other_focused_clients: vec![2, 3, 4],
|
||||
active_swap_layout_name: Some("my cool swap layout".to_owned()),
|
||||
is_swap_layout_dirty: false,
|
||||
viewport_rows: 10,
|
||||
viewport_columns: 10,
|
||||
display_area_rows: 10,
|
||||
display_area_columns: 10,
|
||||
},
|
||||
TabInfo {
|
||||
position: 1,
|
||||
|
|
@ -1766,6 +1786,10 @@ fn serialize_session_update_event_with_non_default_values() {
|
|||
other_focused_clients: vec![1, 5, 111],
|
||||
active_swap_layout_name: None,
|
||||
is_swap_layout_dirty: true,
|
||||
viewport_rows: 10,
|
||||
viewport_columns: 10,
|
||||
display_area_rows: 10,
|
||||
display_area_columns: 10,
|
||||
},
|
||||
TabInfo::default(),
|
||||
];
|
||||
|
|
|
|||
|
|
@ -130,6 +130,7 @@ enum CommandName {
|
|||
ChangeHostFolder = 114;
|
||||
SetFloatingPanePinned = 115;
|
||||
StackPanes = 116;
|
||||
ChangeFloatingPanesCoordinates = 117;
|
||||
}
|
||||
|
||||
message PluginCommand {
|
||||
|
|
@ -216,9 +217,14 @@ message PluginCommand {
|
|||
ChangeHostFolderPayload change_host_folder_payload = 89;
|
||||
SetFloatingPanePinnedPayload set_floating_pane_pinned_payload = 90;
|
||||
StackPanesPayload stack_panes_payload = 91;
|
||||
ChangeFloatingPanesCoordinatesPayload change_floating_panes_coordinates_payload = 92;
|
||||
}
|
||||
}
|
||||
|
||||
message ChangeFloatingPanesCoordinatesPayload {
|
||||
repeated PaneIdAndFloatingPaneCoordinates pane_ids_and_floating_panes_coordinates = 1;
|
||||
}
|
||||
|
||||
message StackPanesPayload {
|
||||
repeated PaneId pane_ids = 1;
|
||||
}
|
||||
|
|
@ -488,6 +494,11 @@ message MovePayload {
|
|||
resize.MoveDirection direction = 1;
|
||||
}
|
||||
|
||||
message PaneIdAndFloatingPaneCoordinates {
|
||||
PaneId pane_id = 1;
|
||||
FloatingPaneCoordinates floating_pane_coordinates = 2;
|
||||
}
|
||||
|
||||
message IdAndNewName {
|
||||
uint32 id = 1; // pane id or tab index
|
||||
string new_name = 2;
|
||||
|
|
|
|||
|
|
@ -4,9 +4,10 @@ pub use super::generated_api::api::{
|
|||
input_mode::InputMode as ProtobufInputMode,
|
||||
plugin_command::{
|
||||
plugin_command::Payload, BreakPanesToNewTabPayload, BreakPanesToTabWithIndexPayload,
|
||||
ChangeHostFolderPayload, ClearScreenForPaneIdPayload, CliPipeOutputPayload,
|
||||
CloseTabWithIndexPayload, CommandName, ContextItem, EditScrollbackForPaneWithIdPayload,
|
||||
EnvVariable, ExecCmdPayload, FixedOrPercent as ProtobufFixedOrPercent,
|
||||
ChangeFloatingPanesCoordinatesPayload, ChangeHostFolderPayload,
|
||||
ClearScreenForPaneIdPayload, CliPipeOutputPayload, CloseTabWithIndexPayload, CommandName,
|
||||
ContextItem, EditScrollbackForPaneWithIdPayload, EnvVariable, ExecCmdPayload,
|
||||
FixedOrPercent as ProtobufFixedOrPercent,
|
||||
FixedOrPercentValue as ProtobufFixedOrPercentValue,
|
||||
FloatingPaneCoordinates as ProtobufFloatingPaneCoordinates, HidePaneWithIdPayload,
|
||||
HttpVerb as ProtobufHttpVerb, IdAndNewName, KeyToRebind, KeyToUnbind, KillSessionsPayload,
|
||||
|
|
@ -14,8 +15,9 @@ pub use super::generated_api::api::{
|
|||
MovePaneWithPaneIdPayload, MovePayload, NewPluginArgs as ProtobufNewPluginArgs,
|
||||
NewTabsWithLayoutInfoPayload, OpenCommandPanePayload, OpenFilePayload,
|
||||
PageScrollDownInPaneIdPayload, PageScrollUpInPaneIdPayload, PaneId as ProtobufPaneId,
|
||||
PaneType as ProtobufPaneType, PluginCommand as ProtobufPluginCommand, PluginMessagePayload,
|
||||
RebindKeysPayload, ReconfigurePayload, ReloadPluginPayload, RequestPluginPermissionPayload,
|
||||
PaneIdAndFloatingPaneCoordinates, PaneType as ProtobufPaneType,
|
||||
PluginCommand as ProtobufPluginCommand, PluginMessagePayload, RebindKeysPayload,
|
||||
ReconfigurePayload, ReloadPluginPayload, RequestPluginPermissionPayload,
|
||||
RerunCommandPanePayload, ResizePaneIdWithDirectionPayload, ResizePayload,
|
||||
RunCommandPayload, ScrollDownInPaneIdPayload, ScrollToBottomInPaneIdPayload,
|
||||
ScrollToTopInPaneIdPayload, ScrollUpInPaneIdPayload, SetFloatingPanePinnedPayload,
|
||||
|
|
@ -1339,7 +1341,26 @@ impl TryFrom<ProtobufPluginCommand> for PluginCommand {
|
|||
.collect(),
|
||||
))
|
||||
},
|
||||
_ => Err("Mismatched payload for SetFloatingPanePinned"),
|
||||
_ => Err("Mismatched payload for StackPanes"),
|
||||
},
|
||||
Some(CommandName::ChangeFloatingPanesCoordinates) => {
|
||||
match protobuf_plugin_command.payload {
|
||||
Some(Payload::ChangeFloatingPanesCoordinatesPayload(
|
||||
change_floating_panes_coordinates_payload,
|
||||
)) => Ok(PluginCommand::ChangeFloatingPanesCoordinates(
|
||||
change_floating_panes_coordinates_payload
|
||||
.pane_ids_and_floating_panes_coordinates
|
||||
.into_iter()
|
||||
.filter_map(|p_id_a_fp| {
|
||||
let pane_id: PaneId = p_id_a_fp.pane_id?.try_into().ok()?;
|
||||
let floating_pane_coordinates: FloatingPaneCoordinates =
|
||||
p_id_a_fp.floating_pane_coordinates?.try_into().ok()?;
|
||||
Some((pane_id, floating_pane_coordinates))
|
||||
})
|
||||
.collect(),
|
||||
)),
|
||||
_ => Err("Mismatched payload for ChangeFloatingPanesCoordinates"),
|
||||
}
|
||||
},
|
||||
None => Err("Unrecognized plugin command"),
|
||||
}
|
||||
|
|
@ -2194,6 +2215,27 @@ impl TryFrom<PluginCommand> for ProtobufPluginCommand {
|
|||
.collect(),
|
||||
})),
|
||||
}),
|
||||
PluginCommand::ChangeFloatingPanesCoordinates(
|
||||
pane_ids_and_floating_panes_coordinates,
|
||||
) => Ok(ProtobufPluginCommand {
|
||||
name: CommandName::ChangeFloatingPanesCoordinates as i32,
|
||||
payload: Some(Payload::ChangeFloatingPanesCoordinatesPayload(
|
||||
ChangeFloatingPanesCoordinatesPayload {
|
||||
pane_ids_and_floating_panes_coordinates:
|
||||
pane_ids_and_floating_panes_coordinates
|
||||
.into_iter()
|
||||
.filter_map(|(p_id, floating_pane_coordinates)| {
|
||||
Some(PaneIdAndFloatingPaneCoordinates {
|
||||
pane_id: Some(p_id.try_into().ok()?),
|
||||
floating_pane_coordinates: Some(
|
||||
floating_pane_coordinates.try_into().ok()?,
|
||||
),
|
||||
})
|
||||
})
|
||||
.collect(),
|
||||
},
|
||||
)),
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue