constrained_box.rs

  1use json::ToJson;
  2use serde_json::json;
  3
  4use crate::{
  5    geometry::{rect::RectF, vector::Vector2F},
  6    json, DebugContext, Element, ElementBox, Event, EventContext, LayoutContext, PaintContext,
  7    SizeConstraint,
  8};
  9
 10pub struct ConstrainedBox {
 11    child: ElementBox,
 12    constraint: Constraint,
 13}
 14
 15pub enum Constraint {
 16    Static(SizeConstraint),
 17    Dynamic(Box<dyn FnMut(SizeConstraint, &mut LayoutContext) -> SizeConstraint>),
 18}
 19
 20impl ToJson for Constraint {
 21    fn to_json(&self) -> serde_json::Value {
 22        match self {
 23            Constraint::Static(constraint) => constraint.to_json(),
 24            Constraint::Dynamic(_) => "dynamic".into(),
 25        }
 26    }
 27}
 28
 29impl ConstrainedBox {
 30    pub fn new(child: ElementBox) -> Self {
 31        Self {
 32            child,
 33            constraint: Constraint::Static(Default::default()),
 34        }
 35    }
 36
 37    pub fn dynamically(
 38        mut self,
 39        constraint: impl 'static + FnMut(SizeConstraint, &mut LayoutContext) -> SizeConstraint,
 40    ) -> Self {
 41        self.constraint = Constraint::Dynamic(Box::new(constraint));
 42        self
 43    }
 44
 45    pub fn with_min_width(mut self, min_width: f32) -> Self {
 46        if let Constraint::Dynamic(_) = self.constraint {
 47            self.constraint = Constraint::Static(Default::default());
 48        }
 49
 50        if let Constraint::Static(constraint) = &mut self.constraint {
 51            constraint.min.set_x(min_width);
 52        } else {
 53            unreachable!()
 54        }
 55
 56        self
 57    }
 58
 59    pub fn with_max_width(mut self, max_width: f32) -> Self {
 60        if let Constraint::Dynamic(_) = self.constraint {
 61            self.constraint = Constraint::Static(Default::default());
 62        }
 63
 64        if let Constraint::Static(constraint) = &mut self.constraint {
 65            constraint.max.set_x(max_width);
 66        } else {
 67            unreachable!()
 68        }
 69
 70        self
 71    }
 72
 73    pub fn with_max_height(mut self, max_height: f32) -> Self {
 74        if let Constraint::Dynamic(_) = self.constraint {
 75            self.constraint = Constraint::Static(Default::default());
 76        }
 77
 78        if let Constraint::Static(constraint) = &mut self.constraint {
 79            constraint.max.set_y(max_height);
 80        } else {
 81            unreachable!()
 82        }
 83
 84        self
 85    }
 86
 87    pub fn with_width(mut self, width: f32) -> Self {
 88        if let Constraint::Dynamic(_) = self.constraint {
 89            self.constraint = Constraint::Static(Default::default());
 90        }
 91
 92        if let Constraint::Static(constraint) = &mut self.constraint {
 93            constraint.min.set_x(width);
 94            constraint.max.set_x(width);
 95        } else {
 96            unreachable!()
 97        }
 98
 99        self
100    }
101
102    pub fn with_height(mut self, height: f32) -> Self {
103        if let Constraint::Dynamic(_) = self.constraint {
104            self.constraint = Constraint::Static(Default::default());
105        }
106
107        if let Constraint::Static(constraint) = &mut self.constraint {
108            constraint.min.set_y(height);
109            constraint.max.set_y(height);
110        } else {
111            unreachable!()
112        }
113
114        self
115    }
116
117    fn constraint(
118        &mut self,
119        input_constraint: SizeConstraint,
120        cx: &mut LayoutContext,
121    ) -> SizeConstraint {
122        match &mut self.constraint {
123            Constraint::Static(constraint) => *constraint,
124            Constraint::Dynamic(compute_constraint) => compute_constraint(input_constraint, cx),
125        }
126    }
127}
128
129impl Element for ConstrainedBox {
130    type LayoutState = ();
131    type PaintState = ();
132
133    fn layout(
134        &mut self,
135        mut parent_constraint: SizeConstraint,
136        cx: &mut LayoutContext,
137    ) -> (Vector2F, Self::LayoutState) {
138        let constraint = self.constraint(parent_constraint, cx);
139        parent_constraint.min = parent_constraint.min.max(constraint.min);
140        parent_constraint.max = parent_constraint.max.min(constraint.max);
141        parent_constraint.max = parent_constraint.max.max(parent_constraint.min);
142        let size = self.child.layout(parent_constraint, cx);
143        (size, ())
144    }
145
146    fn paint(
147        &mut self,
148        bounds: RectF,
149        visible_bounds: RectF,
150        _: &mut Self::LayoutState,
151        cx: &mut PaintContext,
152    ) -> Self::PaintState {
153        self.child.paint(bounds.origin(), visible_bounds, cx);
154    }
155
156    fn dispatch_event(
157        &mut self,
158        event: &Event,
159        _: RectF,
160        _: RectF,
161        _: &mut Self::LayoutState,
162        _: &mut Self::PaintState,
163        cx: &mut EventContext,
164    ) -> bool {
165        self.child.dispatch_event(event, cx)
166    }
167
168    fn debug(
169        &self,
170        _: RectF,
171        _: &Self::LayoutState,
172        _: &Self::PaintState,
173        cx: &DebugContext,
174    ) -> json::Value {
175        json!({"type": "ConstrainedBox", "assigned_constraint": self.constraint.to_json(), "child": self.child.debug(cx)})
176    }
177}