1use std::{
2 borrow::Cow,
3 hash::{
4 Hash,
5 Hasher,
6 },
7};
8
9use paste::paste;
10use ragnarok::CursorPoint;
11use rustc_hash::{
12 FxHashMap,
13 FxHasher,
14};
15use torin::{
16 content::Content,
17 gaps::Gaps,
18 prelude::{
19 Alignment,
20 Direction,
21 Length,
22 Position,
23 VisibleSize,
24 },
25 size::Size,
26};
27
28use crate::{
29 data::{
30 AccessibilityData,
31 EffectData,
32 LayoutData,
33 Overflow,
34 TextStyleData,
35 },
36 diff_key::DiffKey,
37 element::{
38 Element,
39 EventHandlerType,
40 },
41 elements::image::{
42 AspectRatio,
43 ImageCover,
44 ImageData,
45 SamplingMode,
46 },
47 event_handler::EventHandler,
48 events::{
49 data::{
50 Event,
51 KeyboardEventData,
52 MouseEventData,
53 SizedEventData,
54 WheelEventData,
55 },
56 name::EventName,
57 },
58 layers::Layer,
59 prelude::*,
60 style::{
61 font_size::FontSize,
62 font_slant::FontSlant,
63 font_weight::FontWeight,
64 font_width::FontWidth,
65 scale::Scale,
66 text_height::TextHeightBehavior,
67 text_overflow::TextOverflow,
68 text_shadow::TextShadow,
69 transform_origin::TransformOrigin,
70 },
71};
72
73pub trait ChildrenExt: Sized {
75 fn get_children(&mut self) -> &mut Vec<Element>;
86
87 fn children(mut self, children: impl IntoIterator<Item = Element>) -> Self {
94 self.get_children().extend(children);
95 self
96 }
97
98 fn maybe_child<C: IntoElement>(mut self, child: Option<C>) -> Self {
105 if let Some(child) = child {
106 self.get_children().push(child.into_element());
107 }
108 self
109 }
110
111 fn child<C: IntoElement>(mut self, child: C) -> Self {
118 self.get_children().push(child.into_element());
119 self
120 }
121}
122
123pub trait KeyExt: Sized {
125 fn write_key(&mut self) -> &mut DiffKey;
127
128 fn key(mut self, key: impl Hash) -> Self {
130 let mut hasher = FxHasher::default();
131 key.hash(&mut hasher);
132 *self.write_key() = DiffKey::U64(hasher.finish());
133 self
134 }
135}
136
137pub trait ListExt {
139 fn with(self, other: Self) -> Self;
141}
142
143impl<T> ListExt for Vec<T> {
144 fn with(mut self, other: Self) -> Self {
145 self.extend(other);
146 self
147 }
148}
149
150macro_rules! event_handlers {
151 (
152 $handler_variant:ident, $event_data:ty;
153 $(
154 $(#[$attr:meta])*
155 $name:ident => $event_variant:expr ;
156 )*
157 ) => {
158 paste! {
159 $(
160 $(#[$attr])*
161 fn [<on_$name>](mut self, [<on_$name>]: impl Into<EventHandler<Event<$event_data>>>) -> Self {
162 self.get_event_handlers()
163 .insert($event_variant, EventHandlerType::$handler_variant([<on_$name>].into()));
164 self
165 }
166 )*
167 }
168 };
169}
170
171pub trait EventHandlersExt: Sized {
179 fn get_event_handlers(&mut self) -> &mut FxHashMap<EventName, EventHandlerType>;
181
182 fn with_event_handlers(
184 mut self,
185 event_handlers: FxHashMap<EventName, EventHandlerType>,
186 ) -> Self {
187 *self.get_event_handlers() = event_handlers;
188 self
189 }
190
191 event_handlers! {
192 Mouse,
193 MouseEventData;
194
195 mouse_down => EventName::MouseDown;
197 mouse_up => EventName::MouseUp;
199 mouse_move => EventName::MouseMove;
201
202 }
203
204 event_handlers! {
205 Pointer,
206 PointerEventData;
207
208 global_pointer_press => EventName::GlobalPointerPress;
210 global_pointer_down => EventName::GlobalPointerDown;
212 global_pointer_move => EventName::GlobalPointerMove;
214
215 capture_global_pointer_move => EventName::CaptureGlobalPointerMove;
217 capture_global_pointer_press => EventName::CaptureGlobalPointerPress;
219 }
220
221 event_handlers! {
222 Keyboard,
223 KeyboardEventData;
224
225 key_down => EventName::KeyDown;
227 key_up => EventName::KeyUp;
229
230 global_key_down => EventName::GlobalKeyDown;
232 global_key_up => EventName::GlobalKeyUp;
234 }
235
236 event_handlers! {
237 Wheel,
238 WheelEventData;
239
240 wheel => EventName::Wheel;
242 }
243
244 event_handlers! {
245 Touch,
246 TouchEventData;
247
248 touch_cancel => EventName::TouchCancel;
250 touch_start => EventName::TouchStart;
252 touch_move => EventName::TouchMove;
254 touch_end => EventName::TouchEnd;
256 }
257
258 event_handlers! {
259 Pointer,
260 PointerEventData;
261
262 pointer_press => EventName::PointerPress;
264 pointer_down => EventName::PointerDown;
266 pointer_move => EventName::PointerMove;
268 pointer_enter => EventName::PointerEnter;
270 pointer_leave => EventName::PointerLeave;
272 pointer_over => EventName::PointerOver;
274 pointer_out => EventName::PointerOut;
276 }
277
278 event_handlers! {
279 File,
280 FileEventData;
281
282 file_drop => EventName::FileDrop;
284 global_file_hover => EventName::GlobalFileHover;
286 global_file_hover_cancelled => EventName::GlobalFileHoverCancelled;
288 }
289
290 event_handlers! {
291 ImePreedit,
292 ImePreeditEventData;
293
294 ime_preedit => EventName::ImePreedit;
296 }
297
298 fn on_sized(mut self, on_sized: impl Into<EventHandler<Event<SizedEventData>>>) -> Self
300 where
301 Self: LayoutExt,
302 {
303 self.get_event_handlers()
304 .insert(EventName::Sized, EventHandlerType::Sized(on_sized.into()));
305 self.get_layout().layout.has_layout_references = true;
306 self
307 }
308
309 fn on_press(self, on_press: impl Into<EventHandler<Event<PressEventData>>>) -> Self {
316 let on_press = on_press.into();
317 self.on_pointer_press({
318 let on_press = on_press.clone();
319 move |e: Event<PointerEventData>| {
320 let event = e.try_map(|d| match d {
321 PointerEventData::Mouse(m) if m.button == Some(MouseButton::Left) => {
322 Some(PressEventData::Mouse(m))
323 }
324 PointerEventData::Touch(t) => Some(PressEventData::Touch(t)),
325 _ => None,
326 });
327 if let Some(event) = event {
328 on_press.call(event);
329 }
330 }
331 })
332 .on_key_down(move |e: Event<KeyboardEventData>| {
333 if e.is_press_event() {
334 on_press.call(e.map(PressEventData::Keyboard))
335 }
336 })
337 }
338
339 fn on_secondary_down(
343 self,
344 on_secondary_down: impl Into<EventHandler<Event<PressEventData>>>,
345 ) -> Self {
346 let on_secondary_down = on_secondary_down.into();
347 self.on_pointer_down(move |e: Event<PointerEventData>| {
348 let event = e.try_map(|d| match d {
349 PointerEventData::Mouse(m) if m.button == Some(MouseButton::Right) => {
350 Some(PressEventData::Mouse(m))
351 }
352 _ => None,
353 });
354 if let Some(event) = event {
355 on_secondary_down.call(event);
356 }
357 })
358 }
359
360 fn on_all_press(self, on_press: impl Into<EventHandler<Event<PressEventData>>>) -> Self {
365 let on_press = on_press.into();
366 self.on_pointer_press({
367 let on_press = on_press.clone();
368 move |e: Event<PointerEventData>| {
369 let event = e.map(|d| match d {
370 PointerEventData::Mouse(m) => PressEventData::Mouse(m),
371 PointerEventData::Touch(t) => PressEventData::Touch(t),
372 });
373 on_press.call(event);
374 }
375 })
376 .on_key_down(move |e: Event<KeyboardEventData>| {
377 if e.is_press_event() {
378 on_press.call(e.map(PressEventData::Keyboard))
379 }
380 })
381 }
382 fn on_focus_press(
388 self,
389 on_focus_press: impl Into<EventHandler<Event<FocusPressEventData>>>,
390 ) -> Self {
391 let on_focus_press = on_focus_press.into();
392 if cfg!(target_os = "android") {
393 self.on_pointer_press(move |e: Event<PointerEventData>| {
394 let event = e.try_map(|d| match d {
395 PointerEventData::Mouse(m) if m.button == Some(MouseButton::Left) => {
396 Some(FocusPressEventData::Mouse(m))
397 }
398 PointerEventData::Touch(t) => Some(FocusPressEventData::Touch(t)),
399 _ => None,
400 });
401 if let Some(event) = event {
402 on_focus_press.call(event);
403 }
404 })
405 } else {
406 self.on_pointer_down(move |e: Event<PointerEventData>| {
407 let event = e.try_map(|d| match d {
408 PointerEventData::Mouse(m) if m.button == Some(MouseButton::Left) => {
409 Some(FocusPressEventData::Mouse(m))
410 }
411 PointerEventData::Touch(t) => Some(FocusPressEventData::Touch(t)),
412 _ => None,
413 });
414 if let Some(event) = event {
415 on_focus_press.call(event);
416 }
417 })
418 }
419 }
420}
421
422#[derive(Debug, Clone, PartialEq)]
424pub enum FocusPressEventData {
425 Mouse(MouseEventData),
426 Touch(TouchEventData),
427}
428
429impl FocusPressEventData {
430 pub fn global_location(&self) -> CursorPoint {
431 match self {
432 Self::Mouse(m) => m.global_location,
433 Self::Touch(t) => t.global_location,
434 }
435 }
436
437 pub fn element_location(&self) -> CursorPoint {
438 match self {
439 Self::Mouse(m) => m.element_location,
440 Self::Touch(t) => t.element_location,
441 }
442 }
443
444 pub fn button(&self) -> Option<MouseButton> {
445 match self {
446 Self::Mouse(m) => m.button,
447 Self::Touch(_) => None,
448 }
449 }
450}
451
452#[derive(Debug, Clone, PartialEq)]
454pub enum PressEventData {
455 Mouse(MouseEventData),
456 Keyboard(KeyboardEventData),
457 Touch(TouchEventData),
458}
459
460pub trait ContainerWithContentExt
462where
463 Self: LayoutExt,
464{
465 fn direction(mut self, direction: Direction) -> Self {
467 self.get_layout().layout.direction = direction;
468 self
469 }
470 fn main_align(mut self, main_align: Alignment) -> Self {
472 self.get_layout().layout.main_alignment = main_align;
473 self
474 }
475
476 fn cross_align(mut self, cross_align: Alignment) -> Self {
478 self.get_layout().layout.cross_alignment = cross_align;
479 self
480 }
481
482 fn spacing(mut self, spacing: impl Into<f32>) -> Self {
484 self.get_layout().layout.spacing = Length::new(spacing.into());
485 self
486 }
487
488 fn content(mut self, content: Content) -> Self {
490 self.get_layout().layout.content = content;
491 self
492 }
493 fn center(mut self) -> Self {
495 self.get_layout().layout.main_alignment = Alignment::Center;
496 self.get_layout().layout.cross_alignment = Alignment::Center;
497
498 self
499 }
500
501 fn offset_x(mut self, offset_x: impl Into<f32>) -> Self {
503 self.get_layout().layout.offset_x = Length::new(offset_x.into());
504 self
505 }
506
507 fn offset_y(mut self, offset_y: impl Into<f32>) -> Self {
509 self.get_layout().layout.offset_y = Length::new(offset_y.into());
510 self
511 }
512
513 fn vertical(mut self) -> Self {
515 self.get_layout().layout.direction = Direction::vertical();
516 self
517 }
518
519 fn horizontal(mut self) -> Self {
521 self.get_layout().layout.direction = Direction::horizontal();
522 self
523 }
524}
525
526pub trait ContainerSizeExt
528where
529 Self: LayoutExt,
530{
531 fn width(mut self, width: impl Into<Size>) -> Self {
533 self.get_layout().layout.width = width.into();
534 self
535 }
536
537 fn height(mut self, height: impl Into<Size>) -> Self {
539 self.get_layout().layout.height = height.into();
540 self
541 }
542
543 fn expanded(mut self) -> Self {
545 self.get_layout().layout.width = Size::fill();
546 self.get_layout().layout.height = Size::fill();
547 self
548 }
549}
550
551impl<T: ContainerExt> ContainerSizeExt for T {}
552
553pub trait ContainerExt
555where
556 Self: LayoutExt,
557{
558 fn position(mut self, position: impl Into<Position>) -> Self {
560 self.get_layout().layout.position = position.into();
561 self
562 }
563
564 fn padding(mut self, padding: impl Into<Gaps>) -> Self {
566 self.get_layout().layout.padding = padding.into();
567 self
568 }
569
570 fn margin(mut self, margin: impl Into<Gaps>) -> Self {
572 self.get_layout().layout.margin = margin.into();
573 self
574 }
575
576 fn min_width(mut self, minimum_width: impl Into<Size>) -> Self {
578 self.get_layout().layout.minimum_width = minimum_width.into();
579 self
580 }
581
582 fn min_height(mut self, minimum_height: impl Into<Size>) -> Self {
584 self.get_layout().layout.minimum_height = minimum_height.into();
585 self
586 }
587
588 fn max_width(mut self, maximum_width: impl Into<Size>) -> Self {
590 self.get_layout().layout.maximum_width = maximum_width.into();
591 self
592 }
593
594 fn max_height(mut self, maximum_height: impl Into<Size>) -> Self {
596 self.get_layout().layout.maximum_height = maximum_height.into();
597 self
598 }
599
600 fn visible_width(mut self, visible_width: impl Into<VisibleSize>) -> Self {
602 self.get_layout().layout.visible_width = visible_width.into();
603 self
604 }
605
606 fn visible_height(mut self, visible_height: impl Into<VisibleSize>) -> Self {
608 self.get_layout().layout.visible_height = visible_height.into();
609 self
610 }
611}
612
613pub trait LayoutExt
615where
616 Self: Sized,
617{
618 fn get_layout(&mut self) -> &mut LayoutData;
620
621 fn layout(mut self, layout: LayoutData) -> Self {
623 *self.get_layout() = layout;
624 self
625 }
626}
627
628pub trait ImageExt
630where
631 Self: LayoutExt,
632{
633 fn get_image_data(&mut self) -> &mut ImageData;
635
636 fn image_data(mut self, image_data: ImageData) -> Self {
638 *self.get_image_data() = image_data;
639 self
640 }
641
642 fn sampling_mode(mut self, sampling_mode: SamplingMode) -> Self {
644 self.get_image_data().sampling_mode = sampling_mode;
645 self
646 }
647
648 fn aspect_ratio(mut self, aspect_ratio: AspectRatio) -> Self {
650 self.get_image_data().aspect_ratio = aspect_ratio;
651 self
652 }
653
654 fn image_cover(mut self, image_cover: ImageCover) -> Self {
656 self.get_image_data().image_cover = image_cover;
657 self
658 }
659}
660
661pub trait AccessibilityExt: Sized {
663 fn get_accessibility_data(&mut self) -> &mut AccessibilityData;
665
666 fn accessibility(mut self, accessibility: AccessibilityData) -> Self {
668 *self.get_accessibility_data() = accessibility;
669 self
670 }
671
672 fn a11y_id(mut self, a11y_id: impl Into<Option<AccessibilityId>>) -> Self {
674 self.get_accessibility_data().a11y_id = a11y_id.into();
675 self
676 }
677
678 fn a11y_focusable(mut self, a11y_focusable: impl Into<Focusable>) -> Self {
680 self.get_accessibility_data().a11y_focusable = a11y_focusable.into();
681 self
682 }
683
684 fn a11y_auto_focus(mut self, a11y_auto_focus: impl Into<bool>) -> Self {
686 self.get_accessibility_data().a11y_auto_focus = a11y_auto_focus.into();
687 self
688 }
689
690 fn a11y_member_of(mut self, a11y_member_of: impl Into<AccessibilityId>) -> Self {
692 self.get_accessibility_data()
693 .builder
694 .set_member_of(a11y_member_of.into());
695 self
696 }
697
698 fn a11y_role(mut self, a11y_role: impl Into<AccessibilityRole>) -> Self {
700 self.get_accessibility_data()
701 .builder
702 .set_role(a11y_role.into());
703 self
704 }
705
706 fn a11y_alt(mut self, value: impl Into<Box<str>>) -> Self {
708 self.get_accessibility_data().builder.set_label(value);
709 self
710 }
711
712 fn a11y_builder(mut self, with: impl FnOnce(&mut accesskit::Node)) -> Self {
714 with(&mut self.get_accessibility_data().builder);
715 self
716 }
717}
718
719pub trait TextStyleExt
721where
722 Self: Sized,
723{
724 fn get_text_style_data(&mut self) -> &mut TextStyleData;
726
727 fn text_style(mut self, data: TextStyleData) -> Self {
729 *self.get_text_style_data() = data;
730 self
731 }
732
733 fn color(mut self, color: impl Into<Fill>) -> Self {
735 self.get_text_style_data().color = Some(color.into());
736 self
737 }
738
739 fn text_align(mut self, text_align: impl Into<TextAlign>) -> Self {
741 self.get_text_style_data().text_align = Some(text_align.into());
742 self
743 }
744
745 fn font_size(mut self, font_size: impl Into<FontSize>) -> Self {
747 self.get_text_style_data().font_size = Some(font_size.into());
748 self
749 }
750
751 fn font_family(mut self, font_family: impl Into<Cow<'static, str>>) -> Self {
753 self.get_text_style_data()
754 .font_families
755 .push(font_family.into());
756 self
757 }
758
759 fn font_slant(mut self, font_slant: impl Into<FontSlant>) -> Self {
761 self.get_text_style_data().font_slant = Some(font_slant.into());
762 self
763 }
764
765 fn font_weight(mut self, font_weight: impl Into<FontWeight>) -> Self {
767 self.get_text_style_data().font_weight = Some(font_weight.into());
768 self
769 }
770
771 fn font_width(mut self, font_width: impl Into<FontWidth>) -> Self {
773 self.get_text_style_data().font_width = Some(font_width.into());
774 self
775 }
776
777 fn text_height(mut self, text_height: impl Into<TextHeightBehavior>) -> Self {
779 self.get_text_style_data().text_height = Some(text_height.into());
780 self
781 }
782
783 fn text_overflow(mut self, text_overflow: impl Into<TextOverflow>) -> Self {
785 self.get_text_style_data().text_overflow = Some(text_overflow.into());
786 self
787 }
788
789 fn text_shadow(mut self, text_shadow: impl Into<TextShadow>) -> Self {
791 self.get_text_style_data()
792 .text_shadows
793 .push(text_shadow.into());
794 self
795 }
796
797 fn text_decoration(mut self, text_decoration: impl Into<TextDecoration>) -> Self {
799 self.get_text_style_data().text_decoration = Some(text_decoration.into());
800 self
801 }
802}
803
804pub trait StyleExt
806where
807 Self: Sized,
808{
809 fn get_style(&mut self) -> &mut StyleState;
811
812 fn background(mut self, background: impl Into<Fill>) -> Self {
814 self.get_style().background = background.into();
815 self
816 }
817
818 fn border(mut self, border: impl Into<Option<Border>>) -> Self {
820 if let Some(border) = border.into() {
821 self.get_style().borders.push(border);
822 }
823 self
824 }
825
826 fn shadow(mut self, shadow: impl Into<Shadow>) -> Self {
828 self.get_style().shadows.push(shadow.into());
829 self
830 }
831
832 fn corner_radius(mut self, corner_radius: impl Into<CornerRadius>) -> Self {
834 self.get_style().corner_radius = corner_radius.into();
835 self
836 }
837}
838
839impl<T: StyleExt> CornerRadiusExt for T {
840 fn with_corner_radius(mut self, corner_radius: f32) -> Self {
841 self.get_style().corner_radius = CornerRadius::new_all(corner_radius);
842 self
843 }
844}
845
846pub trait CornerRadiusExt: Sized {
848 fn with_corner_radius(self, corner_radius: f32) -> Self;
850
851 fn rounded_none(self) -> Self {
853 self.with_corner_radius(0.)
854 }
855
856 fn rounded(self) -> Self {
858 self.with_corner_radius(6.)
859 }
860
861 fn rounded_sm(self) -> Self {
863 self.with_corner_radius(4.)
864 }
865
866 fn rounded_md(self) -> Self {
868 self.with_corner_radius(6.)
869 }
870
871 fn rounded_lg(self) -> Self {
873 self.with_corner_radius(8.)
874 }
875
876 fn rounded_xl(self) -> Self {
878 self.with_corner_radius(12.)
879 }
880
881 fn rounded_2xl(self) -> Self {
883 self.with_corner_radius(16.)
884 }
885
886 fn rounded_3xl(self) -> Self {
888 self.with_corner_radius(24.)
889 }
890
891 fn rounded_4xl(self) -> Self {
893 self.with_corner_radius(32.)
894 }
895
896 fn rounded_full(self) -> Self {
898 self.with_corner_radius(99.)
899 }
900}
901
902pub trait MaybeExt
904where
905 Self: Sized,
906{
907 fn maybe(self, bool: impl Into<bool>, then: impl FnOnce(Self) -> Self) -> Self {
909 if bool.into() { then(self) } else { self }
910 }
911
912 fn map<T>(self, data: Option<T>, then: impl FnOnce(Self, T) -> Self) -> Self {
914 if let Some(data) = data {
915 then(self, data)
916 } else {
917 self
918 }
919 }
920}
921
922pub trait LayerExt
924where
925 Self: Sized,
926{
927 fn get_layer(&mut self) -> &mut Layer;
929
930 fn layer(mut self, layer: impl Into<Layer>) -> Self {
932 *self.get_layer() = layer.into();
933 self
934 }
935}
936
937pub trait ScrollableExt
938where
939 Self: Sized,
940{
941 fn get_effect(&mut self) -> &mut EffectData;
943
944 fn scrollable(mut self, scrollable: impl Into<bool>) -> Self {
947 self.get_effect().scrollable = scrollable.into();
948 self
949 }
950}
951
952pub trait InteractiveExt
954where
955 Self: Sized,
956{
957 fn get_effect(&mut self) -> &mut EffectData;
959
960 fn interactive(mut self, interactive: impl Into<Interactive>) -> Self {
962 self.get_effect().interactive = interactive.into();
963 self
964 }
965}
966
967pub trait EffectExt: Sized {
969 fn get_effect(&mut self) -> &mut EffectData;
971
972 fn effect(mut self, effect: EffectData) -> Self {
974 *self.get_effect() = effect;
975 self
976 }
977
978 fn overflow(mut self, overflow: impl Into<Overflow>) -> Self {
980 self.get_effect().overflow = overflow.into();
981 self
982 }
983
984 fn blur(mut self, blur: impl Into<f32>) -> Self {
986 self.get_effect().blur = Some(blur.into());
987 self
988 }
989
990 fn rotation(mut self, rotation: impl Into<f32>) -> Self {
992 self.get_effect().rotation = Some(rotation.into());
993 self
994 }
995
996 fn opacity(mut self, opacity: impl Into<f32>) -> Self {
998 self.get_effect().opacity = Some(opacity.into());
999 self
1000 }
1001
1002 fn scale(mut self, scale: impl Into<Scale>) -> Self {
1004 self.get_effect().scale = Some(scale.into());
1005 self
1006 }
1007
1008 fn transform_origin(mut self, transform_origin: impl Into<TransformOrigin>) -> Self {
1012 self.get_effect().transform_origin = transform_origin.into();
1013 self
1014 }
1015}