1#![allow(non_snake_case)]
12
13use super::{
14 Attrs, Modifier, Mods, dp_or_zero, emit, optional_text, paint_or_zero, push_modifiers,
15 role_or_zero, widget_key,
16};
17use crate::drawing::DrawList;
18use crate::runtime::composer::{EventCallback, OwnedAttr};
19use crate::schema::{
20 Alignment, Arrangement, ButtonKind, ButtonVariant, ColorRole, IconRole, Paint, SlotRole,
21 SpaceRole, TextAlign, TextOverflow, TypeRole, WidgetKind,
22};
23use crate::spans::TextSpans;
24use crate::{FileDrop, KeyEvent, PointerEvent, PointerKinds};
25use std::cell::RefCell;
26use std::rc::Rc;
27
28fn unit(handler: Option<Box<dyn FnMut()>>) -> EventCallback {
31 match handler {
32 Some(handler) => EventCallback::Unit(Rc::new(RefCell::new(handler))),
33 None => EventCallback::Unit(Rc::new(RefCell::new(|| {}))),
34 }
35}
36
37fn text_handler(handler: Option<Box<dyn FnMut(String)>>) -> EventCallback {
38 match handler {
39 Some(handler) => EventCallback::Text(Rc::new(RefCell::new(handler))),
40 None => EventCallback::Text(Rc::new(RefCell::new(|_: String| {}))),
41 }
42}
43
44fn value_handler(handler: Option<Box<dyn FnMut(f64)>>) -> EventCallback {
45 match handler {
46 Some(handler) => EventCallback::Value(Rc::new(RefCell::new(handler))),
47 None => EventCallback::Value(Rc::new(RefCell::new(|_: f64| {}))),
48 }
49}
50
51struct PointerSubscription {
54 kinds: PointerKinds,
55 handler: Box<dyn FnMut(PointerEvent)>,
56}
57
58fn pointer_input(
61 subscription: Option<PointerSubscription>,
62 attrs: &mut Attrs,
63 listeners: &mut Vec<(&'static str, EventCallback)>,
64) {
65 let Some(subscription) = subscription else {
66 return;
67 };
68 if subscription.kinds == PointerKinds::NONE {
69 return;
70 }
71 attrs.push((
72 "pointer_kinds",
73 OwnedAttr::Int(i64::from(subscription.kinds.bits())),
74 ));
75 listeners.push((
76 "onpointer",
77 EventCallback::Pointer(Rc::new(RefCell::new(subscription.handler))),
78 ));
79}
80
81macro_rules! pointer_subscription {
83 ($name:ident) => {
84 impl $name {
85 pub fn on_pointer(
91 mut self,
92 kinds: PointerKinds,
93 handler: impl FnMut(PointerEvent) + 'static,
94 ) -> Self {
95 self.pointer = Some(PointerSubscription {
96 kinds,
97 handler: std::boxed::Box::new(handler),
98 });
99 self
100 }
101 }
102 };
103}
104
105macro_rules! composed_on_drop {
107 ($name:ident $(<$lifetime:lifetime>)?) => {
108 impl$(<$lifetime>)? Drop for $name$(<$lifetime>)? {
109 fn drop(&mut self) {
110 if !self.done && !std::thread::panicking() {
113 self.done = true;
114 self.compose(|| {});
115 }
116 }
117 }
118
119 impl$(<$lifetime>)? $name$(<$lifetime>)? {
120 pub fn modifier(mut self, modifier: Modifier) -> Self {
122 self.modifier = modifier;
123 self
124 }
125 }
126 };
127}
128
129macro_rules! container {
131 ($name:ident $(<$lifetime:lifetime>)?) => {
132 composed_on_drop!($name $(<$lifetime>)?);
133
134 impl$(<$lifetime>)? $name$(<$lifetime>)? {
135 pub fn content(mut self, content: impl FnOnce()) {
137 self.done = true;
138 self.compose(content);
139 }
140 }
141 };
142}
143
144#[derive(Default)]
148struct Linear {
149 arrangement: Option<Arrangement>,
150 spacing: Option<f32>,
151 space_role: Option<SpaceRole>,
152 alignment: Option<Alignment>,
153}
154
155impl Linear {
156 fn push(&self, attrs: &mut Attrs) {
157 attrs.push(("arrangement", role_or_zero(self.arrangement)));
158 attrs.push(("spacing", dp_or_zero(self.spacing)));
159 attrs.push(("space_role", role_or_zero(self.space_role)));
160 attrs.push(("alignment", role_or_zero(self.alignment)));
161 }
162}
163
164macro_rules! linear {
165 ($name:ident, $widget:ident, $key:literal) => {
166 #[doc = concat!("A `", stringify!($widget), "` and its builder.")]
167 pub struct $name {
168 modifier: Modifier,
169 linear: Linear,
170 pointer: Option<PointerSubscription>,
171 done: bool,
172 }
173
174 #[doc = concat!("Lays its children out one after another. Compose's `", stringify!($widget), "`.")]
175 pub fn $widget() -> $name {
176 $name {
177 modifier: Modifier::new(),
178 linear: Linear::default(),
179 pointer: None,
180 done: false,
181 }
182 }
183
184 impl $name {
185 pub fn arrangement(mut self, arrangement: Arrangement) -> Self {
187 self.linear.arrangement = Some(arrangement);
188 self
189 }
190
191 pub fn spacing(mut self, dp: f32) -> Self {
193 self.linear.spacing = Some(dp);
194 self
195 }
196
197 pub fn space_role(mut self, role: SpaceRole) -> Self {
199 self.linear.space_role = Some(role);
200 self
201 }
202
203 pub fn alignment(mut self, alignment: Alignment) -> Self {
205 self.linear.alignment = Some(alignment);
206 self
207 }
208
209 fn compose(&mut self, content: impl FnOnce()) {
210 let modifier = self.modifier;
211 let linear = std::mem::take(&mut self.linear);
212 let pointer = self.pointer.take();
213 let mut listeners = Vec::new();
214 let mut subscription = Attrs::new();
215 pointer_input(pointer, &mut subscription, &mut listeners);
216 emit(
217 widget_key($key),
218 WidgetKind::$widget,
219 |attrs| {
220 push_modifiers(&modifier, Mods::Container, attrs);
221 linear.push(attrs);
222 attrs.append(&mut subscription);
223 },
224 listeners,
225 content,
226 );
227 }
228 }
229
230 container!($name);
231 pointer_subscription!($name);
232 };
233}
234
235linear!(ColumnScope, Column, "compose_rust::ui::Column");
236linear!(RowScope, Row, "compose_rust::ui::Row");
237
238pub struct ComposeBox {
241 modifier: Modifier,
242 alignment: Option<Alignment>,
243 pointer: Option<PointerSubscription>,
244 done: bool,
245}
246
247pub fn Box() -> ComposeBox {
249 ComposeBox {
250 modifier: Modifier::new(),
251 alignment: None,
252 pointer: None,
253 done: false,
254 }
255}
256
257impl ComposeBox {
258 pub fn alignment(mut self, alignment: Alignment) -> Self {
260 self.alignment = Some(alignment);
261 self
262 }
263
264 fn compose(&mut self, content: impl FnOnce()) {
265 let (modifier, alignment) = (self.modifier, self.alignment);
266 let pointer = self.pointer.take();
267 let mut listeners = Vec::new();
268 let mut subscription = Attrs::new();
269 pointer_input(pointer, &mut subscription, &mut listeners);
270 emit(
271 widget_key("compose_rust::ui::Box"),
272 WidgetKind::Box,
273 |attrs| {
274 push_modifiers(&modifier, Mods::Container, attrs);
275 attrs.push(("alignment", role_or_zero(alignment)));
276 attrs.append(&mut subscription);
277 },
278 listeners,
279 content,
280 );
281 }
282}
283
284container!(ComposeBox);
285pointer_subscription!(ComposeBox);
286
287macro_rules! plain_container {
289 ($name:ident, $widget:ident, $mods:expr, $key:literal, $doc:literal) => {
290 #[doc = concat!("A `", stringify!($widget), "` and its builder.")]
291 pub struct $name {
292 modifier: Modifier,
293 pointer: Option<PointerSubscription>,
294 done: bool,
295 }
296
297 #[doc = $doc]
298 pub fn $widget() -> $name {
299 $name {
300 modifier: Modifier::new(),
301 pointer: None,
302 done: false,
303 }
304 }
305
306 impl $name {
307 fn compose(&mut self, content: impl FnOnce()) {
308 let modifier = self.modifier;
309 let pointer = self.pointer.take();
310 let mut listeners = Vec::new();
311 let mut subscription = Attrs::new();
312 pointer_input(pointer, &mut subscription, &mut listeners);
313 emit(
314 widget_key($key),
315 WidgetKind::$widget,
316 |attrs| {
317 push_modifiers(&modifier, $mods, attrs);
318 attrs.append(&mut subscription);
319 },
320 listeners,
321 content,
322 );
323 }
324 }
325
326 container!($name);
327 pointer_subscription!($name);
328 };
329}
330
331plain_container!(
332 ScrollColumnScope,
333 ScrollColumn,
334 Mods::Standard,
335 "compose_rust::ui::ScrollColumn",
336 "All of its content, with a vertical scroll the Renderer owns."
337);
338plain_container!(
339 ScrollRowScope,
340 ScrollRow,
341 Mods::Standard,
342 "compose_rust::ui::ScrollRow",
343 "All of its content, with a horizontal scroll the Renderer owns."
344);
345plain_container!(
346 CardScope,
347 Card,
348 Mods::Container,
349 "compose_rust::ui::Card",
350 "A grouped container. What it looks like is the design system's."
351);
352plain_container!(
353 SurfaceScope,
354 Surface,
355 Mods::Container,
356 "compose_rust::ui::Surface",
357 "A plain background and elevation, without a card's meaning."
358);
359plain_container!(
360 SelectionContainerScope,
361 SelectionContainer,
362 Mods::Standard,
363 "compose_rust::ui::SelectionContainer",
364 "Every `Text` inside can be selected and copied as one selection."
365);
366plain_container!(
367 SpacerScope,
368 Spacer,
369 Mods::Standard,
370 "compose_rust::ui::Spacer",
371 "Empty space, sized by its Modifier."
372);
373
374pub struct TextScope {
378 modifier: Modifier,
379 text: String,
380 spans: TextSpans,
381 type_role: Option<TypeRole>,
382 font_size: Option<f32>,
383 font_weight: Option<u16>,
384 line_height: Option<f32>,
385 letter_spacing: Option<f32>,
386 color: Option<Paint>,
387 text_align: Option<TextAlign>,
388 max_lines: Option<u32>,
389 overflow: Option<TextOverflow>,
390 pointer: Option<PointerSubscription>,
391 done: bool,
392}
393
394pub fn Text(text: impl Into<String>) -> TextScope {
399 TextScope {
400 modifier: Modifier::new(),
401 text: text.into(),
402 spans: TextSpans::default(),
403 type_role: None,
404 font_size: None,
405 font_weight: None,
406 line_height: None,
407 letter_spacing: None,
408 color: None,
409 text_align: None,
410 max_lines: None,
411 overflow: None,
412 pointer: None,
413 done: false,
414 }
415}
416
417impl TextScope {
418 pub fn spans(mut self, spans: TextSpans) -> Self {
420 self.spans = spans;
421 self
422 }
423
424 pub fn type_role(mut self, role: TypeRole) -> Self {
425 self.type_role = Some(role);
426 self
427 }
428
429 pub fn font_size(mut self, sp: f32) -> Self {
430 self.font_size = Some(sp);
431 self
432 }
433
434 pub fn font_weight(mut self, weight: u16) -> Self {
435 self.font_weight = Some(weight);
436 self
437 }
438
439 pub fn line_height(mut self, sp: f32) -> Self {
440 self.line_height = Some(sp);
441 self
442 }
443
444 pub fn letter_spacing(mut self, sp: f32) -> Self {
445 self.letter_spacing = Some(sp);
446 self
447 }
448
449 pub fn color(mut self, paint: Paint) -> Self {
450 self.color = Some(paint);
451 self
452 }
453
454 pub fn text_align(mut self, align: TextAlign) -> Self {
455 self.text_align = Some(align);
456 self
457 }
458
459 pub fn max_lines(mut self, lines: u32) -> Self {
460 self.max_lines = Some(lines);
461 self
462 }
463
464 pub fn overflow(mut self, overflow: TextOverflow) -> Self {
465 self.overflow = Some(overflow);
466 self
467 }
468
469 fn compose(&mut self, content: impl FnOnce()) {
470 let modifier = self.modifier;
471 let text = std::mem::take(&mut self.text);
472 let spans = std::mem::take(&mut self.spans);
473 let (type_role, font_size, font_weight, line_height, letter_spacing) = (
474 self.type_role,
475 self.font_size,
476 self.font_weight,
477 self.line_height,
478 self.letter_spacing,
479 );
480 let (color, text_align, max_lines, overflow) =
481 (self.color, self.text_align, self.max_lines, self.overflow);
482 let pointer = self.pointer.take();
483 let mut listeners = Vec::new();
484 let mut subscription = Attrs::new();
485 pointer_input(pointer, &mut subscription, &mut listeners);
486 emit(
487 widget_key("compose_rust::ui::Text"),
488 WidgetKind::Text,
489 |attrs| {
490 push_modifiers(&modifier, Mods::Standard, attrs);
491 attrs.push((
493 "spans",
494 if spans.is_empty() {
495 OwnedAttr::None
496 } else {
497 OwnedAttr::Spans(spans)
498 },
499 ));
500 attrs.push(("text", OwnedAttr::Text(text)));
501 attrs.push(("type_role", role_or_zero(type_role)));
502 attrs.push(("font_size", dp_or_zero(font_size)));
503 attrs.push((
504 "font_weight",
505 OwnedAttr::Int(i64::from(font_weight.unwrap_or(0))),
506 ));
507 attrs.push(("line_height", dp_or_zero(line_height)));
508 attrs.push(("letter_spacing", dp_or_zero(letter_spacing)));
509 attrs.push(("color", paint_or_zero(color)));
510 attrs.push(("text_align", role_or_zero(text_align)));
511 attrs.push((
512 "max_lines",
513 OwnedAttr::Int(i64::from(max_lines.unwrap_or(0))),
514 ));
515 attrs.push(("overflow", role_or_zero(overflow)));
516 attrs.append(&mut subscription);
517 },
518 listeners,
519 content,
520 );
521 }
522}
523
524composed_on_drop!(TextScope);
525pointer_subscription!(TextScope);
526
527pub struct TextFieldScope {
529 modifier: Modifier,
530 text: String,
531 placeholder: String,
532 enabled: bool,
533 multiline: bool,
534 type_role: Option<TypeRole>,
535 on_value_change: Option<Box<dyn FnMut(String)>>,
536 on_submit: Option<Box<dyn FnMut(String)>>,
537 on_focus_lost: Option<Box<dyn FnMut()>>,
538 on_key_down: Option<Box<dyn FnMut(KeyEvent)>>,
539 done: bool,
540}
541
542pub fn TextField() -> TextFieldScope {
546 TextFieldScope {
547 modifier: Modifier::new(),
548 text: String::new(),
549 placeholder: String::new(),
550 enabled: true,
551 multiline: false,
552 type_role: None,
553 on_value_change: None,
554 on_submit: None,
555 on_focus_lost: None,
556 on_key_down: None,
557 done: false,
558 }
559}
560
561impl TextFieldScope {
562 pub fn text(mut self, text: impl Into<String>) -> Self {
567 self.text = text.into();
568 self
569 }
570
571 pub fn placeholder(mut self, placeholder: impl Into<String>) -> Self {
572 self.placeholder = placeholder.into();
573 self
574 }
575
576 pub fn enabled(mut self, enabled: bool) -> Self {
577 self.enabled = enabled;
578 self
579 }
580
581 pub fn multiline(mut self, multiline: bool) -> Self {
582 self.multiline = multiline;
583 self
584 }
585
586 pub fn type_role(mut self, role: TypeRole) -> Self {
588 self.type_role = Some(role);
589 self
590 }
591
592 pub fn on_value_change(mut self, handler: impl FnMut(String) + 'static) -> Self {
593 self.on_value_change = Some(std::boxed::Box::new(handler));
594 self
595 }
596
597 pub fn on_submit(mut self, handler: impl FnMut(String) + 'static) -> Self {
598 self.on_submit = Some(std::boxed::Box::new(handler));
599 self
600 }
601
602 pub fn on_focus_lost(mut self, handler: impl FnMut() + 'static) -> Self {
603 self.on_focus_lost = Some(std::boxed::Box::new(handler));
604 self
605 }
606
607 pub fn on_key_down(mut self, handler: impl FnMut(KeyEvent) + 'static) -> Self {
610 self.on_key_down = Some(std::boxed::Box::new(handler));
611 self
612 }
613
614 fn compose(&mut self, content: impl FnOnce()) {
615 let modifier = self.modifier;
616 let text = std::mem::take(&mut self.text);
617 let placeholder = std::mem::take(&mut self.placeholder);
618 let (enabled, multiline, type_role) = (self.enabled, self.multiline, self.type_role);
619 let key_down: EventCallback = match self.on_key_down.take() {
620 Some(handler) => EventCallback::Key(Rc::new(RefCell::new(handler))),
621 None => EventCallback::Key(Rc::new(RefCell::new(|_: KeyEvent| {}))),
622 };
623 let listeners = vec![
624 ("onvaluechange", text_handler(self.on_value_change.take())),
625 ("onsubmit", text_handler(self.on_submit.take())),
626 ("onfocuslost", unit(self.on_focus_lost.take())),
627 ("onkeydown", key_down),
628 ];
629 emit(
630 widget_key("compose_rust::ui::TextField"),
631 WidgetKind::TextField,
632 |attrs| {
633 push_modifiers(&modifier, Mods::Standard, attrs);
634 if !text.is_empty() {
637 attrs.push(("text", OwnedAttr::Text(text)));
638 }
639 attrs.push(("placeholder", OwnedAttr::Text(placeholder)));
640 attrs.push(("enabled", OwnedAttr::Bool(enabled)));
641 attrs.push(("multiline", OwnedAttr::Bool(multiline)));
642 attrs.push(("type_role", role_or_zero(type_role)));
643 },
644 listeners,
645 content,
646 );
647 }
648}
649
650composed_on_drop!(TextFieldScope);
651
652pub struct ButtonScope {
656 modifier: Modifier,
657 text: String,
658 icon: Option<IconRole>,
659 enabled: bool,
660 variant: Option<ButtonVariant>,
661 kind: Option<ButtonKind>,
662 color: Option<Paint>,
663 on_click: Option<Box<dyn FnMut()>>,
664 done: bool,
665}
666
667pub fn Button(text: impl Into<String>) -> ButtonScope {
673 ButtonScope {
674 modifier: Modifier::new(),
675 text: text.into(),
676 icon: None,
677 enabled: true,
678 variant: None,
679 kind: None,
680 color: None,
681 on_click: None,
682 done: false,
683 }
684}
685
686impl ButtonScope {
687 pub fn icon(mut self, icon: IconRole) -> Self {
689 self.icon = Some(icon);
690 self
691 }
692
693 pub fn enabled(mut self, enabled: bool) -> Self {
694 self.enabled = enabled;
695 self
696 }
697
698 pub fn variant(mut self, variant: ButtonVariant) -> Self {
699 self.variant = Some(variant);
700 self
701 }
702
703 pub fn kind(mut self, kind: ButtonKind) -> Self {
706 self.kind = Some(kind);
707 self
708 }
709
710 pub fn color(mut self, paint: Paint) -> Self {
712 self.color = Some(paint);
713 self
714 }
715
716 pub fn color_if(mut self, paint: Option<Paint>) -> Self {
718 self.color = paint;
719 self
720 }
721
722 pub fn on_click(mut self, handler: impl FnMut() + 'static) -> Self {
723 self.on_click = Some(std::boxed::Box::new(handler));
724 self
725 }
726
727 fn compose(&mut self, content: impl FnOnce()) {
728 let modifier = self.modifier;
729 let text = std::mem::take(&mut self.text);
730 let (icon, enabled, variant, kind, color) =
731 (self.icon, self.enabled, self.variant, self.kind, self.color);
732 let listeners = vec![("onclick", unit(self.on_click.take()))];
733 emit(
734 widget_key("compose_rust::ui::Button"),
735 WidgetKind::Button,
736 |attrs| {
737 push_modifiers(&modifier, Mods::Standard, attrs);
738 attrs.push(("text", OwnedAttr::Text(text)));
739 attrs.push(("icon", super::role(icon)));
740 attrs.push(("enabled", OwnedAttr::Bool(enabled)));
741 attrs.push(("variant", role_or_zero(variant)));
742 attrs.push(("button_kind", role_or_zero(kind)));
743 attrs.push(("color", super::paint(color)));
744 },
745 listeners,
746 content,
747 );
748 }
749}
750
751composed_on_drop!(ButtonScope);
752
753macro_rules! toggle {
756 ($name:ident, $widget:ident, $setter:ident, $key:literal, $doc:literal) => {
757 #[doc = concat!("A `", stringify!($widget), "` and its builder.")]
758 pub struct $name {
759 modifier: Modifier,
760 checked: bool,
761 enabled: bool,
762 on_change: Option<Box<dyn FnMut(bool)>>,
763 done: bool,
764 }
765
766 #[doc = $doc]
767 pub fn $widget($setter: bool) -> $name {
768 $name {
769 modifier: Modifier::new(),
770 checked: $setter,
771 enabled: true,
772 on_change: None,
773 done: false,
774 }
775 }
776
777 impl $name {
778 pub fn enabled(mut self, enabled: bool) -> Self {
779 self.enabled = enabled;
780 self
781 }
782
783 pub fn on_change(mut self, handler: impl FnMut(bool) + 'static) -> Self {
786 self.on_change = Some(std::boxed::Box::new(handler));
787 self
788 }
789
790 fn compose(&mut self, content: impl FnOnce()) {
791 let modifier = self.modifier;
792 let (checked, enabled) = (self.checked, self.enabled);
793 let handler: EventCallback = match self.on_change.take() {
794 Some(mut handler) => EventCallback::Value(Rc::new(RefCell::new(
795 move |value: f64| handler(value != 0.0),
798 ))),
799 None => value_handler(None),
800 };
801 emit(
802 widget_key($key),
803 WidgetKind::$widget,
804 |attrs| {
805 push_modifiers(&modifier, Mods::Standard, attrs);
806 attrs.push(("checked", OwnedAttr::Bool(checked)));
807 attrs.push(("enabled", OwnedAttr::Bool(enabled)));
808 },
809 vec![("onchange", handler)],
810 content,
811 );
812 }
813 }
814
815 composed_on_drop!($name);
816 };
817}
818
819toggle!(
820 CheckboxScope,
821 Checkbox,
822 checked,
823 "compose_rust::ui::Checkbox",
824 "A box that is ticked or not. Compose's `Checkbox(checked, onCheckedChange)`."
825);
826toggle!(
827 RadioButtonScope,
828 RadioButton,
829 selected,
830 "compose_rust::ui::RadioButton",
831 "One choice out of several. Compose's `RadioButton(selected, onClick)`."
832);
833toggle!(
834 SwitchScope,
835 Switch,
836 checked,
837 "compose_rust::ui::Switch",
838 "An on and off control. Compose's `Switch(checked, onCheckedChange)`."
839);
840
841pub struct SliderScope {
843 modifier: Modifier,
844 color: Option<Paint>,
845 value: f32,
846 min: f32,
847 max: f32,
848 steps: u32,
849 enabled: bool,
850 on_change: Option<Box<dyn FnMut(f32)>>,
851 done: bool,
852}
853
854pub fn Slider(value: f32) -> SliderScope {
857 SliderScope {
858 modifier: Modifier::new(),
859 color: None,
860 value,
861 min: 0.0,
862 max: 1.0,
863 steps: 0,
864 enabled: true,
865 on_change: None,
866 done: false,
867 }
868}
869
870impl SliderScope {
871 pub fn range(mut self, min: f32, max: f32) -> Self {
873 self.min = min;
874 self.max = max;
875 self
876 }
877
878 pub fn steps(mut self, steps: u32) -> Self {
880 self.steps = steps;
881 self
882 }
883
884 pub fn enabled(mut self, enabled: bool) -> Self {
885 self.enabled = enabled;
886 self
887 }
888
889 pub fn color(mut self, paint: Paint) -> Self {
891 self.color = Some(paint);
892 self
893 }
894
895 pub fn on_change(mut self, handler: impl FnMut(f32) + 'static) -> Self {
896 self.on_change = Some(std::boxed::Box::new(handler));
897 self
898 }
899
900 fn compose(&mut self, content: impl FnOnce()) {
901 let modifier = self.modifier;
902 let (color, value, min, max, steps, enabled) = (
903 self.color,
904 self.value,
905 self.min,
906 self.max,
907 self.steps,
908 self.enabled,
909 );
910 let handler = match self.on_change.take() {
911 Some(mut handler) => EventCallback::Value(Rc::new(RefCell::new(move |value: f64| {
912 handler(value as f32)
913 }))),
914 None => value_handler(None),
915 };
916 emit(
917 widget_key("compose_rust::ui::Slider"),
918 WidgetKind::Slider,
919 |attrs| {
920 push_modifiers(&modifier, Mods::Standard, attrs);
921 attrs.push(("color", super::paint(color)));
922 attrs.push(("value", OwnedAttr::Float(f64::from(value))));
923 attrs.push(("min", OwnedAttr::Float(f64::from(min))));
924 attrs.push(("max", OwnedAttr::Float(f64::from(max))));
925 attrs.push(("steps", OwnedAttr::Int(i64::from(steps))));
926 attrs.push(("enabled", OwnedAttr::Bool(enabled)));
927 },
928 vec![("onchange", handler)],
929 content,
930 );
931 }
932}
933
934composed_on_drop!(SliderScope);
935
936pub struct ProgressIndicatorScope {
938 modifier: Modifier,
939 value: f32,
940 determinate: bool,
941 circular: bool,
942 done: bool,
943}
944
945pub fn ProgressIndicator(value: f32) -> ProgressIndicatorScope {
947 ProgressIndicatorScope {
948 modifier: Modifier::new(),
949 value,
950 determinate: true,
951 circular: false,
952 done: false,
953 }
954}
955
956pub fn IndeterminateProgressIndicator() -> ProgressIndicatorScope {
958 ProgressIndicator(0.0).determinate(false)
959}
960
961impl ProgressIndicatorScope {
962 pub fn determinate(mut self, determinate: bool) -> Self {
963 self.determinate = determinate;
964 self
965 }
966
967 pub fn circular(mut self, circular: bool) -> Self {
969 self.circular = circular;
970 self
971 }
972
973 fn compose(&mut self, content: impl FnOnce()) {
974 let modifier = self.modifier;
975 let (value, determinate, circular) = (self.value, self.determinate, self.circular);
976 emit(
977 widget_key("compose_rust::ui::ProgressIndicator"),
978 WidgetKind::ProgressIndicator,
979 |attrs| {
980 push_modifiers(&modifier, Mods::Standard, attrs);
981 attrs.push(("value", OwnedAttr::Float(f64::from(value))));
982 attrs.push(("determinate", OwnedAttr::Bool(determinate)));
983 attrs.push(("circular", OwnedAttr::Bool(circular)));
984 },
985 Vec::new(),
986 content,
987 );
988 }
989}
990
991composed_on_drop!(ProgressIndicatorScope);
992
993pub struct DividerScope {
995 modifier: Modifier,
996 vertical: bool,
997 done: bool,
998}
999
1000pub fn Divider() -> DividerScope {
1002 DividerScope {
1003 modifier: Modifier::new(),
1004 vertical: false,
1005 done: false,
1006 }
1007}
1008
1009pub fn Separator(color: Option<Paint>) -> DividerScope {
1012 Divider().modifier(Modifier::new().background_if(color))
1013}
1014
1015impl DividerScope {
1016 pub fn vertical(mut self, vertical: bool) -> Self {
1017 self.vertical = vertical;
1018 self
1019 }
1020
1021 fn compose(&mut self, content: impl FnOnce()) {
1022 let modifier = self.modifier;
1023 let vertical = self.vertical;
1024 emit(
1025 widget_key("compose_rust::ui::Divider"),
1026 WidgetKind::Divider,
1027 |attrs| {
1028 push_modifiers(&modifier, Mods::Standard, attrs);
1029 attrs.push(("vertical", OwnedAttr::Bool(vertical)));
1030 },
1031 Vec::new(),
1032 content,
1033 );
1034 }
1035}
1036
1037composed_on_drop!(DividerScope);
1038
1039pub struct AssetScope {
1043 modifier: Modifier,
1044 widget: WidgetKind,
1045 asset_id: u32,
1046 color: Option<Paint>,
1047 done: bool,
1048}
1049
1050pub fn Image(asset_id: u32) -> AssetScope {
1052 AssetScope {
1053 modifier: Modifier::new(),
1054 widget: WidgetKind::Image,
1055 asset_id,
1056 color: None,
1057 done: false,
1058 }
1059}
1060
1061pub fn Icon(asset_id: u32) -> AssetScope {
1063 AssetScope {
1064 modifier: Modifier::new(),
1065 widget: WidgetKind::Icon,
1066 asset_id,
1067 color: None,
1068 done: false,
1069 }
1070}
1071
1072impl AssetScope {
1073 pub fn color(mut self, paint: Paint) -> Self {
1075 self.color = Some(paint);
1076 self
1077 }
1078
1079 fn compose(&mut self, content: impl FnOnce()) {
1080 let modifier = self.modifier;
1081 let (widget, asset_id, color) = (self.widget, self.asset_id, self.color);
1082 let key = if widget == WidgetKind::Icon {
1083 widget_key("compose_rust::ui::Icon")
1084 } else {
1085 widget_key("compose_rust::ui::Image")
1086 };
1087 emit(
1088 key,
1089 widget,
1090 |attrs| {
1091 push_modifiers(&modifier, Mods::Standard, attrs);
1092 attrs.push(("asset", OwnedAttr::Int(i64::from(asset_id))));
1093 if widget == WidgetKind::Icon {
1094 attrs.push(("color", paint_or_zero(color)));
1095 }
1096 },
1097 Vec::new(),
1098 content,
1099 );
1100 }
1101}
1102
1103composed_on_drop!(AssetScope);
1104
1105pub struct CanvasScope {
1107 modifier: Modifier,
1108 commands: DrawList,
1109 done: bool,
1110}
1111
1112pub fn Canvas(commands: DrawList) -> CanvasScope {
1116 CanvasScope {
1117 modifier: Modifier::new(),
1118 commands,
1119 done: false,
1120 }
1121}
1122
1123impl CanvasScope {
1124 fn compose(&mut self, content: impl FnOnce()) {
1125 let modifier = self.modifier;
1126 let commands = std::mem::take(&mut self.commands);
1127 emit(
1128 widget_key("compose_rust::ui::Canvas"),
1129 WidgetKind::Canvas,
1130 |attrs| {
1131 push_modifiers(&modifier, Mods::Standard, attrs);
1132 attrs.push(("commands", OwnedAttr::Draw(commands)));
1133 },
1134 Vec::new(),
1135 content,
1136 );
1137 }
1138}
1139
1140composed_on_drop!(CanvasScope);
1141
1142pub struct DatePickerScope {
1146 modifier: Modifier,
1147 value: i64,
1148 min: Option<i64>,
1149 max: Option<i64>,
1150 enabled: bool,
1151 on_change: Option<Box<dyn FnMut(i64)>>,
1152 done: bool,
1153}
1154
1155pub fn DatePicker(value: i64) -> DatePickerScope {
1157 DatePickerScope {
1158 modifier: Modifier::new(),
1159 value,
1160 min: None,
1161 max: None,
1162 enabled: true,
1163 on_change: None,
1164 done: false,
1165 }
1166}
1167
1168impl DatePickerScope {
1169 pub fn min(mut self, days: i64) -> Self {
1170 self.min = Some(days);
1171 self
1172 }
1173
1174 pub fn max(mut self, days: i64) -> Self {
1175 self.max = Some(days);
1176 self
1177 }
1178
1179 pub fn enabled(mut self, enabled: bool) -> Self {
1180 self.enabled = enabled;
1181 self
1182 }
1183
1184 pub fn on_change(mut self, handler: impl FnMut(i64) + 'static) -> Self {
1185 self.on_change = Some(std::boxed::Box::new(handler));
1186 self
1187 }
1188
1189 fn compose(&mut self, content: impl FnOnce()) {
1190 let modifier = self.modifier;
1191 let (value, min, max, enabled) = (self.value, self.min, self.max, self.enabled);
1192 let handler = match self.on_change.take() {
1193 Some(mut handler) => EventCallback::Value(Rc::new(RefCell::new(move |value: f64| {
1194 handler(value as i64)
1195 }))),
1196 None => value_handler(None),
1197 };
1198 emit(
1199 widget_key("compose_rust::ui::DatePicker"),
1200 WidgetKind::DatePicker,
1201 |attrs| {
1202 push_modifiers(&modifier, Mods::Standard, attrs);
1203 attrs.push(("value", OwnedAttr::Int(value)));
1204 attrs.push(("min", OwnedAttr::Int(min.unwrap_or(i64::MIN))));
1205 attrs.push(("max", OwnedAttr::Int(max.unwrap_or(i64::MAX))));
1206 attrs.push(("enabled", OwnedAttr::Bool(enabled)));
1207 },
1208 vec![("onchange", handler)],
1209 content,
1210 );
1211 }
1212}
1213
1214composed_on_drop!(DatePickerScope);
1215
1216const MINUTES_IN_A_DAY: u32 = 24 * 60;
1217
1218pub struct TimePickerScope {
1220 modifier: Modifier,
1221 value: u32,
1222 min: Option<u32>,
1223 max: Option<u32>,
1224 enabled: bool,
1225 on_change: Option<Box<dyn FnMut(u32)>>,
1226 done: bool,
1227}
1228
1229pub fn TimePicker(value: u32) -> TimePickerScope {
1231 TimePickerScope {
1232 modifier: Modifier::new(),
1233 value,
1234 min: None,
1235 max: None,
1236 enabled: true,
1237 on_change: None,
1238 done: false,
1239 }
1240}
1241
1242impl TimePickerScope {
1243 pub fn min(mut self, minutes: u32) -> Self {
1244 self.min = Some(minutes);
1245 self
1246 }
1247
1248 pub fn max(mut self, minutes: u32) -> Self {
1249 self.max = Some(minutes);
1250 self
1251 }
1252
1253 pub fn enabled(mut self, enabled: bool) -> Self {
1254 self.enabled = enabled;
1255 self
1256 }
1257
1258 pub fn on_change(mut self, handler: impl FnMut(u32) + 'static) -> Self {
1259 self.on_change = Some(std::boxed::Box::new(handler));
1260 self
1261 }
1262
1263 fn compose(&mut self, content: impl FnOnce()) {
1264 let modifier = self.modifier;
1265 let (value, min, max, enabled) = (self.value, self.min, self.max, self.enabled);
1266 let handler = match self.on_change.take() {
1267 Some(mut handler) => EventCallback::Value(Rc::new(RefCell::new(move |value: f64| {
1268 handler((value as i64).clamp(0, i64::from(MINUTES_IN_A_DAY - 1)) as u32)
1269 }))),
1270 None => value_handler(None),
1271 };
1272 emit(
1273 widget_key("compose_rust::ui::TimePicker"),
1274 WidgetKind::TimePicker,
1275 |attrs| {
1276 push_modifiers(&modifier, Mods::Standard, attrs);
1277 attrs.push(("value", OwnedAttr::Int(i64::from(value))));
1278 attrs.push(("min", OwnedAttr::Int(i64::from(min.unwrap_or(0)))));
1279 attrs.push((
1280 "max",
1281 OwnedAttr::Int(i64::from(max.unwrap_or(MINUTES_IN_A_DAY - 1))),
1282 ));
1283 attrs.push(("enabled", OwnedAttr::Bool(enabled)));
1284 },
1285 vec![("onchange", handler)],
1286 content,
1287 );
1288 }
1289}
1290
1291composed_on_drop!(TimePickerScope);
1292
1293pub struct DropdownScope {
1295 modifier: Modifier,
1296 selected_index: usize,
1297 enabled: bool,
1298 on_change: Option<Box<dyn FnMut(usize)>>,
1299 done: bool,
1300}
1301
1302pub fn Dropdown(selected_index: usize) -> DropdownScope {
1304 DropdownScope {
1305 modifier: Modifier::new(),
1306 selected_index,
1307 enabled: true,
1308 on_change: None,
1309 done: false,
1310 }
1311}
1312
1313impl DropdownScope {
1314 pub fn enabled(mut self, enabled: bool) -> Self {
1315 self.enabled = enabled;
1316 self
1317 }
1318
1319 pub fn on_change(mut self, handler: impl FnMut(usize) + 'static) -> Self {
1320 self.on_change = Some(std::boxed::Box::new(handler));
1321 self
1322 }
1323
1324 fn compose(&mut self, content: impl FnOnce()) {
1325 let modifier = self.modifier;
1326 let (selected_index, enabled) = (self.selected_index, self.enabled);
1327 let handler = match self.on_change.take() {
1328 Some(mut handler) => EventCallback::Value(Rc::new(RefCell::new(move |value: f64| {
1329 handler((value as i64).max(0) as usize)
1330 }))),
1331 None => value_handler(None),
1332 };
1333 emit(
1334 widget_key("compose_rust::ui::Dropdown"),
1335 WidgetKind::Dropdown,
1336 |attrs| {
1337 push_modifiers(&modifier, Mods::Standard, attrs);
1338 attrs.push(("selected_index", OwnedAttr::Int(selected_index as i64)));
1339 attrs.push(("enabled", OwnedAttr::Bool(enabled)));
1340 },
1341 vec![("onchange", handler)],
1342 content,
1343 );
1344 }
1345}
1346
1347container!(DropdownScope);
1348
1349pub struct ModalScope {
1354 modifier: Modifier,
1355 widget: WidgetKind,
1356 open: bool,
1357 on_dismiss: Option<Box<dyn FnMut()>>,
1358 done: bool,
1359}
1360
1361pub fn Dialog(open: bool) -> ModalScope {
1364 ModalScope {
1365 modifier: Modifier::new(),
1366 widget: WidgetKind::Dialog,
1367 open,
1368 on_dismiss: None,
1369 done: false,
1370 }
1371}
1372
1373pub fn Sheet(open: bool) -> ModalScope {
1376 ModalScope {
1377 modifier: Modifier::new(),
1378 widget: WidgetKind::Sheet,
1379 open,
1380 on_dismiss: None,
1381 done: false,
1382 }
1383}
1384
1385impl ModalScope {
1386 pub fn on_dismiss(mut self, handler: impl FnMut() + 'static) -> Self {
1387 self.on_dismiss = Some(std::boxed::Box::new(handler));
1388 self
1389 }
1390
1391 fn compose(&mut self, content: impl FnOnce()) {
1392 let modifier = self.modifier;
1393 let (widget, open) = (self.widget, self.open);
1394 let key = if widget == WidgetKind::Dialog {
1395 widget_key("compose_rust::ui::Dialog")
1396 } else {
1397 widget_key("compose_rust::ui::Sheet")
1398 };
1399 emit(
1400 key,
1401 widget,
1402 |attrs| {
1403 push_modifiers(&modifier, Mods::Standard, attrs);
1404 attrs.push(("open", OwnedAttr::Bool(open)));
1405 },
1406 vec![("ondismiss", unit(self.on_dismiss.take()))],
1407 content,
1408 );
1409 }
1410}
1411
1412container!(ModalScope);
1413
1414pub struct MenuScope<'a> {
1416 modifier: Modifier,
1417 expanded: bool,
1418 on_dismiss: Option<Box<dyn FnMut()>>,
1419 anchor: Option<Box<dyn FnOnce() + 'a>>,
1420 done: bool,
1421}
1422
1423pub fn Menu<'a>(expanded: bool) -> MenuScope<'a> {
1426 MenuScope {
1427 modifier: Modifier::new(),
1428 expanded,
1429 on_dismiss: None,
1430 anchor: None,
1431 done: false,
1432 }
1433}
1434
1435impl<'a> MenuScope<'a> {
1436 pub fn on_dismiss(mut self, handler: impl FnMut() + 'static) -> Self {
1437 self.on_dismiss = Some(std::boxed::Box::new(handler));
1438 self
1439 }
1440
1441 pub fn anchor(mut self, anchor: impl FnOnce() + 'a) -> Self {
1443 self.anchor = Some(std::boxed::Box::new(anchor));
1444 self
1445 }
1446
1447 fn compose(&mut self, content: impl FnOnce()) {
1448 let modifier = self.modifier;
1449 let expanded = self.expanded;
1450 let anchor = self.anchor.take();
1451 emit(
1452 widget_key("compose_rust::ui::Menu"),
1453 WidgetKind::Menu,
1454 |attrs| {
1455 push_modifiers(&modifier, Mods::Standard, attrs);
1456 attrs.push(("open", OwnedAttr::Bool(expanded)));
1457 },
1458 vec![("ondismiss", unit(self.on_dismiss.take()))],
1459 || {
1460 if let Some(anchor) = anchor {
1461 let _anchor =
1462 crate::runtime::group_guard(widget_key("compose_rust::ui::Menu::anchor"));
1463 anchor();
1464 }
1465 let _entries =
1466 crate::runtime::group_guard(widget_key("compose_rust::ui::Menu::entries"));
1467 content();
1468 },
1469 );
1470 }
1471}
1472
1473container!(MenuScope<'a>);
1474
1475pub struct TabsScope {
1477 modifier: Modifier,
1478 color: Option<Paint>,
1479 selected_index: usize,
1480 done: bool,
1481}
1482
1483pub fn Tabs(selected_index: usize) -> TabsScope {
1486 TabsScope {
1487 modifier: Modifier::new(),
1488 color: None,
1489 selected_index,
1490 done: false,
1491 }
1492}
1493
1494impl TabsScope {
1495 pub fn color(mut self, paint: Paint) -> Self {
1497 self.color = Some(paint);
1498 self
1499 }
1500
1501 fn compose(&mut self, content: impl FnOnce()) {
1502 let modifier = self.modifier;
1503 let (color, selected_index) = (self.color, self.selected_index);
1504 emit(
1505 widget_key("compose_rust::ui::Tabs"),
1506 WidgetKind::Tabs,
1507 |attrs| {
1508 push_modifiers(&modifier, Mods::Standard, attrs);
1509 attrs.push(("color", super::paint(color)));
1510 attrs.push(("selected_index", OwnedAttr::Int(selected_index as i64)));
1511 },
1512 Vec::new(),
1513 content,
1514 );
1515 }
1516}
1517
1518container!(TabsScope);
1519
1520pub struct TopAppBarScope {
1522 modifier: Modifier,
1523 title: String,
1524 done: bool,
1525}
1526
1527pub fn TopAppBar() -> TopAppBarScope {
1530 TopAppBarScope {
1531 modifier: Modifier::new(),
1532 title: String::new(),
1533 done: false,
1534 }
1535}
1536
1537impl TopAppBarScope {
1538 pub fn title(mut self, title: impl Into<String>) -> Self {
1539 self.title = title.into();
1540 self
1541 }
1542
1543 fn compose(&mut self, content: impl FnOnce()) {
1544 let modifier = self.modifier;
1545 let title = std::mem::take(&mut self.title);
1546 emit(
1547 widget_key("compose_rust::ui::TopAppBar"),
1548 WidgetKind::TopAppBar,
1549 |attrs| {
1550 push_modifiers(&modifier, Mods::Standard, attrs);
1551 attrs.push(("text", OwnedAttr::Text(title)));
1552 },
1553 Vec::new(),
1554 content,
1555 );
1556 }
1557}
1558
1559container!(TopAppBarScope);
1560
1561pub struct TooltipScope {
1563 modifier: Modifier,
1564 text: String,
1565 done: bool,
1566}
1567
1568pub fn Tooltip(text: impl Into<String>) -> TooltipScope {
1571 TooltipScope {
1572 modifier: Modifier::new(),
1573 text: text.into(),
1574 done: false,
1575 }
1576}
1577
1578impl TooltipScope {
1579 fn compose(&mut self, content: impl FnOnce()) {
1580 let modifier = self.modifier;
1581 let text = std::mem::take(&mut self.text);
1582 emit(
1583 widget_key("compose_rust::ui::Tooltip"),
1584 WidgetKind::Tooltip,
1585 |attrs| {
1586 push_modifiers(&modifier, Mods::Standard, attrs);
1587 attrs.push(("text", OwnedAttr::Text(text)));
1588 },
1589 Vec::new(),
1590 content,
1591 );
1592 }
1593}
1594
1595container!(TooltipScope);
1596
1597fn slot(role: SlotRole, key: u64, content: impl FnOnce()) {
1602 emit(
1603 key,
1604 WidgetKind::ScaffoldSlot,
1605 |attrs| attrs.push(("slot", OwnedAttr::Int(i64::from(u16::from(role))))),
1606 Vec::new(),
1607 content,
1608 );
1609}
1610
1611type Slot<'a> = Option<Box<dyn FnOnce() + 'a>>;
1612
1613pub struct ScaffoldScope<'a> {
1615 modifier: Modifier,
1616 top_bar: Slot<'a>,
1617 bottom_bar: Slot<'a>,
1618 floating_action: Slot<'a>,
1619 done: bool,
1620}
1621
1622pub fn Scaffold<'a>() -> ScaffoldScope<'a> {
1627 ScaffoldScope {
1628 modifier: Modifier::new(),
1629 top_bar: None,
1630 bottom_bar: None,
1631 floating_action: None,
1632 done: false,
1633 }
1634}
1635
1636impl<'a> ScaffoldScope<'a> {
1637 pub fn top_bar(mut self, bar: impl FnOnce() + 'a) -> Self {
1638 self.top_bar = Some(std::boxed::Box::new(bar));
1639 self
1640 }
1641
1642 pub fn bottom_bar(mut self, bar: impl FnOnce() + 'a) -> Self {
1643 self.bottom_bar = Some(std::boxed::Box::new(bar));
1644 self
1645 }
1646
1647 pub fn floating_action(mut self, action: impl FnOnce() + 'a) -> Self {
1648 self.floating_action = Some(std::boxed::Box::new(action));
1649 self
1650 }
1651
1652 fn compose(&mut self, content: impl FnOnce()) {
1653 let modifier = self.modifier;
1654 let (top_bar, bottom_bar, floating_action) = (
1655 self.top_bar.take(),
1656 self.bottom_bar.take(),
1657 self.floating_action.take(),
1658 );
1659 emit(
1660 widget_key("compose_rust::ui::Scaffold"),
1661 WidgetKind::Scaffold,
1662 |attrs| push_modifiers(&modifier, Mods::Scaffold, attrs),
1663 Vec::new(),
1664 || {
1665 if let Some(bar) = top_bar {
1666 slot(
1667 SlotRole::TopBar,
1668 widget_key("compose_rust::ui::Scaffold::top_bar"),
1669 bar,
1670 );
1671 }
1672 if let Some(bar) = bottom_bar {
1673 slot(
1674 SlotRole::BottomBar,
1675 widget_key("compose_rust::ui::Scaffold::bottom_bar"),
1676 bar,
1677 );
1678 }
1679 if let Some(action) = floating_action {
1680 slot(
1681 SlotRole::FloatingAction,
1682 widget_key("compose_rust::ui::Scaffold::floating_action"),
1683 action,
1684 );
1685 }
1686 slot(
1687 SlotRole::Content,
1688 widget_key("compose_rust::ui::Scaffold::content"),
1689 content,
1690 );
1691 },
1692 );
1693 }
1694}
1695
1696container!(ScaffoldScope<'a>);
1697
1698pub struct NavigationScope<'a> {
1700 modifier: Modifier,
1701 selected_index: usize,
1702 head: Slot<'a>,
1703 foot: Slot<'a>,
1704 done: bool,
1705}
1706
1707pub fn Navigation<'a>(selected_index: usize) -> NavigationScope<'a> {
1711 NavigationScope {
1712 modifier: Modifier::new(),
1713 selected_index,
1714 head: None,
1715 foot: None,
1716 done: false,
1717 }
1718}
1719
1720impl<'a> NavigationScope<'a> {
1721 pub fn head(mut self, head: impl FnOnce() + 'a) -> Self {
1723 self.head = Some(std::boxed::Box::new(head));
1724 self
1725 }
1726
1727 pub fn foot(mut self, foot: impl FnOnce() + 'a) -> Self {
1729 self.foot = Some(std::boxed::Box::new(foot));
1730 self
1731 }
1732
1733 fn compose(&mut self, content: impl FnOnce()) {
1734 let modifier = self.modifier;
1735 let selected_index = self.selected_index;
1736 let (head, foot) = (self.head.take(), self.foot.take());
1737 emit(
1738 widget_key("compose_rust::ui::Navigation"),
1739 WidgetKind::Navigation,
1740 |attrs| {
1741 push_modifiers(&modifier, Mods::Standard, attrs);
1742 attrs.push(("selected_index", OwnedAttr::Int(selected_index as i64)));
1743 },
1744 Vec::new(),
1745 || {
1746 if let Some(head) = head {
1747 slot(
1748 SlotRole::TopBar,
1749 widget_key("compose_rust::ui::Navigation::head"),
1750 head,
1751 );
1752 }
1753 if let Some(foot) = foot {
1754 slot(
1755 SlotRole::BottomBar,
1756 widget_key("compose_rust::ui::Navigation::foot"),
1757 foot,
1758 );
1759 }
1760 let _destinations = crate::runtime::group_guard(widget_key(
1761 "compose_rust::ui::Navigation::content",
1762 ));
1763 content();
1764 },
1765 );
1766 }
1767}
1768
1769container!(NavigationScope<'a>);
1770
1771pub struct NavigationItemScope {
1773 modifier: Modifier,
1774 text: String,
1775 icon: Option<IconRole>,
1776 color: Option<Paint>,
1777 enabled: bool,
1778 section: Option<String>,
1779 on_click: Option<Box<dyn FnMut()>>,
1780 done: bool,
1781}
1782
1783pub fn NavigationItem(text: impl Into<String>) -> NavigationItemScope {
1785 NavigationItemScope {
1786 modifier: Modifier::new(),
1787 text: text.into(),
1788 icon: None,
1789 color: None,
1790 enabled: true,
1791 section: None,
1792 on_click: None,
1793 done: false,
1794 }
1795}
1796
1797impl NavigationItemScope {
1798 pub fn icon(mut self, icon: IconRole) -> Self {
1799 self.icon = Some(icon);
1800 self
1801 }
1802
1803 pub fn color(mut self, paint: Paint) -> Self {
1804 self.color = Some(paint);
1805 self
1806 }
1807
1808 pub fn enabled(mut self, enabled: bool) -> Self {
1809 self.enabled = enabled;
1810 self
1811 }
1812
1813 pub fn section(mut self, section: impl Into<String>) -> Self {
1815 self.section = Some(section.into());
1816 self
1817 }
1818
1819 pub fn on_click(mut self, handler: impl FnMut() + 'static) -> Self {
1820 self.on_click = Some(std::boxed::Box::new(handler));
1821 self
1822 }
1823
1824 fn compose(&mut self, content: impl FnOnce()) {
1825 let modifier = self.modifier;
1826 let text = std::mem::take(&mut self.text);
1827 let section = self.section.take();
1828 let (icon, color, enabled) = (self.icon, self.color, self.enabled);
1829 emit(
1830 widget_key("compose_rust::ui::NavigationItem"),
1831 WidgetKind::NavigationItem,
1832 |attrs| {
1833 push_modifiers(&modifier, Mods::Standard, attrs);
1834 attrs.push(("text", OwnedAttr::Text(text)));
1835 attrs.push(("icon", super::role(icon)));
1836 attrs.push(("color", super::paint(color)));
1837 attrs.push(("enabled", OwnedAttr::Bool(enabled)));
1838 attrs.push(("section", optional_text(section)));
1839 },
1840 vec![("onclick", unit(self.on_click.take()))],
1841 content,
1842 );
1843 }
1844}
1845
1846composed_on_drop!(NavigationItemScope);
1847
1848pub struct FileDropTargetScope {
1852 modifier: Modifier,
1853 alignment: Option<Alignment>,
1854 on_files_entered: Option<Box<dyn FnMut()>>,
1855 on_files_dropped: Option<Box<dyn FnMut(FileDrop)>>,
1856 done: bool,
1857}
1858
1859pub fn FileDropTarget() -> FileDropTargetScope {
1861 FileDropTargetScope {
1862 modifier: Modifier::new(),
1863 alignment: None,
1864 on_files_entered: None,
1865 on_files_dropped: None,
1866 done: false,
1867 }
1868}
1869
1870impl FileDropTargetScope {
1871 pub fn alignment(mut self, alignment: Alignment) -> Self {
1872 self.alignment = Some(alignment);
1873 self
1874 }
1875
1876 pub fn on_files_entered(mut self, handler: impl FnMut() + 'static) -> Self {
1877 self.on_files_entered = Some(std::boxed::Box::new(handler));
1878 self
1879 }
1880
1881 pub fn on_files_dropped(mut self, handler: impl FnMut(FileDrop) + 'static) -> Self {
1882 self.on_files_dropped = Some(std::boxed::Box::new(handler));
1883 self
1884 }
1885
1886 fn compose(&mut self, content: impl FnOnce()) {
1887 let modifier = self.modifier;
1888 let alignment = self.alignment;
1889 let dropped: EventCallback = match self.on_files_dropped.take() {
1890 Some(handler) => EventCallback::Files(Rc::new(RefCell::new(handler))),
1891 None => EventCallback::Files(Rc::new(RefCell::new(|_: FileDrop| {}))),
1892 };
1893 emit(
1894 widget_key("compose_rust::ui::FileDropTarget"),
1895 WidgetKind::FileDropTarget,
1896 |attrs| {
1897 push_modifiers(&modifier, Mods::Standard, attrs);
1898 attrs.push(("alignment", super::role(alignment)));
1899 },
1900 vec![
1901 ("onfilesentered", unit(self.on_files_entered.take())),
1902 ("onfilesdropped", dropped),
1903 ],
1904 content,
1905 );
1906 }
1907}
1908
1909container!(FileDropTargetScope);
1910
1911pub struct ChipScope {
1913 modifier: Modifier,
1914 text: String,
1915 icon: Option<IconRole>,
1916 selected: bool,
1917 enabled: bool,
1918 on_click: Option<Box<dyn FnMut()>>,
1919 done: bool,
1920}
1921
1922pub fn Chip(text: impl Into<String>) -> ChipScope {
1924 ChipScope {
1925 modifier: Modifier::new(),
1926 text: text.into(),
1927 icon: None,
1928 selected: false,
1929 enabled: true,
1930 on_click: None,
1931 done: false,
1932 }
1933}
1934
1935impl ChipScope {
1936 pub fn icon(mut self, icon: IconRole) -> Self {
1937 self.icon = Some(icon);
1938 self
1939 }
1940
1941 pub fn selected(mut self, selected: bool) -> Self {
1942 self.selected = selected;
1943 self
1944 }
1945
1946 pub fn enabled(mut self, enabled: bool) -> Self {
1947 self.enabled = enabled;
1948 self
1949 }
1950
1951 pub fn on_click(mut self, handler: impl FnMut() + 'static) -> Self {
1952 self.on_click = Some(std::boxed::Box::new(handler));
1953 self
1954 }
1955
1956 fn compose(&mut self, content: impl FnOnce()) {
1957 let modifier = self.modifier;
1958 let text = std::mem::take(&mut self.text);
1959 let (icon, selected, enabled) = (self.icon, self.selected, self.enabled);
1960 emit(
1961 widget_key("compose_rust::ui::Chip"),
1962 WidgetKind::Chip,
1963 |attrs| {
1964 push_modifiers(&modifier, Mods::Chip, attrs);
1965 attrs.push(("text", OwnedAttr::Text(text)));
1966 attrs.push(("icon", super::role(icon)));
1967 attrs.push(("checked", OwnedAttr::Bool(selected)));
1968 attrs.push(("enabled", OwnedAttr::Bool(enabled)));
1969 },
1970 vec![("onclick", unit(self.on_click.take()))],
1971 content,
1972 );
1973 }
1974}
1975
1976composed_on_drop!(ChipScope);
1977
1978pub struct FloatingActionScope {
1980 modifier: Modifier,
1981 text: String,
1982 icon: Option<IconRole>,
1983 on_click: Option<Box<dyn FnMut()>>,
1984 done: bool,
1985}
1986
1987pub fn FloatingAction(text: impl Into<String>) -> FloatingActionScope {
1990 FloatingActionScope {
1991 modifier: Modifier::new(),
1992 text: text.into(),
1993 icon: None,
1994 on_click: None,
1995 done: false,
1996 }
1997}
1998
1999impl FloatingActionScope {
2000 pub fn icon(mut self, icon: IconRole) -> Self {
2001 self.icon = Some(icon);
2002 self
2003 }
2004
2005 pub fn on_click(mut self, handler: impl FnMut() + 'static) -> Self {
2006 self.on_click = Some(std::boxed::Box::new(handler));
2007 self
2008 }
2009
2010 fn compose(&mut self, content: impl FnOnce()) {
2011 let modifier = self.modifier;
2012 let text = std::mem::take(&mut self.text);
2013 let icon = self.icon;
2014 emit(
2015 widget_key("compose_rust::ui::FloatingAction"),
2016 WidgetKind::FloatingAction,
2017 |attrs| {
2018 push_modifiers(&modifier, Mods::None, attrs);
2019 attrs.push(("text", OwnedAttr::Text(text)));
2020 attrs.push(("icon", super::role(icon)));
2021 },
2022 vec![("onclick", unit(self.on_click.take()))],
2023 content,
2024 );
2025 }
2026}
2027
2028composed_on_drop!(FloatingActionScope);
2029
2030pub struct BadgeScope {
2032 modifier: Modifier,
2033 value: Option<u32>,
2034 text: Option<String>,
2035 color: Option<ColorRole>,
2036 done: bool,
2037}
2038
2039pub fn Badge() -> BadgeScope {
2041 BadgeScope {
2042 modifier: Modifier::new(),
2043 value: None,
2044 text: None,
2045 color: None,
2046 done: false,
2047 }
2048}
2049
2050impl BadgeScope {
2051 pub fn value(mut self, value: u32) -> Self {
2053 self.value = Some(value);
2054 self
2055 }
2056
2057 pub fn text(mut self, text: impl Into<String>) -> Self {
2059 self.text = Some(text.into());
2060 self
2061 }
2062
2063 pub fn color(mut self, role: ColorRole) -> Self {
2065 self.color = Some(role);
2066 self
2067 }
2068
2069 fn compose(&mut self, content: impl FnOnce()) {
2070 let modifier = self.modifier;
2071 let text = self.text.take();
2072 let (value, color) = (self.value, self.color);
2073 emit(
2074 widget_key("compose_rust::ui::Badge"),
2075 WidgetKind::Badge,
2076 |attrs| {
2077 push_modifiers(&modifier, Mods::Standard, attrs);
2078 attrs.push((
2079 "count",
2080 value.map_or(OwnedAttr::None, |value| OwnedAttr::Int(i64::from(value))),
2081 ));
2082 attrs.push(("text", optional_text(text)));
2083 attrs.push(("color", super::paint(color.map(Paint::Role))));
2084 },
2085 Vec::new(),
2086 content,
2087 );
2088 }
2089}
2090
2091container!(BadgeScope);
2092
2093pub struct SplitPaneScope {
2095 modifier: Modifier,
2096 value: Option<f32>,
2097 min: Option<f32>,
2098 max: Option<f32>,
2099 collapsible: bool,
2100 selected_index: usize,
2101 label: Option<String>,
2102 on_change: Option<Box<dyn FnMut(f32)>>,
2103 on_dismiss: Option<Box<dyn FnMut()>>,
2104 done: bool,
2105}
2106
2107pub fn SplitPane() -> SplitPaneScope {
2110 SplitPaneScope {
2111 modifier: Modifier::new(),
2112 value: None,
2113 min: None,
2114 max: None,
2115 collapsible: false,
2116 selected_index: 0,
2117 label: None,
2118 on_change: None,
2119 on_dismiss: None,
2120 done: false,
2121 }
2122}
2123
2124impl SplitPaneScope {
2125 pub fn value(mut self, dp: f32) -> Self {
2127 self.value = Some(dp);
2128 self
2129 }
2130
2131 pub fn min(mut self, dp: f32) -> Self {
2132 self.min = Some(dp);
2133 self
2134 }
2135
2136 pub fn max(mut self, dp: f32) -> Self {
2137 self.max = Some(dp);
2138 self
2139 }
2140
2141 pub fn collapsible(mut self, collapsible: bool) -> Self {
2142 self.collapsible = collapsible;
2143 self
2144 }
2145
2146 pub fn selected_index(mut self, index: usize) -> Self {
2148 self.selected_index = index;
2149 self
2150 }
2151
2152 pub fn label(mut self, label: impl Into<String>) -> Self {
2154 self.label = Some(label.into());
2155 self
2156 }
2157
2158 pub fn on_change(mut self, handler: impl FnMut(f32) + 'static) -> Self {
2159 self.on_change = Some(std::boxed::Box::new(handler));
2160 self
2161 }
2162
2163 pub fn on_dismiss(mut self, handler: impl FnMut() + 'static) -> Self {
2164 self.on_dismiss = Some(std::boxed::Box::new(handler));
2165 self
2166 }
2167
2168 fn compose(&mut self, content: impl FnOnce()) {
2169 let modifier = self.modifier;
2170 let label = self.label.take();
2171 let (value, min, max, collapsible, selected_index) = (
2172 self.value,
2173 self.min,
2174 self.max,
2175 self.collapsible,
2176 self.selected_index,
2177 );
2178 let change = match self.on_change.take() {
2179 Some(mut handler) => EventCallback::Value(Rc::new(RefCell::new(move |value: f64| {
2180 handler(value as f32)
2181 }))),
2182 None => value_handler(None),
2183 };
2184 let as_float = |value: Option<f32>| {
2185 value.map_or(OwnedAttr::None, |value| OwnedAttr::Float(f64::from(value)))
2186 };
2187 emit(
2188 widget_key("compose_rust::ui::SplitPane"),
2189 WidgetKind::SplitPane,
2190 |attrs| {
2191 push_modifiers(&modifier, Mods::Standard, attrs);
2192 attrs.push(("value", as_float(value)));
2193 attrs.push(("min", as_float(min)));
2194 attrs.push(("max", as_float(max)));
2195 attrs.push(("collapsible", OwnedAttr::Bool(collapsible)));
2196 attrs.push(("selected_index", OwnedAttr::Int(selected_index as i64)));
2197 attrs.push(("text", optional_text(label)));
2198 },
2199 vec![
2200 ("onchange", change),
2201 ("ondismiss", unit(self.on_dismiss.take())),
2202 ],
2203 content,
2204 );
2205 }
2206}
2207
2208container!(SplitPaneScope);
2209
2210pub struct LinearProgressIndicatorScope {
2212 modifier: Modifier,
2213 progress: f32,
2214 done: bool,
2215}
2216
2217pub fn LinearProgressIndicator(progress: f32) -> LinearProgressIndicatorScope {
2219 LinearProgressIndicatorScope {
2220 modifier: Modifier::new(),
2221 progress,
2222 done: false,
2223 }
2224}
2225
2226impl LinearProgressIndicatorScope {
2227 fn compose(&mut self, content: impl FnOnce()) {
2228 let modifier = self.modifier;
2229 let progress = self.progress;
2230 emit(
2231 widget_key("compose_rust::ui::LinearProgressIndicator"),
2232 WidgetKind::LinearProgressIndicator,
2233 |attrs| {
2234 push_modifiers(&modifier, Mods::None, attrs);
2235 attrs.push(("progress", OwnedAttr::Float(f64::from(progress))));
2236 },
2237 Vec::new(),
2238 content,
2239 );
2240 }
2241}
2242
2243composed_on_drop!(LinearProgressIndicatorScope);