diff options
author | Aram Drevekenin <aram@poor.dev> | 2022-12-24 15:48:04 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-12-24 15:48:04 +0100 |
commit | 799fa5de8dfd0b826be0f903796bba48a470127e (patch) | |
tree | 8c202e116b757162cadd0cc350da256b9741e248 /zellij-utils | |
parent | 17205793e4cd2da5eb431dacfc87dab574080b5b (diff) |
Floating panes in layouts (#2047)
* work
* tests passing
* tests: floating panes in layouts
* panes(plugins): floating plugins working
* refactor(tab): layout applier
* style(comment): remove outdated
* style(fmt): rustfmt
Diffstat (limited to 'zellij-utils')
50 files changed, 1177 insertions, 178 deletions
diff --git a/zellij-utils/src/input/actions.rs b/zellij-utils/src/input/actions.rs index e1a45bf5c..7bb13540c 100644 --- a/zellij-utils/src/input/actions.rs +++ b/zellij-utils/src/input/actions.rs @@ -1,7 +1,7 @@ //! Definition of the actions that can be bound to keys. use super::command::RunCommandAction; -use super::layout::{Layout, PaneLayout}; +use super::layout::{FloatingPanesLayout, Layout, PaneLayout}; use crate::cli::CliAction; use crate::data::InputMode; use crate::data::{Direction, Resize}; @@ -162,7 +162,7 @@ pub enum Action { PaneNameInput(Vec<u8>), UndoRenamePane, /// Create a new tab, optionally with a specified tab layout. - NewTab(Option<PaneLayout>, Option<String>), // the String is the tab name + NewTab(Option<PaneLayout>, Vec<FloatingPanesLayout>, Option<String>), // the String is the tab name /// Do nothing. NoOp, /// Go to the next tab. @@ -368,15 +368,24 @@ impl Action { if tabs.len() > 1 { return Err(format!("Tab layout cannot itself have tabs")); } else if !tabs.is_empty() { - let (tab_name, layout) = tabs.drain(..).next().unwrap(); + let (tab_name, layout, floating_panes_layout) = + tabs.drain(..).next().unwrap(); let name = tab_name.or(name); - Ok(vec![Action::NewTab(Some(layout), name)]) + Ok(vec![Action::NewTab( + Some(layout), + floating_panes_layout, + name, + )]) } else { - let layout = layout.new_tab(); - Ok(vec![Action::NewTab(Some(layout), name)]) + let (layout, floating_panes_layout) = layout.new_tab(); + Ok(vec![Action::NewTab( + Some(layout), + floating_panes_layout, + name, + )]) } } else { - Ok(vec![Action::NewTab(None, name)]) + Ok(vec![Action::NewTab(None, vec![], name)]) } }, } diff --git a/zellij-utils/src/input/layout.rs b/zellij-utils/src/input/layout.rs index 6c4a222cc..eaa2e3950 100644 --- a/zellij-utils/src/input/layout.rs +++ b/zellij-utils/src/input/layout.rs @@ -213,9 +213,94 @@ impl fmt::Display for RunPluginLocation { #[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq, Default)] pub struct Layout { - pub tabs: Vec<(Option<String>, PaneLayout)>, + pub tabs: Vec<(Option<String>, PaneLayout, Vec<FloatingPanesLayout>)>, pub focused_tab_index: Option<usize>, pub template: Option<PaneLayout>, + pub floating_panes_template: Vec<FloatingPanesLayout>, +} + +#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)] +pub enum PercentOrFixed { + Percent(usize), // 1 to 100 + Fixed(usize), // An absolute number of columns or rows +} + +impl PercentOrFixed { + pub fn to_position(&self, whole: usize) -> usize { + match self { + PercentOrFixed::Percent(percent) => { + (whole as f64 / 100.0 * *percent as f64).ceil() as usize + }, + PercentOrFixed::Fixed(fixed) => { + if *fixed > whole { + whole + } else { + *fixed + } + }, + } + } +} + +impl PercentOrFixed { + pub fn is_zero(&self) -> bool { + match self { + PercentOrFixed::Percent(percent) => *percent == 0, + PercentOrFixed::Fixed(fixed) => *fixed == 0, + } + } +} + +impl FromStr for PercentOrFixed { + type Err = Box<dyn std::error::Error>; + fn from_str(s: &str) -> Result<Self, Self::Err> { + if s.chars().last() == Some('%') { + let char_count = s.chars().count(); + let percent_size = usize::from_str_radix(&s[..char_count.saturating_sub(1)], 10)?; + if percent_size <= 100 { + Ok(PercentOrFixed::Percent(percent_size)) + } else { + Err("Percent must be between 0 and 100".into()) + } + } else { + let fixed_size = usize::from_str_radix(s, 10)?; + Ok(PercentOrFixed::Fixed(fixed_size)) + } + } +} + +#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq, Default)] +pub struct FloatingPanesLayout { + // TODO: change name to singular + pub name: Option<String>, + pub height: Option<PercentOrFixed>, + pub width: Option<PercentOrFixed>, + pub x: Option<PercentOrFixed>, + pub y: Option<PercentOrFixed>, + pub run: Option<Run>, + pub focus: Option<bool>, +} + +impl FloatingPanesLayout { + pub fn add_cwd_to_layout(&mut self, cwd: &PathBuf) { + match self.run.as_mut() { + Some(run) => run.add_cwd(cwd), + None => { + self.run = Some(Run::Cwd(cwd.clone())); + }, + } + } +} + +impl From<&PaneLayout> for FloatingPanesLayout { + fn from(pane_layout: &PaneLayout) -> Self { + FloatingPanesLayout { + name: pane_layout.name.clone(), + run: pane_layout.run.clone(), + focus: pane_layout.focus, + ..Default::default() + } + } } #[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq, Default)] @@ -276,6 +361,8 @@ impl PaneLayout { Ok(layouts) } pub fn extract_run_instructions(&self) -> Vec<Option<Run>> { + // the order of these run instructions is significant and needs to be the same + // as the order of the "flattened" layout panes received from eg. position_panes_in_space let mut run_instructions = vec![]; if self.children.is_empty() { run_instructions.push(self.run.clone()); @@ -452,11 +539,12 @@ impl Layout { Ok(String::from_utf8(setup::COMPACT_BAR_LAYOUT.to_vec())?) } - pub fn new_tab(&self) -> PaneLayout { - match &self.template { + pub fn new_tab(&self) -> (PaneLayout, Vec<FloatingPanesLayout>) { + let template = match &self.template { Some(template) => template.clone(), None => PaneLayout::default(), - } + }; + (template, self.floating_panes_template.clone()) } pub fn is_empty(&self) -> bool { @@ -467,7 +555,7 @@ impl Layout { !self.tabs.is_empty() } - pub fn tabs(&self) -> Vec<(Option<String>, PaneLayout)> { + pub fn tabs(&self) -> Vec<(Option<String>, PaneLayout, Vec<FloatingPanesLayout>)> { // String is the tab name self.tabs.clone() } diff --git a/zellij-utils/src/input/unit/layout_test.rs b/zellij-utils/src/input/unit/layout_test.rs index f11c86869..d588fcda6 100644 --- a/zellij-utils/src/input/unit/layout_test.rs +++ b/zellij-utils/src/input/unit/layout_test.rs @@ -90,6 +90,175 @@ fn layout_with_nested_panes() { } #[test] +fn layout_with_floating_panes() { + let kdl_layout = r#" + layout { + floating_panes { + pane + pane { + x 10 + y "10%" + width 10 + height "10%" + } + pane x=10 y="10%" + pane command="htop" + } + } + "#; + let layout = Layout::from_kdl(kdl_layout, "layout_file_name".into(), None).unwrap(); + let expected_layout = Layout { + template: Some(PaneLayout::default()), + floating_panes_template: vec![ + FloatingPanesLayout::default(), + FloatingPanesLayout { + x: Some(PercentOrFixed::Fixed(10)), + y: Some(PercentOrFixed::Percent(10)), + width: Some(PercentOrFixed::Fixed(10)), + height: Some(PercentOrFixed::Percent(10)), + ..Default::default() + }, + FloatingPanesLayout { + x: Some(PercentOrFixed::Fixed(10)), + y: Some(PercentOrFixed::Percent(10)), + ..Default::default() + }, + FloatingPanesLayout { + run: Some(Run::Command(RunCommand { + command: PathBuf::from("htop"), + hold_on_close: true, |