From 5d6351f48db2ce01cfb1af7eb24cccf1d469cbae Mon Sep 17 00:00:00 2001 From: LGUG2Z Date: Mon, 18 Oct 2021 10:52:08 -0700 Subject: [PATCH] feat(custom_layout): add opt width for primary col This commit adds a ColumnWidth for Column::Primary which can optionally be given as a percentage of the total work area of a monitor. The remaining columns will have their widths calculated by dividing the remaining work area space evenly. This commit also fixes a bug with the Promote command, which was not calculating the primary container index of custom layouts properly, and was also not using this value to update the focused container index at the end of the promotion handler. re #50 --- komorebi-core/src/arrangement.rs | 41 ++++++++++++++++++- komorebi-core/src/custom_layout.rs | 64 ++++++++++++++++++++++++++++-- komorebi/src/window_manager.rs | 14 ++++--- komorebi/src/workspace.rs | 10 +++-- 4 files changed, 113 insertions(+), 16 deletions(-) diff --git a/komorebi-core/src/arrangement.rs b/komorebi-core/src/arrangement.rs index eb23a726..657267c2 100644 --- a/komorebi-core/src/arrangement.rs +++ b/komorebi-core/src/arrangement.rs @@ -256,15 +256,44 @@ impl Arrangement for CustomLayout { Option::from(1) }; + #[allow(clippy::cast_possible_truncation, clippy::cast_possible_wrap)] + let primary_right = self.primary_width_percentage().map_or_else( + || area.right / self.len() as i32, + |percentage| (area.right / 100) * percentage as i32, + ); + for (idx, column) in self.iter().enumerate() { // If we are offsetting a tertiary column for which the threshold // has not yet been met, this loop should not run for that final // tertiary column if idx < self.len() - offset.unwrap_or(0) { - let column_area = self.column_area(area, idx, offset); + let column_area = if idx == 0 { + Self::column_area_with_last(self.len(), area, primary_right, None, offset) + } else { + Self::column_area_with_last( + self.len(), + area, + primary_right, + Option::from(dimensions[self.first_container_idx(idx - 1)]), + offset, + ) + }; match column { - Column::Primary | Column::Secondary(None) => { + Column::Primary(Option::Some(_)) => { + let main_column_area = if idx == 0 { + Self::main_column_area(area, primary_right, None) + } else { + Self::main_column_area( + area, + primary_right, + Option::from(dimensions[self.first_container_idx(idx - 1)]), + ) + }; + + dimensions.push(main_column_area); + } + Column::Primary(None) | Column::Secondary(None) => { dimensions.push(column_area); } Column::Secondary(Some(split)) => match split { @@ -278,6 +307,14 @@ impl Arrangement for CustomLayout { } }, Column::Tertiary(split) => { + let column_area = Self::column_area_with_last( + self.len(), + area, + primary_right, + Option::from(dimensions[self.first_container_idx(idx - 1)]), + offset, + ); + let remaining = container_count - tertiary_trigger_threshold; match split { diff --git a/komorebi-core/src/custom_layout.rs b/komorebi-core/src/custom_layout.rs index b8aa457a..79ad64d6 100644 --- a/komorebi-core/src/custom_layout.rs +++ b/komorebi-core/src/custom_layout.rs @@ -28,7 +28,7 @@ impl CustomLayout { #[must_use] pub fn primary_idx(&self) -> Option { for (i, column) in self.iter().enumerate() { - if let Column::Primary = column { + if let Column::Primary(_) = column { return Option::from(i); } } @@ -36,6 +36,18 @@ impl CustomLayout { None } + #[must_use] + pub fn primary_width_percentage(&self) -> Option { + for column in self.iter() { + if let Column::Primary(Option::Some(ColumnWidth::WidthPercentage(percentage))) = column + { + return Option::from(*percentage); + } + } + + None + } + #[must_use] pub fn is_valid(&self) -> bool { // A valid layout must have at least one column @@ -63,7 +75,7 @@ impl CustomLayout { for column in self.iter() { match column { - Column::Primary => primaries += 1, + Column::Primary(_) => primaries += 1, Column::Tertiary(_) => tertiaries += 1, Column::Secondary(_) => {} } @@ -78,7 +90,7 @@ impl CustomLayout { for (idx, column) in self.iter().enumerate() { match column { - Column::Primary | Column::Secondary(None) => { + Column::Primary(_) | Column::Secondary(None) => { count_map.insert(idx, 1); } Column::Secondary(Some(split)) => { @@ -156,16 +168,60 @@ impl CustomLayout { bottom: work_area.bottom, } } + + #[must_use] + pub fn column_area_with_last( + len: usize, + work_area: &Rect, + primary_right: i32, + last_column: Option, + offset: Option, + ) -> Rect { + let divisor = offset.map_or_else(|| len - 1, |offset| len - offset - 1); + + #[allow(clippy::cast_possible_wrap, clippy::cast_possible_truncation)] + let equal_width = (work_area.right - primary_right) / divisor as i32; + let left = last_column.map_or(work_area.left, |last| last.left + last.right); + let right = equal_width; + + Rect { + left, + top: work_area.top, + right, + bottom: work_area.bottom, + } + } + + #[must_use] + pub fn main_column_area( + work_area: &Rect, + primary_right: i32, + last_column: Option, + ) -> Rect { + let left = last_column.map_or(work_area.left, |last| last.left + last.right); + + Rect { + left, + top: work_area.top, + right: primary_right, + bottom: work_area.bottom, + } + } } #[derive(Clone, Copy, Debug, Serialize, Deserialize)] #[serde(tag = "column", content = "configuration")] pub enum Column { - Primary, + Primary(Option), Secondary(Option), Tertiary(ColumnSplit), } +#[derive(Clone, Copy, Debug, Serialize, Deserialize)] +pub enum ColumnWidth { + WidthPercentage(usize), +} + #[derive(Clone, Copy, Debug, Serialize, Deserialize)] pub enum ColumnSplit { Horizontal, diff --git a/komorebi/src/window_manager.rs b/komorebi/src/window_manager.rs index b5c79e9a..ab1864d4 100644 --- a/komorebi/src/window_manager.rs +++ b/komorebi/src/window_manager.rs @@ -1040,9 +1040,10 @@ impl WindowManager { match workspace.layout() { Layout::Default(_) => {} Layout::Custom(layout) => { - let primary_idx = layout - .primary_idx() - .ok_or_else(|| anyhow!("this custom layout does not have a primary column"))?; + let primary_idx = + layout.first_container_idx(layout.primary_idx().ok_or_else(|| { + anyhow!("this custom layout does not have a primary column") + })?); if !workspace.containers().is_empty() && primary_idx < workspace.containers().len() { @@ -1067,9 +1068,10 @@ impl WindowManager { match workspace.layout() { Layout::Default(_) => { - let primary_idx = layout - .primary_idx() - .ok_or_else(|| anyhow!("this custom layout does not have a primary column"))?; + let primary_idx = + layout.first_container_idx(layout.primary_idx().ok_or_else(|| { + anyhow!("this custom layout does not have a primary column") + })?); if !workspace.containers().is_empty() && primary_idx < workspace.containers().len() { diff --git a/komorebi/src/workspace.rs b/komorebi/src/workspace.rs index b6a357ce..b841e3ce 100644 --- a/komorebi/src/workspace.rs +++ b/komorebi/src/workspace.rs @@ -352,15 +352,17 @@ impl Workspace { let primary_idx = match self.layout() { Layout::Default(_) => 0, - Layout::Custom(layout) => layout - .primary_idx() - .ok_or_else(|| anyhow!("this custom layout does not have a primary column"))?, + Layout::Custom(layout) => layout.first_container_idx( + layout + .primary_idx() + .ok_or_else(|| anyhow!("this custom layout does not have a primary column"))?, + ), }; self.containers_mut().insert(primary_idx, container); self.resize_dimensions_mut().insert(primary_idx, resize); - self.focus_container(0); + self.focus_container(primary_idx); Ok(()) }