kas_widgets/adapt/
reserve.rs

1// Licensed under the Apache License, Version 2.0 (the "License");
2// you may not use this file except in compliance with the License.
3// You may obtain a copy of the License in the LICENSE-APACHE file or at:
4//     https://www.apache.org/licenses/LICENSE-2.0
5
6//! Size reservation
7
8use kas::dir::Directions;
9use kas::prelude::*;
10use kas::theme::MarginStyle;
11
12#[impl_self]
13mod Reserve {
14    /// A generic widget for size reservations
15    ///
16    /// In a few cases it is desirable to reserve more space for a widget than
17    /// required for the current content, e.g. if a label's text may change. This
18    /// widget can be used for this by wrapping the base widget.
19    ///
20    /// Usually, this type will be constructed through one of the methods on
21    /// [`AdaptWidget`](crate::adapt::AdaptWidget).
22    #[derive_widget]
23    pub struct Reserve<W: Widget> {
24        #[widget]
25        pub inner: W,
26        reserve: Box<dyn Fn(SizeCx, AxisInfo) -> SizeRules + 'static>,
27    }
28
29    impl Self {
30        /// Construct a reserve
31        ///
32        /// The closure `reserve` should generate `SizeRules` on request, just like
33        /// [`Layout::size_rules`]. For example:
34        ///```
35        /// use kas_widgets::adapt::Reserve;
36        /// use kas_widgets::Filler;
37        /// use kas::prelude::*;
38        ///
39        /// let label = Reserve::new(Filler::new(), |sizer: SizeCx<'_>, axis| {
40        ///     let size = i32::conv_ceil(sizer.scale_factor() * 100.0);
41        ///     SizeRules::fixed(size, (0, 0))
42        /// });
43        ///```
44        /// The resulting `SizeRules` will be the max of those for the inner widget
45        /// and the result of the `reserve` closure.
46        #[inline]
47        pub fn new(inner: W, reserve: impl Fn(SizeCx, AxisInfo) -> SizeRules + 'static) -> Self {
48            let reserve = Box::new(reserve);
49            Reserve { inner, reserve }
50        }
51    }
52
53    impl Layout for Self {
54        fn size_rules(&mut self, sizer: SizeCx, axis: AxisInfo) -> SizeRules {
55            let inner_rules = self.inner.size_rules(sizer.re(), axis);
56            let reserve_rules = (self.reserve)(sizer.re(), axis);
57            inner_rules.max(reserve_rules)
58        }
59    }
60}
61
62#[impl_self]
63mod Margins {
64    /// Specify margins
65    ///
66    /// This replaces a widget's margins.
67    ///
68    /// Usually, this type will be constructed through one of the methods on
69    /// [`AdaptWidget`](crate::adapt::AdaptWidget).
70    #[derive_widget]
71    pub struct Margins<W: Widget> {
72        #[widget]
73        pub inner: W,
74        dirs: Directions,
75        style: MarginStyle,
76    }
77
78    impl Self {
79        /// Construct
80        #[inline]
81        pub fn new(inner: W, dirs: Directions, style: MarginStyle) -> Self {
82            Margins { inner, dirs, style }
83        }
84    }
85
86    impl Layout for Self {
87        fn size_rules(&mut self, sizer: SizeCx, axis: AxisInfo) -> SizeRules {
88            let mut child_rules = self.inner.size_rules(sizer.re(), axis);
89            if self.dirs.intersects(Directions::from(axis)) {
90                let mut rule_margins = child_rules.margins();
91                let margins = sizer.margins(self.style).extract(axis);
92                if self.dirs.intersects(Directions::LEFT | Directions::UP) {
93                    rule_margins.0 = margins.0;
94                }
95                if self.dirs.intersects(Directions::RIGHT | Directions::DOWN) {
96                    rule_margins.1 = margins.1;
97                }
98                child_rules.set_margins(rule_margins);
99            }
100            child_rules
101        }
102    }
103}