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, on_focus_changed,
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)]
120#[non_exhaustive]
121pub enum PositionType {
122 Relative,
123 Absolute,
124}
125
126#[derive(Clone, Default)]
127pub struct Modifier {
128 pub key: Option<u64>,
134
135 pub size: Option<Size>,
136 pub width: Option<f32>,
137 pub height: Option<f32>,
138 pub fill_max: bool,
139 pub fill_max_w: bool,
140 pub fill_max_h: bool,
141 pub padding: Option<f32>,
142 pub padding_values: Option<PaddingValues>,
143 pub min_width: Option<f32>,
144 pub min_height: Option<f32>,
145 pub max_width: Option<f32>,
146 pub max_height: Option<f32>,
147 pub background: Option<Brush>,
148 pub state_colors: Option<StateColors>,
149 pub state_elevation: Option<StateElevation>,
150
151 pub border: Option<Border>,
152 pub flex_grow: Option<f32>,
153 pub flex_shrink: Option<f32>,
154 pub flex_basis: Option<f32>,
155 pub flex_wrap: Option<FlexWrap>,
156 pub flex_dir: Option<FlexDirection>,
157 pub gap: Option<f32>,
158 pub row_gap: Option<f32>,
159 pub column_gap: Option<f32>,
160 pub align_self: Option<AlignSelf>,
161 pub justify_content: Option<JustifyContent>,
162 pub align_items_container: Option<AlignItems>,
163 pub align_content: Option<AlignContent>,
164 pub clip_rounded: Option<f32>,
165 pub z_index: f32,
167 pub render_z_index: Option<f32>,
169 pub hit_passthrough: bool,
171 pub input_blocker: bool,
173 pub repaint_boundary: bool,
174 pub click: bool,
175 pub disabled: bool,
177 pub on_scroll: Option<Rc<dyn Fn(Vec2) -> Vec2>>,
178 pub on_pointer_down: Option<Rc<dyn Fn(PointerEvent)>>,
179 pub on_pointer_move: Option<Rc<dyn Fn(PointerEvent)>>,
180 pub on_pointer_up: Option<Rc<dyn Fn(PointerEvent)>>,
181 pub on_pointer_enter: Option<Rc<dyn Fn(PointerEvent)>>,
182 pub on_pointer_leave: Option<Rc<dyn Fn(PointerEvent)>>,
183 pub semantics: Option<crate::Semantics>,
184 pub alpha: Option<f32>,
185 pub graphics_layer: Option<f32>,
186 pub shadow: Option<ShadowSpec>,
187 pub transform: Option<Transform>,
188 pub grid: Option<GridConfig>,
189 pub grid_col_span: Option<u16>,
190 pub grid_row_span: Option<u16>,
191 pub position_type: Option<PositionType>,
192 pub offset_left: Option<f32>,
193 pub offset_right: Option<f32>,
194 pub offset_top: Option<f32>,
195 pub offset_bottom: Option<f32>,
196 pub margin_left: Option<f32>,
197 pub margin_right: Option<f32>,
198 pub margin_top: Option<f32>,
199 pub margin_bottom: Option<f32>,
200 pub aspect_ratio: Option<f32>,
201 pub painter: Option<Rc<dyn Fn(&mut crate::Scene, crate::Rect)>>,
202
203 pub on_drag_start: Option<Rc<dyn Fn(crate::dnd::DragStart) -> Option<crate::dnd::DragPayload>>>,
205 pub on_drag_end: Option<Rc<dyn Fn(crate::dnd::DragEnd)>>,
206 pub on_drag_enter: Option<Rc<dyn Fn(crate::dnd::DragOver)>>,
207 pub on_drag_over: Option<Rc<dyn Fn(crate::dnd::DragOver)>>,
208 pub on_drag_leave: Option<Rc<dyn Fn(crate::dnd::DragOver)>>,
209 pub on_drop: Option<Rc<dyn Fn(crate::dnd::DropEvent) -> bool>>,
210
211 pub on_action: Option<Rc<dyn Fn(crate::shortcuts::Action) -> bool>>,
212
213 pub cursor: Option<crate::CursorIcon>,
215
216 pub animate_content_size: Option<AnimationSpec>,
219
220 pub focus_requester: Option<crate::runtime::FocusRequester>,
224
225 pub on_focus_changed: Option<Rc<dyn Fn(bool)>>,
228}
229
230impl std::fmt::Debug for Modifier {
231 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
232 let mut s = f.debug_struct("Modifier");
233
234 macro_rules! opt_val {
235 ($($name:ident),+ $(,)?) => {
236 $( if self.$name.is_some() { s.field(stringify!($name), &self.$name); } )+
237 };
238 }
239 opt_val!(
240 key,
241 size,
242 width,
243 height,
244 padding,
245 padding_values,
246 min_width,
247 min_height,
248 max_width,
249 max_height,
250 background,
251 state_colors,
252 state_elevation,
253 border,
254 flex_grow,
255 flex_shrink,
256 flex_basis,
257 flex_wrap,
258 flex_dir,
259 gap,
260 row_gap,
261 column_gap,
262 align_self,
263 justify_content,
264 align_items_container,
265 align_content,
266 clip_rounded,
267 render_z_index,
268 semantics,
269 alpha,
270 transform,
271 grid,
272 grid_col_span,
273 grid_row_span,
274 position_type,
275 offset_left,
276 offset_right,
277 offset_top,
278 offset_bottom,
279 margin_left,
280 margin_right,
281 margin_top,
282 margin_bottom,
283 aspect_ratio,
284 cursor,
285 animate_content_size,
286 );
287
288 macro_rules! opt_cb {
289 ($($name:ident),+ $(,)?) => {
290 $( if self.$name.is_some() { s.field(stringify!($name), &"…"); } )+
291 };
292 }
293 opt_cb!(
294 on_scroll,
295 on_pointer_down,
296 on_pointer_move,
297 on_pointer_up,
298 on_pointer_enter,
299 on_pointer_leave,
300 painter,
301 on_drag_start,
302 on_drag_end,
303 on_drag_enter,
304 on_drag_over,
305 on_drag_leave,
306 on_drop,
307 on_action,
308 on_focus_changed,
309 );
310
311 macro_rules! flag {
312 ($($name:ident),+ $(,)?) => {
313 $( if self.$name { s.field(stringify!($name), &true); } )+
314 };
315 }
316 flag!(
317 fill_max,
318 fill_max_w,
319 fill_max_h,
320 hit_passthrough,
321 input_blocker,
322 repaint_boundary,
323 click,
324 disabled,
325 );
326
327 if self.z_index != 0.0 {
328 s.field("z_index", &self.z_index);
329 }
330
331 s.finish()
332 }
333}
334
335impl_option_fields!(Modifier);
336
337impl Modifier {
338 pub fn new() -> Self {
339 Self::default()
340 }
341
342 pub fn key(mut self, key: u64) -> Self {
345 self.key = Some(key);
346 self
347 }
348
349 pub fn size(mut self, w: f32, h: f32) -> Self {
350 self.size = Some(Size {
351 width: w,
352 height: h,
353 });
354 self
355 }
356 pub fn width(mut self, w: f32) -> Self {
357 self.width = Some(w);
358 self
359 }
360 pub fn height(mut self, h: f32) -> Self {
361 self.height = Some(h);
362 self
363 }
364 pub fn fill_max_size(mut self) -> Self {
365 self.fill_max = true;
366 self
367 }
368 pub fn fill_max_width(mut self) -> Self {
369 self.fill_max_w = true;
370 self
371 }
372 pub fn fill_max_height(mut self) -> Self {
373 self.fill_max_h = true;
374 self
375 }
376 pub fn padding(mut self, v: f32) -> Self {
377 self.padding = Some(v);
378 self
379 }
380 pub fn padding_values(mut self, padding: PaddingValues) -> Self {
381 self.padding_values = Some(padding);
382 self
383 }
384 pub fn ime_padding(mut self) -> Self {
387 let insets = crate::locals::window_insets();
388 let mut p = self.padding_values.unwrap_or_default();
389 p.bottom += insets.ime_bottom;
390 self.padding_values = Some(p);
391 self
392 }
393 pub fn system_bars_padding(mut self) -> Self {
395 let insets = crate::locals::window_insets();
396 let mut p = self.padding_values.unwrap_or_default();
397 p.top += insets.top;
398 p.bottom += insets.bottom;
399 self.padding_values = Some(p);
400 self
401 }
402 pub fn status_bars_padding(mut self) -> Self {
404 let insets = crate::locals::window_insets();
405 let mut p = self.padding_values.unwrap_or_default();
406 p.top += insets.top;
407 self.padding_values = Some(p);
408 self
409 }
410 pub fn navigation_bars_padding(mut self) -> Self {
412 let insets = crate::locals::window_insets();
413 let mut p = self.padding_values.unwrap_or_default();
414 p.bottom += insets.bottom;
415 self.padding_values = Some(p);
416 self
417 }
418 pub fn min_size(mut self, w: f32, h: f32) -> Self {
419 self.min_width = Some(w);
420 self.min_height = Some(h);
421 self
422 }
423 pub fn max_size(mut self, w: f32, h: f32) -> Self {
424 self.max_width = Some(w);
425 self.max_height = Some(h);
426 self
427 }
428 pub fn min_width(mut self, w: f32) -> Self {
429 self.min_width = Some(w);
430 self
431 }
432 pub fn min_height(mut self, h: f32) -> Self {
433 self.min_height = Some(h);
434 self
435 }
436 pub fn max_width(mut self, w: f32) -> Self {
437 self.max_width = Some(w);
438 self
439 }
440 pub fn max_height(mut self, h: f32) -> Self {
441 self.max_height = Some(h);
442 self
443 }
444 pub fn background(mut self, color: Color) -> Self {
446 self.background = Some(Brush::Solid(color));
447 self
448 }
449 pub fn background_brush(mut self, brush: Brush) -> Self {
451 self.background = Some(brush);
452 self
453 }
454 pub fn border(mut self, width: f32, color: Color, radius: f32) -> Self {
455 self.border = Some(Border {
456 width,
457 color,
458 radius,
459 });
460 self
461 }
462 pub fn flex_grow(mut self, v: f32) -> Self {
463 self.flex_grow = Some(v);
464 self
465 }
466 pub fn flex_shrink(mut self, v: f32) -> Self {
467 self.flex_shrink = Some(v);
468 self
469 }
470 pub fn flex_basis(mut self, v: f32) -> Self {
471 self.flex_basis = Some(v);
472 self
473 }
474 pub fn flex_wrap(mut self, w: FlexWrap) -> Self {
475 self.flex_wrap = Some(w);
476 self
477 }
478 pub fn flex_dir(mut self, d: FlexDirection) -> Self {
479 self.flex_dir = Some(d);
480 self
481 }
482 pub fn gap(mut self, v: f32) -> Self {
483 let v = v.max(0.0);
484 self.gap = Some(v);
485 self.row_gap = Some(v);
486 self.column_gap = Some(v);
487 self
488 }
489 pub fn row_gap(mut self, v: f32) -> Self {
490 self.row_gap = Some(v.max(0.0));
491 self
492 }
493 pub fn column_gap(mut self, v: f32) -> Self {
494 self.column_gap = Some(v.max(0.0));
495 self
496 }
497 pub fn align_self(mut self, a: AlignSelf) -> Self {
498 self.align_self = Some(a);
499 self
500 }
501 pub fn align_self_center(mut self) -> Self {
502 self.align_self = Some(AlignSelf::Center);
503 self
504 }
505 pub fn justify_content(mut self, j: JustifyContent) -> Self {
506 self.justify_content = Some(j);
507 self
508 }
509 pub fn align_items(mut self, a: AlignItems) -> Self {
510 self.align_items_container = Some(a);
511 self
512 }
513 pub fn align_content(mut self, a: AlignContent) -> Self {
514 self.align_content = Some(a);
515 self
516 }
517 pub fn clip_rounded(mut self, radius: f32) -> Self {
518 self.clip_rounded = Some(radius);
519 self
520 }
521 pub fn z_index(mut self, z: f32) -> Self {
522 self.z_index = z;
523 self
524 }
525
526 pub fn render_z_index(mut self, z: f32) -> Self {
529 self.render_z_index = Some(z);
530 self
531 }
532
533 pub fn input_blocker(mut self) -> Self {
535 self.input_blocker = true;
536 self
537 }
538
539 pub fn hit_passthrough(mut self) -> Self {
540 self.hit_passthrough = true;
541 self
542 }
543 pub fn clickable(mut self) -> Self {
544 self.click = true;
545 self
546 }
547 pub fn state_colors(mut self, colors: StateColors) -> Self {
550 self.state_colors = Some(colors);
551 self
552 }
553 pub fn state_elevation(mut self, elev: StateElevation) -> Self {
555 self.state_elevation = Some(elev);
556 self
557 }
558 pub fn disabled(mut self) -> Self {
560 self.disabled = true;
561 self
562 }
563 pub fn enabled(mut self, enabled: bool) -> Self {
565 self.disabled = !enabled;
566 self
567 }
568 pub fn on_scroll(mut self, f: impl Fn(Vec2) -> Vec2 + 'static) -> Self {
569 self.on_scroll = Some(Rc::new(f));
570 self
571 }
572 pub fn on_pointer_down(mut self, f: impl Fn(PointerEvent) + 'static) -> Self {
573 self.on_pointer_down = Some(Rc::new(f));
574 self
575 }
576 pub fn on_pointer_move(mut self, f: impl Fn(PointerEvent) + 'static) -> Self {
577 self.on_pointer_move = Some(Rc::new(f));
578 self
579 }
580 pub fn on_pointer_up(mut self, f: impl Fn(PointerEvent) + 'static) -> Self {
581 self.on_pointer_up = Some(Rc::new(f));
582 self
583 }
584 pub fn on_pointer_enter(mut self, f: impl Fn(PointerEvent) + 'static) -> Self {
585 self.on_pointer_enter = Some(Rc::new(f));
586 self
587 }
588 pub fn on_pointer_leave(mut self, f: impl Fn(PointerEvent) + 'static) -> Self {
589 self.on_pointer_leave = Some(Rc::new(f));
590 self
591 }
592 pub fn semantics(mut self, s: crate::Semantics) -> Self {
593 self.semantics = Some(s);
594 self
595 }
596 pub fn alpha(mut self, a: f32) -> Self {
597 self.alpha = Some(a);
598 self
599 }
600 pub fn graphics_layer(mut self, alpha: f32) -> Self {
605 self.graphics_layer = Some(alpha.clamp(0.0, 1.0));
606 self
607 }
608 pub fn shadow(mut self, blur_radius: f32, offset_y: f32) -> Self {
612 self.shadow = Some(ShadowSpec {
613 blur_radius: blur_radius.max(0.0),
614 offset_y,
615 color: Color(0, 0, 0, 64),
616 });
617 self
618 }
619 pub fn shadow_with_color(mut self, blur_radius: f32, offset_y: f32, color: Color) -> Self {
621 self.shadow = Some(ShadowSpec {
622 blur_radius: blur_radius.max(0.0),
623 offset_y,
624 color,
625 });
626 self
627 }
628 pub fn elevation(mut self, level: f32) -> Self {
632 if level <= 0.0 {
633 self.shadow = None;
634 return self;
635 }
636 self.shadow = Some(ShadowSpec {
637 blur_radius: level * 2.0,
638 offset_y: level * 0.5,
639 color: Color(0, 0, 0, (level * 8.0).clamp(8.0, 80.0) as u8),
640 });
641 self
642 }
643 pub fn transform(mut self, t: Transform) -> Self {
644 self.transform = Some(t);
645 self
646 }
647 pub fn grid(mut self, columns: usize, row_gap: f32, column_gap: f32) -> Self {
648 self.grid = Some(GridConfig {
649 columns,
650 row_gap,
651 column_gap,
652 });
653 self
654 }
655 pub fn grid_span(mut self, col_span: u16, row_span: u16) -> Self {
656 self.grid_col_span = Some(col_span);
657 self.grid_row_span = Some(row_span);
658 self
659 }
660 pub fn absolute(mut self) -> Self {
661 self.position_type = Some(PositionType::Absolute);
662 self
663 }
664 pub fn offset(
665 mut self,
666 left: Option<f32>,
667 top: Option<f32>,
668 right: Option<f32>,
669 bottom: Option<f32>,
670 ) -> Self {
671 self.offset_left = left;
672 self.offset_top = top;
673 self.offset_right = right;
674 self.offset_bottom = bottom;
675 self
676 }
677 pub fn offset_left(mut self, v: f32) -> Self {
678 self.offset_left = Some(v);
679 self
680 }
681 pub fn offset_right(mut self, v: f32) -> Self {
682 self.offset_right = Some(v);
683 self
684 }
685 pub fn offset_top(mut self, v: f32) -> Self {
686 self.offset_top = Some(v);
687 self
688 }
689 pub fn offset_bottom(mut self, v: f32) -> Self {
690 self.offset_bottom = Some(v);
691 self
692 }
693
694 pub fn margin(mut self, v: f32) -> Self {
695 self.margin_left = Some(v);
696 self.margin_right = Some(v);
697 self.margin_top = Some(v);
698 self.margin_bottom = Some(v);
699 self
700 }
701
702 pub fn margin_horizontal(mut self, v: f32) -> Self {
703 self.margin_left = Some(v);
704 self.margin_right = Some(v);
705 self
706 }
707
708 pub fn margin_vertical(mut self, v: f32) -> Self {
709 self.margin_top = Some(v);
710 self.margin_bottom = Some(v);
711 self
712 }
713 pub fn aspect_ratio(mut self, ratio: f32) -> Self {
714 self.aspect_ratio = Some(ratio);
715 self
716 }
717 pub fn painter(mut self, f: impl Fn(&mut crate::Scene, crate::Rect) + 'static) -> Self {
718 self.painter = Some(Rc::new(f));
719 self
720 }
721 pub fn scale(self, s: f32) -> Self {
722 self.scale2(s, s)
723 }
724 pub fn scale2(mut self, sx: f32, sy: f32) -> Self {
725 let mut t = self.transform.unwrap_or_else(Transform::identity);
726 t.scale_x *= sx;
727 t.scale_y *= sy;
728 self.transform = Some(t);
729 self
730 }
731 pub fn translate(mut self, x: f32, y: f32) -> Self {
732 let t = self.transform.unwrap_or_else(Transform::identity);
733 self.transform = Some(t.combine(&Transform::translate(x, y)));
734 self
735 }
736 pub fn translate_vec2(self, v: Vec2) -> Self {
737 self.translate(v.x, v.y)
738 }
739 pub fn rotate(mut self, radians: f32) -> Self {
740 let mut t = self.transform.unwrap_or_else(Transform::identity);
741 t.rotate += radians;
742 self.transform = Some(t);
743 self
744 }
745 pub fn weight(mut self, w: f32) -> Self {
746 let w = w.max(0.0);
747 self.flex_grow = Some(w);
748 self.flex_shrink = Some(1.0);
749 self.flex_basis = Some(0.0);
751 self
752 }
753 pub fn repaint_boundary(mut self) -> Self {
757 self.repaint_boundary = true;
758 self
759 }
760 pub fn on_action(mut self, f: impl Fn(crate::shortcuts::Action) -> bool + 'static) -> Self {
761 self.on_action = Some(Rc::new(f));
762 self
763 }
764
765 pub fn on_drag_start(
767 mut self,
768 f: impl Fn(crate::dnd::DragStart) -> Option<crate::dnd::DragPayload> + 'static,
769 ) -> Self {
770 self.on_drag_start = Some(Rc::new(f));
771 self
772 }
773
774 pub fn on_drag_end(mut self, f: impl Fn(crate::dnd::DragEnd) + 'static) -> Self {
776 self.on_drag_end = Some(Rc::new(f));
777 self
778 }
779
780 pub fn on_drag_enter(mut self, f: impl Fn(crate::dnd::DragOver) + 'static) -> Self {
782 self.on_drag_enter = Some(Rc::new(f));
783 self
784 }
785
786 pub fn on_drag_over(mut self, f: impl Fn(crate::dnd::DragOver) + 'static) -> Self {
788 self.on_drag_over = Some(Rc::new(f));
789 self
790 }
791
792 pub fn on_drag_leave(mut self, f: impl Fn(crate::dnd::DragOver) + 'static) -> Self {
794 self.on_drag_leave = Some(Rc::new(f));
795 self
796 }
797
798 pub fn on_drop(mut self, f: impl Fn(crate::dnd::DropEvent) -> bool + 'static) -> Self {
801 self.on_drop = Some(Rc::new(f));
802 self
803 }
804
805 pub fn cursor(mut self, c: crate::CursorIcon) -> Self {
807 self.cursor = Some(c);
808 self
809 }
810
811 pub fn animate_content_size(mut self, spec: AnimationSpec) -> Self {
815 self.animate_content_size = Some(spec);
816 self
817 }
818
819 pub fn focus_requester(mut self, fr: crate::runtime::FocusRequester) -> Self {
822 self.focus_requester = Some(fr);
823 self
824 }
825
826 pub fn on_focus_changed(mut self, f: impl Fn(bool) + 'static) -> Self {
829 self.on_focus_changed = Some(Rc::new(f));
830 self
831 }
832}