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}