Skip to main content

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 {}