diff --git a/.cargo/config.toml b/.cargo/config.toml index 7eeb0b9dd..9a216d74a 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -11,3 +11,6 @@ linker = "x86_64-w64-mingw32-gcc" [build] target-dir = "target" + +[env] +RUST_MIN_STACK = "16777216" diff --git a/example/default.kdl b/example/default.kdl index 2c5b2b236..a88e74bd8 100644 --- a/example/default.kdl +++ b/example/default.kdl @@ -293,6 +293,35 @@ load_plugins { // // pane_frames true +// pane_frame_style "titles" + +// Configure the look of the pane frame borders +// +// ui { +// pane_frames { +// // round the corners of single-line borders +// rounded_corners true +// // line style for all four sides of tiled pane frames +// // one of: single, double, heavy, dashed, heavy_dashed +// border_style "single" +// // per-side overrides, eg. a double top border with single sides +// border_top "double" +// border_right "single" +// border_bottom "single" +// border_left "single" +// // when two neighbouring panes disagree about a shared grid line +// // (pane frames off), that line falls back to border_style above +// // the same keys prefixed with "floating_" apply to floating panes, +// // they default to the values above +// floating_border_style "double" +// floating_border_top "double" +// floating_border_right "double" +// floating_border_bottom "double" +// floating_border_left "double" +// floating_border_rounded_corners false +// } +// } + // Toggle between having Zellij lay out panes according to a predefined set of layouts whenever possible // Options: // - true (default) diff --git a/src/main.rs b/src/main.rs index 15ed027a1..c77f854e3 100644 --- a/src/main.rs +++ b/src/main.rs @@ -52,6 +52,7 @@ fn main() { near_current_pane, no_focus, borderless, + border_style, tab_id, })) = opts.command { @@ -97,6 +98,7 @@ fn main() { near_current_pane, no_focus, borderless, + border_style, tab_id, }; commands::send_action_to_session(command_cli_action, opts.session, config); @@ -116,6 +118,7 @@ fn main() { pinned, no_focus, borderless, + border_style, tab_id, })) = opts.command { @@ -151,6 +154,7 @@ fn main() { near_current_pane: false, no_focus, borderless, + border_style, tab_id, }; commands::send_action_to_session(command_cli_action, opts.session, config); @@ -172,6 +176,7 @@ fn main() { near_current_pane, no_focus, borderless, + border_style, tab_id, })) = opts.command { @@ -198,6 +203,7 @@ fn main() { near_current_pane, no_focus, borderless, + border_style, tab_id, }; commands::send_action_to_session(command_cli_action, opts.session, config); diff --git a/zellij-client/src/web_client/server_listener.rs b/zellij-client/src/web_client/server_listener.rs index 585f31738..937db28a0 100644 --- a/zellij-client/src/web_client/server_listener.rs +++ b/zellij-client/src/web_client/server_listener.rs @@ -97,6 +97,11 @@ pub fn zellij_server_listener( colors: palette, rounded_corners: config.ui.pane_frames.rounded_corners, hide_session_name: config.ui.pane_frames.hide_session_name, + border_style: config.ui.pane_frames.resolved_border_style(), + floating_border_style: config + .ui + .pane_frames + .resolved_floating_border_style(), }, }; diff --git a/zellij-client/src/web_client/websocket_handlers.rs b/zellij-client/src/web_client/websocket_handlers.rs index 88be7b90f..330368373 100644 --- a/zellij-client/src/web_client/websocket_handlers.rs +++ b/zellij-client/src/web_client/websocket_handlers.rs @@ -368,6 +368,7 @@ fn control_payload_to_server_msg( no_focus: false, borderless: None, tab_id: None, + border_style: None, }, terminal_id: None, client_id: None, diff --git a/zellij-integration-tests/src/runner.rs b/zellij-integration-tests/src/runner.rs index c9d481ca4..15a02ee82 100644 --- a/zellij-integration-tests/src/runner.rs +++ b/zellij-integration-tests/src/runner.rs @@ -320,6 +320,7 @@ fn new_pane_cli_action( no_focus: false, borderless: None, tab_id: None, + border_style: None, } } diff --git a/zellij-integration-tests/tests/no_focus.rs b/zellij-integration-tests/tests/no_focus.rs index 5722a019b..85c06b2ad 100644 --- a/zellij-integration-tests/tests/no_focus.rs +++ b/zellij-integration-tests/tests/no_focus.rs @@ -36,6 +36,7 @@ fn no_focus_new_pane_action(command: &[&str]) -> CliAction { no_focus: true, borderless: None, tab_id: None, + border_style: None, } } diff --git a/zellij-integration-tests/tests/single_pane_tab_title.rs b/zellij-integration-tests/tests/single_pane_tab_title.rs index cc70a1632..2d134e3dc 100644 --- a/zellij-integration-tests/tests/single_pane_tab_title.rs +++ b/zellij-integration-tests/tests/single_pane_tab_title.rs @@ -40,6 +40,7 @@ fn replace_pane_in_place_with_held_command( no_focus: false, borderless: None, tab_id: None, + border_style: None, }); zellij.expect_pty_spawn() } diff --git a/zellij-server/src/lib.rs b/zellij-server/src/lib.rs index c86de1231..31698a7b6 100644 --- a/zellij-server/src/lib.rs +++ b/zellij-server/src/lib.rs @@ -464,6 +464,11 @@ impl SessionMetaData { auto_layout: new_config.options.auto_layout.unwrap_or(true), rounded_corners: new_config.ui.pane_frames.rounded_corners, hide_session_name: new_config.ui.pane_frames.hide_session_name, + border_style: new_config.ui.pane_frames.resolved_border_style(), + floating_border_style: new_config + .ui + .pane_frames + .resolved_floating_border_style(), stacked_resize: new_config.options.stacked_resize.unwrap_or(true), stacked_pane_list: new_config.options.stacked_pane_list.unwrap_or(true), default_editor: new_config.options.scrollback_editor.clone(), @@ -1026,6 +1031,11 @@ pub fn start_server_impl( .unwrap_or_else(|| default_palette().into()), rounded_corners: config.ui.pane_frames.rounded_corners, hide_session_name: config.ui.pane_frames.hide_session_name, + border_style: config.ui.pane_frames.resolved_border_style(), + floating_border_style: config + .ui + .pane_frames + .resolved_floating_border_style(), }, }; @@ -1207,6 +1217,11 @@ pub fn start_server_impl( .unwrap_or_else(|| default_palette().into()), rounded_corners: config.ui.pane_frames.rounded_corners, hide_session_name: config.ui.pane_frames.hide_session_name, + border_style: config.ui.pane_frames.resolved_border_style(), + floating_border_style: config + .ui + .pane_frames + .resolved_floating_border_style(), }, }; diff --git a/zellij-server/src/pane_groups.rs b/zellij-server/src/pane_groups.rs index eb0fcb3de..7e0f762f8 100644 --- a/zellij-server/src/pane_groups.rs +++ b/zellij-server/src/pane_groups.rs @@ -169,6 +169,7 @@ impl PaneGroups { height: Some(PercentOrFixed::Fixed(height)), pinned: Some(true), borderless: Some(false), + border_style: None, }; let _ = self.senders.send_to_pty(PtyInstruction::FillPluginCwd( should_float, diff --git a/zellij-server/src/panes/floating_panes/mod.rs b/zellij-server/src/panes/floating_panes/mod.rs index bf6b98cb8..b89d9d0e9 100644 --- a/zellij-server/src/panes/floating_panes/mod.rs +++ b/zellij-server/src/panes/floating_panes/mod.rs @@ -1,6 +1,6 @@ pub mod floating_pane_grid; use zellij_utils::{ - data::{Direction, FloatingPaneCoordinates, PaneInfo, ResizeStrategy}, + data::{BorderStyle, Direction, FloatingPaneCoordinates, PaneInfo, ResizeStrategy}, position::Position, }; @@ -1710,6 +1710,17 @@ impl FloatingPanes { pane.update_rounded_corners(rounded_corners); } } + pub fn update_border_styles( + &mut self, + border_style: BorderStyle, + floating_border_style: BorderStyle, + ) { + self.style.border_style = border_style; + self.style.floating_border_style = floating_border_style; + for pane in self.panes.values_mut() { + pane.invalidate_frame_cache(); + } + } pub fn next_selectable_pane_id_above(&mut self, pane_id: &PaneId) -> Option { let display_area = *self.display_area.borrow(); let viewport = *self.viewport.borrow(); diff --git a/zellij-server/src/panes/plugin_pane.rs b/zellij-server/src/panes/plugin_pane.rs index de426fd51..d6b0e4d45 100644 --- a/zellij-server/src/panes/plugin_pane.rs +++ b/zellij-server/src/panes/plugin_pane.rs @@ -22,7 +22,8 @@ use std::rc::Rc; use vte; use zellij_utils::data::PaneContents; use zellij_utils::data::{ - BareKey, KeyWithModifier, PermissionStatus, PermissionType, PluginPermission, + BareKey, BorderStyleOverride, KeyWithModifier, PermissionStatus, PermissionType, + PluginPermission, }; use zellij_utils::pane_size::{Offset, SizeInPixels}; use zellij_utils::position::Position; @@ -99,6 +100,7 @@ pub(crate) struct PluginPane { prev_pane_name: String, frame: HashMap, borderless: bool, + border_style_override: BorderStyleOverride, exclude_from_sync: bool, pane_frame_color_override: Option<(PaletteColor, Option)>, invoked_with: Option, @@ -146,6 +148,7 @@ impl PluginPane { content_offset: Offset::default(), pane_title: title, borderless: false, + border_style_override: BorderStyleOverride::default(), pane_name: pane_name.clone(), prev_pane_name: pane_name, terminal_emulator_colors, @@ -747,6 +750,14 @@ impl Pane for PluginPane { fn borderless(&self) -> bool { self.borderless } + fn set_border_style_override(&mut self, border_style_override: BorderStyleOverride) { + self.border_style_override = border_style_override; + self.frame.clear(); + self.set_should_render(true); + } + fn border_style_override(&self) -> BorderStyleOverride { + self.border_style_override + } fn set_exclude_from_sync(&mut self, exclude_from_sync: bool) { self.exclude_from_sync = exclude_from_sync; } @@ -847,6 +858,10 @@ impl Pane for PluginPane { self.style.rounded_corners = rounded_corners; self.frame.clear(); } + fn invalidate_frame_cache(&mut self) { + self.frame.clear(); + self.set_should_render(true); + } fn set_should_be_suppressed(&mut self, should_be_suppressed: bool) { self.should_be_suppressed = should_be_suppressed; } diff --git a/zellij-server/src/panes/terminal_pane.rs b/zellij-server/src/panes/terminal_pane.rs index 9230b2459..f226ad375 100644 --- a/zellij-server/src/panes/terminal_pane.rs +++ b/zellij-server/src/panes/terminal_pane.rs @@ -25,8 +25,8 @@ use zellij_utils::input::mouse::{MouseEvent, MouseEventType}; use zellij_utils::pane_size::Offset; use zellij_utils::{ data::{ - BareKey, InputMode, KeyWithModifier, Palette, PaletteColor, PaneId as ZellijUtilsPaneId, - RegexHighlight, Style, Styling, + BareKey, BorderStyleOverride, InputMode, KeyWithModifier, Palette, PaletteColor, + PaneId as ZellijUtilsPaneId, RegexHighlight, Style, Styling, }, errors::prelude::*, input::layout::Run, @@ -145,6 +145,7 @@ pub struct TerminalPane { prev_pane_name: String, frame: HashMap, borderless: bool, + border_style_override: BorderStyleOverride, exclude_from_sync: bool, fake_cursor_locations: HashSet<(usize, usize)>, // (x, y) - these hold a record of previous fake cursors which we need to clear on render search_term: String, @@ -990,6 +991,14 @@ impl Pane for TerminalPane { self.set_content_offset(Offset::frame(1)); } } + fn set_border_style_override(&mut self, border_style_override: BorderStyleOverride) { + self.border_style_override = border_style_override; + self.frame.clear(); + self.set_should_render(true); + } + fn border_style_override(&self) -> BorderStyleOverride { + self.border_style_override + } fn set_exclude_from_sync(&mut self, exclude_from_sync: bool) { self.exclude_from_sync = exclude_from_sync; @@ -1232,6 +1241,10 @@ impl Pane for TerminalPane { self.style.rounded_corners = rounded_corners; self.frame.clear(); } + fn invalidate_frame_cache(&mut self) { + self.frame.clear(); + self.set_should_render(true); + } fn drain_fake_cursors(&mut self) -> Option> { if !self.fake_cursor_locations.is_empty() { for (y, _x) in &self.fake_cursor_locations { @@ -1418,6 +1431,7 @@ impl TerminalPane { pane_name: pane_name.clone(), prev_pane_name: pane_name, borderless: false, + border_style_override: BorderStyleOverride::default(), exclude_from_sync: false, fake_cursor_locations: HashSet::new(), search_term: String::new(), diff --git a/zellij-server/src/panes/tiled_panes/mod.rs b/zellij-server/src/panes/tiled_panes/mod.rs index 768dff36f..ca31a75fb 100644 --- a/zellij-server/src/panes/tiled_panes/mod.rs +++ b/zellij-server/src/panes/tiled_panes/mod.rs @@ -28,7 +28,10 @@ use crate::{ }; use stacked_panes::StackedPanes; use zellij_utils::{ - data::{Direction, ModeInfo, PaneInfo, Resize, ResizeStrategy, Style, Styling}, + data::{ + BorderStyle, BorderStyleOverride, Direction, LineStyle, ModeInfo, PaneInfo, Resize, + ResizeStrategy, Style, Styling, + }, errors::prelude::*, input::{ command::RunCommand, @@ -1161,6 +1164,11 @@ impl TiledPanes { let connected_clients: Vec = connected_clients.into_iter().collect(); let multiple_users_exist_in_session = { self.connected_clients_in_app.borrow().len() > 1 }; let mut client_id_to_boundaries: HashMap = HashMap::new(); + let fallback_line_style = self + .style + .border_style + .uniform_style() + .unwrap_or(LineStyle::Single); let active_panes = if floating_panes_are_visible { HashMap::new() } else { @@ -1317,9 +1325,12 @@ impl TiledPanes { pane_is_selectable, ) .with_context(err_context)?; - let boundaries = client_id_to_boundaries - .entry(*client_id) - .or_insert_with(|| Boundaries::new(*self.viewport.borrow())); + let boundaries = + client_id_to_boundaries + .entry(*client_id) + .or_insert_with(|| { + Boundaries::new(*self.viewport.borrow(), fallback_line_style) + }); pane_contents_and_ui.render_pane_boundaries( *client_id, client_mode, @@ -1329,9 +1340,12 @@ impl TiledPanes { pane_is_on_bottom_of_stack, ); } else { - let boundaries = client_id_to_boundaries - .entry(*client_id) - .or_insert_with(|| Boundaries::new(*self.viewport.borrow())); + let boundaries = + client_id_to_boundaries + .entry(*client_id) + .or_insert_with(|| { + Boundaries::new(*self.viewport.borrow(), fallback_line_style) + }); pane_contents_and_ui.render_pane_boundaries( *client_id, client_mode, @@ -1415,6 +1429,7 @@ impl TiledPanes { run: Option, geom: PaneGeom, should_be_borderless: Option, + border_style: Option, ) { match self .panes @@ -1423,6 +1438,9 @@ impl TiledPanes { { Some((_, pane)) => { pane.set_geom(geom); + if let Some(border_style) = border_style { + pane.set_border_style_override(border_style); + } if let Some(should_be_borderless) = should_be_borderless { pane.set_borderless(should_be_borderless); @@ -3130,6 +3148,17 @@ impl TiledPanes { pane.update_rounded_corners(rounded_corners); } } + pub fn update_border_styles( + &mut self, + border_style: BorderStyle, + floating_border_style: BorderStyle, + ) { + self.style.border_style = border_style; + self.style.floating_border_style = floating_border_style; + for pane in self.panes.values_mut() { + pane.invalidate_frame_cache(); + } + } pub fn stack_panes( &mut self, root_pane_id: PaneId, diff --git a/zellij-server/src/plugins/zellij_exports.rs b/zellij-server/src/plugins/zellij_exports.rs index ca55c3421..6ab0bd1d6 100644 --- a/zellij-server/src/plugins/zellij_exports.rs +++ b/zellij-server/src/plugins/zellij_exports.rs @@ -21,13 +21,14 @@ use tokio::sync::oneshot; use wasmi::{Caller, Linker}; use zellij_utils::consts::ipc_connect; use zellij_utils::data::{ - BreakPanesToNewTabResponse, BreakPanesToTabWithIdResponse, BreakPanesToTabWithIndexResponse, - CommandType, ConnectToSession, DeleteAllDeadSessionsResponse, DeleteDeadSessionResponse, - DeleteLayoutResponse, EditLayoutResponse, Event, FloatingPaneCoordinates, - FocusOrCreateTabResponse, GetFocusedPaneInfoResponse, GetPaneCwdResponse, GetPanePidResponse, - GetPaneRunningCommandResponse, HttpVerb, KeyWithModifier, KillSessionsResponse, LayoutInfo, - LayoutMetadata, LayoutParsingError, MessageToPlugin, NewPanePlacement, NewTabResponse, - NewTabUnfocusedResponse, NewTiledPaneInTabResponse, OpenCommandPaneBackgroundResponse, + BorderStyleOverride, BreakPanesToNewTabResponse, BreakPanesToTabWithIdResponse, + BreakPanesToTabWithIndexResponse, CommandType, ConnectToSession, DeleteAllDeadSessionsResponse, + DeleteDeadSessionResponse, DeleteLayoutResponse, EditLayoutResponse, Event, + FloatingPaneCoordinates, FocusOrCreateTabResponse, GetFocusedPaneInfoResponse, + GetPaneCwdResponse, GetPanePidResponse, GetPaneRunningCommandResponse, HttpVerb, + KeyWithModifier, KillSessionsResponse, LayoutInfo, LayoutMetadata, LayoutParsingError, + MessageToPlugin, NewPanePlacement, NewTabResponse, NewTabUnfocusedResponse, + NewTiledPaneInTabResponse, OpenCommandPaneBackgroundResponse, OpenCommandPaneFloatingNearPluginResponse, OpenCommandPaneFloatingResponse, OpenCommandPaneInPlaceOfPaneIdResponse, OpenCommandPaneInPlaceOfPluginResponse, OpenCommandPaneInPlaceResponse, OpenCommandPaneNearPluginResponse, OpenCommandPaneResponse, @@ -199,21 +200,35 @@ fn host_run_plugin_command(mut caller: Caller<'_, PluginEnv>) { floating_pane_coordinates, context, ) => open_file_floating(env, file_to_open, floating_pane_coordinates, context), - PluginCommand::OpenTerminal(cwd) => open_terminal(env, cwd.path.try_into()?), + PluginCommand::OpenTerminal(cwd) => { + let border_style = cwd.border_style; + open_terminal(env, cwd.path.try_into()?, border_style) + }, PluginCommand::OpenTerminalNearPlugin(cwd) => { - open_terminal_near_plugin(env, cwd.path.try_into()?) + let border_style = cwd.border_style; + open_terminal_near_plugin(env, cwd.path.try_into()?, border_style) }, PluginCommand::OpenTerminalFloating(cwd, floating_pane_coordinates) => { + let floating_pane_coordinates = FloatingPaneCoordinates::merge_border_style( + floating_pane_coordinates, + cwd.border_style, + ); open_terminal_floating(env, cwd.path.try_into()?, floating_pane_coordinates) }, PluginCommand::OpenTerminalFloatingNearPlugin( cwd, floating_pane_coordinates, - ) => open_terminal_floating_near_plugin( - env, - cwd.path.try_into()?, - floating_pane_coordinates, - ), + ) => { + let floating_pane_coordinates = FloatingPaneCoordinates::merge_border_style( + floating_pane_coordinates, + cwd.border_style, + ); + open_terminal_floating_near_plugin( + env, + cwd.path.try_into()?, + floating_pane_coordinates, + ) + }, PluginCommand::OpenCommandPane(command_to_run, context) => { open_command_pane(env, command_to_run, context) }, @@ -628,6 +643,9 @@ fn host_run_plugin_command(mut caller: Caller<'_, PluginEnv>) { PluginCommand::TogglePaneBorderless(pane_id) => { toggle_pane_borderless(env, pane_id.into()) }, + PluginCommand::SetPaneBorderStyle(pane_id, border_style) => { + set_pane_border_style(env, pane_id.into(), border_style) + }, PluginCommand::SetPaneBorderless(pane_id, borderless) => { set_pane_borderless(env, pane_id.into(), borderless) }, @@ -1214,6 +1232,7 @@ fn open_editor_pane_in_new_tab( .map(|cwd| translate_plugin_path(env, cwd)) .or_else(|| Some(env.plugin_cwd.clone())); let file_to_open = FileToOpen { + border_style: None, path, cwd, ..file_to_open @@ -1413,6 +1432,7 @@ fn open_file(env: &PluginEnv, file_to_open: FileToOpen, context: BTreeMap, ) { + let border_style = file_to_open.border_style; let cwd = file_to_open .cwd .map(|cwd| translate_plugin_path(env, cwd)) @@ -1606,7 +1631,7 @@ fn open_file_near_plugin( let pty_instr = PtyInstruction::SpawnTerminal( Some(open_file), Some(title), - NewPanePlacement::default(), + NewPanePlacement::default().with_border_style(border_style), start_suppressed, ClientTabIndexOrPaneId::PaneId(PaneId::Plugin(env.plugin_id)), Some(NotificationEnd::new(completion_tx)), @@ -1631,6 +1656,10 @@ fn open_file_floating_near_plugin( floating_pane_coordinates: Option, context: BTreeMap, ) { + let floating_pane_coordinates = FloatingPaneCoordinates::merge_border_style( + floating_pane_coordinates, + file_to_open.border_style, + ); let cwd = file_to_open .cwd .map(|cwd| translate_plugin_path(env, cwd)) @@ -1708,7 +1737,7 @@ fn open_file_in_place_of_plugin( .non_fatal(); } -fn open_terminal(env: &PluginEnv, cwd: PathBuf) { +fn open_terminal(env: &PluginEnv, cwd: PathBuf, border_style: Option) { let error_msg = || format!("failed to open file in plugin {}", env.name()); let cwd = translate_plugin_path(env, cwd); let mut default_shell = env.default_shell.clone().unwrap_or_else(|| { @@ -1730,6 +1759,7 @@ fn open_terminal(env: &PluginEnv, cwd: PathBuf) { near_current_pane: false, no_focus: false, borderless: None, + border_style, tab_id: None, }; let result = apply_action!(action, error_msg, env); @@ -1744,7 +1774,11 @@ fn open_terminal(env: &PluginEnv, cwd: PathBuf) { .non_fatal(); } -fn open_terminal_near_plugin(env: &PluginEnv, cwd: PathBuf) { +fn open_terminal_near_plugin( + env: &PluginEnv, + cwd: PathBuf, + border_style: Option, +) { let cwd = translate_plugin_path(env, cwd); let mut default_shell = env.default_shell.clone().unwrap_or_else(|| { TerminalAction::RunCommand(RunCommand { @@ -1764,6 +1798,7 @@ fn open_terminal_near_plugin(env: &PluginEnv, cwd: PathBuf) { NewPanePlacement::Tiled { direction: None, borderless: None, + border_style, }, false, ClientTabIndexOrPaneId::PaneId(PaneId::Plugin(env.plugin_id)), @@ -1946,6 +1981,7 @@ fn open_command_pane_in_place_of_plugin( context: BTreeMap, ) { let command = command_to_run.path; + let _border_style = command_to_run.border_style; let cwd = command_to_run .cwd .map(|cwd| translate_plugin_path(env, cwd)); @@ -2045,6 +2081,7 @@ fn open_command_pane_in_place_of_pane_id( context: BTreeMap, ) { let command = command_to_run.path; + let _border_style = command_to_run.border_style; let cwd = command_to_run .cwd .map(|cwd| translate_plugin_path(env, cwd)); @@ -2140,6 +2177,7 @@ fn open_command_pane( ) { let error_msg = || format!("failed to open command in plugin {}", env.name()); let command = command_to_run.path; + let border_style = command_to_run.border_style; let cwd = command_to_run .cwd .map(|cwd| translate_plugin_path(env, cwd)); @@ -2170,6 +2208,7 @@ fn open_command_pane( near_current_pane: false, no_focus: false, borderless: None, + border_style, tab_id: None, }; let result = apply_action!(action, error_msg, env); @@ -2191,6 +2230,7 @@ fn open_command_pane_near_plugin( context: BTreeMap, ) { let command = command_to_run.path; + let border_style = command_to_run.border_style; let cwd = command_to_run .cwd .map(|cwd| translate_plugin_path(env, cwd)); @@ -2224,6 +2264,7 @@ fn open_command_pane_near_plugin( NewPanePlacement::Tiled { direction: None, borderless: None, + border_style, }, false, ClientTabIndexOrPaneId::PaneId(PaneId::Plugin(env.plugin_id)), @@ -2250,6 +2291,7 @@ fn open_command_pane_floating( ) { let error_msg = || format!("failed to open command in plugin {}", env.name()); let command = command_to_run.path; + let border_style = command_to_run.border_style; let cwd = command_to_run .cwd .map(|cwd| translate_plugin_path(env, cwd)); @@ -2276,7 +2318,10 @@ fn open_command_pane_floating( let action = Action::NewFloatingPane { command: Some(run_command_action), pane_name: name, - coordinates: floating_pane_coordinates, + coordinates: FloatingPaneCoordinates::merge_border_style( + floating_pane_coordinates, + border_style, + ), near_current_pane: false, no_focus: false, tab_id: None, @@ -2301,6 +2346,7 @@ fn open_command_pane_floating_near_plugin( context: BTreeMap, ) { let command = command_to_run.path; + let border_style = command_to_run.border_style; let cwd = command_to_run .cwd .map(|cwd| translate_plugin_path(env, cwd)); @@ -2331,7 +2377,10 @@ fn open_command_pane_floating_near_plugin( let _ = env.senders.send_to_pty(PtyInstruction::SpawnTerminal( Some(run_cmd), name, - NewPanePlacement::Floating(floating_pane_coordinates), + NewPanePlacement::Floating(FloatingPaneCoordinates::merge_border_style( + floating_pane_coordinates, + border_style, + )), false, ClientTabIndexOrPaneId::PaneId(PaneId::Plugin(env.plugin_id)), Some(NotificationEnd::new(completion_tx)), @@ -2361,6 +2410,7 @@ fn open_command_pane_in_place( ) { let error_msg = || format!("failed to open command in plugin {}", env.name()); let command = command_to_run.path; + let _border_style = command_to_run.border_style; let cwd = command_to_run .cwd .map(|cwd| translate_plugin_path(env, cwd)); @@ -2875,6 +2925,7 @@ fn new_tiled_pane_in_tab(env: &PluginEnv, tab_position: usize) { near_current_pane: false, no_focus: false, borderless: None, + border_style: None, tab_id: Some(tab_position), }; let result = apply_action!(action, error_msg, env); @@ -3968,6 +4019,16 @@ fn set_pane_borderless(env: &PluginEnv, pane_id: PaneId, borderless: bool) { )); } +fn set_pane_border_style(env: &PluginEnv, pane_id: PaneId, border_style: BorderStyleOverride) { + let _ = env + .senders + .send_to_screen(ScreenInstruction::SetPaneBorderStyle( + pane_id, + border_style, + None, + )); +} + fn set_pane_color(env: &PluginEnv, pane_id: PaneId, fg: Option, bg: Option) { let _ = env .senders @@ -4708,6 +4769,7 @@ fn try_edit_layout( // Create FileToOpen for the layout file let file_to_open = FileToOpen { + border_style: None, path: file_path, line_number: None, cwd: Some(layout_dir.clone()), @@ -5579,6 +5641,7 @@ fn check_command_permission( | PluginCommand::ChangeFloatingPanesCoordinates(..) | PluginCommand::TogglePaneBorderless(..) | PluginCommand::SetPaneBorderless(..) + | PluginCommand::SetPaneBorderStyle(..) | PluginCommand::SetPaneColor(..) | PluginCommand::GroupAndUngroupPanes(..) | PluginCommand::HighlightAndUnhighlightPanes(..) diff --git a/zellij-server/src/route.rs b/zellij-server/src/route.rs index 21dd2bdf6..07ac4cc0f 100644 --- a/zellij-server/src/route.rs +++ b/zellij-server/src/route.rs @@ -678,8 +678,12 @@ pub(crate) fn route_action( Some(direction) => NewPanePlacement::Tiled { direction: Some(direction), borderless: None, + border_style: None, + }, + None => NewPanePlacement::NoPreference { + borderless: None, + border_style: None, }, - None => NewPanePlacement::NoPreference { borderless: None }, }; senders .send_to_pty(PtyInstruction::SpawnTerminal( @@ -782,7 +786,12 @@ pub(crate) fn route_action( } else { NewPanePlacement::Tiled { direction: split_direction, - borderless: None, + borderless: floating_pane_coordinates + .as_ref() + .and_then(|c| c.borderless), + border_style: floating_pane_coordinates + .as_ref() + .and_then(|c| c.border_style), } }, start_suppressed, @@ -889,6 +898,7 @@ pub(crate) fn route_action( NewPanePlacement::Stacked { pane_id_to_stack_under: None, borderless: None, + border_style: None, }, if no_focus { ClientTabIndexOrPaneId::TabIndexNoFocus(tab_id) @@ -902,6 +912,7 @@ pub(crate) fn route_action( NewPanePlacement::Stacked { pane_id_to_stack_under: Some(pane_id.into()), borderless: None, + border_style: None, }, ClientTabIndexOrPaneId::PaneId(pane_id), ) @@ -910,6 +921,7 @@ pub(crate) fn route_action( NewPanePlacement::Stacked { pane_id_to_stack_under: None, borderless: None, + border_style: None, }, if no_focus { ClientTabIndexOrPaneId::ClientIdNoFocus(client_id) @@ -937,6 +949,7 @@ pub(crate) fn route_action( near_current_pane, no_focus, borderless, + border_style, tab_id, } => { let run_cmd = run_command @@ -951,6 +964,7 @@ pub(crate) fn route_action( NewPanePlacement::Tiled { direction, borderless, + border_style, }, false, client_tab_index_or_paneid, @@ -1008,6 +1022,7 @@ pub(crate) fn route_action( NewPanePlacement::Tiled { direction: command.direction, borderless: None, + border_style: None, }, false, client_tab_index_or_paneid, @@ -1915,6 +1930,18 @@ pub(crate) fn route_action( )) .with_context(err_context)?; }, + Action::SetPaneBorderStyle { + pane_id, + border_style, + } => { + senders + .send_to_screen(ScreenInstruction::SetPaneBorderStyle( + pane_id.into(), + border_style, + Some(NotificationEnd::new(completion_tx)), + )) + .with_context(err_context)?; + }, Action::TogglePaneInGroup => { senders .send_to_screen(ScreenInstruction::TogglePaneInGroup( diff --git a/zellij-server/src/screen.rs b/zellij-server/src/screen.rs index 315b40a5d..ea8f724ee 100644 --- a/zellij-server/src/screen.rs +++ b/zellij-server/src/screen.rs @@ -40,11 +40,12 @@ use crate::route::NotificationEnd; use log::{debug, warn}; use zellij_utils::data::{ - CommandOrPlugin, Direction, EventType, FloatingPaneCoordinates, GetFocusedPaneInfoResponse, - HostTerminalThemeMode, KeyWithModifier, LayoutInfo, LayoutWithError, ListPanesResponse, - ListTabsResponse, NewPanePlacement, PaneContents, PaneInfo, PaneListEntry, PaneManifest, - PaneRenderReport, PaneScrollbackResponse, PluginPermission, RegexHighlight, Resize, - ResizeStrategy, SessionInfo, Styling, TabInfo, ThemeHue, WebSharing, + BorderStyle, BorderStyleOverride, CommandOrPlugin, Direction, EventType, + FloatingPaneCoordinates, GetFocusedPaneInfoResponse, HostTerminalThemeMode, KeyWithModifier, + LayoutInfo, LayoutWithError, ListPanesResponse, ListTabsResponse, NewPanePlacement, + PaneContents, PaneInfo, PaneListEntry, PaneManifest, PaneRenderReport, PaneScrollbackResponse, + PluginPermission, RegexHighlight, Resize, ResizeStrategy, SessionInfo, Styling, TabInfo, + ThemeHue, WebSharing, }; use zellij_utils::errors::prelude::*; use zellij_utils::input::actions::Action; @@ -814,6 +815,8 @@ pub enum ScreenInstruction { auto_layout: bool, rounded_corners: bool, hide_session_name: bool, + border_style: BorderStyle, + floating_border_style: BorderStyle, stacked_resize: bool, stacked_pane_list: bool, default_editor: Option, @@ -892,6 +895,7 @@ pub enum ScreenInstruction { ), TogglePaneBorderless(PaneId, Option), SetPaneBorderless(PaneId, bool, Option), + SetPaneBorderStyle(PaneId, BorderStyleOverride, Option), AddHighlightPaneFrameColorOverride(Vec, Option), // Option => optional // message GroupAndUngroupPanes(Vec, Vec, bool, ClientId), // panes_to_group, panes_to_ungroup, bool -> for all clients @@ -1263,6 +1267,7 @@ impl From<&ScreenInstruction> for ScreenContext { }, ScreenInstruction::TogglePaneBorderless(..) => ScreenContext::TogglePaneBorderless, ScreenInstruction::SetPaneBorderless(..) => ScreenContext::SetPaneBorderless, + ScreenInstruction::SetPaneBorderStyle(..) => ScreenContext::SetPaneBorderStyle, ScreenInstruction::AddHighlightPaneFrameColorOverride(..) => { ScreenContext::AddHighlightPaneFrameColorOverride }, @@ -6726,6 +6731,7 @@ impl Screen { height: Some(PercentOrFixed::Fixed(pane.rows())), pinned: Some(pane.current_geom().is_pinned), borderless: Some(pane.borderless()), + border_style: None, }; new_active_tab.add_floating_pane( pane, @@ -6894,6 +6900,8 @@ impl Screen { auto_layout: bool, rounded_corners: bool, hide_session_name: bool, + border_style: BorderStyle, + floating_border_style: BorderStyle, stacked_resize: bool, stacked_pane_list: bool, default_editor: Option, @@ -6920,6 +6928,10 @@ impl Screen { self.default_mode_info.update_theme(theme); self.default_mode_info .update_rounded_corners(rounded_corners); + self.default_mode_info + .update_border_styles(border_style, floating_border_style); + self.style.border_style = border_style; + self.style.floating_border_style = floating_border_style; // `default_mode_info` is the fallback used by `change_mode` for // clients that don't yet have a per-client `mode_info` entry, so its // keybinds and base mode must be kept in sync with reconfigures. @@ -6960,6 +6972,7 @@ impl Screen { for tab in self.tabs.values_mut() { tab.update_theme(theme); tab.update_rounded_corners(rounded_corners); + tab.update_border_styles(border_style, floating_border_style); tab.update_default_shell(default_shell.clone()); tab.update_default_editor(self.default_editor.clone()); tab.update_auto_layout(auto_layout); @@ -7308,6 +7321,14 @@ impl Screen { } } } + pub fn set_pane_border_style(&mut self, pane_id: PaneId, border_style: BorderStyleOverride) { + for (_tab_id, tab) in self.tabs.iter_mut() { + if tab.has_pane_with_pid(&pane_id) { + tab.set_pane_border_style(pane_id, border_style); + break; + } + } + } pub fn handle_mouse_event(&mut self, event: MouseEvent, client_id: ClientId) { let is_bare_motion = event.event_type == MouseEventType::Motion && !event.left @@ -7524,6 +7545,7 @@ impl Screen { focused_clients, default_fg, default_bg, + p.border_style_override(), ) }) .collect(); @@ -7553,7 +7575,7 @@ impl Screen { PaneLayoutMetadata::new( pane_id, p.position_and_size(), - false, // floating panes are never borderless + p.borderless(), p.invoked_with().clone(), p.custom_title(), !focused_clients.is_empty(), @@ -7565,6 +7587,7 @@ impl Screen { focused_clients, default_fg, default_bg, + p.border_style_override(), ) }) .collect(); @@ -8429,6 +8452,7 @@ pub(crate) fn screen_thread_main( NewPanePlacement::Tiled { direction: Some(direction), borderless, + border_style: None, } => { if direction == Direction::Left || direction == Direction::Right @@ -10894,6 +10918,7 @@ pub(crate) fn screen_thread_main( new_pane_placement = NewPanePlacement::Tiled { direction: None, borderless: None, + border_style: None, }; } if should_be_in_place { @@ -11669,6 +11694,8 @@ pub(crate) fn screen_thread_main( auto_layout, rounded_corners, hide_session_name, + border_style, + floating_border_style, stacked_resize, stacked_pane_list, default_editor, @@ -11702,6 +11729,8 @@ pub(crate) fn screen_thread_main( auto_layout, rounded_corners, hide_session_name, + border_style, + floating_border_style, stacked_resize, stacked_pane_list, default_editor, @@ -12128,6 +12157,10 @@ pub(crate) fn screen_thread_main( screen.set_pane_borderless(pane_id, borderless); let _ = screen.render(None); }, + ScreenInstruction::SetPaneBorderStyle(pane_id, border_style, _completion_tx) => { + screen.set_pane_border_style(pane_id, border_style); + let _ = screen.render(None); + }, ScreenInstruction::GroupAndUngroupPanes( pane_ids_to_group, pane_ids_to_ungroup, @@ -12904,7 +12937,9 @@ pub(crate) fn screen_thread_main( mut _completion_tx, ) => { if let Some(tab) = screen.tabs.get_mut(&tab_id) { - let applied = tab.apply_floating_swap_layout(&layout_name).unwrap_or(false); + let applied = tab + .apply_floating_swap_layout(&layout_name) + .unwrap_or(false); if !applied { log::error!( "Floating swap layout not found or incompatible: {layout_name}" diff --git a/zellij-server/src/session_layout_metadata.rs b/zellij-server/src/session_layout_metadata.rs index 62efea986..0bcf2d17f 100644 --- a/zellij-server/src/session_layout_metadata.rs +++ b/zellij-server/src/session_layout_metadata.rs @@ -6,7 +6,7 @@ use std::time::{SystemTime, UNIX_EPOCH}; use zellij_utils::common_path::common_path_all; use zellij_utils::pane_size::PaneGeom; use zellij_utils::{ - data::{LayoutMetadata, PaneMetadata, TabMetadata}, + data::{BorderStyleOverride, LayoutMetadata, PaneMetadata, TabMetadata}, input::command::RunCommand, input::layout::{Layout, Run, RunPlugin, RunPluginOrAlias}, input::plugins::PluginAliases, @@ -535,6 +535,7 @@ impl Into for PaneLayoutMetadata { pane_contents: self.pane_contents, default_fg: self.default_fg, default_bg: self.default_bg, + border_style: self.border_style.none_if_empty(), } } } @@ -561,6 +562,7 @@ pub struct PaneLayoutMetadata { focused_clients: Vec, default_fg: Option, default_bg: Option, + border_style: BorderStyleOverride, } impl PaneLayoutMetadata { @@ -575,6 +577,7 @@ impl PaneLayoutMetadata { focused_clients: Vec, default_fg: Option, default_bg: Option, + border_style: BorderStyleOverride, ) -> Self { PaneLayoutMetadata { id, @@ -588,6 +591,7 @@ impl PaneLayoutMetadata { focused_clients, default_fg, default_bg, + border_style, } } fn to_pane_metadata(&self) -> PaneMetadata { @@ -701,6 +705,7 @@ mod tests { vec![], None, None, + Default::default(), ) } @@ -720,6 +725,7 @@ mod tests { vec![], None, None, + Default::default(), ) } diff --git a/zellij-server/src/tab/layout_applier.rs b/zellij-server/src/tab/layout_applier.rs index 44e100a43..136bd3eaf 100644 --- a/zellij-server/src/tab/layout_applier.rs +++ b/zellij-server/src/tab/layout_applier.rs @@ -552,6 +552,7 @@ impl<'a> LayoutApplier<'a> { } new_plugin.set_borderless(layout.borderless.unwrap_or(false)); + new_plugin.set_border_style_override(layout.border_style.unwrap_or_default()); if let Some(exclude_from_sync) = layout.exclude_from_sync { new_plugin.set_exclude_from_sync(exclude_from_sync); } @@ -605,6 +606,7 @@ impl<'a> LayoutApplier<'a> { } else { new_pane.set_borderless(false); } + new_pane.set_border_style_override(floating_pane_layout.border_style.unwrap_or_default()); resize_pty!( new_pane, self.os_api, @@ -662,6 +664,7 @@ impl<'a> LayoutApplier<'a> { } else { new_pane.set_borderless(false); } + new_pane.set_border_style_override(floating_pane_layout.border_style.unwrap_or_default()); if floating_pane_layout.default_fg.is_some() || floating_pane_layout.default_bg.is_some() { new_pane.set_pane_default_colors( floating_pane_layout.default_fg.clone(), @@ -735,6 +738,7 @@ impl<'a> LayoutApplier<'a> { new_pane.handle_pty_bytes("\n\r".as_bytes().into()); } new_pane.set_borderless(layout.borderless.unwrap_or(false)); + new_pane.set_border_style_override(layout.border_style.unwrap_or_default()); if let Some(exclude_from_sync) = layout.exclude_from_sync { new_pane.set_exclude_from_sync(exclude_from_sync); } @@ -764,6 +768,7 @@ impl<'a> LayoutApplier<'a> { layout.run, position_and_size, layout.borderless, + layout.border_style, ); found_exact_match = true; } @@ -784,6 +789,7 @@ impl<'a> LayoutApplier<'a> { run_instruction.clone(), position_and_size, layout.borderless, + layout.border_style, ); found_empty_location = true; } @@ -1331,6 +1337,9 @@ impl<'a> PaneApplier<'a> { if let Some(should_be_borderless) = floating_panes_layout.borderless { pane.set_borderless(should_be_borderless); } + if let Some(border_style) = floating_panes_layout.border_style { + pane.set_border_style_override(border_style); + } self.apply_position_and_size_to_floating_pane(pane, position_and_size); Ok(()) } @@ -1410,6 +1419,9 @@ impl<'a> PaneApplier<'a> { if let Some(should_be_borderless) = layout.borderless { pane.set_borderless(should_be_borderless); } + if let Some(border_style) = layout.border_style { + pane.set_border_style_override(border_style); + } if let Some(pane_title) = layout.name.as_ref() { pane.set_title(pane_title.into()); } diff --git a/zellij-server/src/tab/mod.rs b/zellij-server/src/tab/mod.rs index f7dc12a3c..bac5012d1 100644 --- a/zellij-server/src/tab/mod.rs +++ b/zellij-server/src/tab/mod.rs @@ -17,8 +17,9 @@ use unicode_width::UnicodeWidthStr; use uuid::Uuid; use zellij_utils::data::PaneContents; use zellij_utils::data::{ - Direction, KeyWithModifier, NewPanePlacement, PaneInfo, PermissionStatus, PermissionType, - PluginPermission, RegexHighlight, ResizeStrategy, Style, StyledText, WebSharing, + BorderStyle, BorderStyleOverride, Direction, KeyWithModifier, NewPanePlacement, PaneInfo, + PermissionStatus, PermissionType, PluginPermission, RegexHighlight, ResizeStrategy, Style, + StyledText, WebSharing, }; use zellij_utils::errors::prelude::*; use zellij_utils::input::command::RunCommand; @@ -627,6 +628,10 @@ pub trait Pane { fn load_pane_name(&mut self); fn set_borderless(&mut self, borderless: bool); fn borderless(&self) -> bool; + fn set_border_style_override(&mut self, _border_style: BorderStyleOverride) {} + fn border_style_override(&self) -> BorderStyleOverride { + BorderStyleOverride::default() + } fn set_exclude_from_sync(&mut self, exclude_from_sync: bool); fn exclude_from_sync(&self) -> bool; @@ -754,6 +759,7 @@ pub trait Pane { fn update_kitty_host_support(&mut self, _supported: KittyHostSupport) {} fn update_sixel_host_support(&mut self, _supported: bool) {} fn update_rounded_corners(&mut self, _rounded_corners: bool) {} + fn invalidate_frame_cache(&mut self) {} fn set_should_be_suppressed(&mut self, _should_be_suppressed: bool) {} fn query_should_be_suppressed(&self) -> bool { false @@ -2028,14 +2034,17 @@ impl Tab { self.tiled_panes.unset_fullscreen(); } self.dissolve_stack_lists_for_classic_mutation(); - self.swap_layouts.set_current_tiled_layout_position(position); + self.swap_layouts + .set_current_tiled_layout_position(position); self.apply_tiled_layout_candidate(Some(layout_candidate))?; Ok(true) } pub fn apply_floating_swap_layout(&mut self, layout_name: &str) -> Result { - let Some((position, layout_candidate)) = self - .swap_layouts - .floating_layout_candidate_by_name(layout_name, self.floating_panes.visible_panes_count()) + let Some((position, layout_candidate)) = + self.swap_layouts.floating_layout_candidate_by_name( + layout_name, + self.floating_panes.visible_panes_count(), + ) else { return Ok(false); }; @@ -2532,8 +2541,9 @@ impl Tab { blocking_notification: Option, ) -> Result<()> { let invoked_with = self.normalize_invoked_with_for_default_shell(invoked_with); - match new_pane_placement { - NewPanePlacement::NoPreference { borderless } => self.new_no_preference_pane( + let border_style = new_pane_placement.get_border_style(); + let result = match new_pane_placement { + NewPanePlacement::NoPreference { borderless, .. } => self.new_no_preference_pane( pid, initial_pane_title, invoked_with, @@ -2546,6 +2556,7 @@ impl Tab { NewPanePlacement::Tiled { direction: None, borderless, + .. } => self.new_tiled_pane( pid, initial_pane_title, @@ -2559,6 +2570,7 @@ impl Tab { NewPanePlacement::Tiled { direction: Some(direction), borderless, + .. } => { let is_vertical = direction == Direction::Left || direction == Direction::Right; let focused_client_id = client_id.filter(|_| should_focus_pane); @@ -2637,6 +2649,7 @@ impl Tab { pane_id_to_replace, close_replaced_pane, borderless, + .. } => self.new_in_place_pane( pid, initial_pane_title, @@ -2650,6 +2663,7 @@ impl Tab { NewPanePlacement::Stacked { pane_id_to_stack_under, borderless, + .. } => self.new_stacked_pane( pid, initial_pane_title, @@ -2661,7 +2675,11 @@ impl Tab { blocking_notification, borderless, ), + }; + if let Some(border_style) = border_style { + self.set_pane_border_style(pid, border_style); } + result } pub fn new_no_preference_pane( &mut self, @@ -7607,6 +7625,21 @@ impl Tab { pane.update_rounded_corners(rounded_corners); } } + pub fn update_border_styles( + &mut self, + border_style: BorderStyle, + floating_border_style: BorderStyle, + ) { + self.style.border_style = border_style; + self.style.floating_border_style = floating_border_style; + self.floating_panes + .update_border_styles(border_style, floating_border_style); + self.tiled_panes + .update_border_styles(border_style, floating_border_style); + for (_, pane) in self.suppressed_panes.values_mut() { + pane.invalidate_frame_cache(); + } + } pub fn update_arrow_fonts(&mut self, should_support_arrow_fonts: bool) { self.arrow_fonts = should_support_arrow_fonts; self.floating_panes @@ -7882,6 +7915,27 @@ impl Tab { log::error!("Pane with id {:?} not found", pane_id); Ok(()) } + pub fn set_pane_border_style( + &mut self, + pane_id: PaneId, + border_style: BorderStyleOverride, + ) -> bool { + if let Some(pane) = self.floating_panes.get_pane_mut(pane_id) { + pane.set_border_style_override(border_style); + self.set_force_render(); + return true; + } + if let Some(pane) = self.tiled_panes.get_pane_mut(pane_id) { + pane.set_border_style_override(border_style); + self.set_force_render(); + return true; + } + if let Some(pane) = self.suppressed_panes.get_mut(&pane_id) { + pane.1.set_border_style_override(border_style); + return true; + } + false + } pub fn get_viewport(&self) -> Viewport { self.viewport.borrow().clone() } diff --git a/zellij-server/src/tab/unit/tab_integration_tests.rs b/zellij-server/src/tab/unit/tab_integration_tests.rs index b10034f48..192e11dc7 100644 --- a/zellij-server/src/tab/unit/tab_integration_tests.rs +++ b/zellij-server/src/tab/unit/tab_integration_tests.rs @@ -14,10 +14,10 @@ use std::path::PathBuf; use std::sync::Mutex; use zellij_utils::channels::Receiver; -use zellij_utils::data::Direction; use zellij_utils::data::Resize; use zellij_utils::data::ResizeStrategy; use zellij_utils::data::WebSharing; +use zellij_utils::data::{BorderStyle, BorderStyleOverride, Direction, LineStyle}; use zellij_utils::envs::set_session_name; use zellij_utils::errors::{prelude::*, ErrorContext}; use zellij_utils::input::layout::{ @@ -1558,6 +1558,7 @@ fn new_stacked_pane() { NewPanePlacement::Stacked { pane_id_to_stack_under: None, borderless: None, + border_style: None, }, Some(client_id), None, @@ -1597,6 +1598,7 @@ fn kitty_visible_panes_exclude_collapsed_stack_members() { NewPanePlacement::Stacked { pane_id_to_stack_under: None, borderless: None, + border_style: None, }, Some(client_id), None, @@ -10843,6 +10845,7 @@ fn borderless_floating_pane() { height: Some(PercentOrFixed::Fixed(10)), pinned: None, borderless: Some(true), + border_style: None, }; tab.new_floating_pane( @@ -10890,6 +10893,7 @@ fn borderless_pane_content_fills_edges() { height: Some(PercentOrFixed::Fixed(5)), pinned: None, borderless: Some(true), + border_style: None, }; tab.new_floating_pane( @@ -10938,6 +10942,7 @@ fn borderless_pinned_floating_pane() { height: Some(PercentOrFixed::Fixed(8)), pinned: Some(true), borderless: Some(true), + border_style: None, }; tab.new_floating_pane( @@ -10990,6 +10995,7 @@ fn cursor_hidden_when_floating_pane_is_under_pinned_pane() { height: Some(PercentOrFixed::Fixed(10)), pinned: Some(false), borderless: Some(false), + border_style: None, }; tab.new_floating_pane( @@ -11011,6 +11017,7 @@ fn cursor_hidden_when_floating_pane_is_under_pinned_pane() { height: Some(PercentOrFixed::Fixed(8)), pinned: Some(true), borderless: Some(false), + border_style: None, }; tab.new_floating_pane( @@ -11070,6 +11077,7 @@ fn cursor_visible_when_pinned_pane_is_focused() { height: Some(PercentOrFixed::Fixed(10)), pinned: Some(false), borderless: Some(false), + border_style: None, }; tab.new_floating_pane( @@ -11091,6 +11099,7 @@ fn cursor_visible_when_pinned_pane_is_focused() { height: Some(PercentOrFixed::Fixed(8)), pinned: Some(true), borderless: Some(false), + border_style: None, }; tab.new_floating_pane( @@ -11532,6 +11541,7 @@ fn test_ctrl_drag_resizes_floating_pane_from_edge() { height: Some(PercentOrFixed::Fixed(10)), pinned: None, borderless: Some(false), + border_style: None, }; tab.toggle_floating_panes(Some(client_id), None, None) @@ -11613,6 +11623,7 @@ fn test_ctrl_drag_resizes_floating_pane_from_corner() { height: Some(PercentOrFixed::Fixed(10)), pinned: None, borderless: Some(false), + border_style: None, }; tab.toggle_floating_panes(Some(client_id), None, None) @@ -11694,6 +11705,7 @@ fn test_ctrl_drag_resizes_pinned_floating_pane_when_floating_panes_not_shown() { height: Some(PercentOrFixed::Fixed(10)), pinned: Some(true), borderless: Some(false), + border_style: None, }; tab.new_pane( @@ -11957,6 +11969,7 @@ fn test_left_click_on_floating_pane_changes_focus() { height: Some(PercentOrFixed::Fixed(10)), pinned: None, borderless: Some(false), + border_style: None, }; let coordinates_2 = FloatingPaneCoordinates { @@ -11966,6 +11979,7 @@ fn test_left_click_on_floating_pane_changes_focus() { height: Some(PercentOrFixed::Fixed(10)), pinned: None, borderless: Some(false), + border_style: None, }; tab.toggle_floating_panes(Some(client_id), None, None) @@ -12028,6 +12042,7 @@ fn test_left_click_on_pinned_floating_pane() { height: Some(PercentOrFixed::Fixed(10)), pinned: None, borderless: Some(false), + border_style: None, }; let coordinates_2 = FloatingPaneCoordinates { @@ -12037,6 +12052,7 @@ fn test_left_click_on_pinned_floating_pane() { height: Some(PercentOrFixed::Fixed(10)), pinned: None, borderless: Some(false), + border_style: None, }; tab.toggle_floating_panes(Some(client_id), None, None) @@ -13366,6 +13382,7 @@ fn test_ctrl_scroll_up_increases_pinned_floating_pane_size_when_floating_panes_h height: Some(PercentOrFixed::Fixed(10)), pinned: Some(true), borderless: Some(false), + border_style: None, }; tab.new_pane( @@ -13434,6 +13451,7 @@ fn test_ctrl_scroll_down_decreases_pinned_floating_pane_size_when_floating_panes height: Some(PercentOrFixed::Fixed(10)), pinned: Some(true), borderless: Some(false), + border_style: None, }; tab.new_pane( @@ -13706,6 +13724,7 @@ fn adding_stacked_pane_in_stack_list_mode_moves_existing_pane_to_suppressed_imme NewPanePlacement::Stacked { pane_id_to_stack_under: None, borderless: None, + border_style: None, }, Some(client_id), None, @@ -13933,6 +13952,7 @@ fn in_place_pane_with_close_replaced_pane_false_restores_original() { NewPanePlacement::Tiled { direction: None, borderless: None, + border_style: None, }, Some(client_id), None, @@ -13996,6 +14016,7 @@ fn in_place_pane_with_close_replaced_pane_true_closes_original() { NewPanePlacement::Tiled { direction: None, borderless: None, + border_style: None, }, Some(client_id), None, @@ -15060,6 +15081,7 @@ fn focus_follows_mouse_focuses_floating_pane_on_hover() { height: Some(PercentOrFixed::Fixed(10)), pinned: None, borderless: Some(false), + border_style: None, }; let coordinates_2 = FloatingPaneCoordinates { @@ -15069,6 +15091,7 @@ fn focus_follows_mouse_focuses_floating_pane_on_hover() { height: Some(PercentOrFixed::Fixed(10)), pinned: None, borderless: Some(false), + border_style: None, }; tab.toggle_floating_panes(Some(client_id), None, None) @@ -15284,6 +15307,7 @@ fn focus_follows_mouse_ignores_tiled_pane_when_floating_visible() { height: Some(PercentOrFixed::Fixed(10)), pinned: None, borderless: Some(false), + border_style: None, })), Some(client_id), None, @@ -15334,6 +15358,7 @@ fn focus_follows_mouse_focuses_floating_pane_when_floating_visible() { height: Some(PercentOrFixed::Fixed(10)), pinned: None, borderless: Some(false), + border_style: None, })), Some(client_id), None, @@ -15352,6 +15377,7 @@ fn focus_follows_mouse_focuses_floating_pane_when_floating_visible() { height: Some(PercentOrFixed::Fixed(10)), pinned: None, borderless: Some(false), + border_style: None, })), Some(client_id), None, @@ -16545,3 +16571,202 @@ fn host_focus_events_only_reach_panes_that_asked_for_them() { .map(|bytes| String::from_utf8_lossy(bytes).to_string()); assert_eq!(written, Some("\u{1b}[O\u{1b}[I".to_owned())); } + +fn border_style_override(all: LineStyle) -> BorderStyleOverride { + BorderStyleOverride { + all: Some(all), + ..Default::default() + } +} + +fn uniform_border_style(line_style: LineStyle) -> BorderStyle { + BorderStyle { + top: line_style, + right: line_style, + bottom: line_style, + left: line_style, + rounded_corners: false, + } +} + +fn render_tab(tab: &mut Tab, size: Size, client_id: ClientId) -> String { + let mut output = Output::default(); + tab.render(&mut output, None).unwrap(); + take_snapshot( + output.serialize().unwrap().get(&client_id).unwrap(), + size.rows, + size.cols, + Palette::default(), + ) +} + +#[test] +fn a_per_pane_border_style_is_drawn_on_the_pane_frame() { + let size = Size { cols: 40, rows: 10 }; + let client_id = 1; + let mut tab = create_new_tab(size, ModeInfo::default()); + tab.set_pane_border_style( + PaneId::Terminal(1), + border_style_override(LineStyle::Double), + ); + let snapshot = render_tab(&mut tab, size, client_id); + assert!(snapshot.contains('╔'), "{}", snapshot); + assert!(snapshot.contains('║'), "{}", snapshot); + assert!(snapshot.contains('╝'), "{}", snapshot); +} + +#[test] +fn a_per_pane_border_style_reaches_a_floating_pane() { + let size = Size { cols: 60, rows: 20 }; + let client_id = 1; + let mut tab = create_new_tab(size, ModeInfo::default()); + tab.toggle_floating_panes(Some(client_id), None, None) + .unwrap(); + tab.new_pane( + PaneId::Terminal(2), + None, + None, + false, + true, + NewPanePlacement::default(), + Some(client_id), + None, + ) + .unwrap(); + tab.set_pane_border_style(PaneId::Terminal(2), border_style_override(LineStyle::Heavy)); + let snapshot = render_tab(&mut tab, size, client_id); + assert!(snapshot.contains('┏'), "{}", snapshot); + assert!(snapshot.contains('┃'), "{}", snapshot); +} + +#[test] +fn setting_a_border_style_on_an_unknown_pane_reports_failure() { + let size = Size { cols: 40, rows: 10 }; + let mut tab = create_new_tab(size, ModeInfo::default()); + assert!(tab.set_pane_border_style( + PaneId::Terminal(1), + border_style_override(LineStyle::Double) + )); + assert!(!tab.set_pane_border_style( + PaneId::Terminal(99), + border_style_override(LineStyle::Double) + )); +} + +#[test] +fn an_empty_border_style_override_restores_the_configured_default() { + let size = Size { cols: 40, rows: 10 }; + let client_id = 1; + let mut tab = create_new_tab(size, ModeInfo::default()); + tab.set_pane_border_style( + PaneId::Terminal(1), + border_style_override(LineStyle::Double), + ); + assert!(render_tab(&mut tab, size, client_id).contains('╔')); + tab.set_pane_border_style(PaneId::Terminal(1), BorderStyleOverride::default()); + let snapshot = render_tab(&mut tab, size, client_id); + assert!(!snapshot.contains('╔'), "{}", snapshot); + assert!(snapshot.contains('┌'), "{}", snapshot); +} + +#[test] +fn reconfiguring_the_global_border_style_repaints_open_panes() { + let size = Size { cols: 40, rows: 10 }; + let client_id = 1; + let mut tab = create_new_tab(size, ModeInfo::default()); + assert!(render_tab(&mut tab, size, client_id).contains('┌')); + let heavy = uniform_border_style(LineStyle::Heavy); + tab.update_border_styles(heavy, heavy); + let snapshot = render_tab(&mut tab, size, client_id); + assert!(snapshot.contains('┏'), "{}", snapshot); + assert!(snapshot.contains('┛'), "{}", snapshot); +} + +#[test] +fn a_per_pane_border_style_wins_over_the_global_one() { + let size = Size { cols: 40, rows: 10 }; + let client_id = 1; + let mut tab = create_new_tab(size, ModeInfo::default()); + let heavy = uniform_border_style(LineStyle::Heavy); + tab.update_border_styles(heavy, heavy); + tab.set_pane_border_style( + PaneId::Terminal(1), + border_style_override(LineStyle::Double), + ); + let snapshot = render_tab(&mut tab, size, client_id); + assert!(snapshot.contains('╔'), "{}", snapshot); + assert!(!snapshot.contains('┏'), "{}", snapshot); +} + +#[test] +fn border_styles_from_a_layout_reach_the_panes() { + let size = Size { cols: 60, rows: 20 }; + let client_id = 1; + let layout = r#" + layout { + pane border_style="double" + floating_panes { + pane x=2 y=2 width=20 height=6 border_style="heavy" + } + } + "#; + let mut tab = create_new_tab_with_layout(size, ModeInfo::default(), layout); + let snapshot = render_tab(&mut tab, size, client_id); + assert!(snapshot.contains('╔'), "{}", snapshot); + assert!(snapshot.contains('┏'), "{}", snapshot); +} + +#[test] +fn matching_neighbours_share_a_styled_grid_line() { + let size = Size { cols: 60, rows: 10 }; + let client_id = 1; + let mut tab = create_new_tab(size, ModeInfo::default()); + tab.set_pane_frames(PaneFrameStyle::None); + tab.vertical_split(PaneId::Terminal(2), None, client_id, None, None) + .unwrap(); + tab.set_pane_border_style( + PaneId::Terminal(1), + border_style_override(LineStyle::Double), + ); + tab.set_pane_border_style( + PaneId::Terminal(2), + border_style_override(LineStyle::Double), + ); + let snapshot = render_tab(&mut tab, size, client_id); + assert!(snapshot.contains('║'), "{}", snapshot); +} + +#[test] +fn mismatched_neighbours_fall_back_to_the_ambient_grid_line() { + let size = Size { cols: 60, rows: 10 }; + let client_id = 1; + let mut tab = create_new_tab(size, ModeInfo::default()); + tab.set_pane_frames(PaneFrameStyle::None); + tab.vertical_split(PaneId::Terminal(2), None, client_id, None, None) + .unwrap(); + let double = uniform_border_style(LineStyle::Double); + tab.update_border_styles(double, double); + tab.set_pane_border_style(PaneId::Terminal(2), border_style_override(LineStyle::Heavy)); + let snapshot = render_tab(&mut tab, size, client_id); + assert!(snapshot.contains('║'), "{}", snapshot); + assert!(!snapshot.contains('┃'), "{}", snapshot); + assert!(!snapshot.contains('│'), "{}", snapshot); +} + +#[test] +fn mismatched_neighbours_fall_back_to_single_when_the_ambient_style_is_mixed() { + let size = Size { cols: 60, rows: 10 }; + let client_id = 1; + let mut tab = create_new_tab(size, ModeInfo::default()); + tab.set_pane_frames(PaneFrameStyle::None); + tab.vertical_split(PaneId::Terminal(2), None, client_id, None, None) + .unwrap(); + let mixed = BorderStyle { + top: LineStyle::Double, + ..Default::default() + }; + tab.update_border_styles(mixed, mixed); + tab.set_pane_border_style(PaneId::Terminal(2), border_style_override(LineStyle::Heavy)); + let snapshot = render_tab(&mut tab, size, client_id); + assert!(snapshot.contains('│'), "{}", snapshot); +} diff --git a/zellij-server/src/tab/unit/tab_tests.rs b/zellij-server/src/tab/unit/tab_tests.rs index 86c5d6b9f..34dce8c4f 100644 --- a/zellij-server/src/tab/unit/tab_tests.rs +++ b/zellij-server/src/tab/unit/tab_tests.rs @@ -910,6 +910,7 @@ pub fn split_in_direction_without_a_connected_client_still_creates_the_pane() { NewPanePlacement::Tiled { direction: Some(Direction::Down), borderless: None, + border_style: None, }, None, None, @@ -940,6 +941,7 @@ pub fn split_in_direction_without_a_connected_client_splits_the_existing_pane() NewPanePlacement::Tiled { direction: Some(Direction::Right), borderless: None, + border_style: None, }, None, None, diff --git a/zellij-server/src/ui/border_glyphs.rs b/zellij-server/src/ui/border_glyphs.rs new file mode 100644 index 000000000..5c4baca2f --- /dev/null +++ b/zellij-server/src/ui/border_glyphs.rs @@ -0,0 +1,379 @@ +use crate::ui::boundaries::boundary_type; +use zellij_utils::data::LineStyle; + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum Corner { + TopLeft, + TopRight, + BottomLeft, + BottomRight, +} + +pub fn horizontal(line_style: LineStyle) -> &'static str { + match line_style { + LineStyle::Single => "─", + LineStyle::Double => "═", + LineStyle::Heavy => "━", + LineStyle::Dashed => "┄", + LineStyle::HeavyDashed => "┅", + } +} + +pub fn vertical(line_style: LineStyle) -> &'static str { + match line_style { + LineStyle::Single => "│", + LineStyle::Double => "║", + LineStyle::Heavy => "┃", + LineStyle::Dashed => "┆", + LineStyle::HeavyDashed => "┇", + } +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +enum Weight { + Light, + Double, + Heavy, +} + +fn weight(line_style: LineStyle) -> Weight { + match line_style { + LineStyle::Single | LineStyle::Dashed => Weight::Light, + LineStyle::Double => Weight::Double, + LineStyle::Heavy | LineStyle::HeavyDashed => Weight::Heavy, + } +} + +pub fn corner( + corner: Corner, + horizontal_style: LineStyle, + vertical_style: LineStyle, + rounded: bool, +) -> &'static str { + let horizontal_weight = weight(horizontal_style); + let mut vertical_weight = weight(vertical_style); + // there are no glyphs mixing a double arm with a heavy arm, in this case we render both arms + // in the weight of the horizontal arm + if (horizontal_weight == Weight::Double && vertical_weight == Weight::Heavy) + || (horizontal_weight == Weight::Heavy && vertical_weight == Weight::Double) + { + vertical_weight = horizontal_weight; + } + if rounded && horizontal_weight == Weight::Light && vertical_weight == Weight::Light { + return match corner { + Corner::TopLeft => boundary_type::TOP_LEFT_ROUND, + Corner::TopRight => boundary_type::TOP_RIGHT_ROUND, + Corner::BottomLeft => boundary_type::BOTTOM_LEFT_ROUND, + Corner::BottomRight => boundary_type::BOTTOM_RIGHT_ROUND, + }; + } + match (corner, horizontal_weight, vertical_weight) { + (Corner::TopLeft, Weight::Light, Weight::Light) => "┌", + (Corner::TopLeft, Weight::Double, Weight::Light) => "╒", + (Corner::TopLeft, Weight::Light, Weight::Double) => "╓", + (Corner::TopLeft, Weight::Double, Weight::Double) => "╔", + (Corner::TopLeft, Weight::Heavy, Weight::Light) => "┍", + (Corner::TopLeft, Weight::Light, Weight::Heavy) => "┎", + (Corner::TopLeft, Weight::Heavy, Weight::Heavy) => "┏", + + (Corner::TopRight, Weight::Light, Weight::Light) => "┐", + (Corner::TopRight, Weight::Double, Weight::Light) => "╕", + (Corner::TopRight, Weight::Light, Weight::Double) => "╖", + (Corner::TopRight, Weight::Double, Weight::Double) => "╗", + (Corner::TopRight, Weight::Heavy, Weight::Light) => "┑", + (Corner::TopRight, Weight::Light, Weight::Heavy) => "┒", + (Corner::TopRight, Weight::Heavy, Weight::Heavy) => "┓", + + (Corner::BottomLeft, Weight::Light, Weight::Light) => "└", + (Corner::BottomLeft, Weight::Double, Weight::Light) => "╘", + (Corner::BottomLeft, Weight::Light, Weight::Double) => "╙", + (Corner::BottomLeft, Weight::Double, Weight::Double) => "╚", + (Corner::BottomLeft, Weight::Heavy, Weight::Light) => "┕", + (Corner::BottomLeft, Weight::Light, Weight::Heavy) => "┖", + (Corner::BottomLeft, Weight::Heavy, Weight::Heavy) => "┗", + + (Corner::BottomRight, Weight::Light, Weight::Light) => "┘", + (Corner::BottomRight, Weight::Double, Weight::Light) => "╛", + (Corner::BottomRight, Weight::Light, Weight::Double) => "╜", + (Corner::BottomRight, Weight::Double, Weight::Double) => "╝", + (Corner::BottomRight, Weight::Heavy, Weight::Light) => "┙", + (Corner::BottomRight, Weight::Light, Weight::Heavy) => "┚", + (Corner::BottomRight, Weight::Heavy, Weight::Heavy) => "┛", + + (_, Weight::Double, Weight::Heavy) | (_, Weight::Heavy, Weight::Double) => { + unreachable!("double/heavy mixes are normalized above") + }, + } +} + +pub fn title_separator_left(horizontal_style: LineStyle) -> &'static str { + match weight(horizontal_style) { + Weight::Light => boundary_type::VERTICAL_LEFT, + Weight::Double => "╡", + Weight::Heavy => "┥", + } +} + +pub fn title_separator_right(horizontal_style: LineStyle) -> &'static str { + match weight(horizontal_style) { + Weight::Light => boundary_type::VERTICAL_RIGHT, + Weight::Double => "╞", + Weight::Heavy => "┝", + } +} + +pub fn remap_light_glyph(glyph: &'static str, line_style: LineStyle) -> &'static str { + match line_style { + LineStyle::Single => glyph, + LineStyle::Dashed => match glyph { + boundary_type::HORIZONTAL => "┄", + boundary_type::VERTICAL => "┆", + _ => glyph, + }, + LineStyle::Double => match glyph { + boundary_type::HORIZONTAL => "═", + boundary_type::VERTICAL => "║", + boundary_type::TOP_LEFT => "╔", + boundary_type::TOP_LEFT_ROUND => "╔", + boundary_type::TOP_RIGHT => "╗", + boundary_type::TOP_RIGHT_ROUND => "╗", + boundary_type::BOTTOM_LEFT => "╚", + boundary_type::BOTTOM_LEFT_ROUND => "╚", + boundary_type::BOTTOM_RIGHT => "╝", + boundary_type::BOTTOM_RIGHT_ROUND => "╝", + boundary_type::VERTICAL_LEFT => "╣", + boundary_type::VERTICAL_RIGHT => "╠", + boundary_type::HORIZONTAL_DOWN => "╦", + boundary_type::HORIZONTAL_UP => "╩", + boundary_type::CROSS => "╬", + _ => glyph, + }, + LineStyle::Heavy | LineStyle::HeavyDashed => { + let heavy_dashed = line_style == LineStyle::HeavyDashed; + match glyph { + boundary_type::HORIZONTAL => { + if heavy_dashed { + "┅" + } else { + "━" + } + }, + boundary_type::VERTICAL => { + if heavy_dashed { + "┇" + } else { + "┃" + } + }, + boundary_type::TOP_LEFT => "┏", + boundary_type::TOP_LEFT_ROUND => "┏", + boundary_type::TOP_RIGHT => "┓", + boundary_type::TOP_RIGHT_ROUND => "┓", + boundary_type::BOTTOM_LEFT => "┗", + boundary_type::BOTTOM_LEFT_ROUND => "┗", + boundary_type::BOTTOM_RIGHT => "┛", + boundary_type::BOTTOM_RIGHT_ROUND => "┛", + boundary_type::VERTICAL_LEFT => "┫", + boundary_type::VERTICAL_RIGHT => "┣", + boundary_type::HORIZONTAL_DOWN => "┳", + boundary_type::HORIZONTAL_UP => "┻", + boundary_type::CROSS => "╋", + _ => glyph, + } + }, + } +} + +#[cfg(test)] +mod tests { + use super::*; + + const ALL: [LineStyle; 5] = [ + LineStyle::Single, + LineStyle::Double, + LineStyle::Heavy, + LineStyle::Dashed, + LineStyle::HeavyDashed, + ]; + + const CORNERS: [Corner; 4] = [ + Corner::TopLeft, + Corner::TopRight, + Corner::BottomLeft, + Corner::BottomRight, + ]; + + #[test] + fn every_edge_style_has_its_own_glyph() { + let horizontals: Vec<&str> = ALL.iter().map(|s| horizontal(*s)).collect(); + let verticals: Vec<&str> = ALL.iter().map(|s| vertical(*s)).collect(); + assert_eq!(horizontals, vec!["─", "═", "━", "┄", "┅"]); + assert_eq!(verticals, vec!["│", "║", "┃", "┆", "┇"]); + } + + #[test] + fn every_corner_combination_resolves_to_a_single_width_glyph() { + for corner in CORNERS { + for horizontal_style in ALL { + for vertical_style in ALL { + for rounded in [false, true] { + let glyph = + super::corner(corner, horizontal_style, vertical_style, rounded); + assert_eq!( + glyph.chars().count(), + 1, + "{:?} {:?}/{:?} rounded={} produced {:?}", + corner, + horizontal_style, + vertical_style, + rounded, + glyph + ); + } + } + } + } + } + + #[test] + fn corners_only_round_when_both_arms_are_single_width() { + assert_eq!( + corner(Corner::TopLeft, LineStyle::Single, LineStyle::Single, true), + "╭" + ); + assert_eq!( + corner(Corner::TopLeft, LineStyle::Dashed, LineStyle::Dashed, true), + "╭" + ); + assert_eq!( + corner(Corner::TopLeft, LineStyle::Double, LineStyle::Single, true), + "╒" + ); + assert_eq!( + corner(Corner::TopLeft, LineStyle::Single, LineStyle::Heavy, true), + "┎" + ); + } + + #[test] + fn mixed_weight_corners_use_the_dedicated_glyphs() { + assert_eq!( + corner( + Corner::BottomRight, + LineStyle::Double, + LineStyle::Single, + false + ), + "╛" + ); + assert_eq!( + corner( + Corner::BottomRight, + LineStyle::Single, + LineStyle::Double, + false + ), + "╜" + ); + assert_eq!( + corner( + Corner::BottomRight, + LineStyle::Heavy, + LineStyle::Single, + false + ), + "┙" + ); + assert_eq!( + corner( + Corner::BottomRight, + LineStyle::Single, + LineStyle::Heavy, + false + ), + "┚" + ); + } + + #[test] + fn a_double_and_heavy_corner_uses_the_horizontal_arm_for_both() { + assert_eq!( + corner(Corner::TopLeft, LineStyle::Double, LineStyle::Heavy, false), + "╔" + ); + assert_eq!( + corner(Corner::TopLeft, LineStyle::Heavy, LineStyle::Double, false), + "┏" + ); + } + + #[test] + fn title_separators_follow_the_horizontal_weight() { + assert_eq!(title_separator_left(LineStyle::Single), "┤"); + assert_eq!(title_separator_right(LineStyle::Single), "├"); + assert_eq!(title_separator_left(LineStyle::Double), "╡"); + assert_eq!(title_separator_right(LineStyle::Double), "╞"); + assert_eq!(title_separator_left(LineStyle::HeavyDashed), "┥"); + assert_eq!(title_separator_right(LineStyle::HeavyDashed), "┝"); + } + + #[test] + fn remapping_covers_every_light_glyph_for_double_and_heavy() { + let light = [ + boundary_type::HORIZONTAL, + boundary_type::VERTICAL, + boundary_type::TOP_LEFT, + boundary_type::TOP_RIGHT, + boundary_type::BOTTOM_LEFT, + boundary_type::BOTTOM_RIGHT, + boundary_type::VERTICAL_LEFT, + boundary_type::VERTICAL_RIGHT, + boundary_type::HORIZONTAL_DOWN, + boundary_type::HORIZONTAL_UP, + boundary_type::CROSS, + ]; + for glyph in light { + for line_style in [LineStyle::Double, LineStyle::Heavy] { + assert_ne!( + remap_light_glyph(glyph, line_style), + glyph, + "{:?} left {} unmapped", + line_style, + glyph + ); + } + } + } + + #[test] + fn dashed_remapping_only_touches_the_straight_runs() { + assert_eq!( + remap_light_glyph(boundary_type::HORIZONTAL, LineStyle::Dashed), + "┄" + ); + assert_eq!( + remap_light_glyph(boundary_type::VERTICAL, LineStyle::Dashed), + "┆" + ); + assert_eq!( + remap_light_glyph(boundary_type::CROSS, LineStyle::Dashed), + boundary_type::CROSS + ); + assert_eq!( + remap_light_glyph(boundary_type::HORIZONTAL, LineStyle::HeavyDashed), + "┅" + ); + assert_eq!( + remap_light_glyph(boundary_type::CROSS, LineStyle::HeavyDashed), + "╋" + ); + } + + #[test] + fn a_single_style_remap_is_the_identity() { + assert_eq!( + remap_light_glyph(boundary_type::CROSS, LineStyle::Single), + boundary_type::CROSS + ); + } +} diff --git a/zellij-server/src/ui/boundaries.rs b/zellij-server/src/ui/boundaries.rs index 91dc76d4c..888bd5a17 100644 --- a/zellij-server/src/ui/boundaries.rs +++ b/zellij-server/src/ui/boundaries.rs @@ -3,10 +3,14 @@ use zellij_utils::pane_size::{Offset, Viewport}; use crate::output::CharacterChunk; use crate::panes::terminal_character::{TerminalCharacter, EMPTY_TERMINAL_CHARACTER, RESET_STYLES}; use crate::tab::Pane; +use crate::ui::border_glyphs::remap_light_glyph; use ansi_term::Colour::{Fixed, RGB}; use std::collections::HashMap; use zellij_utils::errors::prelude::*; -use zellij_utils::{data::PaletteColor, shared::colors}; +use zellij_utils::{ + data::{BorderStyle, LineStyle, PaletteColor}, + shared::colors, +}; use std::fmt::{Display, Error, Formatter}; pub mod boundary_type { @@ -34,6 +38,7 @@ pub struct BoundarySymbol { boundary_type: BoundaryType, invisible: bool, color: Option<(PaletteColor, usize)>, // (color, color_precedence) + line_style: Option, } impl BoundarySymbol { @@ -42,25 +47,39 @@ impl BoundarySymbol { boundary_type, invisible: false, color: Some((PaletteColor::EightBit(colors::GRAY), 0)), + line_style: Some(LineStyle::Single), } } pub fn color(&mut self, color: Option<(PaletteColor, usize)>) -> Self { self.color = color; *self } - pub fn as_terminal_character(&self) -> Result { + pub fn line_style(&mut self, line_style: LineStyle) -> Self { + self.line_style = Some(line_style); + *self + } + pub fn glyph(&self, fallback_line_style: LineStyle) -> BoundaryType { + remap_light_glyph( + self.boundary_type, + self.line_style.unwrap_or(fallback_line_style), + ) + } + pub fn as_terminal_character( + &self, + fallback_line_style: LineStyle, + ) -> Result { let tc = if self.invisible { EMPTY_TERMINAL_CHARACTER } else { - let character = self - .boundary_type + let glyph = self.glyph(fallback_line_style); + let character = glyph .chars() .next() .context("no boundary symbols defined") .with_context(|| { format!( "failed to convert boundary symbol {} into terminal character", - self.boundary_type + glyph ) })?; TerminalCharacter::new_singlewidth_styled( @@ -76,18 +95,15 @@ impl BoundarySymbol { impl Display for BoundarySymbol { fn fmt(&self, f: &mut Formatter) -> Result<(), Error> { + let glyph = self.glyph(LineStyle::Single); match self.invisible { true => write!(f, " "), false => match self.color { Some(color) => match color.0 { - PaletteColor::Rgb((r, g, b)) => { - write!(f, "{}", RGB(r, g, b).paint(self.boundary_type)) - }, - PaletteColor::EightBit(color) => { - write!(f, "{}", Fixed(color).paint(self.boundary_type)) - }, + PaletteColor::Rgb((r, g, b)) => write!(f, "{}", RGB(r, g, b).paint(glyph)), + PaletteColor::EightBit(color) => write!(f, "{}", Fixed(color).paint(glyph)), }, - None => write!(f, "{}", self.boundary_type), + None => write!(f, "{}", glyph), }, } } @@ -110,6 +126,16 @@ fn combine_symbols( }, _ => current_symbol.color.or(next_symbol.color), }; + let line_style = match (current_symbol.line_style, next_symbol.line_style) { + (Some(current_line_style), Some(next_line_style)) => { + if current_line_style == next_line_style { + Some(current_line_style) + } else { + None + } + }, + _ => None, + }; match (current_symbol.boundary_type, next_symbol.boundary_type) { (CROSS, _) | (_, CROSS) => { // (┼, *) or (*, ┼) => Some(┼) @@ -118,6 +144,7 @@ fn combine_symbols( boundary_type, invisible, color, + line_style, }) }, (TOP_RIGHT, TOP_RIGHT) => { @@ -127,6 +154,7 @@ fn combine_symbols( boundary_type, invisible, color, + line_style, }) }, (TOP_RIGHT, VERTICAL) | (TOP_RIGHT, BOTTOM_RIGHT) | (TOP_RIGHT, VERTICAL_LEFT) => { @@ -138,6 +166,7 @@ fn combine_symbols( boundary_type, invisible, color, + line_style, }) }, (TOP_RIGHT, HORIZONTAL) | (TOP_RIGHT, TOP_LEFT) | (TOP_RIGHT, HORIZONTAL_DOWN) => { @@ -149,6 +178,7 @@ fn combine_symbols( boundary_type, invisible, color, + line_style, }) }, (TOP_RIGHT, BOTTOM_LEFT) | (TOP_RIGHT, VERTICAL_RIGHT) | (TOP_RIGHT, HORIZONTAL_UP) => { @@ -160,6 +190,7 @@ fn combine_symbols( boundary_type, invisible, color, + line_style, }) }, (HORIZONTAL, HORIZONTAL) => { @@ -169,6 +200,7 @@ fn combine_symbols( boundary_type, invisible, color, + line_style, }) }, (HORIZONTAL, VERTICAL) | (HORIZONTAL, VERTICAL_LEFT) | (HORIZONTAL, VERTICAL_RIGHT) => { @@ -180,6 +212,7 @@ fn combine_symbols( boundary_type, invisible, color, + line_style, }) }, (HORIZONTAL, TOP_LEFT) | (HORIZONTAL, HORIZONTAL_DOWN) => { @@ -190,6 +223,7 @@ fn combine_symbols( boundary_type, invisible, color, + line_style, }) }, (HORIZONTAL, BOTTOM_RIGHT) | (HORIZONTAL, BOTTOM_LEFT) | (HORIZONTAL, HORIZONTAL_UP) => { @@ -201,6 +235,7 @@ fn combine_symbols( boundary_type, invisible, color, + line_style, }) }, (VERTICAL, VERTICAL) => { @@ -210,6 +245,7 @@ fn combine_symbols( boundary_type, invisible, color, + line_style, }) }, (VERTICAL, TOP_LEFT) | (VERTICAL, BOTTOM_LEFT) | (VERTICAL, VERTICAL_RIGHT) => { @@ -221,6 +257,7 @@ fn combine_symbols( boundary_type, invisible, color, + line_style, }) }, (VERTICAL, BOTTOM_RIGHT) | (VERTICAL, VERTICAL_LEFT) => { @@ -231,6 +268,7 @@ fn combine_symbols( boundary_type, invisible, color, + line_style, }) }, (VERTICAL, HORIZONTAL_DOWN) | (VERTICAL, HORIZONTAL_UP) => { @@ -241,6 +279,7 @@ fn combine_symbols( boundary_type, invisible, color, + line_style, }) }, (TOP_LEFT, TOP_LEFT) => { @@ -250,6 +289,7 @@ fn combine_symbols( boundary_type, invisible, color, + line_style, }) }, (TOP_LEFT, BOTTOM_RIGHT) | (TOP_LEFT, VERTICAL_LEFT) | (TOP_LEFT, HORIZONTAL_UP) => { @@ -261,6 +301,7 @@ fn combine_symbols( boundary_type, invisible, color, + line_style, }) }, (TOP_LEFT, BOTTOM_LEFT) | (TOP_LEFT, VERTICAL_RIGHT) => { @@ -271,6 +312,7 @@ fn combine_symbols( boundary_type, invisible, color, + line_style, }) }, (TOP_LEFT, HORIZONTAL_DOWN) => { @@ -280,6 +322,7 @@ fn combine_symbols( boundary_type, invisible, color, + line_style, }) }, (BOTTOM_RIGHT, BOTTOM_RIGHT) => { @@ -289,6 +332,7 @@ fn combine_symbols( boundary_type, invisible, color, + line_style, }) }, (BOTTOM_RIGHT, BOTTOM_LEFT) | (BOTTOM_RIGHT, HORIZONTAL_UP) => { @@ -299,6 +343,7 @@ fn combine_symbols( boundary_type, invisible, color, + line_style, }) }, (BOTTOM_RIGHT, VERTICAL_LEFT) => { @@ -308,6 +353,7 @@ fn combine_symbols( boundary_type, invisible, color, + line_style, }) }, (BOTTOM_RIGHT, VERTICAL_RIGHT) | (BOTTOM_RIGHT, HORIZONTAL_DOWN) => { @@ -318,6 +364,7 @@ fn combine_symbols( boundary_type, invisible, color, + line_style, }) }, (BOTTOM_LEFT, BOTTOM_LEFT) => { @@ -327,6 +374,7 @@ fn combine_symbols( boundary_type, invisible, color, + line_style, }) }, (BOTTOM_LEFT, VERTICAL_LEFT) | (BOTTOM_LEFT, HORIZONTAL_DOWN) => { @@ -337,6 +385,7 @@ fn combine_symbols( boundary_type, invisible, color, + line_style, }) }, (BOTTOM_LEFT, VERTICAL_RIGHT) => { @@ -346,6 +395,7 @@ fn combine_symbols( boundary_type, invisible, color, + line_style, }) }, (BOTTOM_LEFT, HORIZONTAL_UP) => { @@ -355,6 +405,7 @@ fn combine_symbols( boundary_type, invisible, color, + line_style, }) }, (VERTICAL_LEFT, VERTICAL_LEFT) => { @@ -364,6 +415,7 @@ fn combine_symbols( boundary_type, invisible, color, + line_style, }) }, (VERTICAL_LEFT, VERTICAL_RIGHT) @@ -377,6 +429,7 @@ fn combine_symbols( boundary_type, invisible, color, + line_style, }) }, (VERTICAL_RIGHT, VERTICAL_RIGHT) => { @@ -386,6 +439,7 @@ fn combine_symbols( boundary_type, invisible, color, + line_style, }) }, (VERTICAL_RIGHT, HORIZONTAL_DOWN) | (VERTICAL_RIGHT, HORIZONTAL_UP) => { @@ -396,6 +450,7 @@ fn combine_symbols( boundary_type, invisible, color, + line_style, }) }, (HORIZONTAL_DOWN, HORIZONTAL_DOWN) => { @@ -405,6 +460,7 @@ fn combine_symbols( boundary_type, invisible, color, + line_style, }) }, (HORIZONTAL_DOWN, HORIZONTAL_UP) => { @@ -414,6 +470,7 @@ fn combine_symbols( boundary_type, invisible, color, + line_style, }) }, (HORIZONTAL_UP, HORIZONTAL_UP) => { @@ -423,6 +480,7 @@ fn combine_symbols( boundary_type, invisible, color, + line_style, }) }, (_, _) => combine_symbols(next_symbol, current_symbol), @@ -443,14 +501,16 @@ impl Coordinates { pub struct Boundaries { viewport: Viewport, + fallback_line_style: LineStyle, pub boundary_characters: HashMap, } #[allow(clippy::if_same_then_else)] impl Boundaries { - pub fn new(viewport: Viewport) -> Self { + pub fn new(viewport: Viewport, fallback_line_style: LineStyle) -> Self { Boundaries { viewport, + fallback_line_style, boundary_characters: HashMap::new(), } } @@ -458,6 +518,7 @@ impl Boundaries { &mut self, rect: &dyn Pane, color: Option<(PaletteColor, usize)>, // (color, color_precedence) + border_style: BorderStyle, pane_is_on_top_of_stack: bool, pane_is_on_bottom_of_stack: bool, pane_is_stacked_under: bool, @@ -479,19 +540,29 @@ impl Boundaries { let coordinates = Coordinates::new(boundary_x_coords, row); let symbol_to_add = if row == first_row_coordinates && row != self.viewport.y { if pane_is_stacked { - BoundarySymbol::new(boundary_type::VERTICAL_RIGHT).color(color) + BoundarySymbol::new(boundary_type::VERTICAL_RIGHT) + .color(color) + .line_style(border_style.left) } else { - BoundarySymbol::new(boundary_type::TOP_LEFT).color(color) + BoundarySymbol::new(boundary_type::TOP_LEFT) + .color(color) + .line_style(border_style.left) } } else if row == first_row_coordinates && pane_is_stacked { - BoundarySymbol::new(boundary_type::TOP_LEFT).color(color) + BoundarySymbol::new(boundary_type::TOP_LEFT) + .color(color) + .line_style(border_style.left) } else if row == last_row_coordinates - 1 && row != self.viewport.y + self.viewport.rows - 1 && content_offset.bottom > 0 { - BoundarySymbol::new(boundary_type::BOTTOM_LEFT).color(color) + BoundarySymbol::new(boundary_type::BOTTOM_LEFT) + .color(color) + .line_style(border_style.left) } else { - BoundarySymbol::new(boundary_type::VERTICAL).color(color) + BoundarySymbol::new(boundary_type::VERTICAL) + .color(color) + .line_style(border_style.left) }; let next_symbol = self .boundary_characters @@ -509,11 +580,17 @@ impl Boundaries { for col in first_col_coordinates..last_col_coordinates { let coordinates = Coordinates::new(col, boundary_y_coords); let symbol_to_add = if col == first_col_coordinates && col != self.viewport.x { - BoundarySymbol::new(boundary_type::TOP_LEFT).color(color) + BoundarySymbol::new(boundary_type::TOP_LEFT) + .color(color) + .line_style(border_style.top) } else if col == last_col_coordinates - 1 && col != self.viewport.cols - 1 { - BoundarySymbol::new(boundary_type::TOP_RIGHT).color(color) + BoundarySymbol::new(boundary_type::TOP_RIGHT) + .color(color) + .line_style(border_style.top) } else { - BoundarySymbol::new(boundary_type::HORIZONTAL).color(color) + BoundarySymbol::new(boundary_type::HORIZONTAL) + .color(color) + .line_style(border_style.top) }; let next_symbol = self .boundary_characters @@ -532,20 +609,30 @@ impl Boundaries { for row in first_row_coordinates..last_row_coordinates { let coordinates = Coordinates::new(boundary_x_coords, row); let symbol_to_add = if row == first_row_coordinates && pane_is_stacked { - BoundarySymbol::new(boundary_type::VERTICAL_LEFT).color(color) + BoundarySymbol::new(boundary_type::VERTICAL_LEFT) + .color(color) + .line_style(border_style.right) } else if row == first_row_coordinates && row != self.viewport.y { if pane_is_stacked { - BoundarySymbol::new(boundary_type::VERTICAL_LEFT).color(color) + BoundarySymbol::new(boundary_type::VERTICAL_LEFT) + .color(color) + .line_style(border_style.right) } else { - BoundarySymbol::new(boundary_type::TOP_RIGHT).color(color) + BoundarySymbol::new(boundary_type::TOP_RIGHT) + .color(color) + .line_style(border_style.right) } } else if row == last_row_coordinates - 1 && row != self.viewport.y + self.viewport.rows - 1 && content_offset.bottom > 0 { - BoundarySymbol::new(boundary_type::BOTTOM_RIGHT).color(color) + BoundarySymbol::new(boundary_type::BOTTOM_RIGHT) + .color(color) + .line_style(border_style.right) } else { - BoundarySymbol::new(boundary_type::VERTICAL).color(color) + BoundarySymbol::new(boundary_type::VERTICAL) + .color(color) + .line_style(border_style.right) }; let next_symbol = self .boundary_characters @@ -563,11 +650,17 @@ impl Boundaries { for col in first_col_coordinates..last_col_coordinates { let coordinates = Coordinates::new(col, boundary_y_coords); let symbol_to_add = if col == first_col_coordinates && col != self.viewport.x { - BoundarySymbol::new(boundary_type::BOTTOM_LEFT).color(color) + BoundarySymbol::new(boundary_type::BOTTOM_LEFT) + .color(color) + .line_style(border_style.bottom) } else if col == last_col_coordinates - 1 && col != self.viewport.cols - 1 { - BoundarySymbol::new(boundary_type::BOTTOM_RIGHT).color(color) + BoundarySymbol::new(boundary_type::BOTTOM_RIGHT) + .color(color) + .line_style(border_style.bottom) } else { - BoundarySymbol::new(boundary_type::HORIZONTAL).color(color) + BoundarySymbol::new(boundary_type::HORIZONTAL) + .color(color) + .line_style(border_style.bottom) }; let next_symbol = self .boundary_characters @@ -584,16 +677,25 @@ impl Boundaries { ) -> Result> { let mut character_chunks = vec![]; for (coordinates, boundary_character) in &self.boundary_characters { + let glyph = boundary_character.glyph(self.fallback_line_style); let already_on_screen = existing_boundaries_on_screen .and_then(|e| e.boundary_characters.get(coordinates)) - .map(|e| e == boundary_character) + .map(|e| { + e.color == boundary_character.color + && e.invisible == boundary_character.invisible + && e.glyph( + existing_boundaries_on_screen + .map(|b| b.fallback_line_style) + .unwrap_or(LineStyle::Single), + ) == glyph + }) .unwrap_or(false); if already_on_screen { continue; } character_chunks.push(CharacterChunk::new( vec![boundary_character - .as_terminal_character() + .as_terminal_character(self.fallback_line_style) .context("failed to render as terminal character")?], coordinates.x, coordinates.y, @@ -653,3 +755,57 @@ impl Boundaries { && rect.y() + rect.rows() <= self.viewport.y + self.viewport.rows } } + +#[cfg(test)] +mod tests { + use super::*; + + fn symbol(boundary_type: BoundaryType, line_style: LineStyle) -> BoundarySymbol { + BoundarySymbol::new(boundary_type).line_style(line_style) + } + + #[test] + fn matching_line_styles_are_kept_through_a_junction() { + let combined = combine_symbols( + symbol(boundary_type::HORIZONTAL, LineStyle::Double), + symbol(boundary_type::VERTICAL, LineStyle::Double), + ) + .unwrap(); + assert_eq!(combined.glyph(LineStyle::Single), "╬"); + } + + #[test] + fn conflicting_line_styles_fall_back_to_the_ambient_style() { + let combined = combine_symbols( + symbol(boundary_type::HORIZONTAL, LineStyle::Double), + symbol(boundary_type::VERTICAL, LineStyle::Heavy), + ) + .unwrap(); + assert_eq!(combined.glyph(LineStyle::Single), "┼"); + assert_eq!(combined.glyph(LineStyle::Double), "╬"); + } + + #[test] + fn a_style_without_a_glyph_for_the_junction_falls_back_to_single() { + let combined = combine_symbols( + symbol(boundary_type::HORIZONTAL, LineStyle::Dashed), + symbol(boundary_type::VERTICAL, LineStyle::Dashed), + ) + .unwrap(); + assert_eq!(combined.glyph(LineStyle::Single), "┼"); + assert_eq!( + symbol(boundary_type::HORIZONTAL, LineStyle::Dashed).glyph(LineStyle::Single), + "┄" + ); + } + + #[test] + fn heavy_junctions_are_kept() { + let combined = combine_symbols( + symbol(boundary_type::HORIZONTAL, LineStyle::Heavy), + symbol(boundary_type::TOP_LEFT, LineStyle::Heavy), + ) + .unwrap(); + assert_eq!(combined.glyph(LineStyle::Single), "┳"); + } +} diff --git a/zellij-server/src/ui/mod.rs b/zellij-server/src/ui/mod.rs index 1d3ba1f61..77344ac2c 100644 --- a/zellij-server/src/ui/mod.rs +++ b/zellij-server/src/ui/mod.rs @@ -1,3 +1,4 @@ +pub mod border_glyphs; pub mod boundaries; pub mod components; pub mod hint_text; diff --git a/zellij-server/src/ui/pane_boundaries_frame.rs b/zellij-server/src/ui/pane_boundaries_frame.rs index 16ba30534..c932fca9a 100644 --- a/zellij-server/src/ui/pane_boundaries_frame.rs +++ b/zellij-server/src/ui/pane_boundaries_frame.rs @@ -3,13 +3,14 @@ use crate::panes::{ AnsiCode, CharacterStyles, RcCharacterStyles, TerminalCharacter, EMPTY_TERMINAL_CHARACTER, }; use crate::tab::GuestChoiceIndicator; +use crate::ui::border_glyphs::{self, Corner}; use crate::ui::boundaries::boundary_type; use crate::ui::hint_text::{ exit_code_segments, hover_segments, rerun_segments, resize_segments, HintExitStatus, HintLevel, HintSegment, HintTier, }; use crate::ClientId; -use zellij_utils::data::{client_id_to_colors, PaletteColor, Style}; +use zellij_utils::data::{client_id_to_colors, BorderStyle, LineStyle, PaletteColor, Style}; use zellij_utils::errors::prelude::*; use zellij_utils::pane_size::{Offset, PaneGeom, Viewport}; use zellij_utils::position::Position; @@ -80,6 +81,7 @@ pub struct FrameParams { pub is_main_client: bool, // more accurately: is_focused_for_main_client pub other_focused_clients: Vec, pub style: Style, + pub border_style: BorderStyle, pub color: Option, pub other_cursors_exist_in_session: bool, pub pane_is_stacked_under: bool, @@ -108,6 +110,7 @@ pub struct PaneFrame { pub title: String, pub scroll_position: (usize, usize), // (position, length) pub style: Style, + pub border_style: BorderStyle, pub color: Option, pub focused_client: Option, pub is_main_client: bool, @@ -147,6 +150,7 @@ impl PaneFrame { title: main_title, scroll_position, style: frame_params.style, + border_style: frame_params.border_style, color: frame_params.color, focused_client: frame_params.focused_client, is_main_client: frame_params.is_main_client, @@ -195,29 +199,63 @@ impl PaneFrame { let color = client_id_to_colors(client_id, self.style.colors.multiplayer_user_colors); background_color(" ", color.map(|c| c.0)) } + fn top_horizontal(&self) -> &'static str { + border_glyphs::horizontal(self.border_style.top) + } + fn bottom_horizontal(&self) -> &'static str { + border_glyphs::horizontal(self.border_style.bottom) + } + fn left_vertical(&self) -> &'static str { + border_glyphs::vertical(self.border_style.left) + } + fn right_vertical(&self) -> &'static str { + border_glyphs::vertical(self.border_style.right) + } + fn title_separator_left(&self) -> &'static str { + border_glyphs::title_separator_left(self.border_style.top) + } + fn title_separator_right(&self) -> &'static str { + border_glyphs::title_separator_right(self.border_style.top) + } + fn horizontal_style_of_corner(&self, corner: Corner) -> LineStyle { + match corner { + Corner::TopLeft | Corner::TopRight => self.border_style.top, + Corner::BottomLeft | Corner::BottomRight => self.border_style.bottom, + } + } + fn vertical_style_of_corner(&self, corner: Corner) -> LineStyle { + match corner { + Corner::TopLeft | Corner::BottomLeft => self.border_style.left, + Corner::TopRight | Corner::BottomRight => self.border_style.right, + } + } fn get_corner(&self, corner: &'static str) -> &'static str { - let corner = if !self.should_draw_pane_frames + if !self.should_draw_pane_frames && (corner == boundary_type::TOP_LEFT || corner == boundary_type::TOP_RIGHT) { - boundary_type::HORIZONTAL - } else if self.pane_is_stacked_under && corner == boundary_type::TOP_RIGHT { - boundary_type::BOTTOM_RIGHT - } else if self.pane_is_stacked_under && corner == boundary_type::TOP_LEFT { - boundary_type::BOTTOM_LEFT - } else { - corner + return self.top_horizontal(); + } + let style_corner = match corner { + boundary_type::TOP_LEFT => Corner::TopLeft, + boundary_type::TOP_RIGHT => Corner::TopRight, + boundary_type::BOTTOM_LEFT => Corner::BottomLeft, + _ => Corner::BottomRight, }; - if self.style.rounded_corners { - match corner { - boundary_type::TOP_RIGHT => boundary_type::TOP_RIGHT_ROUND, - boundary_type::TOP_LEFT => boundary_type::TOP_LEFT_ROUND, - boundary_type::BOTTOM_RIGHT => boundary_type::BOTTOM_RIGHT_ROUND, - boundary_type::BOTTOM_LEFT => boundary_type::BOTTOM_LEFT_ROUND, - _ => corner, + let shape_corner = if self.pane_is_stacked_under { + match style_corner { + Corner::TopRight => Corner::BottomRight, + Corner::TopLeft => Corner::BottomLeft, + other => other, } } else { - corner - } + style_corner + }; + border_glyphs::corner( + shape_corner, + self.horizontal_style_of_corner(style_corner), + self.vertical_style_of_corner(style_corner), + self.border_style.rounded_corners, + ) } fn render_title_right_side( &self, @@ -310,8 +348,8 @@ impl PaneFrame { } } fn render_my_focus(&self, max_length: usize) -> Option<(Vec, usize)> { - let mut left_separator = foreground_color(boundary_type::VERTICAL_LEFT, self.color); - let mut right_separator = foreground_color(boundary_type::VERTICAL_RIGHT, self.color); + let mut left_separator = foreground_color(self.title_separator_left(), self.color); + let mut right_separator = foreground_color(self.title_separator_right(), self.color); let full_indication_text = "MY FOCUS"; let mut full_indication = vec![]; full_indication.append(&mut left_separator); @@ -340,8 +378,8 @@ impl PaneFrame { &self, max_length: usize, ) -> Option<(Vec, usize)> { - let mut left_separator = foreground_color(boundary_type::VERTICAL_LEFT, self.color); - let mut right_separator = foreground_color(boundary_type::VERTICAL_RIGHT, self.color); + let mut left_separator = foreground_color(self.title_separator_left(), self.color); + let mut right_separator = foreground_color(self.title_separator_right(), self.color); let full_indication_text = "MY FOCUS AND:"; let short_indication_text = "+"; let mut full_indication = foreground_color(full_indication_text, self.color); @@ -383,8 +421,8 @@ impl PaneFrame { &self, max_length: usize, ) -> Option<(Vec, usize)> { - let mut left_separator = foreground_color(boundary_type::VERTICAL_LEFT, self.color); - let mut right_separator = foreground_color(boundary_type::VERTICAL_RIGHT, self.color); + let mut left_separator = foreground_color(self.title_separator_left(), self.color); + let mut right_separator = foreground_color(self.title_separator_right(), self.color); let full_indication_text = if self.other_focused_clients.len() == 1 { "FOCUSED USER:" } else { @@ -563,7 +601,7 @@ impl PaneFrame { col += right_side_len; continue; } else { - title_line.append(&mut foreground_color(boundary_type::HORIZONTAL, self.color)); + title_line.append(&mut foreground_color(self.top_horizontal(), self.color)); } if col == self.geom.x + self.geom.cols - 1 { break; @@ -605,7 +643,7 @@ impl PaneFrame { col += *middle_len; continue; } else { - title_line.append(&mut foreground_color(boundary_type::HORIZONTAL, self.color)); + title_line.append(&mut foreground_color(self.top_horizontal(), self.color)); } if col == self.geom.x + self.geom.cols - 1 { break; @@ -640,7 +678,7 @@ impl PaneFrame { col += *middle_len; continue; } else { - title_line.append(&mut foreground_color(boundary_type::HORIZONTAL, self.color)); + title_line.append(&mut foreground_color(self.top_horizontal(), self.color)); } if col == self.geom.x + self.geom.cols - 1 { break; @@ -663,7 +701,7 @@ impl PaneFrame { let total_title_length = self.geom.cols.saturating_sub(2); // 2 for the left and right corners let mut middle = String::new(); for _ in (left_side_len + right_side_len)..total_title_length { - middle.push_str(boundary_type::HORIZONTAL); + middle.push_str(self.top_horizontal()); } let mut ret = vec![]; ret.append(&mut left_boundary); @@ -685,7 +723,7 @@ impl PaneFrame { let total_title_length = self.geom.cols.saturating_sub(2); // 2 for the left and right corners let mut middle_padding = String::new(); for _ in *left_side_len..total_title_length { - middle_padding.push_str(boundary_type::HORIZONTAL); + middle_padding.push_str(self.top_horizontal()); } let mut ret = vec![]; ret.append(&mut left_boundary); @@ -702,7 +740,7 @@ impl PaneFrame { let total_title_length = self.geom.cols.saturating_sub(2); // 2 for the left and right corners let mut middle_padding = String::new(); for _ in 0..total_title_length { - middle_padding.push_str(boundary_type::HORIZONTAL); + middle_padding.push_str(self.top_horizontal()); } let mut ret = vec![]; ret.append(&mut left_boundary); @@ -812,7 +850,7 @@ impl PaneFrame { line.push(EMPTY_TERMINAL_CHARACTER); } } - line.append(&mut foreground_color(boundary_type::VERTICAL, None)); + line.append(&mut foreground_color(self.left_vertical(), None)); for _ in 0..corner_padding { line.push(EMPTY_TERMINAL_CHARACTER); } @@ -824,7 +862,7 @@ impl PaneFrame { for _ in 0..corner_padding { line.push(EMPTY_TERMINAL_CHARACTER); } - line.append(&mut foreground_color(boundary_type::VERTICAL, None)); + line.append(&mut foreground_color(self.right_vertical(), None)); let mut occupied_columns = entry_start + full_width_entry_length; let (mut scroll_part, scroll_length) = self .bracketed_scroll_indicator(usable_cols.saturating_sub(occupied_columns)) @@ -1045,7 +1083,7 @@ impl PaneFrame { }; let right_start = width.saturating_sub(right_length).max(middle_end); let fill_character = if self.pane_is_stacked { - foreground_color(boundary_type::HORIZONTAL, self.color) + foreground_color(self.bottom_horizontal(), self.color) .into_iter() .next() .unwrap_or(EMPTY_TERMINAL_CHARACTER) @@ -1096,7 +1134,7 @@ impl PaneFrame { let padding_len = max_undertitle_length.saturating_sub(text_len); let mut padding = String::new(); for _ in 0..padding_len { - padding.push_str(boundary_type::HORIZONTAL); + padding.push_str(self.bottom_horizontal()); } let mut ret = vec![]; @@ -1130,7 +1168,7 @@ impl PaneFrame { let padding_len = max_undertitle_length.saturating_sub(help_text_len); let mut padding = String::new(); for _ in 0..padding_len { - padding.push_str(boundary_type::HORIZONTAL); + padding.push_str(self.bottom_horizontal()); } let mut ret = vec![]; @@ -1183,7 +1221,7 @@ impl PaneFrame { // render exit status and tips let mut padding = String::new(); for _ in full_text_len..max_undertitle_length { - padding.push_str(boundary_type::HORIZONTAL); + padding.push_str(self.bottom_horizontal()); } let mut ret = vec![]; ret.append(&mut left_boundary); @@ -1196,7 +1234,7 @@ impl PaneFrame { // render only exit status let mut padding = String::new(); for _ in first_part_len..max_undertitle_length { - padding.push_str(boundary_type::HORIZONTAL); + padding.push_str(self.bottom_horizontal()); } let mut ret = vec![]; ret.append(&mut left_boundary); @@ -1213,7 +1251,7 @@ impl PaneFrame { let full_text_len = first_part_len; let mut padding = String::new(); for _ in full_text_len..max_undertitle_length { - padding.push_str(boundary_type::HORIZONTAL); + padding.push_str(self.bottom_horizontal()); } let mut ret = vec![]; ret.append(&mut left_boundary); @@ -1241,7 +1279,7 @@ impl PaneFrame { // render exit status and tips let mut padding = String::new(); for _ in hover_shortcuts_len..max_undertitle_length { - padding.push_str(boundary_type::HORIZONTAL); + padding.push_str(self.bottom_horizontal()); } let mut ret = vec![]; ret.append(&mut left_boundary); @@ -1369,7 +1407,7 @@ impl PaneFrame { // bottom right corner self.get_corner(boundary_type::BOTTOM_RIGHT) } else { - boundary_type::HORIZONTAL + self.bottom_horizontal() }; let mut boundary_character = foreground_color(boundary, self.color); @@ -1381,9 +1419,9 @@ impl PaneFrame { } } else { let boundary_character_left = - foreground_color(boundary_type::VERTICAL, self.color); + foreground_color(self.left_vertical(), self.color); let boundary_character_right = - foreground_color(boundary_type::VERTICAL, self.color); + foreground_color(self.right_vertical(), self.color); let x = self.geom.x; let y = self.geom.y + row; @@ -1421,7 +1459,7 @@ impl PaneFrame { Some(ExitStatus::Exited) => { self.render_hint_segments(&exit_code_segments(HintExitStatus::Exited)) }, - None => (foreground_color(boundary_type::HORIZONTAL, self.color), 1), + None => (foreground_color(self.bottom_horizontal(), self.color), 1), } } fn second_held_title_part_full(&self) -> (Vec, usize) { @@ -1438,7 +1476,7 @@ impl PaneFrame { let mut ret = vec![]; let mut padding = String::new(); for _ in 0..max_undertitle_length { - padding.push_str(boundary_type::HORIZONTAL); + padding.push_str(self.bottom_horizontal()); } ret.append(&mut left_boundary); ret.append(&mut foreground_color(&padding, self.color)); @@ -1468,6 +1506,7 @@ mod tests { is_main_client: true, other_focused_clients: vec![], style: Style::default(), + border_style: BorderStyle::default(), color: None, other_cursors_exist_in_session: false, pane_is_stacked_over: false, @@ -1496,6 +1535,94 @@ mod tests { chars.iter().map(|c| c.character).collect() } + fn frame_rows(border_style: BorderStyle) -> Vec { + let mut frame = pane_frame_with(false, false, 10); + frame.border_style = border_style; + frame.geom.rows = 3; + frame.mouse_hover_tips = false; + frame.show_help_text = false; + let (chunks, _) = frame.render().unwrap(); + let mut rows: Vec<(usize, usize, String)> = chunks + .iter() + .map(|c| (c.y, c.x, characters_to_string(&c.terminal_characters))) + .collect(); + rows.sort(); + let mut by_row: Vec = vec![String::new(); 3]; + for (y, _x, text) in rows { + by_row[y].push_str(&text); + } + by_row + } + + #[test] + fn single_line_frame_with_rounded_corners() { + let rows = frame_rows(BorderStyle { + rounded_corners: true, + ..Default::default() + }); + assert!(rows[0].starts_with('╭'), "{}", rows[0]); + assert!(rows[0].ends_with('╮'), "{}", rows[0]); + assert_eq!(rows[1], "││"); + assert_eq!(rows[2], "╰────────╯"); + } + + #[test] + fn double_top_border_with_single_sides() { + let rows = frame_rows(BorderStyle { + top: LineStyle::Double, + rounded_corners: true, + ..Default::default() + }); + assert!(rows[0].starts_with('╒'), "{}", rows[0]); + assert!(rows[0].ends_with('╕'), "{}", rows[0]); + assert_eq!(rows[1], "││"); + assert_eq!(rows[2], "╰────────╯"); + } + + #[test] + fn all_double_border() { + let rows = frame_rows(BorderStyle { + top: LineStyle::Double, + right: LineStyle::Double, + bottom: LineStyle::Double, + left: LineStyle::Double, + rounded_corners: true, + }); + assert!(rows[0].starts_with('╔'), "{}", rows[0]); + assert!(rows[0].ends_with('╗'), "{}", rows[0]); + assert_eq!(rows[1], "║║"); + assert_eq!(rows[2], "╚════════╝"); + } + + #[test] + fn heavy_and_double_corner_falls_back_to_the_horizontal_arm() { + let rows = frame_rows(BorderStyle { + top: LineStyle::Double, + right: LineStyle::Heavy, + bottom: LineStyle::Heavy, + left: LineStyle::Heavy, + rounded_corners: false, + }); + assert!(rows[0].starts_with('╔'), "{}", rows[0]); + assert!(rows[0].ends_with('╗'), "{}", rows[0]); + assert_eq!(rows[1], "┃┃"); + assert_eq!(rows[2], "┗━━━━━━━━┛"); + } + + #[test] + fn dashed_border_uses_light_corners() { + let rows = frame_rows(BorderStyle { + top: LineStyle::Dashed, + right: LineStyle::Dashed, + bottom: LineStyle::Dashed, + left: LineStyle::Dashed, + rounded_corners: false, + }); + assert!(rows[0].starts_with('┌'), "{}", rows[0]); + assert_eq!(rows[1], "┆┆"); + assert_eq!(rows[2], "└┄┄┄┄┄┄┄┄┘"); + } + #[test] fn help_text_full_includes_ctrl_scroll_when_enabled_for_tiled_pane() { let frame = pane_frame_with(true, false, 80); diff --git a/zellij-server/src/ui/pane_contents_and_ui.rs b/zellij-server/src/ui/pane_contents_and_ui.rs index dad3765d4..5d9f1cd58 100644 --- a/zellij-server/src/ui/pane_contents_and_ui.rs +++ b/zellij-server/src/ui/pane_contents_and_ui.rs @@ -6,7 +6,7 @@ use crate::ui::boundaries::Boundaries; use crate::ui::pane_boundaries_frame::{FrameParams, StackListEntry}; use crate::ClientId; use std::collections::{HashMap, HashSet}; -use zellij_utils::data::{client_id_to_colors, InputMode, PaletteColor, Style}; +use zellij_utils::data::{client_id_to_colors, BorderStyle, InputMode, PaletteColor, Style}; use zellij_utils::errors::prelude::*; use zellij_utils::pane_size::PaneGeom; @@ -358,12 +358,14 @@ impl<'a> PaneContentsAndUi<'a> { }); let frame_is_dimmed = self.frame_is_dimmed_for_client(client_id); let guest_choice_indicator = self.pane.guest_choice_indicator(client_id); + let border_style = self.border_style(pane_is_floating); let frame_params = if session_is_mirrored { FrameParams { focused_client, is_main_client: pane_focused_for_client_id, other_focused_clients: vec![], style: self.style, + border_style, color: frame_color.map(|c| c.0), other_cursors_exist_in_session: false, pane_is_stacked_over: self.pane_is_stacked_over, @@ -393,6 +395,7 @@ impl<'a> PaneContentsAndUi<'a> { is_main_client: pane_focused_for_client_id, other_focused_clients, style: self.style, + border_style, color: frame_color.map(|c| c.0), other_cursors_exist_in_session: self.multiple_users_exist_in_session, pane_is_stacked_over: self.pane_is_stacked_over, @@ -437,6 +440,14 @@ impl<'a> PaneContentsAndUi<'a> { Ok(()) } + pub fn border_style(&self, pane_is_floating: bool) -> BorderStyle { + let base = if pane_is_floating { + self.style.floating_border_style + } else { + self.style.border_style + }; + self.pane.border_style_override().apply_to(base) + } pub fn render_pane_boundaries( &self, client_id: ClientId, @@ -447,9 +458,11 @@ impl<'a> PaneContentsAndUi<'a> { pane_is_on_bottom_of_stack: bool, ) { let color = self.frame_color(client_id, client_mode, session_is_mirrored); + let pane_is_floating = false; boundaries.add_rect( self.pane.as_ref(), color, + self.border_style(pane_is_floating), pane_is_on_top_of_stack, pane_is_on_bottom_of_stack, self.pane_is_stacked_under, diff --git a/zellij-server/src/unit/screen_tests.rs b/zellij-server/src/unit/screen_tests.rs index 88586bfde..2da4cef6e 100644 --- a/zellij-server/src/unit/screen_tests.rs +++ b/zellij-server/src/unit/screen_tests.rs @@ -1744,6 +1744,7 @@ fn open_new_floating_pane_with_custom_coordinates() { height: Some(PercentOrFixed::Fixed(2)), pinned: None, borderless: Some(false), + border_style: None, })), Some(1), None, @@ -1780,6 +1781,7 @@ fn open_new_floating_pane_with_custom_coordinates_exceeding_viewport() { height: Some(PercentOrFixed::Fixed(10)), pinned: None, borderless: Some(false), + border_style: None, })), Some(1), None, @@ -1816,6 +1818,7 @@ fn floating_pane_auto_centers_horizontally_with_only_width() { height: Some(PercentOrFixed::Fixed(10)), pinned: None, borderless: Some(false), + border_style: None, })), Some(1), None, @@ -1852,6 +1855,7 @@ fn floating_pane_auto_centers_vertically_with_only_height() { height: Some(PercentOrFixed::Fixed(20)), pinned: None, borderless: Some(false), + border_style: None, })), Some(1), None, @@ -1888,6 +1892,7 @@ fn floating_pane_auto_centers_both_axes_with_only_size() { height: Some(PercentOrFixed::Fixed(30)), pinned: None, borderless: Some(false), + border_style: None, })), Some(1), None, @@ -1924,6 +1929,7 @@ fn floating_pane_respects_explicit_coordinates_with_size() { height: Some(PercentOrFixed::Fixed(30)), pinned: None, borderless: Some(false), + border_style: None, })), Some(1), None, @@ -1960,6 +1966,7 @@ fn floating_pane_centers_with_percentage_width() { height: Some(PercentOrFixed::Fixed(20)), pinned: None, borderless: Some(false), + border_style: None, })), Some(1), None, @@ -2001,6 +2008,7 @@ fn floating_pane_centers_large_pane_safely() { height: Some(PercentOrFixed::Fixed(50)), pinned: None, borderless: Some(false), + border_style: None, })), Some(1), None, @@ -2413,6 +2421,7 @@ fn group_panes_following_focus() { NewPanePlacement::Tiled { direction: None, borderless: None, + border_style: None, }, Some(client_id), None, @@ -2474,6 +2483,7 @@ fn break_group_with_mouse() { NewPanePlacement::Tiled { direction: None, borderless: None, + border_style: None, }, Some(client_id), None, @@ -3572,6 +3582,7 @@ pub fn send_cli_new_pane_action_with_default_parameters() { no_focus: false, borderless: Some(false), tab_id: None, + border_style: None, }; send_cli_action_to_server(&session_metadata, cli_new_pane_action, client_id); std::thread::sleep(std::time::Duration::from_millis(100)); // give time for actions to be @@ -3607,6 +3618,7 @@ pub fn web_new_pane_in_tab_action_targets_requested_tab() { no_focus: false, borderless: None, tab_id: Some(0), + border_style: None, }; route_arbitrary_action_to_server(&session_metadata, action, client_id); std::thread::sleep(std::time::Duration::from_millis(100)); @@ -3682,6 +3694,7 @@ pub fn send_cli_new_pane_action_with_split_direction() { no_focus: false, borderless: Some(false), tab_id: None, + border_style: None, }; send_cli_action_to_server(&session_metadata, cli_new_pane_action, client_id); std::thread::sleep(std::time::Duration::from_millis(100)); // give time for actions to be @@ -3738,6 +3751,7 @@ pub fn send_cli_new_pane_action_with_command_and_cwd() { no_focus: false, borderless: Some(false), tab_id: None, + border_style: None, }; send_cli_action_to_server(&session_metadata, cli_new_pane_action, client_id); std::thread::sleep(std::time::Duration::from_millis(100)); // give time for actions to be @@ -3805,6 +3819,7 @@ pub fn send_cli_new_pane_action_with_floating_pane_and_coordinates() { no_focus: false, borderless: Some(false), tab_id: None, + border_style: None, }; send_cli_action_to_server(&session_metadata, cli_new_pane_action, client_id); std::thread::sleep(std::time::Duration::from_millis(100)); // give time for actions to be @@ -3849,6 +3864,7 @@ pub fn send_cli_edit_action_with_default_parameters() { near_current_pane: false, no_focus: false, tab_id: None, + border_style: None, }; send_cli_action_to_server(&session_metadata, cli_edit_action, client_id); std::thread::sleep(std::time::Duration::from_millis(100)); // give time for actions to be @@ -3893,6 +3909,7 @@ pub fn send_cli_edit_action_with_line_number() { near_current_pane: false, no_focus: false, tab_id: None, + border_style: None, }; send_cli_action_to_server(&session_metadata, cli_edit_action, client_id); std::thread::sleep(std::time::Duration::from_millis(100)); // give time for actions to be @@ -3937,6 +3954,7 @@ pub fn send_cli_edit_action_with_split_direction() { near_current_pane: false, no_focus: false, tab_id: None, + border_style: None, }; send_cli_action_to_server(&session_metadata, cli_edit_action, client_id); std::thread::sleep(std::time::Duration::from_millis(100)); // give time for actions to be @@ -5310,6 +5328,7 @@ pub fn send_cli_change_floating_pane_coordinates_action() { height: Some("10".to_owned()), pinned: None, borderless: Some(false), + border_style: None, }; send_cli_action_to_server( &session_metadata, @@ -5329,6 +5348,39 @@ pub fn send_cli_change_floating_pane_coordinates_action() { assert_snapshot!(format!("{}", snapshot_count)); } +#[test] +pub fn send_cli_set_pane_border_style_action() { + let size = Size { cols: 80, rows: 10 }; + let client_id = 10; + let mut mock_screen = MockScreen::new(size); + let session_metadata = mock_screen.clone_session_metadata(); + let screen_thread = mock_screen.run(Some(TiledPaneLayout::default()), vec![]); + + 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 set_pane_border_style_action = CliAction::SetPaneBorderStyle { + pane_id: "0".to_owned(), + border_style: Some("double,rounded:false".to_owned()), + }; + send_cli_action_to_server(&session_metadata, set_pane_border_style_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)); +} + #[test] pub fn go_to_tab_by_id_verifies_screen_state() { let size = Size { @@ -5633,6 +5685,7 @@ pub fn send_cli_new_pane_in_place_with_close_replaced_pane() { no_focus: false, borderless: None, tab_id: None, + border_style: None, }; send_cli_action_to_server(&session_metadata, cli_action, client_id); std::thread::sleep(std::time::Duration::from_millis(100)); @@ -5685,6 +5738,7 @@ pub fn send_cli_edit_in_place_with_close_replaced_pane() { no_focus: false, borderless: None, tab_id: None, + border_style: None, }; send_cli_action_to_server(&session_metadata, cli_action, client_id); std::thread::sleep(std::time::Duration::from_millis(100)); @@ -8424,6 +8478,7 @@ pub fn send_cli_new_pane_action_with_tab_id() { no_focus: false, borderless: Some(false), tab_id: Some(0), + border_style: None, }; send_cli_action_to_server(&session_metadata, cli_new_pane_action, client_id); std::thread::sleep(std::time::Duration::from_millis(100)); @@ -8487,6 +8542,7 @@ pub fn send_cli_new_floating_pane_action_with_tab_id() { no_focus: false, borderless: None, tab_id: Some(0), + border_style: None, }; send_cli_action_to_server(&session_metadata, cli_new_pane_action, client_id); std::thread::sleep(std::time::Duration::from_millis(100)); @@ -8537,6 +8593,7 @@ pub fn send_cli_edit_action_with_tab_id() { no_focus: false, borderless: None, tab_id: Some(0), + border_style: None, }; send_cli_action_to_server(&session_metadata, cli_edit_action, client_id); std::thread::sleep(std::time::Duration::from_millis(100)); @@ -8599,6 +8656,7 @@ pub fn send_cli_new_pane_action_with_tab_id_and_direction() { no_focus: false, borderless: Some(false), tab_id: Some(0), + border_style: None, }; send_cli_action_to_server(&session_metadata, cli_new_pane_action, client_id); std::thread::sleep(std::time::Duration::from_millis(100)); @@ -8661,6 +8719,7 @@ pub fn send_cli_new_pane_action_with_tab_id_and_stacked() { no_focus: false, borderless: None, tab_id: Some(0), + border_style: None, }; send_cli_action_to_server(&session_metadata, cli_new_pane_action, client_id); std::thread::sleep(std::time::Duration::from_millis(100)); diff --git a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_edit_action_with_default_parameters.snap b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_edit_action_with_default_parameters.snap index 8996ded86..5cf42d136 100644 --- a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_edit_action_with_default_parameters.snap +++ b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_edit_action_with_default_parameters.snap @@ -2,4 +2,4 @@ source: zellij-server/src/./unit/screen_tests.rs expression: "format!(\"{:?}\", *received_pty_instructions.lock().unwrap())" --- -[UpdateActivePane(Some(Terminal(0)), 1), UpdateActivePane(Some(Terminal(0)), 1), SpawnTerminal(Some(OpenFile(OpenFilePayload { path: "/file/to/edit", line_number: None, cwd: Some("."), originating_plugin: None })), Some("Editing: /file/to/edit"), Tiled { direction: None, borderless: None }, false, ClientId(10), Some(NotificationEnd { channel: None, exit_status: None, unblock_condition: None, affected_pane_id: None, affected_tab_id: None, error_message: None, stdout_message: None }), false), Exit] +[UpdateActivePane(Some(Terminal(0)), 1), UpdateActivePane(Some(Terminal(0)), 1), SpawnTerminal(Some(OpenFile(OpenFilePayload { path: "/file/to/edit", line_number: None, cwd: Some("."), originating_plugin: None })), Some("Editing: /file/to/edit"), Tiled { direction: None, borderless: Some(false), border_style: None }, false, ClientId(10), Some(NotificationEnd { channel: None, exit_status: None, unblock_condition: None, affected_pane_id: None, affected_tab_id: None, error_message: None, stdout_message: None }), false), Exit] diff --git a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_edit_action_with_line_number.snap b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_edit_action_with_line_number.snap index 2866060be..5e9b73444 100644 --- a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_edit_action_with_line_number.snap +++ b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_edit_action_with_line_number.snap @@ -2,4 +2,4 @@ source: zellij-server/src/./unit/screen_tests.rs expression: "format!(\"{:?}\", *received_pty_instructions.lock().unwrap())" --- -[UpdateActivePane(Some(Terminal(0)), 1), UpdateActivePane(Some(Terminal(0)), 1), SpawnTerminal(Some(OpenFile(OpenFilePayload { path: "/file/to/edit", line_number: Some(100), cwd: Some("."), originating_plugin: None })), Some("Editing: /file/to/edit"), Tiled { direction: None, borderless: None }, false, ClientId(10), Some(NotificationEnd { channel: None, exit_status: None, unblock_condition: None, affected_pane_id: None, affected_tab_id: None, error_message: None, stdout_message: None }), false), Exit] +[UpdateActivePane(Some(Terminal(0)), 1), UpdateActivePane(Some(Terminal(0)), 1), SpawnTerminal(Some(OpenFile(OpenFilePayload { path: "/file/to/edit", line_number: Some(100), cwd: Some("."), originating_plugin: None })), Some("Editing: /file/to/edit"), Tiled { direction: None, borderless: Some(false), border_style: None }, false, ClientId(10), Some(NotificationEnd { channel: None, exit_status: None, unblock_condition: None, affected_pane_id: None, affected_tab_id: None, error_message: None, stdout_message: None }), false), Exit] diff --git a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_edit_action_with_split_direction.snap b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_edit_action_with_split_direction.snap index 036f62bfc..920731822 100644 --- a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_edit_action_with_split_direction.snap +++ b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_edit_action_with_split_direction.snap @@ -2,4 +2,4 @@ source: zellij-server/src/./unit/screen_tests.rs expression: "format!(\"{:?}\", *received_pty_instructions.lock().unwrap())" --- -[UpdateActivePane(Some(Terminal(0)), 1), UpdateActivePane(Some(Terminal(0)), 1), SpawnTerminal(Some(OpenFile(OpenFilePayload { path: "/file/to/edit", line_number: None, cwd: Some("."), originating_plugin: None })), Some("Editing: /file/to/edit"), Tiled { direction: Some(Down), borderless: None }, false, ClientId(10), Some(NotificationEnd { channel: None, exit_status: None, unblock_condition: None, affected_pane_id: None, affected_tab_id: None, error_message: None, stdout_message: None }), false), Exit] +[UpdateActivePane(Some(Terminal(0)), 1), UpdateActivePane(Some(Terminal(0)), 1), SpawnTerminal(Some(OpenFile(OpenFilePayload { path: "/file/to/edit", line_number: None, cwd: Some("."), originating_plugin: None })), Some("Editing: /file/to/edit"), Tiled { direction: Some(Down), borderless: Some(false), border_style: None }, false, ClientId(10), Some(NotificationEnd { channel: None, exit_status: None, unblock_condition: None, affected_pane_id: None, affected_tab_id: None, error_message: None, stdout_message: None }), false), Exit] diff --git a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_new_pane_action_with_command_and_cwd.snap b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_new_pane_action_with_command_and_cwd.snap index 43dd40d13..e77a4a2b6 100644 --- a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_new_pane_action_with_command_and_cwd.snap +++ b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_new_pane_action_with_command_and_cwd.snap @@ -2,4 +2,4 @@ source: zellij-server/src/./unit/screen_tests.rs expression: "format!(\"{:?}\", new_pane_instruction)" --- -Some(SpawnTerminal(Some(RunCommand(RunCommand { command: "htop", args: [], cwd: Some("/some/folder"), hold_on_close: true, hold_on_start: false, originating_plugin: None, use_terminal_title: false })), None, Tiled { direction: Some(Right), borderless: Some(false) }, false, ClientId(10), Some(NotificationEnd { channel: None, exit_status: None, unblock_condition: None, affected_pane_id: None, affected_tab_id: None, error_message: None, stdout_message: None }), false)) +Some(SpawnTerminal(Some(RunCommand(RunCommand { command: "htop", args: [], cwd: Some("/some/folder"), hold_on_close: true, hold_on_start: false, originating_plugin: None, use_terminal_title: false })), None, Tiled { direction: Some(Right), borderless: Some(false), border_style: None }, false, ClientId(10), Some(NotificationEnd { channel: None, exit_status: None, unblock_condition: None, affected_pane_id: None, affected_tab_id: None, error_message: None, stdout_message: None }), false)) diff --git a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_new_pane_action_with_default_parameters.snap b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_new_pane_action_with_default_parameters.snap index 433f4d932..88c147f9e 100644 --- a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_new_pane_action_with_default_parameters.snap +++ b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_new_pane_action_with_default_parameters.snap @@ -2,4 +2,4 @@ source: zellij-server/src/./unit/screen_tests.rs expression: "format!(\"{:?}\", *received_pty_instructions.lock().unwrap())" --- -[UpdateActivePane(Some(Terminal(0)), 1), UpdateActivePane(Some(Terminal(0)), 1), SpawnTerminal(None, None, Tiled { direction: None, borderless: Some(false) }, false, ClientId(10), Some(NotificationEnd { channel: None, exit_status: None, unblock_condition: None, affected_pane_id: None, affected_tab_id: None, error_message: None, stdout_message: None }), false), Exit] +[UpdateActivePane(Some(Terminal(0)), 1), UpdateActivePane(Some(Terminal(0)), 1), SpawnTerminal(None, None, Tiled { direction: None, borderless: Some(false), border_style: None }, false, ClientId(10), Some(NotificationEnd { channel: None, exit_status: None, unblock_condition: None, affected_pane_id: None, affected_tab_id: None, error_message: None, stdout_message: None }), false), Exit] diff --git a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_new_pane_action_with_floating_pane_and_coordinates.snap b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_new_pane_action_with_floating_pane_and_coordinates.snap index f33730b20..085ac9a99 100644 --- a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_new_pane_action_with_floating_pane_and_coordinates.snap +++ b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_new_pane_action_with_floating_pane_and_coordinates.snap @@ -2,4 +2,4 @@ source: zellij-server/src/./unit/screen_tests.rs expression: "format!(\"{:?}\", *received_pty_instructions.lock().unwrap())" --- -[UpdateActivePane(Some(Terminal(0)), 1), UpdateActivePane(Some(Terminal(0)), 1), SpawnTerminal(Some(RunCommand(RunCommand { command: "htop", args: [], cwd: Some("/some/folder"), hold_on_close: true, hold_on_start: false, originating_plugin: None, use_terminal_title: false })), None, Floating(Some(FloatingPaneCoordinates { x: Some(Fixed(10)), y: None, width: Some(Percent(20)), height: None, pinned: None, borderless: Some(false) })), false, ClientId(10), Some(NotificationEnd { channel: None, exit_status: None, unblock_condition: None, affected_pane_id: None, affected_tab_id: None, error_message: None, stdout_message: None }), false), Exit] +[UpdateActivePane(Some(Terminal(0)), 1), UpdateActivePane(Some(Terminal(0)), 1), SpawnTerminal(Some(RunCommand(RunCommand { command: "htop", args: [], cwd: Some("/some/folder"), hold_on_close: true, hold_on_start: false, originating_plugin: None, use_terminal_title: false })), None, Floating(Some(FloatingPaneCoordinates { x: Some(Fixed(10)), y: None, width: Some(Percent(20)), height: None, pinned: None, borderless: Some(false), border_style: None })), false, ClientId(10), Some(NotificationEnd { channel: None, exit_status: None, unblock_condition: None, affected_pane_id: None, affected_tab_id: None, error_message: None, stdout_message: None }), false), Exit] diff --git a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_new_pane_action_with_split_direction.snap b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_new_pane_action_with_split_direction.snap index 1aebde3e7..55cd8cbb3 100644 --- a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_new_pane_action_with_split_direction.snap +++ b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_new_pane_action_with_split_direction.snap @@ -2,4 +2,4 @@ source: zellij-server/src/./unit/screen_tests.rs expression: "format!(\"{:?}\", *received_pty_instructions.lock().unwrap())" --- -[UpdateActivePane(Some(Terminal(0)), 1), UpdateActivePane(Some(Terminal(0)), 1), SpawnTerminal(None, None, Tiled { direction: Some(Right), borderless: Some(false) }, false, ClientId(10), Some(NotificationEnd { channel: None, exit_status: None, unblock_condition: None, affected_pane_id: None, affected_tab_id: None, error_message: None, stdout_message: None }), false), Exit] +[UpdateActivePane(Some(Terminal(0)), 1), UpdateActivePane(Some(Terminal(0)), 1), SpawnTerminal(None, None, Tiled { direction: Some(Right), borderless: Some(false), border_style: None }, false, ClientId(10), Some(NotificationEnd { channel: None, exit_status: None, unblock_condition: None, affected_pane_id: None, affected_tab_id: None, error_message: None, stdout_message: None }), false), Exit] diff --git a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_new_tab_action_default_params.snap b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_new_tab_action_default_params.snap index aa0e35966..c99cb2d37 100644 --- a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_new_tab_action_default_params.snap +++ b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_new_tab_action_default_params.snap @@ -1,6 +1,5 @@ --- source: zellij-server/src/./unit/screen_tests.rs -assertion_line: 3515 expression: "format!(\"{:#?}\", new_tab_action)" --- Some( @@ -19,6 +18,7 @@ Some( split_size: None, run: None, borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -37,6 +37,7 @@ Some( split_size: None, run: None, borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -52,6 +53,7 @@ Some( split_size: None, run: None, borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, diff --git a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_new_tab_action_with_layout_string.snap b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_new_tab_action_with_layout_string.snap index b12db36c0..6a5bf0f11 100644 --- a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_new_tab_action_with_layout_string.snap +++ b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_new_tab_action_with_layout_string.snap @@ -1,6 +1,6 @@ --- source: zellij-server/src/./unit/screen_tests.rs -expression: "format!(\"{:#?}\", new_tab_instruction)" +expression: output --- NewTab( None, @@ -21,6 +21,7 @@ NewTab( ), ), borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -43,6 +44,7 @@ NewTab( ), ), borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -65,6 +67,7 @@ NewTab( ), ), borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -84,6 +87,7 @@ NewTab( ), ), borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, diff --git a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_new_tab_action_with_layout_string_and_name.snap b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_new_tab_action_with_layout_string_and_name.snap index b12db36c0..6a5bf0f11 100644 --- a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_new_tab_action_with_layout_string_and_name.snap +++ b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_new_tab_action_with_layout_string_and_name.snap @@ -1,6 +1,6 @@ --- source: zellij-server/src/./unit/screen_tests.rs -expression: "format!(\"{:#?}\", new_tab_instruction)" +expression: output --- NewTab( None, @@ -21,6 +21,7 @@ NewTab( ), ), borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -43,6 +44,7 @@ NewTab( ), ), borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -65,6 +67,7 @@ NewTab( ), ), borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -84,6 +87,7 @@ NewTab( ), ), borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, diff --git a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_new_tab_action_with_name_and_layout.snap b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_new_tab_action_with_name_and_layout.snap index efad1fca0..6a5bf0f11 100644 --- a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_new_tab_action_with_name_and_layout.snap +++ b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_new_tab_action_with_name_and_layout.snap @@ -21,6 +21,7 @@ NewTab( ), ), borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -43,6 +44,7 @@ NewTab( ), ), borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -65,6 +67,7 @@ NewTab( ), ), borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -84,6 +87,7 @@ NewTab( ), ), borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, diff --git a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_set_pane_border_style_action-2.snap b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_set_pane_border_style_action-2.snap new file mode 100644 index 000000000..79d38b17c --- /dev/null +++ b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_set_pane_border_style_action-2.snap @@ -0,0 +1,14 @@ +--- +source: zellij-server/src/./unit/screen_tests.rs +expression: "format!(\"{}\", snapshot)" +--- +00 (C): ╔ Pane #1 ═════════════════════════════════════════════════════════════════════╗ +01 (C): ║ ║ +02 (C): ║ ║ +03 (C): ║ ║ +04 (C): ║ ║ +05 (C): ║ ║ +06 (C): ║ ║ +07 (C): ║ ║ +08 (C): ║ ║ +09 (C): ╚══════════════════════════════════════════════════════════════════════════════╝ diff --git a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_set_pane_border_style_action-3.snap b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_set_pane_border_style_action-3.snap new file mode 100644 index 000000000..45361b56c --- /dev/null +++ b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_set_pane_border_style_action-3.snap @@ -0,0 +1,5 @@ +--- +source: zellij-server/src/./unit/screen_tests.rs +expression: "format!(\"{}\", snapshot_count)" +--- +2 diff --git a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_set_pane_border_style_action.snap b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_set_pane_border_style_action.snap new file mode 100644 index 000000000..7b7638500 --- /dev/null +++ b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_set_pane_border_style_action.snap @@ -0,0 +1,14 @@ +--- +source: zellij-server/src/./unit/screen_tests.rs +expression: "format!(\"{}\", snapshot)" +--- +00 (C): ┌ Pane #1 ─────────────────────────────────────────────────────────────────────┐ +01 (C): │ │ +02 (C): │ │ +03 (C): │ │ +04 (C): │ │ +05 (C): │ │ +06 (C): │ │ +07 (C): │ │ +08 (C): │ │ +09 (C): └──────────────────────────────────────────────────────────────────────────────┘ diff --git a/zellij-tile/src/shim.rs b/zellij-tile/src/shim.rs index 9c44ef436..79aea4093 100644 --- a/zellij-tile/src/shim.rs +++ b/zellij-tile/src/shim.rs @@ -2603,6 +2603,13 @@ pub fn set_pane_borderless(pane_id: PaneId, borderless: bool) { unsafe { host_run_plugin_command() }; } +pub fn set_pane_border_style(pane_id: PaneId, border_style: BorderStyleOverride) { + let plugin_command = PluginCommand::SetPaneBorderStyle(pane_id, border_style); + let protobuf_plugin_command: ProtobufPluginCommand = plugin_command.try_into().unwrap(); + object_to_stdout(&protobuf_plugin_command.encode_to_vec()); + unsafe { host_run_plugin_command() }; +} + /// Set the default foreground and/or background color of a pane /// /// # Arguments diff --git a/zellij-utils/assets/config/default.kdl b/zellij-utils/assets/config/default.kdl index 5ec86b2d7..5e5806187 100644 --- a/zellij-utils/assets/config/default.kdl +++ b/zellij-utils/assets/config/default.kdl @@ -312,6 +312,33 @@ load_plugins { // // pane_frame_style "titles" +// Configure the look of the pane frame borders +// +// ui { +// pane_frames { +// // round the corners of single-line borders +// rounded_corners true +// // line style for all four sides of tiled pane frames +// // one of: single, double, heavy, dashed, heavy_dashed +// border_style "single" +// // per-side overrides, eg. a double top border with single sides +// border_top "double" +// border_right "single" +// border_bottom "single" +// border_left "single" +// // when two neighbouring panes disagree about a shared grid line +// // (pane frames off), that line falls back to border_style above +// // the same keys prefixed with "floating_" apply to floating panes, +// // they default to the values above +// floating_border_style "double" +// floating_border_top "double" +// floating_border_right "double" +// floating_border_bottom "double" +// floating_border_left "double" +// floating_border_rounded_corners false +// } +// } + // Toggle between having Zellij lay out panes according to a predefined set of layouts whenever possible // Options: // - true (default) diff --git a/zellij-utils/assets/prost/api.action.rs b/zellij-utils/assets/prost/api.action.rs index ce9ce0561..5153c4107 100644 --- a/zellij-utils/assets/prost/api.action.rs +++ b/zellij-utils/assets/prost/api.action.rs @@ -158,6 +158,8 @@ pub struct TiledPaneLayout { pub hide_floating_panes: bool, #[prost(string, optional, tag="13")] pub pane_initial_contents: ::core::option::Option<::prost::alloc::string::String>, + #[prost(message, optional, tag="14")] + pub border_style: ::core::option::Option, } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] @@ -186,6 +188,8 @@ pub struct FloatingPaneLayout { pub logical_position: ::core::option::Option, #[prost(bool, optional, tag="12")] pub borderless: ::core::option::Option, + #[prost(message, optional, tag="13")] + pub border_style: ::core::option::Option, } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] @@ -303,7 +307,7 @@ pub struct OverrideLayoutPayload { pub struct Action { #[prost(enumeration="ActionName", tag="1")] pub name: i32, - #[prost(oneof="action::OptionalPayload", 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, 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")] + #[prost(oneof="action::OptionalPayload", 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, 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")] pub optional_payload: ::core::option::Option, } /// Nested message and enum types in `Action`. @@ -429,10 +433,20 @@ pub mod action { ApplyTiledSwapLayoutPayload(::prost::alloc::string::String), #[prost(string, tag="63")] ApplyFloatingSwapLayoutPayload(::prost::alloc::string::String), + #[prost(message, tag="64")] + SetPaneBorderStylePayload(super::SetPaneBorderStylePayload), } } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] +pub struct SetPaneBorderStylePayload { + #[prost(message, optional, tag="1")] + pub pane_id: ::core::option::Option, + #[prost(message, optional, tag="2")] + pub border_style: ::core::option::Option, +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] pub struct SetPaneFrameStylePayload { #[prost(enumeration="super::pane_frame_style::PaneFrameStyle", tag="1")] pub pane_frame_style: i32, @@ -546,6 +560,8 @@ pub struct NewTiledPanePayload { pub near_current_pane: bool, #[prost(bool, optional, tag="4")] pub borderless: ::core::option::Option, + #[prost(message, optional, tag="5")] + pub border_style: ::core::option::Option, } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] @@ -709,6 +725,8 @@ pub struct FloatingPaneCoordinates { pub pinned: ::core::option::Option, #[prost(bool, optional, tag="6")] pub borderless: ::core::option::Option, + #[prost(message, optional, tag="7")] + pub border_style: ::core::option::Option, } /// SetPaneBorderlessPayload specifies the pane and borderless state #[allow(clippy::derive_partial_eq_without_eq)] @@ -748,6 +766,8 @@ pub mod new_pane_placement { pub struct NoPreferenceOptions { #[prost(bool, optional, tag="1")] pub borderless: ::core::option::Option, + #[prost(message, optional, tag="2")] + pub border_style: ::core::option::Option, } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] @@ -756,6 +776,8 @@ pub struct TiledPlacement { pub direction: ::core::option::Option, #[prost(bool, optional, tag="2")] pub borderless: ::core::option::Option, + #[prost(message, optional, tag="3")] + pub border_style: ::core::option::Option, } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] @@ -772,6 +794,8 @@ pub struct InPlaceConfig { pub close_replaced_pane: bool, #[prost(bool, optional, tag="3")] pub borderless: ::core::option::Option, + #[prost(message, optional, tag="4")] + pub border_style: ::core::option::Option, } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] @@ -780,6 +804,8 @@ pub struct StackedPlacement { pub pane_id: ::core::option::Option, #[prost(bool, optional, tag="2")] pub borderless: ::core::option::Option, + #[prost(message, optional, tag="3")] + pub border_style: ::core::option::Option, } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] @@ -1149,6 +1175,7 @@ pub enum ActionName { CopyLastCommandOutput = 113, ApplyTiledSwapLayout = 114, ApplyFloatingSwapLayout = 115, + SetPaneBorderStyle = 116, } impl ActionName { /// String value of the enum field names used in the ProtoBuf definition. @@ -1270,6 +1297,7 @@ impl ActionName { ActionName::CopyLastCommandOutput => "CopyLastCommandOutput", ActionName::ApplyTiledSwapLayout => "ApplyTiledSwapLayout", ActionName::ApplyFloatingSwapLayout => "ApplyFloatingSwapLayout", + ActionName::SetPaneBorderStyle => "SetPaneBorderStyle", } } /// Creates an enum from field names used in the ProtoBuf definition. @@ -1388,6 +1416,7 @@ impl ActionName { "CopyLastCommandOutput" => Some(Self::CopyLastCommandOutput), "ApplyTiledSwapLayout" => Some(Self::ApplyTiledSwapLayout), "ApplyFloatingSwapLayout" => Some(Self::ApplyFloatingSwapLayout), + "SetPaneBorderStyle" => Some(Self::SetPaneBorderStyle), _ => None, } } diff --git a/zellij-utils/assets/prost/api.command.rs b/zellij-utils/assets/prost/api.command.rs index bbb66d4ae..e130f97f9 100644 --- a/zellij-utils/assets/prost/api.command.rs +++ b/zellij-utils/assets/prost/api.command.rs @@ -8,4 +8,6 @@ pub struct Command { pub args: ::prost::alloc::vec::Vec<::prost::alloc::string::String>, #[prost(string, optional, tag="3")] pub cwd: ::core::option::Option<::prost::alloc::string::String>, + #[prost(message, optional, tag="4")] + pub border_style: ::core::option::Option, } diff --git a/zellij-utils/assets/prost/api.file.rs b/zellij-utils/assets/prost/api.file.rs index a87d0e7e0..8846ad407 100644 --- a/zellij-utils/assets/prost/api.file.rs +++ b/zellij-utils/assets/prost/api.file.rs @@ -8,4 +8,6 @@ pub struct File { pub line_number: ::core::option::Option, #[prost(string, optional, tag="3")] pub cwd: ::core::option::Option<::prost::alloc::string::String>, + #[prost(message, optional, tag="4")] + pub border_style: ::core::option::Option, } diff --git a/zellij-utils/assets/prost/api.plugin_command.rs b/zellij-utils/assets/prost/api.plugin_command.rs index 9331c92a7..6c0864734 100644 --- a/zellij-utils/assets/prost/api.plugin_command.rs +++ b/zellij-utils/assets/prost/api.plugin_command.rs @@ -4,7 +4,7 @@ pub struct PluginCommand { #[prost(enumeration="CommandName", tag="1")] 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, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 168, 169, 170, 171, 172, 173")] + #[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, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 168, 169, 170, 171, 172, 173, 174")] pub payload: ::core::option::Option, } /// Nested message and enum types in `PluginCommand`. @@ -324,6 +324,8 @@ pub mod plugin_command { ApplyTiledSwapLayoutPayload(::prost::alloc::string::String), #[prost(string, tag="173")] ApplyFloatingSwapLayoutPayload(::prost::alloc::string::String), + #[prost(message, tag="174")] + SetPaneBorderStylePayload(super::SetPaneBorderStylePayload), } } #[allow(clippy::derive_partial_eq_without_eq)] @@ -574,6 +576,14 @@ pub struct SetPaneBorderlessPayload { } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] +pub struct SetPaneBorderStylePayload { + #[prost(message, optional, tag="1")] + pub pane_id: ::core::option::Option, + #[prost(message, optional, tag="2")] + pub border_style: ::core::option::Option, +} +#[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, @@ -1006,6 +1016,8 @@ pub struct FloatingPaneCoordinates { pub pinned: ::core::option::Option, #[prost(bool, optional, tag="6")] pub borderless: ::core::option::Option, + #[prost(message, optional, tag="7")] + pub border_style: ::core::option::Option, } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] @@ -2233,6 +2245,7 @@ pub enum CommandName { FocusHostSession = 228, ApplyTiledSwapLayout = 229, ApplyFloatingSwapLayout = 230, + SetPaneBorderStyle = 231, } impl CommandName { /// String value of the enum field names used in the ProtoBuf definition. @@ -2446,6 +2459,7 @@ impl CommandName { CommandName::FocusHostSession => "FocusHostSession", CommandName::ApplyTiledSwapLayout => "ApplyTiledSwapLayout", CommandName::ApplyFloatingSwapLayout => "ApplyFloatingSwapLayout", + CommandName::SetPaneBorderStyle => "SetPaneBorderStyle", } } /// Creates an enum from field names used in the ProtoBuf definition. @@ -2656,6 +2670,7 @@ impl CommandName { "FocusHostSession" => Some(Self::FocusHostSession), "ApplyTiledSwapLayout" => Some(Self::ApplyTiledSwapLayout), "ApplyFloatingSwapLayout" => Some(Self::ApplyFloatingSwapLayout), + "SetPaneBorderStyle" => Some(Self::SetPaneBorderStyle), _ => None, } } diff --git a/zellij-utils/assets/prost/api.style.rs b/zellij-utils/assets/prost/api.style.rs index 8329d8f08..71ce9fb9d 100644 --- a/zellij-utils/assets/prost/api.style.rs +++ b/zellij-utils/assets/prost/api.style.rs @@ -11,6 +11,40 @@ pub struct Style { pub hide_session_name: bool, #[prost(message, optional, tag="4")] pub styling: ::core::option::Option, + #[prost(message, optional, tag="5")] + pub border_style: ::core::option::Option, + #[prost(message, optional, tag="6")] + pub floating_border_style: ::core::option::Option, +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct BorderStyle { + #[prost(enumeration="LineStyle", tag="1")] + pub top: i32, + #[prost(enumeration="LineStyle", tag="2")] + pub right: i32, + #[prost(enumeration="LineStyle", tag="3")] + pub bottom: i32, + #[prost(enumeration="LineStyle", tag="4")] + pub left: i32, + #[prost(bool, tag="5")] + pub rounded_corners: bool, +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct BorderStyleOverride { + #[prost(enumeration="LineStyle", optional, tag="1")] + pub all: ::core::option::Option, + #[prost(enumeration="LineStyle", optional, tag="2")] + pub top: ::core::option::Option, + #[prost(enumeration="LineStyle", optional, tag="3")] + pub right: ::core::option::Option, + #[prost(enumeration="LineStyle", optional, tag="4")] + pub bottom: ::core::option::Option, + #[prost(enumeration="LineStyle", optional, tag="5")] + pub left: ::core::option::Option, + #[prost(bool, optional, tag="6")] + pub rounded_corners: ::core::option::Option, } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] @@ -117,6 +151,41 @@ pub struct Styling { } #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)] #[repr(i32)] +pub enum LineStyle { + Single = 0, + Double = 1, + Heavy = 2, + Dashed = 3, + HeavyDashed = 4, +} +impl LineStyle { + /// String value of the enum field names used in the ProtoBuf definition. + /// + /// The values are not transformed in any way and thus are considered stable + /// (if the ProtoBuf definition does not change) and safe for programmatic use. + pub fn as_str_name(&self) -> &'static str { + match self { + LineStyle::Single => "Single", + LineStyle::Double => "Double", + LineStyle::Heavy => "Heavy", + LineStyle::Dashed => "Dashed", + LineStyle::HeavyDashed => "HeavyDashed", + } + } + /// Creates an enum from field names used in the ProtoBuf definition. + pub fn from_str_name(value: &str) -> ::core::option::Option { + match value { + "Single" => Some(Self::Single), + "Double" => Some(Self::Double), + "Heavy" => Some(Self::Heavy), + "Dashed" => Some(Self::Dashed), + "HeavyDashed" => Some(Self::HeavyDashed), + _ => None, + } + } +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)] +#[repr(i32)] pub enum ColorType { Rgb = 0, EightBit = 1, diff --git a/zellij-utils/assets/prost_ipc/client_server_contract.rs b/zellij-utils/assets/prost_ipc/client_server_contract.rs index 84c48e501..98104b5e7 100644 --- a/zellij-utils/assets/prost_ipc/client_server_contract.rs +++ b/zellij-utils/assets/prost_ipc/client_server_contract.rs @@ -71,6 +71,40 @@ pub struct Style { pub rounded_corners: bool, #[prost(bool, tag="3")] pub hide_session_name: bool, + #[prost(message, optional, tag="4")] + pub border_style: ::core::option::Option, + #[prost(message, optional, tag="5")] + pub floating_border_style: ::core::option::Option, +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct BorderStyle { + #[prost(enumeration="LineStyle", tag="1")] + pub top: i32, + #[prost(enumeration="LineStyle", tag="2")] + pub right: i32, + #[prost(enumeration="LineStyle", tag="3")] + pub bottom: i32, + #[prost(enumeration="LineStyle", tag="4")] + pub left: i32, + #[prost(bool, tag="5")] + pub rounded_corners: bool, +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct BorderStyleOverride { + #[prost(enumeration="LineStyle", optional, tag="1")] + pub all: ::core::option::Option, + #[prost(enumeration="LineStyle", optional, tag="2")] + pub top: ::core::option::Option, + #[prost(enumeration="LineStyle", optional, tag="3")] + pub right: ::core::option::Option, + #[prost(enumeration="LineStyle", optional, tag="4")] + pub bottom: ::core::option::Option, + #[prost(enumeration="LineStyle", optional, tag="5")] + pub left: ::core::option::Option, + #[prost(bool, optional, tag="6")] + pub rounded_corners: ::core::option::Option, } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] @@ -120,7 +154,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, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155")] + #[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, 156, 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, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155")] pub action_type: ::core::option::Option, } /// Nested message and enum types in `Action`. @@ -320,6 +354,8 @@ pub mod action { TogglePaneBorderless(super::TogglePaneBorderlessAction), #[prost(message, tag="96")] SetPaneBorderless(super::SetPaneBorderlessAction), + #[prost(message, tag="156")] + SetPaneBorderStyle(super::SetPaneBorderStyleAction), #[prost(message, tag="97")] SaveSession(super::SaveSessionAction), #[prost(message, tag="98")] @@ -949,6 +985,8 @@ pub struct NewTiledPaneAction { pub tab_id: ::core::option::Option, #[prost(bool, tag="10")] pub no_focus: bool, + #[prost(message, optional, tag="11")] + pub border_style: ::core::option::Option, } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] @@ -1366,6 +1404,14 @@ pub struct SetPaneColorAction { } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] +pub struct SetPaneBorderStyleAction { + #[prost(message, optional, tag="1")] + pub pane_id: ::core::option::Option, + #[prost(message, optional, tag="2")] + pub border_style: ::core::option::Option, +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] pub struct Position { /// Changed from uint32 to int32 to support negative line numbers #[prost(int32, tag="1")] @@ -1518,6 +1564,8 @@ pub mod new_pane_placement { pub struct NoPreferencePlacement { #[prost(bool, optional, tag="1")] pub borderless: ::core::option::Option, + #[prost(message, optional, tag="2")] + pub border_style: ::core::option::Option, } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] @@ -1526,6 +1574,8 @@ pub struct TiledPlacement { pub direction: ::core::option::Option, #[prost(bool, optional, tag="2")] pub borderless: ::core::option::Option, + #[prost(message, optional, tag="3")] + pub border_style: ::core::option::Option, } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] @@ -1534,6 +1584,8 @@ pub struct StackedPlacement { pub pane_id_to_stack_under: ::core::option::Option, #[prost(bool, optional, tag="2")] pub borderless: ::core::option::Option, + #[prost(message, optional, tag="3")] + pub border_style: ::core::option::Option, } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] @@ -1544,6 +1596,8 @@ pub struct NewPanePlacementInPlace { pub close_replaced_pane: bool, #[prost(bool, optional, tag="3")] pub borderless: ::core::option::Option, + #[prost(message, optional, tag="4")] + pub border_style: ::core::option::Option, } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] @@ -1582,6 +1636,8 @@ pub struct FloatingPaneCoordinates { pub pinned: ::core::option::Option, #[prost(bool, optional, tag="6")] pub borderless: ::core::option::Option, + #[prost(message, optional, tag="7")] + pub border_style: ::core::option::Option, } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] @@ -1656,9 +1712,11 @@ pub struct TiledPaneLayout { pub pane_initial_contents: ::core::option::Option<::prost::alloc::string::String>, #[prost(string, optional, tag="14")] pub default_fg: ::core::option::Option<::prost::alloc::string::String>, - /// NOTE: run_instructions_to_ignore is not represented here because it's a field used only inside the server itself and not part of the server/client contract #[prost(string, optional, tag="15")] pub default_bg: ::core::option::Option<::prost::alloc::string::String>, + /// NOTE: run_instructions_to_ignore is not represented here because it's a field used only inside the server itself and not part of the server/client contract + #[prost(message, optional, tag="16")] + pub border_style: ::core::option::Option, } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] @@ -1691,6 +1749,8 @@ pub struct FloatingPaneLayout { pub default_fg: ::core::option::Option<::prost::alloc::string::String>, #[prost(string, optional, tag="14")] pub default_bg: ::core::option::Option<::prost::alloc::string::String>, + #[prost(message, optional, tag="15")] + pub border_style: ::core::option::Option, } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] @@ -2438,6 +2498,41 @@ impl KeyModifier { } #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)] #[repr(i32)] +pub enum LineStyle { + Single = 0, + Double = 1, + Heavy = 2, + Dashed = 3, + HeavyDashed = 4, +} +impl LineStyle { + /// String value of the enum field names used in the ProtoBuf definition. + /// + /// The values are not transformed in any way and thus are considered stable + /// (if the ProtoBuf definition does not change) and safe for programmatic use. + pub fn as_str_name(&self) -> &'static str { + match self { + LineStyle::Single => "LINE_STYLE_SINGLE", + LineStyle::Double => "LINE_STYLE_DOUBLE", + LineStyle::Heavy => "LINE_STYLE_HEAVY", + LineStyle::Dashed => "LINE_STYLE_DASHED", + LineStyle::HeavyDashed => "LINE_STYLE_HEAVY_DASHED", + } + } + /// Creates an enum from field names used in the ProtoBuf definition. + pub fn from_str_name(value: &str) -> ::core::option::Option { + match value { + "LINE_STYLE_SINGLE" => Some(Self::Single), + "LINE_STYLE_DOUBLE" => Some(Self::Double), + "LINE_STYLE_HEAVY" => Some(Self::Heavy), + "LINE_STYLE_DASHED" => Some(Self::Dashed), + "LINE_STYLE_HEAVY_DASHED" => Some(Self::HeavyDashed), + _ => None, + } + } +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)] +#[repr(i32)] pub enum AnsiCode { Unspecified = 0, Black = 1, diff --git a/zellij-utils/src/cli.rs b/zellij-utils/src/cli.rs index 80df4edaf..e1430905a 100644 --- a/zellij-utils/src/cli.rs +++ b/zellij-utils/src/cli.rs @@ -537,6 +537,9 @@ pub enum Sessions { conflicts_with("in_place") )] tab_id: Option, + /// Border line style, eg. "double" or "top:double,left:single,rounded" + #[clap(long, value_parser)] + border_style: Option, }, /// Load a plugin /// Returns: Created pane ID (format: plugin_) @@ -592,6 +595,9 @@ pub enum Sessions { /// Target a specific tab by ID #[clap(long, value_parser, conflicts_with("in_place"))] tab_id: Option, + /// Border line style, eg. "double" or "top:double,left:single,rounded" + #[clap(long, value_parser)] + border_style: Option, }, /// Edit file with default $EDITOR / $VISUAL /// Returns: Created pane ID (format: terminal_) @@ -657,6 +663,9 @@ pub enum Sessions { conflicts_with("in_place") )] tab_id: Option, + /// Border line style, eg. "double" or "top:double,left:single,rounded" + #[clap(long, value_parser)] + border_style: Option, }, /// Send data to one or more plugins, launch them if they are not running. #[clap(override_usage( @@ -1014,6 +1023,9 @@ pub enum CliAction { conflicts_with("in_place") )] tab_id: Option, + /// Border line style, eg. "double" or "top:double,left:single,rounded" + #[clap(long, value_parser)] + border_style: Option, }, /// Open the specified file in a new zellij pane with your default EDITOR /// Returns: Created pane ID (format: terminal_) @@ -1078,6 +1090,9 @@ pub enum CliAction { conflicts_with("in_place") )] tab_id: Option, + /// Border line style, eg. "double" or "top:double,left:single,rounded" + #[clap(long, value_parser)] + border_style: Option, }, /// Switch input mode of all connected clients [locked|pane|tab|resize|move|search|session] SwitchMode { @@ -1549,6 +1564,9 @@ tail -f /tmp/my-live-logfile | zellij action pipe --name logs --plugin https://e /// mouse if without a border) #[clap(short, long, value_parser)] borderless: Option, + /// Border line style, eg. "double" or "top:double,left:single,rounded" + #[clap(long, value_parser)] + border_style: Option, }, TogglePaneBorderless { /// The pane_id of the pane, eg. terminal_1, plugin_2 or 3 (equivalent to terminal_3) @@ -1563,6 +1581,15 @@ tail -f /tmp/my-live-logfile | zellij action pipe --name logs --plugin https://e #[clap(short, long, value_parser)] borderless: bool, }, + /// Change the border line style of an existing pane + SetPaneBorderStyle { + /// The pane_id of the pane, eg. terminal_1, plugin_2 or 3 (equivalent to terminal_3) + #[clap(short, long, value_parser)] + pane_id: String, + /// Border line style, eg. "double" or "top:double,left:single,rounded" + #[clap(long, value_parser)] + border_style: Option, + }, /// Detach from the current session Detach, /// Switch the theme to dark (uses configured `theme_dark`). diff --git a/zellij-utils/src/client_server_contract/common_types.proto b/zellij-utils/src/client_server_contract/common_types.proto index 2ff95d61c..d792109f5 100644 --- a/zellij-utils/src/client_server_contract/common_types.proto +++ b/zellij-utils/src/client_server_contract/common_types.proto @@ -89,6 +89,33 @@ message Style { Styling colors = 1; bool rounded_corners = 2; bool hide_session_name = 3; + BorderStyle border_style = 4; + BorderStyle floating_border_style = 5; +} + +enum LineStyle { + LINE_STYLE_SINGLE = 0; + LINE_STYLE_DOUBLE = 1; + LINE_STYLE_HEAVY = 2; + LINE_STYLE_DASHED = 3; + LINE_STYLE_HEAVY_DASHED = 4; +} + +message BorderStyle { + LineStyle top = 1; + LineStyle right = 2; + LineStyle bottom = 3; + LineStyle left = 4; + bool rounded_corners = 5; +} + +message BorderStyleOverride { + optional LineStyle all = 1; + optional LineStyle top = 2; + optional LineStyle right = 3; + optional LineStyle bottom = 4; + optional LineStyle left = 5; + optional bool rounded_corners = 6; } message Styling { @@ -233,6 +260,7 @@ message Action { OverrideLayoutAction override_layout = 94; TogglePaneBorderlessAction toggle_pane_borderless = 95; SetPaneBorderlessAction set_pane_borderless = 96; + SetPaneBorderStyleAction set_pane_border_style = 156; SaveSessionAction save_session = 97; ListPanesAction list_panes = 98; WriteCharsToPaneIdAction write_chars_to_pane_id = 99; @@ -511,6 +539,7 @@ message NewTiledPaneAction { optional bool borderless = 8; optional uint32 tab_id = 9; bool no_focus = 10; + optional BorderStyleOverride border_style = 11; } message NewInPlacePaneAction { @@ -759,6 +788,11 @@ message SetPaneColorAction { optional string bg = 3; } +message SetPaneBorderStyleAction { + PaneId pane_id = 1; + BorderStyleOverride border_style = 2; +} + // Supporting enums and messages enum InputMode { INPUT_MODE_UNSPECIFIED = 0; @@ -880,22 +914,26 @@ message NewPanePlacement { message NoPreferencePlacement { optional bool borderless = 1; + optional BorderStyleOverride border_style = 2; } message TiledPlacement { optional Direction direction = 1; optional bool borderless = 2; + optional BorderStyleOverride border_style = 3; } message StackedPlacement { optional PaneId pane_id_to_stack_under = 1; optional bool borderless = 2; + optional BorderStyleOverride border_style = 3; } message NewPanePlacementInPlace { optional PaneId pane_id_to_replace = 1; bool close_replaced_pane = 2; optional bool borderless = 3; + optional BorderStyleOverride border_style = 4; } message PaneIdWithPlugin { @@ -932,6 +970,7 @@ message FloatingPaneCoordinates { optional FloatingCoordinate height = 4; optional bool pinned = 5; optional bool borderless = 6; + optional BorderStyleOverride border_style = 7; } message FloatingCoordinate { @@ -968,6 +1007,7 @@ message TiledPaneLayout { optional string pane_initial_contents = 13; optional string default_fg = 14; optional string default_bg = 15; + optional BorderStyleOverride border_style = 16; // NOTE: run_instructions_to_ignore is not represented here because it's a field used only inside the server itself and not part of the server/client contract } @@ -986,6 +1026,7 @@ message FloatingPaneLayout { optional bool borderless = 12; optional string default_fg = 13; optional string default_bg = 14; + optional BorderStyleOverride border_style = 15; } message SwapTiledLayout { diff --git a/zellij-utils/src/data.rs b/zellij-utils/src/data.rs index daaa1e982..d00a0f7d3 100644 --- a/zellij-utils/src/data.rs +++ b/zellij-utils/src/data.rs @@ -1426,11 +1426,210 @@ pub struct Palette { pub brown: PaletteColor, } +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Default, Serialize, Deserialize)] +pub enum LineStyle { + #[default] + Single, + Double, + Heavy, + Dashed, + HeavyDashed, +} + +impl LineStyle { + pub fn is_heavy(&self) -> bool { + matches!(self, LineStyle::Heavy | LineStyle::HeavyDashed) + } + pub fn is_double(&self) -> bool { + matches!(self, LineStyle::Double) + } + pub fn is_single(&self) -> bool { + matches!(self, LineStyle::Single | LineStyle::Dashed) + } +} + +impl FromStr for LineStyle { + type Err = String; + fn from_str(s: &str) -> Result { + match s.trim().to_lowercase().as_str() { + "single" | "light" | "normal" => Ok(LineStyle::Single), + "double" => Ok(LineStyle::Double), + "heavy" | "bold" | "thick" => Ok(LineStyle::Heavy), + "dashed" => Ok(LineStyle::Dashed), + "heavy_dashed" | "heavy-dashed" | "heavydashed" => Ok(LineStyle::HeavyDashed), + _ => Err(format!( + "Unknown line style: '{}', expected one of: single, double, heavy, dashed, heavy_dashed", + s + )), + } + } +} + +impl fmt::Display for LineStyle { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let name = match self { + LineStyle::Single => "single", + LineStyle::Double => "double", + LineStyle::Heavy => "heavy", + LineStyle::Dashed => "dashed", + LineStyle::HeavyDashed => "heavy_dashed", + }; + write!(f, "{}", name) + } +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Default, Serialize, Deserialize)] +pub struct BorderStyle { + pub top: LineStyle, + pub right: LineStyle, + pub bottom: LineStyle, + pub left: LineStyle, + pub rounded_corners: bool, +} + +impl BorderStyle { + pub fn with_rounded_corners(rounded_corners: bool) -> Self { + BorderStyle { + rounded_corners, + ..Default::default() + } + } + pub fn uniform_style(&self) -> Option { + if self.top == self.right && self.right == self.bottom && self.bottom == self.left { + Some(self.top) + } else { + None + } + } +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Default, Serialize, Deserialize)] +pub struct BorderStyleOverride { + pub all: Option, + pub top: Option, + pub right: Option, + pub bottom: Option, + pub left: Option, + pub rounded_corners: Option, +} + +impl BorderStyleOverride { + pub fn is_empty(&self) -> bool { + self.all.is_none() + && self.top.is_none() + && self.right.is_none() + && self.bottom.is_none() + && self.left.is_none() + && self.rounded_corners.is_none() + } + pub fn none_if_empty(self) -> Option { + if self.is_empty() { + None + } else { + Some(self) + } + } + pub fn from_cli_string(value: &str) -> Result { + let mut border_style_override = BorderStyleOverride::default(); + for token in value.split(',') { + let token = token.trim(); + if token.is_empty() { + continue; + } + let (key, value) = match token.split_once(|c| c == ':' || c == '=') { + Some((key, value)) => (key.trim().to_lowercase(), Some(value.trim())), + None => (token.to_lowercase(), None), + }; + match (key.as_str(), value) { + ("rounded" | "rounded_corners" | "rounded-corners", value) => { + let rounded = match value { + None => true, + Some(value) => value.parse::().map_err(|_| { + format!("Expected true or false for rounded corners, got '{}'", value) + })?, + }; + border_style_override.rounded_corners = Some(rounded); + }, + ("all", Some(value)) => border_style_override.all = Some(LineStyle::from_str(value)?), + ("top", Some(value)) => border_style_override.top = Some(LineStyle::from_str(value)?), + ("right", Some(value)) => { + border_style_override.right = Some(LineStyle::from_str(value)?) + }, + ("bottom", Some(value)) => { + border_style_override.bottom = Some(LineStyle::from_str(value)?) + }, + ("left", Some(value)) => { + border_style_override.left = Some(LineStyle::from_str(value)?) + }, + (_, Some(_)) => { + return Err(format!( + "Unknown border side: '{}', expected one of: all, top, right, bottom, left, rounded", + key + )) + }, + (line_style, None) => border_style_override.all = Some(LineStyle::from_str(line_style)?), + } + } + Ok(border_style_override) + } + pub fn from_optional_cli_string(value: Option<&str>) -> Result, String> { + match value { + Some(value) => Self::from_cli_string(value).map(|b| b.none_if_empty()), + None => Ok(None), + } + } + pub fn from_strings( + all: Option, + top: Option, + right: Option, + bottom: Option, + left: Option, + rounded_corners: Option, + ) -> Result { + let parse = |value: Option| -> Result, String> { + match value { + Some(value) => LineStyle::from_str(&value).map(Some), + None => Ok(None), + } + }; + Ok(BorderStyleOverride { + all: parse(all)?, + top: parse(top)?, + right: parse(right)?, + bottom: parse(bottom)?, + left: parse(left)?, + rounded_corners, + }) + } + pub fn apply_to(&self, base: BorderStyle) -> BorderStyle { + let all = self.all; + BorderStyle { + top: self.top.or(all).unwrap_or(base.top), + right: self.right.or(all).unwrap_or(base.right), + bottom: self.bottom.or(all).unwrap_or(base.bottom), + left: self.left.or(all).unwrap_or(base.left), + rounded_corners: self.rounded_corners.unwrap_or(base.rounded_corners), + } + } + pub fn merge(&self, other: &BorderStyleOverride) -> BorderStyleOverride { + BorderStyleOverride { + all: other.all.or(self.all), + top: other.top.or(self.top), + right: other.right.or(self.right), + bottom: other.bottom.or(self.bottom), + left: other.left.or(self.left), + rounded_corners: other.rounded_corners.or(self.rounded_corners), + } + } +} + #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Default, Serialize, Deserialize)] pub struct Style { pub colors: Styling, pub rounded_corners: bool, pub hide_session_name: bool, + pub border_style: BorderStyle, + pub floating_border_style: BorderStyle, } #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Deserialize, Serialize)] @@ -1827,6 +2026,14 @@ impl ModeInfo { pub fn update_rounded_corners(&mut self, rounded_corners: bool) { self.style.rounded_corners = rounded_corners; } + pub fn update_border_styles( + &mut self, + border_style: BorderStyle, + floating_border_style: BorderStyle, + ) { + self.style.border_style = border_style; + self.style.floating_border_style = floating_border_style; + } pub fn update_arrow_fonts(&mut self, should_support_arrow_fonts: bool) { // it is honestly quite baffling to me how "arrow_fonts: false" can mean "I support arrow // fonts", but since this is a public API... ¯\_(ツ)_/¯ @@ -2821,6 +3028,8 @@ pub struct FileToOpen { pub path: PathBuf, pub line_number: Option, pub cwd: Option, + #[serde(default)] + pub border_style: Option, } impl FileToOpen { @@ -2838,6 +3047,10 @@ impl FileToOpen { self.cwd = Some(cwd); self } + pub fn with_border_style(mut self, border_style: BorderStyleOverride) -> Self { + self.border_style = Some(border_style); + self + } } #[derive(Debug, Default, Clone)] @@ -2845,6 +3058,7 @@ pub struct CommandToRun { pub path: PathBuf, pub args: Vec, pub cwd: Option, + pub border_style: Option, } impl CommandToRun { @@ -2861,6 +3075,10 @@ impl CommandToRun { ..Default::default() } } + pub fn with_border_style(mut self, border_style: BorderStyleOverride) -> Self { + self.border_style = Some(border_style); + self + } } #[derive(Debug, Default, Clone)] @@ -3095,6 +3313,8 @@ pub struct FloatingPaneCoordinates { pub height: Option, pub pinned: Option, pub borderless: Option, + #[serde(default)] + pub border_style: Option, } impl FloatingPaneCoordinates { @@ -3146,9 +3366,27 @@ impl FloatingPaneCoordinates { height, pinned, borderless, + border_style: None, }) } } + pub fn with_border_style(mut self, border_style: Option) -> Self { + self.border_style = border_style; + self + } + pub fn merge_border_style( + coordinates: Option, + border_style: Option, + ) -> Option { + match border_style { + Some(border_style) => Some( + coordinates + .unwrap_or_default() + .with_border_style(Some(border_style)), + ), + None => coordinates, + } + } pub fn with_x_fixed(mut self, x: usize) -> Self { self.x = Some(PercentOrFixed::Fixed(x)); self @@ -3208,6 +3446,7 @@ impl From for FloatingPaneCoordinates { height: Some(PercentOrFixed::Fixed(pane_geom.rows.as_usize())), pinned: Some(pane_geom.is_pinned), borderless: None, + border_style: None, } } } @@ -3304,26 +3543,33 @@ impl FromStr for WebSharing { pub enum NewPanePlacement { NoPreference { borderless: Option, + border_style: Option, }, Tiled { direction: Option, borderless: Option, + border_style: Option, }, Floating(Option), InPlace { pane_id_to_replace: Option, close_replaced_pane: bool, borderless: Option, + border_style: Option, }, Stacked { pane_id_to_stack_under: Option, borderless: Option, + border_style: Option, }, } impl Default for NewPanePlacement { fn default() -> Self { - NewPanePlacement::NoPreference { borderless: None } + NewPanePlacement::NoPreference { + borderless: None, + border_style: None, + } } } @@ -3343,6 +3589,7 @@ impl NewPanePlacement { pane_id_to_replace: None, close_replaced_pane, borderless: None, + border_style: None, } } else { self @@ -3356,6 +3603,7 @@ impl NewPanePlacement { pane_id_to_replace, close_replaced_pane, borderless: None, + border_style: None, } } pub fn should_float(&self) -> Option { @@ -3390,13 +3638,67 @@ impl NewPanePlacement { } pub fn get_borderless(&self) -> Option { match self { - NewPanePlacement::NoPreference { borderless } => *borderless, + NewPanePlacement::NoPreference { borderless, .. } => *borderless, NewPanePlacement::Tiled { borderless, .. } => *borderless, NewPanePlacement::Floating(coords) => coords.as_ref().and_then(|c| c.borderless), NewPanePlacement::InPlace { borderless, .. } => *borderless, NewPanePlacement::Stacked { borderless, .. } => *borderless, } } + pub fn get_border_style(&self) -> Option { + match self { + NewPanePlacement::NoPreference { border_style, .. } => *border_style, + NewPanePlacement::Tiled { border_style, .. } => *border_style, + NewPanePlacement::Floating(coords) => { + coords.as_ref().and_then(|c| c.border_style.clone()) + }, + NewPanePlacement::InPlace { border_style, .. } => *border_style, + NewPanePlacement::Stacked { border_style, .. } => *border_style, + } + } + pub fn with_border_style(self, border_style: Option) -> Self { + if border_style.is_none() { + return self; + } + match self { + NewPanePlacement::NoPreference { borderless, .. } => NewPanePlacement::NoPreference { + borderless, + border_style, + }, + NewPanePlacement::Tiled { + direction, + borderless, + .. + } => NewPanePlacement::Tiled { + direction, + borderless, + border_style, + }, + NewPanePlacement::Floating(coords) => NewPanePlacement::Floating(Some( + coords.unwrap_or_default().with_border_style(border_style), + )), + NewPanePlacement::InPlace { + pane_id_to_replace, + close_replaced_pane, + borderless, + .. + } => NewPanePlacement::InPlace { + pane_id_to_replace, + close_replaced_pane, + borderless, + border_style, + }, + NewPanePlacement::Stacked { + pane_id_to_stack_under, + borderless, + .. + } => NewPanePlacement::Stacked { + pane_id_to_stack_under, + borderless, + border_style, + }, + } + } } type Context = BTreeMap; @@ -3593,6 +3895,7 @@ pub enum PluginCommand { ChangeFloatingPanesCoordinates(Vec<(PaneId, FloatingPaneCoordinates)>), TogglePaneBorderless(PaneId), SetPaneBorderless(PaneId, bool), + SetPaneBorderStyle(PaneId, BorderStyleOverride), OpenCommandPaneNearPlugin(CommandToRun, Context), OpenTerminalNearPlugin(FileToOpen), OpenTerminalFloatingNearPlugin(FileToOpen, Option), @@ -3759,3 +4062,185 @@ pub fn can_parse_unicode_bare_keys() { "Can parse a bare 'ъ' keypress" ); } + +#[test] +fn line_style_names_round_trip() { + for line_style in [ + LineStyle::Single, + LineStyle::Double, + LineStyle::Heavy, + LineStyle::Dashed, + LineStyle::HeavyDashed, + ] { + assert_eq!(LineStyle::from_str(&line_style.to_string()), Ok(line_style)); + } + assert!(LineStyle::from_str("squiggly").is_err()); +} + +#[test] +fn border_style_override_from_a_bare_cli_style() { + assert_eq!( + BorderStyleOverride::from_cli_string("double"), + Ok(BorderStyleOverride { + all: Some(LineStyle::Double), + ..Default::default() + }) + ); +} + +#[test] +fn border_style_override_from_a_compound_cli_string() { + assert_eq!( + BorderStyleOverride::from_cli_string("top:double, left=heavy ,rounded"), + Ok(BorderStyleOverride { + top: Some(LineStyle::Double), + left: Some(LineStyle::Heavy), + rounded_corners: Some(true), + ..Default::default() + }) + ); + assert_eq!( + BorderStyleOverride::from_cli_string("all:dashed,rounded:false"), + Ok(BorderStyleOverride { + all: Some(LineStyle::Dashed), + rounded_corners: Some(false), + ..Default::default() + }) + ); +} + +#[test] +fn border_style_override_rejects_unknown_cli_input() { + assert!(BorderStyleOverride::from_cli_string("diagonal:double").is_err()); + assert!(BorderStyleOverride::from_cli_string("top:squiggly").is_err()); + assert!(BorderStyleOverride::from_cli_string("rounded:maybe").is_err()); +} + +#[test] +fn empty_cli_border_style_is_treated_as_no_override() { + assert_eq!( + BorderStyleOverride::from_optional_cli_string(Some("")), + Ok(None) + ); + assert_eq!( + BorderStyleOverride::from_optional_cli_string(None), + Ok(None) + ); +} + +#[test] +fn border_style_override_applies_per_side_over_the_shorthand() { + let base = BorderStyle::with_rounded_corners(true); + let applied = BorderStyleOverride { + all: Some(LineStyle::Double), + top: Some(LineStyle::Heavy), + ..Default::default() + } + .apply_to(base); + assert_eq!( + applied, + BorderStyle { + top: LineStyle::Heavy, + right: LineStyle::Double, + bottom: LineStyle::Double, + left: LineStyle::Double, + rounded_corners: true, + } + ); +} + +#[test] +fn an_empty_border_style_override_leaves_the_base_untouched() { + let base = BorderStyle { + top: LineStyle::Double, + right: LineStyle::Heavy, + bottom: LineStyle::Dashed, + left: LineStyle::Single, + rounded_corners: true, + }; + assert!(BorderStyleOverride::default().is_empty()); + assert_eq!(BorderStyleOverride::default().apply_to(base), base); +} + +#[test] +fn border_style_overrides_merge_with_the_later_one_winning() { + let merged = BorderStyleOverride { + all: Some(LineStyle::Single), + top: Some(LineStyle::Single), + ..Default::default() + } + .merge(&BorderStyleOverride { + top: Some(LineStyle::Double), + rounded_corners: Some(true), + ..Default::default() + }); + assert_eq!( + merged, + BorderStyleOverride { + all: Some(LineStyle::Single), + top: Some(LineStyle::Double), + rounded_corners: Some(true), + ..Default::default() + } + ); +} + +#[test] +fn a_uniform_border_style_reports_its_line_style() { + assert_eq!( + BorderStyle { + top: LineStyle::Double, + right: LineStyle::Double, + bottom: LineStyle::Double, + left: LineStyle::Double, + rounded_corners: false, + } + .uniform_style(), + Some(LineStyle::Double) + ); + assert_eq!( + BorderStyle { + top: LineStyle::Double, + ..Default::default() + } + .uniform_style(), + None + ); +} + +#[test] +fn new_pane_placement_carries_a_border_style() { + let placement = NewPanePlacement::Tiled { + direction: None, + borderless: None, + border_style: None, + } + .with_border_style(Some(BorderStyleOverride { + all: Some(LineStyle::Heavy), + ..Default::default() + })); + assert_eq!( + placement.get_border_style(), + Some(BorderStyleOverride { + all: Some(LineStyle::Heavy), + ..Default::default() + }) + ); +} + +#[test] +fn a_floating_placement_carries_its_border_style_in_the_coordinates() { + let placement = NewPanePlacement::Floating(None).with_border_style(Some(BorderStyleOverride { + all: Some(LineStyle::Double), + ..Default::default() + })); + let coordinates = placement.floating_pane_coordinates().unwrap(); + assert_eq!( + coordinates.border_style, + Some(BorderStyleOverride { + all: Some(LineStyle::Double), + ..Default::default() + }) + ); + assert_eq!(placement.get_border_style(), coordinates.border_style); +} diff --git a/zellij-utils/src/errors.rs b/zellij-utils/src/errors.rs index 6ec905d33..9d631baf4 100644 --- a/zellij-utils/src/errors.rs +++ b/zellij-utils/src/errors.rs @@ -429,6 +429,7 @@ pub enum ScreenContext { ChangeFloatingPanesCoordinates, TogglePaneBorderless, SetPaneBorderless, + SetPaneBorderStyle, AddHighlightPaneFrameColorOverride, GroupAndUngroupPanes, HighlightAndUnhighlightPanes, diff --git a/zellij-utils/src/input/actions.rs b/zellij-utils/src/input/actions.rs index 388b996e8..f5e9a5c07 100644 --- a/zellij-utils/src/input/actions.rs +++ b/zellij-utils/src/input/actions.rs @@ -7,8 +7,8 @@ use super::layout::{ }; use crate::cli::CliAction; use crate::data::{ - CommandOrPlugin, Direction, KeyWithModifier, LayoutInfo, NewPanePlacement, OriginatingPlugin, - PaneId, Resize, UnblockCondition, + BorderStyleOverride, CommandOrPlugin, Direction, KeyWithModifier, LayoutInfo, NewPanePlacement, + OriginatingPlugin, PaneId, Resize, UnblockCondition, }; use crate::data::{FloatingPaneCoordinates, InputMode}; use crate::home::{find_default_config_dir, get_layout_dir}; @@ -320,6 +320,7 @@ pub enum Action { near_current_pane: bool, no_focus: bool, borderless: Option, + border_style: Option, tab_id: Option, }, /// Open a new pane in place of the focused one, suppressing it instead @@ -631,6 +632,10 @@ pub enum Action { pane_id: PaneId, borderless: bool, }, + SetPaneBorderStyle { + pane_id: PaneId, + border_style: BorderStyleOverride, + }, TogglePaneInGroup, ToggleGroupMarking, // Pane-targeting CLI-only variants @@ -1130,8 +1135,11 @@ impl Action { near_current_pane, no_focus, borderless, + border_style, tab_id, } => { + let border_style = + BorderStyleOverride::from_optional_cli_string(border_style.as_deref())?; let pane_id_to_replace = match pane_id { Some(pane_id_str) => match PaneId::from_str(&pane_id_str) { Ok(parsed_pane_id) => Some(parsed_pane_id), @@ -1187,24 +1195,28 @@ impl Action { }; let placement = if floating { - NewPanePlacement::Floating(FloatingPaneCoordinates::new( - x, y, width, height, pinned, borderless, + NewPanePlacement::Floating(FloatingPaneCoordinates::merge_border_style( + FloatingPaneCoordinates::new(x, y, width, height, pinned, borderless), + border_style, )) } else if in_place { NewPanePlacement::InPlace { pane_id_to_replace, close_replaced_pane, borderless, + border_style, } } else if stacked { NewPanePlacement::Stacked { pane_id_to_stack_under: None, borderless, + border_style, } } else { NewPanePlacement::Tiled { direction, borderless, + border_style, } }; @@ -1244,8 +1256,11 @@ impl Action { pane_name: name, skip_cache: skip_plugin_cache, cwd, - coordinates: FloatingPaneCoordinates::new( - x, y, width, height, pinned, borderless, + coordinates: FloatingPaneCoordinates::merge_border_style( + FloatingPaneCoordinates::new( + x, y, width, height, pinned, borderless, + ), + border_style, ), no_focus, tab_id, @@ -1295,8 +1310,11 @@ impl Action { Ok(vec![Action::NewFloatingPane { command: Some(run_command_action), pane_name: name, - coordinates: FloatingPaneCoordinates::new( - x, y, width, height, pinned, borderless, + coordinates: FloatingPaneCoordinates::merge_border_style( + FloatingPaneCoordinates::new( + x, y, width, height, pinned, borderless, + ), + border_style, ), near_current_pane, no_focus, @@ -1328,6 +1346,7 @@ impl Action { near_current_pane, no_focus, borderless, + border_style, tab_id, }]) } @@ -1336,8 +1355,11 @@ impl Action { Ok(vec![Action::NewFloatingPane { command: None, pane_name: name, - coordinates: FloatingPaneCoordinates::new( - x, y, width, height, pinned, borderless, + coordinates: FloatingPaneCoordinates::merge_border_style( + FloatingPaneCoordinates::new( + x, y, width, height, pinned, borderless, + ), + border_style, ), near_current_pane, no_focus, @@ -1369,6 +1391,7 @@ impl Action { near_current_pane, no_focus, borderless, + border_style, tab_id, }]) } @@ -1390,8 +1413,11 @@ impl Action { near_current_pane, no_focus, borderless, + border_style, tab_id, } => { + let border_style = + BorderStyleOverride::from_optional_cli_string(border_style.as_deref())?; let mut file = file; let current_dir = get_current_dir(); let cwd = cwd @@ -1410,8 +1436,9 @@ impl Action { in_place, close_replaced_pane, start_suppressed, - coordinates: FloatingPaneCoordinates::new( - x, y, width, height, pinned, borderless, + coordinates: FloatingPaneCoordinates::merge_border_style( + FloatingPaneCoordinates::new(x, y, width, height, pinned, borderless), + border_style, ), near_current_pane, no_focus, @@ -2077,10 +2104,14 @@ impl Action { height, pinned, borderless, + border_style, } => { - let Some(coordinates) = - FloatingPaneCoordinates::new(x, y, width, height, pinned, borderless) - else { + let border_style = + BorderStyleOverride::from_optional_cli_string(border_style.as_deref())?; + let Some(coordinates) = FloatingPaneCoordinates::merge_border_style( + FloatingPaneCoordinates::new(x, y, width, height, pinned, borderless), + border_style, + ) else { return Err(format!("Failed to parse floating pane coordinates")); }; let parsed_pane_id = PaneId::from_str(&pane_id); @@ -2099,6 +2130,29 @@ impl Action { } } }, + CliAction::SetPaneBorderStyle { + pane_id, + border_style, + } => { + let border_style = + BorderStyleOverride::from_optional_cli_string(border_style.as_deref())? + .unwrap_or_default(); + let parsed_pane_id = PaneId::from_str(&pane_id); + match parsed_pane_id { + Ok(parsed_pane_id) => { + Ok(vec![Action::SetPaneBorderStyle { + pane_id: parsed_pane_id, + border_style, + }]) + }, + Err(_e) => { + Err(format!( + "Malformed pane id: {}, expecting either a bare integer (eg. 1), a terminal pane id (eg. terminal_1) or a plugin pane id (eg. plugin_1)", + pane_id + )) + } + } + }, CliAction::TogglePaneBorderless { pane_id } => { let parsed_pane_id = PaneId::from_str(&pane_id); match parsed_pane_id { @@ -3698,6 +3752,194 @@ mod tests { assert!(result.is_err()); } + fn new_pane_cli_action(floating: bool, border_style: Option<&str>) -> CliAction { + CliAction::NewPane { + direction: None, + command: vec![], + plugin: None, + cwd: None, + floating, + in_place: false, + close_replaced_pane: false, + pane_id: None, + name: None, + close_on_exit: false, + start_suspended: false, + configuration: None, + skip_plugin_cache: false, + x: None, + y: None, + width: None, + height: None, + pinned: None, + stacked: false, + blocking: false, + block_until_exit_success: false, + block_until_exit_failure: false, + block_until_exit: false, + unblock_condition: None, + near_current_pane: false, + no_focus: false, + borderless: None, + tab_id: None, + border_style: border_style.map(|s| s.to_string()), + } + } + + #[test] + fn test_new_tiled_pane_with_border_style() { + let result = Action::actions_from_cli( + new_pane_cli_action(false, Some("top:double,rounded")), + Box::new(|| PathBuf::from("/tmp")), + None, + ); + let actions = result.unwrap(); + match &actions[0] { + Action::NewTiledPane { border_style, .. } => { + assert_eq!( + *border_style, + Some(BorderStyleOverride { + top: Some(crate::data::LineStyle::Double), + rounded_corners: Some(true), + ..Default::default() + }) + ); + }, + other => panic!("Expected NewTiledPane action, got {:?}", other), + } + } + + #[test] + fn test_new_floating_pane_with_border_style() { + let result = Action::actions_from_cli( + new_pane_cli_action(true, Some("heavy")), + Box::new(|| PathBuf::from("/tmp")), + None, + ); + let actions = result.unwrap(); + match &actions[0] { + Action::NewFloatingPane { coordinates, .. } => { + assert_eq!( + coordinates.as_ref().unwrap().border_style, + Some(BorderStyleOverride { + all: Some(crate::data::LineStyle::Heavy), + ..Default::default() + }) + ); + }, + other => panic!("Expected NewFloatingPane action, got {:?}", other), + } + } + + #[test] + fn test_new_pane_without_border_style() { + let result = Action::actions_from_cli( + new_pane_cli_action(false, None), + Box::new(|| PathBuf::from("/tmp")), + None, + ); + let actions = result.unwrap(); + match &actions[0] { + Action::NewTiledPane { border_style, .. } => assert_eq!(*border_style, None), + other => panic!("Expected NewTiledPane action, got {:?}", other), + } + } + + #[test] + fn test_new_pane_with_malformed_border_style_is_an_error() { + let result = Action::actions_from_cli( + new_pane_cli_action(false, Some("squiggly")), + Box::new(|| PathBuf::from("/tmp")), + None, + ); + assert!(result.is_err()); + } + + #[test] + fn test_set_pane_border_style() { + let cli_action = CliAction::SetPaneBorderStyle { + pane_id: "plugin_4".to_string(), + border_style: Some("bottom:heavy".to_string()), + }; + let result = Action::actions_from_cli(cli_action, Box::new(|| PathBuf::from("/tmp")), None); + let actions = result.unwrap(); + assert_eq!(actions.len(), 1); + match &actions[0] { + Action::SetPaneBorderStyle { + pane_id, + border_style, + } => { + assert_eq!(*pane_id, PaneId::Plugin(4)); + assert_eq!( + *border_style, + BorderStyleOverride { + bottom: Some(crate::data::LineStyle::Heavy), + ..Default::default() + } + ); + }, + other => panic!("Expected SetPaneBorderStyle action, got {:?}", other), + } + } + + #[test] + fn test_set_pane_border_style_without_a_style_clears_the_override() { + let cli_action = CliAction::SetPaneBorderStyle { + pane_id: "3".to_string(), + border_style: None, + }; + let actions = + Action::actions_from_cli(cli_action, Box::new(|| PathBuf::from("/tmp")), None).unwrap(); + match &actions[0] { + Action::SetPaneBorderStyle { border_style, .. } => { + assert!(border_style.is_empty()); + }, + other => panic!("Expected SetPaneBorderStyle action, got {:?}", other), + } + } + + #[test] + fn test_set_pane_border_style_with_a_malformed_pane_id_is_an_error() { + let cli_action = CliAction::SetPaneBorderStyle { + pane_id: "not_a_pane".to_string(), + border_style: None, + }; + assert!( + Action::actions_from_cli(cli_action, Box::new(|| PathBuf::from("/tmp")), None).is_err() + ); + } + + #[test] + fn test_change_floating_pane_coordinates_with_border_style() { + let cli_action = CliAction::ChangeFloatingPaneCoordinates { + pane_id: "terminal_2".to_string(), + x: Some("10".to_string()), + y: None, + width: None, + height: None, + pinned: None, + borderless: None, + border_style: Some("double".to_string()), + }; + let actions = + Action::actions_from_cli(cli_action, Box::new(|| PathBuf::from("/tmp")), None).unwrap(); + match &actions[0] { + Action::ChangeFloatingPaneCoordinates { coordinates, .. } => { + assert_eq!( + coordinates.border_style, + Some(BorderStyleOverride { + all: Some(crate::data::LineStyle::Double), + ..Default::default() + }) + ); + }, + other => panic!( + "Expected ChangeFloatingPaneCoordinates action, got {:?}", + other + ), + } + } + // Tab-targeting for pane creation commands #[test] @@ -3731,6 +3973,7 @@ mod tests { no_focus: false, borderless: None, tab_id: Some(3), + border_style: None, }; let result = Action::actions_from_cli(cli_action, Box::new(|| PathBuf::from("/tmp")), None); assert!(result.is_ok()); @@ -3775,6 +4018,7 @@ mod tests { no_focus: false, borderless: None, tab_id: None, + border_style: None, }; let result = Action::actions_from_cli(cli_action, Box::new(|| PathBuf::from("/tmp")), None); assert!(result.is_ok()); @@ -3819,6 +4063,7 @@ mod tests { no_focus: false, borderless: None, tab_id: None, + border_style: None, }; let result = Action::actions_from_cli(cli_action, Box::new(|| PathBuf::from("/tmp")), None); assert!(result.is_ok()); @@ -3865,6 +4110,7 @@ mod tests { no_focus: false, borderless: None, tab_id: None, + border_style: None, }; let result = Action::actions_from_cli(cli_action, Box::new(|| PathBuf::from("/tmp")), None); assert!(result.is_err()); @@ -3902,6 +4148,7 @@ mod tests { no_focus: false, borderless: None, tab_id: Some(5), + border_style: None, }; let result = Action::actions_from_cli(cli_action, Box::new(|| PathBuf::from("/tmp")), None); assert!(result.is_ok()); @@ -3946,6 +4193,7 @@ mod tests { no_focus: false, borderless: None, tab_id: Some(1), + border_style: None, }; let result = Action::actions_from_cli(cli_action, Box::new(|| PathBuf::from("/tmp")), None); assert!(result.is_ok()); @@ -3990,6 +4238,7 @@ mod tests { no_focus: false, borderless: None, tab_id: Some(2), + border_style: None, }; let result = Action::actions_from_cli(cli_action, Box::new(|| PathBuf::from("/tmp")), None); assert!(result.is_ok()); @@ -4022,6 +4271,7 @@ mod tests { no_focus: false, borderless: None, tab_id: Some(4), + border_style: None, }; let result = Action::actions_from_cli(cli_action, Box::new(|| PathBuf::from("/tmp")), None); assert!(result.is_ok()); @@ -4054,6 +4304,7 @@ mod tests { no_focus: false, borderless: None, tab_id: None, + border_style: None, }; let result = Action::actions_from_cli(cli_action, Box::new(|| PathBuf::from("/tmp")), None); assert!(result.is_ok()); @@ -4098,6 +4349,7 @@ mod tests { no_focus: false, borderless: None, tab_id: Some(2), + border_style: None, }; let result = Action::actions_from_cli(cli_action, Box::new(|| PathBuf::from("/tmp")), None); assert!(result.is_ok()); @@ -4142,6 +4394,7 @@ mod tests { no_focus: false, borderless: None, tab_id: Some(1), + border_style: None, }; let result = Action::actions_from_cli(cli_action, Box::new(|| PathBuf::from("/tmp")), None); assert!(result.is_ok()); diff --git a/zellij-utils/src/input/config.rs b/zellij-utils/src/input/config.rs index 8ad16db13..63468d24b 100644 --- a/zellij-utils/src/input/config.rs +++ b/zellij-utils/src/input/config.rs @@ -1409,6 +1409,7 @@ mod config_test { pane_frames: FrameConfig { rounded_corners: true, hide_session_name: true, + ..Default::default() }, }; assert_eq!(config.ui, expected_ui_config, "Ui config defined in config"); diff --git a/zellij-utils/src/input/layout.rs b/zellij-utils/src/input/layout.rs index 08287d42d..ea1680864 100644 --- a/zellij-utils/src/input/layout.rs +++ b/zellij-utils/src/input/layout.rs @@ -11,7 +11,10 @@ #[cfg(not(target_family = "wasm"))] use crate::downloader::Downloader; use crate::{ - data::{Direction, LayoutInfo, LayoutMetadata, LayoutParsingError, LayoutWithError}, + data::{ + BorderStyleOverride, Direction, LayoutInfo, LayoutMetadata, LayoutParsingError, + LayoutWithError, + }, home::{default_layout_dir, find_default_config_dir}, input::{ command::RunCommand, @@ -789,6 +792,8 @@ pub struct FloatingPaneLayout { pub y: Option, pub pinned: Option, pub borderless: Option, + #[serde(default)] + pub border_style: Option, pub run: Option, pub focus: Option, pub already_running: bool, @@ -808,6 +813,7 @@ impl FloatingPaneLayout { y: None, pinned: None, borderless: None, + border_style: None, run: None, focus: None, already_running: false, @@ -838,6 +844,7 @@ impl From<&TiledPaneLayout> for FloatingPaneLayout { name: pane_layout.name.clone(), run: pane_layout.run.clone(), focus: pane_layout.focus, + border_style: pane_layout.border_style, ..Default::default() } } @@ -851,6 +858,8 @@ pub struct TiledPaneLayout { pub split_size: Option, pub run: Option, pub borderless: Option, + #[serde(default)] + pub border_style: Option, pub focus: Option, pub external_children_index: Option, pub children_are_stacked: bool, diff --git a/zellij-utils/src/input/theme.rs b/zellij-utils/src/input/theme.rs index b4fbc630e..7f15283f3 100644 --- a/zellij-utils/src/input/theme.rs +++ b/zellij-utils/src/input/theme.rs @@ -7,7 +7,7 @@ use std::{ fmt, }; -use crate::data::Styling; +use crate::data::{BorderStyle, BorderStyleOverride, Styling}; #[derive(Debug, Default, Clone, Copy, PartialEq, Deserialize, Serialize)] pub struct UiConfig { @@ -26,6 +26,10 @@ impl UiConfig { pub struct FrameConfig { pub rounded_corners: bool, pub hide_session_name: bool, + #[serde(default)] + pub border_style: BorderStyleOverride, + #[serde(default)] + pub floating_border_style: BorderStyleOverride, } impl FrameConfig { @@ -33,8 +37,20 @@ impl FrameConfig { let mut merged = self.clone(); merged.rounded_corners = other.rounded_corners; merged.hide_session_name = other.hide_session_name; + merged.border_style = merged.border_style.merge(&other.border_style); + merged.floating_border_style = merged + .floating_border_style + .merge(&other.floating_border_style); merged } + pub fn resolved_border_style(&self) -> BorderStyle { + self.border_style + .apply_to(BorderStyle::with_rounded_corners(self.rounded_corners)) + } + pub fn resolved_floating_border_style(&self) -> BorderStyle { + self.floating_border_style + .apply_to(self.resolved_border_style()) + } } #[derive(Clone, PartialEq, Default, Serialize, Deserialize)] diff --git a/zellij-utils/src/input/unit/layout_test.rs b/zellij-utils/src/input/unit/layout_test.rs index 52b716133..59aee8340 100644 --- a/zellij-utils/src/input/unit/layout_test.rs +++ b/zellij-utils/src/input/unit/layout_test.rs @@ -46,6 +46,47 @@ fn layout_with_one_pane() { assert_eq!(layout, expected_layout); } +#[test] +fn layout_with_border_styles() { + use crate::data::{BorderStyleOverride, LineStyle}; + let kdl_layout = r#" + layout { + pane border_style="double" border_top="heavy" rounded_corners=true + floating_panes { + pane border_style="dashed" + } + } + "#; + let layout = Layout::from_kdl(kdl_layout, Some("layout_file_name".into()), None, None).unwrap(); + let (tiled, floating) = layout.template.clone().unwrap(); + assert_eq!( + tiled.children[0].border_style, + Some(BorderStyleOverride { + all: Some(LineStyle::Double), + top: Some(LineStyle::Heavy), + rounded_corners: Some(true), + ..Default::default() + }) + ); + assert_eq!( + floating[0].border_style, + Some(BorderStyleOverride { + all: Some(LineStyle::Dashed), + ..Default::default() + }) + ); +} + +#[test] +fn layout_with_invalid_border_style() { + let kdl_layout = r#" + layout { + pane border_style="squiggly" + } + "#; + assert!(Layout::from_kdl(kdl_layout, Some("layout_file_name".into()), None, None).is_err()); +} + #[test] fn layout_with_multiple_panes() { let kdl_layout = r#" diff --git a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__args_added_to_args_in_template.snap b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__args_added_to_args_in_template.snap index bb4010cb8..6b09abc25 100644 --- a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__args_added_to_args_in_template.snap +++ b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__args_added_to_args_in_template.snap @@ -1,7 +1,6 @@ --- source: zellij-utils/src/input/./unit/layout_test.rs -assertion_line: 1410 -expression: "format!(\"{:#?}\", layout)" +expression: "normalize_layout_debug(format!(\"{:#?}\", layout))" --- Layout { tabs: [], @@ -31,6 +30,7 @@ Layout { ), ), borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -64,6 +64,7 @@ Layout { ), ), borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -79,6 +80,7 @@ Layout { split_size: None, run: None, borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, diff --git a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__args_override_args_in_template.snap b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__args_override_args_in_template.snap index 9830de489..200430f5a 100644 --- a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__args_override_args_in_template.snap +++ b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__args_override_args_in_template.snap @@ -1,7 +1,6 @@ --- source: zellij-utils/src/input/./unit/layout_test.rs -assertion_line: 1375 -expression: "format!(\"{:#?}\", layout)" +expression: "normalize_layout_debug(format!(\"{:#?}\", layout))" --- Layout { tabs: [], @@ -34,6 +33,7 @@ Layout { ), ), borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -67,6 +67,7 @@ Layout { ), ), borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -82,6 +83,7 @@ Layout { split_size: None, run: None, borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, diff --git a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__can_define_a_stack_with_an_expanded_pane.snap b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__can_define_a_stack_with_an_expanded_pane.snap index 688f2b80e..0a472d9d5 100644 --- a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__can_define_a_stack_with_an_expanded_pane.snap +++ b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__can_define_a_stack_with_an_expanded_pane.snap @@ -1,7 +1,6 @@ --- source: zellij-utils/src/input/./unit/layout_test.rs -assertion_line: 1984 -expression: "format!(\"{:#?}\", layout)" +expression: "normalize_layout_debug(format!(\"{:#?}\", layout))" --- Layout { tabs: [], @@ -23,6 +22,7 @@ Layout { split_size: None, run: None, borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -41,6 +41,7 @@ Layout { split_size: None, run: None, borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -59,6 +60,7 @@ Layout { split_size: None, run: None, borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -74,6 +76,7 @@ Layout { split_size: None, run: None, borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: true, @@ -89,6 +92,7 @@ Layout { split_size: None, run: None, borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, diff --git a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__can_define_stacked_children_for_pane_node.snap b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__can_define_stacked_children_for_pane_node.snap index debfb03fd..3561152bb 100644 --- a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__can_define_stacked_children_for_pane_node.snap +++ b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__can_define_stacked_children_for_pane_node.snap @@ -1,7 +1,6 @@ --- source: zellij-utils/src/input/./unit/layout_test.rs -assertion_line: 1952 -expression: "format!(\"{:#?}\", layout)" +expression: "normalize_layout_debug(format!(\"{:#?}\", layout))" --- Layout { tabs: [], @@ -23,6 +22,7 @@ Layout { split_size: None, run: None, borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -41,6 +41,7 @@ Layout { split_size: None, run: None, borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -56,6 +57,7 @@ Layout { split_size: None, run: None, borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: true, @@ -71,6 +73,7 @@ Layout { split_size: None, run: None, borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, diff --git a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__can_define_stacked_children_for_pane_template.snap b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__can_define_stacked_children_for_pane_template.snap index 35c767a67..69cf97884 100644 --- a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__can_define_stacked_children_for_pane_template.snap +++ b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__can_define_stacked_children_for_pane_template.snap @@ -1,7 +1,6 @@ --- source: zellij-utils/src/input/./unit/layout_test.rs -assertion_line: 1969 -expression: "format!(\"{:#?}\", layout)" +expression: "normalize_layout_debug(format!(\"{:#?}\", layout))" --- Layout { tabs: [], @@ -27,6 +26,7 @@ Layout { split_size: None, run: None, borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -45,6 +45,7 @@ Layout { split_size: None, run: None, borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -60,6 +61,7 @@ Layout { split_size: None, run: None, borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: true, @@ -75,6 +77,7 @@ Layout { split_size: None, run: None, borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: true, @@ -90,6 +93,7 @@ Layout { split_size: None, run: None, borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, diff --git a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__can_load_swap_layouts_from_a_different_file.snap b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__can_load_swap_layouts_from_a_different_file.snap index a1fcdacce..cdbbdd8ac 100644 --- a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__can_load_swap_layouts_from_a_different_file.snap +++ b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__can_load_swap_layouts_from_a_different_file.snap @@ -1,7 +1,6 @@ --- source: zellij-utils/src/input/./unit/layout_test.rs -assertion_line: 1938 -expression: "format!(\"{:#?}\", layout)" +expression: "normalize_layout_debug(format!(\"{:#?}\", layout))" --- Layout { tabs: [], @@ -19,6 +18,7 @@ Layout { split_size: None, run: None, borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -34,6 +34,7 @@ Layout { split_size: None, run: None, borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -88,6 +89,7 @@ Layout { borderless: Some( true, ), + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -114,6 +116,7 @@ Layout { split_size: None, run: None, borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -132,6 +135,7 @@ Layout { split_size: None, run: None, borderless: None, + border_style: None, focus: None, external_children_index: Some( 0, @@ -149,6 +153,7 @@ Layout { split_size: None, run: None, borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -164,6 +169,7 @@ Layout { split_size: None, run: None, borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -205,6 +211,7 @@ Layout { borderless: Some( true, ), + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -220,6 +227,7 @@ Layout { split_size: None, run: None, borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -267,6 +275,7 @@ Layout { borderless: Some( true, ), + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -293,6 +302,7 @@ Layout { split_size: None, run: None, borderless: None, + border_style: None, focus: None, external_children_index: Some( 0, @@ -317,6 +327,7 @@ Layout { split_size: None, run: None, borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -335,6 +346,7 @@ Layout { split_size: None, run: None, borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -353,6 +365,7 @@ Layout { split_size: None, run: None, borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -371,6 +384,7 @@ Layout { split_size: None, run: None, borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -386,6 +400,7 @@ Layout { split_size: None, run: None, borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -401,6 +416,7 @@ Layout { split_size: None, run: None, borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -416,6 +432,7 @@ Layout { split_size: None, run: None, borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -457,6 +474,7 @@ Layout { borderless: Some( true, ), + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -472,6 +490,7 @@ Layout { split_size: None, run: None, borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -519,6 +538,7 @@ Layout { borderless: Some( true, ), + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -545,6 +565,7 @@ Layout { split_size: None, run: None, borderless: None, + border_style: None, focus: None, external_children_index: Some( 0, @@ -569,6 +590,7 @@ Layout { split_size: None, run: None, borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -587,6 +609,7 @@ Layout { split_size: None, run: None, borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -605,6 +628,7 @@ Layout { split_size: None, run: None, borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -623,6 +647,7 @@ Layout { split_size: None, run: None, borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -638,6 +663,7 @@ Layout { split_size: None, run: None, borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -660,6 +686,7 @@ Layout { split_size: None, run: None, borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -678,6 +705,7 @@ Layout { split_size: None, run: None, borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -696,6 +724,7 @@ Layout { split_size: None, run: None, borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -714,6 +743,7 @@ Layout { split_size: None, run: None, borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -729,6 +759,7 @@ Layout { split_size: None, run: None, borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -744,6 +775,7 @@ Layout { split_size: None, run: None, borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -759,6 +791,7 @@ Layout { split_size: None, run: None, borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -800,6 +833,7 @@ Layout { borderless: Some( true, ), + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -815,6 +849,7 @@ Layout { split_size: None, run: None, borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, diff --git a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__children_not_as_first_child_of_pane_template.snap b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__children_not_as_first_child_of_pane_template.snap index 56ba1f222..7d25cc1ca 100644 --- a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__children_not_as_first_child_of_pane_template.snap +++ b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__children_not_as_first_child_of_pane_template.snap @@ -1,7 +1,6 @@ --- source: zellij-utils/src/input/./unit/layout_test.rs -assertion_line: 1033 -expression: "format!(\"{:#?}\", layout)" +expression: "normalize_layout_debug(format!(\"{:#?}\", layout))" --- Layout { tabs: [], @@ -29,6 +28,7 @@ Layout { split_size: None, run: None, borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -51,6 +51,7 @@ Layout { split_size: None, run: None, borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -69,6 +70,7 @@ Layout { split_size: None, run: None, borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -84,6 +86,7 @@ Layout { split_size: None, run: None, borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -99,6 +102,7 @@ Layout { split_size: None, run: None, borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -117,6 +121,7 @@ Layout { split_size: None, run: None, borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -132,6 +137,7 @@ Layout { split_size: None, run: None, borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -158,6 +164,7 @@ Layout { split_size: None, run: None, borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -173,6 +180,7 @@ Layout { split_size: None, run: None, borderless: None, + border_style: None, focus: None, external_children_index: Some( 1, @@ -193,6 +201,7 @@ Layout { split_size: None, run: None, borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -208,6 +217,7 @@ Layout { split_size: None, run: None, borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -223,6 +233,7 @@ Layout { split_size: None, run: None, borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, diff --git a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__children_not_as_first_child_of_tab_template.snap b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__children_not_as_first_child_of_tab_template.snap index 3ae6e4a9c..997acdfaa 100644 --- a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__children_not_as_first_child_of_tab_template.snap +++ b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__children_not_as_first_child_of_tab_template.snap @@ -1,7 +1,6 @@ --- source: zellij-utils/src/input/./unit/layout_test.rs -assertion_line: 985 -expression: "format!(\"{:#?}\", layout)" +expression: "normalize_layout_debug(format!(\"{:#?}\", layout))" --- Layout { tabs: [ @@ -24,6 +23,7 @@ Layout { split_size: None, run: None, borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -46,6 +46,7 @@ Layout { split_size: None, run: None, borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -64,6 +65,7 @@ Layout { split_size: None, run: None, borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -79,6 +81,7 @@ Layout { split_size: None, run: None, borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -94,6 +97,7 @@ Layout { split_size: None, run: None, borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -112,6 +116,7 @@ Layout { split_size: None, run: None, borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -127,6 +132,7 @@ Layout { split_size: None, run: None, borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -157,6 +163,7 @@ Layout { split_size: None, run: None, borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -172,6 +179,7 @@ Layout { split_size: None, run: None, borderless: None, + border_style: None, focus: None, external_children_index: Some( 1, @@ -192,6 +200,7 @@ Layout { split_size: None, run: None, borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -207,6 +216,7 @@ Layout { split_size: None, run: None, borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -231,6 +241,7 @@ Layout { split_size: None, run: None, borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, diff --git a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__close_on_exit_added_to_close_on_exit_in_template.snap b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__close_on_exit_added_to_close_on_exit_in_template.snap index 8ba07ad39..59835811a 100644 --- a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__close_on_exit_added_to_close_on_exit_in_template.snap +++ b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__close_on_exit_added_to_close_on_exit_in_template.snap @@ -1,7 +1,6 @@ --- source: zellij-utils/src/input/./unit/layout_test.rs -assertion_line: 1427 -expression: "format!(\"{:#?}\", layout)" +expression: "normalize_layout_debug(format!(\"{:#?}\", layout))" --- Layout { tabs: [], @@ -31,6 +30,7 @@ Layout { ), ), borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -61,6 +61,7 @@ Layout { ), ), borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -76,6 +77,7 @@ Layout { split_size: None, run: None, borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, diff --git a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__close_on_exit_overrides_close_on_exit_in_template.snap b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__close_on_exit_overrides_close_on_exit_in_template.snap index 032bd843b..59835811a 100644 --- a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__close_on_exit_overrides_close_on_exit_in_template.snap +++ b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__close_on_exit_overrides_close_on_exit_in_template.snap @@ -1,7 +1,6 @@ --- source: zellij-utils/src/input/./unit/layout_test.rs -assertion_line: 1393 -expression: "format!(\"{:#?}\", layout)" +expression: "normalize_layout_debug(format!(\"{:#?}\", layout))" --- Layout { tabs: [], @@ -31,6 +30,7 @@ Layout { ), ), borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -61,6 +61,7 @@ Layout { ), ), borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -76,6 +77,7 @@ Layout { split_size: None, run: None, borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, diff --git a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__combined_tab_and_pane_template_both_with_children.snap b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__combined_tab_and_pane_template_both_with_children.snap index 6e99b11c9..5b70f3bc1 100644 --- a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__combined_tab_and_pane_template_both_with_children.snap +++ b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__combined_tab_and_pane_template_both_with_children.snap @@ -1,7 +1,6 @@ --- source: zellij-utils/src/input/./unit/layout_test.rs -assertion_line: 1085 -expression: "format!(\"{:#?}\", layout)" +expression: "normalize_layout_debug(format!(\"{:#?}\", layout))" --- Layout { tabs: [ @@ -24,6 +23,7 @@ Layout { split_size: None, run: None, borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -48,6 +48,7 @@ Layout { split_size: None, run: None, borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -63,6 +64,7 @@ Layout { split_size: None, run: None, borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -81,6 +83,7 @@ Layout { split_size: None, run: None, borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -96,6 +99,7 @@ Layout { split_size: None, run: None, borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -118,6 +122,7 @@ Layout { split_size: None, run: None, borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -136,6 +141,7 @@ Layout { split_size: None, run: None, borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -151,6 +157,7 @@ Layout { split_size: None, run: None, borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -166,6 +173,7 @@ Layout { split_size: None, run: None, borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -196,6 +204,7 @@ Layout { split_size: None, run: None, borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -220,6 +229,7 @@ Layout { split_size: None, run: None, borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -235,6 +245,7 @@ Layout { split_size: None, run: None, borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -253,6 +264,7 @@ Layout { split_size: None, run: None, borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -268,6 +280,7 @@ Layout { split_size: None, run: None, borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -286,6 +299,7 @@ Layout { split_size: None, run: None, borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -301,6 +315,7 @@ Layout { split_size: None, run: None, borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -325,6 +340,7 @@ Layout { split_size: None, run: None, borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, diff --git a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__cwd_added_to_cwd_in_template.snap b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__cwd_added_to_cwd_in_template.snap index c2b277c1f..99328a34b 100644 --- a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__cwd_added_to_cwd_in_template.snap +++ b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__cwd_added_to_cwd_in_template.snap @@ -1,7 +1,6 @@ --- source: zellij-utils/src/input/./unit/layout_test.rs -assertion_line: 1462 -expression: "format!(\"{:#?}\", layout)" +expression: "normalize_layout_debug(format!(\"{:#?}\", layout))" --- Layout { tabs: [], @@ -31,6 +30,7 @@ Layout { ), ), borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -63,6 +63,7 @@ Layout { ), ), borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -78,6 +79,7 @@ Layout { split_size: None, run: None, borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, diff --git a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__cwd_override_cwd_in_template.snap b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__cwd_override_cwd_in_template.snap index 303764157..3ae8644b5 100644 --- a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__cwd_override_cwd_in_template.snap +++ b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__cwd_override_cwd_in_template.snap @@ -1,7 +1,6 @@ --- source: zellij-utils/src/input/./unit/layout_test.rs -assertion_line: 1445 -expression: "format!(\"{:#?}\", layout)" +expression: "normalize_layout_debug(format!(\"{:#?}\", layout))" --- Layout { tabs: [], @@ -33,6 +32,7 @@ Layout { ), ), borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -65,6 +65,7 @@ Layout { ), ), borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -80,6 +81,7 @@ Layout { split_size: None, run: None, borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, diff --git a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__env_var_expansion.snap b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__env_var_expansion.snap index 30b301da4..6d1a76c2a 100644 --- a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__env_var_expansion.snap +++ b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__env_var_expansion.snap @@ -1,7 +1,6 @@ --- source: zellij-utils/src/input/./unit/layout_test.rs -assertion_line: 2230 -expression: "format!(\"{layout:#?}\")" +expression: "normalize_layout_debug(format!(\"{layout:#?}\"))" --- Layout { tabs: [ @@ -22,6 +21,7 @@ Layout { ), ), borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -44,6 +44,7 @@ Layout { ), ), borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -66,6 +67,7 @@ Layout { ), ), borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -88,6 +90,7 @@ Layout { ), ), borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -120,6 +123,7 @@ Layout { ), ), borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -146,6 +150,7 @@ Layout { ), ), borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -172,6 +177,7 @@ Layout { ), ), borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -204,6 +210,7 @@ Layout { ), ), borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -230,6 +237,7 @@ Layout { ), ), borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -249,6 +257,7 @@ Layout { ), ), borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -281,6 +290,7 @@ Layout { ), ), borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -303,6 +313,7 @@ Layout { ), ), borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -325,6 +336,7 @@ Layout { ), ), borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -347,6 +359,7 @@ Layout { ), ), borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -379,6 +392,7 @@ Layout { ), ), borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -405,6 +419,7 @@ Layout { ), ), borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -431,6 +446,7 @@ Layout { ), ), borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -463,6 +479,7 @@ Layout { ), ), borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -489,6 +506,7 @@ Layout { ), ), borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -508,6 +526,7 @@ Layout { ), ), borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, diff --git a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__global_cwd_and_tab_cwd_prepended_to_panes_with_and_without_cwd.snap b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__global_cwd_and_tab_cwd_prepended_to_panes_with_and_without_cwd.snap index 4fa28d02a..fa1aadf30 100644 --- a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__global_cwd_and_tab_cwd_prepended_to_panes_with_and_without_cwd.snap +++ b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__global_cwd_and_tab_cwd_prepended_to_panes_with_and_without_cwd.snap @@ -1,7 +1,6 @@ --- source: zellij-utils/src/input/./unit/layout_test.rs -assertion_line: 1847 -expression: "format!(\"{:#?}\", layout)" +expression: "normalize_layout_debug(format!(\"{:#?}\", layout))" --- Layout { tabs: [ @@ -22,6 +21,7 @@ Layout { ), ), borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -54,6 +54,7 @@ Layout { ), ), borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -73,6 +74,7 @@ Layout { ), ), borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -97,6 +99,7 @@ Layout { split_size: None, run: None, borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, diff --git a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__global_cwd_and_tab_cwd_prepended_to_panes_with_and_without_cwd_in_pane_templates.snap b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__global_cwd_and_tab_cwd_prepended_to_panes_with_and_without_cwd_in_pane_templates.snap index 36b254dad..d9473fe0f 100644 --- a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__global_cwd_and_tab_cwd_prepended_to_panes_with_and_without_cwd_in_pane_templates.snap +++ b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__global_cwd_and_tab_cwd_prepended_to_panes_with_and_without_cwd_in_pane_templates.snap @@ -1,7 +1,6 @@ --- source: zellij-utils/src/input/./unit/layout_test.rs -assertion_line: 1868 -expression: "format!(\"{:#?}\", layout)" +expression: "normalize_layout_debug(format!(\"{:#?}\", layout))" --- Layout { tabs: [ @@ -26,6 +25,7 @@ Layout { ), ), borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -58,6 +58,7 @@ Layout { ), ), borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -84,6 +85,7 @@ Layout { ), ), borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -103,6 +105,7 @@ Layout { ), ), borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -122,6 +125,7 @@ Layout { ), ), borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -141,6 +145,7 @@ Layout { ), ), borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -165,6 +170,7 @@ Layout { split_size: None, run: None, borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, diff --git a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__global_cwd_and_tab_cwd_prepended_to_panes_with_and_without_cwd_in_tab_templates.snap b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__global_cwd_and_tab_cwd_prepended_to_panes_with_and_without_cwd_in_tab_templates.snap index 248d46827..0bacbd09b 100644 --- a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__global_cwd_and_tab_cwd_prepended_to_panes_with_and_without_cwd_in_tab_templates.snap +++ b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__global_cwd_and_tab_cwd_prepended_to_panes_with_and_without_cwd_in_tab_templates.snap @@ -1,7 +1,6 @@ --- source: zellij-utils/src/input/./unit/layout_test.rs -assertion_line: 1887 -expression: "format!(\"{:#?}\", layout)" +expression: "normalize_layout_debug(format!(\"{:#?}\", layout))" --- Layout { tabs: [ @@ -22,6 +21,7 @@ Layout { ), ), borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -54,6 +54,7 @@ Layout { ), ), borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -80,6 +81,7 @@ Layout { ), ), borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -99,6 +101,7 @@ Layout { ), ), borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -118,6 +121,7 @@ Layout { ), ), borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -142,6 +146,7 @@ Layout { split_size: None, run: None, borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, diff --git a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__global_cwd_given_to_panes_without_cwd.snap b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__global_cwd_given_to_panes_without_cwd.snap index b6171a495..416906e9d 100644 --- a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__global_cwd_given_to_panes_without_cwd.snap +++ b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__global_cwd_given_to_panes_without_cwd.snap @@ -1,7 +1,6 @@ --- source: zellij-utils/src/input/./unit/layout_test.rs -assertion_line: 1732 -expression: "format!(\"{:#?}\", layout)" +expression: "normalize_layout_debug(format!(\"{:#?}\", layout))" --- Layout { tabs: [ @@ -22,6 +21,7 @@ Layout { ), ), borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -54,6 +54,7 @@ Layout { ), ), borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -73,6 +74,7 @@ Layout { ), ), borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -105,6 +107,7 @@ Layout { ), ), borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -137,6 +140,7 @@ Layout { ), ), borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -156,6 +160,7 @@ Layout { ), ), borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, diff --git a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__global_cwd_passed_from_layout_constructor.snap b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__global_cwd_passed_from_layout_constructor.snap index aaca18c62..303cf5755 100644 --- a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__global_cwd_passed_from_layout_constructor.snap +++ b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__global_cwd_passed_from_layout_constructor.snap @@ -1,7 +1,6 @@ --- source: zellij-utils/src/input/./unit/layout_test.rs -assertion_line: 1765 -expression: "format!(\"{:#?}\", layout)" +expression: "normalize_layout_debug(format!(\"{:#?}\", layout))" --- Layout { tabs: [], @@ -23,6 +22,7 @@ Layout { ), ), borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -55,6 +55,7 @@ Layout { ), ), borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -74,6 +75,7 @@ Layout { ), ), borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, diff --git a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__global_cwd_passed_from_layout_constructor_overrides_global_cwd_in_layout_file.snap b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__global_cwd_passed_from_layout_constructor_overrides_global_cwd_in_layout_file.snap index d5067f75a..c7019b2b2 100644 --- a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__global_cwd_passed_from_layout_constructor_overrides_global_cwd_in_layout_file.snap +++ b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__global_cwd_passed_from_layout_constructor_overrides_global_cwd_in_layout_file.snap @@ -1,7 +1,6 @@ --- source: zellij-utils/src/input/./unit/layout_test.rs -assertion_line: 1786 -expression: "format!(\"{:#?}\", layout)" +expression: "normalize_layout_debug(format!(\"{:#?}\", layout))" --- Layout { tabs: [ @@ -22,6 +21,7 @@ Layout { ), ), borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -54,6 +54,7 @@ Layout { ), ), borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -73,6 +74,7 @@ Layout { ), ), borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -105,6 +107,7 @@ Layout { ), ), borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -137,6 +140,7 @@ Layout { ), ), borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -156,6 +160,7 @@ Layout { ), ), borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, diff --git a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__global_cwd_prepended_to_panes_with_cwd.snap b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__global_cwd_prepended_to_panes_with_cwd.snap index 1666c83bf..9c46d0b2f 100644 --- a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__global_cwd_prepended_to_panes_with_cwd.snap +++ b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__global_cwd_prepended_to_panes_with_cwd.snap @@ -1,7 +1,6 @@ --- source: zellij-utils/src/input/./unit/layout_test.rs -assertion_line: 1745 -expression: "format!(\"{:#?}\", layout)" +expression: "normalize_layout_debug(format!(\"{:#?}\", layout))" --- Layout { tabs: [ @@ -22,6 +21,7 @@ Layout { ), ), borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -54,6 +54,7 @@ Layout { ), ), borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -73,6 +74,7 @@ Layout { ), ), borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -105,6 +107,7 @@ Layout { ), ), borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -137,6 +140,7 @@ Layout { ), ), borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -156,6 +160,7 @@ Layout { ), ), borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, diff --git a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__global_cwd_with_tab_cwd_given_to_panes_without_cwd.snap b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__global_cwd_with_tab_cwd_given_to_panes_without_cwd.snap index e745a99b0..be87fd3a5 100644 --- a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__global_cwd_with_tab_cwd_given_to_panes_without_cwd.snap +++ b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__global_cwd_with_tab_cwd_given_to_panes_without_cwd.snap @@ -1,7 +1,6 @@ --- source: zellij-utils/src/input/./unit/layout_test.rs -assertion_line: 1802 -expression: "format!(\"{:#?}\", layout)" +expression: "normalize_layout_debug(format!(\"{:#?}\", layout))" --- Layout { tabs: [ @@ -22,6 +21,7 @@ Layout { ), ), borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -54,6 +54,7 @@ Layout { ), ), borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -73,6 +74,7 @@ Layout { ), ), borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -97,6 +99,7 @@ Layout { split_size: None, run: None, borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, diff --git a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__layout_with_command_panes_and_close_on_exit.snap b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__layout_with_command_panes_and_close_on_exit.snap index baf0eca48..a48e1eeb6 100644 --- a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__layout_with_command_panes_and_close_on_exit.snap +++ b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__layout_with_command_panes_and_close_on_exit.snap @@ -1,7 +1,6 @@ --- source: zellij-utils/src/input/./unit/layout_test.rs -assertion_line: 511 -expression: "format!(\"{:#?}\", layout)" +expression: "normalize_layout_debug(format!(\"{:#?}\", layout))" --- Layout { tabs: [], @@ -31,6 +30,7 @@ Layout { ), ), borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -46,6 +46,7 @@ Layout { split_size: None, run: None, borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, diff --git a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__layout_with_command_panes_and_start_suspended.snap b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__layout_with_command_panes_and_start_suspended.snap index 481a74d89..cc67b70b9 100644 --- a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__layout_with_command_panes_and_start_suspended.snap +++ b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__layout_with_command_panes_and_start_suspended.snap @@ -1,7 +1,6 @@ --- source: zellij-utils/src/input/./unit/layout_test.rs -assertion_line: 524 -expression: "format!(\"{:#?}\", layout)" +expression: "normalize_layout_debug(format!(\"{:#?}\", layout))" --- Layout { tabs: [], @@ -31,6 +30,7 @@ Layout { ), ), borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -46,6 +46,7 @@ Layout { split_size: None, run: None, borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, diff --git a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__layout_with_default_tab_template.snap b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__layout_with_default_tab_template.snap index abddb5c9d..60562ae86 100644 --- a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__layout_with_default_tab_template.snap +++ b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__layout_with_default_tab_template.snap @@ -1,7 +1,6 @@ --- source: zellij-utils/src/input/./unit/layout_test.rs -assertion_line: 819 -expression: "format!(\"{:#?}\", layout)" +expression: "normalize_layout_debug(format!(\"{:#?}\", layout))" --- Layout { tabs: [ @@ -20,6 +19,7 @@ Layout { split_size: None, run: None, borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -42,6 +42,7 @@ Layout { split_size: None, run: None, borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -60,6 +61,7 @@ Layout { split_size: None, run: None, borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -75,6 +77,7 @@ Layout { split_size: None, run: None, borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -93,6 +96,7 @@ Layout { split_size: None, run: None, borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -108,6 +112,7 @@ Layout { split_size: None, run: None, borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -136,6 +141,7 @@ Layout { split_size: None, run: None, borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -158,6 +164,7 @@ Layout { split_size: None, run: None, borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -176,6 +183,7 @@ Layout { split_size: None, run: None, borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -191,6 +199,7 @@ Layout { split_size: None, run: None, borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -209,6 +218,7 @@ Layout { split_size: None, run: None, borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -224,6 +234,7 @@ Layout { split_size: None, run: None, borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -250,6 +261,7 @@ Layout { split_size: None, run: None, borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -268,6 +280,7 @@ Layout { split_size: None, run: None, borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -286,6 +299,7 @@ Layout { split_size: None, run: None, borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -301,6 +315,7 @@ Layout { split_size: None, run: None, borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -329,6 +344,7 @@ Layout { split_size: None, run: None, borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -347,6 +363,7 @@ Layout { split_size: None, run: None, borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -365,6 +382,7 @@ Layout { split_size: None, run: None, borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -380,6 +398,7 @@ Layout { split_size: None, run: None, borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, diff --git a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__layout_with_nested_branched_pane_templates.snap b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__layout_with_nested_branched_pane_templates.snap index 3d267afa0..bc97cb533 100644 --- a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__layout_with_nested_branched_pane_templates.snap +++ b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__layout_with_nested_branched_pane_templates.snap @@ -1,7 +1,6 @@ --- source: zellij-utils/src/input/./unit/layout_test.rs -assertion_line: 939 -expression: "format!(\"{:#?}\", layout)" +expression: "normalize_layout_debug(format!(\"{:#?}\", layout))" --- Layout { tabs: [], @@ -23,6 +22,7 @@ Layout { split_size: None, run: None, borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -41,6 +41,7 @@ Layout { split_size: None, run: None, borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -63,6 +64,7 @@ Layout { split_size: None, run: None, borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -81,6 +83,7 @@ Layout { split_size: None, run: None, borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -96,6 +99,7 @@ Layout { split_size: None, run: None, borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -118,6 +122,7 @@ Layout { split_size: None, run: None, borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -136,6 +141,7 @@ Layout { split_size: None, run: None, borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -154,6 +160,7 @@ Layout { split_size: None, run: None, borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -169,6 +176,7 @@ Layout { split_size: None, run: None, borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -184,6 +192,7 @@ Layout { split_size: None, run: None, borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -199,6 +208,7 @@ Layout { split_size: None, run: None, borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, diff --git a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__layout_with_nested_pane_templates.snap b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__layout_with_nested_pane_templates.snap index 6f24f2eeb..37cf739cc 100644 --- a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__layout_with_nested_pane_templates.snap +++ b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__layout_with_nested_pane_templates.snap @@ -1,7 +1,6 @@ --- source: zellij-utils/src/input/./unit/layout_test.rs -assertion_line: 913 -expression: "format!(\"{:#?}\", layout)" +expression: "normalize_layout_debug(format!(\"{:#?}\", layout))" --- Layout { tabs: [], @@ -23,6 +22,7 @@ Layout { split_size: None, run: None, borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -41,6 +41,7 @@ Layout { split_size: None, run: None, borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -63,6 +64,7 @@ Layout { split_size: None, run: None, borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -81,6 +83,7 @@ Layout { split_size: None, run: None, borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -96,6 +99,7 @@ Layout { split_size: None, run: None, borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -114,6 +118,7 @@ Layout { split_size: None, run: None, borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -129,6 +134,7 @@ Layout { split_size: None, run: None, borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -144,6 +150,7 @@ Layout { split_size: None, run: None, borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, diff --git a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__layout_with_new_tab_template.snap b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__layout_with_new_tab_template.snap index 3eb32a534..25c04680c 100644 --- a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__layout_with_new_tab_template.snap +++ b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__layout_with_new_tab_template.snap @@ -1,7 +1,6 @@ --- source: zellij-utils/src/input/./unit/layout_test.rs -assertion_line: 844 -expression: "format!(\"{:#?}\", layout)" +expression: "normalize_layout_debug(format!(\"{:#?}\", layout))" --- Layout { tabs: [ @@ -20,6 +19,7 @@ Layout { split_size: None, run: None, borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -38,6 +38,7 @@ Layout { split_size: None, run: None, borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -53,6 +54,7 @@ Layout { split_size: None, run: None, borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -81,6 +83,7 @@ Layout { split_size: None, run: None, borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -99,6 +102,7 @@ Layout { split_size: None, run: None, borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -114,6 +118,7 @@ Layout { split_size: None, run: None, borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -136,6 +141,7 @@ Layout { split_size: None, run: None, borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -168,6 +174,7 @@ Layout { split_size: None, run: None, borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -186,6 +193,7 @@ Layout { split_size: None, run: None, borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -201,6 +209,7 @@ Layout { split_size: None, run: None, borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -216,6 +225,7 @@ Layout { split_size: None, run: None, borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, diff --git a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__layout_with_pane_excluded_from_sync.snap b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__layout_with_pane_excluded_from_sync.snap index a14262ee9..8bca4df02 100644 --- a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__layout_with_pane_excluded_from_sync.snap +++ b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__layout_with_pane_excluded_from_sync.snap @@ -1,7 +1,6 @@ --- source: zellij-utils/src/input/./unit/layout_test.rs -assertion_line: 1096 -expression: "format!(\"{:#?}\", layout)" +expression: "normalize_layout_debug(format!(\"{:#?}\", layout))" --- Layout { tabs: [], @@ -19,6 +18,7 @@ Layout { split_size: None, run: None, borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -36,6 +36,7 @@ Layout { split_size: None, run: None, borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, diff --git a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__layout_with_pane_templates.snap b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__layout_with_pane_templates.snap index 43965e114..ed876b075 100644 --- a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__layout_with_pane_templates.snap +++ b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__layout_with_pane_templates.snap @@ -1,7 +1,6 @@ --- source: zellij-utils/src/input/./unit/layout_test.rs -assertion_line: 871 -expression: "format!(\"{:#?}\", layout)" +expression: "normalize_layout_debug(format!(\"{:#?}\", layout))" --- Layout { tabs: [], @@ -23,6 +22,7 @@ Layout { split_size: None, run: None, borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -45,6 +45,7 @@ Layout { split_size: None, run: None, borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -60,6 +61,7 @@ Layout { split_size: None, run: None, borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -78,6 +80,7 @@ Layout { split_size: None, run: None, borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -93,6 +96,7 @@ Layout { split_size: None, run: None, borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -115,6 +119,7 @@ Layout { split_size: None, run: None, borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -137,6 +142,7 @@ Layout { split_size: None, run: None, borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -155,6 +161,7 @@ Layout { split_size: None, run: None, borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -170,6 +177,7 @@ Layout { split_size: None, run: None, borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -188,6 +196,7 @@ Layout { split_size: None, run: None, borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -203,6 +212,7 @@ Layout { split_size: None, run: None, borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -225,6 +235,7 @@ Layout { split_size: None, run: None, borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -247,6 +258,7 @@ Layout { split_size: None, run: None, borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -265,6 +277,7 @@ Layout { split_size: None, run: None, borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -280,6 +293,7 @@ Layout { split_size: None, run: None, borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -298,6 +312,7 @@ Layout { split_size: None, run: None, borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -313,6 +328,7 @@ Layout { split_size: None, run: None, borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -335,6 +351,7 @@ Layout { split_size: None, run: None, borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -353,6 +370,7 @@ Layout { split_size: None, run: None, borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -371,6 +389,7 @@ Layout { split_size: None, run: None, borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -386,6 +405,7 @@ Layout { split_size: None, run: None, borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -401,6 +421,7 @@ Layout { split_size: None, run: None, borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, diff --git a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__layout_with_tab_and_pane_templates.snap b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__layout_with_tab_and_pane_templates.snap index edc76b2ee..9a8db02ef 100644 --- a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__layout_with_tab_and_pane_templates.snap +++ b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__layout_with_tab_and_pane_templates.snap @@ -1,7 +1,6 @@ --- source: zellij-utils/src/input/./unit/layout_test.rs -assertion_line: 892 -expression: "format!(\"{:#?}\", layout)" +expression: "normalize_layout_debug(format!(\"{:#?}\", layout))" --- Layout { tabs: [ @@ -22,6 +21,7 @@ Layout { split_size: None, run: None, borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -56,6 +56,7 @@ Layout { ), ), borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -71,6 +72,7 @@ Layout { split_size: None, run: None, borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -89,6 +91,7 @@ Layout { split_size: None, run: None, borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -104,6 +107,7 @@ Layout { split_size: None, run: None, borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -119,6 +123,7 @@ Layout { split_size: None, run: None, borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -143,6 +148,7 @@ Layout { split_size: None, run: None, borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, 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 9e7bf8af8..134117b37 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,7 +1,6 @@ --- source: zellij-utils/src/input/./unit/layout_test.rs -assertion_line: 305 -expression: "format!(\"{:#?}\", layout)" +expression: "normalize_layout_debug(format!(\"{:#?}\", layout))" --- Layout { tabs: [ @@ -18,6 +17,7 @@ Layout { split_size: None, run: None, borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -33,6 +33,7 @@ Layout { split_size: None, run: None, borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -53,6 +54,7 @@ Layout { y: None, pinned: None, borderless: None, + border_style: None, run: None, focus: None, already_running: false, @@ -76,6 +78,7 @@ Layout { split_size: None, run: None, borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -91,6 +94,7 @@ Layout { split_size: None, run: None, borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -111,6 +115,7 @@ Layout { y: None, pinned: None, borderless: None, + border_style: None, run: None, focus: None, already_running: false, @@ -127,6 +132,7 @@ Layout { y: None, pinned: None, borderless: None, + border_style: None, run: None, focus: None, already_running: false, @@ -148,6 +154,7 @@ Layout { split_size: None, run: None, borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, diff --git a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__pane_template_command_with_cwd_is_overriden_by_its_consumers_bare_cwd.snap b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__pane_template_command_with_cwd_is_overriden_by_its_consumers_bare_cwd.snap index f6ffefa06..e346e266e 100644 --- a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__pane_template_command_with_cwd_is_overriden_by_its_consumers_bare_cwd.snap +++ b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__pane_template_command_with_cwd_is_overriden_by_its_consumers_bare_cwd.snap @@ -1,7 +1,6 @@ --- source: zellij-utils/src/input/./unit/layout_test.rs -assertion_line: 1616 -expression: "format!(\"{:#?}\", layout)" +expression: "normalize_layout_debug(format!(\"{:#?}\", layout))" --- Layout { tabs: [ @@ -32,6 +31,7 @@ Layout { ), ), borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -51,6 +51,7 @@ Layout { ), ), borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -93,6 +94,7 @@ Layout { ), ), borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -112,6 +114,7 @@ Layout { ), ), borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, diff --git a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__pane_template_command_with_cwd_overriden_by_its_consumers_command_cwd.snap b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__pane_template_command_with_cwd_overriden_by_its_consumers_command_cwd.snap index 7e3cb306d..b56e6f07f 100644 --- a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__pane_template_command_with_cwd_overriden_by_its_consumers_command_cwd.snap +++ b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__pane_template_command_with_cwd_overriden_by_its_consumers_command_cwd.snap @@ -1,7 +1,6 @@ --- source: zellij-utils/src/input/./unit/layout_test.rs -assertion_line: 1562 -expression: "format!(\"{:#?}\", layout)" +expression: "normalize_layout_debug(format!(\"{:#?}\", layout))" --- Layout { tabs: [ @@ -32,6 +31,7 @@ Layout { ), ), borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -51,6 +51,7 @@ Layout { ), ), borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -93,6 +94,7 @@ Layout { ), ), borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -112,6 +114,7 @@ Layout { ), ), borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, diff --git a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__pane_template_command_with_cwd_remains_when_its_consumer_command_does_not_have_a_cwd.snap b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__pane_template_command_with_cwd_remains_when_its_consumer_command_does_not_have_a_cwd.snap index 91e2f3582..c560ec9f5 100644 --- a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__pane_template_command_with_cwd_remains_when_its_consumer_command_does_not_have_a_cwd.snap +++ b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__pane_template_command_with_cwd_remains_when_its_consumer_command_does_not_have_a_cwd.snap @@ -1,7 +1,6 @@ --- source: zellij-utils/src/input/./unit/layout_test.rs -assertion_line: 1579 -expression: "format!(\"{:#?}\", layout)" +expression: "normalize_layout_debug(format!(\"{:#?}\", layout))" --- Layout { tabs: [ @@ -32,6 +31,7 @@ Layout { ), ), borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -51,6 +51,7 @@ Layout { ), ), borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -93,6 +94,7 @@ Layout { ), ), borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -112,6 +114,7 @@ Layout { ), ), borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, diff --git a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__pane_template_command_without_cwd_is_overriden_by_its_consumers_cwd.snap b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__pane_template_command_without_cwd_is_overriden_by_its_consumers_cwd.snap index 2d0ca3604..c560ec9f5 100644 --- a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__pane_template_command_without_cwd_is_overriden_by_its_consumers_cwd.snap +++ b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__pane_template_command_without_cwd_is_overriden_by_its_consumers_cwd.snap @@ -1,7 +1,6 @@ --- source: zellij-utils/src/input/./unit/layout_test.rs -assertion_line: 1597 -expression: "format!(\"{:#?}\", layout)" +expression: "normalize_layout_debug(format!(\"{:#?}\", layout))" --- Layout { tabs: [ @@ -32,6 +31,7 @@ Layout { ), ), borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -51,6 +51,7 @@ Layout { ), ), borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -93,6 +94,7 @@ Layout { ), ), borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -112,6 +114,7 @@ Layout { ), ), borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, diff --git a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__pane_template_command_without_cwd_receives_its_consumers_bare_cwd.snap b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__pane_template_command_without_cwd_receives_its_consumers_bare_cwd.snap index fdbf56a3a..e346e266e 100644 --- a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__pane_template_command_without_cwd_receives_its_consumers_bare_cwd.snap +++ b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__pane_template_command_without_cwd_receives_its_consumers_bare_cwd.snap @@ -1,7 +1,6 @@ --- source: zellij-utils/src/input/./unit/layout_test.rs -assertion_line: 1634 -expression: "format!(\"{:#?}\", layout)" +expression: "normalize_layout_debug(format!(\"{:#?}\", layout))" --- Layout { tabs: [ @@ -32,6 +31,7 @@ Layout { ), ), borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -51,6 +51,7 @@ Layout { ), ), borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -93,6 +94,7 @@ Layout { ), ), borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -112,6 +114,7 @@ Layout { ), ), borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, diff --git a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__pane_template_with_bare_cwd_overriden_by_its_consumers_bare_cwd.snap b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__pane_template_with_bare_cwd_overriden_by_its_consumers_bare_cwd.snap index 5bb6cceca..0d32be8f5 100644 --- a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__pane_template_with_bare_cwd_overriden_by_its_consumers_bare_cwd.snap +++ b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__pane_template_with_bare_cwd_overriden_by_its_consumers_bare_cwd.snap @@ -1,7 +1,6 @@ --- source: zellij-utils/src/input/./unit/layout_test.rs -assertion_line: 1652 -expression: "format!(\"{:#?}\", layout)" +expression: "normalize_layout_debug(format!(\"{:#?}\", layout))" --- Layout { tabs: [ @@ -22,6 +21,7 @@ Layout { ), ), borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -41,6 +41,7 @@ Layout { ), ), borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -73,6 +74,7 @@ Layout { ), ), borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -92,6 +94,7 @@ Layout { ), ), borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, diff --git a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__pane_template_with_bare_propagated_to_its_consumer_command_with_cwd.snap b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__pane_template_with_bare_propagated_to_its_consumer_command_with_cwd.snap index 4e8812fa5..e346e266e 100644 --- a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__pane_template_with_bare_propagated_to_its_consumer_command_with_cwd.snap +++ b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__pane_template_with_bare_propagated_to_its_consumer_command_with_cwd.snap @@ -1,7 +1,6 @@ --- source: zellij-utils/src/input/./unit/layout_test.rs -assertion_line: 1686 -expression: "format!(\"{:#?}\", layout)" +expression: "normalize_layout_debug(format!(\"{:#?}\", layout))" --- Layout { tabs: [ @@ -32,6 +31,7 @@ Layout { ), ), borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -51,6 +51,7 @@ Layout { ), ), borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -93,6 +94,7 @@ Layout { ), ), borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -112,6 +114,7 @@ Layout { ), ), borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, diff --git a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__pane_template_with_bare_propagated_to_its_consumer_command_without_cwd.snap b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__pane_template_with_bare_propagated_to_its_consumer_command_without_cwd.snap index 5ea6f110c..1542be0d5 100644 --- a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__pane_template_with_bare_propagated_to_its_consumer_command_without_cwd.snap +++ b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__pane_template_with_bare_propagated_to_its_consumer_command_without_cwd.snap @@ -1,7 +1,6 @@ --- source: zellij-utils/src/input/./unit/layout_test.rs -assertion_line: 1668 -expression: "format!(\"{:#?}\", layout)" +expression: "normalize_layout_debug(format!(\"{:#?}\", layout))" --- Layout { tabs: [ @@ -32,6 +31,7 @@ Layout { ), ), borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -51,6 +51,7 @@ Layout { ), ), borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -93,6 +94,7 @@ Layout { ), ), borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -112,6 +114,7 @@ Layout { ), ), borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, diff --git a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__pane_template_with_bare_propagated_to_its_consumer_edit.snap b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__pane_template_with_bare_propagated_to_its_consumer_edit.snap index 1913b50f5..72391a0b3 100644 --- a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__pane_template_with_bare_propagated_to_its_consumer_edit.snap +++ b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__pane_template_with_bare_propagated_to_its_consumer_edit.snap @@ -1,7 +1,6 @@ --- source: zellij-utils/src/input/./unit/layout_test.rs -assertion_line: 1702 -expression: "format!(\"{:#?}\", layout)" +expression: "normalize_layout_debug(format!(\"{:#?}\", layout))" --- Layout { tabs: [ @@ -26,6 +25,7 @@ Layout { ), ), borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -45,6 +45,7 @@ Layout { ), ), borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -81,6 +82,7 @@ Layout { ), ), borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -100,6 +102,7 @@ Layout { ), ), borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, diff --git a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__pane_template_with_command_propagated_to_its_consumer_edit.snap b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__pane_template_with_command_propagated_to_its_consumer_edit.snap index f6bbaeed6..e214e3e95 100644 --- a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__pane_template_with_command_propagated_to_its_consumer_edit.snap +++ b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__pane_template_with_command_propagated_to_its_consumer_edit.snap @@ -1,7 +1,6 @@ --- source: zellij-utils/src/input/./unit/layout_test.rs -assertion_line: 1718 -expression: "format!(\"{:#?}\", layout)" +expression: "normalize_layout_debug(format!(\"{:#?}\", layout))" --- Layout { tabs: [ @@ -26,6 +25,7 @@ Layout { ), ), borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -45,6 +45,7 @@ Layout { ), ), borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -81,6 +82,7 @@ Layout { ), ), borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -100,6 +102,7 @@ Layout { ), ), borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, diff --git a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__tab_cwd_given_to_panes_without_cwd.snap b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__tab_cwd_given_to_panes_without_cwd.snap index 8a8e5420d..2152b1d3e 100644 --- a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__tab_cwd_given_to_panes_without_cwd.snap +++ b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__tab_cwd_given_to_panes_without_cwd.snap @@ -1,7 +1,6 @@ --- source: zellij-utils/src/input/./unit/layout_test.rs -assertion_line: 1817 -expression: "format!(\"{:#?}\", layout)" +expression: "normalize_layout_debug(format!(\"{:#?}\", layout))" --- Layout { tabs: [ @@ -22,6 +21,7 @@ Layout { ), ), borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -54,6 +54,7 @@ Layout { ), ), borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -73,6 +74,7 @@ Layout { ), ), borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -97,6 +99,7 @@ Layout { split_size: None, run: None, borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, diff --git a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__tab_cwd_prepended_to_panes_with_cwd.snap b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__tab_cwd_prepended_to_panes_with_cwd.snap index 1b37491ef..385dcf218 100644 --- a/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__tab_cwd_prepended_to_panes_with_cwd.snap +++ b/zellij-utils/src/input/unit/snapshots/zellij_utils__input__layout__layout_test__tab_cwd_prepended_to_panes_with_cwd.snap @@ -1,7 +1,6 @@ --- source: zellij-utils/src/input/./unit/layout_test.rs -assertion_line: 1832 -expression: "format!(\"{:#?}\", layout)" +expression: "normalize_layout_debug(format!(\"{:#?}\", layout))" --- Layout { tabs: [ @@ -22,6 +21,7 @@ Layout { ), ), borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -54,6 +54,7 @@ Layout { ), ), borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -73,6 +74,7 @@ Layout { ), ), borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, @@ -97,6 +99,7 @@ Layout { split_size: None, run: None, borderless: None, + border_style: None, focus: None, external_children_index: None, children_are_stacked: false, diff --git a/zellij-utils/src/ipc/protobuf_conversion.rs b/zellij-utils/src/ipc/protobuf_conversion.rs index 9177732fd..30cc3cc4b 100644 --- a/zellij-utils/src/ipc/protobuf_conversion.rs +++ b/zellij-utils/src/ipc/protobuf_conversion.rs @@ -1280,6 +1280,7 @@ impl From SelectCommandAtScrollPositionAction, SetDarkThemeAction, SetLightThemeAction, + SetPaneBorderStyleAction, SetPaneBorderlessAction, SetPaneColorAction, SetPaneFrameStyleAction, @@ -1558,6 +1559,7 @@ impl From near_current_pane, no_focus, borderless, + border_style, tab_id, .. } => ActionType::NewTiledPane(NewTiledPaneAction { @@ -1566,6 +1568,7 @@ impl From pane_name, near_current_pane, borderless, + border_style: border_style.map(|b| b.into()), tab_id: tab_id.map(|t| t as u32), no_focus, }), @@ -2060,6 +2063,13 @@ impl From pane_id: Some(pane_id.into()), borderless, }), + crate::input::actions::Action::SetPaneBorderStyle { + pane_id, + border_style, + } => ActionType::SetPaneBorderStyle(SetPaneBorderStyleAction { + pane_id: Some(pane_id.into()), + border_style: Some(border_style.into()), + }), crate::input::actions::Action::TogglePaneInGroup => { ActionType::TogglePaneInGroup(TogglePaneInGroupAction {}) }, @@ -2481,6 +2491,7 @@ impl TryFrom near_current_pane: new_tiled_action.near_current_pane, no_focus: new_tiled_action.no_focus, borderless: new_tiled_action.borderless, + border_style: new_tiled_action.border_style.map(|b| b.into()), tab_id: new_tiled_action.tab_id.map(|t| t as usize), }) }, @@ -3008,6 +3019,18 @@ impl TryFrom borderless: set_borderless_action.borderless, }) }, + ActionType::SetPaneBorderStyle(set_border_style_action) => { + Ok(crate::input::actions::Action::SetPaneBorderStyle { + pane_id: set_border_style_action + .pane_id + .ok_or_else(|| anyhow!("SetPaneBorderStyle missing pane_id"))? + .try_into()?, + border_style: set_border_style_action + .border_style + .map(|b| b.into()) + .unwrap_or_default(), + }) + }, ActionType::TogglePaneInGroup(_) => { Ok(crate::input::actions::Action::TogglePaneInGroup) }, @@ -3222,12 +3245,12 @@ impl TryFrom ActionType::NextSwapLayoutByTabId(a) => { Ok(crate::input::actions::Action::NextSwapLayoutByTabId { id: a.id }) }, - ActionType::ApplyTiledSwapLayoutByTabId(a) => Ok( - crate::input::actions::Action::ApplyTiledSwapLayoutByTabId { + ActionType::ApplyTiledSwapLayoutByTabId(a) => { + Ok(crate::input::actions::Action::ApplyTiledSwapLayoutByTabId { id: a.id, name: a.name, - }, - ), + }) + }, ActionType::ApplyFloatingSwapLayoutByTabId(a) => Ok( crate::input::actions::Action::ApplyFloatingSwapLayoutByTabId { id: a.id, @@ -3859,6 +3882,62 @@ impl TryFrom i32 { + use crate::client_server_contract::client_server_contract::LineStyle as ProtoLineStyle; + let proto = match line_style { + crate::data::LineStyle::Single => ProtoLineStyle::Single, + crate::data::LineStyle::Double => ProtoLineStyle::Double, + crate::data::LineStyle::Heavy => ProtoLineStyle::Heavy, + crate::data::LineStyle::Dashed => ProtoLineStyle::Dashed, + crate::data::LineStyle::HeavyDashed => ProtoLineStyle::HeavyDashed, + }; + proto as i32 +} + +fn proto_i32_to_line_style(value: i32) -> crate::data::LineStyle { + use crate::client_server_contract::client_server_contract::LineStyle as ProtoLineStyle; + match ProtoLineStyle::try_from(value) { + Ok(ProtoLineStyle::Single) => crate::data::LineStyle::Single, + Ok(ProtoLineStyle::Double) => crate::data::LineStyle::Double, + Ok(ProtoLineStyle::Heavy) => crate::data::LineStyle::Heavy, + Ok(ProtoLineStyle::Dashed) => crate::data::LineStyle::Dashed, + Ok(ProtoLineStyle::HeavyDashed) => crate::data::LineStyle::HeavyDashed, + Err(_) => crate::data::LineStyle::Single, + } +} + +impl From + for crate::client_server_contract::client_server_contract::BorderStyleOverride +{ + fn from(border_style: crate::data::BorderStyleOverride) -> Self { + Self { + all: border_style.all.map(line_style_to_proto_i32), + top: border_style.top.map(line_style_to_proto_i32), + right: border_style.right.map(line_style_to_proto_i32), + bottom: border_style.bottom.map(line_style_to_proto_i32), + left: border_style.left.map(line_style_to_proto_i32), + rounded_corners: border_style.rounded_corners, + } + } +} + +impl From + for crate::data::BorderStyleOverride +{ + fn from( + border_style: crate::client_server_contract::client_server_contract::BorderStyleOverride, + ) -> Self { + Self { + all: border_style.all.map(proto_i32_to_line_style), + top: border_style.top.map(proto_i32_to_line_style), + right: border_style.right.map(proto_i32_to_line_style), + bottom: border_style.bottom.map(proto_i32_to_line_style), + left: border_style.left.map(proto_i32_to_line_style), + rounded_corners: border_style.rounded_corners, + } + } +} + // FloatingPaneCoordinates conversion impl From for crate::client_server_contract::client_server_contract::FloatingPaneCoordinates @@ -3871,6 +3950,7 @@ impl From height: coords.height.map(|h| h.into()), pinned: coords.pinned, borderless: coords.borderless, + border_style: coords.border_style.map(|b| b.into()), } } } @@ -3890,6 +3970,7 @@ impl TryFrom }; let placement_type = match placement { crate::data::NewPanePlacement::NoPreference { - borderless: Some(b), + borderless: None, + border_style: None, + } => PlacementType::NoPreference(true), + crate::data::NewPanePlacement::NoPreference { + borderless, + border_style, } => PlacementType::NoPreferenceWithOptions(NoPreferencePlacement { - borderless: Some(b), - }), - crate::data::NewPanePlacement::NoPreference { borderless: None } => { - PlacementType::NoPreference(true) - }, - crate::data::NewPanePlacement::Tiled { - direction, - borderless: Some(b), - } => PlacementType::TiledWithOptions(TiledPlacement { - direction: direction.map(direction_to_proto_i32), - borderless: Some(b), + borderless, + border_style: border_style.map(|b| b.into()), }), crate::data::NewPanePlacement::Tiled { direction, borderless: None, + border_style: None, } => PlacementType::Tiled(direction.map(direction_to_proto_i32).unwrap_or(0)), + crate::data::NewPanePlacement::Tiled { + direction, + borderless, + border_style, + } => PlacementType::TiledWithOptions(TiledPlacement { + direction: direction.map(direction_to_proto_i32), + borderless, + border_style: border_style.map(|b| b.into()), + }), crate::data::NewPanePlacement::Floating(coords) => { PlacementType::Floating(coords.map(|c| c.into()).unwrap_or_default()) }, @@ -3930,28 +4017,33 @@ impl From pane_id_to_replace, close_replaced_pane, borderless, + border_style, } => PlacementType::InPlace( crate::client_server_contract::client_server_contract::NewPanePlacementInPlace { pane_id_to_replace: pane_id_to_replace.map(|id| id.into()), close_replaced_pane, borderless, + border_style: border_style.map(|b| b.into()), }, ), - crate::data::NewPanePlacement::Stacked { - pane_id_to_stack_under, - borderless: Some(b), - } => PlacementType::StackedWithOptions(StackedPlacement { - pane_id_to_stack_under: pane_id_to_stack_under.map(|id| id.into()), - borderless: Some(b), - }), crate::data::NewPanePlacement::Stacked { pane_id_to_stack_under, borderless: None, + border_style: None, } => PlacementType::Stacked( pane_id_to_stack_under .map(|id| id.into()) .unwrap_or_default(), ), + crate::data::NewPanePlacement::Stacked { + pane_id_to_stack_under, + borderless, + border_style, + } => PlacementType::StackedWithOptions(StackedPlacement { + pane_id_to_stack_under: pane_id_to_stack_under.map(|id| id.into()), + borderless, + border_style: border_style.map(|b| b.into()), + }), }; Self { placement_type: Some(placement_type), @@ -3976,6 +4068,7 @@ impl TryFrom { Ok(crate::data::NewPanePlacement::NoPreference { borderless: opts.borderless, + border_style: opts.border_style.map(|b| b.into()), }) }, PlacementType::TiledWithOptions(opts) => { @@ -3983,6 +4076,7 @@ impl TryFrom { @@ -3993,12 +4087,14 @@ impl TryFrom { - Ok(crate::data::NewPanePlacement::NoPreference { borderless: None }) - }, + PlacementType::NoPreference(_) => Ok(crate::data::NewPanePlacement::NoPreference { + borderless: None, + border_style: None, + }), PlacementType::Tiled(direction) => { let direction = if direction == 0 { None @@ -4008,6 +4104,7 @@ impl TryFrom { @@ -4025,6 +4122,7 @@ impl TryFrom { let pane_id = if pane_id == Default::default() { @@ -4035,6 +4133,7 @@ impl TryFrom pane_initial_contents: layout.pane_initial_contents, default_fg: layout.default_fg, default_bg: layout.default_bg, + border_style: layout.border_style.map(|b| b.into()), } } } @@ -4336,6 +4436,7 @@ impl From borderless: layout.borderless, default_fg: layout.default_fg, default_bg: layout.default_bg, + border_style: layout.border_style.map(|b| b.into()), } } } @@ -4548,6 +4649,7 @@ impl TryFrom KdlLayoutParser<'a> { || word == "swap_floating_layout" || word == "hide_floating_panes" || word == "contents_file" + || word == "border_style" + || word == "border_top" + || word == "border_right" + || word == "border_bottom" + || word == "border_left" + || word == "rounded_corners" } fn is_a_valid_pane_property(&self, property_name: &str) -> bool { property_name == "borderless" @@ -108,6 +115,44 @@ impl<'a> KdlLayoutParser<'a> { || property_name == "contents_file" || property_name == "default_fg" || property_name == "default_bg" + || self.is_a_border_style_property(property_name) + } + fn is_a_border_style_property(&self, property_name: &str) -> bool { + property_name == "border_style" + || property_name == "border_top" + || property_name == "border_right" + || property_name == "border_bottom" + || property_name == "border_left" + || property_name == "rounded_corners" + } + fn parse_border_style( + &self, + kdl_node: &KdlNode, + ) -> Result, ConfigError> { + let parse = |property_name: &str| -> Result, ConfigError> { + match kdl_get_string_property_or_child_value_with_error!(kdl_node, property_name) { + Some(value) => LineStyle::from_str(value).map(Some).map_err(|e| { + ConfigError::new_layout_kdl_error( + e, + kdl_node.span().offset(), + kdl_node.span().len(), + ) + }), + None => Ok(None), + } + }; + let border_style_override = BorderStyleOverride { + all: parse("border_style")?, + top: parse("border_top")?, + right: parse("border_right")?, + bottom: parse("border_bottom")?, + left: parse("border_left")?, + rounded_corners: kdl_get_bool_property_or_child_value_with_error!( + kdl_node, + "rounded_corners" + ), + }; + Ok(border_style_override.none_if_empty()) } fn is_a_valid_floating_pane_property(&self, property_name: &str) -> bool { property_name == "borderless" @@ -128,6 +173,7 @@ impl<'a> KdlLayoutParser<'a> { || property_name == "contents_file" || property_name == "default_fg" || property_name == "default_bg" + || self.is_a_border_style_property(property_name) } fn is_a_valid_tab_property(&self, property_name: &str) -> bool { property_name == "focus" @@ -580,8 +626,10 @@ impl<'a> KdlLayoutParser<'a> { std::fs::read_to_string(parent_folder.join(contents_file)).ok() }) }); + let border_style = self.parse_border_style(kdl_node)?; Ok(TiledPaneLayout { borderless, + border_style, focus, name, split_size, @@ -628,6 +676,7 @@ impl<'a> KdlLayoutParser<'a> { std::fs::read_to_string(parent_folder.join(contents_file)).ok() }) }); + let border_style = self.parse_border_style(kdl_node)?; Ok(FloatingPaneLayout { name, height, @@ -638,6 +687,7 @@ impl<'a> KdlLayoutParser<'a> { focus, pinned, borderless, + border_style, pane_initial_contents, default_fg, default_bg, @@ -767,6 +817,14 @@ impl<'a> KdlLayoutParser<'a> { if let Some(borderless) = borderless { pane_template.borderless = Some(borderless); } + if let Some(border_style) = self.parse_border_style(kdl_node)? { + pane_template.border_style = Some( + pane_template + .border_style + .unwrap_or_default() + .merge(&border_style), + ); + } if let Some(focus) = focus { pane_template.focus = Some(focus); } @@ -882,6 +940,14 @@ impl<'a> KdlLayoutParser<'a> { if let Some(pinned) = pinned { pane_template.pinned = Some(pinned); } + if let Some(border_style) = self.parse_border_style(kdl_node)? { + pane_template.border_style = Some( + pane_template + .border_style + .unwrap_or_default() + .merge(&border_style), + ); + } Ok(pane_template) }, PaneOrFloatingPane::Either(mut pane_template) => { @@ -937,6 +1003,14 @@ impl<'a> KdlLayoutParser<'a> { if let Some(pinned) = pinned { floating_pane.pinned = Some(pinned); } + if let Some(border_style) = self.parse_border_style(kdl_node)? { + floating_pane.border_style = Some( + floating_pane + .border_style + .unwrap_or_default() + .merge(&border_style), + ); + } Ok(floating_pane) }, } @@ -1100,6 +1174,7 @@ impl<'a> KdlLayoutParser<'a> { PaneOrFloatingPane::Either(TiledPaneLayout { focus, run, + border_style: self.parse_border_style(kdl_node)?, ..Default::default() }), kdl_node.clone(), @@ -1124,6 +1199,7 @@ impl<'a> KdlLayoutParser<'a> { x, y, pinned, + border_style: self.parse_border_style(kdl_node)?, ..Default::default() }), kdl_node.clone(), @@ -1134,6 +1210,7 @@ impl<'a> KdlLayoutParser<'a> { // pane properties let borderless = kdl_get_bool_property_or_child_value_with_error!(kdl_node, "borderless"); + let border_style = self.parse_border_style(kdl_node)?; let children_are_stacked = kdl_get_bool_property_or_child_value_with_error!(kdl_node, "stacked") .unwrap_or(false); @@ -1154,6 +1231,7 @@ impl<'a> KdlLayoutParser<'a> { ( PaneOrFloatingPane::Pane(TiledPaneLayout { borderless, + border_style, focus, split_size, run, diff --git a/zellij-utils/src/kdl/mod.rs b/zellij-utils/src/kdl/mod.rs index 0c7d81b08..84f13bec6 100644 --- a/zellij-utils/src/kdl/mod.rs +++ b/zellij-utils/src/kdl/mod.rs @@ -1,9 +1,9 @@ mod kdl_layout_parser; use crate::data::{ - BareKey, Direction, FloatingPaneCoordinates, InputMode, KeyWithModifier, LayoutInfo, - LayoutMetadata, MultiplayerColors, Palette, PaletteColor, PaneId, PaneInfo, PaneManifest, - PermissionType, Resize, SessionInfo, StyleDeclaration, Styling, TabInfo, ThemeHue, WebSharing, - DEFAULT_STYLES, + BareKey, BorderStyleOverride, Direction, FloatingPaneCoordinates, InputMode, KeyWithModifier, + LayoutInfo, LayoutMetadata, LineStyle, MultiplayerColors, Palette, PaletteColor, PaneId, + PaneInfo, PaneManifest, PermissionType, Resize, SessionInfo, StyleDeclaration, Styling, + TabInfo, ThemeHue, WebSharing, DEFAULT_STYLES, }; use crate::envs::EnvironmentVariables; use crate::home::{find_default_config_dir, get_layout_dir}; @@ -886,6 +886,7 @@ impl Action { pane_name: name, near_current_pane: false, borderless: _, + border_style: _, .. } => { let mut node = KdlNode::new("Run"); @@ -2094,12 +2095,20 @@ impl TryFrom<(&KdlNode, &Options)> for Action { command_metadata.and_then(|c_m| kdl_child_bool_value_for_entry(c_m, "pinned")); let borderless = command_metadata .and_then(|c_m| kdl_child_bool_value_for_entry(c_m, "borderless")); + let border_style = match command_metadata { + Some(command_metadata) => { + border_style_override_from_kdl_document(command_metadata, "border")? + .none_if_empty() + }, + None => None, + }; if floating { Ok(Action::NewFloatingPane { command: Some(run_command_action), pane_name: name, - coordinates: FloatingPaneCoordinates::new( - x, y, width, height, pinned, borderless, + coordinates: FloatingPaneCoordinates::merge_border_style( + FloatingPaneCoordinates::new(x, y, width, height, pinned, borderless), + border_style, ), near_current_pane: false, no_focus: false, @@ -2131,6 +2140,7 @@ impl TryFrom<(&KdlNode, &Options)> for Action { near_current_pane: false, no_focus: false, borderless: None, + border_style, tab_id: None, }) } @@ -5694,6 +5704,84 @@ fn load_plugins_from_kdl( Ok(load_plugins) } +pub fn line_style_from_kdl( + kdl_document: &KdlDocument, + child_name: &str, +) -> Result, ConfigError> { + let Some(child) = kdl_document.get(child_name) else { + return Ok(None); + }; + let value = child + .get(0) + .and_then(|v| v.value().as_string()) + .ok_or_else(|| { + ConfigError::new_kdl_error( + format!("'{}' must have a string value", child_name), + child.span().offset(), + child.span().len(), + ) + })?; + LineStyle::from_str(value) + .map(Some) + .map_err(|e| ConfigError::new_kdl_error(e, child.span().offset(), child.span().len())) +} + +pub fn border_style_override_from_kdl_document( + kdl_document: &KdlDocument, + prefix: &str, +) -> Result { + let rounded_corners = kdl_document + .get(&format!("{}_rounded_corners", prefix)) + .and_then(|n| n.entries().iter().next()) + .and_then(|e| e.value().as_bool()); + Ok(BorderStyleOverride { + all: line_style_from_kdl(kdl_document, &format!("{}_style", prefix))?, + top: line_style_from_kdl(kdl_document, &format!("{}_top", prefix))?, + right: line_style_from_kdl(kdl_document, &format!("{}_right", prefix))?, + bottom: line_style_from_kdl(kdl_document, &format!("{}_bottom", prefix))?, + left: line_style_from_kdl(kdl_document, &format!("{}_left", prefix))?, + rounded_corners, + }) +} + +pub fn border_style_override_from_kdl_children( + kdl_node: &KdlNode, + prefix: &str, +) -> Result { + match kdl_node.children() { + Some(children) => border_style_override_from_kdl_document(children, prefix), + None => Ok(BorderStyleOverride::default()), + } +} + +pub fn border_style_override_to_kdl_children( + border_style_override: &BorderStyleOverride, + prefix: &str, + children: &mut KdlDocument, +) -> bool { + let mut has_any = false; + let mut push = |name: String, line_style: Option| { + if let Some(line_style) = line_style { + let mut node = KdlNode::new(name); + node.push(KdlValue::String(line_style.to_string())); + children.nodes_mut().push(node); + has_any = true; + } + }; + push(format!("{}_style", prefix), border_style_override.all); + push(format!("{}_top", prefix), border_style_override.top); + push(format!("{}_right", prefix), border_style_override.right); + push(format!("{}_bottom", prefix), border_style_override.bottom); + push(format!("{}_left", prefix), border_style_override.left); + if let Some(rounded_corners) = border_style_override.rounded_corners { + let mut node = KdlNode::new(format!("{}_rounded_corners", prefix)); + node.push(KdlValue::Bool(rounded_corners)); + children.nodes_mut().push(node); + has_any = true; + } + has_any +} + impl UiConfig { pub fn from_kdl(kdl_ui_config: &KdlNode) -> Result { let mut ui_config = UiConfig::default(); @@ -5703,9 +5791,14 @@ impl UiConfig { .unwrap_or(false); let hide_session_name = kdl_get_child_entry_bool_value!(pane_frames, "hide_session_name").unwrap_or(false); + let border_style = border_style_override_from_kdl_children(pane_frames, "border")?; + let floating_border_style = + border_style_override_from_kdl_children(pane_frames, "floating_border")?; let frame_config = FrameConfig { rounded_corners, hide_session_name, + border_style, + floating_border_style, }; ui_config.pane_frames = frame_config; } @@ -5729,6 +5822,20 @@ impl UiConfig { hide_session_name.push(KdlValue::Bool(true)); frame_config_children.nodes_mut().push(hide_session_name); } + if border_style_override_to_kdl_children( + &self.pane_frames.border_style, + "border", + &mut frame_config_children, + ) { + has_ui_config = true; + } + if border_style_override_to_kdl_children( + &self.pane_frames.floating_border_style, + "floating_border", + &mut frame_config_children, + ) { + has_ui_config = true; + } if has_ui_config { frame_config.set_children(frame_config_children); ui_config_children.nodes_mut().push(frame_config); @@ -7724,6 +7831,71 @@ fn ui_config_to_string() { insta::assert_snapshot!(serialized.to_string()); } +#[test] +fn ui_config_with_border_styles_to_string() { + let fake_config = r##" + ui { + pane_frames { + rounded_corners true + border_style "single" + border_top "double" + floating_border_style "heavy" + floating_border_rounded_corners false + } + }"##; + let document: KdlDocument = fake_config.parse().unwrap(); + let deserialized = UiConfig::from_kdl(document.get("ui").unwrap()).unwrap(); + assert_eq!( + deserialized.pane_frames.border_style, + BorderStyleOverride { + all: Some(LineStyle::Single), + top: Some(LineStyle::Double), + ..Default::default() + } + ); + assert_eq!( + deserialized.pane_frames.floating_border_style, + BorderStyleOverride { + all: Some(LineStyle::Heavy), + rounded_corners: Some(false), + ..Default::default() + } + ); + let resolved = deserialized.pane_frames.resolved_border_style(); + assert_eq!(resolved.top, LineStyle::Double); + assert_eq!(resolved.bottom, LineStyle::Single); + assert!(resolved.rounded_corners); + let resolved_floating = deserialized.pane_frames.resolved_floating_border_style(); + assert_eq!(resolved_floating.top, LineStyle::Heavy); + assert!(!resolved_floating.rounded_corners); + let serialized = UiConfig::to_kdl(&deserialized).unwrap(); + let deserialized_from_serialized = UiConfig::from_kdl( + serialized + .to_string() + .parse::() + .unwrap() + .get("ui") + .unwrap(), + ) + .unwrap(); + assert_eq!( + deserialized, deserialized_from_serialized, + "Deserialized and serialized ui config are the same" + ); +} + +#[test] +fn ui_config_with_invalid_border_style_is_an_error() { + let fake_config = r##" + ui { + pane_frames { + border_style "squiggly" + } + }"##; + let document: KdlDocument = fake_config.parse().unwrap(); + assert!(UiConfig::from_kdl(document.get("ui").unwrap()).is_err()); +} + #[test] fn ui_config_to_string_with_no_ui_config() { let fake_config = r##" diff --git a/zellij-utils/src/plugin_api/action.proto b/zellij-utils/src/plugin_api/action.proto index 96ba61d3a..fc36761bc 100644 --- a/zellij-utils/src/plugin_api/action.proto +++ b/zellij-utils/src/plugin_api/action.proto @@ -3,6 +3,7 @@ syntax = "proto3"; import "input_mode.proto"; import "resize.proto"; import "pane_frame_style.proto"; +import "style.proto"; package api.action; @@ -108,6 +109,7 @@ message TiledPaneLayout { bool is_expanded_in_stack = 11; bool hide_floating_panes = 12; optional string pane_initial_contents = 13; + optional style.BorderStyleOverride border_style = 14; } message FloatingPaneLayout { @@ -123,6 +125,7 @@ message FloatingPaneLayout { optional string pane_initial_contents = 10; optional uint32 logical_position = 11; optional bool borderless = 12; + optional style.BorderStyleOverride border_style = 13; } message SwapTiledLayout { @@ -249,9 +252,15 @@ message Action { SetPaneFrameStylePayload set_pane_frame_style_payload = 61; string apply_tiled_swap_layout_payload = 62; string apply_floating_swap_layout_payload = 63; + SetPaneBorderStylePayload set_pane_border_style_payload = 64; } } +message SetPaneBorderStylePayload { + PaneId pane_id = 1; + style.BorderStyleOverride border_style = 2; +} + message SetPaneFrameStylePayload { pane_frame_style.PaneFrameStyle pane_frame_style = 1; } @@ -337,6 +346,7 @@ message NewTiledPanePayload { optional resize.ResizeDirection direction = 2; bool near_current_pane = 3; optional bool borderless = 4; + optional style.BorderStyleOverride border_style = 5; } message MovePanePayload { @@ -513,6 +523,7 @@ enum ActionName { CopyLastCommandOutput = 113; ApplyTiledSwapLayout = 114; ApplyFloatingSwapLayout = 115; + SetPaneBorderStyle = 116; } message Position { @@ -544,6 +555,7 @@ message FloatingPaneCoordinates { optional SplitSize height = 4; optional bool pinned = 5; optional bool borderless = 6; + optional style.BorderStyleOverride border_style = 7; } // SetPaneBorderlessPayload specifies the pane and borderless state @@ -572,11 +584,13 @@ message NewPanePlacement { message NoPreferenceOptions { optional bool borderless = 1; + optional style.BorderStyleOverride border_style = 2; } message TiledPlacement { optional resize.ResizeDirection direction = 1; optional bool borderless = 2; + optional style.BorderStyleOverride border_style = 3; } message FloatingPlacement { @@ -587,11 +601,13 @@ message InPlaceConfig { optional PaneId pane_id_to_replace = 1; bool close_replaced_pane = 2; optional bool borderless = 3; + optional style.BorderStyleOverride border_style = 4; } message StackedPlacement { optional PaneId pane_id = 1; optional bool borderless = 2; + optional style.BorderStyleOverride border_style = 3; } message MouseEventPayload { diff --git a/zellij-utils/src/plugin_api/action.rs b/zellij-utils/src/plugin_api/action.rs index 5855a36df..818bc60f2 100644 --- a/zellij-utils/src/plugin_api/action.rs +++ b/zellij-utils/src/plugin_api/action.rs @@ -79,8 +79,8 @@ pub use super::generated_api::api::{ resize::{Resize as ProtobufResize, ResizeDirection as ProtobufResizeDirection}, }; use crate::data::{ - CommandOrPlugin, Direction, FloatingPaneCoordinates, InputMode, KeyWithModifier, - NewPanePlacement, PaneId, PluginTag, ResizeStrategy, UnblockCondition, + BorderStyleOverride, CommandOrPlugin, Direction, FloatingPaneCoordinates, InputMode, + KeyWithModifier, NewPanePlacement, PaneId, PluginTag, ResizeStrategy, UnblockCondition, }; use crate::errors::prelude::*; use crate::input::actions::Action; @@ -430,6 +430,7 @@ impl TryFrom for Action { .and_then(|d| d.try_into().ok()); let near_current_pane = payload.near_current_pane; let borderless = payload.borderless; + let border_style = payload.border_style.map(BorderStyleOverride::from); if let Some(payload) = payload.command { let pane_name = payload.pane_name.clone(); let run_command_action: RunCommandAction = payload.try_into()?; @@ -440,6 +441,7 @@ impl TryFrom for Action { near_current_pane, no_focus: false, borderless, + border_style, tab_id: None, }) } else { @@ -450,6 +452,7 @@ impl TryFrom for Action { near_current_pane, no_focus: false, borderless, + border_style, tab_id: None, }) } @@ -1505,6 +1508,7 @@ impl TryFrom for ProtobufAction { pane_name, near_current_pane, borderless, + border_style, .. } => { let direction = direction.and_then(|direction| { @@ -1526,6 +1530,7 @@ impl TryFrom for ProtobufAction { command, near_current_pane, borderless, + border_style: border_style.map(|b| b.into()), }, )), }) @@ -2092,6 +2097,7 @@ impl TryFrom for ProtobufAction { } | Action::TogglePaneBorderless { pane_id: _ } | Action::SetPaneBorderless { .. } + | Action::SetPaneBorderStyle { .. } | Action::SkipConfirm { action: _ } | Action::SwitchSession { .. } | Action::SaveSession @@ -2580,6 +2586,7 @@ impl TryFrom for FloatingPaneCoordinates { height: protobuf_coords.height.and_then(|h| h.try_into().ok()), pinned: protobuf_coords.pinned, borderless: protobuf_coords.borderless, + border_style: protobuf_coords.border_style.map(BorderStyleOverride::from), }) } } @@ -2594,6 +2601,7 @@ impl TryFrom for ProtobufFloatingPaneCoordinates { height: coords.height.and_then(|h| h.try_into().ok()), pinned: coords.pinned, borderless: coords.borderless, + border_style: coords.border_style.map(|b| b.into()), }) } } @@ -2607,6 +2615,7 @@ impl TryFrom for NewPanePlacement { match protobuf_placement.placement_variant { Some(PlacementVariant::NoPreference(opts)) => Ok(NewPanePlacement::NoPreference { borderless: opts.borderless, + border_style: opts.border_style.map(BorderStyleOverride::from), }), Some(PlacementVariant::Tiled(tiled)) => { let direction = tiled @@ -2616,6 +2625,7 @@ impl TryFrom for NewPanePlacement { Ok(NewPanePlacement::Tiled { direction, borderless: tiled.borderless, + border_style: tiled.border_style.map(BorderStyleOverride::from), }) }, Some(PlacementVariant::Floating(floating)) => { @@ -2629,6 +2639,7 @@ impl TryFrom for NewPanePlacement { pane_id_to_replace, close_replaced_pane: config.close_replaced_pane, borderless: config.borderless, + border_style: config.border_style.map(BorderStyleOverride::from), }) }, Some(PlacementVariant::Stacked(stacked)) => { @@ -2636,6 +2647,7 @@ impl TryFrom for NewPanePlacement { Ok(NewPanePlacement::Stacked { pane_id_to_stack_under: pane_id, borderless: stacked.borderless, + border_style: stacked.border_style.map(BorderStyleOverride::from), }) }, None => Err("NewPanePlacement must have a placement variant"), @@ -2650,14 +2662,17 @@ impl TryFrom for ProtobufNewPanePlacement { use super::generated_api::api::action::NoPreferenceOptions; let placement_variant = match placement { - NewPanePlacement::NoPreference { borderless } => { - Some(PlacementVariant::NoPreference(NoPreferenceOptions { - borderless, - })) - }, + NewPanePlacement::NoPreference { + borderless, + border_style, + } => Some(PlacementVariant::NoPreference(NoPreferenceOptions { + borderless, + border_style: border_style.map(|b| b.into()), + })), NewPanePlacement::Tiled { direction, borderless, + border_style, } => { let direction = direction.and_then(|d| { let protobuf_direction: ProtobufResizeDirection = d.try_into().ok()?; @@ -2666,6 +2681,7 @@ impl TryFrom for ProtobufNewPanePlacement { Some(PlacementVariant::Tiled(ProtobufTiledPlacement { direction, borderless, + border_style: border_style.map(|b| b.into()), })) }, NewPanePlacement::Floating(coords) => { @@ -2678,22 +2694,26 @@ impl TryFrom for ProtobufNewPanePlacement { pane_id_to_replace, close_replaced_pane, borderless, + border_style, } => { let pane_id_to_replace = pane_id_to_replace.and_then(|id| id.try_into().ok()); Some(PlacementVariant::InPlace(ProtobufInPlaceConfig { pane_id_to_replace, close_replaced_pane, borderless, + border_style: border_style.map(|b| b.into()), })) }, NewPanePlacement::Stacked { pane_id_to_stack_under, borderless, + border_style, } => { let pane_id = pane_id_to_stack_under.and_then(|id| id.try_into().ok()); Some(PlacementVariant::Stacked(ProtobufStackedPlacement { pane_id, borderless, + border_style: border_style.map(|b| b.into()), })) }, }; @@ -3043,6 +3063,7 @@ impl TryFrom for CommandOrPlugin { path: std::path::PathBuf::from(&f.path), line_number: f.line_number.map(|n| n as usize), cwd: f.cwd.map(std::path::PathBuf::from), + border_style: None, })) }, None => Err("CommandOrPlugin must have command_or_plugin_type"), @@ -3185,6 +3206,7 @@ impl TryFrom for TiledPaneLayout { pane_initial_contents: protobuf.pane_initial_contents, default_fg: None, default_bg: None, + border_style: protobuf.border_style.map(BorderStyleOverride::from), }) } } @@ -3216,6 +3238,7 @@ impl TryFrom for ProtobufTiledPaneLayout { exclude_from_sync: internal.exclude_from_sync, hide_floating_panes: internal.hide_floating_panes, pane_initial_contents: internal.pane_initial_contents, + border_style: internal.border_style.map(|b| b.into()), }) } } @@ -3243,6 +3266,7 @@ impl TryFrom for FloatingPaneLayout { borderless: protobuf.borderless, default_fg: None, default_bg: None, + border_style: protobuf.border_style.map(BorderStyleOverride::from), }) } } @@ -3268,6 +3292,7 @@ impl TryFrom for ProtobufFloatingPaneLayout { pane_initial_contents: internal.pane_initial_contents, logical_position: internal.logical_position.map(|p| p as u32), borderless: internal.borderless, + border_style: internal.border_style.map(|b| b.into()), }) } } @@ -3388,6 +3413,110 @@ mod tests { use super::*; use crate::input::options::PaneFrameStyle; + #[test] + fn new_tiled_pane_action_carries_a_border_style_over_protobuf() { + use crate::data::LineStyle; + let original = Action::NewTiledPane { + direction: None, + command: None, + pane_name: None, + near_current_pane: false, + no_focus: false, + borderless: None, + border_style: Some(BorderStyleOverride { + all: Some(LineStyle::Double), + top: Some(LineStyle::Heavy), + rounded_corners: Some(true), + ..Default::default() + }), + tab_id: None, + }; + let protobuf: ProtobufAction = original.clone().try_into().expect("encode"); + let decoded: Action = protobuf.try_into().expect("decode"); + assert_eq!(original, decoded); + } + + #[test] + fn a_new_pane_placement_carries_a_border_style_over_protobuf() { + use super::super::generated_api::api::action::NewPanePlacement as ProtobufNewPanePlacement; + use crate::data::LineStyle; + for original in [ + NewPanePlacement::NoPreference { + borderless: None, + border_style: Some(BorderStyleOverride { + all: Some(LineStyle::Double), + ..Default::default() + }), + }, + NewPanePlacement::Tiled { + direction: None, + borderless: Some(true), + border_style: Some(BorderStyleOverride { + left: Some(LineStyle::Heavy), + ..Default::default() + }), + }, + NewPanePlacement::InPlace { + pane_id_to_replace: None, + close_replaced_pane: false, + borderless: None, + border_style: Some(BorderStyleOverride { + bottom: Some(LineStyle::Dashed), + ..Default::default() + }), + }, + NewPanePlacement::Stacked { + pane_id_to_stack_under: None, + borderless: None, + border_style: Some(BorderStyleOverride { + rounded_corners: Some(false), + ..Default::default() + }), + }, + NewPanePlacement::Floating(Some(FloatingPaneCoordinates::default().with_border_style( + Some(BorderStyleOverride { + all: Some(LineStyle::HeavyDashed), + ..Default::default() + }), + ))), + ] { + let protobuf: ProtobufNewPanePlacement = original.clone().try_into().expect("encode"); + let decoded: NewPanePlacement = protobuf.try_into().expect("decode"); + assert_eq!(original, decoded); + } + } + + #[test] + fn pane_layouts_carry_border_styles_over_protobuf() { + use super::super::generated_api::api::action::{ + FloatingPaneLayout as ProtobufFloatingPaneLayout, + TiledPaneLayout as ProtobufTiledPaneLayout, + }; + use crate::data::LineStyle; + use crate::input::layout::{FloatingPaneLayout, TiledPaneLayout}; + let border_style = Some(BorderStyleOverride { + all: Some(LineStyle::Double), + top: Some(LineStyle::Heavy), + rounded_corners: Some(true), + ..Default::default() + }); + let tiled = TiledPaneLayout { + border_style, + ..Default::default() + }; + let protobuf: ProtobufTiledPaneLayout = tiled.clone().try_into().expect("encode"); + let decoded: TiledPaneLayout = protobuf.try_into().expect("decode"); + assert_eq!(decoded.border_style, border_style); + + let floating = FloatingPaneLayout { + border_style, + ..Default::default() + }; + let protobuf: ProtobufFloatingPaneLayout = floating.clone().try_into().expect("encode"); + let decoded: FloatingPaneLayout = protobuf.try_into().expect("decode"); + assert_eq!(decoded.border_style, border_style); + } + #[test] fn set_pane_frame_style_action_protobuf_round_trip() { for style in [ diff --git a/zellij-utils/src/plugin_api/command.proto b/zellij-utils/src/plugin_api/command.proto index 70402f25a..feb2ebefe 100644 --- a/zellij-utils/src/plugin_api/command.proto +++ b/zellij-utils/src/plugin_api/command.proto @@ -2,8 +2,11 @@ syntax = "proto3"; package api.command; +import "style.proto"; + message Command { string path = 1; repeated string args = 2; optional string cwd = 3; + optional style.BorderStyleOverride border_style = 4; } diff --git a/zellij-utils/src/plugin_api/command.rs b/zellij-utils/src/plugin_api/command.rs index 2f8bf3005..4ff8c22d5 100644 --- a/zellij-utils/src/plugin_api/command.rs +++ b/zellij-utils/src/plugin_api/command.rs @@ -1,5 +1,5 @@ pub use super::generated_api::api::command::Command as ProtobufCommand; -use crate::data::CommandToRun; +use crate::data::{BorderStyleOverride, CommandToRun}; use std::convert::TryFrom; use std::path::PathBuf; @@ -10,7 +10,13 @@ impl TryFrom for CommandToRun { let path = PathBuf::from(protobuf_command.path); let args = protobuf_command.args; let cwd = protobuf_command.cwd.map(|c| PathBuf::from(c)); - Ok(CommandToRun { path, args, cwd }) + let border_style = protobuf_command.border_style.map(BorderStyleOverride::from); + Ok(CommandToRun { + path, + args, + cwd, + border_style, + }) } } @@ -21,6 +27,7 @@ impl TryFrom for ProtobufCommand { path: command_to_run.path.display().to_string(), args: command_to_run.args, cwd: command_to_run.cwd.map(|c| c.display().to_string()), + border_style: command_to_run.border_style.map(|b| b.into()), }) } } diff --git a/zellij-utils/src/plugin_api/event.rs b/zellij-utils/src/plugin_api/event.rs index 07ee5a540..83aeedef5 100644 --- a/zellij-utils/src/plugin_api/event.rs +++ b/zellij-utils/src/plugin_api/event.rs @@ -2482,6 +2482,8 @@ fn serialize_mode_update_event_with_non_default_values() { // TODO: replace default rounded_corners: true, hide_session_name: false, + border_style: crate::data::BorderStyle::with_rounded_corners(true), + floating_border_style: crate::data::BorderStyle::with_rounded_corners(true), }, capabilities: PluginCapabilities { arrow_fonts: false }, session_name: Some("my awesome test session".to_owned()), diff --git a/zellij-utils/src/plugin_api/file.proto b/zellij-utils/src/plugin_api/file.proto index b2c5372b8..21243974f 100644 --- a/zellij-utils/src/plugin_api/file.proto +++ b/zellij-utils/src/plugin_api/file.proto @@ -2,8 +2,11 @@ syntax = "proto3"; package api.file; +import "style.proto"; + message File { string path = 1; optional int32 line_number = 2; optional string cwd = 3; + optional style.BorderStyleOverride border_style = 4; } diff --git a/zellij-utils/src/plugin_api/file.rs b/zellij-utils/src/plugin_api/file.rs index 7c06aa8e0..a3f225be5 100644 --- a/zellij-utils/src/plugin_api/file.rs +++ b/zellij-utils/src/plugin_api/file.rs @@ -1,5 +1,5 @@ pub use super::generated_api::api::file::File as ProtobufFile; -use crate::data::FileToOpen; +use crate::data::{BorderStyleOverride, FileToOpen}; use std::convert::TryFrom; use std::path::PathBuf; @@ -14,6 +14,7 @@ impl TryFrom for FileToOpen { path, line_number, cwd, + border_style: protobuf_file.border_style.map(BorderStyleOverride::from), }) } } @@ -25,6 +26,7 @@ impl TryFrom for ProtobufFile { path: file_to_open.path.display().to_string(), line_number: file_to_open.line_number.map(|l| l as i32), cwd: file_to_open.cwd.map(|c| c.display().to_string()), + border_style: file_to_open.border_style.map(|b| b.into()), }) } } diff --git a/zellij-utils/src/plugin_api/plugin_command.proto b/zellij-utils/src/plugin_api/plugin_command.proto index 754e169f7..2be85503f 100644 --- a/zellij-utils/src/plugin_api/plugin_command.proto +++ b/zellij-utils/src/plugin_api/plugin_command.proto @@ -10,6 +10,7 @@ import "plugin_permission.proto"; import "input_mode.proto"; import "key.proto"; import "pane_frame_style.proto"; +import "style.proto"; package api.plugin_command; @@ -220,6 +221,7 @@ enum CommandName { FocusHostSession = 228; ApplyTiledSwapLayout = 229; ApplyFloatingSwapLayout = 230; + SetPaneBorderStyle = 231; } message PluginCommand { @@ -382,6 +384,7 @@ message PluginCommand { ToggleFloatingPanesPayload toggle_floating_panes_payload = 171; string apply_tiled_swap_layout_payload = 172; string apply_floating_swap_layout_payload = 173; + SetPaneBorderStylePayload set_pane_border_style_payload = 174; } } @@ -539,6 +542,11 @@ message SetPaneBorderlessPayload { bool borderless = 2; } +message SetPaneBorderStylePayload { + PaneId pane_id = 1; + style.BorderStyleOverride border_style = 2; +} + message ChangeHostFolderPayload { string new_host_folder = 1; } @@ -819,6 +827,7 @@ message FloatingPaneCoordinates { optional FixedOrPercentValue height = 4; optional bool pinned = 5; optional bool borderless = 6; + optional style.BorderStyleOverride border_style = 7; } message FixedOrPercentValue { diff --git a/zellij-utils/src/plugin_api/plugin_command.rs b/zellij-utils/src/plugin_api/plugin_command.rs index f83db2e35..66b589551 100644 --- a/zellij-utils/src/plugin_api/plugin_command.rs +++ b/zellij-utils/src/plugin_api/plugin_command.rs @@ -121,7 +121,7 @@ pub use super::generated_api::api::{ SaveSessionResponse as ProtobufSaveSessionResponse, ScrollDownInPaneIdPayload, ScrollToBottomInPaneIdPayload, ScrollToTopInPaneIdPayload, ScrollUpInPaneIdPayload, SessionListSnapshot as ProtobufSessionListSnapshot, SetFloatingPanePinnedPayload, - SetPaneBorderlessPayload, SetPaneColorPayload, + SetPaneBorderStylePayload, SetPaneBorderlessPayload, SetPaneColorPayload, SetPaneFrameStylePayload as ProtobufSetPaneFrameStylePayload, SetPaneRegexHighlightsPayload, SetSelfMouseSelectionSupportPayload, SetSoftKeyboardPayload as ProtobufSetSoftKeyboardPayload, SetTimeoutPayload, @@ -147,6 +147,7 @@ use crate::data::{ use crate::input::actions::Action; use crate::input::layout::PercentOrFixed; +use crate::data::BorderStyleOverride; use std::collections::BTreeMap; use std::convert::TryFrom; use std::path::PathBuf; @@ -200,6 +201,7 @@ impl Into for ProtobufFloatingPaneCoordinates { }), pinned: self.pinned, borderless: self.borderless, + border_style: self.border_style.map(BorderStyleOverride::from), } } } @@ -253,6 +255,7 @@ impl Into for FloatingPaneCoordinates { }, pinned: self.pinned, borderless: self.borderless, + border_style: self.border_style.map(|b| b.into()), } } } @@ -2106,6 +2109,19 @@ impl TryFrom for PluginCommand { }, _ => Err("Mismatched payload for SetPaneBorderless"), }, + Some(CommandName::SetPaneBorderStyle) => match protobuf_plugin_command.payload { + Some(Payload::SetPaneBorderStylePayload(payload)) => match payload.pane_id { + Some(pane_id) => Ok(PluginCommand::SetPaneBorderStyle( + pane_id.try_into()?, + payload + .border_style + .map(BorderStyleOverride::from) + .unwrap_or_default(), + )), + _ => Err("Malformed SetPaneBorderStyle payload"), + }, + _ => Err("Mismatched payload for SetPaneBorderStyle"), + }, Some(CommandName::OpenCommandPaneNearPlugin) => match protobuf_plugin_command.payload { Some(Payload::OpenCommandPaneNearPluginPayload(command_to_run_payload)) => { match command_to_run_payload.command_to_run { @@ -3955,6 +3971,15 @@ impl TryFrom for ProtobufPluginCommand { }, )), }), + PluginCommand::SetPaneBorderStyle(pane_id, border_style) => Ok(ProtobufPluginCommand { + name: CommandName::SetPaneBorderStyle as i32, + payload: Some(Payload::SetPaneBorderStylePayload( + SetPaneBorderStylePayload { + pane_id: Some(pane_id.try_into()?), + border_style: Some(border_style.into()), + }, + )), + }), PluginCommand::OpenCommandPaneNearPlugin(command_to_run, context) => { let context: Vec<_> = context .into_iter() @@ -5250,6 +5275,109 @@ mod tests { use super::*; use crate::data::PluginCommand; + #[test] + fn set_pane_border_style_protobuf_round_trip() { + use crate::data::{BorderStyleOverride, LineStyle, PaneId}; + let border_style = BorderStyleOverride { + all: Some(LineStyle::Double), + top: Some(LineStyle::Heavy), + right: Some(LineStyle::Dashed), + bottom: Some(LineStyle::HeavyDashed), + left: Some(LineStyle::Single), + rounded_corners: Some(true), + }; + let original = PluginCommand::SetPaneBorderStyle(PaneId::Plugin(3), border_style); + let protobuf: ProtobufPluginCommand = original.try_into().expect("encode"); + let decoded: PluginCommand = protobuf.try_into().expect("decode"); + match decoded { + PluginCommand::SetPaneBorderStyle(pane_id, decoded_border_style) => { + assert_eq!(pane_id, PaneId::Plugin(3)); + assert_eq!(decoded_border_style, border_style); + }, + other => panic!("expected SetPaneBorderStyle, got {:?}", other), + } + } + + #[test] + fn an_empty_pane_border_style_round_trips() { + use crate::data::{BorderStyleOverride, PaneId}; + let original = + PluginCommand::SetPaneBorderStyle(PaneId::Terminal(1), BorderStyleOverride::default()); + let protobuf: ProtobufPluginCommand = original.try_into().expect("encode"); + let decoded: PluginCommand = protobuf.try_into().expect("decode"); + match decoded { + PluginCommand::SetPaneBorderStyle(pane_id, border_style) => { + assert_eq!(pane_id, PaneId::Terminal(1)); + assert!(border_style.is_empty()); + }, + other => panic!("expected SetPaneBorderStyle, got {:?}", other), + } + } + + #[test] + fn floating_pane_coordinates_carry_a_border_style_over_protobuf() { + use crate::data::{BorderStyleOverride, FileToOpen, FloatingPaneCoordinates, LineStyle}; + let border_style = BorderStyleOverride { + all: Some(LineStyle::Heavy), + ..Default::default() + }; + let coordinates = FloatingPaneCoordinates::default().with_border_style(Some(border_style)); + let original = + PluginCommand::OpenTerminalFloating(FileToOpen::new("/tmp"), Some(coordinates.clone())); + let protobuf: ProtobufPluginCommand = original.try_into().expect("encode"); + let decoded: PluginCommand = protobuf.try_into().expect("decode"); + match decoded { + PluginCommand::OpenTerminalFloating(_, decoded_coordinates) => { + assert_eq!( + decoded_coordinates.unwrap().border_style, + Some(border_style) + ); + }, + other => panic!("expected OpenTerminalFloating, got {:?}", other), + } + } + + #[test] + fn a_file_to_open_carries_a_border_style_over_protobuf() { + use crate::data::{BorderStyleOverride, FileToOpen, LineStyle}; + let border_style = BorderStyleOverride { + top: Some(LineStyle::Double), + ..Default::default() + }; + let original = + PluginCommand::OpenTerminal(FileToOpen::new("/tmp").with_border_style(border_style)); + let protobuf: ProtobufPluginCommand = original.try_into().expect("encode"); + let decoded: PluginCommand = protobuf.try_into().expect("decode"); + match decoded { + PluginCommand::OpenTerminal(file_to_open) => { + assert_eq!(file_to_open.border_style, Some(border_style)); + }, + other => panic!("expected OpenTerminal, got {:?}", other), + } + } + + #[test] + fn a_command_to_run_carries_a_border_style_over_protobuf() { + use crate::data::{BorderStyleOverride, CommandToRun, LineStyle}; + let border_style = BorderStyleOverride { + bottom: Some(LineStyle::Dashed), + rounded_corners: Some(false), + ..Default::default() + }; + let original = PluginCommand::OpenCommandPane( + CommandToRun::new("/bin/sh").with_border_style(border_style), + BTreeMap::new(), + ); + let protobuf: ProtobufPluginCommand = original.try_into().expect("encode"); + let decoded: PluginCommand = protobuf.try_into().expect("decode"); + match decoded { + PluginCommand::OpenCommandPane(command_to_run, _) => { + assert_eq!(command_to_run.border_style, Some(border_style)); + }, + other => panic!("expected OpenCommandPane, got {:?}", other), + } + } + #[test] fn set_pane_frame_style_protobuf_round_trip() { use crate::input::options::PaneFrameStyle; diff --git a/zellij-utils/src/plugin_api/style.proto b/zellij-utils/src/plugin_api/style.proto index cfea362e5..e424235ad 100644 --- a/zellij-utils/src/plugin_api/style.proto +++ b/zellij-utils/src/plugin_api/style.proto @@ -7,6 +7,33 @@ message Style { bool rounded_corners = 2; bool hide_session_name = 3; Styling styling = 4; + BorderStyle border_style = 5; + BorderStyle floating_border_style = 6; +} + +enum LineStyle { + Single = 0; + Double = 1; + Heavy = 2; + Dashed = 3; + HeavyDashed = 4; +} + +message BorderStyle { + LineStyle top = 1; + LineStyle right = 2; + LineStyle bottom = 3; + LineStyle left = 4; + bool rounded_corners = 5; +} + +message BorderStyleOverride { + optional LineStyle all = 1; + optional LineStyle top = 2; + optional LineStyle right = 3; + optional LineStyle bottom = 4; + optional LineStyle left = 5; + optional bool rounded_corners = 6; } message Palette { diff --git a/zellij-utils/src/plugin_api/style.rs b/zellij-utils/src/plugin_api/style.rs index bb1e10ec6..0d55684c6 100644 --- a/zellij-utils/src/plugin_api/style.rs +++ b/zellij-utils/src/plugin_api/style.rs @@ -1,10 +1,13 @@ use super::generated_api::api::style::{ - color::Payload as ProtobufColorPayload, Color as ProtobufColor, ColorType as ProtobufColorType, - Palette as ProtobufPalette, RgbColorPayload as ProtobufRgbColorPayload, Style as ProtobufStyle, - Styling as ProtobufStyling, ThemeHue as ProtobufThemeHue, + color::Payload as ProtobufColorPayload, BorderStyle as ProtobufBorderStyle, + BorderStyleOverride as ProtobufBorderStyleOverride, Color as ProtobufColor, + ColorType as ProtobufColorType, LineStyle as ProtobufLineStyle, Palette as ProtobufPalette, + RgbColorPayload as ProtobufRgbColorPayload, Style as ProtobufStyle, Styling as ProtobufStyling, + ThemeHue as ProtobufThemeHue, }; use crate::data::{ - MultiplayerColors, Palette, PaletteColor, Style, StyleDeclaration, Styling, ThemeHue, + BorderStyle, BorderStyleOverride, LineStyle, MultiplayerColors, Palette, PaletteColor, Style, + StyleDeclaration, Styling, ThemeHue, }; use crate::errors::prelude::*; @@ -21,6 +24,18 @@ impl TryFrom for Style { colors: s, rounded_corners: protobuf_style.rounded_corners, hide_session_name: protobuf_style.hide_session_name, + border_style: protobuf_style + .border_style + .map(BorderStyle::from) + .unwrap_or_else(|| { + BorderStyle::with_rounded_corners(protobuf_style.rounded_corners) + }), + floating_border_style: protobuf_style + .floating_border_style + .map(BorderStyle::from) + .unwrap_or_else(|| { + BorderStyle::with_rounded_corners(protobuf_style.rounded_corners) + }), }) } } @@ -36,10 +51,94 @@ impl TryFrom