limn_layout/
constraint.rs

1use cassowary::{Variable, Constraint, Term, Expression};
2use cassowary::WeightedRelation::*;
3use cassowary::strength::*;
4
5use euclid::{TypedPoint2D, TypedSize2D};
6
7use super::{LAYOUT, LayoutRef, LayoutVars, Size, Point};
8
9pub fn width(width: f32) -> WidgetConstraintBuilder {
10    WidgetConstraint::Width(width).builder(REQUIRED)
11}
12pub fn height(height: f32) -> WidgetConstraintBuilder {
13    WidgetConstraint::Height(height).builder(REQUIRED)
14}
15pub fn min_width(width: f32) -> WidgetConstraintBuilder {
16    WidgetConstraint::MinWidth(width).builder(REQUIRED)
17}
18pub fn min_height(height: f32) -> WidgetConstraintBuilder {
19    WidgetConstraint::MinHeight(height).builder(REQUIRED)
20}
21pub fn size<T>(size: TypedSize2D<f32, T>) -> WidgetConstraintBuilder {
22    WidgetConstraint::Size(size.to_untyped()).builder(REQUIRED)
23}
24pub fn min_size<T>(size: TypedSize2D<f32, T>) -> WidgetConstraintBuilder {
25    WidgetConstraint::MinSize(size.to_untyped()).builder(REQUIRED)
26}
27pub fn aspect_ratio(aspect_ratio: f32) -> WidgetConstraintBuilder {
28    WidgetConstraint::AspectRatio(aspect_ratio).builder(REQUIRED)
29}
30pub fn shrink() -> WidgetConstraintBuilder {
31    WidgetConstraint::Shrink.builder(WEAK)
32}
33pub fn shrink_horizontal() -> WidgetConstraintBuilder {
34    WidgetConstraint::ShrinkHorizontal.builder(WEAK)
35}
36pub fn shrink_vertical() -> WidgetConstraintBuilder {
37    WidgetConstraint::ShrinkVertical.builder(WEAK)
38}
39pub fn top_left<T>(point: TypedPoint2D<f32, T>) -> WidgetConstraintBuilder {
40    WidgetConstraint::TopLeft(point.to_untyped()).builder(REQUIRED)
41}
42pub fn center<T: LayoutRef>(widget: &T) -> WidgetConstraintBuilder {
43    WidgetConstraint::Center(widget.layout_ref().clone()).builder(REQUIRED)
44}
45pub fn center_horizontal<T: LayoutRef>(widget: &T) -> WidgetConstraintBuilder {
46    let widget = widget.layout_ref();
47    WidgetConstraint::CenterHorizontal(widget.left, widget.right).builder(REQUIRED)
48}
49pub fn center_vertical<T: LayoutRef>(widget: &T) -> WidgetConstraintBuilder {
50    let widget = widget.layout_ref();
51    WidgetConstraint::CenterVertical(widget.top, widget.bottom).builder(REQUIRED)
52}
53
54pub fn align_top<T: LayoutRef>(widget: &T) -> PaddableConstraintBuilder {
55    let widget = widget.layout_ref();
56    PaddableConstraint::AlignTop(widget.top).builder(REQUIRED)
57}
58pub fn align_bottom<T: LayoutRef>(widget: &T) -> PaddableConstraintBuilder {
59    let widget = widget.layout_ref();
60    PaddableConstraint::AlignBottom(widget.bottom).builder(REQUIRED)
61}
62pub fn align_left<T: LayoutRef>(widget: &T) -> PaddableConstraintBuilder {
63    let widget = widget.layout_ref();
64    PaddableConstraint::AlignLeft(widget.left).builder(REQUIRED)
65}
66pub fn align_right<T: LayoutRef>(widget: &T) -> PaddableConstraintBuilder {
67    let widget = widget.layout_ref();
68    PaddableConstraint::AlignRight(widget.right).builder(REQUIRED)
69}
70
71pub fn align_above<T: LayoutRef>(widget: &T) -> PaddableConstraintBuilder {
72    let widget = widget.layout_ref();
73    PaddableConstraint::AlignAbove(widget.top).builder(REQUIRED)
74}
75pub fn align_below<T: LayoutRef>(widget: &T) -> PaddableConstraintBuilder {
76    let widget = widget.layout_ref();
77    PaddableConstraint::AlignBelow(widget.bottom).builder(REQUIRED)
78}
79pub fn align_to_left_of<T: LayoutRef>(widget: &T) -> PaddableConstraintBuilder {
80    let widget = widget.layout_ref();
81    PaddableConstraint::AlignToLeftOf(widget.left).builder(REQUIRED)
82}
83pub fn align_to_right_of<T: LayoutRef>(widget: &T) -> PaddableConstraintBuilder {
84    let widget = widget.layout_ref();
85    PaddableConstraint::AlignToRightOf(widget.right).builder(REQUIRED)
86}
87
88pub fn above<T: LayoutRef>(widget: &T) -> PaddableConstraintBuilder {
89    let widget = widget.layout_ref();
90    PaddableConstraint::Above(widget.top).builder(REQUIRED)
91}
92pub fn below<T: LayoutRef>(widget: &T) -> PaddableConstraintBuilder {
93    let widget = widget.layout_ref();
94    PaddableConstraint::Below(widget.bottom).builder(REQUIRED)
95}
96pub fn to_left_of<T: LayoutRef>(widget: &T) -> PaddableConstraintBuilder {
97    let widget = widget.layout_ref();
98    PaddableConstraint::ToLeftOf(widget.left).builder(REQUIRED)
99}
100pub fn to_right_of<T: LayoutRef>(widget: &T) -> PaddableConstraintBuilder {
101    let widget = widget.layout_ref();
102    PaddableConstraint::ToRightOf(widget.right).builder(REQUIRED)
103}
104
105pub fn bound_left<T: LayoutRef>(outer: &T) -> PaddableConstraintBuilder {
106    let outer = outer.layout_ref();
107    PaddableConstraint::BoundLeft(outer.left).builder(REQUIRED)
108}
109pub fn bound_top<T: LayoutRef>(outer: &T) -> PaddableConstraintBuilder {
110    let outer = outer.layout_ref();
111    PaddableConstraint::BoundTop(outer.top).builder(REQUIRED)
112}
113pub fn bound_right<T: LayoutRef>(outer: &T) -> PaddableConstraintBuilder {
114    let outer = outer.layout_ref();
115    PaddableConstraint::BoundRight(outer.right).builder(REQUIRED)
116}
117pub fn bound_bottom<T: LayoutRef>(outer: &T) -> PaddableConstraintBuilder {
118    let outer = outer.layout_ref();
119    PaddableConstraint::BoundBottom(outer.bottom).builder(REQUIRED)
120}
121
122pub fn bound_by<T: LayoutRef>(outer: &T) -> PaddableConstraintBuilder {
123    let outer = outer.layout_ref();
124    PaddableConstraint::BoundBy(outer.clone()).builder(REQUIRED)
125}
126
127pub fn match_layout<T: LayoutRef>(widget: &T) -> PaddableConstraintBuilder {
128    let widget = widget.layout_ref();
129    PaddableConstraint::MatchLayout(widget.clone()).builder(REQUIRED)
130}
131pub fn match_width<T: LayoutRef>(widget: &T) -> PaddableConstraintBuilder {
132    let widget = widget.layout_ref();
133    PaddableConstraint::MatchWidth(widget.width).builder(REQUIRED)
134}
135pub fn match_height<T: LayoutRef>(widget: &T) -> PaddableConstraintBuilder {
136    let widget = widget.layout_ref();
137    PaddableConstraint::MatchHeight(widget.height).builder(REQUIRED)
138}
139
140#[derive(Debug, Copy, Clone)]
141pub enum WidgetConstraint {
142    Width(f32),
143    Height(f32),
144    MinWidth(f32),
145    MinHeight(f32),
146    Size(Size),
147    MinSize(Size),
148    AspectRatio(f32),
149    Shrink,
150    ShrinkHorizontal,
151    ShrinkVertical,
152    TopLeft(Point),
153    Center(LayoutVars),
154    CenterHorizontal(Variable, Variable),
155    CenterVertical(Variable, Variable),
156}
157
158#[derive(Debug, Copy, Clone)]
159pub enum PaddableConstraint {
160    AlignTop(Variable),
161    AlignBottom(Variable),
162    AlignLeft(Variable),
163    AlignRight(Variable),
164    AlignAbove(Variable),
165    AlignBelow(Variable),
166    AlignToLeftOf(Variable),
167    AlignToRightOf(Variable),
168    Above(Variable),
169    Below(Variable),
170    ToLeftOf(Variable),
171    ToRightOf(Variable),
172    BoundLeft(Variable),
173    BoundTop(Variable),
174    BoundRight(Variable),
175    BoundBottom(Variable),
176    BoundBy(LayoutVars),
177    MatchLayout(LayoutVars),
178    MatchWidth(Variable),
179    MatchHeight(Variable),
180}
181
182impl WidgetConstraint {
183    pub fn builder(self, default_strength: f64) -> WidgetConstraintBuilder {
184        WidgetConstraintBuilder {
185            constraint: self,
186            strength: default_strength,
187        }
188    }
189}
190
191impl PaddableConstraint {
192    pub fn builder(self, default_strength: f64) -> PaddableConstraintBuilder {
193        PaddableConstraintBuilder {
194            constraint: self,
195            strength: default_strength,
196            padding: 0.0,
197        }
198    }
199}
200
201#[derive(Debug, Copy, Clone)]
202pub struct WidgetConstraintBuilder {
203    constraint: WidgetConstraint,
204    strength: f64,
205}
206
207impl WidgetConstraintBuilder {
208    pub fn strength(mut self, strength: f64) -> Self {
209        self.strength = strength;
210        self
211    }
212}
213
214#[derive(Debug, Copy, Clone)]
215pub struct PaddableConstraintBuilder {
216    constraint: PaddableConstraint,
217    strength: f64,
218    padding: f32,
219}
220
221impl PaddableConstraintBuilder {
222    pub fn strength(mut self, strength: f64) -> Self {
223        self.strength = strength;
224        self
225    }
226    pub fn padding(mut self, padding: f32) -> Self {
227        self.padding = padding;
228        self
229    }
230}
231
232pub trait ConstraintBuilder {
233    fn build(&self, widget: &LayoutVars) -> Vec<Constraint>;
234}
235
236impl ConstraintBuilder for Constraint {
237    fn build(&self, widget: &LayoutVars) -> Vec<Constraint> {
238        let ref terms = self.expr().terms;
239        let mut vars_replaced = false;
240        let mut new_terms = Vec::new();
241        for term in terms {
242            let var = if term.variable == LAYOUT.left {
243                widget.left
244            } else if term.variable == LAYOUT.top {
245                widget.top
246            } else if term.variable == LAYOUT.right {
247                widget.right
248            } else if term.variable == LAYOUT.bottom {
249                widget.bottom
250            } else if term.variable == LAYOUT.width {
251                widget.width
252            } else if term.variable == LAYOUT.height {
253                widget.height
254            } else {
255                term.variable
256            };
257            if var != term.variable {
258                vars_replaced = true;
259            }
260            new_terms.push(Term {
261                variable: var,
262                coefficient: term.coefficient,
263            });
264        }
265        if vars_replaced {
266            let expr = Expression::new(new_terms, self.expr().constant);
267            let cons = Constraint::new(expr, self.op(), self.strength());
268            vec![ cons ]
269        } else {
270            // ensure hash value (from pointer) is unchanged if terms unchanged
271            vec![ self.clone() ]
272        }
273    }
274}
275
276impl ConstraintBuilder for WidgetConstraintBuilder {
277    fn build(&self, widget: &LayoutVars) -> Vec<Constraint> {
278        let strength = self.strength;
279        match self.constraint.clone() {
280            WidgetConstraint::Width(width) => {
281                vec![ widget.width | EQ(strength) | width ]
282            }
283            WidgetConstraint::Height(height) => {
284                vec![ widget.height | EQ(strength) | height ]
285            }
286            WidgetConstraint::MinWidth(width) => {
287                vec![ widget.width | GE(strength) | width ]
288            }
289            WidgetConstraint::MinHeight(height) => {
290                vec![ widget.height | GE(strength) | height ]
291            }
292            WidgetConstraint::Size(size) => {
293                vec![
294                    widget.width | EQ(strength) | size.width,
295                    widget.height | EQ(strength) | size.height,
296                ]
297            }
298            WidgetConstraint::MinSize(size) => {
299                vec![
300                    widget.width | GE(strength) | size.width,
301                    widget.height | GE(strength) | size.height,
302                ]
303            }
304            WidgetConstraint::AspectRatio(aspect_ratio) => {
305                vec![ aspect_ratio * widget.width | EQ(strength) | widget.height ]
306            }
307            WidgetConstraint::Shrink => {
308                vec![
309                    widget.width | EQ(strength) | 0.0,
310                    widget.height | EQ(strength) | 0.0,
311                ]
312            }
313            WidgetConstraint::ShrinkHorizontal => {
314                vec![ widget.width | EQ(strength) | 0.0 ]
315            }
316            WidgetConstraint::ShrinkVertical => {
317                vec![ widget.height | EQ(strength) | 0.0 ]
318            }
319            WidgetConstraint::TopLeft(point) => {
320                vec![
321                    widget.left | EQ(strength) | point.x,
322                    widget.top | EQ(strength) | point.y,
323                ]
324            }
325            WidgetConstraint::Center(other) => {
326                vec![
327                    widget.left - other.left | EQ(REQUIRED) | other.right - widget.right,
328                    widget.top - other.top | EQ(REQUIRED) | other.bottom - widget.bottom,
329                ]
330            }
331            WidgetConstraint::CenterHorizontal(left, right) => {
332                vec![ widget.left - left | EQ(REQUIRED) | right - widget.right ]
333            }
334            WidgetConstraint::CenterVertical(top, bottom) => {
335                vec![ widget.top - top | EQ(REQUIRED) | bottom - widget.bottom ]
336            }
337        }
338    }
339}
340
341impl ConstraintBuilder for PaddableConstraintBuilder {
342    fn build(&self, widget: &LayoutVars) -> Vec<Constraint> {
343        let strength = self.strength;
344        let padding = self.padding;
345        match self.constraint.clone() {
346            PaddableConstraint::AlignTop(top) => {
347                vec![ widget.top - top | EQ(strength) | padding ]
348            }
349            PaddableConstraint::AlignBottom(bottom) => {
350                vec![ bottom - widget.bottom | EQ(strength) | padding ]
351            }
352            PaddableConstraint::AlignLeft(left) => {
353                vec![ widget.left - left | EQ(strength) | padding ]
354            }
355            PaddableConstraint::AlignRight(right) => {
356                vec![ right - widget.right | EQ(strength) | padding ]
357            }
358            PaddableConstraint::AlignAbove(top) => {
359                vec![ top - widget.bottom | EQ(strength) | padding ]
360            }
361            PaddableConstraint::AlignBelow(bottom) => {
362                vec![ widget.top - bottom | EQ(strength) | padding ]
363            }
364            PaddableConstraint::AlignToLeftOf(left) => {
365                vec![ left - widget.right | EQ(strength) | padding ]
366            }
367            PaddableConstraint::AlignToRightOf(right) => {
368                vec![ widget.left - right | EQ(strength) | padding ]
369            }
370            PaddableConstraint::Above(top) => {
371                vec![ top - widget.bottom | GE(strength) | padding ]
372            }
373            PaddableConstraint::Below(bottom) => {
374                vec![ widget.top - bottom | GE(strength) | padding ]
375            }
376            PaddableConstraint::ToLeftOf(left) => {
377                vec![ left - widget.right | GE(strength) | padding ]
378            }
379            PaddableConstraint::ToRightOf(right) => {
380                vec![ widget.left - right | GE(strength) | padding ]
381            }
382            PaddableConstraint::BoundLeft(left) => {
383                vec![ widget.left - left | GE(strength) | padding ]
384            }
385            PaddableConstraint::BoundTop(top) => {
386                vec![ widget.top - top | GE(strength) | padding ]
387            }
388            PaddableConstraint::BoundRight(right) => {
389                vec![ right - widget.right | GE(strength) | padding ]
390            }
391            PaddableConstraint::BoundBottom(bottom) => {
392                vec![ bottom - widget.bottom | GE(strength) | padding ]
393            }
394            PaddableConstraint::BoundBy(other) => {
395                vec![
396                    widget.left - other.left | GE(strength) | padding,
397                    widget.top - other.top | GE(strength) | padding,
398                    other.right - widget.right | GE(strength) | padding,
399                    other.bottom - widget.bottom | GE(strength) | padding,
400                ]
401            }
402            PaddableConstraint::MatchLayout(other) => {
403                vec![
404                    widget.left - other.left | EQ(strength) | padding,
405                    widget.top - other.top | EQ(strength) | padding,
406                    other.right - widget.right | EQ(strength) | padding,
407                    other.bottom - widget.bottom | EQ(strength) | padding,
408                ]
409            }
410            PaddableConstraint::MatchWidth(width) => {
411                vec![ width - widget.width | EQ(strength) | padding ]
412            }
413            PaddableConstraint::MatchHeight(height) => {
414                vec![ height - widget.height | EQ(strength) | padding ]
415            }
416        }
417    }
418}
419
420impl <C: ConstraintBuilder> ConstraintBuilder for Vec<C> {
421    fn build(&self, widget: &LayoutVars) -> Vec<Constraint> {
422        let mut constraints = Vec::new();
423        for builder in self {
424            constraints.extend(builder.build(widget));
425        }
426        constraints
427    }
428}
429
430impl ConstraintBuilder for Box<ConstraintBuilder> {
431    fn build(&self, widget: &LayoutVars) -> Vec<Constraint> {
432        self.as_ref().build(widget)
433    }
434}