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