limn_layout/
lib.rs

1// ---- START CLIPPY CONFIG
2
3#![cfg_attr(all(not(test), feature="clippy"), warn(result_unwrap_used))]
4#![cfg_attr(feature="clippy", warn(unseparated_literal_suffix))]
5#![cfg_attr(feature="clippy", warn(wrong_pub_self_convention))]
6
7// Enable clippy if our Cargo.toml file asked us to do so.
8#![cfg_attr(feature="clippy", feature(plugin))]
9#![cfg_attr(feature="clippy", plugin(clippy))]
10
11#![warn(missing_copy_implementations,
12        trivial_numeric_casts,
13        trivial_casts,
14        unused_extern_crates,
15        unused_import_braces,
16        unused_qualifications)]
17#![cfg_attr(feature="clippy", warn(cast_possible_truncation))]
18#![cfg_attr(feature="clippy", warn(cast_possible_wrap))]
19#![cfg_attr(feature="clippy", warn(cast_precision_loss))]
20#![cfg_attr(feature="clippy", warn(cast_sign_loss))]
21#![cfg_attr(feature="clippy", warn(missing_docs_in_private_items))]
22#![cfg_attr(feature="clippy", warn(mut_mut))]
23
24// Disallow `println!`. Use `debug!` for debug output
25// (which is provided by the `log` crate).
26#![cfg_attr(feature="clippy", warn(print_stdout))]
27
28// This allows us to use `unwrap` on `Option` values (because doing makes
29// working with Regex matches much nicer) and when compiling in test mode
30// (because using it in tests is idiomatic).
31#![cfg_attr(all(not(test), feature="clippy"), warn(result_unwrap_used))]
32#![cfg_attr(feature="clippy", warn(unseparated_literal_suffix))]
33#![cfg_attr(feature="clippy", warn(wrong_pub_self_convention))]
34
35// ---- END CLIPPY CONFIG
36
37#[macro_use]
38extern crate log;
39#[macro_use]
40extern crate lazy_static;
41extern crate cassowary;
42extern crate euclid;
43
44use std::collections::HashSet;
45use std::ops::Drop;
46use std::mem;
47use std::rc::Rc;
48use std::cell::RefCell;
49
50use cassowary::{Variable, Constraint};
51use cassowary::WeightedRelation::*;
52use cassowary::strength::*;
53
54use euclid::{Point2D, Size2D, UnknownUnit};
55
56use self::constraint::ConstraintBuilder;
57use self::constraint::*;
58
59pub type Length = euclid::Length<f32, UnknownUnit>;
60pub type Size = Size2D<f32>;
61pub type Point = Point2D<f32>;
62pub type Rect = euclid::Rect<f32>;
63
64pub type LayoutId = usize;
65
66/// A set of cassowary `Variable`s representing the
67/// bounding rectangle of a layout.
68#[derive(Debug, Copy, Clone)]
69pub struct LayoutVars {
70    pub left: Variable,
71    pub top: Variable,
72    pub right: Variable,
73    pub bottom: Variable,
74    pub width: Variable,
75    pub height: Variable,
76}
77
78impl LayoutVars {
79    pub fn new() -> Self {
80        LayoutVars {
81            left: Variable::new(),
82            top: Variable::new(),
83            right: Variable::new(),
84            bottom: Variable::new(),
85            width: Variable::new(),
86            height: Variable::new(),
87        }
88    }
89
90    /// Returns the current inner state of this struct as an array
91    pub fn array(&self) -> [Variable; 6] {
92        [self.left,
93         self.top,
94         self.right,
95         self.bottom,
96         self.width,
97         self.height]
98    }
99
100    /// If a `Variable` matches one of the variables in this layout, return it's type
101    pub fn var_type(&self, var: Variable) -> VarType {
102        if var == self.left { VarType::Left }
103        else if var == self.top { VarType::Top }
104        else if var == self.right { VarType::Right }
105        else if var == self.bottom { VarType::Bottom }
106        else if var == self.width { VarType::Width }
107        else if var == self.height { VarType::Height }
108        else { VarType::Other }
109    }
110}
111
112#[derive(Debug, Clone, Copy)]
113pub enum VarType {
114    Left,
115    Top,
116    Right,
117    Bottom,
118    Width,
119    Height,
120    Other,
121}
122
123pub trait LayoutRef {
124    fn layout_ref(&self) -> LayoutVars;
125}
126
127impl<'a> LayoutRef for &'a mut Layout {
128    fn layout_ref(&self) -> LayoutVars {
129        self.vars
130    }
131}
132
133impl LayoutRef for Layout {
134    fn layout_ref(&self) -> LayoutVars {
135        self.vars
136    }
137}
138
139impl LayoutRef for LayoutVars {
140    /// Returns a copy of the current `LayoutVars`
141    fn layout_ref(&self) -> LayoutVars {
142        *self
143    }
144}
145
146/// Represents a single item in the overall layout hierarchy with a bounding rectangle and an id.
147///
148/// Modifying any properties of this layout only stores those changes here, they won't affect the
149/// solver until this struct is passed to the solver.
150pub struct Layout {
151    pub vars: LayoutVars,
152    pub name: Option<String>,
153    pub id: LayoutId,
154    container: Option<Rc<RefCell<LayoutContainer>>>,
155    parent: Option<LayoutId>,
156    children: Vec<LayoutId>,
157    edit_vars: Vec<EditVariable>,
158    constraints: HashSet<Constraint>,
159    new_constraints: HashSet<Constraint>,
160    removed_constraints: Vec<Constraint>,
161    removed_children: Vec<LayoutId>,
162    associated_vars: Vec<(Variable, String)>,
163    pub hidden: bool,
164}
165
166impl Layout {
167
168    /// Creates a new `Layout`.
169    pub fn new(id: LayoutId, name: Option<String>) -> Self {
170        let vars = LayoutVars::new();
171        let mut new_constraints = HashSet::new();
172        new_constraints.insert(vars.right - vars.left| EQ(REQUIRED) | vars.width);
173        new_constraints.insert(vars.bottom - vars.top | EQ(REQUIRED) | vars.height);
174        new_constraints.insert(vars.width | GE(REQUIRED) | 0.0);
175        new_constraints.insert(vars.height | GE(REQUIRED) | 0.0);
176        Layout {
177            vars: vars,
178            name: name,
179            id: id,
180            container: Some(Rc::new(RefCell::new(Frame::default()))),
181            parent: None,
182            children: Vec::new(),
183            edit_vars: Vec::new(),
184            constraints: HashSet::new(),
185            new_constraints: new_constraints,
186            removed_constraints: Vec::new(),
187            removed_children: Vec::new(),
188            associated_vars: Vec::new(),
189            hidden: false,
190        }
191    }
192
193    pub fn layout(&mut self) -> &mut Self {
194        self
195    }
196
197    /// Clears the container of the current `Layout`.
198    pub fn no_container(&mut self) {
199        self.container = None;
200    }
201
202    /// Replaces the container of the current layout.
203    /// The container is what determines what constraints will be added between this layout
204    /// and it's children, as they are added, if any.
205    pub fn set_container<T>(&mut self, container: T) where T: LayoutContainer + 'static {
206        self.container = Some(Rc::new(RefCell::new(container)));
207    }
208    pub fn edit_left(&mut self) -> VariableEditable {
209        let var = self.vars.left;
210        VariableEditable::new(self, var)
211    }
212    pub fn edit_top(&mut self) -> VariableEditable {
213        let var = self.vars.top;
214        VariableEditable::new(self, var)
215    }
216    pub fn edit_right(&mut self) -> VariableEditable {
217        let var = self.vars.right;
218        VariableEditable::new(self, var)
219    }
220    pub fn edit_bottom(&mut self) -> VariableEditable {
221        let var = self.vars.bottom;
222        VariableEditable::new(self, var)
223    }
224    pub fn edit_width(&mut self) -> VariableEditable {
225        let var = self.vars.width;
226        VariableEditable::new(self, var)
227    }
228    pub fn edit_height(&mut self) -> VariableEditable {
229        let var = self.vars.height;
230        VariableEditable::new(self, var)
231    }
232    pub fn create_constraint<B: ConstraintBuilder>(&self, builder: B) -> Vec<Constraint> {
233        builder.build(&self.vars)
234    }
235    pub fn add<B: ConstraintBuilder>(&mut self, builder: B) {
236        let new_constraints = builder.build(&self.vars);
237        self.new_constraints.extend(new_constraints);
238    }
239    pub fn remove_constraint(&mut self, constraint: Constraint) {
240        if !self.new_constraints.remove(&constraint) {
241            self.removed_constraints.push(constraint);
242        }
243    }
244    pub fn remove_constraints(&mut self, constraints: Vec<Constraint>) {
245        for constraint in constraints {
246            if !self.new_constraints.remove(&constraint) {
247                self.removed_constraints.push(constraint);
248            }
249        }
250    }
251    pub fn has_constraint(&mut self, constraints: &Vec<Constraint>) -> bool {
252        for constraint in constraints {
253            if self.new_constraints.contains(constraint) || self.constraints.contains(constraint) {
254                return true
255            }
256        }
257        false
258    }
259    pub fn get_constraints(&mut self) -> HashSet<Constraint> {
260        let new_constraints = mem::replace(&mut self.new_constraints, HashSet::new());
261        for constraint in new_constraints.clone() {
262            self.constraints.insert(constraint);
263        }
264        new_constraints
265    }
266    pub fn get_removed_constraints(&mut self) -> Vec<Constraint> {
267        let removed_constraints = mem::replace(&mut self.removed_constraints, Vec::new());
268        for ref constraint in &removed_constraints {
269            self.constraints.remove(constraint);
270        }
271        removed_constraints
272    }
273    pub fn get_edit_vars(&mut self) -> Vec<EditVariable> {
274        mem::replace(&mut self.edit_vars, Vec::new())
275    }
276    pub fn add_child(&mut self, child: &mut Layout) {
277        child.parent = Some(self.id);
278        self.children.push(child.id);
279        if let Some(container) = self.container.clone() {
280            container.borrow_mut().add_child(self, child);
281        }
282    }
283    pub fn remove_child(&mut self, child: &mut Layout) {
284        if let Some(container) = self.container.clone() {
285            container.borrow_mut().remove_child(self, child);
286        }
287        if let Some(pos) = self.children.iter().position(|id| child.id == *id) {
288            self.children.remove(pos);
289        }
290        self.removed_children.push(child.id);
291    }
292    pub fn get_removed_children(&mut self) -> Vec<LayoutId> {
293        mem::replace(&mut self.removed_children, Vec::new())
294    }
295    pub fn get_children(&self) -> &Vec<LayoutId> {
296        &self.children
297    }
298    pub fn add_associated_vars(&mut self, vars: &LayoutVars, name: &str) {
299        for var in vars.array().iter() {
300            let var_type = format!("{:?}", vars.var_type(*var)).to_lowercase();
301            self.associated_vars.push((*var, format!("{}.{}", name, var_type)));
302        }
303    }
304    pub fn add_associated_var(&mut self, var: Variable, name: &str) {
305        self.associated_vars.push((var, name.to_owned()));
306    }
307    pub fn get_associated_vars(&mut self) -> Vec<(Variable, String)> {
308        mem::replace(&mut self.associated_vars, Vec::new())
309    }
310    pub fn hide(&mut self) {
311        self.hidden = true;
312    }
313    pub fn show(&mut self) {
314        self.hidden = false;
315    }
316}
317
318pub struct VariableEditable<'a> {
319    pub builder: &'a mut Layout,
320    pub var: Variable,
321    val: Option<f64>,
322    strength: f64,
323}
324
325impl<'a> VariableEditable<'a> {
326    pub fn new(builder: &'a mut Layout, var: Variable) -> Self {
327        VariableEditable {
328            builder: builder,
329            var: var,
330            val: None,
331            strength: STRONG,
332        }
333    }
334    pub fn strength(mut self, strength: f64) -> Self {
335        self.strength = strength;
336        self
337    }
338    pub fn set(mut self, val: f32) -> Self {
339        self.val = Some(val as f64);
340        self
341    }
342}
343
344impl<'a> Drop for VariableEditable<'a> {
345    fn drop(&mut self) {
346        let edit_var = EditVariable::new(&self);
347        self.builder.edit_vars.push(edit_var);
348    }
349}
350
351#[derive(Debug, Copy, Clone)]
352pub struct EditVariable {
353    var: Variable,
354    val: f64,
355    strength: f64,
356}
357
358impl EditVariable {
359    fn new(editable: &VariableEditable) -> Self {
360        EditVariable {
361            var: editable.var,
362            val: editable.val.unwrap_or(0.0),
363            strength: editable.strength,
364        }
365    }
366}
367
368/// Used to specify a list of constraints.
369// Needed to box different ConstraintBuilder impls,
370// can't be done without specifying Vec<Box<ConstraintBuilder>>.
371// Can be removed if/when variadic generics are added to rust.
372#[macro_export]
373macro_rules! constraints {
374    ( $ ( $ x : expr ) , * ) => {
375        constraints!( $ ( $ x , ) * )
376    };
377    ( $ ( $ x : expr , ) * ) => {
378        {
379            let mut vec: Vec<Box<ConstraintBuilder>> = Vec::new();
380            $(
381                vec.push(Box::new($x));
382            )*
383            vec
384        }
385    };
386}
387
388/// Defines what constraints a parent applies to it's children as they are added
389pub trait LayoutContainer {
390    fn add_child(&mut self, parent: &mut Layout, child: &mut Layout);
391    fn remove_child(&mut self, _: &mut Layout, _: &mut Layout) {}
392}
393
394#[derive(Debug, Default, Copy, Clone)]
395pub struct Frame {
396    padding: f32,
397}
398
399impl LayoutContainer for Frame {
400
401    /// Adds a weak constraint to a Frame
402    fn add_child(&mut self, parent: &mut Layout, child: &mut Layout) {
403        child.add(constraints![
404            bound_by(&parent).padding(self.padding),
405            match_layout(&parent).strength(WEAK),
406        ]);
407    }
408}
409
410#[derive(Debug, Copy, Clone)]
411pub struct ExactFrame;
412
413impl LayoutContainer for ExactFrame {
414    fn add_child(&mut self, parent: &mut Layout, child: &mut Layout) {
415        child.add(match_layout(&parent));
416    }
417}
418
419pub mod solver;
420pub mod constraint;
421pub mod linear_layout;
422pub mod grid_layout;
423
424pub use self::solver::LimnSolver;
425
426lazy_static! {
427    pub static ref LAYOUT: LayoutVars = LayoutVars::new();
428}