use std::rc::Rc; use super::Regions; use crate::frame::Frame; use crate::geom::{Length, Linear, Size, Spec}; /// Constrain a frame with constraints. pub trait Constrain { /// Reference-count the frame and wrap it with constraints. fn constrain(self, cts: Constraints) -> Constrained>; } impl Constrain for Frame { fn constrain(self, cts: Constraints) -> Constrained> { Constrained::new(Rc::new(self), cts) } } /// Carries an item that is only valid in certain regions and the constraints /// that describe these regions. #[derive(Debug, Copy, Clone, Eq, PartialEq)] pub struct Constrained { /// The item that is only valid if the constraints are fullfilled. pub item: T, /// Constraints on regions in which the item is valid. pub cts: Constraints, } impl Constrained { /// Constrain an item with constraints. pub fn new(item: T, cts: Constraints) -> Self { Self { item, cts } } } /// Describe regions that match them. #[derive(Debug, Copy, Clone, Eq, PartialEq)] pub struct Constraints { /// The minimum available length in the region. pub min: Spec>, /// The maximum available length in the region. pub max: Spec>, /// The available length in the region. pub exact: Spec>, /// The base length of the region used for relative length resolution. pub base: Spec>, /// The expand settings of the region. pub expand: Spec, } impl Constraints { /// Create a new region constraint. pub fn new(expand: Spec) -> Self { Self { min: Spec::default(), max: Spec::default(), exact: Spec::default(), base: Spec::default(), expand, } } /// Create tight constraints for a region. pub fn tight(regions: &Regions) -> Self { Self { min: Spec::default(), max: Spec::default(), exact: regions.current.to_spec().map(Some), base: regions.base.to_spec().map(Some), expand: regions.expand, } } /// Check whether the constraints are fullfilled in a region with the given /// properties. pub fn check(&self, current: Size, base: Size, expand: Spec) -> bool { self.expand == expand && verify(self.min, current, |m, c| c.fits(m)) && verify(self.max, current, |m, c| m.fits(c)) && verify(self.exact, current, Length::approx_eq) && verify(self.base, base, Length::approx_eq) } /// Set the appropriate base constraints for linear width and height sizing. pub fn set_base_if_linear(&mut self, base: Size, sizing: Spec>) { // The full sizes need to be equal if there is a relative component in // the sizes. if sizing.x.map_or(false, |l| l.is_relative()) { self.base.x = Some(base.w); } if sizing.y.map_or(false, |l| l.is_relative()) { self.base.y = Some(base.h); } } } /// Verify a single constraint. fn verify(spec: Spec>, size: Size, f: fn(Length, Length) -> bool) -> bool { spec.zip(size).all(|&(opt, s)| opt.map_or(true, |m| f(m, s))) }