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