1use std::{
2 borrow::Cow,
3 hash::{
4 Hash,
5 Hasher,
6 },
7};
8
9use paste::paste;
10use rustc_hash::{
11 FxHashMap,
12 FxHasher,
13};
14use torin::{
15 content::Content,
16 gaps::Gaps,
17 prelude::{
18 Alignment,
19 Direction,
20 Length,
21 Position,
22 VisibleSize,
23 },
24 size::{
25 Size,
26 SizeFn,
27 SizeFnContext,
28 },
29};
30
31use crate::{
32 data::{
33 AccessibilityData,
34 LayoutData,
35 TextStyleData,
36 },
37 diff_key::DiffKey,
38 element::{
39 Element,
40 EventHandlerType,
41 },
42 elements::image::{
43 AspectRatio,
44 ImageCover,
45 ImageData,
46 SamplingMode,
47 },
48 event_handler::EventHandler,
49 events::{
50 data::{
51 Event,
52 KeyboardEventData,
53 MouseEventData,
54 SizedEventData,
55 WheelEventData,
56 },
57 name::EventName,
58 },
59 layers::Layer,
60 prelude::*,
61 style::{
62 font_size::FontSize,
63 font_slant::FontSlant,
64 font_weight::FontWeight,
65 font_width::FontWidth,
66 text_height::TextHeightBehavior,
67 text_overflow::TextOverflow,
68 text_shadow::TextShadow,
69 },
70};
71
72pub trait SizeExt {
73 fn auto() -> Size;
74 fn fill() -> Size;
75 fn fill_minimum() -> Size;
76 fn percent(percent: impl Into<f32>) -> Size;
77 fn px(px: impl Into<f32>) -> Size;
78 fn window_percent(percent: impl Into<f32>) -> Size;
79 fn flex(flex: impl Into<f32>) -> Size;
80 fn func(func: impl Fn(SizeFnContext) -> Option<f32> + 'static + Sync + Send) -> Size;
81 fn func_data<D: Hash>(
82 func: impl Fn(SizeFnContext) -> Option<f32> + 'static + Sync + Send,
83 data: &D,
84 ) -> Size;
85}
86
87impl SizeExt for Size {
88 fn auto() -> Size {
89 Size::Inner
90 }
91
92 fn fill() -> Size {
93 Size::Fill
94 }
95
96 fn fill_minimum() -> Size {
97 Size::FillMinimum
98 }
99
100 fn percent(percent: impl Into<f32>) -> Size {
101 Size::Percentage(Length::new(percent.into()))
102 }
103
104 fn px(px: impl Into<f32>) -> Size {
105 Size::Pixels(Length::new(px.into()))
106 }
107
108 fn window_percent(percent: impl Into<f32>) -> Size {
109 Size::RootPercentage(Length::new(percent.into()))
110 }
111
112 fn flex(flex: impl Into<f32>) -> Size {
113 Size::Flex(Length::new(flex.into()))
114 }
115
116 fn func(func: impl Fn(SizeFnContext) -> Option<f32> + 'static + Sync + Send) -> Size {
117 Self::Fn(Box::new(SizeFn::new(func)))
118 }
119
120 fn func_data<D: Hash>(
121 func: impl Fn(SizeFnContext) -> Option<f32> + 'static + Sync + Send,
122 data: &D,
123 ) -> Size {
124 Self::Fn(Box::new(SizeFn::new_data(func, data)))
125 }
126}
127
128pub trait DirectionExt {
129 fn vertical() -> Direction;
130 fn horizontal() -> Direction;
131}
132
133impl DirectionExt for Direction {
134 fn vertical() -> Direction {
135 Direction::Vertical
136 }
137 fn horizontal() -> Direction {
138 Direction::Horizontal
139 }
140}
141
142pub trait AlignmentExt {
143 fn start() -> Alignment;
144 fn center() -> Alignment;
145 fn end() -> Alignment;
146 fn space_between() -> Alignment;
147 fn space_evenly() -> Alignment;
148 fn space_around() -> Alignment;
149}
150
151impl AlignmentExt for Alignment {
152 fn start() -> Alignment {
153 Alignment::Start
154 }
155
156 fn center() -> Alignment {
157 Alignment::Center
158 }
159
160 fn end() -> Alignment {
161 Alignment::End
162 }
163
164 fn space_between() -> Alignment {
165 Alignment::SpaceBetween
166 }
167
168 fn space_evenly() -> Alignment {
169 Alignment::SpaceEvenly
170 }
171
172 fn space_around() -> Alignment {
173 Alignment::SpaceAround
174 }
175}
176
177pub trait ContentExt {
178 fn normal() -> Content;
179 fn fit() -> Content;
180 fn flex() -> Content;
181 fn wrap() -> Content;
182}
183
184impl ContentExt for Content {
185 fn normal() -> Content {
186 Content::Normal
187 }
188
189 fn fit() -> Content {
190 Content::Fit
191 }
192
193 fn flex() -> Content {
194 Content::Flex
195 }
196
197 fn wrap() -> Content {
198 Content::Wrap
199 }
200}
201
202pub trait VisibleSizeExt {
203 fn full() -> VisibleSize;
204 fn inner_percent(value: impl Into<f32>) -> VisibleSize;
205}
206
207impl VisibleSizeExt for VisibleSize {
208 fn full() -> VisibleSize {
209 VisibleSize::Full
210 }
211
212 fn inner_percent(value: impl Into<f32>) -> VisibleSize {
213 VisibleSize::InnerPercentage(Length::new(value.into()))
214 }
215}
216
217pub trait ChildrenExt: Sized {
218 fn get_children(&mut self) -> &mut Vec<Element>;
219
220 fn children(mut self, children: impl IntoIterator<Item = Element>) -> Self {
221 self.get_children().extend(children);
222 self
223 }
224
225 fn maybe_child<C: IntoElement>(mut self, child: Option<C>) -> Self {
226 if let Some(child) = child {
227 self.get_children().push(child.into_element());
228 }
229 self
230 }
231
232 fn child<C: IntoElement>(mut self, child: C) -> Self {
233 self.get_children().push(child.into_element());
234 self
235 }
236}
237
238pub trait KeyExt: Sized {
239 fn write_key(&mut self) -> &mut DiffKey;
240
241 fn key(mut self, key: impl Hash) -> Self {
242 let mut hasher = FxHasher::default();
243 key.hash(&mut hasher);
244 *self.write_key() = DiffKey::U64(hasher.finish());
245 self
246 }
247}
248
249pub trait ListExt {
250 fn with(self, other: Self) -> Self;
251}
252
253impl<T> ListExt for Vec<T> {
254 fn with(mut self, other: Self) -> Self {
255 self.extend(other);
256 self
257 }
258}
259
260macro_rules! event_handlers {
261 (
262 $handler_variant:ident, $event_data:ty;
263 $(
264 $name:ident => $event_variant:expr ;
265 )*
266 ) => {
267 paste! {
268 $(
269 fn [<on_$name>](mut self, [<on_$name>]: impl Into<EventHandler<Event<$event_data>>>) -> Self {
270 self.get_event_handlers()
271 .insert($event_variant, EventHandlerType::$handler_variant([<on_$name>].into()));
272 self
273 }
274 )*
275 }
276 };
277}
278
279pub trait EventHandlersExt: Sized + LayoutExt {
280 fn get_event_handlers(&mut self) -> &mut FxHashMap<EventName, EventHandlerType>;
281
282 event_handlers! {
283 Mouse,
284 MouseEventData;
285
286 mouse_down => EventName::MouseDown;
287 mouse_up => EventName::MouseUp;
288 mouse_move => EventName::MouseMove;
289
290 global_mouse_up => EventName::GlobalMouseUp;
291 global_mouse_down => EventName::GlobalMouseDown;
292 global_mouse_move => EventName::GlobalMouseMove;
293
294 capture_global_mouse_move => EventName::CaptureGlobalMouseMove;
295 capture_global_mouse_up => EventName::CaptureGlobalMouseUp;
296 }
297
298 event_handlers! {
299 Keyboard,
300 KeyboardEventData;
301
302 key_down => EventName::KeyDown;
303 key_up => EventName::KeyUp;
304
305 global_key_down => EventName::GlobalKeyDown;
306 global_key_up => EventName::GlobalKeyUp;
307 }
308
309 event_handlers! {
310 Wheel,
311 WheelEventData;
312
313 wheel => EventName::Wheel;
314 }
315
316 event_handlers! {
317 Touch,
318 TouchEventData;
319
320 touch_cancel => EventName::TouchCancel;
321 touch_start => EventName::TouchStart;
322 touch_move => EventName::TouchMove;
323 touch_end => EventName::TouchEnd;
324 }
325
326 event_handlers! {
327 Pointer,
328 PointerEventData;
329
330 pointer_press => EventName::PointerPress;
331 pointer_down => EventName::PointerDown;
332 pointer_enter => EventName::PointerEnter;
333 pointer_leave => EventName::PointerLeave;
334 }
335
336 event_handlers! {
337 File,
338 FileEventData;
339
340 file_drop => EventName::FileDrop;
341 global_file_hover => EventName::GlobalFileHover;
342 global_file_hover_cancelled => EventName::GlobalFileHoverCancelled;
343 }
344
345 event_handlers! {
346 ImePreedit,
347 ImePreeditEventData;
348
349 ime_preedit => EventName::ImePreedit;
350 }
351
352 fn on_sized(mut self, on_sized: impl Into<EventHandler<Event<SizedEventData>>>) -> Self {
353 self.get_event_handlers()
354 .insert(EventName::Sized, EventHandlerType::Sized(on_sized.into()));
355 self.get_layout().layout.has_layout_references = true;
356 self
357 }
358
359 fn on_press(self, on_press: impl Into<EventHandler<Event<PressEventData>>>) -> Self {
366 let on_press = on_press.into();
367 self.on_pointer_press({
368 let on_press = on_press.clone();
369 move |e: Event<PointerEventData>| {
370 let event = e.try_map(|d| match d {
371 PointerEventData::Mouse(m) if m.button == Some(MouseButton::Left) => {
372 Some(PressEventData::Mouse(m))
373 }
374 PointerEventData::Touch(t) => Some(PressEventData::Touch(t)),
375 _ => None,
376 });
377 if let Some(event) = event {
378 on_press.call(event);
379 }
380 }
381 })
382 .on_key_down({
383 let on_press = on_press.clone();
384 move |e: Event<KeyboardEventData>| {
385 if Focus::is_pressed(&e) {
386 on_press.call(e.map(PressEventData::Keyboard))
387 }
388 }
389 })
390 }
391
392 fn on_secondary_press(
396 self,
397 on_pointer_press: impl Into<EventHandler<Event<PressEventData>>>,
398 ) -> Self {
399 let on_pointer_press = on_pointer_press.into();
400 self.on_pointer_press({
401 let on_pointer_press = on_pointer_press.clone();
402 move |e: Event<PointerEventData>| {
403 let event = e.try_map(|d| match d {
404 PointerEventData::Mouse(m) if m.button == Some(MouseButton::Right) => {
405 Some(PressEventData::Mouse(m))
406 }
407 _ => None,
408 });
409 if let Some(event) = event {
410 on_pointer_press.call(event);
411 }
412 }
413 })
414 }
415
416 fn on_all_press(self, on_press: impl Into<EventHandler<Event<PressEventData>>>) -> Self {
421 let on_press = on_press.into();
422 self.on_pointer_press({
423 let on_press = on_press.clone();
424 move |e: Event<PointerEventData>| {
425 let event = e.try_map(|d| match d {
426 PointerEventData::Mouse(m) => Some(PressEventData::Mouse(m)),
427 PointerEventData::Touch(t) => Some(PressEventData::Touch(t)),
428 });
429 if let Some(event) = event {
430 on_press.call(event);
431 }
432 }
433 })
434 .on_key_down({
435 let on_press = on_press.clone();
436 move |e: Event<KeyboardEventData>| {
437 if Focus::is_pressed(&e) {
438 on_press.call(e.map(PressEventData::Keyboard))
439 }
440 }
441 })
442 }
443}
444
445#[derive(Debug, Clone, PartialEq)]
446pub enum PressEventData {
447 Mouse(MouseEventData),
448 Keyboard(KeyboardEventData),
449 Touch(TouchEventData),
450}
451
452pub trait ContainerWithContentExt
453where
454 Self: LayoutExt,
455{
456 fn direction(mut self, direction: Direction) -> Self {
457 self.get_layout().layout.direction = direction;
458 self
459 }
460 fn main_align(mut self, main_align: Alignment) -> Self {
461 self.get_layout().layout.main_alignment = main_align;
462 self
463 }
464
465 fn cross_align(mut self, cross_align: Alignment) -> Self {
466 self.get_layout().layout.cross_alignment = cross_align;
467 self
468 }
469
470 fn spacing(mut self, spacing: impl Into<f32>) -> Self {
471 self.get_layout().layout.spacing = Length::new(spacing.into());
472 self
473 }
474
475 fn content(mut self, content: Content) -> Self {
476 self.get_layout().layout.content = content;
477 self
478 }
479 fn center(mut self) -> Self {
480 self.get_layout().layout.main_alignment = Alignment::Center;
481 self.get_layout().layout.cross_alignment = Alignment::Center;
482
483 self
484 }
485
486 fn offset_x(mut self, offset_x: impl Into<f32>) -> Self {
487 self.get_layout().layout.offset_x = Length::new(offset_x.into());
488 self
489 }
490
491 fn offset_y(mut self, offset_y: impl Into<f32>) -> Self {
492 self.get_layout().layout.offset_y = Length::new(offset_y.into());
493 self
494 }
495
496 fn vertical(mut self) -> Self {
497 self.get_layout().layout.direction = Direction::vertical();
498 self
499 }
500
501 fn horizontal(mut self) -> Self {
502 self.get_layout().layout.direction = Direction::horizontal();
503 self
504 }
505}
506
507pub trait ContainerSizeExt
508where
509 Self: LayoutExt,
510{
511 fn width(mut self, width: impl Into<Size>) -> Self {
512 self.get_layout().layout.width = width.into();
513 self
514 }
515
516 fn height(mut self, height: impl Into<Size>) -> Self {
517 self.get_layout().layout.height = height.into();
518 self
519 }
520
521 fn expanded(mut self) -> Self {
523 self.get_layout().layout.width = Size::fill();
524 self.get_layout().layout.height = Size::fill();
525 self
526 }
527}
528
529impl<T: ContainerExt> ContainerSizeExt for T {}
530
531pub trait ContainerExt
532where
533 Self: LayoutExt,
534{
535 fn position(mut self, position: impl Into<Position>) -> Self {
536 self.get_layout().layout.position = position.into();
537 self
538 }
539
540 fn padding(mut self, padding: impl Into<Gaps>) -> Self {
541 self.get_layout().layout.padding = padding.into();
542 self
543 }
544
545 fn margin(mut self, margin: impl Into<Gaps>) -> Self {
546 self.get_layout().layout.margin = margin.into();
547 self
548 }
549
550 fn min_width(mut self, minimum_width: impl Into<Size>) -> Self {
551 self.get_layout().layout.minimum_width = minimum_width.into();
552 self
553 }
554
555 fn min_height(mut self, minimum_height: impl Into<Size>) -> Self {
556 self.get_layout().layout.minimum_height = minimum_height.into();
557 self
558 }
559
560 fn max_width(mut self, maximum_width: impl Into<Size>) -> Self {
561 self.get_layout().layout.maximum_width = maximum_width.into();
562 self
563 }
564
565 fn max_height(mut self, maximum_height: impl Into<Size>) -> Self {
566 self.get_layout().layout.maximum_height = maximum_height.into();
567 self
568 }
569
570 fn visible_width(mut self, visible_width: impl Into<VisibleSize>) -> Self {
571 self.get_layout().layout.visible_width = visible_width.into();
572 self
573 }
574
575 fn visible_height(mut self, visible_height: impl Into<VisibleSize>) -> Self {
576 self.get_layout().layout.visible_height = visible_height.into();
577 self
578 }
579}
580
581pub trait LayoutExt
582where
583 Self: Sized,
584{
585 fn get_layout(&mut self) -> &mut LayoutData;
586
587 fn layout(mut self, layout: LayoutData) -> Self {
588 *self.get_layout() = layout;
589 self
590 }
591}
592
593pub trait ImageExt
594where
595 Self: LayoutExt,
596{
597 fn get_image_data(&mut self) -> &mut ImageData;
598
599 fn image_data(mut self, image_data: ImageData) -> Self {
600 *self.get_image_data() = image_data;
601 self
602 }
603
604 fn sampling_mode(mut self, sampling_mode: SamplingMode) -> Self {
605 self.get_image_data().sampling_mode = sampling_mode;
606 self
607 }
608
609 fn aspect_ratio(mut self, aspect_ratio: AspectRatio) -> Self {
610 self.get_image_data().aspect_ratio = aspect_ratio;
611 self
612 }
613
614 fn image_cover(mut self, image_cover: ImageCover) -> Self {
615 self.get_image_data().image_cover = image_cover;
616 self
617 }
618}
619
620pub trait AccessibilityExt: Sized {
621 fn get_accessibility_data(&mut self) -> &mut AccessibilityData;
622
623 fn accessibility(mut self, accessibility: AccessibilityData) -> Self {
624 *self.get_accessibility_data() = accessibility;
625 self
626 }
627
628 fn a11y_id(mut self, a11y_id: impl Into<Option<AccessibilityId>>) -> Self {
629 self.get_accessibility_data().a11y_id = a11y_id.into();
630 self
631 }
632
633 fn a11y_focusable(mut self, a11y_focusable: impl Into<Focusable>) -> Self {
634 self.get_accessibility_data().a11y_focusable = a11y_focusable.into();
635 self
636 }
637
638 fn a11y_auto_focus(mut self, a11y_auto_focus: impl Into<bool>) -> Self {
639 self.get_accessibility_data().a11y_auto_focus = a11y_auto_focus.into();
640 self
641 }
642
643 fn a11y_member_of(mut self, a11y_member_of: impl Into<AccessibilityId>) -> Self {
644 self.get_accessibility_data()
645 .builder
646 .set_member_of(a11y_member_of.into());
647 self
648 }
649
650 fn a11y_role(mut self, a11y_role: impl Into<AccessibilityRole>) -> Self {
651 self.get_accessibility_data()
652 .builder
653 .set_role(a11y_role.into());
654 self
655 }
656
657 fn a11y_alt(mut self, value: impl Into<Box<str>>) -> Self {
658 self.get_accessibility_data().builder.set_label(value);
659 self
660 }
661
662 fn a11y_builder(mut self, with: impl FnOnce(&mut accesskit::Node)) -> Self {
663 with(&mut self.get_accessibility_data().builder);
664 self
665 }
666}
667
668pub trait TextStyleExt
669where
670 Self: Sized,
671{
672 fn get_text_style_data(&mut self) -> &mut TextStyleData;
673
674 fn color(mut self, color: impl Into<Color>) -> Self {
675 self.get_text_style_data().color = Some(color.into());
676 self
677 }
678
679 fn text_align(mut self, text_align: impl Into<TextAlign>) -> Self {
680 self.get_text_style_data().text_align = Some(text_align.into());
681 self
682 }
683
684 fn font_size(mut self, font_size: impl Into<FontSize>) -> Self {
685 self.get_text_style_data().font_size = Some(font_size.into());
686 self
687 }
688
689 fn font_family(mut self, font_family: impl Into<Cow<'static, str>>) -> Self {
690 self.get_text_style_data()
691 .font_families
692 .push(font_family.into());
693 self
694 }
695
696 fn font_slant(mut self, font_slant: impl Into<FontSlant>) -> Self {
697 self.get_text_style_data().font_slant = Some(font_slant.into());
698 self
699 }
700
701 fn font_weight(mut self, font_weight: impl Into<FontWeight>) -> Self {
702 self.get_text_style_data().font_weight = Some(font_weight.into());
703 self
704 }
705
706 fn font_width(mut self, font_width: impl Into<FontWidth>) -> Self {
707 self.get_text_style_data().font_width = Some(font_width.into());
708 self
709 }
710
711 fn text_height(mut self, text_height: impl Into<TextHeightBehavior>) -> Self {
712 self.get_text_style_data().text_height = Some(text_height.into());
713 self
714 }
715
716 fn text_overflow(mut self, text_overflow: impl Into<TextOverflow>) -> Self {
717 self.get_text_style_data().text_overflow = Some(text_overflow.into());
718 self
719 }
720
721 fn text_shadow(mut self, text_shadow: impl Into<TextShadow>) -> Self {
722 self.get_text_style_data()
723 .text_shadows
724 .push(text_shadow.into());
725 self
726 }
727}
728
729pub trait StyleExt
730where
731 Self: Sized,
732{
733 fn get_style(&mut self) -> &mut StyleState;
734
735 fn background<S: Into<Color>>(mut self, background: S) -> Self {
736 self.get_style().background = Fill::Color(background.into());
737 self
738 }
739
740 fn background_conic_gradient<S: Into<ConicGradient>>(mut self, background: S) -> Self {
741 self.get_style().background = Fill::ConicGradient(Box::new(background.into()));
742 self
743 }
744
745 fn background_linear_gradient<S: Into<LinearGradient>>(mut self, background: S) -> Self {
746 self.get_style().background = Fill::LinearGradient(Box::new(background.into()));
747 self
748 }
749
750 fn background_radial_gradient<S: Into<RadialGradient>>(mut self, background: S) -> Self {
751 self.get_style().background = Fill::RadialGradient(Box::new(background.into()));
752 self
753 }
754
755 fn border(mut self, border: impl Into<Option<Border>>) -> Self {
756 if let Some(border) = border.into() {
757 self.get_style().borders.push(border);
758 }
759 self
760 }
761
762 fn shadow(mut self, shadow: impl Into<Shadow>) -> Self {
763 self.get_style().shadows.push(shadow.into());
764 self
765 }
766
767 fn corner_radius(mut self, corner_radius: impl Into<CornerRadius>) -> Self {
768 self.get_style().corner_radius = corner_radius.into();
769 self
770 }
771}
772
773impl<T: StyleExt> CornerRadiusExt for T {
774 fn with_corner_radius(mut self, corner_radius: f32) -> Self {
775 self.get_style().corner_radius = CornerRadius::new_all(corner_radius);
776 self
777 }
778}
779
780pub trait CornerRadiusExt: Sized {
781 fn with_corner_radius(self, corner_radius: f32) -> Self;
782
783 fn rounded_none(self) -> Self {
785 self.with_corner_radius(0.)
786 }
787
788 fn rounded(self) -> Self {
790 self.with_corner_radius(6.)
791 }
792
793 fn rounded_sm(self) -> Self {
795 self.with_corner_radius(4.)
796 }
797
798 fn rounded_md(self) -> Self {
800 self.with_corner_radius(6.)
801 }
802
803 fn rounded_lg(self) -> Self {
805 self.with_corner_radius(8.)
806 }
807
808 fn rounded_xl(self) -> Self {
810 self.with_corner_radius(12.)
811 }
812
813 fn rounded_2xl(self) -> Self {
815 self.with_corner_radius(16.)
816 }
817
818 fn rounded_3xl(self) -> Self {
820 self.with_corner_radius(24.)
821 }
822
823 fn rounded_4xl(self) -> Self {
825 self.with_corner_radius(32.)
826 }
827
828 fn rounded_full(self) -> Self {
830 self.with_corner_radius(99.)
831 }
832}
833
834pub trait MaybeExt
835where
836 Self: Sized,
837{
838 fn maybe(self, bool: impl Into<bool>, then: impl FnOnce(Self) -> Self) -> Self {
839 if bool.into() { then(self) } else { self }
840 }
841
842 fn map<T>(self, data: Option<T>, then: impl FnOnce(Self, T) -> Self) -> Self {
843 if let Some(data) = data {
844 then(self, data)
845 } else {
846 self
847 }
848 }
849}
850
851pub trait LayerExt
852where
853 Self: Sized,
854{
855 fn get_layer(&mut self) -> &mut Layer;
856
857 fn layer(mut self, layer: impl Into<Layer>) -> Self {
858 *self.get_layer() = layer.into();
859 self
860 }
861}
862
863pub trait ScrollableExt
864where
865 Self: Sized,
866{
867 fn get_effect(&mut self) -> &mut EffectData;
868
869 fn scrollable(mut self, scrollable: impl Into<bool>) -> Self {
870 self.get_effect().scrollable = scrollable.into();
871 self
872 }
873}
874
875pub trait InteractiveExt
876where
877 Self: Sized,
878{
879 fn get_effect(&mut self) -> &mut EffectData;
880
881 fn interactive(mut self, interactive: impl Into<Interactive>) -> Self {
882 self.get_effect().interactive = interactive.into();
883 self
884 }
885}