use crate::layout::constraints::Constraint;
const RATIO_EPSILON: f32 = 0.001;
#[derive(Debug, Clone)]
pub struct Panel {
pub id: String,
pub ratio: f32,
pub constraint: Constraint,
}
impl Panel {
pub fn new(id: impl Into<String>, ratio: f32) -> Self {
Self { id: id.into(), ratio, constraint: Constraint::default() }
}
pub fn with_constraint(mut self, min: f32, max: f32) -> Self {
self.constraint = Constraint::new(min, max);
self
}
}
pub fn drag(panels: &mut [Panel], handle_idx: usize, delta: f32) -> f32 {
assert!(handle_idx + 1 < panels.len(), "handle_idx out of bounds");
let left = handle_idx;
let right = handle_idx + 1;
let transfer = delta.clamp(
panels[left].constraint.min - panels[left].ratio, panels[right].ratio - panels[right].constraint.min, );
let transfer = transfer.min(panels[left].constraint.max - panels[left].ratio);
let transfer = transfer.max(-(panels[right].constraint.max - panels[right].ratio));
panels[left].ratio += transfer;
panels[right].ratio -= transfer;
transfer
}
pub fn normalize(panels: &mut [Panel]) {
let total: f32 = panels.iter().map(|p| p.ratio).sum();
if total > 0.0 {
for p in panels.iter_mut() {
p.ratio /= total;
}
}
}
pub fn check_sum(panels: &[Panel]) -> bool {
let total: f32 = panels.iter().map(|p| p.ratio).sum();
(total - 1.0).abs() < RATIO_EPSILON
}
#[cfg(test)]
mod tests {
use super::*;
fn two_panels() -> Vec<Panel> {
vec![
Panel::new("left", 0.5),
Panel::new("right", 0.5),
]
}
fn three_panels() -> Vec<Panel> {
vec![
Panel::new("a", 0.33),
Panel::new("b", 0.34),
Panel::new("c", 0.33),
]
}
#[test]
fn drag_transfers_ratio() {
let mut panels = two_panels();
drag(&mut panels, 0, 0.1);
assert!((panels[0].ratio - 0.6).abs() < 0.001);
assert!((panels[1].ratio - 0.4).abs() < 0.001);
}
#[test]
fn sum_preserved_after_drag() {
let mut panels = three_panels();
drag(&mut panels, 0, 0.1);
assert!(check_sum(&panels), "sum not 1.0 after drag");
drag(&mut panels, 1, -0.05);
assert!(check_sum(&panels), "sum not 1.0 after second drag");
}
#[test]
fn drag_respects_min_constraint() {
let mut panels = vec![
Panel::new("left", 0.1).with_constraint(0.1, 0.9),
Panel::new("right", 0.9).with_constraint(0.1, 0.9),
];
drag(&mut panels, 0, -0.2);
assert!(panels[0].ratio >= 0.1 - 0.001, "left went below min");
}
#[test]
fn drag_respects_max_constraint() {
let mut panels = vec![
Panel::new("left", 0.5).with_constraint(0.1, 0.6),
Panel::new("right", 0.5).with_constraint(0.1, 0.9),
];
drag(&mut panels, 0, 0.3);
assert!(panels[0].ratio <= 0.6 + 0.001, "left exceeded max");
}
#[test]
fn normalize_makes_sum_one() {
let mut panels = vec![
Panel::new("a", 1.0),
Panel::new("b", 2.0),
Panel::new("c", 1.0),
];
normalize(&mut panels);
assert!(check_sum(&panels));
assert!((panels[1].ratio - 0.5).abs() < 0.001);
}
}