kas_widgets/adapt/adapt_widget.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//! Widget extension traits
7
8use super::*;
9#[allow(unused)] use kas::Events;
10use kas::Widget;
11use kas::cast::{Cast, CastFloat};
12use kas::dir::Directional;
13use kas::geom::Vec2;
14use kas::layout::{AlignHints, AxisInfo, SizeRules};
15use kas::text::AccessString;
16use kas::theme::{MarginStyle, SizeCx, TextClass};
17use std::fmt::Debug;
18
19/// Provides `.map_any()`
20///
21/// TODO: move to `AdaptWidget` with `where Self::Data == ()` constraint
22/// once supported (Rust#20041).
23pub trait AdaptWidgetAny: Widget<Data = ()> + Sized {
24 /// Map any input data to `()`
25 ///
26 /// Returns a wrapper around the input widget.
27 #[must_use]
28 fn map_any<A>(self) -> MapAny<A, Self> {
29 MapAny::new(self)
30 }
31}
32impl<W: Widget<Data = ()>> AdaptWidgetAny for W {}
33
34/// Provides some convenience methods on widgets
35pub trait AdaptWidget: Widget + Sized {
36 /// Apply an alignment hint
37 ///
38 /// The inner widget chooses how to apply (or ignore) this hint.
39 ///
40 /// Returns a wrapper around the input widget.
41 #[must_use]
42 fn align(self, hints: AlignHints) -> Align<Self> {
43 Align::new(self, hints)
44 }
45
46 /// Apply an alignment hint, squash and align the result
47 ///
48 /// The inner widget chooses how to apply (or ignore) this hint.
49 /// The widget is then prevented from stretching beyond its ideal size,
50 /// aligning within the available rect.
51 ///
52 /// Returns a wrapper around the input widget.
53 #[must_use]
54 fn pack(self, hints: AlignHints) -> Pack<Self> {
55 Pack::new(self, hints)
56 }
57
58 /// Adjust stretch policy
59 ///
60 /// Replaces the stretch policy of the inner widget.
61 ///
62 /// Returns a wrapper around the input widget.
63 #[must_use]
64 fn with_stretch(
65 self,
66 horiz: impl Into<Option<Stretch>>,
67 vert: impl Into<Option<Stretch>>,
68 ) -> WithStretch<Self> {
69 WithStretch::new(self, horiz, vert)
70 }
71
72 /// Set the margin style
73 ///
74 /// Returns a wrapper around the input widget.
75 #[must_use]
76 fn with_margin_style(self, style: MarginStyle) -> WithMarginStyle<Self> {
77 WithMarginStyle::new(self, style)
78 }
79
80 /// Map data type via a function
81 ///
82 /// Returns a wrapper around the input widget.
83 #[must_use]
84 fn map<A, F>(self, f: F) -> Map<A, Self, F>
85 where
86 F: for<'a> Fn(&'a A) -> &'a Self::Data,
87 {
88 Map::new(self, f)
89 }
90
91 /// Call the given closure on [`Events::configure`]
92 ///
93 /// Returns a wrapper around the input widget.
94 #[must_use]
95 fn on_configure<F>(self, f: F) -> AdaptEvents<Self>
96 where
97 F: Fn(&mut AdaptConfigCx, &mut Self) + 'static,
98 {
99 AdaptEvents::new(self).on_configure(f)
100 }
101
102 /// Call the given closure on [`Events::update`]
103 ///
104 /// Returns a wrapper around the input widget.
105 #[must_use]
106 fn on_update<F>(self, f: F) -> AdaptEvents<Self>
107 where
108 F: Fn(&mut AdaptConfigCx, &mut Self, &Self::Data) + 'static,
109 {
110 AdaptEvents::new(self).on_update(f)
111 }
112
113 /// Add a handler on message of type `M`
114 ///
115 /// Where access to input data is required, use [`Self::on_messages`] instead.
116 ///
117 /// Returns a wrapper around the input widget.
118 #[must_use]
119 fn on_message<M, H>(self, handler: H) -> AdaptEvents<Self>
120 where
121 M: Debug + 'static,
122 H: Fn(&mut AdaptEventCx, &mut Self, M) + 'static,
123 {
124 AdaptEvents::new(self).on_message(handler)
125 }
126
127 /// Add a child handler to map messages of type `M` to `N`
128 ///
129 /// # Example
130 ///
131 /// ```
132 /// use kas::messages::Select;
133 /// use kas_widgets::{AdaptWidget, Row, Tab};
134 ///
135 /// #[derive(Clone, Debug)]
136 /// struct MsgSelectIndex(usize);
137 ///
138 /// let tabs = Row::new([Tab::new("A")])
139 /// .map_message(|index, Select| MsgSelectIndex(index));
140 /// ```
141 fn map_message<M, N, H>(self, handler: H) -> AdaptEvents<Self>
142 where
143 M: Debug + 'static,
144 N: Debug + 'static,
145 H: Fn(usize, M) -> N + 'static,
146 {
147 AdaptEvents::new(self).map_message(handler)
148 }
149
150 /// Add a generic message handler
151 ///
152 /// Returns a wrapper around the input widget.
153 #[must_use]
154 fn on_messages<H>(self, handler: H) -> AdaptEvents<Self>
155 where
156 H: Fn(&mut AdaptEventCx, &mut Self, &Self::Data) + 'static,
157 {
158 AdaptEvents::new(self).on_messages(handler)
159 }
160
161 /// Construct a wrapper, setting minimum size in pixels
162 ///
163 /// The input size is scaled by the scale factor.
164 ///
165 /// Returns a wrapper around the input widget.
166 #[must_use]
167 fn with_min_size_px(self, w: i32, h: i32) -> Reserve<Self> {
168 let size = Vec2(w.cast(), h.cast());
169 Reserve::new(self, move |cx: &mut SizeCx, axis: AxisInfo| {
170 let size = size.extract(axis) * cx.scale_factor();
171 SizeRules::fixed(size.cast_ceil())
172 })
173 }
174
175 /// Construct a wrapper, setting minimum size in Em
176 ///
177 /// This depends on the size of the font for [`TextClass::Standard`].
178 ///
179 /// Returns a wrapper around the input widget.
180 #[must_use]
181 fn with_min_size_em(self, w: f32, h: f32) -> Reserve<Self> {
182 let size = Vec2(w, h);
183 Reserve::new(self, move |cx: &mut SizeCx, axis: AxisInfo| {
184 let size = size.extract(axis) * cx.dpem(TextClass::Standard);
185 SizeRules::fixed(size.cast_ceil())
186 })
187 }
188
189 /// Construct a wrapper widget adding a label
190 ///
191 /// Returns a wrapper around the input widget.
192 #[must_use]
193 fn with_label<D, T>(self, direction: D, label: T) -> WithLabel<Self, D>
194 where
195 D: Directional,
196 T: Into<AccessString>,
197 {
198 WithLabel::new_dir(self, direction, label)
199 }
200
201 /// Construct a wrapper widget adding a hidden label
202 ///
203 /// This label is not normally visible but may be read by accessibility
204 /// tools and tooltips.
205 ///
206 /// Returns a wrapper around the input widget.
207 #[must_use]
208 fn with_hidden_label<T: ToString>(self, label: T) -> WithHiddenLabel<Self> {
209 WithHiddenLabel::new(self, label)
210 }
211
212 /// Construct an [`Adapt`] widget over input
213 #[inline]
214 #[must_use]
215 fn with_state<A>(self, state: Self::Data) -> Adapt<A, Self> {
216 Adapt::new(self, state)
217 }
218}
219impl<W: Widget> AdaptWidget for W {}