Skip to main content

layuit/
limit.rs

1//! Containers that create upper bounds for nodes.
2
3use thunderdome::Index as NodeIndex;
4
5use crate::{Alignment, Anchor, NodeCache, Rect, UiNode, UiTree};
6
7/// Limits a node's maximum size.
8///
9/// If the node's minimum size exceeds the maximum, the minimum takes precedence.
10pub struct Clamp {
11    /// The maximum size to maintain.
12    pub max_override: (f32, f32),
13
14    /// The position to place the shrunken space. The child is then aligned within the new space.
15    pub anchor: (Anchor, Anchor),
16
17    child: Option<NodeIndex>,
18    align: (Alignment, Alignment),
19}
20
21impl Clamp {
22    /// Creates a new `Clamp` with no child, a size limit of 0, default anchoring, and ([`Begin`],
23    /// [`Begin`]) alignment.
24    ///
25    /// The default maximum has no effect, as the maximum is immediately overridden by the child's
26    /// min size.
27    ///
28    /// [`Begin`]: Alignment::Begin
29    pub fn new() -> Self {
30        Self {
31            max_override: (0.0, 0.0),
32            child: None,
33            anchor: (Anchor::Center, Anchor::Center),
34            align: (Alignment::Begin, Alignment::Begin),
35        }
36    }
37
38    /// bind a child node.
39    ///
40    /// # Panics
41    /// If there is already a child node.
42    pub fn with_child(mut self, index: NodeIndex) -> Self {
43        assert!(self.child.is_none());
44        self.child = Some(index);
45        self
46    }
47
48    /// Set the horizontal and vertical alignment.
49    pub fn with_align(mut self, align: (Alignment, Alignment)) -> Self {
50        self.align = align;
51        self
52    }
53
54    /// Set the horizontal and vertical anchor mode for the shrunken space.
55    pub fn with_anchor(mut self, anchor: (Anchor, Anchor)) -> Self {
56        self.anchor = anchor;
57        self
58    }
59
60    /// Set the maximum size.
61    ///
62    /// If the maximum size is smaller than the minimum size, the minimum takes precedence.
63    pub fn with_max(mut self, max: (f32, f32)) -> Self {
64        self.max_override = max;
65        self
66    }
67
68    /// Bind a child node to the node.
69    ///
70    /// # Panics
71    /// If there is already a child node.
72    pub fn add_child(&mut self, index: NodeIndex) {
73        assert!(self.child.is_none());
74        self.child = Some(index);
75    }
76
77    /// Get the tree index of the child.
78    pub fn get_child(&self) -> Option<NodeIndex> {
79        self.child
80    }
81}
82
83impl Default for Clamp {
84    fn default() -> Self {
85        Self::new()
86    }
87}
88
89impl UiNode for Clamp {
90    fn get_align(&self) -> (Alignment, Alignment) {
91        self.align
92    }
93
94    fn get_align_mut(&mut self) -> (&mut Alignment, &mut Alignment) {
95        (&mut self.align.0, &mut self.align.1)
96    }
97
98    fn calculate_min_size(&self, tree: &UiTree) -> (f32, f32) {
99        if let Some(index) = self.child {
100            tree.get_cache(index).expect("Child not in cache").min_size
101        } else {
102            (0.0, 0.0)
103        }
104    }
105
106    fn calculate_rects(&self, cache: &NodeCache, tree: &UiTree) -> Vec<Rect> {
107        if let Some(index) = self.child {
108            let child_min = tree.get_cache(index).expect("Child not in cache").min_size;
109            let child = tree.get_node(index).expect("Child not in cache");
110
111            // The size must be at most the given, at least the min, and defaults to the max
112            let w = cache.rect.w.clamp(child_min.0, self.max_override.0);
113            let h = cache.rect.h.clamp(child_min.1, self.max_override.1);
114
115            let shrunk = cache.rect.anchor(self.anchor, (w, h));
116            let space = shrunk.align(child.get_align(), child_min);
117            vec![space]
118        } else {
119            vec![]
120        }
121    }
122
123    fn get_children(&self) -> Vec<NodeIndex> {
124        self.child.into_iter().collect()
125    }
126}