1use std::rc::Rc;
2
3use taffy::{AlignContent, AlignItems, AlignSelf, FlexDirection, FlexWrap, JustifyContent};
4
5use crate::animation::AnimationSpec;
6use crate::{Brush, Color, PointerEvent, Size, Transform, Vec2};
7
8#[derive(Clone, Copy, Debug)]
12pub struct StateColors {
13 pub default: Color,
14 pub hovered: Color,
15 pub pressed: Color,
16 pub disabled: Color,
17}
18
19#[derive(Clone, Copy, Debug)]
21pub struct StateElevation {
22 pub default: f32,
23 pub hovered: f32,
24 pub pressed: f32,
25 pub disabled: f32,
26}
27
28macro_rules! merge_opts {
29 ($dst:ident, $src:ident; $($f:ident),+ $(,)?) => {
30 $( $dst.$f = $src.$f.or($dst.$f); )+
31 };
32}
33macro_rules! merge_flags {
34 ($dst:ident, $src:ident; $($f:ident),+ $(,)?) => {
35 $( $dst.$f |= $src.$f; )+
36 };
37}
38
39macro_rules! impl_option_fields {
40 ($ty:ty, $fn:ident) => {
41 impl $ty {
42 $fn!(replace);
43 }
44 };
45 ($ty:ident) => {
46 impl $ty {
47 pub fn then(mut self, other: Self) -> Self {
50 merge_opts!(self, other;
51 key, size, width, height,
52 padding, padding_values,
53 min_width, min_height, max_width, max_height,
54 background, state_colors, state_elevation, border,
55 flex_grow, flex_shrink, flex_basis, flex_wrap, flex_dir,
56 gap, row_gap, column_gap,
57 align_self, justify_content, align_items_container, align_content,
58 clip_rounded, render_z_index,
59 on_scroll,
60 on_pointer_down, on_pointer_move, on_pointer_up,
61 on_pointer_enter, on_pointer_leave,
62 semantics, alpha, transform,
63 grid, grid_col_span, grid_row_span,
64 position_type,
65 offset_left, offset_right, offset_top, offset_bottom,
66 margin_left, margin_right, margin_top, margin_bottom,
67 aspect_ratio, painter,
68 on_drag_start, on_drag_end, on_drag_enter, on_drag_over, on_drag_leave, on_drop,
69 on_action, cursor, animate_content_size, focus_requester,
70 );
71 merge_flags!(self, other;
72 fill_max, fill_max_w, fill_max_h,
73 hit_passthrough, input_blocker, repaint_boundary, click, disabled,
74 );
75 if other.z_index != 0.0 {
76 self.z_index = other.z_index;
77 }
78 self
79 }
80 }
81 };
82}
83
84#[derive(Clone, Debug)]
85pub struct Border {
86 pub width: f32,
87 pub color: Color,
88 pub radius: f32,
89}
90
91#[derive(Clone, Copy, Debug, Default)]
92pub struct PaddingValues {
93 pub left: f32,
94 pub right: f32,
95 pub top: f32,
96 pub bottom: f32,
97}
98
99#[derive(Clone, Debug)]
100pub struct GridConfig {
101 pub columns: usize,
102 pub row_gap: f32,
103 pub column_gap: f32,
104}
105
106#[derive(Clone, Copy, Debug)]
113pub struct ShadowSpec {
114 pub blur_radius: f32,
115 pub offset_y: f32,
116 pub color: Color,
117}
118
119#[derive(Clone, Copy, Debug)]
120pub enum PositionType {
121 Relative,
122 Absolute,
123}
124
125#[derive(Clone, Default)]
126pub struct Modifier {
127 pub key: Option<u64>,
133
134 pub size: Option<Size>,
135 pub width: Option<f32>,
136 pub height: Option<f32>,
137 pub fill_max: bool,
138 pub fill_max_w: bool,
139 pub fill_max_h: bool,
140 pub padding: Option<f32>,
141 pub padding_values: Option<PaddingValues>,
142 pub min_width: Option<f32>,
143 pub min_height: Option<f32>,
144 pub max_width: Option<f32>,
145 pub max_height: Option<f32>,
146 pub background: Option<Brush>,
147 pub state_colors: Option<StateColors>,
148 pub state_elevation: Option<StateElevation>,
149
150 pub border: Option<Border>,
151 pub flex_grow: Option<f32>,
152 pub flex_shrink: Option<f32>,
153 pub flex_basis: Option<f32>,
154 pub flex_wrap: Option<FlexWrap>,
155 pub flex_dir: Option<FlexDirection>,
156 pub gap: Option<f32>,
157 pub row_gap: Option<f32>,
158 pub column_gap: Option<f32>,
159 pub align_self: Option<AlignSelf>,
160 pub justify_content: Option<JustifyContent>,
161 pub align_items_container: Option<AlignItems>,
162 pub align_content: Option<AlignContent>,
163 pub clip_rounded: Option<f32>,
164 pub z_index: f32,
166 pub render_z_index: Option<f32>,
168 pub hit_passthrough: bool,
170 pub input_blocker: bool,
172 pub repaint_boundary: bool,
173 pub click: bool,
174 pub disabled: bool,
176 pub on_scroll: Option<Rc<dyn Fn(Vec2) -> Vec2>>,
177 pub on_pointer_down: Option<Rc<dyn Fn(PointerEvent)>>,
178 pub on_pointer_move: Option<Rc<dyn Fn(PointerEvent)>>,
179 pub on_pointer_up: Option<Rc<dyn Fn(PointerEvent)>>,
180 pub on_pointer_enter: Option<Rc<dyn Fn(PointerEvent)>>,
181 pub on_pointer_leave: Option<Rc<dyn Fn(PointerEvent)>>,
182 pub semantics: Option<crate::Semantics>,
183 pub alpha: Option<f32>,
184 pub graphics_layer: Option<f32>,
185 pub shadow: Option<ShadowSpec>,
186 pub transform: Option<Transform>,
187 pub grid: Option<GridConfig>,
188 pub grid_col_span: Option<u16>,
189 pub grid_row_span: Option<u16>,
190 pub position_type: Option<PositionType>,
191 pub offset_left: Option<f32>,
192 pub offset_right: Option<f32>,
193 pub offset_top: Option<f32>,
194 pub offset_bottom: Option<f32>,
195 pub margin_left: Option<f32>,
196 pub margin_right: Option<f32>,
197 pub margin_top: Option<f32>,
198 pub margin_bottom: Option<f32>,
199 pub aspect_ratio: Option<f32>,
200 pub painter: Option<Rc<dyn Fn(&mut crate::Scene, crate::Rect)>>,
201
202 pub on_drag_start: Option<Rc<dyn Fn(crate::dnd::DragStart) -> Option<crate::dnd::DragPayload>>>,
204 pub on_drag_end: Option<Rc<dyn Fn(crate::dnd::DragEnd)>>,
205 pub on_drag_enter: Option<Rc<dyn Fn(crate::dnd::DragOver)>>,
206 pub on_drag_over: Option<Rc<dyn Fn(crate::dnd::DragOver)>>,
207 pub on_drag_leave: Option<Rc<dyn Fn(crate::dnd::DragOver)>>,
208 pub on_drop: Option<Rc<dyn Fn(crate::dnd::DropEvent) -> bool>>,
209
210 pub on_action: Option<Rc<dyn Fn(crate::shortcuts::Action) -> bool>>,
211
212 pub cursor: Option<crate::CursorIcon>,
214
215 pub animate_content_size: Option<AnimationSpec>,
218
219 pub focus_requester: Option<crate::runtime::FocusRequester>,
223}
224
225impl std::fmt::Debug for Modifier {
226 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
227 let mut s = f.debug_struct("Modifier");
228
229 macro_rules! opt_val {
230 ($($name:ident),+ $(,)?) => {
231 $( if self.$name.is_some() { s.field(stringify!($name), &self.$name); } )+
232 };
233 }
234 opt_val!(
235 key,
236 size,
237 width,
238 height,
239 padding,
240 padding_values,
241 min_width,
242 min_height,
243 max_width,
244 max_height,
245 background,
246 state_colors,
247 state_elevation,
248 border,
249 flex_grow,
250 flex_shrink,
251 flex_basis,
252 flex_wrap,
253 flex_dir,
254 gap,
255 row_gap,
256 column_gap,
257 align_self,
258 justify_content,
259 align_items_container,
260 align_content,
261 clip_rounded,
262 render_z_index,
263 semantics,
264 alpha,
265 transform,
266 grid,
267 grid_col_span,
268 grid_row_span,
269 position_type,
270 offset_left,
271 offset_right,
272 offset_top,
273 offset_bottom,
274 margin_left,
275 margin_right,
276 margin_top,
277 margin_bottom,
278 aspect_ratio,
279 cursor,
280 animate_content_size,
281 );
282
283 macro_rules! opt_cb {
284 ($($name:ident),+ $(,)?) => {
285 $( if self.$name.is_some() { s.field(stringify!($name), &"…"); } )+
286 };
287 }
288 opt_cb!(
289 on_scroll,
290 on_pointer_down,
291 on_pointer_move,
292 on_pointer_up,
293 on_pointer_enter,
294 on_pointer_leave,
295 painter,
296 on_drag_start,
297 on_drag_end,
298 on_drag_enter,
299 on_drag_over,
300 on_drag_leave,
301 on_drop,
302 on_action,
303 );
304
305 macro_rules! flag {
306 ($($name:ident),+ $(,)?) => {
307 $( if self.$name { s.field(stringify!($name), &true); } )+
308 };
309 }
310 flag!(
311 fill_max,
312 fill_max_w,
313 fill_max_h,
314 hit_passthrough,
315 input_blocker,
316 repaint_boundary,
317 click,
318 disabled,
319 );
320
321 if self.z_index != 0.0 {
322 s.field("z_index", &self.z_index);
323 }
324
325 s.finish()
326 }
327}
328
329impl_option_fields!(Modifier);
330
331impl Modifier {
332 pub fn new() -> Self {
333 Self::default()
334 }
335
336 pub fn key(mut self, key: u64) -> Self {
339 self.key = Some(key);
340 self
341 }
342
343 pub fn size(mut self, w: f32, h: f32) -> Self {
344 self.size = Some(Size {
345 width: w,
346 height: h,
347 });
348 self
349 }
350 pub fn width(mut self, w: f32) -> Self {
351 self.width = Some(w);
352 self
353 }
354 pub fn height(mut self, h: f32) -> Self {
355 self.height = Some(h);
356 self
357 }
358 pub fn fill_max_size(mut self) -> Self {
359 self.fill_max = true;
360 self
361 }
362 pub fn fill_max_width(mut self) -> Self {
363 self.fill_max_w = true;
364 self
365 }
366 pub fn fill_max_height(mut self) -> Self {
367 self.fill_max_h = true;
368 self
369 }
370 pub fn padding(mut self, v: f32) -> Self {
371 self.padding = Some(v);
372 self
373 }
374 pub fn padding_values(mut self, padding: PaddingValues) -> Self {
375 self.padding_values = Some(padding);
376 self
377 }
378 pub fn ime_padding(mut self) -> Self {
381 let insets = crate::locals::window_insets();
382 let mut p = self.padding_values.unwrap_or_default();
383 p.bottom += insets.ime_bottom;
384 self.padding_values = Some(p);
385 self
386 }
387 pub fn system_bars_padding(mut self) -> Self {
389 let insets = crate::locals::window_insets();
390 let mut p = self.padding_values.unwrap_or_default();
391 p.top += insets.top;
392 p.bottom += insets.bottom;
393 self.padding_values = Some(p);
394 self
395 }
396 pub fn status_bars_padding(mut self) -> Self {
398 let insets = crate::locals::window_insets();
399 let mut p = self.padding_values.unwrap_or_default();
400 p.top += insets.top;
401 self.padding_values = Some(p);
402 self
403 }
404 pub fn navigation_bars_padding(mut self) -> Self {
406 let insets = crate::locals::window_insets();
407 let mut p = self.padding_values.unwrap_or_default();
408 p.bottom += insets.bottom;
409 self.padding_values = Some(p);
410 self
411 }
412 pub fn min_size(mut self, w: f32, h: f32) -> Self {
413 self.min_width = Some(w);
414 self.min_height = Some(h);
415 self
416 }
417 pub fn max_size(mut self, w: f32, h: f32) -> Self {
418 self.max_width = Some(w);
419 self.max_height = Some(h);
420 self
421 }
422 pub fn min_width(mut self, w: f32) -> Self {
423 self.min_width = Some(w);
424 self
425 }
426 pub fn min_height(mut self, h: f32) -> Self {
427 self.min_height = Some(h);
428 self
429 }
430 pub fn max_width(mut self, w: f32) -> Self {
431 self.max_width = Some(w);
432 self
433 }
434 pub fn max_height(mut self, h: f32) -> Self {
435 self.max_height = Some(h);
436 self
437 }
438 pub fn background(mut self, color: Color) -> Self {
440 self.background = Some(Brush::Solid(color));
441 self
442 }
443 pub fn background_brush(mut self, brush: Brush) -> Self {
445 self.background = Some(brush);
446 self
447 }
448 pub fn border(mut self, width: f32, color: Color, radius: f32) -> Self {
449 self.border = Some(Border {
450 width,
451 color,
452 radius,
453 });
454 self
455 }
456 pub fn flex_grow(mut self, v: f32) -> Self {
457 self.flex_grow = Some(v);
458 self
459 }
460 pub fn flex_shrink(mut self, v: f32) -> Self {
461 self.flex_shrink = Some(v);
462 self
463 }
464 pub fn flex_basis(mut self, v: f32) -> Self {
465 self.flex_basis = Some(v);
466 self
467 }
468 pub fn flex_wrap(mut self, w: FlexWrap) -> Self {
469 self.flex_wrap = Some(w);
470 self
471 }
472 pub fn flex_dir(mut self, d: FlexDirection) -> Self {
473 self.flex_dir = Some(d);
474 self
475 }
476 pub fn gap(mut self, v: f32) -> Self {
477 let v = v.max(0.0);
478 self.gap = Some(v);
479 self.row_gap = Some(v);
480 self.column_gap = Some(v);
481 self
482 }
483 pub fn row_gap(mut self, v: f32) -> Self {
484 self.row_gap = Some(v.max(0.0));
485 self
486 }
487 pub fn column_gap(mut self, v: f32) -> Self {
488 self.column_gap = Some(v.max(0.0));
489 self
490 }
491 pub fn align_self(mut self, a: AlignSelf) -> Self {
492 self.align_self = Some(a);
493 self
494 }
495 pub fn align_self_center(mut self) -> Self {
496 self.align_self = Some(AlignSelf::Center);
497 self
498 }
499 pub fn justify_content(mut self, j: JustifyContent) -> Self {
500 self.justify_content = Some(j);
501 self
502 }
503 pub fn align_items(mut self, a: AlignItems) -> Self {
504 self.align_items_container = Some(a);
505 self
506 }
507 pub fn align_content(mut self, a: AlignContent) -> Self {
508 self.align_content = Some(a);
509 self
510 }
511 pub fn clip_rounded(mut self, radius: f32) -> Self {
512 self.clip_rounded = Some(radius);
513 self
514 }
515 pub fn z_index(mut self, z: f32) -> Self {
516 self.z_index = z;
517 self
518 }
519
520 pub fn render_z_index(mut self, z: f32) -> Self {
523 self.render_z_index = Some(z);
524 self
525 }
526
527 pub fn input_blocker(mut self) -> Self {
529 self.input_blocker = true;
530 self
531 }
532
533 pub fn hit_passthrough(mut self) -> Self {
534 self.hit_passthrough = true;
535 self
536 }
537 pub fn clickable(mut self) -> Self {
538 self.click = true;
539 self
540 }
541 pub fn state_colors(mut self, colors: StateColors) -> Self {
544 self.state_colors = Some(colors);
545 self
546 }
547 pub fn state_elevation(mut self, elev: StateElevation) -> Self {
549 self.state_elevation = Some(elev);
550 self
551 }
552 pub fn disabled(mut self) -> Self {
554 self.disabled = true;
555 self
556 }
557 pub fn enabled(mut self, enabled: bool) -> Self {
559 self.disabled = !enabled;
560 self
561 }
562 pub fn on_scroll(mut self, f: impl Fn(Vec2) -> Vec2 + 'static) -> Self {
563 self.on_scroll = Some(Rc::new(f));
564 self
565 }
566 pub fn on_pointer_down(mut self, f: impl Fn(PointerEvent) + 'static) -> Self {
567 self.on_pointer_down = Some(Rc::new(f));
568 self
569 }
570 pub fn on_pointer_move(mut self, f: impl Fn(PointerEvent) + 'static) -> Self {
571 self.on_pointer_move = Some(Rc::new(f));
572 self
573 }
574 pub fn on_pointer_up(mut self, f: impl Fn(PointerEvent) + 'static) -> Self {
575 self.on_pointer_up = Some(Rc::new(f));
576 self
577 }
578 pub fn on_pointer_enter(mut self, f: impl Fn(PointerEvent) + 'static) -> Self {
579 self.on_pointer_enter = Some(Rc::new(f));
580 self
581 }
582 pub fn on_pointer_leave(mut self, f: impl Fn(PointerEvent) + 'static) -> Self {
583 self.on_pointer_leave = Some(Rc::new(f));
584 self
585 }
586 pub fn semantics(mut self, s: crate::Semantics) -> Self {
587 self.semantics = Some(s);
588 self
589 }
590 pub fn alpha(mut self, a: f32) -> Self {
591 self.alpha = Some(a);
592 self
593 }
594 pub fn graphics_layer(mut self, alpha: f32) -> Self {
599 self.graphics_layer = Some(alpha.clamp(0.0, 1.0));
600 self
601 }
602 pub fn shadow(mut self, blur_radius: f32, offset_y: f32) -> Self {
606 self.shadow = Some(ShadowSpec {
607 blur_radius: blur_radius.max(0.0),
608 offset_y,
609 color: Color(0, 0, 0, 64),
610 });
611 self
612 }
613 pub fn shadow_with_color(mut self, blur_radius: f32, offset_y: f32, color: Color) -> Self {
615 self.shadow = Some(ShadowSpec {
616 blur_radius: blur_radius.max(0.0),
617 offset_y,
618 color,
619 });
620 self
621 }
622 pub fn elevation(mut self, level: f32) -> Self {
626 if level <= 0.0 {
627 self.shadow = None;
628 return self;
629 }
630 self.shadow = Some(ShadowSpec {
631 blur_radius: level * 2.0,
632 offset_y: level * 0.5,
633 color: Color(0, 0, 0, (level * 8.0).clamp(8.0, 80.0) as u8),
634 });
635 self
636 }
637 pub fn transform(mut self, t: Transform) -> Self {
638 self.transform = Some(t);
639 self
640 }
641 pub fn grid(mut self, columns: usize, row_gap: f32, column_gap: f32) -> Self {
642 self.grid = Some(GridConfig {
643 columns,
644 row_gap,
645 column_gap,
646 });
647 self
648 }
649 pub fn grid_span(mut self, col_span: u16, row_span: u16) -> Self {
650 self.grid_col_span = Some(col_span);
651 self.grid_row_span = Some(row_span);
652 self
653 }
654 pub fn absolute(mut self) -> Self {
655 self.position_type = Some(PositionType::Absolute);
656 self
657 }
658 pub fn offset(
659 mut self,
660 left: Option<f32>,
661 top: Option<f32>,
662 right: Option<f32>,
663 bottom: Option<f32>,
664 ) -> Self {
665 self.offset_left = left;
666 self.offset_top = top;
667 self.offset_right = right;
668 self.offset_bottom = bottom;
669 self
670 }
671 pub fn offset_left(mut self, v: f32) -> Self {
672 self.offset_left = Some(v);
673 self
674 }
675 pub fn offset_right(mut self, v: f32) -> Self {
676 self.offset_right = Some(v);
677 self
678 }
679 pub fn offset_top(mut self, v: f32) -> Self {
680 self.offset_top = Some(v);
681 self
682 }
683 pub fn offset_bottom(mut self, v: f32) -> Self {
684 self.offset_bottom = Some(v);
685 self
686 }
687
688 pub fn margin(mut self, v: f32) -> Self {
689 self.margin_left = Some(v);
690 self.margin_right = Some(v);
691 self.margin_top = Some(v);
692 self.margin_bottom = Some(v);
693 self
694 }
695
696 pub fn margin_horizontal(mut self, v: f32) -> Self {
697 self.margin_left = Some(v);
698 self.margin_right = Some(v);
699 self
700 }
701
702 pub fn margin_vertical(mut self, v: f32) -> Self {
703 self.margin_top = Some(v);
704 self.margin_bottom = Some(v);
705 self
706 }
707 pub fn aspect_ratio(mut self, ratio: f32) -> Self {
708 self.aspect_ratio = Some(ratio);
709 self
710 }
711 pub fn painter(mut self, f: impl Fn(&mut crate::Scene, crate::Rect) + 'static) -> Self {
712 self.painter = Some(Rc::new(f));
713 self
714 }
715 pub fn scale(self, s: f32) -> Self {
716 self.scale2(s, s)
717 }
718 pub fn scale2(mut self, sx: f32, sy: f32) -> Self {
719 let mut t = self.transform.unwrap_or_else(Transform::identity);
720 t.scale_x *= sx;
721 t.scale_y *= sy;
722 self.transform = Some(t);
723 self
724 }
725 pub fn translate(mut self, x: f32, y: f32) -> Self {
726 let t = self.transform.unwrap_or_else(Transform::identity);
727 self.transform = Some(t.combine(&Transform::translate(x, y)));
728 self
729 }
730 pub fn translate_vec2(mut self, v: Vec2) -> Self {
731 self.translate(v.x, v.y)
732 }
733 pub fn rotate(mut self, radians: f32) -> Self {
734 let mut t = self.transform.unwrap_or_else(Transform::identity);
735 t.rotate += radians;
736 self.transform = Some(t);
737 self
738 }
739 pub fn weight(mut self, w: f32) -> Self {
740 let w = w.max(0.0);
741 self.flex_grow = Some(w);
742 self.flex_shrink = Some(1.0);
743 self.flex_basis = Some(0.0);
745 self
746 }
747 pub fn repaint_boundary(mut self) -> Self {
751 self.repaint_boundary = true;
752 self
753 }
754 pub fn on_action(mut self, f: impl Fn(crate::shortcuts::Action) -> bool + 'static) -> Self {
755 self.on_action = Some(Rc::new(f));
756 self
757 }
758
759 pub fn on_drag_start(
761 mut self,
762 f: impl Fn(crate::dnd::DragStart) -> Option<crate::dnd::DragPayload> + 'static,
763 ) -> Self {
764 self.on_drag_start = Some(Rc::new(f));
765 self
766 }
767
768 pub fn on_drag_end(mut self, f: impl Fn(crate::dnd::DragEnd) + 'static) -> Self {
770 self.on_drag_end = Some(Rc::new(f));
771 self
772 }
773
774 pub fn on_drag_enter(mut self, f: impl Fn(crate::dnd::DragOver) + 'static) -> Self {
776 self.on_drag_enter = Some(Rc::new(f));
777 self
778 }
779
780 pub fn on_drag_over(mut self, f: impl Fn(crate::dnd::DragOver) + 'static) -> Self {
782 self.on_drag_over = Some(Rc::new(f));
783 self
784 }
785
786 pub fn on_drag_leave(mut self, f: impl Fn(crate::dnd::DragOver) + 'static) -> Self {
788 self.on_drag_leave = Some(Rc::new(f));
789 self
790 }
791
792 pub fn on_drop(mut self, f: impl Fn(crate::dnd::DropEvent) -> bool + 'static) -> Self {
795 self.on_drop = Some(Rc::new(f));
796 self
797 }
798
799 pub fn cursor(mut self, c: crate::CursorIcon) -> Self {
801 self.cursor = Some(c);
802 self
803 }
804
805 pub fn animate_content_size(mut self, spec: AnimationSpec) -> Self {
809 self.animate_content_size = Some(spec);
810 self
811 }
812
813 pub fn focus_requester(mut self, fr: crate::runtime::FocusRequester) -> Self {
816 self.focus_requester = Some(fr);
817 self
818 }
819}