#![allow(non_snake_case)]
use super::{
Attrs, Modifier, Mods, dp_or_zero, emit, optional_text, paint_or_zero, push_modifiers,
role_or_zero, widget_key,
};
use crate::drawing::DrawList;
use crate::runtime::composer::{EventCallback, OwnedAttr};
use crate::schema::{
Alignment, Arrangement, ButtonKind, ButtonVariant, ColorRole, IconRole, Paint, SlotRole,
SpaceRole, TextAlign, TextOverflow, TypeRole, WidgetKind,
};
use crate::spans::TextSpans;
use crate::{FileDrop, KeyEvent, PointerEvent, PointerKinds};
use std::cell::RefCell;
use std::rc::Rc;
fn unit(handler: Option<Box<dyn FnMut()>>) -> EventCallback {
match handler {
Some(handler) => EventCallback::Unit(Rc::new(RefCell::new(handler))),
None => EventCallback::Unit(Rc::new(RefCell::new(|| {}))),
}
}
fn text_handler(handler: Option<Box<dyn FnMut(String)>>) -> EventCallback {
match handler {
Some(handler) => EventCallback::Text(Rc::new(RefCell::new(handler))),
None => EventCallback::Text(Rc::new(RefCell::new(|_: String| {}))),
}
}
fn value_handler(handler: Option<Box<dyn FnMut(f64)>>) -> EventCallback {
match handler {
Some(handler) => EventCallback::Value(Rc::new(RefCell::new(handler))),
None => EventCallback::Value(Rc::new(RefCell::new(|_: f64| {}))),
}
}
struct PointerSubscription {
kinds: PointerKinds,
handler: Box<dyn FnMut(PointerEvent)>,
}
fn pointer_input(
subscription: Option<PointerSubscription>,
attrs: &mut Attrs,
listeners: &mut Vec<(&'static str, EventCallback)>,
) {
let Some(subscription) = subscription else {
return;
};
if subscription.kinds == PointerKinds::NONE {
return;
}
attrs.push((
"pointer_kinds",
OwnedAttr::Int(i64::from(subscription.kinds.bits())),
));
listeners.push((
"onpointer",
EventCallback::Pointer(Rc::new(RefCell::new(subscription.handler))),
));
}
macro_rules! pointer_subscription {
($name:ident) => {
impl $name {
pub fn on_pointer(
mut self,
kinds: PointerKinds,
handler: impl FnMut(PointerEvent) + 'static,
) -> Self {
self.pointer = Some(PointerSubscription {
kinds,
handler: std::boxed::Box::new(handler),
});
self
}
}
};
}
macro_rules! composed_on_drop {
($name:ident $(<$lifetime:lifetime>)?) => {
impl$(<$lifetime>)? Drop for $name$(<$lifetime>)? {
fn drop(&mut self) {
if !self.done && !std::thread::panicking() {
self.done = true;
self.compose(|| {});
}
}
}
impl$(<$lifetime>)? $name$(<$lifetime>)? {
pub fn modifier(mut self, modifier: Modifier) -> Self {
self.modifier = modifier;
self
}
}
};
}
macro_rules! container {
($name:ident $(<$lifetime:lifetime>)?) => {
composed_on_drop!($name $(<$lifetime>)?);
impl$(<$lifetime>)? $name$(<$lifetime>)? {
pub fn content(mut self, content: impl FnOnce()) {
self.done = true;
self.compose(content);
}
}
};
}
#[derive(Default)]
struct Linear {
arrangement: Option<Arrangement>,
spacing: Option<f32>,
space_role: Option<SpaceRole>,
alignment: Option<Alignment>,
}
impl Linear {
fn push(&self, attrs: &mut Attrs) {
attrs.push(("arrangement", role_or_zero(self.arrangement)));
attrs.push(("spacing", dp_or_zero(self.spacing)));
attrs.push(("space_role", role_or_zero(self.space_role)));
attrs.push(("alignment", role_or_zero(self.alignment)));
}
}
macro_rules! linear {
($name:ident, $widget:ident, $key:literal) => {
#[doc = concat!("A `", stringify!($widget), "` and its builder.")]
pub struct $name {
modifier: Modifier,
linear: Linear,
pointer: Option<PointerSubscription>,
done: bool,
}
#[doc = concat!("Lays its children out one after another. Compose's `", stringify!($widget), "`.")]
pub fn $widget() -> $name {
$name {
modifier: Modifier::new(),
linear: Linear::default(),
pointer: None,
done: false,
}
}
impl $name {
pub fn arrangement(mut self, arrangement: Arrangement) -> Self {
self.linear.arrangement = Some(arrangement);
self
}
pub fn spacing(mut self, dp: f32) -> Self {
self.linear.spacing = Some(dp);
self
}
pub fn space_role(mut self, role: SpaceRole) -> Self {
self.linear.space_role = Some(role);
self
}
pub fn alignment(mut self, alignment: Alignment) -> Self {
self.linear.alignment = Some(alignment);
self
}
fn compose(&mut self, content: impl FnOnce()) {
let modifier = self.modifier;
let linear = std::mem::take(&mut self.linear);
let pointer = self.pointer.take();
let mut listeners = Vec::new();
let mut subscription = Attrs::new();
pointer_input(pointer, &mut subscription, &mut listeners);
emit(
widget_key($key),
WidgetKind::$widget,
|attrs| {
push_modifiers(&modifier, Mods::Container, attrs);
linear.push(attrs);
attrs.append(&mut subscription);
},
listeners,
content,
);
}
}
container!($name);
pointer_subscription!($name);
};
}
linear!(ColumnScope, Column, "compose_rust::ui::Column");
linear!(RowScope, Row, "compose_rust::ui::Row");
pub struct ComposeBox {
modifier: Modifier,
alignment: Option<Alignment>,
pointer: Option<PointerSubscription>,
done: bool,
}
pub fn Box() -> ComposeBox {
ComposeBox {
modifier: Modifier::new(),
alignment: None,
pointer: None,
done: false,
}
}
impl ComposeBox {
pub fn alignment(mut self, alignment: Alignment) -> Self {
self.alignment = Some(alignment);
self
}
fn compose(&mut self, content: impl FnOnce()) {
let (modifier, alignment) = (self.modifier, self.alignment);
let pointer = self.pointer.take();
let mut listeners = Vec::new();
let mut subscription = Attrs::new();
pointer_input(pointer, &mut subscription, &mut listeners);
emit(
widget_key("compose_rust::ui::Box"),
WidgetKind::Box,
|attrs| {
push_modifiers(&modifier, Mods::Container, attrs);
attrs.push(("alignment", role_or_zero(alignment)));
attrs.append(&mut subscription);
},
listeners,
content,
);
}
}
container!(ComposeBox);
pointer_subscription!(ComposeBox);
macro_rules! plain_container {
($name:ident, $widget:ident, $mods:expr, $key:literal, $doc:literal) => {
#[doc = concat!("A `", stringify!($widget), "` and its builder.")]
pub struct $name {
modifier: Modifier,
pointer: Option<PointerSubscription>,
done: bool,
}
#[doc = $doc]
pub fn $widget() -> $name {
$name {
modifier: Modifier::new(),
pointer: None,
done: false,
}
}
impl $name {
fn compose(&mut self, content: impl FnOnce()) {
let modifier = self.modifier;
let pointer = self.pointer.take();
let mut listeners = Vec::new();
let mut subscription = Attrs::new();
pointer_input(pointer, &mut subscription, &mut listeners);
emit(
widget_key($key),
WidgetKind::$widget,
|attrs| {
push_modifiers(&modifier, $mods, attrs);
attrs.append(&mut subscription);
},
listeners,
content,
);
}
}
container!($name);
pointer_subscription!($name);
};
}
plain_container!(
ScrollColumnScope,
ScrollColumn,
Mods::Standard,
"compose_rust::ui::ScrollColumn",
"All of its content, with a vertical scroll the Renderer owns."
);
plain_container!(
ScrollRowScope,
ScrollRow,
Mods::Standard,
"compose_rust::ui::ScrollRow",
"All of its content, with a horizontal scroll the Renderer owns."
);
plain_container!(
CardScope,
Card,
Mods::Container,
"compose_rust::ui::Card",
"A grouped container. What it looks like is the design system's."
);
plain_container!(
SurfaceScope,
Surface,
Mods::Container,
"compose_rust::ui::Surface",
"A plain background and elevation, without a card's meaning."
);
plain_container!(
SelectionContainerScope,
SelectionContainer,
Mods::Standard,
"compose_rust::ui::SelectionContainer",
"Every `Text` inside can be selected and copied as one selection."
);
plain_container!(
SpacerScope,
Spacer,
Mods::Standard,
"compose_rust::ui::Spacer",
"Empty space, sized by its Modifier."
);
pub struct TextScope {
modifier: Modifier,
text: String,
spans: TextSpans,
type_role: Option<TypeRole>,
font_size: Option<f32>,
font_weight: Option<u16>,
line_height: Option<f32>,
letter_spacing: Option<f32>,
color: Option<Paint>,
text_align: Option<TextAlign>,
max_lines: Option<u32>,
overflow: Option<TextOverflow>,
pointer: Option<PointerSubscription>,
done: bool,
}
pub fn Text(text: impl Into<String>) -> TextScope {
TextScope {
modifier: Modifier::new(),
text: text.into(),
spans: TextSpans::default(),
type_role: None,
font_size: None,
font_weight: None,
line_height: None,
letter_spacing: None,
color: None,
text_align: None,
max_lines: None,
overflow: None,
pointer: None,
done: false,
}
}
impl TextScope {
pub fn spans(mut self, spans: TextSpans) -> Self {
self.spans = spans;
self
}
pub fn type_role(mut self, role: TypeRole) -> Self {
self.type_role = Some(role);
self
}
pub fn font_size(mut self, sp: f32) -> Self {
self.font_size = Some(sp);
self
}
pub fn font_weight(mut self, weight: u16) -> Self {
self.font_weight = Some(weight);
self
}
pub fn line_height(mut self, sp: f32) -> Self {
self.line_height = Some(sp);
self
}
pub fn letter_spacing(mut self, sp: f32) -> Self {
self.letter_spacing = Some(sp);
self
}
pub fn color(mut self, paint: Paint) -> Self {
self.color = Some(paint);
self
}
pub fn text_align(mut self, align: TextAlign) -> Self {
self.text_align = Some(align);
self
}
pub fn max_lines(mut self, lines: u32) -> Self {
self.max_lines = Some(lines);
self
}
pub fn overflow(mut self, overflow: TextOverflow) -> Self {
self.overflow = Some(overflow);
self
}
fn compose(&mut self, content: impl FnOnce()) {
let modifier = self.modifier;
let text = std::mem::take(&mut self.text);
let spans = std::mem::take(&mut self.spans);
let (type_role, font_size, font_weight, line_height, letter_spacing) = (
self.type_role,
self.font_size,
self.font_weight,
self.line_height,
self.letter_spacing,
);
let (color, text_align, max_lines, overflow) =
(self.color, self.text_align, self.max_lines, self.overflow);
let pointer = self.pointer.take();
let mut listeners = Vec::new();
let mut subscription = Attrs::new();
pointer_input(pointer, &mut subscription, &mut listeners);
emit(
widget_key("compose_rust::ui::Text"),
WidgetKind::Text,
|attrs| {
push_modifiers(&modifier, Mods::Standard, attrs);
attrs.push((
"spans",
if spans.is_empty() {
OwnedAttr::None
} else {
OwnedAttr::Spans(spans)
},
));
attrs.push(("text", OwnedAttr::Text(text)));
attrs.push(("type_role", role_or_zero(type_role)));
attrs.push(("font_size", dp_or_zero(font_size)));
attrs.push((
"font_weight",
OwnedAttr::Int(i64::from(font_weight.unwrap_or(0))),
));
attrs.push(("line_height", dp_or_zero(line_height)));
attrs.push(("letter_spacing", dp_or_zero(letter_spacing)));
attrs.push(("color", paint_or_zero(color)));
attrs.push(("text_align", role_or_zero(text_align)));
attrs.push((
"max_lines",
OwnedAttr::Int(i64::from(max_lines.unwrap_or(0))),
));
attrs.push(("overflow", role_or_zero(overflow)));
attrs.append(&mut subscription);
},
listeners,
content,
);
}
}
composed_on_drop!(TextScope);
pointer_subscription!(TextScope);
pub struct TextFieldScope {
modifier: Modifier,
text: String,
placeholder: String,
enabled: bool,
multiline: bool,
type_role: Option<TypeRole>,
on_value_change: Option<Box<dyn FnMut(String)>>,
on_submit: Option<Box<dyn FnMut(String)>>,
on_focus_lost: Option<Box<dyn FnMut()>>,
on_key_down: Option<Box<dyn FnMut(KeyEvent)>>,
done: bool,
}
pub fn TextField() -> TextFieldScope {
TextFieldScope {
modifier: Modifier::new(),
text: String::new(),
placeholder: String::new(),
enabled: true,
multiline: false,
type_role: None,
on_value_change: None,
on_submit: None,
on_focus_lost: None,
on_key_down: None,
done: false,
}
}
impl TextFieldScope {
pub fn text(mut self, text: impl Into<String>) -> Self {
self.text = text.into();
self
}
pub fn placeholder(mut self, placeholder: impl Into<String>) -> Self {
self.placeholder = placeholder.into();
self
}
pub fn enabled(mut self, enabled: bool) -> Self {
self.enabled = enabled;
self
}
pub fn multiline(mut self, multiline: bool) -> Self {
self.multiline = multiline;
self
}
pub fn type_role(mut self, role: TypeRole) -> Self {
self.type_role = Some(role);
self
}
pub fn on_value_change(mut self, handler: impl FnMut(String) + 'static) -> Self {
self.on_value_change = Some(std::boxed::Box::new(handler));
self
}
pub fn on_submit(mut self, handler: impl FnMut(String) + 'static) -> Self {
self.on_submit = Some(std::boxed::Box::new(handler));
self
}
pub fn on_focus_lost(mut self, handler: impl FnMut() + 'static) -> Self {
self.on_focus_lost = Some(std::boxed::Box::new(handler));
self
}
pub fn on_key_down(mut self, handler: impl FnMut(KeyEvent) + 'static) -> Self {
self.on_key_down = Some(std::boxed::Box::new(handler));
self
}
fn compose(&mut self, content: impl FnOnce()) {
let modifier = self.modifier;
let text = std::mem::take(&mut self.text);
let placeholder = std::mem::take(&mut self.placeholder);
let (enabled, multiline, type_role) = (self.enabled, self.multiline, self.type_role);
let key_down: EventCallback = match self.on_key_down.take() {
Some(handler) => EventCallback::Key(Rc::new(RefCell::new(handler))),
None => EventCallback::Key(Rc::new(RefCell::new(|_: KeyEvent| {}))),
};
let listeners = vec![
("onvaluechange", text_handler(self.on_value_change.take())),
("onsubmit", text_handler(self.on_submit.take())),
("onfocuslost", unit(self.on_focus_lost.take())),
("onkeydown", key_down),
];
emit(
widget_key("compose_rust::ui::TextField"),
WidgetKind::TextField,
|attrs| {
push_modifiers(&modifier, Mods::Standard, attrs);
if !text.is_empty() {
attrs.push(("text", OwnedAttr::Text(text)));
}
attrs.push(("placeholder", OwnedAttr::Text(placeholder)));
attrs.push(("enabled", OwnedAttr::Bool(enabled)));
attrs.push(("multiline", OwnedAttr::Bool(multiline)));
attrs.push(("type_role", role_or_zero(type_role)));
},
listeners,
content,
);
}
}
composed_on_drop!(TextFieldScope);
pub struct ButtonScope {
modifier: Modifier,
text: String,
icon: Option<IconRole>,
enabled: bool,
variant: Option<ButtonVariant>,
kind: Option<ButtonKind>,
color: Option<Paint>,
on_click: Option<Box<dyn FnMut()>>,
done: bool,
}
pub fn Button(text: impl Into<String>) -> ButtonScope {
ButtonScope {
modifier: Modifier::new(),
text: text.into(),
icon: None,
enabled: true,
variant: None,
kind: None,
color: None,
on_click: None,
done: false,
}
}
impl ButtonScope {
pub fn icon(mut self, icon: IconRole) -> Self {
self.icon = Some(icon);
self
}
pub fn enabled(mut self, enabled: bool) -> Self {
self.enabled = enabled;
self
}
pub fn variant(mut self, variant: ButtonVariant) -> Self {
self.variant = Some(variant);
self
}
pub fn kind(mut self, kind: ButtonKind) -> Self {
self.kind = Some(kind);
self
}
pub fn color(mut self, paint: Paint) -> Self {
self.color = Some(paint);
self
}
pub fn color_if(mut self, paint: Option<Paint>) -> Self {
self.color = paint;
self
}
pub fn on_click(mut self, handler: impl FnMut() + 'static) -> Self {
self.on_click = Some(std::boxed::Box::new(handler));
self
}
fn compose(&mut self, content: impl FnOnce()) {
let modifier = self.modifier;
let text = std::mem::take(&mut self.text);
let (icon, enabled, variant, kind, color) =
(self.icon, self.enabled, self.variant, self.kind, self.color);
let listeners = vec![("onclick", unit(self.on_click.take()))];
emit(
widget_key("compose_rust::ui::Button"),
WidgetKind::Button,
|attrs| {
push_modifiers(&modifier, Mods::Standard, attrs);
attrs.push(("text", OwnedAttr::Text(text)));
attrs.push(("icon", super::role(icon)));
attrs.push(("enabled", OwnedAttr::Bool(enabled)));
attrs.push(("variant", role_or_zero(variant)));
attrs.push(("button_kind", role_or_zero(kind)));
attrs.push(("color", super::paint(color)));
},
listeners,
content,
);
}
}
composed_on_drop!(ButtonScope);
macro_rules! toggle {
($name:ident, $widget:ident, $setter:ident, $key:literal, $doc:literal) => {
#[doc = concat!("A `", stringify!($widget), "` and its builder.")]
pub struct $name {
modifier: Modifier,
checked: bool,
enabled: bool,
on_change: Option<Box<dyn FnMut(bool)>>,
done: bool,
}
#[doc = $doc]
pub fn $widget($setter: bool) -> $name {
$name {
modifier: Modifier::new(),
checked: $setter,
enabled: true,
on_change: None,
done: false,
}
}
impl $name {
pub fn enabled(mut self, enabled: bool) -> Self {
self.enabled = enabled;
self
}
pub fn on_change(mut self, handler: impl FnMut(bool) + 'static) -> Self {
self.on_change = Some(std::boxed::Box::new(handler));
self
}
fn compose(&mut self, content: impl FnOnce()) {
let modifier = self.modifier;
let (checked, enabled) = (self.checked, self.enabled);
let handler: EventCallback = match self.on_change.take() {
Some(mut handler) => EventCallback::Value(Rc::new(RefCell::new(
move |value: f64| handler(value != 0.0),
))),
None => value_handler(None),
};
emit(
widget_key($key),
WidgetKind::$widget,
|attrs| {
push_modifiers(&modifier, Mods::Standard, attrs);
attrs.push(("checked", OwnedAttr::Bool(checked)));
attrs.push(("enabled", OwnedAttr::Bool(enabled)));
},
vec![("onchange", handler)],
content,
);
}
}
composed_on_drop!($name);
};
}
toggle!(
CheckboxScope,
Checkbox,
checked,
"compose_rust::ui::Checkbox",
"A box that is ticked or not. Compose's `Checkbox(checked, onCheckedChange)`."
);
toggle!(
RadioButtonScope,
RadioButton,
selected,
"compose_rust::ui::RadioButton",
"One choice out of several. Compose's `RadioButton(selected, onClick)`."
);
toggle!(
SwitchScope,
Switch,
checked,
"compose_rust::ui::Switch",
"An on and off control. Compose's `Switch(checked, onCheckedChange)`."
);
pub struct SliderScope {
modifier: Modifier,
color: Option<Paint>,
value: f32,
min: f32,
max: f32,
steps: u32,
enabled: bool,
on_change: Option<Box<dyn FnMut(f32)>>,
done: bool,
}
pub fn Slider(value: f32) -> SliderScope {
SliderScope {
modifier: Modifier::new(),
color: None,
value,
min: 0.0,
max: 1.0,
steps: 0,
enabled: true,
on_change: None,
done: false,
}
}
impl SliderScope {
pub fn range(mut self, min: f32, max: f32) -> Self {
self.min = min;
self.max = max;
self
}
pub fn steps(mut self, steps: u32) -> Self {
self.steps = steps;
self
}
pub fn enabled(mut self, enabled: bool) -> Self {
self.enabled = enabled;
self
}
pub fn color(mut self, paint: Paint) -> Self {
self.color = Some(paint);
self
}
pub fn on_change(mut self, handler: impl FnMut(f32) + 'static) -> Self {
self.on_change = Some(std::boxed::Box::new(handler));
self
}
fn compose(&mut self, content: impl FnOnce()) {
let modifier = self.modifier;
let (color, value, min, max, steps, enabled) = (
self.color,
self.value,
self.min,
self.max,
self.steps,
self.enabled,
);
let handler = match self.on_change.take() {
Some(mut handler) => EventCallback::Value(Rc::new(RefCell::new(move |value: f64| {
handler(value as f32)
}))),
None => value_handler(None),
};
emit(
widget_key("compose_rust::ui::Slider"),
WidgetKind::Slider,
|attrs| {
push_modifiers(&modifier, Mods::Standard, attrs);
attrs.push(("color", super::paint(color)));
attrs.push(("value", OwnedAttr::Float(f64::from(value))));
attrs.push(("min", OwnedAttr::Float(f64::from(min))));
attrs.push(("max", OwnedAttr::Float(f64::from(max))));
attrs.push(("steps", OwnedAttr::Int(i64::from(steps))));
attrs.push(("enabled", OwnedAttr::Bool(enabled)));
},
vec![("onchange", handler)],
content,
);
}
}
composed_on_drop!(SliderScope);
pub struct ProgressIndicatorScope {
modifier: Modifier,
value: f32,
determinate: bool,
circular: bool,
done: bool,
}
pub fn ProgressIndicator(value: f32) -> ProgressIndicatorScope {
ProgressIndicatorScope {
modifier: Modifier::new(),
value,
determinate: true,
circular: false,
done: false,
}
}
pub fn IndeterminateProgressIndicator() -> ProgressIndicatorScope {
ProgressIndicator(0.0).determinate(false)
}
impl ProgressIndicatorScope {
pub fn determinate(mut self, determinate: bool) -> Self {
self.determinate = determinate;
self
}
pub fn circular(mut self, circular: bool) -> Self {
self.circular = circular;
self
}
fn compose(&mut self, content: impl FnOnce()) {
let modifier = self.modifier;
let (value, determinate, circular) = (self.value, self.determinate, self.circular);
emit(
widget_key("compose_rust::ui::ProgressIndicator"),
WidgetKind::ProgressIndicator,
|attrs| {
push_modifiers(&modifier, Mods::Standard, attrs);
attrs.push(("value", OwnedAttr::Float(f64::from(value))));
attrs.push(("determinate", OwnedAttr::Bool(determinate)));
attrs.push(("circular", OwnedAttr::Bool(circular)));
},
Vec::new(),
content,
);
}
}
composed_on_drop!(ProgressIndicatorScope);
pub struct DividerScope {
modifier: Modifier,
vertical: bool,
done: bool,
}
pub fn Divider() -> DividerScope {
DividerScope {
modifier: Modifier::new(),
vertical: false,
done: false,
}
}
pub fn Separator(color: Option<Paint>) -> DividerScope {
Divider().modifier(Modifier::new().background_if(color))
}
impl DividerScope {
pub fn vertical(mut self, vertical: bool) -> Self {
self.vertical = vertical;
self
}
fn compose(&mut self, content: impl FnOnce()) {
let modifier = self.modifier;
let vertical = self.vertical;
emit(
widget_key("compose_rust::ui::Divider"),
WidgetKind::Divider,
|attrs| {
push_modifiers(&modifier, Mods::Standard, attrs);
attrs.push(("vertical", OwnedAttr::Bool(vertical)));
},
Vec::new(),
content,
);
}
}
composed_on_drop!(DividerScope);
pub struct AssetScope {
modifier: Modifier,
widget: WidgetKind,
asset_id: u32,
color: Option<Paint>,
done: bool,
}
pub fn Image(asset_id: u32) -> AssetScope {
AssetScope {
modifier: Modifier::new(),
widget: WidgetKind::Image,
asset_id,
color: None,
done: false,
}
}
pub fn Icon(asset_id: u32) -> AssetScope {
AssetScope {
modifier: Modifier::new(),
widget: WidgetKind::Icon,
asset_id,
color: None,
done: false,
}
}
impl AssetScope {
pub fn color(mut self, paint: Paint) -> Self {
self.color = Some(paint);
self
}
fn compose(&mut self, content: impl FnOnce()) {
let modifier = self.modifier;
let (widget, asset_id, color) = (self.widget, self.asset_id, self.color);
let key = if widget == WidgetKind::Icon {
widget_key("compose_rust::ui::Icon")
} else {
widget_key("compose_rust::ui::Image")
};
emit(
key,
widget,
|attrs| {
push_modifiers(&modifier, Mods::Standard, attrs);
attrs.push(("asset", OwnedAttr::Int(i64::from(asset_id))));
if widget == WidgetKind::Icon {
attrs.push(("color", paint_or_zero(color)));
}
},
Vec::new(),
content,
);
}
}
composed_on_drop!(AssetScope);
pub struct CanvasScope {
modifier: Modifier,
commands: DrawList,
done: bool,
}
pub fn Canvas(commands: DrawList) -> CanvasScope {
CanvasScope {
modifier: Modifier::new(),
commands,
done: false,
}
}
impl CanvasScope {
fn compose(&mut self, content: impl FnOnce()) {
let modifier = self.modifier;
let commands = std::mem::take(&mut self.commands);
emit(
widget_key("compose_rust::ui::Canvas"),
WidgetKind::Canvas,
|attrs| {
push_modifiers(&modifier, Mods::Standard, attrs);
attrs.push(("commands", OwnedAttr::Draw(commands)));
},
Vec::new(),
content,
);
}
}
composed_on_drop!(CanvasScope);
pub struct DatePickerScope {
modifier: Modifier,
value: i64,
min: Option<i64>,
max: Option<i64>,
enabled: bool,
on_change: Option<Box<dyn FnMut(i64)>>,
done: bool,
}
pub fn DatePicker(value: i64) -> DatePickerScope {
DatePickerScope {
modifier: Modifier::new(),
value,
min: None,
max: None,
enabled: true,
on_change: None,
done: false,
}
}
impl DatePickerScope {
pub fn min(mut self, days: i64) -> Self {
self.min = Some(days);
self
}
pub fn max(mut self, days: i64) -> Self {
self.max = Some(days);
self
}
pub fn enabled(mut self, enabled: bool) -> Self {
self.enabled = enabled;
self
}
pub fn on_change(mut self, handler: impl FnMut(i64) + 'static) -> Self {
self.on_change = Some(std::boxed::Box::new(handler));
self
}
fn compose(&mut self, content: impl FnOnce()) {
let modifier = self.modifier;
let (value, min, max, enabled) = (self.value, self.min, self.max, self.enabled);
let handler = match self.on_change.take() {
Some(mut handler) => EventCallback::Value(Rc::new(RefCell::new(move |value: f64| {
handler(value as i64)
}))),
None => value_handler(None),
};
emit(
widget_key("compose_rust::ui::DatePicker"),
WidgetKind::DatePicker,
|attrs| {
push_modifiers(&modifier, Mods::Standard, attrs);
attrs.push(("value", OwnedAttr::Int(value)));
attrs.push(("min", OwnedAttr::Int(min.unwrap_or(i64::MIN))));
attrs.push(("max", OwnedAttr::Int(max.unwrap_or(i64::MAX))));
attrs.push(("enabled", OwnedAttr::Bool(enabled)));
},
vec![("onchange", handler)],
content,
);
}
}
composed_on_drop!(DatePickerScope);
const MINUTES_IN_A_DAY: u32 = 24 * 60;
pub struct TimePickerScope {
modifier: Modifier,
value: u32,
min: Option<u32>,
max: Option<u32>,
enabled: bool,
on_change: Option<Box<dyn FnMut(u32)>>,
done: bool,
}
pub fn TimePicker(value: u32) -> TimePickerScope {
TimePickerScope {
modifier: Modifier::new(),
value,
min: None,
max: None,
enabled: true,
on_change: None,
done: false,
}
}
impl TimePickerScope {
pub fn min(mut self, minutes: u32) -> Self {
self.min = Some(minutes);
self
}
pub fn max(mut self, minutes: u32) -> Self {
self.max = Some(minutes);
self
}
pub fn enabled(mut self, enabled: bool) -> Self {
self.enabled = enabled;
self
}
pub fn on_change(mut self, handler: impl FnMut(u32) + 'static) -> Self {
self.on_change = Some(std::boxed::Box::new(handler));
self
}
fn compose(&mut self, content: impl FnOnce()) {
let modifier = self.modifier;
let (value, min, max, enabled) = (self.value, self.min, self.max, self.enabled);
let handler = match self.on_change.take() {
Some(mut handler) => EventCallback::Value(Rc::new(RefCell::new(move |value: f64| {
handler((value as i64).clamp(0, i64::from(MINUTES_IN_A_DAY - 1)) as u32)
}))),
None => value_handler(None),
};
emit(
widget_key("compose_rust::ui::TimePicker"),
WidgetKind::TimePicker,
|attrs| {
push_modifiers(&modifier, Mods::Standard, attrs);
attrs.push(("value", OwnedAttr::Int(i64::from(value))));
attrs.push(("min", OwnedAttr::Int(i64::from(min.unwrap_or(0)))));
attrs.push((
"max",
OwnedAttr::Int(i64::from(max.unwrap_or(MINUTES_IN_A_DAY - 1))),
));
attrs.push(("enabled", OwnedAttr::Bool(enabled)));
},
vec![("onchange", handler)],
content,
);
}
}
composed_on_drop!(TimePickerScope);
pub struct DropdownScope {
modifier: Modifier,
selected_index: usize,
enabled: bool,
on_change: Option<Box<dyn FnMut(usize)>>,
done: bool,
}
pub fn Dropdown(selected_index: usize) -> DropdownScope {
DropdownScope {
modifier: Modifier::new(),
selected_index,
enabled: true,
on_change: None,
done: false,
}
}
impl DropdownScope {
pub fn enabled(mut self, enabled: bool) -> Self {
self.enabled = enabled;
self
}
pub fn on_change(mut self, handler: impl FnMut(usize) + 'static) -> Self {
self.on_change = Some(std::boxed::Box::new(handler));
self
}
fn compose(&mut self, content: impl FnOnce()) {
let modifier = self.modifier;
let (selected_index, enabled) = (self.selected_index, self.enabled);
let handler = match self.on_change.take() {
Some(mut handler) => EventCallback::Value(Rc::new(RefCell::new(move |value: f64| {
handler((value as i64).max(0) as usize)
}))),
None => value_handler(None),
};
emit(
widget_key("compose_rust::ui::Dropdown"),
WidgetKind::Dropdown,
|attrs| {
push_modifiers(&modifier, Mods::Standard, attrs);
attrs.push(("selected_index", OwnedAttr::Int(selected_index as i64)));
attrs.push(("enabled", OwnedAttr::Bool(enabled)));
},
vec![("onchange", handler)],
content,
);
}
}
container!(DropdownScope);
pub struct ModalScope {
modifier: Modifier,
widget: WidgetKind,
open: bool,
on_dismiss: Option<Box<dyn FnMut()>>,
done: bool,
}
pub fn Dialog(open: bool) -> ModalScope {
ModalScope {
modifier: Modifier::new(),
widget: WidgetKind::Dialog,
open,
on_dismiss: None,
done: false,
}
}
pub fn Sheet(open: bool) -> ModalScope {
ModalScope {
modifier: Modifier::new(),
widget: WidgetKind::Sheet,
open,
on_dismiss: None,
done: false,
}
}
impl ModalScope {
pub fn on_dismiss(mut self, handler: impl FnMut() + 'static) -> Self {
self.on_dismiss = Some(std::boxed::Box::new(handler));
self
}
fn compose(&mut self, content: impl FnOnce()) {
let modifier = self.modifier;
let (widget, open) = (self.widget, self.open);
let key = if widget == WidgetKind::Dialog {
widget_key("compose_rust::ui::Dialog")
} else {
widget_key("compose_rust::ui::Sheet")
};
emit(
key,
widget,
|attrs| {
push_modifiers(&modifier, Mods::Standard, attrs);
attrs.push(("open", OwnedAttr::Bool(open)));
},
vec![("ondismiss", unit(self.on_dismiss.take()))],
content,
);
}
}
container!(ModalScope);
pub struct MenuScope<'a> {
modifier: Modifier,
expanded: bool,
on_dismiss: Option<Box<dyn FnMut()>>,
anchor: Option<Box<dyn FnOnce() + 'a>>,
done: bool,
}
pub fn Menu<'a>(expanded: bool) -> MenuScope<'a> {
MenuScope {
modifier: Modifier::new(),
expanded,
on_dismiss: None,
anchor: None,
done: false,
}
}
impl<'a> MenuScope<'a> {
pub fn on_dismiss(mut self, handler: impl FnMut() + 'static) -> Self {
self.on_dismiss = Some(std::boxed::Box::new(handler));
self
}
pub fn anchor(mut self, anchor: impl FnOnce() + 'a) -> Self {
self.anchor = Some(std::boxed::Box::new(anchor));
self
}
fn compose(&mut self, content: impl FnOnce()) {
let modifier = self.modifier;
let expanded = self.expanded;
let anchor = self.anchor.take();
emit(
widget_key("compose_rust::ui::Menu"),
WidgetKind::Menu,
|attrs| {
push_modifiers(&modifier, Mods::Standard, attrs);
attrs.push(("open", OwnedAttr::Bool(expanded)));
},
vec![("ondismiss", unit(self.on_dismiss.take()))],
|| {
if let Some(anchor) = anchor {
let _anchor =
crate::runtime::group_guard(widget_key("compose_rust::ui::Menu::anchor"));
anchor();
}
let _entries =
crate::runtime::group_guard(widget_key("compose_rust::ui::Menu::entries"));
content();
},
);
}
}
container!(MenuScope<'a>);
pub struct TabsScope {
modifier: Modifier,
color: Option<Paint>,
selected_index: usize,
done: bool,
}
pub fn Tabs(selected_index: usize) -> TabsScope {
TabsScope {
modifier: Modifier::new(),
color: None,
selected_index,
done: false,
}
}
impl TabsScope {
pub fn color(mut self, paint: Paint) -> Self {
self.color = Some(paint);
self
}
fn compose(&mut self, content: impl FnOnce()) {
let modifier = self.modifier;
let (color, selected_index) = (self.color, self.selected_index);
emit(
widget_key("compose_rust::ui::Tabs"),
WidgetKind::Tabs,
|attrs| {
push_modifiers(&modifier, Mods::Standard, attrs);
attrs.push(("color", super::paint(color)));
attrs.push(("selected_index", OwnedAttr::Int(selected_index as i64)));
},
Vec::new(),
content,
);
}
}
container!(TabsScope);
pub struct TopAppBarScope {
modifier: Modifier,
title: String,
done: bool,
}
pub fn TopAppBar() -> TopAppBarScope {
TopAppBarScope {
modifier: Modifier::new(),
title: String::new(),
done: false,
}
}
impl TopAppBarScope {
pub fn title(mut self, title: impl Into<String>) -> Self {
self.title = title.into();
self
}
fn compose(&mut self, content: impl FnOnce()) {
let modifier = self.modifier;
let title = std::mem::take(&mut self.title);
emit(
widget_key("compose_rust::ui::TopAppBar"),
WidgetKind::TopAppBar,
|attrs| {
push_modifiers(&modifier, Mods::Standard, attrs);
attrs.push(("text", OwnedAttr::Text(title)));
},
Vec::new(),
content,
);
}
}
container!(TopAppBarScope);
pub struct TooltipScope {
modifier: Modifier,
text: String,
done: bool,
}
pub fn Tooltip(text: impl Into<String>) -> TooltipScope {
TooltipScope {
modifier: Modifier::new(),
text: text.into(),
done: false,
}
}
impl TooltipScope {
fn compose(&mut self, content: impl FnOnce()) {
let modifier = self.modifier;
let text = std::mem::take(&mut self.text);
emit(
widget_key("compose_rust::ui::Tooltip"),
WidgetKind::Tooltip,
|attrs| {
push_modifiers(&modifier, Mods::Standard, attrs);
attrs.push(("text", OwnedAttr::Text(text)));
},
Vec::new(),
content,
);
}
}
container!(TooltipScope);
fn slot(role: SlotRole, key: u64, content: impl FnOnce()) {
emit(
key,
WidgetKind::ScaffoldSlot,
|attrs| attrs.push(("slot", OwnedAttr::Int(i64::from(u16::from(role))))),
Vec::new(),
content,
);
}
type Slot<'a> = Option<Box<dyn FnOnce() + 'a>>;
pub struct ScaffoldScope<'a> {
modifier: Modifier,
top_bar: Slot<'a>,
bottom_bar: Slot<'a>,
floating_action: Slot<'a>,
done: bool,
}
pub fn Scaffold<'a>() -> ScaffoldScope<'a> {
ScaffoldScope {
modifier: Modifier::new(),
top_bar: None,
bottom_bar: None,
floating_action: None,
done: false,
}
}
impl<'a> ScaffoldScope<'a> {
pub fn top_bar(mut self, bar: impl FnOnce() + 'a) -> Self {
self.top_bar = Some(std::boxed::Box::new(bar));
self
}
pub fn bottom_bar(mut self, bar: impl FnOnce() + 'a) -> Self {
self.bottom_bar = Some(std::boxed::Box::new(bar));
self
}
pub fn floating_action(mut self, action: impl FnOnce() + 'a) -> Self {
self.floating_action = Some(std::boxed::Box::new(action));
self
}
fn compose(&mut self, content: impl FnOnce()) {
let modifier = self.modifier;
let (top_bar, bottom_bar, floating_action) = (
self.top_bar.take(),
self.bottom_bar.take(),
self.floating_action.take(),
);
emit(
widget_key("compose_rust::ui::Scaffold"),
WidgetKind::Scaffold,
|attrs| push_modifiers(&modifier, Mods::Scaffold, attrs),
Vec::new(),
|| {
if let Some(bar) = top_bar {
slot(
SlotRole::TopBar,
widget_key("compose_rust::ui::Scaffold::top_bar"),
bar,
);
}
if let Some(bar) = bottom_bar {
slot(
SlotRole::BottomBar,
widget_key("compose_rust::ui::Scaffold::bottom_bar"),
bar,
);
}
if let Some(action) = floating_action {
slot(
SlotRole::FloatingAction,
widget_key("compose_rust::ui::Scaffold::floating_action"),
action,
);
}
slot(
SlotRole::Content,
widget_key("compose_rust::ui::Scaffold::content"),
content,
);
},
);
}
}
container!(ScaffoldScope<'a>);
pub struct NavigationScope<'a> {
modifier: Modifier,
selected_index: usize,
head: Slot<'a>,
foot: Slot<'a>,
done: bool,
}
pub fn Navigation<'a>(selected_index: usize) -> NavigationScope<'a> {
NavigationScope {
modifier: Modifier::new(),
selected_index,
head: None,
foot: None,
done: false,
}
}
impl<'a> NavigationScope<'a> {
pub fn head(mut self, head: impl FnOnce() + 'a) -> Self {
self.head = Some(std::boxed::Box::new(head));
self
}
pub fn foot(mut self, foot: impl FnOnce() + 'a) -> Self {
self.foot = Some(std::boxed::Box::new(foot));
self
}
fn compose(&mut self, content: impl FnOnce()) {
let modifier = self.modifier;
let selected_index = self.selected_index;
let (head, foot) = (self.head.take(), self.foot.take());
emit(
widget_key("compose_rust::ui::Navigation"),
WidgetKind::Navigation,
|attrs| {
push_modifiers(&modifier, Mods::Standard, attrs);
attrs.push(("selected_index", OwnedAttr::Int(selected_index as i64)));
},
Vec::new(),
|| {
if let Some(head) = head {
slot(
SlotRole::TopBar,
widget_key("compose_rust::ui::Navigation::head"),
head,
);
}
if let Some(foot) = foot {
slot(
SlotRole::BottomBar,
widget_key("compose_rust::ui::Navigation::foot"),
foot,
);
}
let _destinations = crate::runtime::group_guard(widget_key(
"compose_rust::ui::Navigation::content",
));
content();
},
);
}
}
container!(NavigationScope<'a>);
pub struct NavigationItemScope {
modifier: Modifier,
text: String,
icon: Option<IconRole>,
color: Option<Paint>,
enabled: bool,
section: Option<String>,
on_click: Option<Box<dyn FnMut()>>,
done: bool,
}
pub fn NavigationItem(text: impl Into<String>) -> NavigationItemScope {
NavigationItemScope {
modifier: Modifier::new(),
text: text.into(),
icon: None,
color: None,
enabled: true,
section: None,
on_click: None,
done: false,
}
}
impl NavigationItemScope {
pub fn icon(mut self, icon: IconRole) -> Self {
self.icon = Some(icon);
self
}
pub fn color(mut self, paint: Paint) -> Self {
self.color = Some(paint);
self
}
pub fn enabled(mut self, enabled: bool) -> Self {
self.enabled = enabled;
self
}
pub fn section(mut self, section: impl Into<String>) -> Self {
self.section = Some(section.into());
self
}
pub fn on_click(mut self, handler: impl FnMut() + 'static) -> Self {
self.on_click = Some(std::boxed::Box::new(handler));
self
}
fn compose(&mut self, content: impl FnOnce()) {
let modifier = self.modifier;
let text = std::mem::take(&mut self.text);
let section = self.section.take();
let (icon, color, enabled) = (self.icon, self.color, self.enabled);
emit(
widget_key("compose_rust::ui::NavigationItem"),
WidgetKind::NavigationItem,
|attrs| {
push_modifiers(&modifier, Mods::Standard, attrs);
attrs.push(("text", OwnedAttr::Text(text)));
attrs.push(("icon", super::role(icon)));
attrs.push(("color", super::paint(color)));
attrs.push(("enabled", OwnedAttr::Bool(enabled)));
attrs.push(("section", optional_text(section)));
},
vec![("onclick", unit(self.on_click.take()))],
content,
);
}
}
composed_on_drop!(NavigationItemScope);
pub struct FileDropTargetScope {
modifier: Modifier,
alignment: Option<Alignment>,
on_files_entered: Option<Box<dyn FnMut()>>,
on_files_dropped: Option<Box<dyn FnMut(FileDrop)>>,
done: bool,
}
pub fn FileDropTarget() -> FileDropTargetScope {
FileDropTargetScope {
modifier: Modifier::new(),
alignment: None,
on_files_entered: None,
on_files_dropped: None,
done: false,
}
}
impl FileDropTargetScope {
pub fn alignment(mut self, alignment: Alignment) -> Self {
self.alignment = Some(alignment);
self
}
pub fn on_files_entered(mut self, handler: impl FnMut() + 'static) -> Self {
self.on_files_entered = Some(std::boxed::Box::new(handler));
self
}
pub fn on_files_dropped(mut self, handler: impl FnMut(FileDrop) + 'static) -> Self {
self.on_files_dropped = Some(std::boxed::Box::new(handler));
self
}
fn compose(&mut self, content: impl FnOnce()) {
let modifier = self.modifier;
let alignment = self.alignment;
let dropped: EventCallback = match self.on_files_dropped.take() {
Some(handler) => EventCallback::Files(Rc::new(RefCell::new(handler))),
None => EventCallback::Files(Rc::new(RefCell::new(|_: FileDrop| {}))),
};
emit(
widget_key("compose_rust::ui::FileDropTarget"),
WidgetKind::FileDropTarget,
|attrs| {
push_modifiers(&modifier, Mods::Standard, attrs);
attrs.push(("alignment", super::role(alignment)));
},
vec![
("onfilesentered", unit(self.on_files_entered.take())),
("onfilesdropped", dropped),
],
content,
);
}
}
container!(FileDropTargetScope);
pub struct ChipScope {
modifier: Modifier,
text: String,
icon: Option<IconRole>,
selected: bool,
enabled: bool,
on_click: Option<Box<dyn FnMut()>>,
done: bool,
}
pub fn Chip(text: impl Into<String>) -> ChipScope {
ChipScope {
modifier: Modifier::new(),
text: text.into(),
icon: None,
selected: false,
enabled: true,
on_click: None,
done: false,
}
}
impl ChipScope {
pub fn icon(mut self, icon: IconRole) -> Self {
self.icon = Some(icon);
self
}
pub fn selected(mut self, selected: bool) -> Self {
self.selected = selected;
self
}
pub fn enabled(mut self, enabled: bool) -> Self {
self.enabled = enabled;
self
}
pub fn on_click(mut self, handler: impl FnMut() + 'static) -> Self {
self.on_click = Some(std::boxed::Box::new(handler));
self
}
fn compose(&mut self, content: impl FnOnce()) {
let modifier = self.modifier;
let text = std::mem::take(&mut self.text);
let (icon, selected, enabled) = (self.icon, self.selected, self.enabled);
emit(
widget_key("compose_rust::ui::Chip"),
WidgetKind::Chip,
|attrs| {
push_modifiers(&modifier, Mods::Chip, attrs);
attrs.push(("text", OwnedAttr::Text(text)));
attrs.push(("icon", super::role(icon)));
attrs.push(("checked", OwnedAttr::Bool(selected)));
attrs.push(("enabled", OwnedAttr::Bool(enabled)));
},
vec![("onclick", unit(self.on_click.take()))],
content,
);
}
}
composed_on_drop!(ChipScope);
pub struct FloatingActionScope {
modifier: Modifier,
text: String,
icon: Option<IconRole>,
on_click: Option<Box<dyn FnMut()>>,
done: bool,
}
pub fn FloatingAction(text: impl Into<String>) -> FloatingActionScope {
FloatingActionScope {
modifier: Modifier::new(),
text: text.into(),
icon: None,
on_click: None,
done: false,
}
}
impl FloatingActionScope {
pub fn icon(mut self, icon: IconRole) -> Self {
self.icon = Some(icon);
self
}
pub fn on_click(mut self, handler: impl FnMut() + 'static) -> Self {
self.on_click = Some(std::boxed::Box::new(handler));
self
}
fn compose(&mut self, content: impl FnOnce()) {
let modifier = self.modifier;
let text = std::mem::take(&mut self.text);
let icon = self.icon;
emit(
widget_key("compose_rust::ui::FloatingAction"),
WidgetKind::FloatingAction,
|attrs| {
push_modifiers(&modifier, Mods::None, attrs);
attrs.push(("text", OwnedAttr::Text(text)));
attrs.push(("icon", super::role(icon)));
},
vec![("onclick", unit(self.on_click.take()))],
content,
);
}
}
composed_on_drop!(FloatingActionScope);
pub struct BadgeScope {
modifier: Modifier,
value: Option<u32>,
text: Option<String>,
color: Option<ColorRole>,
done: bool,
}
pub fn Badge() -> BadgeScope {
BadgeScope {
modifier: Modifier::new(),
value: None,
text: None,
color: None,
done: false,
}
}
impl BadgeScope {
pub fn value(mut self, value: u32) -> Self {
self.value = Some(value);
self
}
pub fn text(mut self, text: impl Into<String>) -> Self {
self.text = Some(text.into());
self
}
pub fn color(mut self, role: ColorRole) -> Self {
self.color = Some(role);
self
}
fn compose(&mut self, content: impl FnOnce()) {
let modifier = self.modifier;
let text = self.text.take();
let (value, color) = (self.value, self.color);
emit(
widget_key("compose_rust::ui::Badge"),
WidgetKind::Badge,
|attrs| {
push_modifiers(&modifier, Mods::Standard, attrs);
attrs.push((
"count",
value.map_or(OwnedAttr::None, |value| OwnedAttr::Int(i64::from(value))),
));
attrs.push(("text", optional_text(text)));
attrs.push(("color", super::paint(color.map(Paint::Role))));
},
Vec::new(),
content,
);
}
}
container!(BadgeScope);
pub struct SplitPaneScope {
modifier: Modifier,
value: Option<f32>,
min: Option<f32>,
max: Option<f32>,
collapsible: bool,
selected_index: usize,
label: Option<String>,
on_change: Option<Box<dyn FnMut(f32)>>,
on_dismiss: Option<Box<dyn FnMut()>>,
done: bool,
}
pub fn SplitPane() -> SplitPaneScope {
SplitPaneScope {
modifier: Modifier::new(),
value: None,
min: None,
max: None,
collapsible: false,
selected_index: 0,
label: None,
on_change: None,
on_dismiss: None,
done: false,
}
}
impl SplitPaneScope {
pub fn value(mut self, dp: f32) -> Self {
self.value = Some(dp);
self
}
pub fn min(mut self, dp: f32) -> Self {
self.min = Some(dp);
self
}
pub fn max(mut self, dp: f32) -> Self {
self.max = Some(dp);
self
}
pub fn collapsible(mut self, collapsible: bool) -> Self {
self.collapsible = collapsible;
self
}
pub fn selected_index(mut self, index: usize) -> Self {
self.selected_index = index;
self
}
pub fn label(mut self, label: impl Into<String>) -> Self {
self.label = Some(label.into());
self
}
pub fn on_change(mut self, handler: impl FnMut(f32) + 'static) -> Self {
self.on_change = Some(std::boxed::Box::new(handler));
self
}
pub fn on_dismiss(mut self, handler: impl FnMut() + 'static) -> Self {
self.on_dismiss = Some(std::boxed::Box::new(handler));
self
}
fn compose(&mut self, content: impl FnOnce()) {
let modifier = self.modifier;
let label = self.label.take();
let (value, min, max, collapsible, selected_index) = (
self.value,
self.min,
self.max,
self.collapsible,
self.selected_index,
);
let change = match self.on_change.take() {
Some(mut handler) => EventCallback::Value(Rc::new(RefCell::new(move |value: f64| {
handler(value as f32)
}))),
None => value_handler(None),
};
let as_float = |value: Option<f32>| {
value.map_or(OwnedAttr::None, |value| OwnedAttr::Float(f64::from(value)))
};
emit(
widget_key("compose_rust::ui::SplitPane"),
WidgetKind::SplitPane,
|attrs| {
push_modifiers(&modifier, Mods::Standard, attrs);
attrs.push(("value", as_float(value)));
attrs.push(("min", as_float(min)));
attrs.push(("max", as_float(max)));
attrs.push(("collapsible", OwnedAttr::Bool(collapsible)));
attrs.push(("selected_index", OwnedAttr::Int(selected_index as i64)));
attrs.push(("text", optional_text(label)));
},
vec![
("onchange", change),
("ondismiss", unit(self.on_dismiss.take())),
],
content,
);
}
}
container!(SplitPaneScope);
pub struct LinearProgressIndicatorScope {
modifier: Modifier,
progress: f32,
done: bool,
}
pub fn LinearProgressIndicator(progress: f32) -> LinearProgressIndicatorScope {
LinearProgressIndicatorScope {
modifier: Modifier::new(),
progress,
done: false,
}
}
impl LinearProgressIndicatorScope {
fn compose(&mut self, content: impl FnOnce()) {
let modifier = self.modifier;
let progress = self.progress;
emit(
widget_key("compose_rust::ui::LinearProgressIndicator"),
WidgetKind::LinearProgressIndicator,
|attrs| {
push_modifiers(&modifier, Mods::None, attrs);
attrs.push(("progress", OwnedAttr::Float(f64::from(progress))));
},
Vec::new(),
content,
);
}
}
composed_on_drop!(LinearProgressIndicatorScope);