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::*;
9use kas::cast::{Cast, CastFloat};
10use kas::dir::{Directional, Directions};
11use kas::geom::Vec2;
12use kas::layout::{AlignHints, AxisInfo, SizeRules};
13use kas::text::AccessString;
14use kas::theme::{MarginStyle, SizeCx};
15#[allow(unused)] use kas::Events;
16use kas::Widget;
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    /// Specify margins
59    ///
60    /// This replaces a widget's margins.
61    ///
62    /// Returns a wrapper around the input widget.
63    #[must_use]
64    fn margins(self, dirs: Directions, style: MarginStyle) -> Margins<Self> {
65        Margins::new(self, dirs, style)
66    }
67
68    /// Map data type via a function
69    ///
70    /// Returns a wrapper around the input widget.
71    #[must_use]
72    fn map<A, F>(self, f: F) -> Map<A, Self, F>
73    where
74        F: for<'a> Fn(&'a A) -> &'a Self::Data,
75    {
76        Map::new(self, f)
77    }
78
79    /// Call the given closure on [`Events::configure`]
80    ///
81    /// Returns a wrapper around the input widget.
82    #[must_use]
83    fn on_configure<F>(self, f: F) -> AdaptEvents<Self>
84    where
85        F: Fn(&mut AdaptConfigCx, &mut Self) + 'static,
86    {
87        AdaptEvents::new(self).on_configure(f)
88    }
89
90    /// Call the given closure on [`Events::update`]
91    ///
92    /// Returns a wrapper around the input widget.
93    #[must_use]
94    fn on_update<F>(self, f: F) -> AdaptEvents<Self>
95    where
96        F: Fn(&mut AdaptConfigCx, &mut Self, &Self::Data) + 'static,
97    {
98        AdaptEvents::new(self).on_update(f)
99    }
100
101    /// Add a handler on message of type `M`
102    ///
103    /// Where access to input data is required, use [`Self::on_messages`] instead.
104    ///
105    /// Returns a wrapper around the input widget.
106    #[must_use]
107    fn on_message<M, H>(self, handler: H) -> AdaptEvents<Self>
108    where
109        M: Debug + 'static,
110        H: Fn(&mut AdaptEventCx, &mut Self, M) + 'static,
111    {
112        AdaptEvents::new(self).on_message(handler)
113    }
114
115    /// Add a child handler to map messages of type `M` to `N`
116    ///
117    /// # Example
118    ///
119    /// ```
120    /// use kas::messages::Select;
121    /// use kas_widgets::{AdaptWidget, Row, Tab};
122    ///
123    /// #[derive(Clone, Debug)]
124    /// struct MsgSelectIndex(usize);
125    ///
126    /// let tabs = Row::new([Tab::new("A")])
127    ///     .map_message(|index, Select| MsgSelectIndex(index));
128    /// ```
129    fn map_message<M, N, H>(self, handler: H) -> AdaptEvents<Self>
130    where
131        M: Debug + 'static,
132        N: Debug + 'static,
133        H: Fn(usize, M) -> N + 'static,
134    {
135        AdaptEvents::new(self).map_message(handler)
136    }
137
138    /// Add a generic message handler
139    ///
140    /// Returns a wrapper around the input widget.
141    #[must_use]
142    fn on_messages<H>(self, handler: H) -> AdaptEvents<Self>
143    where
144        H: Fn(&mut AdaptEventCx, &mut Self, &Self::Data) + 'static,
145    {
146        AdaptEvents::new(self).on_messages(handler)
147    }
148
149    /// Construct a wrapper, setting minimum size in pixels
150    ///
151    /// The input size is scaled by the scale factor.
152    ///
153    /// Returns a wrapper around the input widget.
154    #[must_use]
155    fn with_min_size_px(self, w: i32, h: i32) -> Reserve<Self> {
156        let size = Vec2(w.cast(), h.cast());
157        Reserve::new(self, move |sizer: SizeCx, axis: AxisInfo| {
158            let size = size.extract(axis) * sizer.scale_factor();
159            SizeRules::fixed(size.cast_ceil(), (0, 0))
160        })
161    }
162
163    /// Construct a wrapper, setting minimum size in Em
164    ///
165    /// This depends on the font size, though not the exact font in use.
166    ///
167    /// Returns a wrapper around the input widget.
168    #[must_use]
169    fn with_min_size_em(self, w: f32, h: f32) -> Reserve<Self> {
170        let size = Vec2(w, h);
171        Reserve::new(self, move |sizer: SizeCx, axis: AxisInfo| {
172            let size = size.extract(axis) * sizer.dpem();
173            SizeRules::fixed(size.cast_ceil(), (0, 0))
174        })
175    }
176
177    /// Construct a wrapper widget adding a label
178    ///
179    /// Returns a wrapper around the input widget.
180    #[must_use]
181    fn with_label<D, T>(self, direction: D, label: T) -> WithLabel<Self, D>
182    where
183        D: Directional,
184        T: Into<AccessString>,
185    {
186        WithLabel::new_dir(self, direction, label)
187    }
188}
189impl<W: Widget> AdaptWidget for W {}