constrained_box.rs

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