kas_widgets/adapt/
adapt_events.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//! Event adapters
7
8use super::{AdaptConfigCx, AdaptEventCx};
9use kas::autoimpl;
10use kas::event::{ConfigCx, Event, EventCx, IsUsed};
11use kas::geom::{Coord, Offset, Rect};
12use kas::layout::{AlignHints, AxisInfo, SizeRules};
13use kas::theme::{DrawCx, SizeCx};
14#[allow(unused)] use kas::Events;
15use kas::{Id, Layout, LayoutExt, NavAdvance, Node, Widget};
16use std::fmt::Debug;
17
18/// Wrapper with configure / update / message handling callbacks.
19///
20/// This type is constructed by some [`AdaptWidget`](super::AdaptWidget) methods.
21#[autoimpl(Deref, DerefMut using self.inner)]
22#[autoimpl(Scrollable using self.inner where W: trait)]
23pub struct AdaptEvents<W: Widget> {
24    pub inner: W,
25    on_configure: Option<Box<dyn Fn(&mut AdaptConfigCx, &mut W)>>,
26    on_update: Option<Box<dyn Fn(&mut AdaptConfigCx, &mut W, &W::Data)>>,
27    message_handlers: Vec<Box<dyn Fn(&mut AdaptEventCx, &mut W, &W::Data)>>,
28}
29
30impl<W: Widget> AdaptEvents<W> {
31    /// Construct
32    #[inline]
33    pub fn new(inner: W) -> Self {
34        AdaptEvents {
35            inner,
36            on_configure: None,
37            on_update: None,
38            message_handlers: vec![],
39        }
40    }
41
42    /// Call the given closure on [`Events::configure`]
43    #[must_use]
44    pub fn on_configure<F>(mut self, f: F) -> Self
45    where
46        F: Fn(&mut AdaptConfigCx, &mut W) + 'static,
47    {
48        self.on_configure = Some(Box::new(f));
49        self
50    }
51
52    /// Call the given closure on [`Events::update`]
53    #[must_use]
54    pub fn on_update<F>(mut self, f: F) -> Self
55    where
56        F: Fn(&mut AdaptConfigCx, &mut W, &W::Data) + 'static,
57    {
58        self.on_update = Some(Box::new(f));
59        self
60    }
61
62    /// Add a handler on message of type `M`
63    ///
64    /// The child index may be inferred via [`EventCx::last_child`].
65    /// (Note: this is only possible since `AdaptEvents` is a special "thin" wrapper.)
66    ///
67    /// Where access to input data is required, use [`Self::on_messages`] instead.
68    #[must_use]
69    pub fn on_message<M, H>(self, handler: H) -> Self
70    where
71        M: Debug + 'static,
72        H: Fn(&mut AdaptEventCx, &mut W, M) + 'static,
73    {
74        self.on_messages(move |cx, w, _data| {
75            if let Some(m) = cx.try_pop() {
76                handler(cx, w, m);
77            }
78        })
79    }
80
81    /// Add a child handler to map messages of type `M` to `N`
82    ///
83    /// # Example
84    ///
85    /// ```
86    /// use kas::messages::Select;
87    /// use kas_widgets::{AdaptWidget, Row, Tab};
88    ///
89    /// #[derive(Clone, Debug)]
90    /// struct MsgSelectIndex(usize);
91    ///
92    /// let tabs = Row::new([Tab::new("A")])
93    ///     .map_message(|index, Select| MsgSelectIndex(index));
94    /// ```
95    pub fn map_message<M, N, H>(self, handler: H) -> Self
96    where
97        M: Debug + 'static,
98        N: Debug + 'static,
99        H: Fn(usize, M) -> N + 'static,
100    {
101        self.on_messages(move |cx, _, _| {
102            if let Some(index) = cx.last_child() {
103                if let Some(m) = cx.try_pop() {
104                    cx.push(handler(index, m));
105                }
106            }
107        })
108    }
109
110    /// Add a generic message handler
111    ///
112    /// The child index may be inferred via [`EventCx::last_child`].
113    /// (Note: this is only possible since `AdaptEvents` is a special "thin" wrapper.)
114    #[must_use]
115    pub fn on_messages<H>(mut self, handler: H) -> Self
116    where
117        H: Fn(&mut AdaptEventCx, &mut W, &W::Data) + 'static,
118    {
119        self.message_handlers.push(Box::new(handler));
120        self
121    }
122}
123
124impl<W: Widget> Widget for AdaptEvents<W> {
125    type Data = W::Data;
126
127    #[inline]
128    fn as_node<'a>(&'a mut self, data: &'a Self::Data) -> Node<'a> {
129        Node::new(self, data)
130    }
131
132    #[inline]
133    fn for_child_node(
134        &mut self,
135        data: &Self::Data,
136        index: usize,
137        closure: Box<dyn FnOnce(Node<'_>) + '_>,
138    ) {
139        self.inner.for_child_node(data, index, closure);
140    }
141
142    #[inline]
143    fn _configure(&mut self, cx: &mut ConfigCx, data: &Self::Data, id: Id) {
144        self.inner._configure(cx, data, id);
145
146        if let Some(ref f) = self.on_configure {
147            let mut cx = AdaptConfigCx::new(cx, self.inner.id());
148            f(&mut cx, &mut self.inner);
149        }
150        if let Some(ref f) = self.on_update {
151            let mut cx = AdaptConfigCx::new(cx, self.inner.id());
152            f(&mut cx, &mut self.inner, data);
153        }
154    }
155
156    fn _update(&mut self, cx: &mut ConfigCx, data: &Self::Data) {
157        self.inner._update(cx, data);
158
159        if let Some(ref f) = self.on_update {
160            let mut cx = AdaptConfigCx::new(cx, self.inner.id());
161            f(&mut cx, &mut self.inner, data);
162        }
163    }
164
165    #[inline]
166    fn _send(&mut self, cx: &mut EventCx, data: &Self::Data, id: Id, event: Event) -> IsUsed {
167        let is_used = self.inner._send(cx, data, id, event);
168
169        if cx.has_msg() {
170            let mut cx = AdaptEventCx::new(cx, self.inner.id());
171            for handler in self.message_handlers.iter() {
172                handler(&mut cx, &mut self.inner, data);
173            }
174        }
175
176        is_used
177    }
178
179    #[inline]
180    fn _replay(&mut self, cx: &mut EventCx, data: &Self::Data, id: Id) {
181        self.inner._replay(cx, data, id);
182
183        if cx.has_msg() {
184            let mut cx = AdaptEventCx::new(cx, self.inner.id());
185            for handler in self.message_handlers.iter() {
186                handler(&mut cx, &mut self.inner, data);
187            }
188        }
189    }
190
191    #[inline]
192    fn _nav_next(
193        &mut self,
194        cx: &mut ConfigCx,
195        data: &Self::Data,
196        focus: Option<&Id>,
197        advance: NavAdvance,
198    ) -> Option<Id> {
199        self.inner._nav_next(cx, data, focus, advance)
200    }
201}
202
203impl<W: Widget> Layout for AdaptEvents<W> {
204    #[inline]
205    fn as_layout(&self) -> &dyn Layout {
206        self
207    }
208
209    #[inline]
210    fn id_ref(&self) -> &Id {
211        self.inner.id_ref()
212    }
213
214    #[inline]
215    fn rect(&self) -> Rect {
216        self.inner.rect()
217    }
218
219    #[inline]
220    fn widget_name(&self) -> &'static str {
221        "AdaptEvents"
222    }
223
224    #[inline]
225    fn num_children(&self) -> usize {
226        self.inner.num_children()
227    }
228
229    #[inline]
230    fn get_child(&self, index: usize) -> Option<&dyn Layout> {
231        self.inner.get_child(index)
232    }
233
234    #[inline]
235    fn find_child_index(&self, id: &Id) -> Option<usize> {
236        self.inner.find_child_index(id)
237    }
238
239    #[inline]
240    fn size_rules(&mut self, sizer: SizeCx, axis: AxisInfo) -> SizeRules {
241        self.inner.size_rules(sizer, axis)
242    }
243
244    #[inline]
245    fn set_rect(&mut self, cx: &mut ConfigCx, rect: Rect, hints: AlignHints) {
246        self.inner.set_rect(cx, rect, hints);
247    }
248
249    #[inline]
250    fn nav_next(&self, reverse: bool, from: Option<usize>) -> Option<usize> {
251        self.inner.nav_next(reverse, from)
252    }
253
254    #[inline]
255    fn translation(&self) -> Offset {
256        self.inner.translation()
257    }
258
259    #[inline]
260    fn find_id(&mut self, coord: Coord) -> Option<Id> {
261        self.inner.find_id(coord)
262    }
263
264    #[inline]
265    fn draw(&mut self, draw: DrawCx) {
266        self.inner.draw(draw);
267    }
268}