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}
182
183impl ContentExt for Content {
184 fn normal() -> Content {
185 Content::Normal
186 }
187
188 fn fit() -> Content {
189 Content::Fit
190 }
191
192 fn flex() -> Content {
193 Content::Flex
194 }
195}
196
197pub trait VisibleSizeExt {
198 fn full() -> VisibleSize;
199 fn inner_percent(value: impl Into<f32>) -> VisibleSize;
200}
201
202impl VisibleSizeExt for VisibleSize {
203 fn full() -> VisibleSize {
204 VisibleSize::Full
205 }
206
207 fn inner_percent(value: impl Into<f32>) -> VisibleSize {
208 VisibleSize::InnerPercentage(Length::new(value.into()))
209 }
210}
211
212pub trait ChildrenExt: Sized {
213 fn get_children(&mut self) -> &mut Vec<Element>;
214
215 fn children_iter<I>(mut self, children_iter: I) -> Self
216 where
217 I: Iterator<Item = Element>,
218 {
219 self.get_children().extend(children_iter);
220 self
221 }
222
223 fn children<V: Into<Vec<Element>>>(mut self, children: V) -> Self {
224 self.get_children().extend(children.into());
225 self
226 }
227
228 fn maybe_child<C: IntoElement>(mut self, child: Option<C>) -> Self {
229 if let Some(child) = child {
230 self.get_children().push(child.into_element());
231 }
232 self
233 }
234
235 fn child<C: IntoElement>(mut self, child: C) -> Self {
236 self.get_children().push(child.into_element());
237 self
238 }
239}
240
241pub trait KeyExt: Sized {
242 fn write_key(&mut self) -> &mut DiffKey;
243
244 fn key(mut self, key: impl Hash) -> Self {
245 let mut hasher = FxHasher::default();
246 key.hash(&mut hasher);
247 *self.write_key() = DiffKey::U64(hasher.finish());
248 self
249 }
250}
251
252pub trait ListExt {
253 fn with(self, other: Self) -> Self;
254}
255
256impl<T> ListExt for Vec<T> {
257 fn with(mut self, other: Self) -> Self {
258 self.extend(other);
259 self
260 }
261}
262
263macro_rules! event_handlers {
264 (
265 $handler_variant:ident, $event_data:ty;
266 $(
267 $name:ident => $event_variant:expr ;
268 )*
269 ) => {
270 paste! {
271 $(
272 fn [<on_$name>](mut self, [<on_$name>]: impl Into<EventHandler<Event<$event_data>>>) -> Self {
273 self.get_event_handlers()
274 .insert($event_variant, EventHandlerType::$handler_variant([<on_$name>].into()));
275 self
276 }
277 )*
278 }
279 };
280}
281
282pub trait EventHandlersExt: Sized + LayoutExt {
283 fn get_event_handlers(&mut self) -> &mut FxHashMap<EventName, EventHandlerType>;
284
285 event_handlers! {
286 Mouse,
287 MouseEventData;
288
289 mouse_down => EventName::MouseDown;
290 mouse_up => EventName::MouseUp;
291 mouse_move => EventName::MouseMove;
292
293 global_mouse_up => EventName::GlobalMouseUp;
294 global_mouse_down => EventName::GlobalMouseDown;
295 global_mouse_move => EventName::GlobalMouseMove;
296
297 capture_global_mouse_move => EventName::CaptureGlobalMouseMove;
298 capture_global_mouse_up => EventName::CaptureGlobalMouseUp;
299 }
300
301 event_handlers! {
302 Keyboard,
303 KeyboardEventData;
304
305 key_down => EventName::KeyDown;
306 key_up => EventName::KeyUp;
307
308 global_key_down => EventName::GlobalKeyDown;
309 global_key_up => EventName::GlobalKeyUp;
310 }
311
312 event_handlers! {
313 Wheel,
314 WheelEventData;
315
316 wheel => EventName::Wheel;
317 }
318
319 event_handlers! {
320 Touch,
321 TouchEventData;
322
323 touch_cancel => EventName::TouchCancel;
324 touch_start => EventName::TouchStart;
325 touch_move => EventName::TouchMove;
326 touch_end => EventName::TouchEnd;
327 }
328
329 event_handlers! {
330 Pointer,
331 PointerEventData;
332
333 pointer_press => EventName::PointerPress;
334 pointer_down => EventName::PointerDown;
335 pointer_enter => EventName::PointerEnter;
336 pointer_leave => EventName::PointerLeave;
337 }
338
339 event_handlers! {
340 File,
341 FileEventData;
342
343 file_drop => EventName::FileDrop;
344 global_file_hover => EventName::GlobalFileHover;
345 global_file_hover_cancelled => EventName::GlobalFileHoverCancelled;
346 }
347
348 event_handlers! {
349 ImePreedit,
350 ImePreeditEventData;
351
352 ime_preedit => EventName::ImePreedit;
353 }
354
355 fn on_sized(mut self, on_sized: impl Into<EventHandler<Event<SizedEventData>>>) -> Self {
356 self.get_event_handlers()
357 .insert(EventName::Sized, EventHandlerType::Sized(on_sized.into()));
358 self.get_layout().layout.has_layout_references = true;
359 self
360 }
361
362 fn on_press(self, on_press: impl Into<EventHandler<Event<PressEventData>>>) -> Self {
369 let on_press = on_press.into();
370 self.on_pointer_press({
371 let on_press = on_press.clone();
372 move |e: Event<PointerEventData>| {
373 let event = e.try_map(|d| match d {
374 PointerEventData::Mouse(m) if m.button == Some(MouseButton::Left) => {
375 Some(PressEventData::Mouse(m))
376 }
377 PointerEventData::Touch(t) => Some(PressEventData::Touch(t)),
378 _ => None,
379 });
380 if let Some(event) = event {
381 on_press.call(event);
382 }
383 }
384 })
385 .on_key_down({
386 let on_press = on_press.clone();
387 move |e: Event<KeyboardEventData>| {
388 if Focus::is_pressed(&e) {
389 on_press.call(e.map(PressEventData::Keyboard))
390 }
391 }
392 })
393 }
394
395 fn on_secondary_press(
399 self,
400 on_pointer_press: impl Into<EventHandler<Event<PressEventData>>>,
401 ) -> Self {
402 let on_pointer_press = on_pointer_press.into();
403 self.on_pointer_press({
404 let on_pointer_press = on_pointer_press.clone();
405 move |e: Event<PointerEventData>| {
406 let event = e.try_map(|d| match d {
407 PointerEventData::Mouse(m) if m.button == Some(MouseButton::Right) => {
408 Some(PressEventData::Mouse(m))
409 }
410 _ => None,
411 });
412 if let Some(event) = event {
413 on_pointer_press.call(event);
414 }
415 }
416 })
417 }
418
419 fn on_all_press(self, on_press: impl Into<EventHandler<Event<PressEventData>>>) -> Self {
424 let on_press = on_press.into();
425 self.on_pointer_press({
426 let on_press = on_press.clone();
427 move |e: Event<PointerEventData>| {
428 let event = e.try_map(|d| match d {
429 PointerEventData::Mouse(m) => Some(PressEventData::Mouse(m)),
430 PointerEventData::Touch(t) => Some(PressEventData::Touch(t)),
431 });
432 if let Some(event) = event {
433 on_press.call(event);
434 }
435 }
436 })
437 .on_key_down({
438 let on_press = on_press.clone();
439 move |e: Event<KeyboardEventData>| {
440 if Focus::is_pressed(&e) {
441 on_press.call(e.map(PressEventData::Keyboard))
442 }
443 }
444 })
445 }
446}
447
448#[derive(Debug, Clone, PartialEq)]
449pub enum PressEventData {
450 Mouse(MouseEventData),
451 Keyboard(KeyboardEventData),
452 Touch(TouchEventData),
453}
454
455pub trait ContainerWithContentExt
456where
457 Self: LayoutExt,
458{
459 fn direction(mut self, direction: Direction) -> Self {
460 self.get_layout().layout.direction = direction;
461 self
462 }
463 fn main_align(mut self, main_align: Alignment) -> Self {
464 self.get_layout().layout.main_alignment = main_align;
465 self
466 }
467
468 fn cross_align(mut self, cross_align: Alignment) -> Self {
469 self.get_layout().layout.cross_alignment = cross_align;
470 self
471 }
472
473 fn spacing(mut self, spacing: impl Into<f32>) -> Self {
474 self.get_layout().layout.spacing = Length::new(spacing.into());
475 self
476 }
477
478 fn content(mut self, content: Content) -> Self {
479 self.get_layout().layout.content = content;
480 self
481 }
482 fn center(mut self) -> Self {
483 self.get_layout().layout.main_alignment = Alignment::Center;
484 self.get_layout().layout.cross_alignment = Alignment::Center;
485
486 self
487 }
488
489 fn offset_x(mut self, offset_x: impl Into<f32>) -> Self {
490 self.get_layout().layout.offset_x = Length::new(offset_x.into());
491 self
492 }
493
494 fn offset_y(mut self, offset_y: impl Into<f32>) -> Self {
495 self.get_layout().layout.offset_y = Length::new(offset_y.into());
496 self
497 }
498
499 fn vertical(mut self) -> Self {
500 self.get_layout().layout.direction = Direction::vertical();
501 self
502 }
503
504 fn horizontal(mut self) -> Self {
505 self.get_layout().layout.direction = Direction::horizontal();
506 self
507 }
508}
509
510pub trait ContainerSizeExt
511where
512 Self: LayoutExt,
513{
514 fn width(mut self, width: impl Into<Size>) -> Self {
515 self.get_layout().layout.width = width.into();
516 self
517 }
518
519 fn height(mut self, height: impl Into<Size>) -> Self {
520 self.get_layout().layout.height = height.into();
521 self
522 }
523
524 fn expanded(mut self) -> Self {
526 self.get_layout().layout.width = Size::fill();
527 self.get_layout().layout.height = Size::fill();
528 self
529 }
530}
531
532impl<T: ContainerExt> ContainerSizeExt for T {}
533
534pub trait ContainerExt
535where
536 Self: LayoutExt,
537{
538 fn position(mut self, position: impl Into<Position>) -> Self {
539 self.get_layout().layout.position = position.into();
540 self
541 }
542
543 fn padding(mut self, padding: impl Into<Gaps>) -> Self {
544 self.get_layout().layout.padding = padding.into();
545 self
546 }
547
548 fn margin(mut self, margin: impl Into<Gaps>) -> Self {
549 self.get_layout().layout.margin = margin.into();
550 self
551 }
552
553 fn min_width(mut self, minimum_width: impl Into<Size>) -> Self {
554 self.get_layout().layout.minimum_width = minimum_width.into();
555 self
556 }
557
558 fn min_height(mut self, minimum_height: impl Into<Size>) -> Self {
559 self.get_layout().layout.minimum_height = minimum_height.into();
560 self
561 }
562
563 fn max_width(mut self, maximum_width: impl Into<Size>) -> Self {
564 self.get_layout().layout.maximum_width = maximum_width.into();
565 self
566 }
567
568 fn max_height(mut self, maximum_height: impl Into<Size>) -> Self {
569 self.get_layout().layout.maximum_height = maximum_height.into();
570 self
571 }
572
573 fn visible_width(mut self, visible_width: impl Into<VisibleSize>) -> Self {
574 self.get_layout().layout.visible_width = visible_width.into();
575 self
576 }
577
578 fn visible_height(mut self, visible_height: impl Into<VisibleSize>) -> Self {
579 self.get_layout().layout.visible_height = visible_height.into();
580 self
581 }
582}
583
584pub trait LayoutExt
585where
586 Self: Sized,
587{
588 fn get_layout(&mut self) -> &mut LayoutData;
589
590 fn layout(mut self, layout: LayoutData) -> Self {
591 *self.get_layout() = layout;
592 self
593 }
594}
595
596pub trait ImageExt
597where
598 Self: LayoutExt,
599{
600 fn width(mut self, width: Size) -> Self {
601 self.get_layout().layout.width = width;
602 self
603 }
604
605 fn height(mut self, height: Size) -> Self {
606 self.get_layout().layout.height = height;
607 self
608 }
609
610 fn get_image_data(&mut self) -> &mut ImageData;
611
612 fn image_data(mut self, image_data: ImageData) -> Self {
613 *self.get_image_data() = image_data;
614 self
615 }
616
617 fn sampling_mode(mut self, sampling_mode: SamplingMode) -> Self {
618 self.get_image_data().sampling_mode = sampling_mode;
619 self
620 }
621
622 fn aspect_ratio(mut self, aspect_ratio: AspectRatio) -> Self {
623 self.get_image_data().aspect_ratio = aspect_ratio;
624 self
625 }
626
627 fn image_cover(mut self, image_cover: ImageCover) -> Self {
628 self.get_image_data().image_cover = image_cover;
629 self
630 }
631}
632
633pub trait AccessibilityExt: Sized {
634 fn get_accessibility_data(&mut self) -> &mut AccessibilityData;
635
636 fn accessibility(mut self, accessibility: AccessibilityData) -> Self {
637 *self.get_accessibility_data() = accessibility;
638 self
639 }
640
641 fn a11y_id(mut self, a11y_id: impl Into<Option<AccessibilityId>>) -> Self {
642 self.get_accessibility_data().a11y_id = a11y_id.into();
643 self
644 }
645
646 fn a11y_focusable(mut self, a11y_focusable: impl Into<Focusable>) -> Self {
647 self.get_accessibility_data().a11y_focusable = a11y_focusable.into();
648 self
649 }
650
651 fn a11y_auto_focus(mut self, a11y_auto_focus: impl Into<bool>) -> Self {
652 self.get_accessibility_data().a11y_auto_focus = a11y_auto_focus.into();
653 self
654 }
655
656 fn a11y_member_of(mut self, a11y_member_of: impl Into<AccessibilityId>) -> Self {
657 self.get_accessibility_data()
658 .builder
659 .set_member_of(a11y_member_of.into());
660 self
661 }
662
663 fn a11y_role(mut self, a11y_role: impl Into<AccessibilityRole>) -> Self {
664 self.get_accessibility_data()
665 .builder
666 .set_role(a11y_role.into());
667 self
668 }
669
670 fn a11y_alt(mut self, value: impl Into<Box<str>>) -> Self {
671 self.get_accessibility_data().builder.set_label(value);
672 self
673 }
674
675 fn a11y_builder(mut self, with: impl FnOnce(&mut accesskit::Node)) -> Self {
676 with(&mut self.get_accessibility_data().builder);
677 self
678 }
679}
680
681pub trait TextStyleExt
682where
683 Self: Sized,
684{
685 fn get_text_style_data(&mut self) -> &mut TextStyleData;
686
687 fn color(mut self, color: impl Into<Color>) -> Self {
688 self.get_text_style_data().color = Some(color.into());
689 self
690 }
691
692 fn text_align(mut self, text_align: impl Into<TextAlign>) -> Self {
693 self.get_text_style_data().text_align = Some(text_align.into());
694 self
695 }
696
697 fn font_size(mut self, font_size: impl Into<FontSize>) -> Self {
698 self.get_text_style_data().font_size = Some(font_size.into());
699 self
700 }
701
702 fn font_family(mut self, font_family: impl Into<Cow<'static, str>>) -> Self {
703 self.get_text_style_data()
704 .font_families
705 .push(font_family.into());
706 self
707 }
708
709 fn font_slant(mut self, font_slant: impl Into<FontSlant>) -> Self {
710 self.get_text_style_data().font_slant = Some(font_slant.into());
711 self
712 }
713
714 fn font_weight(mut self, font_weight: impl Into<FontWeight>) -> Self {
715 self.get_text_style_data().font_weight = Some(font_weight.into());
716 self
717 }
718
719 fn font_width(mut self, font_width: impl Into<FontWidth>) -> Self {
720 self.get_text_style_data().font_width = Some(font_width.into());
721 self
722 }
723
724 fn text_height(mut self, text_height: impl Into<TextHeightBehavior>) -> Self {
725 self.get_text_style_data().text_height = Some(text_height.into());
726 self
727 }
728
729 fn text_overflow(mut self, text_overflow: impl Into<TextOverflow>) -> Self {
730 self.get_text_style_data().text_overflow = Some(text_overflow.into());
731 self
732 }
733
734 fn text_shadow(mut self, text_shadow: impl Into<TextShadow>) -> Self {
735 self.get_text_style_data()
736 .text_shadows
737 .push(text_shadow.into());
738 self
739 }
740}
741
742pub trait StyleExt
743where
744 Self: Sized,
745{
746 fn get_style(&mut self) -> &mut StyleState;
747
748 fn background<S: Into<Color>>(mut self, background: S) -> Self {
749 self.get_style().background = Fill::Color(background.into());
750 self
751 }
752
753 fn background_conic_gradient<S: Into<ConicGradient>>(mut self, background: S) -> Self {
754 self.get_style().background = Fill::ConicGradient(Box::new(background.into()));
755 self
756 }
757
758 fn background_linear_gradient<S: Into<LinearGradient>>(mut self, background: S) -> Self {
759 self.get_style().background = Fill::LinearGradient(Box::new(background.into()));
760 self
761 }
762
763 fn background_radial_gradient<S: Into<RadialGradient>>(mut self, background: S) -> Self {
764 self.get_style().background = Fill::RadialGradient(Box::new(background.into()));
765 self
766 }
767
768 fn border(mut self, border: impl Into<Option<Border>>) -> Self {
769 if let Some(border) = border.into() {
770 self.get_style().borders.push(border);
771 }
772 self
773 }
774
775 fn shadow(mut self, shadow: impl Into<Shadow>) -> Self {
776 self.get_style().shadows.push(shadow.into());
777 self
778 }
779
780 fn corner_radius(mut self, corner_radius: impl Into<CornerRadius>) -> Self {
781 self.get_style().corner_radius = corner_radius.into();
782 self
783 }
784}
785
786pub trait MaybeExt
787where
788 Self: Sized,
789{
790 fn maybe(self, bool: impl Into<bool>, then: impl FnOnce(Self) -> Self) -> Self {
791 if bool.into() { then(self) } else { self }
792 }
793
794 fn map<T>(self, data: Option<T>, then: impl FnOnce(Self, T) -> Self) -> Self {
795 if let Some(data) = data {
796 then(self, data)
797 } else {
798 self
799 }
800 }
801}
802
803pub trait LayerExt
804where
805 Self: Sized,
806{
807 fn get_layer(&mut self) -> &mut Layer;
808
809 fn layer(mut self, layer: impl Into<Layer>) -> Self {
810 *self.get_layer() = layer.into();
811 self
812 }
813}
814
815pub trait ScrollableExt
816where
817 Self: Sized,
818{
819 fn get_effect(&mut self) -> &mut EffectData;
820
821 fn scrollable(mut self, scrollable: impl Into<bool>) -> Self {
822 self.get_effect().scrollable = scrollable.into();
823 self
824 }
825}
826
827pub trait InteractiveExt
828where
829 Self: Sized,
830{
831 fn get_effect(&mut self) -> &mut EffectData;
832
833 fn interactive(mut self, interactive: impl Into<Interactive>) -> Self {
834 self.get_effect().interactive = interactive.into();
835 self
836 }
837}