summaryrefslogtreecommitdiffstats
path: root/src/tuine/constraints.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/tuine/constraints.rs')
-rw-r--r--src/tuine/constraints.rs154
1 files changed, 154 insertions, 0 deletions
diff --git a/src/tuine/constraints.rs b/src/tuine/constraints.rs
new file mode 100644
index 00000000..a05462be
--- /dev/null
+++ b/src/tuine/constraints.rs
@@ -0,0 +1,154 @@
+use tui::layout::{Direction, Rect};
+
+use crate::canvas::LayoutConstraint;
+
+pub(super) fn get_constraints(
+ direction: Direction, constraints: &[LayoutConstraint], area: Rect,
+) -> Vec<Rect> {
+ // Order of operations:
+ // - Ratios first + canvas-handled (which is just zero)
+ // - Then any flex-grows to take up remaining space; divide amongst remaining
+ // hand out any remaining space
+
+ #[derive(Debug, Default, Clone, Copy)]
+ struct Size {
+ width: u16,
+ height: u16,
+ }
+
+ impl Size {
+ fn shrink_width(&mut self, amount: u16) {
+ self.width -= amount;
+ }
+
+ fn shrink_height(&mut self, amount: u16) {
+ self.height -= amount;
+ }
+ }
+
+ let mut bounds = Size {
+ width: area.width,
+ height: area.height,
+ };
+ let mut sizes = vec![Size::default(); constraints.len()];
+ let mut grow = vec![];
+ let mut num_non_ch = 0;
+
+ for (itx, (constraint, size)) in constraints.iter().zip(sizes.iter_mut()).enumerate() {
+ match constraint {
+ LayoutConstraint::Ratio(a, b) => {
+ match direction {
+ Direction::Horizontal => {
+ let amount = (((area.width as u32) * (*a)) / (*b)) as u16;
+ bounds.shrink_width(amount);
+ size.width = amount;
+ size.height = area.height;
+ }
+ Direction::Vertical => {
+ let amount = (((area.height as u32) * (*a)) / (*b)) as u16;
+ bounds.shrink_height(amount);
+ size.width = area.width;
+ size.height = amount;
+ }
+ }
+ num_non_ch += 1;
+ }
+ LayoutConstraint::Grow => {
+ // Mark it as grow in the vector and handle in second pass.
+ grow.push(itx);
+ num_non_ch += 1;
+ }
+ LayoutConstraint::CanvasHandled => {
+ // Do nothing in this case. It's already 0.
+ }
+ }
+ }
+
+ if !grow.is_empty() {
+ match direction {
+ Direction::Horizontal => {
+ let width = bounds.width / grow.len() as u16;
+ bounds.shrink_width(width * grow.len() as u16);
+ for g in grow {
+ sizes[g] = Size {
+ width,
+ height: area.height,
+ };
+ }
+ }
+ Direction::Vertical => {
+ let height = bounds.height / grow.len() as u16;
+ bounds.shrink_height(height * grow.len() as u16);
+ for g in grow {
+ sizes[g] = Size {
+ width: area.width,
+ height,
+ };
+ }
+ }
+ }
+ }
+
+ if num_non_ch > 0 {
+ match direction {
+ Direction::Horizontal => {
+ let per_item = bounds.width / num_non_ch;
+ let mut remaining_width = bounds.width % num_non_ch;
+ for (size, constraint) in sizes.iter_mut().zip(constraints) {
+ match constraint {
+ LayoutConstraint::CanvasHandled => {}
+ LayoutConstraint::Grow | LayoutConstraint::Ratio(_, _) => {
+ if remaining_width > 0 {
+ size.width += per_item + 1;
+ remaining_width -= 1;
+ } else {
+ size.width += per_item;
+ }
+ }
+ }
+ }
+ }
+ Direction::Vertical => {
+ let per_item = bounds.height / num_non_ch;
+ let mut remaining_height = bounds.height % num_non_ch;
+ for (size, constraint) in sizes.iter_mut().zip(constraints) {
+ match constraint {
+ LayoutConstraint::CanvasHandled => {}
+ LayoutConstraint::Grow | LayoutConstraint::Ratio(_, _) => {
+ if remaining_height > 0 {
+ size.height += per_item + 1;
+ remaining_height -= 1;
+ } else {
+ size.height += per_item;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ let mut curr_x = area.x;
+ let mut curr_y = area.y;
+ sizes
+ .into_iter()
+ .map(|size| {
+ let rect = Rect::new(curr_x, curr_y, size.width, size.height);
+ match direction {
+ Direction::Horizontal => {
+ curr_x += size.width;
+ }
+ Direction::Vertical => {
+ curr_y += size.height;
+ }
+ }
+
+ rect
+ })
+ .collect()
+}
+
+#[cfg(test)]
+mod test {
+ // TODO: Add some tests.
+}