blizz-ui 3.0.0-dev.13

Self-rendering terminal UI components for the blizz wizard
Documentation
use crate::layout;

/// Compositor-controlled positioning for a component.
///
/// The panel IS the position: components render at `(column, row)`,
/// using at most `width` columns. Components never compute their own
/// centering — the compositor provides the panel.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct LayoutPanel {
  pub row: u16,
  pub column: u16,
  pub width: u16,
}

impl LayoutPanel {
  /// Horizontally centered panel for content of `content_width` at `row`.
  pub fn centered(terminal_width: u16, content_width: u16, row: u16) -> Self {
    let column = layout::centered_column(terminal_width, content_width);
    Self {
      row,
      column,
      width: content_width,
    }
  }

  /// New panel offset downward by `rows`, keeping column and width.
  pub fn below(self, rows: u16) -> Self {
    Self {
      row: self.row.saturating_add(rows),
      ..self
    }
  }
}

#[cfg(test)]
mod tests {
  use super::*;

  #[test]
  fn centered_places_panel_at_correct_column() {
    let panel = LayoutPanel::centered(80, 20, 5);
    assert_eq!(panel.row, 5);
    assert_eq!(panel.column, 30);
    assert_eq!(panel.width, 20);
  }

  #[test]
  fn below_offsets_row_preserving_column_and_width() {
    let panel = LayoutPanel {
      row: 3,
      column: 10,
      width: 40,
    };
    let shifted = panel.below(5);
    assert_eq!(shifted.row, 8);
    assert_eq!(shifted.column, 10);
    assert_eq!(shifted.width, 40);
  }

  #[test]
  fn below_saturates_at_max() {
    let panel = LayoutPanel {
      row: u16::MAX - 1,
      column: 0,
      width: 10,
    };
    let shifted = panel.below(5);
    assert_eq!(shifted.row, u16::MAX);
  }
}