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