Skip to content

Commit

Permalink
feat(custom_layout): add opt width for primary col
Browse files Browse the repository at this point in the history
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
  • Loading branch information
LGUG2Z committed Oct 21, 2021
1 parent ac0f33f commit 5d6351f
Show file tree
Hide file tree
Showing 4 changed files with 113 additions and 16 deletions.
41 changes: 39 additions & 2 deletions komorebi-core/src/arrangement.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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 {
Expand Down
64 changes: 60 additions & 4 deletions komorebi-core/src/custom_layout.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,14 +28,26 @@ impl CustomLayout {
#[must_use]
pub fn primary_idx(&self) -> Option<usize> {
for (i, column) in self.iter().enumerate() {
if let Column::Primary = column {
if let Column::Primary(_) = column {
return Option::from(i);
}
}

None
}

#[must_use]
pub fn primary_width_percentage(&self) -> Option<usize> {
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
Expand Down Expand Up @@ -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(_) => {}
}
Expand All @@ -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)) => {
Expand Down Expand Up @@ -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<Rect>,
offset: Option<usize>,
) -> 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>,
) -> 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<ColumnWidth>),
Secondary(Option<ColumnSplitWithCapacity>),
Tertiary(ColumnSplit),
}

#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
pub enum ColumnWidth {
WidthPercentage(usize),
}

#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
pub enum ColumnSplit {
Horizontal,
Expand Down
14 changes: 8 additions & 6 deletions komorebi/src/window_manager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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()
{
Expand All @@ -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()
{
Expand Down
10 changes: 6 additions & 4 deletions komorebi/src/workspace.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(())
}
Expand Down

0 comments on commit 5d6351f

Please sign in to comment.