1mod lazy;
30mod widgets;
31
32pub use crate::boundary::LaunchBuilder;
33pub use crate::brush::{Brush, Stop, brush};
34pub use crate::drawing::{DrawCommand, DrawList, DrawListBuilder};
35pub use crate::message::{Message, show_message};
36pub use crate::runtime::{application, launch};
37pub use crate::schema::{
38 Alignment, Arrangement, ButtonKind, ButtonVariant, Color, ColorRole, ColorScheme, DesignSystem,
39 IconRole, Key, MaterialRole, MessageDuration, MotionRole, Paint, ShapeRole, SpaceRole,
40 TextAlign, TextOverflow, Theme, TypeRole, WindowHeightClass, WindowSizeClass,
41};
42pub use crate::spans::TextSpans;
43pub use crate::theme::{ThemeHandle, current_theme};
44
45pub fn theme() -> ThemeHandle {
48 ThemeHandle::default()
49}
50pub use crate::window::{NodeSize, WindowSize};
51pub use crate::{
52 Cursor, FileDrop, KeyEvent, PointerEvent, PointerKind, PointerKinds, RangeRequest, UserSelect,
53 asset, pointer_button,
54};
55pub use lazy::{LazyColumn, LazyGrid, LazyList, LazyListScope, LazyRow};
56pub use widgets::*;
57
58use crate::runtime::composer::{EventCallback, OwnedAttr};
59use crate::runtime::{group_guard, with_composer, with_composer_if_idle};
60use crate::schema::WidgetKind;
61
62#[derive(Clone, Copy, Debug, Default, PartialEq)]
75pub struct Modifier {
76 observe_size: Option<i64>,
77 motion: Option<MotionRole>,
78 material: Option<MaterialRole>,
79 weight: Option<f32>,
80 width: Option<f32>,
81 height: Option<f32>,
82 padding: Option<f32>,
83 padding_role: Option<SpaceRole>,
84 background: Option<Paint>,
85 shape_role: Option<ShapeRole>,
86 corner_radius: Option<f32>,
87 border_width: Option<f32>,
88 border_color: Option<Paint>,
89 elevation: Option<f32>,
90 alpha: Option<f32>,
91 transform: Option<[f32; 8]>,
92 animated_alpha: Option<crate::Animated<f32>>,
93 animated_background: Option<crate::Animated<Paint>>,
94 animated_offset: Option<crate::Animated<(f32, f32)>>,
95 cursor: Option<crate::Cursor>,
96 user_select: Option<crate::UserSelect>,
97 fill_max_width: bool,
98 fill_max_height: bool,
99}
100
101#[allow(non_upper_case_globals)]
103pub const Modifier: Modifier = Modifier::new();
104
105impl Modifier {
106 pub const fn new() -> Self {
108 Self {
109 observe_size: None,
110 motion: None,
111 material: None,
112 weight: None,
113 width: None,
114 height: None,
115 padding: None,
116 padding_role: None,
117 background: None,
118 shape_role: None,
119 corner_radius: None,
120 border_width: None,
121 border_color: None,
122 elevation: None,
123 alpha: None,
124 transform: None,
125 animated_alpha: None,
126 animated_background: None,
127 animated_offset: None,
128 cursor: None,
129 user_select: None,
130 fill_max_width: false,
131 fill_max_height: false,
132 }
133 }
134
135 pub const fn weight(mut self, weight: f32) -> Self {
137 self.weight = Some(weight);
138 self
139 }
140
141 pub const fn width(mut self, dp: f32) -> Self {
142 self.width = Some(dp);
143 self
144 }
145
146 pub const fn height(mut self, dp: f32) -> Self {
147 self.height = Some(dp);
148 self
149 }
150
151 pub const fn size(self, width: f32, height: f32) -> Self {
153 self.width(width).height(height)
154 }
155
156 pub const fn width_if(mut self, dp: Option<f32>) -> Self {
158 self.width = dp;
159 self
160 }
161
162 pub const fn fill_max_width(mut self) -> Self {
163 self.fill_max_width = true;
164 self
165 }
166
167 pub const fn fill_max_height(mut self) -> Self {
168 self.fill_max_height = true;
169 self
170 }
171
172 pub const fn fill_max_size(self) -> Self {
173 self.fill_max_width().fill_max_height()
174 }
175
176 pub const fn fill_max_width_if(mut self, fill: bool) -> Self {
178 self.fill_max_width = fill;
179 self
180 }
181
182 pub const fn padding(mut self, dp: f32) -> Self {
184 self.padding = Some(dp);
185 self
186 }
187
188 pub const fn padding_role(mut self, role: SpaceRole) -> Self {
190 self.padding_role = Some(role);
191 self
192 }
193
194 pub const fn padding_role_if(mut self, role: Option<SpaceRole>) -> Self {
196 self.padding_role = role;
197 self
198 }
199
200 pub const fn background(mut self, paint: Paint) -> Self {
201 self.background = Some(paint);
202 self
203 }
204
205 pub const fn background_if(mut self, paint: Option<Paint>) -> Self {
207 self.background = paint;
208 self
209 }
210
211 pub const fn shape_role(mut self, role: ShapeRole) -> Self {
213 self.shape_role = Some(role);
214 self
215 }
216
217 pub const fn corner_radius(mut self, dp: f32) -> Self {
219 self.corner_radius = Some(dp);
220 self
221 }
222
223 pub const fn border(mut self, width: f32, paint: Paint) -> Self {
224 self.border_width = Some(width);
225 self.border_color = Some(paint);
226 self
227 }
228
229 pub const fn elevation(mut self, dp: f32) -> Self {
231 self.elevation = Some(dp);
232 self
233 }
234
235 pub const fn observe_size(mut self, token: i64) -> Self {
238 self.observe_size = Some(token);
239 self
240 }
241
242 pub const fn motion(mut self, role: MotionRole) -> Self {
244 self.motion = Some(role);
245 self
246 }
247
248 pub const fn material(mut self, role: MaterialRole) -> Self {
250 self.material = Some(role);
251 self
252 }
253
254 pub const fn alpha(mut self, alpha: f32) -> Self {
256 self.alpha = Some(alpha);
257 self
258 }
259
260 pub fn graphics_layer(mut self, layer: crate::GraphicsLayer) -> Self {
264 if let crate::schema::Modifier::Transform {
265 a,
266 b,
267 c,
268 d,
269 e,
270 f,
271 origin_x,
272 origin_y,
273 } = layer.transform()
274 {
275 self.transform = Some([a, b, c, d, e, f, origin_x, origin_y]);
276 }
277 self.alpha = Some(layer.alpha);
278 self
279 }
280
281 pub const fn animated_alpha(mut self, value: crate::Animated<f32>) -> Self {
284 self.animated_alpha = Some(value);
285 self
286 }
287
288 pub const fn animated_background(mut self, value: crate::Animated<Paint>) -> Self {
291 self.animated_background = Some(value);
292 self
293 }
294
295 pub const fn animated_offset(mut self, value: crate::Animated<(f32, f32)>) -> Self {
298 self.animated_offset = Some(value);
299 self
300 }
301
302 pub const fn cursor(mut self, cursor: crate::Cursor) -> Self {
306 self.cursor = Some(cursor);
307 self
308 }
309
310 pub const fn user_select(mut self, user_select: crate::UserSelect) -> Self {
313 self.user_select = Some(user_select);
314 self
315 }
316}
317
318#[derive(Clone, Copy, PartialEq, Eq)]
321pub(crate) enum Mods {
322 Container,
324 Standard,
326 Chip,
328 Scaffold,
330 None,
332}
333
334pub(crate) type Attrs = Vec<(&'static str, OwnedAttr)>;
335
336fn float(value: Option<f32>) -> OwnedAttr {
337 value.map_or(OwnedAttr::None, |value| OwnedAttr::Float(f64::from(value)))
338}
339
340fn role(value: Option<impl Into<u16>>) -> OwnedAttr {
341 value.map_or(OwnedAttr::None, |value| {
342 OwnedAttr::Int(i64::from(value.into()))
343 })
344}
345
346fn paint(value: Option<Paint>) -> OwnedAttr {
347 value.map_or(OwnedAttr::None, |paint| {
348 OwnedAttr::Int(paint.to_bits() as i64)
349 })
350}
351
352pub(crate) fn role_or_zero(value: Option<impl Into<u16>>) -> OwnedAttr {
354 OwnedAttr::Int(value.map_or(0, |value| i64::from(value.into())))
355}
356
357pub(crate) fn dp_or_zero(value: Option<f32>) -> OwnedAttr {
359 OwnedAttr::Float(f64::from(value.unwrap_or(0.0)))
360}
361
362pub(crate) fn paint_or_zero(value: Option<Paint>) -> OwnedAttr {
364 OwnedAttr::Int(value.map_or(0, |paint| paint.to_bits() as i64))
365}
366
367pub(crate) fn optional_text(value: Option<String>) -> OwnedAttr {
368 value.map_or(OwnedAttr::None, OwnedAttr::Text)
369}
370
371pub(crate) fn push_modifiers(modifier: &Modifier, mods: Mods, out: &mut Attrs) {
373 let container = mods == Mods::Container;
374 let standard = container || mods == Mods::Standard;
375 let chip = standard || mods == Mods::Chip;
376 let scaffold = mods == Mods::Scaffold;
377 let mut push = |always: bool, name: &'static str, value: OwnedAttr| {
378 let set = !matches!(value, OwnedAttr::None | OwnedAttr::Bool(false));
379 if always || set {
380 out.push((name, value));
381 }
382 };
383 push(
384 container,
385 "observe_size",
386 modifier
387 .observe_size
388 .map_or(OwnedAttr::None, OwnedAttr::Int),
389 );
390 push(container, "motion", role(modifier.motion));
391 push(container || scaffold, "material", role(modifier.material));
392 push(chip, "weight", float(modifier.weight));
393 push(chip, "width", float(modifier.width));
394 push(chip, "height", float(modifier.height));
395 push(chip, "padding", float(modifier.padding));
396 push(chip, "padding_role", role(modifier.padding_role));
397 push(
399 standard || scaffold,
400 "background",
401 paint(if modifier.animated_background.is_some() {
402 None
403 } else {
404 modifier.background
405 }),
406 );
407 push(standard, "shape_role", role(modifier.shape_role));
408 push(standard, "corner_radius", float(modifier.corner_radius));
409 push(standard, "border_width", float(modifier.border_width));
410 push(standard, "border_color", paint(modifier.border_color));
411 push(standard, "elevation", float(modifier.elevation));
412 push(
413 chip,
414 "fill_max_width",
415 OwnedAttr::Bool(modifier.fill_max_width),
416 );
417 push(
418 standard,
419 "fill_max_height",
420 OwnedAttr::Bool(modifier.fill_max_height),
421 );
422 push(
425 false,
426 "alpha",
427 float(if modifier.animated_alpha.is_some() {
428 None
429 } else {
430 modifier.alpha
431 }),
432 );
433 push(
434 false,
435 "transform",
436 match (modifier.animated_offset, modifier.transform) {
437 (None, Some(values)) => OwnedAttr::Bytes(
438 values
439 .iter()
440 .flat_map(|value| value.to_le_bytes())
441 .collect(),
442 ),
443 _ => OwnedAttr::None,
444 },
445 );
446 push(
448 false,
449 "cursor",
450 match modifier.cursor {
451 Some(crate::Cursor::Auto) | None => OwnedAttr::None,
452 Some(cursor) => OwnedAttr::Int(i64::from(u16::from(cursor))),
453 },
454 );
455 push(
456 false,
457 "user_select",
458 match modifier.user_select {
459 Some(crate::UserSelect::Auto) | None => OwnedAttr::None,
460 Some(select) => OwnedAttr::Int(i64::from(u16::from(select))),
461 },
462 );
463 push(
464 false,
465 "animated_alpha",
466 modifier.animated_alpha.map_or(OwnedAttr::None, |value| {
467 OwnedAttr::Bytes(crate::animation::animated_bytes(
468 &[value.target.to_bits()],
469 &value.spec,
470 ))
471 }),
472 );
473 push(
474 false,
475 "animated_background",
476 modifier
477 .animated_background
478 .map_or(OwnedAttr::None, |value| {
479 let bits = value.target.to_bits();
480 OwnedAttr::Bytes(crate::animation::animated_bytes(
481 &[bits as u32, (bits >> 32) as u32],
482 &value.spec,
483 ))
484 }),
485 );
486 push(
487 false,
488 "animated_offset",
489 modifier.animated_offset.map_or(OwnedAttr::None, |value| {
490 OwnedAttr::Bytes(crate::animation::animated_bytes(
491 &[value.target.0.to_bits(), value.target.1.to_bits()],
492 &value.spec,
493 ))
494 }),
495 );
496}
497
498pub(crate) fn emit(
504 key: u64,
505 widget: WidgetKind,
506 fill: impl FnOnce(&mut Attrs),
507 listeners: Vec<(&'static str, EventCallback)>,
508 content: impl FnOnce(),
509) {
510 let _group = group_guard(key);
511 emit_node(widget, fill, listeners, content);
512}
513
514pub(crate) fn emit_node(
517 widget: WidgetKind,
518 fill: impl FnOnce(&mut Attrs),
519 listeners: Vec<(&'static str, EventCallback)>,
520 content: impl FnOnce(),
521) {
522 let node = with_composer_if_idle(|composer| {
523 if !composer.composing() {
524 return None;
525 }
526 let mut attrs = composer.take_attrs();
527 fill(&mut attrs);
528 Some(composer.update_node(widget, attrs, listeners))
529 })
530 .flatten();
531 let Some(node) = node else {
532 return;
533 };
534 with_composer(|composer| composer.push_node_context(node));
535 let _context = NodeContextGuard;
536 content();
537}
538
539struct NodeContextGuard;
541
542impl Drop for NodeContextGuard {
543 fn drop(&mut self) {
544 with_composer_if_idle(|composer| composer.pop_node_context());
545 }
546}
547
548pub(crate) const fn widget_key(name: &str) -> u64 {
550 crate::runtime::composer::call_site(name, 0)
551}