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 fn spacing(mut self, spacing: f32) -> Self {
473 self.get_layout().layout.spacing = torin::geometry::Length::new(spacing);
474 self
475 }
476
477 fn content(mut self, content: Content) -> Self {
478 self.get_layout().layout.content = content;
479 self
480 }
481 fn center(mut self) -> Self {
482 self.get_layout().layout.main_alignment = Alignment::Center;
483 self.get_layout().layout.cross_alignment = Alignment::Center;
484
485 self
486 }
487
488 fn offset_x(mut self, offset_x: impl Into<f32>) -> Self {
489 self.get_layout().layout.offset_x = Length::new(offset_x.into());
490 self
491 }
492
493 fn offset_y(mut self, offset_y: impl Into<f32>) -> Self {
494 self.get_layout().layout.offset_y = Length::new(offset_y.into());
495 self
496 }
497
498 fn vertical(mut self) -> Self {
499 self.get_layout().layout.direction = Direction::vertical();
500 self
501 }
502
503 fn horizontal(mut self) -> Self {
504 self.get_layout().layout.direction = Direction::horizontal();
505 self
506 }
507}
508
509pub trait ContainerExt
510where
511 Self: LayoutExt,
512{
513 fn position(mut self, position: impl Into<Position>) -> Self {
514 self.get_layout().layout.position = position.into();
515 self
516 }
517
518 fn width(mut self, width: impl Into<Size>) -> Self {
519 self.get_layout().layout.width = width.into();
520 self
521 }
522
523 fn height(mut self, height: impl Into<Size>) -> Self {
524 self.get_layout().layout.height = height.into();
525 self
526 }
527
528 fn padding(mut self, padding: impl Into<Gaps>) -> Self {
529 self.get_layout().layout.padding = padding.into();
530 self
531 }
532
533 fn margin(mut self, margin: impl Into<Gaps>) -> Self {
534 self.get_layout().layout.margin = margin.into();
535 self
536 }
537
538 fn min_width(mut self, minimum_width: impl Into<Size>) -> Self {
539 self.get_layout().layout.minimum_width = minimum_width.into();
540 self
541 }
542
543 fn min_height(mut self, minimum_height: impl Into<Size>) -> Self {
544 self.get_layout().layout.minimum_height = minimum_height.into();
545 self
546 }
547
548 fn max_width(mut self, maximum_width: impl Into<Size>) -> Self {
549 self.get_layout().layout.maximum_width = maximum_width.into();
550 self
551 }
552
553 fn max_height(mut self, maximum_height: impl Into<Size>) -> Self {
554 self.get_layout().layout.maximum_height = maximum_height.into();
555 self
556 }
557
558 fn visible_width(mut self, visible_width: impl Into<VisibleSize>) -> Self {
559 self.get_layout().layout.visible_width = visible_width.into();
560 self
561 }
562
563 fn visible_height(mut self, visible_height: impl Into<VisibleSize>) -> Self {
564 self.get_layout().layout.visible_height = visible_height.into();
565 self
566 }
567
568 fn expanded(mut self) -> Self {
569 self.get_layout().layout.width = Size::fill();
570 self.get_layout().layout.height = Size::fill();
571 self
572 }
573}
574
575pub trait LayoutExt
576where
577 Self: Sized,
578{
579 fn get_layout(&mut self) -> &mut LayoutData;
580
581 fn layout(mut self, layout: LayoutData) -> Self {
582 *self.get_layout() = layout;
583 self
584 }
585}
586
587pub trait ImageExt
588where
589 Self: LayoutExt,
590{
591 fn width(mut self, width: Size) -> Self {
592 self.get_layout().layout.width = width;
593 self
594 }
595
596 fn height(mut self, height: Size) -> Self {
597 self.get_layout().layout.height = height;
598 self
599 }
600
601 fn get_image_data(&mut self) -> &mut ImageData;
602
603 fn image_data(mut self, image_data: ImageData) -> Self {
604 *self.get_image_data() = image_data;
605 self
606 }
607
608 fn sampling_mode(mut self, sampling_mode: SamplingMode) -> Self {
609 self.get_image_data().sampling_mode = sampling_mode;
610 self
611 }
612
613 fn aspect_ratio(mut self, aspect_ratio: AspectRatio) -> Self {
614 self.get_image_data().aspect_ratio = aspect_ratio;
615 self
616 }
617
618 fn image_cover(mut self, image_cover: ImageCover) -> Self {
619 self.get_image_data().image_cover = image_cover;
620 self
621 }
622}
623
624pub trait AccessibilityExt: Sized {
625 fn get_accessibility_data(&mut self) -> &mut AccessibilityData;
626
627 fn accessibility(mut self, accessibility: AccessibilityData) -> Self {
628 *self.get_accessibility_data() = accessibility;
629 self
630 }
631
632 fn a11y_id(mut self, a11y_id: impl Into<Option<AccessibilityId>>) -> Self {
633 self.get_accessibility_data().a11y_id = a11y_id.into();
634 self
635 }
636
637 fn a11y_focusable(mut self, a11y_focusable: impl Into<Focusable>) -> Self {
638 self.get_accessibility_data().a11y_focusable = a11y_focusable.into();
639 self
640 }
641
642 fn a11y_auto_focus(mut self, a11y_auto_focus: impl Into<bool>) -> Self {
643 self.get_accessibility_data().a11y_auto_focus = a11y_auto_focus.into();
644 self
645 }
646
647 fn a11y_member_of(mut self, a11y_member_of: impl Into<AccessibilityId>) -> Self {
648 self.get_accessibility_data()
649 .builder
650 .set_member_of(a11y_member_of.into());
651 self
652 }
653
654 fn a11y_role(mut self, a11y_role: impl Into<AccessibilityRole>) -> Self {
655 self.get_accessibility_data()
656 .builder
657 .set_role(a11y_role.into());
658 self
659 }
660
661 fn a11y_alt(mut self, value: impl Into<Box<str>>) -> Self {
662 self.get_accessibility_data().builder.set_label(value);
663 self
664 }
665
666 fn a11y_builder(mut self, with: impl FnOnce(&mut accesskit::Node)) -> Self {
667 with(&mut self.get_accessibility_data().builder);
668 self
669 }
670}
671
672pub trait TextStyleExt
673where
674 Self: Sized,
675{
676 fn get_text_style_data(&mut self) -> &mut TextStyleData;
677
678 fn color(mut self, color: impl Into<Color>) -> Self {
679 self.get_text_style_data().color = Some(color.into());
680 self
681 }
682
683 fn text_align(mut self, text_align: impl Into<TextAlign>) -> Self {
684 self.get_text_style_data().text_align = Some(text_align.into());
685 self
686 }
687
688 fn font_size(mut self, font_size: impl Into<FontSize>) -> Self {
689 self.get_text_style_data().font_size = Some(font_size.into());
690 self
691 }
692
693 fn font_family(mut self, font_family: impl Into<Cow<'static, str>>) -> Self {
694 self.get_text_style_data()
695 .font_families
696 .push(font_family.into());
697 self
698 }
699
700 fn font_slant(mut self, font_slant: impl Into<FontSlant>) -> Self {
701 self.get_text_style_data().font_slant = Some(font_slant.into());
702 self
703 }
704
705 fn font_weight(mut self, font_weight: impl Into<FontWeight>) -> Self {
706 self.get_text_style_data().font_weight = Some(font_weight.into());
707 self
708 }
709
710 fn font_width(mut self, font_width: impl Into<FontWidth>) -> Self {
711 self.get_text_style_data().font_width = Some(font_width.into());
712 self
713 }
714
715 fn text_height(mut self, text_height: impl Into<TextHeightBehavior>) -> Self {
716 self.get_text_style_data().text_height = Some(text_height.into());
717 self
718 }
719
720 fn text_overflow(mut self, text_overflow: impl Into<TextOverflow>) -> Self {
721 self.get_text_style_data().text_overflow = Some(text_overflow.into());
722 self
723 }
724
725 fn text_shadow(mut self, text_shadow: impl Into<TextShadow>) -> Self {
726 self.get_text_style_data()
727 .text_shadows
728 .push(text_shadow.into());
729 self
730 }
731}
732
733pub trait MaybeExt
734where
735 Self: Sized,
736{
737 fn maybe(self, bool: impl Into<bool>, then: impl FnOnce(Self) -> Self) -> Self {
738 if bool.into() { then(self) } else { self }
739 }
740
741 fn map<T>(self, data: Option<T>, then: impl FnOnce(Self, T) -> Self) -> Self {
742 if let Some(data) = data {
743 then(self, data)
744 } else {
745 self
746 }
747 }
748}
749
750pub trait LayerExt
751where
752 Self: Sized,
753{
754 fn get_layer(&mut self) -> &mut Layer;
755
756 fn layer(mut self, layer: impl Into<Layer>) -> Self {
757 *self.get_layer() = layer.into();
758 self
759 }
760}
761
762pub trait ScrollableExt
763where
764 Self: Sized,
765{
766 fn get_effect(&mut self) -> &mut EffectData;
767
768 fn scrollable(mut self, scrollable: impl Into<bool>) -> Self {
769 self.get_effect().scrollable = scrollable.into();
770 self
771 }
772}
773
774pub trait InteractiveExt
775where
776 Self: Sized,
777{
778 fn get_effect(&mut self) -> &mut EffectData;
779
780 fn interactive(mut self, interactive: impl Into<Interactive>) -> Self {
781 self.get_effect().interactive = interactive.into();
782 self
783 }
784}