1#![deny(missing_docs)]
2
3use floem_reactive::{Effect, SignalUpdate, UpdaterEffect};
8use std::rc::Rc;
9use ui_events::keyboard::{Key, KeyboardEvent, Modifiers};
10
11use crate::{
12 ViewId,
13 action::{set_window_scale, set_window_title},
14 animate::Animation,
15 context::EventCallbackConfig,
16 event::{EventCx, EventPropagation, listener},
17 platform::menu::Menu,
18 style::{Style, StyleClass},
19 view::{HasViewId, IntoView},
20};
21
22pub trait Decorators: IntoView {
32 fn style(self, style: impl Fn(Style) -> Style + 'static) -> Self::Intermediate {
81 let intermediate = self.into_intermediate();
82 let view_id = intermediate.view_id();
83 let state = view_id.state();
84
85 let offset = state.borrow_mut().style.next_offset();
86 let style = UpdaterEffect::new(
87 move || style(Style::new()),
88 move |style| {
89 view_id.update_style(offset, style);
90 },
91 );
92 state.borrow_mut().style.push(style);
93
94 intermediate
95 }
96
97 fn debug_name(self, name: impl Into<String>) -> Self::Intermediate {
101 let intermediate = self.into_intermediate();
102 let view_id = intermediate.view_id();
103 let state = view_id.state();
104 state.borrow_mut().debug_name.push(name.into());
105 intermediate
106 }
107
108 fn debug_name_if<S: Into<String>>(
113 self,
114 apply: impl Fn() -> bool + 'static,
115 name: impl Fn() -> S + 'static,
116 ) -> Self::Intermediate {
117 let intermediate = self.into_intermediate();
118 let view_id = intermediate.view_id();
119 Effect::new(move |_| {
120 let apply = apply();
121 let state = view_id.state();
122 if apply {
123 state.borrow_mut().debug_name.push(name().into());
124 } else {
125 state
126 .borrow_mut()
127 .debug_name
128 .retain_mut(|n| n != &name().into());
129 }
130 });
131
132 intermediate
133 }
134
135 fn dragging_style(self, style: impl Fn(Style) -> Style + 'static) -> Self::Intermediate {
137 let intermediate = self.into_intermediate();
138 let view_id = intermediate.view_id();
139 Effect::new(move |_| {
140 let style = style(Style::new());
141 {
142 let state = view_id.state();
143 state.borrow_mut().dragging_style = Some(style);
144 }
145 view_id.request_style(crate::style::recalc::StyleReason::full_recalc());
146 });
147 intermediate
148 }
149
150 fn class<C: StyleClass>(self, _class: C) -> Self::Intermediate {
152 let intermediate = self.into_intermediate();
153 intermediate.view_id().add_class(C::class_ref());
154 intermediate
155 }
156
157 fn class_if<C: StyleClass>(
159 self,
160 apply: impl Fn() -> bool + 'static,
161 _class: C,
162 ) -> Self::Intermediate {
163 let intermediate = self.into_intermediate();
164 let id = intermediate.view_id();
165 Effect::new(move |_| {
166 let apply = apply();
167 if apply {
168 id.add_class(C::class_ref());
169 } else {
170 ViewId::remove_class(&id, C::class_ref());
171 }
172 });
173 intermediate
174 }
175
176 fn remove_class<C: StyleClass>(self, _class: C) -> Self::Intermediate {
178 let intermediate = self.into_intermediate();
179 intermediate.view_id().remove_class(C::class_ref());
180 intermediate
181 }
182
183 #[deprecated(note = "Set this property using `Style::keyboard_navigable` instead")]
185 fn keyboard_navigable(self) -> Self::Intermediate {
186 self.style(|s| s.keyboard_navigable())
187 }
188
189 fn disable_default_event<L: listener::EventListenerTrait>(
196 self,
197 disable: impl Fn() -> (L, bool) + 'static,
198 ) -> Self::Intermediate {
199 let intermediate = self.into_intermediate();
200 let id = intermediate.view_id();
201 Effect::new(move |_| {
202 let (_event, disable) = disable();
203 if disable {
204 id.disable_default_event(L::listener_key());
205 } else {
206 id.remove_disable_default_event(L::listener_key());
207 }
208 });
209 intermediate
210 }
211
212 fn draggable(self) -> <<Self as IntoView>::Intermediate as IntoView>::Intermediate {
216 self.draggable_with_config(crate::event::DragConfig::default)
217 }
218
219 fn draggable_with_config(
234 self,
235 config: impl Fn() -> crate::event::DragConfig + 'static,
236 ) -> <<Self as IntoView>::Intermediate as IntoView>::Intermediate {
237 self.on_event_stop(listener::PointerDown, |cx, pbe| {
238 if let Some(pointer_id) = pbe.pointer.pointer_id
239 && pointer_id.is_primary_pointer()
240 {
241 cx.request_pointer_capture(pointer_id);
242 }
243 })
244 .on_event_stop(listener::GainedPointerCapture, move |cx, dt| {
245 cx.start_drag(*dt, config(), true);
246 })
247 }
248
249 #[deprecated(note = "use `Style::set_disabled` directly instead")]
254 fn disabled(self, disabled_fn: impl Fn() -> bool + 'static) -> Self::Intermediate {
255 self.style(move |s| s.set_disabled(disabled_fn()))
256 }
257
258 fn on_event_with_config<L: listener::EventListenerTrait>(
260 self,
261 listener: L,
262 config: EventCallbackConfig,
263 mut action: impl FnMut(&mut EventCx, &L::EventData) -> EventPropagation + 'static,
264 ) -> Self::Intermediate
265 where
266 L::EventData: Sized,
267 {
268 let _ = listener;
269 let intermediate = self.into_intermediate();
270 let id = intermediate.view_id();
271 id.add_event_listener(
272 L::listener_key(),
273 Box::new(move |cx| {
274 let event = std::mem::replace(&mut cx.event, crate::event::Event::Extracted);
275 let result = if let Some(extracted) = L::extract(&event) {
276 action(cx, extracted)
277 } else {
278 EventPropagation::Continue
279 };
280 cx.event = event;
281 result
282 }),
283 config,
284 );
285 intermediate
286 }
287
288 fn on_event_cont_with_config<L: listener::EventListenerTrait>(
291 self,
292 listener: L,
293 config: EventCallbackConfig,
294 mut action: impl FnMut(&mut EventCx, &L::EventData) + 'static,
295 ) -> Self::Intermediate
296 where
297 L::EventData: Clone,
298 {
299 self.on_event_with_config(listener, config, move |e, data| {
300 action(e, data);
301 EventPropagation::Continue
302 })
303 }
304
305 fn on_event_stop_with_config<L: listener::EventListenerTrait>(
308 self,
309 listener: L,
310 config: EventCallbackConfig,
311 mut action: impl FnMut(&mut EventCx, &L::EventData) + 'static,
312 ) -> Self::Intermediate
313 where
314 L::EventData: Clone,
315 {
316 self.on_event_with_config(listener, config, move |e, data| {
317 action(e, data);
318 EventPropagation::Stop
319 })
320 }
321
322 fn on_event<L: listener::EventListenerTrait>(
326 self,
327 listener: L,
328 action: impl FnMut(&mut EventCx, &L::EventData) -> EventPropagation + 'static,
329 ) -> Self::Intermediate
330 where
331 L::EventData: Sized,
332 {
333 self.on_event_with_config(listener, EventCallbackConfig::default(), action)
334 }
335
336 fn on_event_cont<L: listener::EventListenerTrait>(
339 self,
340 listener: L,
341 action: impl FnMut(&mut EventCx, &L::EventData) + 'static,
342 ) -> Self::Intermediate
343 where
344 L::EventData: Clone,
345 {
346 self.on_event_cont_with_config(listener, EventCallbackConfig::default(), action)
347 }
348
349 fn on_event_stop<L: listener::EventListenerTrait>(
352 self,
353 listener: L,
354 action: impl FnMut(&mut EventCx, &L::EventData) + 'static,
355 ) -> Self::Intermediate
356 where
357 L::EventData: Clone,
358 {
359 self.on_event_stop_with_config(listener, EventCallbackConfig::default(), action)
360 }
361
362 #[deprecated(note = "Use `on_event(listener::KeyDown, ...)` instead.")]
364 fn on_key_down(
365 self,
366 key: Key,
367 cmp: impl Fn(Modifiers) -> bool + 'static,
368 mut action: impl FnMut(&mut EventCx, &KeyboardEvent) + 'static,
369 ) -> Self::Intermediate {
370 self.on_event(listener::KeyDown, move |cx, kb_event| {
371 if kb_event.key == key && cmp(kb_event.modifiers) {
372 action(cx, kb_event);
373 return EventPropagation::Stop;
374 }
375 EventPropagation::Continue
376 })
377 }
378
379 #[deprecated(note = "Use `on_event(listener::KeyUp, ...)` instead.")]
381 fn on_key_up(
382 self,
383 key: Key,
384 cmp: impl Fn(Modifiers) -> bool + 'static,
385 mut action: impl FnMut(&mut EventCx, &KeyboardEvent) + 'static,
386 ) -> Self::Intermediate {
387 self.on_event(listener::KeyUp, move |e, kb_event| {
388 if kb_event.key == key && cmp(kb_event.modifiers) {
389 action(e, kb_event);
390 return EventPropagation::Stop;
391 }
392 EventPropagation::Continue
393 })
394 }
395
396 #[deprecated(
398 note = "Use `on_event(Click, |cx, _event| { ... })` instead. The new API provides direct access to typed event data."
399 )]
400 fn on_click(
401 self,
402 mut action: impl FnMut(&mut EventCx) -> EventPropagation + 'static,
403 ) -> Self::Intermediate {
404 self.on_event(listener::Click, move |cx, _event| action(cx))
405 }
406
407 #[deprecated(
410 note = "Use `on_event_cont(Click, |cx, _event| { ... })` instead. The new API provides direct access to typed event data."
411 )]
412 fn on_click_cont(self, mut action: impl FnMut(&mut EventCx) + 'static) -> Self::Intermediate {
413 self.on_event_cont(listener::Click, move |cx, _event| action(cx))
414 }
415
416 #[deprecated(
419 note = "Use `on_event_stop(Click, |cx, _event| { ... })` instead. The new API provides direct access to typed event data."
420 )]
421 fn on_click_stop(self, mut action: impl FnMut(&mut EventCx) + 'static) -> Self::Intermediate {
422 self.on_event_stop(listener::Click, move |cx, _event| action(cx))
423 }
424
425 fn action(self, mut action: impl FnMut() + 'static) -> Self::Intermediate {
427 self.on_event_stop(listener::Click, move |_cx, _event| action())
428 }
429
430 #[deprecated(
432 note = "Use `on_event(DoubleClick, |cx, _event| { ... })` instead. The new API provides direct access to typed event data."
433 )]
434 fn on_double_click(
435 self,
436 mut action: impl FnMut(&mut EventCx) -> EventPropagation + 'static,
437 ) -> Self::Intermediate {
438 self.on_event(listener::DoubleClick, move |cx, _event| action(cx))
439 }
440
441 #[deprecated(
444 note = "Use `on_event_cont(DoubleClick, |cx, _event| { ... })` instead. The new API provides direct access to typed event data."
445 )]
446 fn on_double_click_cont(
447 self,
448 mut action: impl FnMut(&mut EventCx) + 'static,
449 ) -> Self::Intermediate {
450 self.on_event_cont(listener::DoubleClick, move |cx, _event| action(cx))
451 }
452
453 #[deprecated(
456 note = "Use `on_event_stop(DoubleClick, |cx, _event| { ... })` instead. The new API provides direct access to typed event data."
457 )]
458 fn on_double_click_stop(
459 self,
460 mut action: impl FnMut(&mut EventCx) + 'static,
461 ) -> Self::Intermediate {
462 self.on_event_stop(listener::DoubleClick, move |cx, _event| action(cx))
463 }
464
465 #[deprecated(
467 note = "Use `on_event(SecondaryClick, |cx, _event| { ... })` instead. The new API provides direct access to typed event data."
468 )]
469 fn on_secondary_click(
470 self,
471 mut action: impl FnMut(&mut EventCx) -> EventPropagation + 'static,
472 ) -> Self::Intermediate {
473 self.on_event(listener::SecondaryClick, move |cx, _event| action(cx))
474 }
475
476 #[deprecated(
479 note = "Use `on_event_cont(SecondaryClick, |cx, _event| { ... })` instead. The new API provides direct access to typed event data."
480 )]
481 fn on_secondary_click_cont(
482 self,
483 mut action: impl FnMut(&mut EventCx) + 'static,
484 ) -> Self::Intermediate {
485 self.on_event_cont(listener::SecondaryClick, move |cx, _event| action(cx))
486 }
487
488 #[deprecated(
491 note = "Use `on_event_stop(SecondaryClick, |cx, _event| { ... })` instead. The new API provides direct access to typed event data."
492 )]
493 fn on_secondary_click_stop(
494 self,
495 mut action: impl FnMut(&mut EventCx) + 'static,
496 ) -> Self::Intermediate {
497 self.on_event_stop(listener::SecondaryClick, move |cx, _event| action(cx))
498 }
499
500 fn on_cleanup(self, action: impl Fn() + 'static) -> Self::Intermediate {
507 let intermediate = self.into_intermediate();
508 let id = intermediate.view_id();
509 let state = id.state();
510 state.borrow_mut().add_cleanup_listener(Rc::new(action));
511 intermediate
512 }
513
514 fn animation(self, animation: impl Fn(Animation) -> Animation + 'static) -> Self::Intermediate {
523 let intermediate = self.into_intermediate();
524 let view_id = intermediate.view_id();
525 let state = view_id.state();
526
527 let offset = state.borrow_mut().animations.next_offset();
528 let initial_animation = UpdaterEffect::new(
529 move || animation(Animation::new()),
530 move |animation| {
531 view_id.update_animation(offset, animation);
532 },
533 );
534 for effect_state in &initial_animation.effect_states {
535 effect_state.update(|stack| stack.push((view_id, offset)));
536 }
537
538 state.borrow_mut().animations.push(initial_animation);
539
540 intermediate
541 }
542
543 fn clear_focus(self, when: impl Fn() + 'static) -> Self::Intermediate {
548 let intermediate = self.into_intermediate();
549 let id = intermediate.view_id();
550 Effect::new(move |_| {
551 when();
552 ViewId::clear_focus(&id);
553 });
554 intermediate
555 }
556
557 fn request_focus(self, when: impl Fn() + 'static) -> Self::Intermediate {
562 let intermediate = self.into_intermediate();
563 let id = intermediate.view_id();
564 Effect::new(move |_| {
565 when();
566 ViewId::request_focus(&id);
567 });
568 intermediate
569 }
570
571 fn window_scale(self, scale_fn: impl Fn() -> f64 + 'static) -> Self::Intermediate {
578 let intermediate = self.into_intermediate();
579 Effect::new(move |_| {
580 let window_scale = scale_fn();
581 set_window_scale(window_scale);
582 });
583 intermediate
584 }
585
586 fn window_title(self, title_fn: impl Fn() -> String + 'static) -> Self::Intermediate {
593 let intermediate = self.into_intermediate();
594 Effect::new(move |_| {
595 let window_title = title_fn();
596 set_window_title(window_title);
597 });
598 intermediate
599 }
600
601 #[cfg(not(target_arch = "wasm32"))]
614 fn window_menu(self, menu_fn: impl Fn() -> Menu + 'static) -> Self::Intermediate {
615 let intermediate = self.into_intermediate();
616 Effect::new(move |_| {
617 let menu = menu_fn();
618 crate::action::set_window_menu(menu);
619 });
620 intermediate
621 }
622
623 fn context_menu(self, menu: impl Fn() -> Menu + 'static) -> Self::Intermediate {
628 let intermediate = self.into_intermediate();
629 let id = intermediate.view_id();
630 id.update_context_menu(menu);
631 intermediate
632 }
633
634 fn popout_menu(self, menu: impl Fn() -> Menu + 'static) -> Self::Intermediate {
639 let intermediate = self.into_intermediate();
640 let id = intermediate.view_id();
641 id.update_popout_menu(menu);
642 intermediate
643 }
644}
645
646impl<T: IntoView> Decorators for T {}