kas_widgets/adapt/adapt.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//! Adapt widget
7
8use super::{AdaptConfigCx, AdaptEventCx};
9use kas::event::TimerHandle;
10use kas::prelude::*;
11use linear_map::LinearMap;
12use std::fmt::Debug;
13use std::marker::PhantomData;
14
15#[impl_self]
16mod Adapt {
17 /// Data adaption node
18 ///
19 /// Where [`Map`] allows mapping to a sub-set of input data, `Adapt` allows
20 /// mapping to a super-set (including internal storage). Further, `Adapt`
21 /// supports message handlers which mutate internal storage.
22 ///
23 /// # Inner data type
24 ///
25 /// Note that, at least for now, the type of state stored by `Adapt` must
26 /// equal the data type of the inner widget: `state: <W as Widget>::Data`.
27 /// Since `W::Data` must outlive `W` (for our purposes this is not much
28 /// different than if `Widget::Data: 'static`), we cannot support `W::Data`
29 /// like `(&A, &S)` where `state: S`, so we might as well simply pass `&S`
30 /// to the inner widget `W`. This implies that any state from `A` which
31 /// needs to be passed into `W` must be *copied* into `state: W::Data` by
32 /// [`Adapt::on_update`].
33 ///
34 /// (It is possible that the above restrictions will change in the future,
35 /// but they would require Rust to support generic associated types in
36 /// dyn-safe traits (also known as object safe GATs), at least for lifetime
37 /// parameters. There *was* an unstable feature for this,
38 /// `generic_associated_types_extended`, but it was removed due to being
39 /// stale, experimental and unsound. But even if Rust did gain this feature,
40 /// it is not clear that [`Widget::Data`] should be generic.)
41 #[autoimpl(Scrollable using self.inner where W: trait)]
42 #[widget]
43 #[layout(self.inner)]
44 pub struct Adapt<A, W: Widget> {
45 core: widget_core!(),
46 state: W::Data,
47 #[widget(&self.state)]
48 inner: W,
49 configure_handler: Option<Box<dyn Fn(&mut AdaptConfigCx, &mut W::Data)>>,
50 update_handler: Option<Box<dyn Fn(&mut AdaptConfigCx, &mut W::Data, &A)>>,
51 timer_handlers: LinearMap<TimerHandle, Box<dyn Fn(&mut AdaptEventCx, &mut W::Data, &A)>>,
52 message_handlers: Vec<Box<dyn Fn(&mut AdaptEventCx, &mut W::Data, &A)>>,
53 }
54
55 impl Self {
56 /// Construct over `inner` with additional `state`
57 #[inline]
58 pub fn new(inner: W, state: W::Data) -> Self {
59 Adapt {
60 core: Default::default(),
61 state,
62 inner,
63 configure_handler: None,
64 update_handler: None,
65 timer_handlers: LinearMap::new(),
66 message_handlers: vec![],
67 }
68 }
69
70 /// Add a handler to be called on configuration
71 pub fn on_configure<F>(mut self, handler: F) -> Self
72 where
73 F: Fn(&mut AdaptConfigCx, &mut W::Data) + 'static,
74 {
75 debug_assert!(self.configure_handler.is_none());
76 self.configure_handler = Some(Box::new(handler));
77 self
78 }
79
80 /// Add a handler to be called on update of input data
81 ///
82 /// Children will be updated after the handler is called.
83 pub fn on_update<F>(mut self, handler: F) -> Self
84 where
85 F: Fn(&mut AdaptConfigCx, &mut W::Data, &A) + 'static,
86 {
87 debug_assert!(self.update_handler.is_none());
88 self.update_handler = Some(Box::new(handler));
89 self
90 }
91
92 /// Set a timer handler
93 ///
94 /// It is assumed that state is modified by this timer. Frequent usage
95 /// of timers which don't do anything may be inefficient; prefer usage
96 /// of [`EventState::send_async`](kas::event::EventState::send_async).
97 pub fn on_timer<H>(mut self, timer_id: TimerHandle, handler: H) -> Self
98 where
99 H: Fn(&mut AdaptEventCx, &mut W::Data, &A) + 'static,
100 {
101 debug_assert!(self.timer_handlers.get(&timer_id).is_none());
102 self.timer_handlers.insert(timer_id, Box::new(handler));
103 self
104 }
105
106 /// Add a handler on message of type `M`
107 ///
108 /// Children will be updated whenever this handler is invoked.
109 ///
110 /// Where access to input data (from parent widgets) is required,
111 /// use [`Self::on_messages`] instead.
112 pub fn on_message<M, H>(self, handler: H) -> Self
113 where
114 M: Debug + 'static,
115 H: Fn(&mut AdaptEventCx, &mut W::Data, M) + 'static,
116 {
117 self.on_messages(move |cx, state, _data| {
118 if let Some(m) = cx.try_pop() {
119 handler(cx, state, m);
120 }
121 })
122 }
123
124 /// Add a generic message handler
125 pub fn on_messages<H>(mut self, handler: H) -> Self
126 where
127 H: Fn(&mut AdaptEventCx, &mut W::Data, &A) + 'static,
128 {
129 self.message_handlers.push(Box::new(handler));
130 self
131 }
132 }
133
134 impl Events for Self {
135 type Data = A;
136
137 fn configure(&mut self, cx: &mut ConfigCx) {
138 if let Some(handler) = self.configure_handler.as_ref() {
139 let mut cx = AdaptConfigCx::new(cx, self.id());
140 handler(&mut cx, &mut self.state);
141 }
142 }
143
144 fn update(&mut self, cx: &mut ConfigCx, data: &A) {
145 if let Some(handler) = self.update_handler.as_ref() {
146 let mut cx = AdaptConfigCx::new(cx, self.id());
147 handler(&mut cx, &mut self.state, data);
148 }
149 }
150
151 fn handle_event(&mut self, cx: &mut EventCx, data: &Self::Data, event: Event) -> IsUsed {
152 match event {
153 Event::Timer(timer_id) => {
154 if let Some(handler) = self.timer_handlers.get(&timer_id) {
155 let mut cx = AdaptEventCx::new(cx, self.id());
156 handler(&mut cx, &mut self.state, data);
157 cx.update(self.as_node(data));
158 Used
159 } else {
160 Unused
161 }
162 }
163 _ => Unused,
164 }
165 }
166
167 fn handle_messages(&mut self, cx: &mut EventCx, data: &A) {
168 let count = cx.msg_op_count();
169 let mut cx = AdaptEventCx::new(cx, self.id());
170 for handler in self.message_handlers.iter() {
171 handler(&mut cx, &mut self.state, data);
172 }
173 if cx.msg_op_count() != count {
174 cx.update(self.as_node(data));
175 }
176 }
177 }
178}
179
180#[impl_self]
181mod Map {
182 /// Data mapping
183 ///
184 /// This is a generic data-mapping widget-wrapper.
185 /// See also [`Adapt`], [`MapAny`](super::MapAny).
186 ///
187 /// This struct is a thin wrapper around the inner widget without its own
188 /// [`Id`]. It supports [`Deref`](std::ops::Deref) and
189 /// [`DerefMut`](std::ops::DerefMut) to the inner widget.
190 #[autoimpl(Deref, DerefMut using self.inner)]
191 #[autoimpl(Scrollable using self.inner where W: trait)]
192 #[derive_widget(type Data = A)]
193 pub struct Map<A, W: Widget, F>
194 where
195 F: for<'a> Fn(&'a A) -> &'a W::Data,
196 {
197 /// The inner widget
198 #[widget((self.map_fn)(data))]
199 pub inner: W,
200 map_fn: F,
201 _data: PhantomData<A>,
202 }
203
204 impl Self {
205 /// Construct
206 ///
207 /// - Over an `inner` widget
208 /// - And `map_fn` mapping to the inner widget's data type
209 pub fn new(inner: W, map_fn: F) -> Self {
210 Map {
211 inner,
212 map_fn,
213 _data: PhantomData,
214 }
215 }
216 }
217}