1#![allow(missing_docs)]
23
24use std::cell::RefCell;
25use std::collections::VecDeque;
26use std::rc::Rc;
27
28use crate::*;
29
30pub type EventFn<S, T> = dyn FnMut(&mut S, &mut EventCtx, &Event, &mut T, &Env);
31pub type LifeCycleFn<S, T> = dyn FnMut(&mut S, &mut LifeCycleCtx, &LifeCycle, &T, &Env);
32pub type UpdateFn<S, T> = dyn FnMut(&mut S, &mut UpdateCtx, &T, &T, &Env);
33pub type LayoutFn<S, T> = dyn FnMut(&mut S, &mut LayoutCtx, &BoxConstraints, &T, &Env) -> Size;
34pub type PaintFn<S, T> = dyn FnMut(&mut S, &mut PaintCtx, &T, &Env);
35
36pub const REPLACE_CHILD: Selector = Selector::new("druid-test.replace-child");
37
38pub struct ModularWidget<S, T> {
42 state: S,
43 event: Option<Box<EventFn<S, T>>>,
44 lifecycle: Option<Box<LifeCycleFn<S, T>>>,
45 update: Option<Box<UpdateFn<S, T>>>,
46 layout: Option<Box<LayoutFn<S, T>>>,
47 paint: Option<Box<PaintFn<S, T>>>,
48}
49
50pub struct ReplaceChild<T> {
52 child: WidgetPod<T, Box<dyn Widget<T>>>,
53 replacer: Box<dyn Fn() -> Box<dyn Widget<T>>>,
54}
55
56pub struct Recorder<W> {
74 recording: Recording,
75 child: W,
76}
77
78#[derive(Debug, Clone, Default)]
80pub struct Recording(Rc<RefCell<VecDeque<Record>>>);
81
82#[derive(Debug, Clone)]
86pub enum Record {
87 E(Event),
89 L(LifeCycle),
91 Layout(Size),
92 Update(Region),
93 Paint,
94 None,
98}
99
100pub trait TestWidgetExt<T: Data>: Widget<T> + Sized + 'static {
102 fn record(self, recording: &Recording) -> Recorder<Self> {
103 Recorder {
104 child: self,
105 recording: recording.clone(),
106 }
107 }
108}
109
110impl<T: Data, W: Widget<T> + 'static> TestWidgetExt<T> for W {}
111
112#[allow(dead_code)]
113impl<S, T> ModularWidget<S, T> {
114 pub fn new(state: S) -> Self {
115 ModularWidget {
116 state,
117 event: None,
118 lifecycle: None,
119 update: None,
120 layout: None,
121 paint: None,
122 }
123 }
124
125 pub fn event_fn(
126 mut self,
127 f: impl FnMut(&mut S, &mut EventCtx, &Event, &mut T, &Env) + 'static,
128 ) -> Self {
129 self.event = Some(Box::new(f));
130 self
131 }
132
133 pub fn lifecycle_fn(
134 mut self,
135 f: impl FnMut(&mut S, &mut LifeCycleCtx, &LifeCycle, &T, &Env) + 'static,
136 ) -> Self {
137 self.lifecycle = Some(Box::new(f));
138 self
139 }
140
141 pub fn update_fn(
142 mut self,
143 f: impl FnMut(&mut S, &mut UpdateCtx, &T, &T, &Env) + 'static,
144 ) -> Self {
145 self.update = Some(Box::new(f));
146 self
147 }
148
149 pub fn layout_fn(
150 mut self,
151 f: impl FnMut(&mut S, &mut LayoutCtx, &BoxConstraints, &T, &Env) -> Size + 'static,
152 ) -> Self {
153 self.layout = Some(Box::new(f));
154 self
155 }
156
157 pub fn paint_fn(mut self, f: impl FnMut(&mut S, &mut PaintCtx, &T, &Env) + 'static) -> Self {
158 self.paint = Some(Box::new(f));
159 self
160 }
161}
162
163impl<S, T: Data> Widget<T> for ModularWidget<S, T> {
164 fn event(&mut self, ctx: &mut EventCtx, event: &Event, data: &mut T, env: &Env) {
165 if let Some(f) = self.event.as_mut() {
166 f(&mut self.state, ctx, event, data, env)
167 }
168 }
169
170 fn lifecycle(&mut self, ctx: &mut LifeCycleCtx, event: &LifeCycle, data: &T, env: &Env) {
171 if let Some(f) = self.lifecycle.as_mut() {
172 f(&mut self.state, ctx, event, data, env)
173 }
174 }
175
176 fn update(&mut self, ctx: &mut UpdateCtx, old_data: &T, data: &T, env: &Env) {
177 if let Some(f) = self.update.as_mut() {
178 f(&mut self.state, ctx, old_data, data, env)
179 }
180 }
181
182 fn layout(&mut self, ctx: &mut LayoutCtx, bc: &BoxConstraints, data: &T, env: &Env) -> Size {
183 let ModularWidget {
184 ref mut state,
185 ref mut layout,
186 ..
187 } = self;
188 layout
189 .as_mut()
190 .map(|f| f(state, ctx, bc, data, env))
191 .unwrap_or_else(|| Size::new(100., 100.))
192 }
193
194 fn paint(&mut self, ctx: &mut PaintCtx, data: &T, env: &Env) {
195 if let Some(f) = self.paint.as_mut() {
196 f(&mut self.state, ctx, data, env)
197 }
198 }
199}
200
201impl<T: Data> ReplaceChild<T> {
202 pub fn new<W: Widget<T> + 'static>(
203 child: impl Widget<T> + 'static,
204 f: impl Fn() -> W + 'static,
205 ) -> Self {
206 let child = WidgetPod::new(child.boxed());
207 let replacer = Box::new(move || f().boxed());
208 ReplaceChild { child, replacer }
209 }
210}
211
212impl<T: Data> Widget<T> for ReplaceChild<T> {
213 fn event(&mut self, ctx: &mut EventCtx, event: &Event, data: &mut T, env: &Env) {
214 if let Event::Command(cmd) = event {
215 if cmd.is(REPLACE_CHILD) {
216 self.child = WidgetPod::new((self.replacer)());
217 ctx.children_changed();
218 return;
219 }
220 }
221 self.child.event(ctx, event, data, env)
222 }
223
224 fn lifecycle(&mut self, ctx: &mut LifeCycleCtx, event: &LifeCycle, data: &T, env: &Env) {
225 self.child.lifecycle(ctx, event, data, env)
226 }
227
228 fn update(&mut self, ctx: &mut UpdateCtx, _old_data: &T, data: &T, env: &Env) {
229 self.child.update(ctx, data, env)
230 }
231
232 fn layout(&mut self, ctx: &mut LayoutCtx, bc: &BoxConstraints, data: &T, env: &Env) -> Size {
233 self.child.layout(ctx, bc, data, env)
234 }
235
236 fn paint(&mut self, ctx: &mut PaintCtx, data: &T, env: &Env) {
237 self.child.paint_raw(ctx, data, env)
238 }
239}
240
241#[allow(dead_code)]
242impl Recording {
243 pub fn is_empty(&self) -> bool {
244 self.0.borrow().is_empty()
245 }
246
247 #[allow(dead_code)]
248 pub fn len(&self) -> usize {
249 self.0.borrow().len()
250 }
251
252 pub fn clear(&self) {
253 self.0.borrow_mut().clear()
254 }
255
256 pub fn next(&self) -> Record {
260 self.0.borrow_mut().pop_front().unwrap_or(Record::None)
261 }
262
263 pub fn drain(&self) -> impl Iterator<Item = Record> {
265 self.0
266 .borrow_mut()
267 .drain(..)
268 .collect::<Vec<_>>()
269 .into_iter()
270 }
271
272 fn push(&self, event: Record) {
273 self.0.borrow_mut().push_back(event)
274 }
275}
276
277impl<T: Data, W: Widget<T>> Widget<T> for Recorder<W> {
278 fn event(&mut self, ctx: &mut EventCtx, event: &Event, data: &mut T, env: &Env) {
279 self.recording.push(Record::E(event.clone()));
280 self.child.event(ctx, event, data, env)
281 }
282
283 fn lifecycle(&mut self, ctx: &mut LifeCycleCtx, event: &LifeCycle, data: &T, env: &Env) {
284 let should_record = !matches!(
285 event,
286 LifeCycle::Internal(InternalLifeCycle::DebugRequestState { .. })
287 | LifeCycle::Internal(InternalLifeCycle::DebugInspectState(_))
288 );
289
290 if should_record {
291 self.recording.push(Record::L(event.clone()));
292 }
293
294 self.child.lifecycle(ctx, event, data, env)
295 }
296
297 fn update(&mut self, ctx: &mut UpdateCtx, old_data: &T, data: &T, env: &Env) {
298 self.child.update(ctx, old_data, data, env);
299 self.recording
300 .push(Record::Update(ctx.widget_state.invalid.clone()));
301 }
302
303 fn layout(&mut self, ctx: &mut LayoutCtx, bc: &BoxConstraints, data: &T, env: &Env) -> Size {
304 let size = self.child.layout(ctx, bc, data, env);
305 self.recording.push(Record::Layout(size));
306 size
307 }
308
309 fn paint(&mut self, ctx: &mut PaintCtx, data: &T, env: &Env) {
310 self.child.paint(ctx, data, env);
311 self.recording.push(Record::Paint)
312 }
313}
314
315pub fn widget_ids<const N: usize>() -> [WidgetId; N] {
316 let mut ids = [WidgetId::reserved(0); N];
317
318 for id in &mut ids {
319 *id = WidgetId::next()
320 }
321
322 ids
323}