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