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 prelude::*,
60 style::{
61 font_size::FontSize,
62 font_slant::FontSlant,
63 font_weight::FontWeight,
64 font_width::FontWidth,
65 text_height::TextHeightBehavior,
66 text_overflow::TextOverflow,
67 text_shadow::TextShadow,
68 },
69};
70
71pub trait SizeExt {
72 fn auto() -> Size;
73 fn fill() -> Size;
74 fn fill_minimum() -> Size;
75 fn percent(percent: impl Into<f32>) -> Size;
76 fn px(px: impl Into<f32>) -> Size;
77 fn window_percent(percent: impl Into<f32>) -> Size;
78 fn flex(flex: impl Into<f32>) -> Size;
79 fn func(func: impl Fn(SizeFnContext) -> Option<f32> + 'static + Sync + Send) -> Size;
80 fn func_data<D: Hash>(
81 func: impl Fn(SizeFnContext) -> Option<f32> + 'static + Sync + Send,
82 data: &D,
83 ) -> Size;
84}
85
86impl SizeExt for Size {
87 fn auto() -> Size {
88 Size::Inner
89 }
90
91 fn fill() -> Size {
92 Size::Fill
93 }
94
95 fn fill_minimum() -> Size {
96 Size::FillMinimum
97 }
98
99 fn percent(percent: impl Into<f32>) -> Size {
100 Size::Percentage(Length::new(percent.into()))
101 }
102
103 fn px(px: impl Into<f32>) -> Size {
104 Size::Pixels(Length::new(px.into()))
105 }
106
107 fn window_percent(percent: impl Into<f32>) -> Size {
108 Size::RootPercentage(Length::new(percent.into()))
109 }
110
111 fn flex(flex: impl Into<f32>) -> Size {
112 Size::Flex(Length::new(flex.into()))
113 }
114
115 fn func(func: impl Fn(SizeFnContext) -> Option<f32> + 'static + Sync + Send) -> Size {
116 Self::Fn(Box::new(SizeFn::new(func)))
117 }
118
119 fn func_data<D: Hash>(
120 func: impl Fn(SizeFnContext) -> Option<f32> + 'static + Sync + Send,
121 data: &D,
122 ) -> Size {
123 Self::Fn(Box::new(SizeFn::new_data(func, data)))
124 }
125}
126
127pub trait DirectionExt {
128 fn vertical() -> Direction;
129 fn horizontal() -> Direction;
130}
131
132impl DirectionExt for Direction {
133 fn vertical() -> Direction {
134 Direction::Vertical
135 }
136 fn horizontal() -> Direction {
137 Direction::Horizontal
138 }
139}
140
141pub trait AlignmentExt {
142 fn start() -> Alignment;
143 fn center() -> Alignment;
144 fn end() -> Alignment;
145 fn space_between() -> Alignment;
146 fn space_evenly() -> Alignment;
147 fn space_around() -> Alignment;
148}
149
150impl AlignmentExt for Alignment {
151 fn start() -> Alignment {
152 Alignment::Start
153 }
154
155 fn center() -> Alignment {
156 Alignment::Center
157 }
158
159 fn end() -> Alignment {
160 Alignment::End
161 }
162
163 fn space_between() -> Alignment {
164 Alignment::SpaceBetween
165 }
166
167 fn space_evenly() -> Alignment {
168 Alignment::SpaceEvenly
169 }
170
171 fn space_around() -> Alignment {
172 Alignment::SpaceAround
173 }
174}
175
176pub trait ContentExt {
177 fn normal() -> Content;
178 fn fit() -> Content;
179 fn flex() -> Content;
180}
181
182impl ContentExt for Content {
183 fn normal() -> Content {
184 Content::Normal
185 }
186
187 fn fit() -> Content {
188 Content::Fit
189 }
190
191 fn flex() -> Content {
192 Content::Flex
193 }
194}
195
196pub trait VisibleSizeExt {
197 fn full() -> VisibleSize;
198 fn inner_percent(value: impl Into<f32>) -> VisibleSize;
199}
200
201impl VisibleSizeExt for VisibleSize {
202 fn full() -> VisibleSize {
203 VisibleSize::Full
204 }
205
206 fn inner_percent(value: impl Into<f32>) -> VisibleSize {
207 VisibleSize::InnerPercentage(Length::new(value.into()))
208 }
209}
210
211pub trait ChildrenExt: Sized {
212 fn get_children(&mut self) -> &mut Vec<Element>;
213
214 fn children_iter<I>(mut self, children_iter: I) -> Self
215 where
216 I: Iterator<Item = Element>,
217 {
218 self.get_children().extend(children_iter);
219 self
220 }
221
222 fn children<V: Into<Vec<Element>>>(mut self, children: V) -> Self {
223 self.get_children().extend(children.into());
224 self
225 }
226
227 fn maybe_child<C: IntoElement>(mut self, child: Option<C>) -> Self {
228 if let Some(child) = child {
229 self.get_children().push(child.into_element());
230 }
231 self
232 }
233
234 fn child<C: IntoElement>(mut self, child: C) -> Self {
235 self.get_children().push(child.into_element());
236 self
237 }
238}
239
240pub trait KeyExt: Sized {
241 fn write_key(&mut self) -> &mut DiffKey;
242
243 fn key(mut self, key: impl Hash) -> Self {
244 let mut hasher = FxHasher::default();
245 key.hash(&mut hasher);
246 *self.write_key() = DiffKey::U64(hasher.finish());
247 self
248 }
249}
250
251pub trait ListExt {
252 fn with(self, other: Self) -> Self;
253}
254
255impl<T> ListExt for Vec<T> {
256 fn with(mut self, other: Self) -> Self {
257 self.extend(other);
258 self
259 }
260}
261
262macro_rules! event_handlers {
263 (
264 $handler_variant:ident, $event_data:ty;
265 $(
266 $name:ident => $event_variant:expr ;
267 )*
268 ) => {
269 paste! {
270 $(
271 fn [<on_$name>](mut self, [<on_$name>]: impl Into<EventHandler<Event<$event_data>>>) -> Self {
272 self.get_event_handlers()
273 .insert($event_variant, EventHandlerType::$handler_variant([<on_$name>].into()));
274 self
275 }
276 )*
277 }
278 };
279}
280
281pub trait EventHandlersExt: Sized + LayoutExt {
282 fn get_event_handlers(&mut self) -> &mut FxHashMap<EventName, EventHandlerType>;
283
284 event_handlers! {
285 Mouse,
286 MouseEventData;
287
288 mouse_down => EventName::MouseDown;
289 mouse_up => EventName::MouseUp;
290 mouse_move => EventName::MouseMove;
291
292 global_mouse_up => EventName::GlobalMouseUp;
293 global_mouse_down => EventName::GlobalMouseDown;
294 global_mouse_move => EventName::GlobalMouseMove;
295
296 capture_global_mouse_move => EventName::CaptureGlobalMouseMove;
297 capture_global_mouse_up => EventName::CaptureGlobalMouseUp;
298 }
299
300 event_handlers! {
301 Keyboard,
302 KeyboardEventData;
303
304 key_down => EventName::KeyDown;
305 key_up => EventName::KeyUp;
306
307 global_key_down => EventName::GlobalKeyDown;
308 global_key_up => EventName::GlobalKeyUp;
309 }
310
311 event_handlers! {
312 Wheel,
313 WheelEventData;
314
315 wheel => EventName::Wheel;
316 }
317
318 event_handlers! {
319 Touch,
320 TouchEventData;
321
322 touch_cancel => EventName::TouchCancel;
323 touch_start => EventName::TouchStart;
324 touch_move => EventName::TouchMove;
325 touch_end => EventName::TouchEnd;
326 }
327
328 event_handlers! {
329 Pointer,
330 PointerEventData;
331
332 pointer_press => EventName::PointerPress;
333 pointer_down => EventName::PointerDown;
334 pointer_enter => EventName::PointerEnter;
335 pointer_leave => EventName::PointerLeave;
336 }
337
338 event_handlers! {
339 File,
340 FileEventData;
341
342 file_drop => EventName::FileDrop;
343 global_file_hover => EventName::GlobalFileHover;
344 global_file_hover_cancelled => EventName::GlobalFileHoverCancelled;
345 }
346
347 event_handlers! {
348 ImePreedit,
349 ImePreeditEventData;
350
351 ime_preedit => EventName::ImePreedit;
352 }
353
354 fn on_sized(mut self, on_sized: impl Into<EventHandler<Event<SizedEventData>>>) -> Self {
355 self.get_event_handlers()
356 .insert(EventName::Sized, EventHandlerType::Sized(on_sized.into()));
357 self.get_layout().layout.has_layout_references = true;
358 self
359 }
360
361 fn on_press(self, on_press: impl Into<EventHandler<Event<PressEventData>>>) -> Self {
368 let on_press = on_press.into();
369 self.on_pointer_press({
370 let on_press = on_press.clone();
371 move |e: Event<PointerEventData>| {
372 let event = e.try_map(|d| match d {
373 PointerEventData::Mouse(m) if m.button == Some(MouseButton::Left) => {
374 Some(PressEventData::Mouse(m))
375 }
376 PointerEventData::Touch(t) => Some(PressEventData::Touch(t)),
377 _ => None,
378 });
379 if let Some(event) = event {
380 on_press.call(event);
381 }
382 }
383 })
384 .on_key_down({
385 let on_press = on_press.clone();
386 move |e: Event<KeyboardEventData>| {
387 if Focus::is_pressed(&e) {
388 on_press.call(e.map(PressEventData::Keyboard))
389 }
390 }
391 })
392 }
393}
394
395#[derive(Debug, Clone, PartialEq)]
396pub enum PressEventData {
397 Mouse(MouseEventData),
398 Keyboard(KeyboardEventData),
399 Touch(TouchEventData),
400}
401
402pub trait ContainerWithContentExt
403where
404 Self: LayoutExt,
405{
406 fn direction(mut self, direction: Direction) -> Self {
407 self.get_layout().layout.direction = direction;
408 self
409 }
410 fn main_align(mut self, main_align: Alignment) -> Self {
411 self.get_layout().layout.main_alignment = main_align;
412 self
413 }
414
415 fn cross_align(mut self, cross_align: Alignment) -> Self {
416 self.get_layout().layout.cross_alignment = cross_align;
417 self
418 }
419 fn spacing(mut self, spacing: f32) -> Self {
420 self.get_layout().layout.spacing = torin::geometry::Length::new(spacing);
421 self
422 }
423
424 fn content(mut self, content: Content) -> Self {
425 self.get_layout().layout.content = content;
426 self
427 }
428 fn center(mut self) -> Self {
429 self.get_layout().layout.main_alignment = Alignment::Center;
430 self.get_layout().layout.cross_alignment = Alignment::Center;
431
432 self
433 }
434
435 fn offset_x(mut self, offset_x: impl Into<f32>) -> Self {
436 self.get_layout().layout.offset_x = Length::new(offset_x.into());
437 self
438 }
439
440 fn offset_y(mut self, offset_y: impl Into<f32>) -> Self {
441 self.get_layout().layout.offset_y = Length::new(offset_y.into());
442 self
443 }
444
445 fn vertical(mut self) -> Self {
446 self.get_layout().layout.direction = Direction::vertical();
447 self
448 }
449
450 fn horizontal(mut self) -> Self {
451 self.get_layout().layout.direction = Direction::horizontal();
452 self
453 }
454}
455
456pub trait ContainerExt
457where
458 Self: LayoutExt,
459{
460 fn position(mut self, position: impl Into<Position>) -> Self {
461 self.get_layout().layout.position = position.into();
462 self
463 }
464
465 fn width(mut self, width: impl Into<Size>) -> Self {
466 self.get_layout().layout.width = width.into();
467 self
468 }
469
470 fn height(mut self, height: impl Into<Size>) -> Self {
471 self.get_layout().layout.height = height.into();
472 self
473 }
474
475 fn padding(mut self, padding: impl Into<Gaps>) -> Self {
476 self.get_layout().layout.padding = padding.into();
477 self
478 }
479
480 fn margin(mut self, margin: impl Into<Gaps>) -> Self {
481 self.get_layout().layout.margin = margin.into();
482 self
483 }
484
485 fn min_width(mut self, minimum_width: impl Into<Size>) -> Self {
486 self.get_layout().layout.minimum_width = minimum_width.into();
487 self
488 }
489
490 fn min_height(mut self, minimum_height: impl Into<Size>) -> Self {
491 self.get_layout().layout.minimum_height = minimum_height.into();
492 self
493 }
494
495 fn max_width(mut self, maximum_width: impl Into<Size>) -> Self {
496 self.get_layout().layout.maximum_width = maximum_width.into();
497 self
498 }
499
500 fn max_height(mut self, maximum_height: impl Into<Size>) -> Self {
501 self.get_layout().layout.maximum_height = maximum_height.into();
502 self
503 }
504
505 fn visible_width(mut self, visible_width: impl Into<VisibleSize>) -> Self {
506 self.get_layout().layout.visible_width = visible_width.into();
507 self
508 }
509
510 fn visible_height(mut self, visible_height: impl Into<VisibleSize>) -> Self {
511 self.get_layout().layout.visible_height = visible_height.into();
512 self
513 }
514
515 fn expanded(mut self) -> Self {
516 self.get_layout().layout.width = Size::fill();
517 self.get_layout().layout.height = Size::fill();
518 self
519 }
520}
521
522pub trait LayoutExt
523where
524 Self: Sized,
525{
526 fn get_layout(&mut self) -> &mut LayoutData;
527
528 fn layout(mut self, layout: LayoutData) -> Self {
529 *self.get_layout() = layout;
530 self
531 }
532}
533
534pub trait ImageExt
535where
536 Self: LayoutExt,
537{
538 fn width(mut self, width: Size) -> Self {
539 self.get_layout().layout.width = width;
540 self
541 }
542
543 fn height(mut self, height: Size) -> Self {
544 self.get_layout().layout.height = height;
545 self
546 }
547
548 fn get_image_data(&mut self) -> &mut ImageData;
549
550 fn image_data(mut self, image_data: ImageData) -> Self {
551 *self.get_image_data() = image_data;
552 self
553 }
554
555 fn sampling_mode(mut self, sampling_mode: SamplingMode) -> Self {
556 self.get_image_data().sampling_mode = sampling_mode;
557 self
558 }
559
560 fn aspect_ratio(mut self, aspect_ratio: AspectRatio) -> Self {
561 self.get_image_data().aspect_ratio = aspect_ratio;
562 self
563 }
564
565 fn image_cover(mut self, image_cover: ImageCover) -> Self {
566 self.get_image_data().image_cover = image_cover;
567 self
568 }
569}
570
571pub trait AccessibilityExt: Sized {
572 fn get_accessibility_data(&mut self) -> &mut AccessibilityData;
573
574 fn accessibility(mut self, accessibility: AccessibilityData) -> Self {
575 *self.get_accessibility_data() = accessibility;
576 self
577 }
578
579 fn a11y_id(mut self, a11y_id: impl Into<Option<AccessibilityId>>) -> Self {
580 self.get_accessibility_data().a11y_id = a11y_id.into();
581 self
582 }
583
584 fn a11y_focusable(mut self, a11y_focusable: impl Into<Focusable>) -> Self {
585 self.get_accessibility_data().a11y_focusable = a11y_focusable.into();
586 self
587 }
588
589 fn a11y_auto_focus(mut self, a11y_auto_focus: impl Into<bool>) -> Self {
590 self.get_accessibility_data().a11y_auto_focus = a11y_auto_focus.into();
591 self
592 }
593
594 fn a11y_member_of(mut self, a11y_member_of: impl Into<AccessibilityId>) -> Self {
595 self.get_accessibility_data()
596 .builder
597 .set_member_of(a11y_member_of.into());
598 self
599 }
600
601 fn a11y_role(mut self, a11y_role: impl Into<AccessibilityRole>) -> Self {
602 self.get_accessibility_data()
603 .builder
604 .set_role(a11y_role.into());
605 self
606 }
607
608 fn a11y_alt(mut self, value: impl Into<Box<str>>) -> Self {
609 self.get_accessibility_data().builder.set_label(value);
610 self
611 }
612
613 fn a11y_builder(mut self, with: impl FnOnce(&mut accesskit::Node)) -> Self {
614 with(&mut self.get_accessibility_data().builder);
615 self
616 }
617}
618
619pub trait TextStyleExt
620where
621 Self: Sized,
622{
623 fn get_text_style_data(&mut self) -> &mut TextStyleData;
624
625 fn color(mut self, color: impl Into<Color>) -> Self {
626 self.get_text_style_data().color = Some(color.into());
627 self
628 }
629
630 fn text_align(mut self, text_align: impl Into<TextAlign>) -> Self {
631 self.get_text_style_data().text_align = Some(text_align.into());
632 self
633 }
634
635 fn font_size(mut self, font_size: impl Into<FontSize>) -> Self {
636 self.get_text_style_data().font_size = Some(font_size.into());
637 self
638 }
639
640 fn font_family(mut self, font_family: impl Into<Cow<'static, str>>) -> Self {
641 self.get_text_style_data()
642 .font_families
643 .push(font_family.into());
644 self
645 }
646
647 fn font_slant(mut self, font_slant: impl Into<FontSlant>) -> Self {
648 self.get_text_style_data().font_slant = Some(font_slant.into());
649 self
650 }
651
652 fn font_weight(mut self, font_weight: impl Into<FontWeight>) -> Self {
653 self.get_text_style_data().font_weight = Some(font_weight.into());
654 self
655 }
656
657 fn font_width(mut self, font_width: impl Into<FontWidth>) -> Self {
658 self.get_text_style_data().font_width = Some(font_width.into());
659 self
660 }
661
662 fn text_height(mut self, text_height: impl Into<TextHeightBehavior>) -> Self {
663 self.get_text_style_data().text_height = Some(text_height.into());
664 self
665 }
666
667 fn text_overflow(mut self, text_overflow: impl Into<TextOverflow>) -> Self {
668 self.get_text_style_data().text_overflow = Some(text_overflow.into());
669 self
670 }
671
672 fn text_shadow(mut self, text_shadow: impl Into<TextShadow>) -> Self {
673 self.get_text_style_data()
674 .text_shadows
675 .push(text_shadow.into());
676 self
677 }
678}
679
680pub trait MaybeExt
681where
682 Self: Sized,
683{
684 fn maybe(self, bool: impl Into<bool>, then: impl FnOnce(Self) -> Self) -> Self {
685 if bool.into() { then(self) } else { self }
686 }
687
688 fn map<T>(self, data: Option<T>, then: impl FnOnce(Self, T) -> Self) -> Self {
689 if let Some(data) = data {
690 then(self, data)
691 } else {
692 self
693 }
694 }
695}
696
697pub trait LayerExt
698where
699 Self: Sized,
700{
701 fn get_layer(&mut self) -> &mut i16;
702
703 fn layer(mut self, layer: i16) -> Self {
704 *self.get_layer() = layer;
705 self
706 }
707}
708
709pub trait ScrollableExt
710where
711 Self: Sized,
712{
713 fn get_effect(&mut self) -> &mut EffectData;
714
715 fn scrollable(mut self, scrollable: impl Into<bool>) -> Self {
716 self.get_effect().scrollable = scrollable.into();
717 self
718 }
719}