1#[cfg(test)]
8mod insets_tests;
9mod interaction;
10mod shadow;
11mod visual;
12
13use tessera_foundation::modifier::ModifierExt as FoundationModifierExt;
14use tessera_ui::{
15 AxisConstraint, Callback, CallbackWith, Color, Dp, Modifier, State,
16 modifier::ModifierCapabilityExt as _,
17};
18
19pub use tessera_foundation::modifier::{
20 ClickableArgs, DragDelta, DraggableArgs, InteractionState, InteractionVisualState,
21 MinimumInteractiveComponentEnforcement, Padding, PointerEventContext, SelectableArgs,
22 SemanticsArgs, ToggleableArgs,
23};
24
25pub(crate) use tessera_foundation::modifier::{AlignmentParentData, WeightParentData};
26
27use crate::{
28 alignment::Alignment, scrollable::ScrollableController, shape_def::Shape,
29 theme::MaterialColorScheme,
30};
31
32use interaction::{
33 apply_block_touch_propagation_modifier, apply_clickable_modifier, apply_selectable_modifier,
34 apply_toggleable_modifier, apply_window_drag_region_modifier,
35};
36use visual::{AlphaModifierNode, BackgroundModifierNode, BorderModifierNode, ClipModifierNode};
37
38pub use shadow::ShadowArgs;
39
40pub(crate) use interaction::{
41 DisabledToggleSemantics, apply_combined_clickable_modifier, with_keyboard_input,
42 with_pointer_input,
43};
44
45pub trait ModifierExt {
48 fn window_insets_padding(self, insets: tessera_ui::WindowInsets) -> Modifier;
50
51 fn consume_window_insets(self, insets: tessera_ui::WindowInsets) -> Modifier;
53
54 fn window_insets_left_width(self, insets: tessera_ui::WindowInsets) -> Modifier;
56
57 fn window_insets_top_height(self, insets: tessera_ui::WindowInsets) -> Modifier;
59
60 fn window_insets_right_width(self, insets: tessera_ui::WindowInsets) -> Modifier;
62
63 fn window_insets_bottom_height(self, insets: tessera_ui::WindowInsets) -> Modifier;
65
66 fn padding(self, padding: Padding) -> Modifier;
68
69 fn padding_all(self, padding: Dp) -> Modifier;
71
72 fn padding_symmetric(self, horizontal: Dp, vertical: Dp) -> Modifier;
74
75 fn offset(self, x: Dp, y: Dp) -> Modifier;
77
78 fn alpha(self, alpha: f32) -> Modifier;
80
81 fn clip_to_bounds(self) -> Modifier;
83
84 fn clip_shape(self, shape: Shape) -> Modifier;
86
87 fn vertical_scroll(self, controller: State<ScrollableController>) -> Modifier;
89
90 fn horizontal_scroll(self, controller: State<ScrollableController>) -> Modifier;
92
93 fn scroll(
95 self,
96 controller: State<ScrollableController>,
97 vertical: bool,
98 horizontal: bool,
99 ) -> Modifier;
100
101 fn background(self, color: Color) -> Modifier;
103
104 fn background_with_shape(self, color: Color, shape: Shape) -> Modifier;
106
107 fn background_with_scaled_shape(self, color: Color, shape: Shape, scale: [f32; 2]) -> Modifier;
113
114 fn border(self, width: Dp, color: Color) -> Modifier;
116
117 fn border_with_shape(self, width: Dp, color: Color, shape: Shape) -> Modifier;
119
120 fn shadow(self, scheme: &MaterialColorScheme, args: &ShadowArgs) -> Modifier;
122
123 fn size(self, width: Dp, height: Dp) -> Modifier;
125
126 fn required_size(self, width: Dp, height: Dp) -> Modifier;
128
129 fn required_width(self, width: Dp) -> Modifier;
131
132 fn required_height(self, height: Dp) -> Modifier;
134
135 fn default_min_size(self, min_width: Dp, min_height: Dp) -> Modifier;
138
139 fn width(self, width: Dp) -> Modifier;
141
142 fn height(self, height: Dp) -> Modifier;
144
145 fn size_in(
147 self,
148 min_width: Option<Dp>,
149 max_width: Option<Dp>,
150 min_height: Option<Dp>,
151 max_height: Option<Dp>,
152 ) -> Modifier;
153
154 fn constrain(self, width: Option<AxisConstraint>, height: Option<AxisConstraint>) -> Modifier;
156
157 fn fill_max_width(self) -> Modifier;
159
160 fn fill_max_height(self) -> Modifier;
162
163 fn fill_max_size(self) -> Modifier;
165
166 fn minimum_interactive_component_size(self) -> Modifier;
168
169 fn weight(self, weight: f32) -> Modifier;
171
172 fn align(self, alignment: Alignment) -> Modifier;
174
175 fn draggable<C>(self, on_drag_delta: C) -> Modifier
177 where
178 C: Into<CallbackWith<DragDelta, ()>>;
179
180 fn draggable_with(self, args: DraggableArgs) -> Modifier;
182
183 fn block_touch_propagation(self) -> Modifier;
186
187 fn semantics(self, args: SemanticsArgs) -> Modifier;
189
190 fn clear_and_set_semantics(self, args: SemanticsArgs) -> Modifier;
192
193 fn clickable<C>(self, on_click: C) -> Modifier
196 where
197 C: Into<Callback>;
198
199 fn clickable_with(self, args: ClickableArgs) -> Modifier;
201
202 fn toggleable<C>(self, value: bool, on_value_change: C) -> Modifier
204 where
205 C: Into<CallbackWith<bool, ()>>;
206
207 fn toggleable_with(self, args: ToggleableArgs) -> Modifier;
209
210 fn selectable<C>(self, selected: bool, on_click: C) -> Modifier
212 where
213 C: Into<Callback>;
214
215 fn selectable_with(self, args: SelectableArgs) -> Modifier;
217
218 fn window_drag_region(self) -> Modifier;
221}
222
223impl ModifierExt for Modifier {
224 fn window_insets_padding(self, insets: tessera_ui::WindowInsets) -> Modifier {
225 FoundationModifierExt::window_insets_padding(self, insets)
226 }
227 fn consume_window_insets(self, insets: tessera_ui::WindowInsets) -> Modifier {
228 FoundationModifierExt::consume_window_insets(self, insets)
229 }
230 fn window_insets_left_width(self, insets: tessera_ui::WindowInsets) -> Modifier {
231 FoundationModifierExt::window_insets_left_width(self, insets)
232 }
233 fn window_insets_top_height(self, insets: tessera_ui::WindowInsets) -> Modifier {
234 FoundationModifierExt::window_insets_top_height(self, insets)
235 }
236 fn window_insets_right_width(self, insets: tessera_ui::WindowInsets) -> Modifier {
237 FoundationModifierExt::window_insets_right_width(self, insets)
238 }
239 fn window_insets_bottom_height(self, insets: tessera_ui::WindowInsets) -> Modifier {
240 FoundationModifierExt::window_insets_bottom_height(self, insets)
241 }
242
243 fn padding(self, padding: Padding) -> Modifier {
244 FoundationModifierExt::padding(self, padding)
245 }
246
247 fn padding_all(self, padding: Dp) -> Modifier {
248 FoundationModifierExt::padding(self, Padding::all(padding))
249 }
250
251 fn padding_symmetric(self, horizontal: Dp, vertical: Dp) -> Modifier {
252 FoundationModifierExt::padding(self, Padding::symmetric(horizontal, vertical))
253 }
254
255 fn offset(self, x: Dp, y: Dp) -> Modifier {
256 FoundationModifierExt::offset(self, x, y)
257 }
258
259 fn alpha(self, alpha: f32) -> Modifier {
260 let alpha = alpha.clamp(0.0, 1.0);
261 if (alpha - 1.0).abs() <= f32::EPSILON {
262 return self;
263 }
264
265 self.push_draw(AlphaModifierNode { alpha })
266 }
267
268 fn clip_to_bounds(self) -> Modifier {
269 self.push_draw(ClipModifierNode)
270 }
271
272 fn clip_shape(self, shape: Shape) -> Modifier {
273 self.push_draw(visual::ShapeClipModifierNode { shape })
274 }
275
276 fn vertical_scroll(self, controller: State<ScrollableController>) -> Modifier {
277 crate::scrollable::apply_scroll_modifier(self, controller, true, false)
278 }
279
280 fn horizontal_scroll(self, controller: State<ScrollableController>) -> Modifier {
281 crate::scrollable::apply_scroll_modifier(self, controller, false, true)
282 }
283
284 fn scroll(
285 self,
286 controller: State<ScrollableController>,
287 vertical: bool,
288 horizontal: bool,
289 ) -> Modifier {
290 crate::scrollable::apply_scroll_modifier(self, controller, vertical, horizontal)
291 }
292
293 fn background(self, color: Color) -> Modifier {
294 self.background_with_shape(color, Shape::RECTANGLE)
295 }
296
297 fn background_with_shape(self, color: Color, shape: Shape) -> Modifier {
298 if color.a <= 0.0 {
299 return self;
300 }
301
302 self.push_draw(BackgroundModifierNode { color, shape })
303 }
304
305 fn background_with_scaled_shape(self, color: Color, shape: Shape, scale: [f32; 2]) -> Modifier {
306 assert!(
307 scale
308 .iter()
309 .all(|value| value.is_finite() && (0.0..=1.0).contains(value)),
310 "background scales must be finite and in 0..=1",
311 );
312 self.push_draw(visual::ScaledBackgroundModifierNode {
313 color,
314 shape,
315 scale,
316 })
317 }
318
319 fn border(self, width: Dp, color: Color) -> Modifier {
320 self.border_with_shape(width, color, Shape::RECTANGLE)
321 }
322
323 fn border_with_shape(self, width: Dp, color: Color, shape: Shape) -> Modifier {
324 if width.0 <= 0.0 || color.a <= 0.0 {
325 return self;
326 }
327
328 self.push_draw(BorderModifierNode {
329 width,
330 color,
331 shape,
332 })
333 }
334
335 fn shadow(self, scheme: &MaterialColorScheme, args: &ShadowArgs) -> Modifier {
336 shadow::apply_shadow_modifier(self, scheme, args.clone())
337 }
338
339 fn default_min_size(self, min_width: Dp, min_height: Dp) -> Modifier {
340 FoundationModifierExt::default_min_size(self, min_width, min_height)
341 }
342
343 fn size(self, width: Dp, height: Dp) -> Modifier {
344 FoundationModifierExt::size(self, width, height)
345 }
346
347 fn required_size(self, width: Dp, height: Dp) -> Modifier {
348 FoundationModifierExt::required_size(self, width, height)
349 }
350
351 fn required_width(self, width: Dp) -> Modifier {
352 FoundationModifierExt::required_width(self, width)
353 }
354
355 fn required_height(self, height: Dp) -> Modifier {
356 FoundationModifierExt::required_height(self, height)
357 }
358 fn width(self, width: Dp) -> Modifier {
359 FoundationModifierExt::width(self, width)
360 }
361
362 fn height(self, height: Dp) -> Modifier {
363 FoundationModifierExt::height(self, height)
364 }
365
366 fn size_in(
367 self,
368 min_width: Option<Dp>,
369 max_width: Option<Dp>,
370 min_height: Option<Dp>,
371 max_height: Option<Dp>,
372 ) -> Modifier {
373 FoundationModifierExt::size_in(self, min_width, max_width, min_height, max_height)
374 }
375
376 fn constrain(self, width: Option<AxisConstraint>, height: Option<AxisConstraint>) -> Modifier {
377 FoundationModifierExt::constrain(self, width, height)
378 }
379
380 fn fill_max_width(self) -> Modifier {
381 FoundationModifierExt::fill_max_width(self)
382 }
383
384 fn fill_max_height(self) -> Modifier {
385 FoundationModifierExt::fill_max_height(self)
386 }
387
388 fn fill_max_size(self) -> Modifier {
389 FoundationModifierExt::fill_max_size(self)
390 }
391
392 fn minimum_interactive_component_size(self) -> Modifier {
393 if !MinimumInteractiveComponentEnforcement::default().enabled {
394 return self;
395 }
396
397 FoundationModifierExt::minimum_interactive_component_size(self)
398 }
399
400 fn weight(self, weight: f32) -> Modifier {
401 FoundationModifierExt::weight(self, weight)
402 }
403
404 fn align(self, alignment: Alignment) -> Modifier {
405 FoundationModifierExt::align(self, alignment)
406 }
407
408 fn draggable<C>(self, on_drag_delta: C) -> Modifier
409 where
410 C: Into<CallbackWith<DragDelta, ()>>,
411 {
412 FoundationModifierExt::draggable(self, on_drag_delta)
413 }
414
415 fn draggable_with(self, args: DraggableArgs) -> Modifier {
416 FoundationModifierExt::draggable_with(self, args)
417 }
418
419 fn block_touch_propagation(self) -> Modifier {
420 apply_block_touch_propagation_modifier(self)
421 }
422
423 fn semantics(self, args: SemanticsArgs) -> Modifier {
424 FoundationModifierExt::semantics(self, args)
425 }
426
427 fn clear_and_set_semantics(self, mut args: SemanticsArgs) -> Modifier {
428 args.merge_descendants = false;
429 FoundationModifierExt::semantics(self, args)
430 }
431
432 fn clickable<C>(self, on_click: C) -> Modifier
433 where
434 C: Into<Callback>,
435 {
436 self.clickable_with(ClickableArgs {
437 on_click: on_click.into(),
438 ..Default::default()
439 })
440 }
441
442 fn clickable_with(self, args: ClickableArgs) -> Modifier {
443 apply_clickable_modifier(self, args)
444 }
445
446 fn toggleable<C>(self, value: bool, on_value_change: C) -> Modifier
447 where
448 C: Into<CallbackWith<bool, ()>>,
449 {
450 self.toggleable_with(ToggleableArgs {
451 value,
452 on_value_change: on_value_change.into(),
453 ..Default::default()
454 })
455 }
456
457 fn toggleable_with(self, args: ToggleableArgs) -> Modifier {
458 apply_toggleable_modifier(self, args)
459 }
460
461 fn selectable<C>(self, selected: bool, on_click: C) -> Modifier
462 where
463 C: Into<Callback>,
464 {
465 self.selectable_with(SelectableArgs {
466 selected,
467 on_click: on_click.into(),
468 ..Default::default()
469 })
470 }
471
472 fn selectable_with(self, args: SelectableArgs) -> Modifier {
473 apply_selectable_modifier(self, args)
474 }
475
476 fn window_drag_region(self) -> Modifier {
477 apply_window_drag_region_modifier(self)
478 }
479}