1use std::cell::{Cell, RefCell};
2use std::collections::HashSet;
3use std::rc::Rc;
4use std::sync::atomic::{AtomicU64, Ordering};
5
6use taffy::{
7 AlignContent, AlignItems, AlignSelf, Contain, FlexDirection, FlexWrap, JustifyContent,
8};
9
10use crate::animation::AnimationSpec;
11use crate::gesture::DragOrientation;
12use crate::indication::IndicationNodeFactory;
13use crate::units::{Dp, DpSize};
14use crate::{Brush, Color, PointerEvent, Transform, Vec2};
15
16#[derive(Clone, Copy, Debug)]
20pub struct StateColors {
21 pub default: Color,
22 pub hovered: Color,
23 pub focused: Color,
25 pub pressed: Color,
26 pub disabled: Color,
27 pub dragged: Color,
29}
30
31#[derive(Clone, Copy, Debug)]
34pub struct StateElevation {
35 pub default: Dp,
36 pub hovered: Dp,
37 pub focused: Dp,
39 pub pressed: Dp,
40 pub disabled: Dp,
41 pub dragged: Dp,
43}
44
45impl StateColors {
46 pub const fn transparent() -> Self {
47 Self {
48 default: Color::TRANSPARENT,
49 hovered: Color::TRANSPARENT,
50 focused: Color::TRANSPARENT,
51 pressed: Color::TRANSPARENT,
52 disabled: Color::TRANSPARENT,
53 dragged: Color::TRANSPARENT,
54 }
55 }
56}
57
58impl StateElevation {
59 pub const fn zero() -> Self {
60 Self {
61 default: Dp(0.0),
62 hovered: Dp(0.0),
63 focused: Dp(0.0),
64 pressed: Dp(0.0),
65 disabled: Dp(0.0),
66 dragged: Dp(0.0),
67 }
68 }
69}
70
71macro_rules! merge_opts {
72 ($dst:ident, $src:ident; $($f:ident),+ $(,)?) => {
73 $( $dst.$f = $src.$f.or($dst.$f); )+
74 };
75}
76macro_rules! merge_flags {
77 ($dst:ident, $src:ident; $($f:ident),+ $(,)?) => {
78 $( $dst.$f |= $src.$f; )+
79 };
80}
81
82macro_rules! impl_option_fields {
83 ($ty:ty, $fn:ident) => {
84 impl $ty {
85 $fn!(replace);
86 }
87 };
88 ($ty:ident) => {
89 impl $ty {
90 pub fn then(mut self, other: Self) -> Self {
93 merge_opts!(self, other;
94 key, size, width, height, required_size,
95 padding, padding_values,
96 min_width, min_height, max_width, max_height,
97 required_min_width, required_max_width,
98 required_min_height, required_max_height,
99 default_min_width, default_min_height,
100 fill_max, fill_max_w, fill_max_h,
101 background, state_colors, state_elevation, border,
102 flex_grow, flex_shrink, flex_basis, flex_wrap, flex_dir,
103 flex_line_count,
104 gap, row_gap, column_gap,
105 align_self, justify_content, align_items_container, align_content,
106 baseline_align,
107 clip_rounded, clip_rect, overflow, render_z_index,
108 on_scroll, on_scroll_with_source,
109 scrollbar_style,
110 nested_scroll_connection,
111 scroll,
112 on_pointer_down, on_pointer_move, on_pointer_up,
113 on_pointer_cancel,
114 on_pointer_enter, on_pointer_leave,
115 on_click, on_double_click, on_long_click,
116 on_globally_positioned, on_size_changed,
117 on_key_event, on_preview_key_event, on_ime,
118 semantics, alpha, transform,
119 grid, grid_col_span, grid_row_span,
120 position_type,
121 offset_left, offset_right, offset_top, offset_bottom,
122 margin_left, margin_right, margin_top, margin_bottom,
123 aspect_ratio, intrinsic_width, intrinsic_height,
124 fit_content_width, fit_content_height,
125 contain,
126 painter,
127 paint_callback,
128 layout,
129 blur, graphics_layer, shadow, shadow_shape,
130 on_drag_start, on_drag_end, on_drag_enter, on_drag_over, on_drag_leave, on_drop,
131 drag_preview,
132 on_action, cursor, animate_content_size, focus_requester, on_focus_changed,
133 interaction_source, text_input, text_selection,
134 );
135 merge_flags!(self, other;
136 hit_passthrough, input_blocker, repaint_boundary, click, disabled,
137 propagate_min, focus_group,
138 );
139
140 self.flex_basis_content |= other.flex_basis_content;
142
143 if let Some(f) = other.focusable {
144 self.focusable = Some(f);
145 }
146 if other.indication.is_some() {
147 self.indication = other.indication;
148 }
149 self.z_index = other.z_index;
151 self
152 }
153 }
154 };
155}
156
157#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
158pub enum ClipOp {
159 #[default]
161 Intersect,
162 Difference,
164}
165
166#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
172pub enum Overflow {
173 #[default]
174 Clip,
175 Visible,
176}
177
178#[derive(Clone, Copy, Debug)]
181pub struct ClipRect {
182 pub left: Dp,
183 pub top: Dp,
184 pub right: Dp,
185 pub bottom: Dp,
186 pub op: ClipOp,
187}
188
189#[derive(Clone, Debug)]
190pub struct Border {
191 pub width: Dp,
192 pub brush: Brush,
194 pub radius: [Dp; 4],
195}
196
197#[derive(Clone, Copy, Debug, Default)]
198pub struct PaddingValues {
199 pub left: Dp,
200 pub right: Dp,
201 pub top: Dp,
202 pub bottom: Dp,
203}
204
205#[derive(Clone, Debug)]
206pub struct GridConfig {
207 pub columns: usize,
208 pub row_gap: Dp,
209 pub column_gap: Dp,
210}
211
212#[derive(Clone, Copy, Debug, PartialEq)]
215pub enum BlurredEdgeTreatment {
216 Rectangle,
219 Unbounded,
222}
223
224#[derive(Clone, Copy, Debug)]
226pub struct BlurStyle {
227 pub radius_x: Dp,
229 pub radius_y: Dp,
231 pub edge_treatment: BlurredEdgeTreatment,
233}
234
235#[derive(Clone, Copy, Debug)]
241pub struct LayoutConstraints {
242 pub min_width: Dp,
243 pub max_width: Dp,
244 pub min_height: Dp,
245 pub max_height: Dp,
246}
247
248#[derive(Clone, Copy, Debug)]
255pub struct ShadowSpec {
256 pub blur_radius: Dp,
257 pub offset_y: Dp,
258 pub color: Color,
259}
260
261#[derive(Clone, Copy, Debug)]
262#[non_exhaustive]
263pub enum PositionType {
264 Relative,
265 Absolute,
266}
267
268#[derive(Clone)]
270pub struct TextInputConfig {
271 pub hint: String,
272 pub multiline: bool,
273 pub on_change: Option<Rc<dyn Fn(String)>>,
274 pub on_submit: Option<Rc<dyn Fn(String)>>,
275 pub focus_tracker: Option<Rc<Cell<bool>>>,
276 pub value: String,
277 pub visual_transformation: Option<Rc<dyn crate::text::VisualTransformation>>,
278 pub keyboard_type: crate::text::KeyboardType,
279 pub capitalization: crate::text::KeyboardCapitalization,
280 pub ime_action: crate::text::ImeAction,
281 pub auto_correct_enabled: Option<bool>,
284 pub enabled: bool,
286 pub read_only: bool,
288 pub sensitive: bool,
289 pub max_lines: Option<usize>,
291 pub min_lines: usize,
293 pub cursor_color: Option<Color>,
295 pub on_text_layout: Option<Rc<dyn Fn(&crate::text::TextLayoutResult)>>,
298 pub text_style: Option<crate::text::TextStyle>,
301 pub keyboard_actions: Option<crate::text::KeyboardActions>,
304 pub interaction_source: Option<InteractionSource>,
306 pub line_limits: Option<crate::text::TextFieldLineLimits>,
308}
309
310impl Default for TextInputConfig {
311 fn default() -> Self {
312 Self {
313 hint: String::new(),
314 multiline: false,
315 on_change: None,
316 on_submit: None,
317 focus_tracker: None,
318 value: String::new(),
319 visual_transformation: None,
320 keyboard_type: crate::text::KeyboardType::default(),
321 capitalization: crate::text::KeyboardCapitalization::default(),
322 ime_action: crate::text::ImeAction::default(),
323 auto_correct_enabled: None,
324 enabled: true,
325 read_only: false,
326 sensitive: false,
327 max_lines: None,
328 min_lines: 1,
329 cursor_color: None,
330 on_text_layout: None,
331 text_style: None,
332 keyboard_actions: None,
333 interaction_source: None,
334 line_limits: None,
335 }
336 }
337}
338
339impl std::fmt::Debug for TextInputConfig {
340 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
341 let mut s = f.debug_struct("TextInputConfig");
342 s.field("hint", &self.hint);
343 s.field("multiline", &self.multiline);
344 if self.on_change.is_some() {
345 s.field("on_change", &"…");
346 }
347 if self.on_submit.is_some() {
348 s.field("on_submit", &"…");
349 }
350 if self.focus_tracker.is_some() {
351 s.field("focus_tracker", &"…");
352 }
353 s.field("value", &self.value);
354 if self.visual_transformation.is_some() {
355 s.field("visual_transformation", &"…");
356 }
357 s.field("keyboard_type", &self.keyboard_type);
358 s.field("capitalization", &self.capitalization);
359 s.field("ime_action", &self.ime_action);
360 s.field("auto_correct_enabled", &self.auto_correct_enabled);
361 s.field("enabled", &self.enabled);
362 s.field("read_only", &self.read_only);
363 s.field("sensitive", &self.sensitive);
364 s.field("max_lines", &self.max_lines);
365 s.field("min_lines", &self.min_lines);
366 s.field("cursor_color", &self.cursor_color);
367 if self.on_text_layout.is_some() {
368 s.field("on_text_layout", &"…");
369 }
370 s.finish()
371 }
372}
373
374#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
377pub enum IntrinsicSize {
378 Min,
379 Max,
380}
381
382#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
390pub enum BaselineAlign {
391 FirstBaseline,
392 LastBaseline,
393}
394
395static PRESS_COUNTER: AtomicU64 = AtomicU64::new(1);
396
397thread_local! {
398 static INTERACTION_EPOCH: Cell<u64> = const { Cell::new(0) };
399}
400
401pub type PressId = u64;
403
404#[derive(Clone, Copy, Debug, PartialEq)]
412pub enum Interaction {
413 Press(PressId, Vec2),
416 Release(PressId),
418 Cancel(PressId),
421 HoverEnter,
422 HoverLeave,
423 Focus,
424 Unfocus,
425 DragStart,
426 DragStop,
427 DragCancel,
428}
429
430impl Interaction {
431 #[inline]
433 pub fn new_press(position: Vec2) -> Self {
434 Interaction::Press(PRESS_COUNTER.fetch_add(1, Ordering::Relaxed), position)
435 }
436}
437
438#[derive(Clone)]
445pub struct InteractionSource {
446 pub(crate) state: Rc<RefCell<InteractionState>>,
447}
448
449impl InteractionSource {
450 pub fn collect_is_pressed(&self) -> bool {
451 !self.state.borrow().active_presses.is_empty()
452 }
453 pub fn collect_is_hovered(&self) -> bool {
454 self.state.borrow().hovered
455 }
456 pub fn collect_is_focused(&self) -> bool {
457 self.state.borrow().focused
458 }
459
460 pub fn collect_is_focus_visible(&self) -> bool {
462 crate::input::is_focus_visible(self.collect_is_focused())
463 }
464
465 pub fn collect_is_dragged(&self) -> bool {
466 self.state.borrow().dragged > 0
467 }
468 pub fn collect_last_press_position(&self) -> Option<Vec2> {
469 self.state.borrow().last_press_position
470 }
471 pub fn collect_last_press_id(&self) -> Option<PressId> {
472 self.state.borrow().last_press_id
473 }
474 pub fn stable_id(&self) -> *const () {
476 Rc::as_ptr(&self.state) as *const ()
477 }
478 pub fn to_mutable(&self) -> MutableInteractionSource {
482 MutableInteractionSource {
483 state: self.state.clone(),
484 }
485 }
486
487 pub fn reset(&self) {
489 self.to_mutable().reset();
490 }
491
492 pub fn reset_hover(&self) {
494 self.to_mutable().reset_hover();
495 }
496}
497
498pub fn interaction_epoch() -> u64 {
499 INTERACTION_EPOCH.with(Cell::get)
500}
501
502fn bump_interaction_epoch() {
503 INTERACTION_EPOCH.with(|epoch| epoch.set(epoch.get().wrapping_add(1)));
504}
505
506#[derive(Clone)]
516pub struct MutableInteractionSource {
517 pub(crate) state: Rc<RefCell<InteractionState>>,
518}
519
520impl std::fmt::Debug for MutableInteractionSource {
521 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
522 f.debug_struct("MutableInteractionSource")
523 .finish_non_exhaustive()
524 }
525}
526
527impl MutableInteractionSource {
528 pub fn new() -> Self {
529 Self {
530 state: Rc::new(RefCell::new(InteractionState::default())),
531 }
532 }
533
534 pub fn emit(&self, interaction: Interaction) {
536 let changed = {
537 let mut s = self.state.borrow_mut();
538 let changed = match interaction {
539 Interaction::Press(id, pos) => {
540 let inserted = s.active_presses.insert(id);
541 s.last_press_id = Some(id);
542 s.last_press_position = Some(pos);
543 inserted
544 }
545 Interaction::Release(id) | Interaction::Cancel(id) => {
546 if s.active_presses.remove(&id) {
547 true
548 } else if id == 0 {
549 if let Some(any) = s.active_presses.iter().next().copied() {
550 s.active_presses.remove(&any);
551 true
552 } else {
553 false
554 }
555 } else {
556 false
557 }
558 }
559 Interaction::HoverEnter => {
560 let changed = !s.hovered;
561 s.hovered = true;
562 changed
563 }
564 Interaction::HoverLeave => {
565 let changed = s.hovered;
569 s.hovered = false;
570 changed
571 }
572 Interaction::Focus => {
573 let changed = !s.focused;
574 s.focused = true;
575 changed
576 }
577 Interaction::Unfocus => {
578 let changed = s.focused;
579 s.focused = false;
580 changed
581 }
582 Interaction::DragStart => {
583 let changed = s.dragged == 0;
584 s.dragged = s.dragged.saturating_add(1);
585 changed
586 }
587 Interaction::DragStop | Interaction::DragCancel => {
588 let was = s.dragged;
589 s.dragged = s.dragged.saturating_sub(1);
590 was != s.dragged
591 }
592 };
593 if changed {
594 bump_interaction_epoch();
595 }
596 changed
597 };
598 if changed {
599 crate::frame_clock::request_frame();
601 }
602 }
603
604 pub fn source(&self) -> InteractionSource {
606 InteractionSource {
607 state: self.state.clone(),
608 }
609 }
610
611 pub fn reset(&self) {
613 let mut s = self.state.borrow_mut();
614 *s = InteractionState::default();
615 drop(s);
616 bump_interaction_epoch();
617 crate::frame_clock::request_frame();
618 }
619
620 pub fn reset_hover(&self) {
622 let mut s = self.state.borrow_mut();
623 if s.hovered {
624 s.hovered = false;
625 drop(s);
626 bump_interaction_epoch();
627 crate::frame_clock::request_frame();
628 }
629 }
630}
631
632impl Default for MutableInteractionSource {
633 fn default() -> Self {
634 Self::new()
635 }
636}
637
638#[derive(Clone, Default)]
639pub(crate) struct InteractionState {
640 active_presses: HashSet<PressId>,
642 hovered: bool,
643 focused: bool,
644 dragged: u32,
645 pub(crate) last_press_position: Option<Vec2>,
647 pub(crate) last_press_id: Option<PressId>,
649}
650
651#[derive(Clone, Default)]
652pub struct Modifier {
653 pub key: Option<u64>,
659
660 pub size: Option<DpSize>,
661 pub width: Option<Dp>,
662 pub height: Option<Dp>,
663 pub required_size: Option<DpSize>,
664 pub fill_max: Option<f32>,
665 pub fill_max_w: Option<f32>,
666 pub fill_max_h: Option<f32>,
667 pub padding: Option<Dp>,
668 pub padding_values: Option<PaddingValues>,
669 pub min_width: Option<Dp>,
670 pub min_height: Option<Dp>,
671 pub max_width: Option<Dp>,
672 pub max_height: Option<Dp>,
673 pub required_min_width: Option<Dp>,
675 pub required_max_width: Option<Dp>,
677 pub required_min_height: Option<Dp>,
679 pub required_max_height: Option<Dp>,
681 pub default_min_width: Option<Dp>,
684 pub default_min_height: Option<Dp>,
685 pub background: Option<Brush>,
686 pub state_colors: Option<StateColors>,
687 pub state_elevation: Option<StateElevation>,
688
689 pub border: Option<Border>,
690 pub flex_grow: Option<f32>,
691 pub flex_shrink: Option<f32>,
692 pub flex_basis: Option<Dp>,
693 pub flex_basis_content: bool,
696 pub flex_wrap: Option<FlexWrap>,
697 pub flex_line_count: Option<u16>,
700 pub flex_dir: Option<FlexDirection>,
701 pub gap: Option<Dp>,
702 pub row_gap: Option<Dp>,
703 pub column_gap: Option<Dp>,
704 pub align_self: Option<AlignSelf>,
705 pub justify_content: Option<JustifyContent>,
706 pub align_items_container: Option<AlignItems>,
707 pub align_content: Option<AlignContent>,
708 pub clip_rounded: Option<[Dp; 4]>,
709 pub clip_rect: Option<ClipRect>,
712 pub overflow: Option<Overflow>,
717 pub z_index: f32,
719 pub render_z_index: Option<f32>,
721 pub hit_passthrough: bool,
723 pub input_blocker: bool,
725 pub repaint_boundary: bool,
726 pub click: bool,
727 pub disabled: bool,
729 pub focusable: Option<bool>,
733 pub propagate_min: bool,
735 pub focus_group: bool,
738 pub on_scroll: Option<Rc<dyn Fn(Vec2) -> Vec2>>,
739 pub on_scroll_with_source: Option<Rc<dyn Fn(Vec2, crate::input::ScrollSource) -> Vec2>>,
742 pub scroll: Option<crate::scroll::ScrollBinding>,
748 pub scrollbar_style: Option<crate::scroll::ScrollbarStyle>,
749 pub nested_scroll_connection: Option<crate::nested_scroll::NestedScrollConnection>,
758 pub on_pointer_down: Option<Rc<dyn Fn(PointerEvent)>>,
759 pub on_pointer_move: Option<Rc<dyn Fn(PointerEvent)>>,
760 pub on_pointer_up: Option<Rc<dyn Fn(PointerEvent)>>,
761 pub on_pointer_cancel: Option<Rc<dyn Fn(PointerEvent)>>,
762 pub on_pointer_enter: Option<Rc<dyn Fn(PointerEvent)>>,
763 pub on_pointer_leave: Option<Rc<dyn Fn(PointerEvent)>>,
764 pub on_click: Option<Rc<dyn Fn()>>,
766 pub on_double_click: Option<Rc<dyn Fn()>>,
768 pub on_long_click: Option<Rc<dyn Fn()>>,
770 pub on_globally_positioned: Option<Rc<dyn Fn(crate::Rect)>>,
773 pub on_size_changed: Option<Rc<dyn Fn(crate::Vec2)>>,
776 pub on_key_event: Option<Rc<dyn Fn(crate::input::KeyEvent) -> bool>>,
779 pub on_preview_key_event: Option<Rc<dyn Fn(crate::input::KeyEvent) -> bool>>,
782 pub on_ime: Option<Rc<dyn Fn(crate::input::ImeEvent) -> bool>>,
785 pub blur: Option<BlurStyle>,
790 pub layout: Option<Rc<dyn Fn(LayoutConstraints) -> (Dp, Dp)>>,
795 pub semantics: Option<crate::Semantics>,
796 pub alpha: Option<f32>,
797 pub graphics_layer: Option<f32>,
798 pub shadow: Option<ShadowSpec>,
799 pub shadow_shape: Option<[Dp; 4]>,
804 pub transform: Option<Transform>,
805 pub grid: Option<GridConfig>,
806 pub grid_col_span: Option<u16>,
807 pub grid_row_span: Option<u16>,
808 pub position_type: Option<PositionType>,
809 pub offset_left: Option<Dp>,
810 pub offset_right: Option<Dp>,
811 pub offset_top: Option<Dp>,
812 pub offset_bottom: Option<Dp>,
813
814 pub margin_left: Option<Dp>,
815 pub margin_right: Option<Dp>,
816 pub margin_top: Option<Dp>,
817 pub margin_bottom: Option<Dp>,
818 pub aspect_ratio: Option<f32>,
819 pub intrinsic_width: Option<IntrinsicSize>,
822 pub intrinsic_height: Option<IntrinsicSize>,
825 pub fit_content_width: Option<Dp>,
828 pub fit_content_height: Option<Dp>,
831 pub baseline_align: Option<BaselineAlign>,
834 pub contain: Option<Contain>,
839 pub painter: Option<Rc<dyn Fn(&mut crate::Scene, crate::Rect, f32)>>,
840 pub paint_callback: Option<crate::PaintCallbackPayload>,
841
842 pub on_drag_start: Option<Rc<dyn Fn(crate::dnd::DragStart) -> Option<crate::dnd::DragPayload>>>,
844 pub on_drag_end: Option<Rc<dyn Fn(crate::dnd::DragEnd)>>,
845 pub on_drag_enter: Option<Rc<dyn Fn(crate::dnd::DragOver)>>,
846 pub on_drag_over: Option<Rc<dyn Fn(crate::dnd::DragOver)>>,
847 pub on_drag_leave: Option<Rc<dyn Fn(crate::dnd::DragOver)>>,
848 pub on_drop: Option<Rc<dyn Fn(crate::dnd::DropEvent) -> bool>>,
849 pub drag_preview: Option<crate::dnd::DragPreview>,
851
852 pub on_action: Option<Rc<dyn Fn(crate::shortcuts::Action) -> bool>>,
853 pub on_set_progress: Option<Rc<dyn Fn(f32)>>,
854 pub on_expand: Option<Rc<dyn Fn()>>,
855 pub on_collapse: Option<Rc<dyn Fn()>>,
856 pub on_dismiss: Option<Rc<dyn Fn()>>,
857
858 pub cursor: Option<crate::CursorIcon>,
860
861 pub animate_content_size: Option<AnimationSpec>,
864
865 pub focus_requester: Option<crate::runtime::FocusRequester>,
869
870 pub on_focus_changed: Option<Rc<dyn Fn(bool)>>,
873
874 pub interaction_source: Option<InteractionSource>,
881
882 pub text_input: Option<TextInputConfig>,
884
885 pub text_selection: Option<crate::text::TextSelection>,
888
889 pub indication: Option<Rc<dyn IndicationNodeFactory>>,
891}
892
893impl std::fmt::Debug for Modifier {
894 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
895 let mut s = f.debug_struct("Modifier");
896
897 macro_rules! opt_val {
898 ($($name:ident),+ $(,)?) => {
899 $( if self.$name.is_some() { s.field(stringify!($name), &self.$name); } )+
900 };
901 }
902 if self.indication.is_some() {
903 s.field("indication", &"…");
904 }
905 s.field("scrollbar_style", &self.scrollbar_style);
906
907 opt_val!(
908 key,
909 size,
910 width,
911 height,
912 required_size,
913 padding,
914 padding_values,
915 min_width,
916 min_height,
917 max_width,
918 max_height,
919 required_min_width,
920 required_max_width,
921 required_min_height,
922 required_max_height,
923 default_min_width,
924 default_min_height,
925 fill_max,
926 fill_max_w,
927 fill_max_h,
928 background,
929 state_colors,
930 state_elevation,
931 border,
932 flex_grow,
933 flex_shrink,
934 flex_basis,
935 flex_wrap,
936 flex_dir,
937 gap,
938 row_gap,
939 column_gap,
940 align_self,
941 justify_content,
942 align_items_container,
943 align_content,
944 clip_rounded,
945 clip_rect,
946 render_z_index,
947 semantics,
948 alpha,
949 transform,
950 grid,
951 grid_col_span,
952 grid_row_span,
953 position_type,
954 offset_left,
955 offset_right,
956 offset_top,
957 offset_bottom,
958 margin_left,
959 margin_right,
960 margin_top,
961 margin_bottom,
962 aspect_ratio,
963 intrinsic_width,
964 intrinsic_height,
965 cursor,
966 animate_content_size,
967 blur,
968 );
969
970 macro_rules! opt_cb {
971 ($($name:ident),+ $(,)?) => {
972 $( if self.$name.is_some() { s.field(stringify!($name), &"…"); } )+
973 };
974 }
975 opt_cb!(
976 on_scroll,
977 scroll,
978 nested_scroll_connection,
979 on_pointer_down,
980 on_pointer_move,
981 on_pointer_up,
982 on_pointer_cancel,
983 on_pointer_enter,
984 on_pointer_leave,
985 on_click,
986 on_double_click,
987 on_long_click,
988 on_globally_positioned,
989 on_size_changed,
990 on_key_event,
991 on_preview_key_event,
992 on_ime,
993 painter,
994 paint_callback,
995 on_drag_start,
996 on_drag_end,
997 on_drag_enter,
998 on_drag_over,
999 on_drag_leave,
1000 on_drop,
1001 drag_preview,
1002 on_action,
1003 on_focus_changed,
1004 interaction_source,
1005 text_input,
1006 text_selection,
1007 layout,
1008 );
1009
1010 macro_rules! flag {
1011 ($($name:ident),+ $(,)?) => {
1012 $( if self.$name { s.field(stringify!($name), &true); } )+
1013 };
1014 }
1015 flag!(
1016 hit_passthrough,
1017 input_blocker,
1018 repaint_boundary,
1019 click,
1020 disabled,
1021 propagate_min,
1022 focus_group,
1023 );
1024
1025 if let Some(f) = self.focusable {
1026 s.field("focusable", &f);
1027 }
1028 if self.z_index != 0.0 {
1029 s.field("z_index", &self.z_index);
1030 }
1031
1032 s.finish()
1033 }
1034}
1035
1036impl_option_fields!(Modifier);
1037
1038#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
1042pub enum Alignment {
1043 TopStart,
1044 TopCenter,
1045 TopEnd,
1046 CenterStart,
1047 #[default]
1048 Center,
1049 CenterEnd,
1050 BottomStart,
1051 BottomCenter,
1052 BottomEnd,
1053}
1054
1055impl Alignment {
1056 pub fn to_flex(self) -> (AlignItems, JustifyContent) {
1058 use AlignItems as AI;
1059 use JustifyContent as JC;
1060 match self {
1061 Self::TopStart => (AI::START, JC::START),
1062 Self::TopCenter => (AI::START, JC::CENTER),
1063 Self::TopEnd => (AI::START, JC::END),
1064 Self::CenterStart => (AI::CENTER, JC::START),
1065 Self::Center => (AI::CENTER, JC::CENTER),
1066 Self::CenterEnd => (AI::CENTER, JC::END),
1067 Self::BottomStart => (AI::END, JC::START),
1068 Self::BottomCenter => (AI::END, JC::CENTER),
1069 Self::BottomEnd => (AI::END, JC::END),
1070 }
1071 }
1072
1073 pub fn to_flex_safe(self) -> (AlignItems, JustifyContent) {
1078 use AlignItems as AI;
1079 use JustifyContent as JC;
1080 match self {
1081 Self::TopStart => (AI::SAFE_START, JC::SAFE_START),
1082 Self::TopCenter => (AI::SAFE_START, JC::SAFE_CENTER),
1083 Self::TopEnd => (AI::SAFE_START, JC::SAFE_END),
1084 Self::CenterStart => (AI::SAFE_CENTER, JC::SAFE_START),
1085 Self::Center => (AI::SAFE_CENTER, JC::SAFE_CENTER),
1086 Self::CenterEnd => (AI::SAFE_CENTER, JC::SAFE_END),
1087 Self::BottomStart => (AI::SAFE_END, JC::SAFE_START),
1088 Self::BottomCenter => (AI::SAFE_END, JC::SAFE_CENTER),
1089 Self::BottomEnd => (AI::SAFE_END, JC::SAFE_END),
1090 }
1091 }
1092}
1093
1094impl Modifier {
1095 pub fn new() -> Self {
1096 Self::default()
1097 }
1098
1099 pub fn key(mut self, key: u64) -> Self {
1102 self.key = Some(key);
1103 self
1104 }
1105
1106 pub fn size(mut self, w: Dp, h: Dp) -> Self {
1107 self.size = Some(DpSize::new(w, h));
1108 self
1109 }
1110 pub fn width(mut self, w: Dp) -> Self {
1111 self.width = Some(w);
1112 self
1113 }
1114 pub fn height(mut self, h: Dp) -> Self {
1115 self.height = Some(h);
1116 self
1117 }
1118 pub fn required_size(mut self, w: Dp, h: Dp) -> Self {
1123 self.required_size = Some(DpSize::new(w, h));
1124 self
1125 }
1126 pub fn required_width_in(mut self, min: Dp, max: Dp) -> Self {
1127 self.required_min_width = Some(Dp(min.0.max(0.0)));
1128 self.required_max_width = Some(Dp(max.0.max(0.0)));
1129 self
1130 }
1131 pub fn required_height_in(mut self, min: Dp, max: Dp) -> Self {
1132 self.required_min_height = Some(Dp(min.0.max(0.0)));
1133 self.required_max_height = Some(Dp(max.0.max(0.0)));
1134 self
1135 }
1136 pub fn required_min_width(mut self, w: Dp) -> Self {
1137 self.required_min_width = Some(Dp(w.0.max(0.0)));
1138 self
1139 }
1140 pub fn required_max_width(mut self, w: Dp) -> Self {
1141 self.required_max_width = Some(Dp(w.0.max(0.0)));
1142 self
1143 }
1144 pub fn required_min_height(mut self, h: Dp) -> Self {
1145 self.required_min_height = Some(Dp(h.0.max(0.0)));
1146 self
1147 }
1148 pub fn required_max_height(mut self, h: Dp) -> Self {
1149 self.required_max_height = Some(Dp(h.0.max(0.0)));
1150 self
1151 }
1152 pub fn default_min_size(mut self, w: Dp, h: Dp) -> Self {
1154 self.default_min_width = Some(Dp(w.0.max(0.0)));
1155 self.default_min_height = Some(Dp(h.0.max(0.0)));
1156 self
1157 }
1158 pub fn fill_max_size(mut self) -> Self {
1161 self.fill_max = Some(1.0);
1162 self
1163 }
1164 pub fn fill_max_size_frac(mut self, fraction: f32) -> Self {
1165 self.fill_max = Some(fraction.clamp(0.0, 1.0));
1166 self
1167 }
1168 pub fn fill_max_width(mut self) -> Self {
1170 self.fill_max_w = Some(1.0);
1171 self
1172 }
1173 pub fn fill_max_width_frac(mut self, fraction: f32) -> Self {
1174 self.fill_max_w = Some(fraction.clamp(0.0, 1.0));
1175 self
1176 }
1177 pub fn fill_max_height(mut self) -> Self {
1179 self.fill_max_h = Some(1.0);
1180 self
1181 }
1182 pub fn fill_max_height_frac(mut self, fraction: f32) -> Self {
1183 self.fill_max_h = Some(fraction.clamp(0.0, 1.0));
1184 self
1185 }
1186 pub fn padding(mut self, v: Dp) -> Self {
1187 self.padding = Some(v);
1188 self
1189 }
1190 pub fn padding_values(mut self, padding: PaddingValues) -> Self {
1191 self.padding_values = Some(padding);
1192 self
1193 }
1194 pub fn ime_padding(mut self) -> Self {
1198 use crate::units::Px;
1199 let insets = crate::locals::window_insets();
1200 let mut p = self.padding_values.unwrap_or_default();
1201 p.bottom = p.bottom + Px(insets.ime_bottom).to_dp();
1202 self.padding_values = Some(p);
1203 self
1204 }
1205 pub fn system_bars_padding(mut self) -> Self {
1207 use crate::units::Px;
1208 let insets = crate::locals::window_insets();
1209 let mut p = self.padding_values.unwrap_or_default();
1210 p.top = p.top + Px(insets.top).to_dp();
1211 p.bottom = p.bottom + Px(insets.bottom).to_dp();
1212 self.padding_values = Some(p);
1213 self
1214 }
1215 pub fn status_bars_padding(mut self) -> Self {
1217 use crate::units::Px;
1218 let insets = crate::locals::window_insets();
1219 let mut p = self.padding_values.unwrap_or_default();
1220 p.top = p.top + Px(insets.top).to_dp();
1221 self.padding_values = Some(p);
1222 self
1223 }
1224 pub fn navigation_bars_padding(mut self) -> Self {
1226 let insets = crate::locals::window_insets();
1227 use crate::units::Px;
1228 let mut p = self.padding_values.unwrap_or_default();
1229 p.bottom = p.bottom + Px(insets.bottom).to_dp();
1230 self.padding_values = Some(p);
1231 self
1232 }
1233 pub fn min_size(mut self, w: Dp, h: Dp) -> Self {
1234 self.min_width = Some(w);
1235 self.min_height = Some(h);
1236 self
1237 }
1238 pub fn max_size(mut self, w: Dp, h: Dp) -> Self {
1239 self.max_width = Some(w);
1240 self.max_height = Some(h);
1241 self
1242 }
1243 pub fn min_width(mut self, w: Dp) -> Self {
1244 self.min_width = Some(w);
1245 self
1246 }
1247 pub fn min_height(mut self, h: Dp) -> Self {
1248 self.min_height = Some(h);
1249 self
1250 }
1251 pub fn max_width(mut self, w: Dp) -> Self {
1252 self.max_width = Some(w);
1253 self
1254 }
1255 pub fn max_height(mut self, h: Dp) -> Self {
1256 self.max_height = Some(h);
1257 self
1258 }
1259 pub fn background(mut self, color: Color) -> Self {
1261 self.background = Some(Brush::Solid(color));
1262 self
1263 }
1264 pub fn background_brush(mut self, brush: Brush) -> Self {
1266 self.background = Some(brush);
1267 self
1268 }
1269 pub fn border(mut self, width: Dp, color: Color, radius: Dp) -> Self {
1270 self.border = Some(Border {
1271 width,
1272 brush: Brush::Solid(color),
1273 radius: [radius; 4],
1274 });
1275 self
1276 }
1277 pub fn border_brush(mut self, width: Dp, brush: Brush, radius: Dp) -> Self {
1280 self.border = Some(Border {
1281 width,
1282 brush,
1283 radius: [radius; 4],
1284 });
1285 self
1286 }
1287 pub fn border_radii(mut self, width: Dp, color: Color, radii: [Dp; 4]) -> Self {
1288 self.border = Some(Border {
1289 width,
1290 brush: Brush::Solid(color),
1291 radius: radii,
1292 });
1293 self
1294 }
1295 pub fn border_brush_radii(mut self, width: Dp, brush: Brush, radii: [Dp; 4]) -> Self {
1297 self.border = Some(Border {
1298 width,
1299 brush,
1300 radius: radii,
1301 });
1302 self
1303 }
1304 pub fn flex_grow(mut self, v: f32) -> Self {
1305 self.flex_grow = Some(v);
1306 self
1307 }
1308 pub fn flex_shrink(mut self, v: f32) -> Self {
1309 self.flex_shrink = Some(v);
1310 self
1311 }
1312 pub fn flex_basis(mut self, v: Dp) -> Self {
1313 self.flex_basis = Some(v);
1314 self
1315 }
1316 pub fn flex_wrap(mut self, w: FlexWrap) -> Self {
1317 self.flex_wrap = Some(w);
1318 self
1319 }
1320 pub fn flex_basis_content(mut self) -> Self {
1323 self.flex_basis_content = true;
1324 self
1325 }
1326 pub fn flex_dir(mut self, d: FlexDirection) -> Self {
1327 self.flex_dir = Some(d);
1328 self
1329 }
1330 pub fn gap(mut self, v: Dp) -> Self {
1331 let v = Dp(v.0.max(0.0));
1332 self.gap = Some(v);
1333 self.row_gap = Some(v);
1334 self.column_gap = Some(v);
1335 self
1336 }
1337 pub fn row_gap(mut self, v: Dp) -> Self {
1338 self.row_gap = Some(Dp(v.0.max(0.0)));
1339 self
1340 }
1341 pub fn column_gap(mut self, v: Dp) -> Self {
1342 self.column_gap = Some(Dp(v.0.max(0.0)));
1343 self
1344 }
1345 pub fn align_self(mut self, a: AlignSelf) -> Self {
1346 self.align_self = Some(a);
1347 self
1348 }
1349 pub fn align_self_center(mut self) -> Self {
1350 self.align_self = Some(AlignSelf::CENTER);
1351 self
1352 }
1353 pub fn justify_content(mut self, j: JustifyContent) -> Self {
1354 self.justify_content = Some(j);
1355 self
1356 }
1357 pub fn align_items(mut self, a: AlignItems) -> Self {
1358 self.align_items_container = Some(a);
1359 self
1360 }
1361 pub fn content_alignment(self, alignment: Alignment) -> Self {
1364 let (ai, jc) = alignment.to_flex();
1365 self.align_items(ai).justify_content(jc)
1366 }
1367 pub fn content_alignment_safe(self, alignment: Alignment) -> Self {
1372 let (ai, jc) = alignment.to_flex_safe();
1373 self.align_items(ai).justify_content(jc)
1374 }
1375 pub fn align_content(mut self, a: AlignContent) -> Self {
1376 self.align_content = Some(a);
1377 self
1378 }
1379 pub fn clip_rounded(mut self, radius: Dp) -> Self {
1380 self.clip_rounded = Some([radius; 4]);
1381 self
1382 }
1383 pub fn clip_rounded_radii(mut self, radii: [Dp; 4]) -> Self {
1384 self.clip_rounded = Some(radii);
1385 self
1386 }
1387 pub fn clip_rect(mut self, left: Dp, top: Dp, right: Dp, bottom: Dp, op: ClipOp) -> Self {
1390 self.clip_rect = Some(ClipRect {
1391 left,
1392 top,
1393 right,
1394 bottom,
1395 op,
1396 });
1397 self
1398 }
1399 pub fn overflow(mut self, overflow: Overflow) -> Self {
1400 self.overflow = Some(overflow);
1401 self
1402 }
1403 pub fn z_index(mut self, z: f32) -> Self {
1404 self.z_index = z;
1405 self
1406 }
1407
1408 pub fn render_z_index(mut self, z: f32) -> Self {
1411 self.render_z_index = Some(z);
1412 self
1413 }
1414
1415 pub fn input_blocker(mut self) -> Self {
1417 self.input_blocker = true;
1418 self
1419 }
1420
1421 pub fn hit_passthrough(mut self) -> Self {
1422 self.hit_passthrough = true;
1423 self
1424 }
1425 pub fn clickable(mut self) -> Self {
1426 self.click = true;
1427 if self.indication.is_none() {
1428 self.indication = crate::locals::local_indication();
1429 }
1430 self
1431 }
1432 pub fn clickable_with_source(mut self, source: &MutableInteractionSource) -> Self {
1435 self.click = true;
1436 self.interaction_source = Some(source.source());
1437 if self.indication.is_none() {
1438 self.indication = crate::locals::local_indication();
1439 }
1440 self
1441 }
1442 pub fn state_colors(mut self, colors: StateColors) -> Self {
1445 self.state_colors = Some(colors);
1446 self
1447 }
1448 pub fn state_elevation(mut self, elev: StateElevation) -> Self {
1450 self.state_elevation = Some(elev);
1451 self
1452 }
1453 pub fn disabled(mut self) -> Self {
1455 self.disabled = true;
1456 self
1457 }
1458 pub fn enabled(mut self, enabled: bool) -> Self {
1460 self.disabled = !enabled;
1461 self
1462 }
1463 pub fn focusable(mut self, focusable: bool) -> Self {
1468 self.focusable = Some(focusable);
1469 self
1470 }
1471 pub fn focus_group(mut self) -> Self {
1474 self.focus_group = true;
1475 self
1476 }
1477 pub fn interaction_source(mut self, source: &MutableInteractionSource) -> Self {
1488 self.interaction_source = Some(source.source());
1489 self
1490 }
1491 pub fn hoverable(
1494 mut self,
1495 on_enter: impl Fn() + 'static,
1496 on_leave: impl Fn() + 'static,
1497 ) -> Self {
1498 self.on_pointer_enter = Some(Rc::new(move |_| on_enter()));
1499 self.on_pointer_leave = Some(Rc::new(move |_| on_leave()));
1500 self
1501 }
1502 pub fn hoverable_enabled(
1503 self,
1504 enabled: bool,
1505 on_enter: impl Fn() + 'static,
1506 on_leave: impl Fn() + 'static,
1507 ) -> Self {
1508 if enabled {
1509 self.hoverable(on_enter, on_leave)
1510 } else {
1511 self
1512 }
1513 }
1514 pub fn hoverable_with_source(mut self, source: &MutableInteractionSource) -> Self {
1518 self.interaction_source = Some(source.source());
1519 self
1520 }
1521 pub fn propagate_min_constraints(mut self, propagate: bool) -> Self {
1524 self.propagate_min = propagate;
1525 self
1526 }
1527 pub fn on_scroll(mut self, f: impl Fn(Vec2) -> Vec2 + 'static) -> Self {
1528 self.on_scroll = Some(Rc::new(f));
1529 self
1530 }
1531 pub fn on_scroll_with_source(
1538 mut self,
1539 f: impl Fn(Vec2, crate::input::ScrollSource) -> Vec2 + 'static,
1540 ) -> Self {
1541 self.on_scroll_with_source = Some(Rc::new(f));
1542 self
1543 }
1544 pub fn vertical_scroll(mut self, binding: crate::scroll::ScrollAxisBinding) -> Self {
1548 self.scroll = Some(crate::scroll::ScrollBinding::Vertical(binding));
1549 self
1550 }
1551 pub fn horizontal_scroll(mut self, binding: crate::scroll::ScrollAxisBinding) -> Self {
1553 self.scroll = Some(crate::scroll::ScrollBinding::Horizontal(binding));
1554 self
1555 }
1556 pub fn scrollable(mut self, binding: crate::scroll::ScrollBothBinding) -> Self {
1558 self.scroll = Some(crate::scroll::ScrollBinding::Both(binding));
1559 self
1560 }
1561 pub fn scrollbar_style(mut self, style: crate::scroll::ScrollbarStyle) -> Self {
1562 self.scrollbar_style = Some(style);
1563 self
1564 }
1565 pub fn scrollbar_style_or_default(&self) -> crate::scroll::ScrollbarStyle {
1566 self.scrollbar_style.clone().unwrap_or_default()
1567 }
1568 pub fn nested_scroll(mut self, conn: crate::nested_scroll::NestedScrollConnection) -> Self {
1575 self.nested_scroll_connection = Some(conn);
1576 self
1577 }
1578 pub fn on_pointer_down(mut self, f: impl Fn(PointerEvent) + 'static) -> Self {
1579 self.on_pointer_down = Some(Rc::new(f));
1580 self
1581 }
1582 pub fn on_pointer_move(mut self, f: impl Fn(PointerEvent) + 'static) -> Self {
1583 self.on_pointer_move = Some(Rc::new(f));
1584 self
1585 }
1586 pub fn on_pointer_up(mut self, f: impl Fn(PointerEvent) + 'static) -> Self {
1587 self.on_pointer_up = Some(Rc::new(f));
1588 self
1589 }
1590 pub fn on_pointer_cancel(mut self, f: impl Fn(PointerEvent) + 'static) -> Self {
1591 self.on_pointer_cancel = Some(Rc::new(f));
1592 self
1593 }
1594
1595 pub fn draggable(self, on_drag: impl Fn(Vec2) + 'static) -> Self {
1596 self.draggable_with_end(on_drag, || {})
1597 }
1598
1599 pub fn draggable_with_end(
1602 self,
1603 on_drag: impl Fn(Vec2) + 'static,
1604 on_drag_end: impl Fn() + 'static,
1605 ) -> Self {
1606 let drag_pos = crate::state::remember_mutable(Vec2::default);
1607 let is_dragging = crate::state::remember_mutable(|| false);
1608 let on_drag = Rc::new(on_drag);
1609 let on_drag_end = Rc::new(on_drag_end);
1610 self.on_pointer_down({
1611 let drag_pos = drag_pos.clone();
1612 let is_dragging = is_dragging.clone();
1613 move |ev| {
1614 is_dragging.set(true);
1615 drag_pos.set(ev.position);
1616 }
1617 })
1618 .on_pointer_up({
1619 let is_dragging = is_dragging.clone();
1620 let on_drag_end = on_drag_end.clone();
1621 move |_| {
1622 if *is_dragging.get() {
1623 is_dragging.set(false);
1624 on_drag_end();
1625 }
1626 }
1627 })
1628 .on_pointer_cancel({
1629 let is_dragging = is_dragging.clone();
1630 let on_drag_end = on_drag_end.clone();
1631 move |_| {
1632 if *is_dragging.get() {
1633 is_dragging.set(false);
1634 on_drag_end();
1635 }
1636 }
1637 })
1638 .on_pointer_move({
1639 let drag_pos = drag_pos.clone();
1640 let is_dragging = is_dragging.clone();
1641 let on_drag = on_drag.clone();
1642 move |ev| {
1643 if !*is_dragging.get() {
1644 return;
1645 }
1646 let prev = *drag_pos.get();
1647 let cur = ev.position;
1648 let delta = Vec2 {
1649 x: cur.x - prev.x,
1650 y: cur.y - prev.y,
1651 };
1652 drag_pos.set(cur);
1653 on_drag(delta);
1654 crate::frame_clock::request_frame();
1655 }
1656 })
1657 }
1658 pub fn transformable(
1659 self,
1660 state: &crate::gesture::TransformableState,
1661 lock_rotation_on_zoom_pan: bool,
1662 enabled: bool,
1663 ) -> Self {
1664 state.set_lock_rotation_on_zoom_pan(lock_rotation_on_zoom_pan);
1665 state.set_enabled(enabled);
1666 let state = state.clone();
1667 self.on_action(move |action| match &action {
1668 crate::shortcuts::Action::Gesture(g) => state.handle_gesture(g),
1669 _ => false,
1670 })
1671 }
1672
1673 pub fn draggable_with_config(
1674 self,
1675 config: crate::gesture::DraggableConfig,
1676 on_drag_started: impl Fn() + 'static,
1677 on_drag: impl Fn(Vec2) + 'static,
1678 on_drag_stopped: impl Fn(f32) + 'static,
1679 ) -> Self {
1680 if !config.enabled {
1681 return self;
1682 }
1683 let drag_pos = crate::state::remember_mutable(Vec2::default);
1684 let press_pos = crate::state::remember_mutable(Vec2::default);
1685 let dragging = crate::state::remember_mutable(|| false);
1686 let last_time = crate::state::remember_mutable(|| None::<web_time::Instant>);
1687 let last_pos = crate::state::remember_mutable(|| None::<Vec2>);
1688 let velocity = crate::state::remember_mutable(|| 0.0f32);
1689 let on_drag = Rc::new(on_drag);
1690 let on_drag_started = Rc::new(on_drag_started);
1691 let on_drag_stopped = Rc::new(on_drag_stopped);
1692 let axis = config.orientation;
1693 let reverse = config.reverse_direction;
1694 let slop = config.slop_px;
1695 let immediate = config.start_drag_immediately;
1696 let filter = Rc::new(move |d: Vec2| -> Vec2 {
1697 let mut d = match axis {
1698 Some(DragOrientation::Horizontal) => Vec2 { x: d.x, y: 0.0 },
1699 Some(DragOrientation::Vertical) => Vec2 { x: 0.0, y: d.y },
1700 None => d,
1701 };
1702 if reverse {
1703 d = Vec2 { x: -d.x, y: -d.y };
1704 }
1705 d
1706 });
1707 self.on_pointer_down({
1708 let drag_pos = drag_pos.clone();
1709 let press_pos = press_pos.clone();
1710 let dragging = dragging.clone();
1711 let last_time = last_time.clone();
1712 let last_pos = last_pos.clone();
1713 let velocity = velocity.clone();
1714 move |ev| {
1715 press_pos.set(ev.position);
1716 drag_pos.set(ev.position);
1717 last_pos.set(Some(ev.position));
1718 last_time.set(Some(web_time::Instant::now()));
1719 velocity.set(0.0);
1720 if immediate {
1721 dragging.set(true);
1722 }
1723 }
1724 })
1725 .on_pointer_move({
1726 let drag_pos = drag_pos.clone();
1727 let press_pos = press_pos.clone();
1728 let dragging = dragging.clone();
1729 let last_time = last_time.clone();
1730 let last_pos = last_pos.clone();
1731 let velocity = velocity.clone();
1732 let on_drag = on_drag.clone();
1733 let on_drag_started = on_drag_started.clone();
1734 let filter = filter.clone();
1735 move |ev| {
1736 if !*dragging.get() {
1737 let dx = ev.position.x - press_pos.get().x;
1738 let dy = ev.position.y - press_pos.get().y;
1739 let dist = match axis {
1740 Some(DragOrientation::Horizontal) => dx.abs(),
1741 Some(DragOrientation::Vertical) => dy.abs(),
1742 None => dx.hypot(dy),
1743 };
1744 if dist < slop {
1745 return;
1746 }
1747 dragging.set(true);
1748 on_drag_started();
1749 drag_pos.set(ev.position);
1750 last_pos.set(Some(ev.position));
1751 last_time.set(Some(web_time::Instant::now()));
1752 return;
1753 }
1754 let prev = *drag_pos.get();
1755 let delta = filter(Vec2 {
1756 x: ev.position.x - prev.x,
1757 y: ev.position.y - prev.y,
1758 });
1759 drag_pos.set(ev.position);
1760 if let (Some(lp), Some(lt)) = (*last_pos.get(), *last_time.get()) {
1761 let now = web_time::Instant::now();
1762 let dt = (now - lt).as_secs_f32().max(1.0 / 240.0);
1763 let axis_delta = match axis {
1764 Some(DragOrientation::Horizontal) => ev.position.x - lp.x,
1765 Some(DragOrientation::Vertical) => ev.position.y - lp.y,
1766 None => (ev.position.x - lp.x).hypot(ev.position.y - lp.y),
1767 };
1768 velocity.set(axis_delta / dt);
1769 last_pos.set(Some(ev.position));
1770 last_time.set(Some(now));
1771 }
1772 on_drag(delta);
1773 crate::frame_clock::request_frame();
1774 }
1775 })
1776 .on_pointer_up({
1777 let dragging = dragging.clone();
1778 let velocity = velocity.clone();
1779 let on_drag_stopped = on_drag_stopped.clone();
1780 move |_| {
1781 if *dragging.get() {
1782 dragging.set(false);
1783 on_drag_stopped(*velocity.get());
1784 }
1785 }
1786 })
1787 .on_pointer_cancel({
1788 let dragging = dragging.clone();
1789 let velocity = velocity.clone();
1790 let on_drag_stopped = on_drag_stopped.clone();
1791 move |_| {
1792 if *dragging.get() {
1793 dragging.set(false);
1794 on_drag_stopped(*velocity.get());
1795 }
1796 }
1797 })
1798 }
1799
1800 pub fn draggable_2d(
1801 self,
1802 on_drag_started: impl Fn() + 'static,
1803 on_drag: impl Fn(Vec2) + 'static,
1804 on_drag_stopped: impl Fn(f32) + 'static,
1805 ) -> Self {
1806 self.draggable_with_config(
1807 crate::gesture::DraggableConfig::default(),
1808 on_drag_started,
1809 on_drag,
1810 on_drag_stopped,
1811 )
1812 }
1813
1814 pub fn on_pointer_enter(mut self, f: impl Fn(PointerEvent) + 'static) -> Self {
1815 self.on_pointer_enter = Some(Rc::new(f));
1816 self
1817 }
1818 pub fn on_pointer_leave(mut self, f: impl Fn(PointerEvent) + 'static) -> Self {
1819 self.on_pointer_leave = Some(Rc::new(f));
1820 self
1821 }
1822 pub fn on_click(mut self, f: impl Fn() + 'static) -> Self {
1823 self.on_click = Some(Rc::new(f));
1824 self.click = true;
1825 if self.semantics.is_none() {
1826 self.semantics = Some(crate::Semantics::new(crate::Role::Button));
1827 }
1828 self
1829 }
1830 pub fn on_double_click(mut self, f: impl Fn() + 'static) -> Self {
1831 self.on_double_click = Some(Rc::new(f));
1832 self.click = true;
1833 self
1834 }
1835 pub fn on_long_click(mut self, f: impl Fn() + 'static) -> Self {
1836 self.on_long_click = Some(Rc::new(f));
1837 self.click = true;
1838 self
1839 }
1840 pub fn clickable_ext(
1841 mut self,
1842 enabled: bool,
1843 on_click_label: Option<String>,
1844 role: Option<crate::semantics::Role>,
1845 on_click: impl Fn() + 'static,
1846 ) -> Self {
1847 if !enabled {
1848 let mut s = self.semantics.clone().unwrap_or_else(|| {
1849 crate::semantics::Semantics::new(role.unwrap_or(crate::semantics::Role::Button))
1850 });
1851 s.enabled = false;
1852 if let Some(r) = role {
1853 s.role = r;
1854 }
1855 if let Some(l) = on_click_label {
1856 s.label = Some(l);
1857 }
1858 return self
1859 .clickable()
1860 .enabled(false)
1861 .default_min_size(Dp(48.0), Dp(48.0))
1862 .semantics(s);
1863 }
1864 self = self.clickable().on_click(on_click);
1865 if role.is_some() || on_click_label.is_some() {
1866 let mut s = self.semantics.clone().unwrap_or_else(|| {
1867 crate::semantics::Semantics::new(role.unwrap_or(crate::semantics::Role::Button))
1868 });
1869 s.enabled = true;
1870 if let Some(r) = role {
1871 s.role = r;
1872 }
1873 if let Some(l) = on_click_label {
1874 s.label = Some(l);
1875 }
1876 self = self.semantics(s);
1877 }
1878 self.default_min_size(Dp(48.0), Dp(48.0))
1879 }
1880 pub fn combined_clickable(
1881 mut self,
1882 enabled: bool,
1883 on_click_label: Option<String>,
1884 role: Option<crate::semantics::Role>,
1885 on_long_click_label: Option<String>,
1886 on_click: impl Fn() + 'static,
1887 on_long_click: Option<impl Fn() + 'static>,
1888 on_double_click: Option<impl Fn() + 'static>,
1889 ) -> Self {
1890 let _ = on_long_click_label;
1891 if !enabled {
1892 return self.clickable_ext(false, on_click_label, role, || {});
1893 }
1894 self = self.clickable_ext(true, on_click_label, role, on_click);
1895 if let Some(f) = on_long_click {
1896 self = self.on_long_click(f);
1897 }
1898 if let Some(f) = on_double_click {
1899 self = self.on_double_click(f);
1900 }
1901 self.default_min_size(Dp(48.0), Dp(48.0))
1902 }
1903 pub fn semantics(mut self, s: crate::Semantics) -> Self {
1904 self.semantics = Some(s);
1905 self
1906 }
1907 pub fn alpha(mut self, a: f32) -> Self {
1908 self.alpha = Some(a);
1909 self
1910 }
1911 pub fn graphics_layer(mut self, alpha: f32) -> Self {
1916 self.graphics_layer = Some(alpha.clamp(0.0, 1.0));
1917 self
1918 }
1919 pub fn shadow(mut self, blur_radius: Dp, offset_y: Dp) -> Self {
1923 self.shadow = Some(ShadowSpec {
1924 blur_radius: Dp(blur_radius.0.max(0.0)),
1925 offset_y,
1926 color: Color(0, 0, 0, 64),
1927 });
1928 self
1929 }
1930 pub fn shadow_with_color(mut self, blur_radius: Dp, offset_y: Dp, color: Color) -> Self {
1932 self.shadow = Some(ShadowSpec {
1933 blur_radius: Dp(blur_radius.0.max(0.0)),
1934 offset_y,
1935 color,
1936 });
1937 self
1938 }
1939 pub fn elevation(mut self, level: Dp) -> Self {
1943 if level.0 <= 0.0 {
1944 self.shadow = None;
1945 return self;
1946 }
1947 self.shadow = Some(ShadowSpec {
1948 blur_radius: Dp(level.0 * 2.0),
1949 offset_y: Dp(level.0 * 0.5),
1950 color: Color(0, 0, 0, (level.0 * 8.0).clamp(8.0, 80.0) as u8),
1951 });
1952 self
1953 }
1954 pub fn elevation_with_color(mut self, level: Dp, color: Color) -> Self {
1957 if level.0 <= 0.0 {
1958 self.shadow = None;
1959 return self;
1960 }
1961 self.shadow = Some(ShadowSpec {
1962 blur_radius: Dp(level.0 * 2.0),
1963 offset_y: Dp(level.0 * 0.5),
1964 color,
1965 });
1966 self
1967 }
1968 pub fn shadow_rounded(mut self, radius: Dp) -> Self {
1973 self.shadow_shape = Some([radius; 4]);
1974 self
1975 }
1976 pub fn shadow_rounded_radii(mut self, radii: [Dp; 4]) -> Self {
1979 self.shadow_shape = Some(radii);
1980 self
1981 }
1982 pub fn transform(mut self, t: Transform) -> Self {
1983 self.transform = Some(t);
1984 self
1985 }
1986 pub fn grid(mut self, columns: usize, row_gap: Dp, column_gap: Dp) -> Self {
1987 self.grid = Some(GridConfig {
1988 columns,
1989 row_gap,
1990 column_gap,
1991 });
1992 self
1993 }
1994 pub fn grid_span(mut self, col_span: u16, row_span: u16) -> Self {
1995 self.grid_col_span = Some(col_span);
1996 self.grid_row_span = Some(row_span);
1997 self
1998 }
1999 pub fn absolute(mut self) -> Self {
2008 self.position_type = Some(PositionType::Absolute);
2009 self
2010 }
2011 pub fn offset(
2016 mut self,
2017 left: Option<Dp>,
2018 top: Option<Dp>,
2019 right: Option<Dp>,
2020 bottom: Option<Dp>,
2021 ) -> Self {
2022 self.offset_left = left;
2023 self.offset_top = top;
2024 self.offset_right = right;
2025 self.offset_bottom = bottom;
2026 self
2027 }
2028 pub fn offset_left(mut self, v: Dp) -> Self {
2029 self.offset_left = Some(v);
2030 self
2031 }
2032 pub fn offset_right(mut self, v: Dp) -> Self {
2033 self.offset_right = Some(v);
2034 self
2035 }
2036 pub fn offset_top(mut self, v: Dp) -> Self {
2037 self.offset_top = Some(v);
2038 self
2039 }
2040 pub fn offset_bottom(mut self, v: Dp) -> Self {
2041 self.offset_bottom = Some(v);
2042 self
2043 }
2044 pub fn margin(mut self, v: Dp) -> Self {
2045 self.margin_left = Some(v);
2046 self.margin_right = Some(v);
2047 self.margin_top = Some(v);
2048 self.margin_bottom = Some(v);
2049 self
2050 }
2051
2052 pub fn margin_horizontal(mut self, v: Dp) -> Self {
2053 self.margin_left = Some(v);
2054 self.margin_right = Some(v);
2055 self
2056 }
2057
2058 pub fn margin_vertical(mut self, v: Dp) -> Self {
2059 self.margin_top = Some(v);
2060 self.margin_bottom = Some(v);
2061 self
2062 }
2063 pub fn aspect_ratio(mut self, ratio: f32) -> Self {
2064 self.aspect_ratio = Some(ratio);
2065 self
2066 }
2067 pub fn fit_content_width(mut self, limit: Dp) -> Self {
2069 self.fit_content_width = Some(Dp(limit.0.max(0.0)));
2070 self
2071 }
2072 pub fn fit_content_height(mut self, limit: Dp) -> Self {
2074 self.fit_content_height = Some(Dp(limit.0.max(0.0)));
2075 self
2076 }
2077 pub fn contain(mut self, c: Contain) -> Self {
2079 self.contain = Some(c);
2080 self
2081 }
2082 pub fn contain_layout(mut self) -> Self {
2084 self.contain = Some(Contain::LAYOUT);
2085 self
2086 }
2087 pub fn contain_paint(mut self) -> Self {
2089 self.contain = Some(Contain::PAINT);
2090 self
2091 }
2092 pub fn contain_content(mut self) -> Self {
2094 self.contain = Some(Contain::CONTENT);
2095 self
2096 }
2097 pub fn intrinsic_width(mut self, mode: IntrinsicSize) -> Self {
2099 self.intrinsic_width = Some(mode);
2100 self
2101 }
2102 pub fn intrinsic_height(mut self, mode: IntrinsicSize) -> Self {
2104 self.intrinsic_height = Some(mode);
2105 self
2106 }
2107 pub fn painter(mut self, f: impl Fn(&mut crate::Scene, crate::Rect, f32) + 'static) -> Self {
2108 self.painter = Some(Rc::new(f));
2109 self
2110 }
2111 pub fn paint_callback(mut self, payload: crate::PaintCallbackPayload) -> Self {
2117 self.paint_callback = Some(payload);
2118 self
2119 }
2120 pub fn scale(self, s: f32) -> Self {
2121 self.scale2(s, s)
2122 }
2123 pub fn scale2(mut self, sx: f32, sy: f32) -> Self {
2124 let mut t = self.transform.unwrap_or_else(Transform::identity);
2125 t.scale_x *= sx;
2126 t.scale_y *= sy;
2127 self.transform = Some(t);
2128 self
2129 }
2130 pub fn translate(mut self, x: f32, y: f32) -> Self {
2131 let t = self.transform.unwrap_or_else(Transform::identity);
2132 self.transform = Some(t.combine(&Transform::translate(x, y)));
2133 self
2134 }
2135 pub fn translate_vec2(self, v: Vec2) -> Self {
2136 self.translate(v.x, v.y)
2137 }
2138 pub fn rotate(mut self, radians: f32) -> Self {
2139 let mut t = self.transform.unwrap_or_else(Transform::identity);
2140 t.rotate += radians;
2141 self.transform = Some(t);
2142 self
2143 }
2144 pub fn transform_origin(mut self, x: f32, y: f32) -> Self {
2145 let mut t = self.transform.unwrap_or_else(Transform::identity);
2146 t.origin_x = x;
2147 t.origin_y = y;
2148 self.transform = Some(t);
2149 self
2150 }
2151 pub fn weight(mut self, w: f32) -> Self {
2152 let w = w.max(0.0);
2153 self.flex_grow = Some(w);
2154 self.flex_shrink = Some(1.0);
2155 self.flex_basis = Some(Dp::ZERO);
2156 self.flex_basis_content = false;
2157 self.min_width = Some(Dp::ZERO);
2161 self.min_height = Some(Dp::ZERO);
2162 self
2163 }
2164 pub fn repaint_boundary(mut self) -> Self {
2168 self.repaint_boundary = true;
2169 self
2170 }
2171 pub fn on_action(mut self, f: impl Fn(crate::shortcuts::Action) -> bool + 'static) -> Self {
2172 self.on_action = Some(Rc::new(f));
2173 self
2174 }
2175 pub fn on_set_progress(mut self, f: impl Fn(f32) + 'static) -> Self {
2176 self.on_set_progress = Some(Rc::new(f));
2177 self
2178 }
2179 pub fn on_expand(mut self, f: impl Fn() + 'static) -> Self {
2180 self.on_expand = Some(Rc::new(f));
2181 self
2182 }
2183 pub fn on_collapse(mut self, f: impl Fn() + 'static) -> Self {
2184 self.on_collapse = Some(Rc::new(f));
2185 self
2186 }
2187 pub fn on_dismiss(mut self, f: impl Fn() + 'static) -> Self {
2188 self.on_dismiss = Some(Rc::new(f));
2189 self
2190 }
2191 fn with_semantics_mut(mut self, f: impl FnOnce(&mut crate::Semantics)) -> Self {
2192 let mut s = self.semantics.take().unwrap_or_default();
2193 f(&mut s);
2194 self.semantics = Some(s);
2195 self
2196 }
2197 pub fn heading(self) -> Self {
2198 self.with_semantics_mut(|s| s.heading = true)
2199 }
2200 pub fn live_region(self, region: crate::LiveRegion) -> Self {
2201 self.with_semantics_mut(|s| s.live_region = Some(region))
2202 }
2203 pub fn state_description(self, description: impl Into<String>) -> Self {
2204 self.with_semantics_mut(|s| s.state_description = Some(description.into()))
2205 }
2206 pub fn traversal_index(self, index: f32) -> Self {
2207 self.with_semantics_mut(|s| s.traversal_index = Some(index))
2208 }
2209 pub fn traversal_group(self) -> Self {
2210 self.with_semantics_mut(|s| s.is_traversal_group = true)
2211 }
2212 pub fn hidden_from_accessibility(self) -> Self {
2213 self.with_semantics_mut(|s| s.hidden = true)
2214 }
2215 pub fn collection_info(self, rows: usize, columns: usize) -> Self {
2216 self.with_semantics_mut(|s| {
2217 s.collection_rows = Some(rows);
2218 s.collection_columns = Some(columns);
2219 })
2220 }
2221 pub fn collection_item(self, row: usize, column: usize) -> Self {
2222 self.with_semantics_mut(|s| {
2223 s.row_index = Some(row);
2224 s.column_index = Some(column);
2225 })
2226 }
2227 pub fn progress_range(self, info: crate::ProgressRangeInfo) -> Self {
2228 self.with_semantics_mut(|s| s.progress = Some(info))
2229 }
2230 pub fn semantics_error(self, message: impl Into<String>) -> Self {
2231 self.with_semantics_mut(|s| s.error = Some(message.into()))
2232 }
2233 pub fn expandable(self, expanded: bool) -> Self {
2234 self.with_semantics_mut(|s| s.expanded = Some(expanded))
2235 }
2236
2237 pub fn on_drag_start(
2239 mut self,
2240 f: impl Fn(crate::dnd::DragStart) -> Option<crate::dnd::DragPayload> + 'static,
2241 ) -> Self {
2242 self.on_drag_start = Some(Rc::new(f));
2243 self
2244 }
2245
2246 pub fn on_drag_end(mut self, f: impl Fn(crate::dnd::DragEnd) + 'static) -> Self {
2248 self.on_drag_end = Some(Rc::new(f));
2249 self
2250 }
2251
2252 pub fn on_drag_enter(mut self, f: impl Fn(crate::dnd::DragOver) + 'static) -> Self {
2254 self.on_drag_enter = Some(Rc::new(f));
2255 self
2256 }
2257
2258 pub fn on_drag_over(mut self, f: impl Fn(crate::dnd::DragOver) + 'static) -> Self {
2260 self.on_drag_over = Some(Rc::new(f));
2261 self
2262 }
2263
2264 pub fn on_drag_leave(mut self, f: impl Fn(crate::dnd::DragOver) + 'static) -> Self {
2266 self.on_drag_leave = Some(Rc::new(f));
2267 self
2268 }
2269
2270 pub fn on_drop(mut self, f: impl Fn(crate::dnd::DropEvent) -> bool + 'static) -> Self {
2273 self.on_drop = Some(Rc::new(f));
2274 self
2275 }
2276
2277 pub fn draw_drag_decoration(
2282 mut self,
2283 f: impl Fn(&mut crate::Scene, &crate::dnd::DragPreviewCtx) + 'static,
2284 ) -> Self {
2285 self.drag_preview = Some(Rc::new(f));
2286 self
2287 }
2288
2289 pub fn draw_drag_decoration_rc(mut self, preview: crate::dnd::DragPreview) -> Self {
2291 self.drag_preview = Some(preview);
2292 self
2293 }
2294
2295 pub fn drag_preview_label(self, label: impl Into<String>, accent: crate::Color) -> Self {
2297 self.draw_drag_decoration_rc(crate::dnd::drag_preview_label(label, accent))
2298 }
2299
2300 pub fn drag_preview_chip(self, label: impl Into<String>, accent: crate::Color) -> Self {
2302 self.draw_drag_decoration_rc(crate::dnd::drag_preview_chip(label, accent))
2303 }
2304
2305 pub fn cursor(mut self, c: crate::CursorIcon) -> Self {
2307 self.cursor = Some(c);
2308 self
2309 }
2310
2311 pub fn animate_content_size(mut self, spec: AnimationSpec) -> Self {
2315 self.animate_content_size = Some(spec);
2316 self
2317 }
2318
2319 pub fn focus_requester(mut self, fr: crate::runtime::FocusRequester) -> Self {
2322 self.focus_requester = Some(fr);
2323 self
2324 }
2325
2326 pub fn focus_target(mut self) -> Self {
2329 self.focusable = Some(true);
2330 self
2331 }
2332
2333 pub fn on_focus_changed(mut self, f: impl Fn(bool) + 'static) -> Self {
2336 self.on_focus_changed = Some(Rc::new(f));
2337 self
2338 }
2339
2340 pub fn on_globally_positioned(mut self, f: impl Fn(crate::Rect) + 'static) -> Self {
2345 self.on_globally_positioned = Some(Rc::new(f));
2346 self
2347 }
2348
2349 pub fn on_size_changed(mut self, f: impl Fn(crate::Vec2) + 'static) -> Self {
2352 self.on_size_changed = Some(Rc::new(f));
2353 self
2354 }
2355
2356 pub fn on_key_event(mut self, f: impl Fn(crate::input::KeyEvent) -> bool + 'static) -> Self {
2360 self.on_key_event = Some(Rc::new(f));
2361 self
2362 }
2363
2364 pub fn on_ime(mut self, f: impl Fn(crate::input::ImeEvent) -> bool + 'static) -> Self {
2367 self.on_ime = Some(Rc::new(f));
2368 self
2369 }
2370
2371 pub fn on_preview_key_event(
2375 mut self,
2376 f: impl Fn(crate::input::KeyEvent) -> bool + 'static,
2377 ) -> Self {
2378 self.on_preview_key_event = Some(Rc::new(f));
2379 self
2380 }
2381
2382 pub fn blur(mut self, radius: Dp) -> Self {
2389 self.blur = Some(BlurStyle {
2390 radius_x: Dp(radius.0.max(0.0)),
2391 radius_y: Dp(radius.0.max(0.0)),
2392 edge_treatment: BlurredEdgeTreatment::Rectangle,
2393 });
2394 self
2395 }
2396
2397 pub fn blur_with_edge(
2402 mut self,
2403 radius_x: Dp,
2404 radius_y: Dp,
2405 edge_treatment: BlurredEdgeTreatment,
2406 ) -> Self {
2407 self.blur = Some(BlurStyle {
2408 radius_x: Dp(radius_x.0.max(0.0)),
2409 radius_y: Dp(radius_y.0.max(0.0)),
2410 edge_treatment,
2411 });
2412 self
2413 }
2414
2415 pub fn layout(mut self, f: impl Fn(LayoutConstraints) -> (Dp, Dp) + 'static) -> Self {
2423 self.layout = Some(Rc::new(f));
2424 self
2425 }
2426
2427 pub fn text_input(mut self, config: TextInputConfig) -> Self {
2429 self.text_input = Some(config);
2430 self
2431 }
2432
2433 pub fn text_selection(mut self, selection: crate::text::TextSelection) -> Self {
2436 self.text_selection = Some(selection);
2437 self
2438 }
2439
2440 pub fn indication(mut self, factory: Rc<dyn IndicationNodeFactory>) -> Self {
2444 self.indication = Some(factory);
2445 self
2446 }
2447}