#[macro_export]
macro_rules! view {
(Column [$($prop:ident : $val:expr),* $(,)?] { $($child:tt)* }) => {{
let mut col = $crate::widget::container::Column::new();
$(
col = $crate::__apply_prop!(col, $prop, $val);
)*
$(
col = col.child($crate::view!($child));
)*
col
}};
(Column { $($child:tt)* }) => {{
let mut col = $crate::widget::container::Column::new();
$(
col = col.child($crate::view!($child));
)*
col
}};
(Row [$($prop:ident : $val:expr),* $(,)?] { $($child:tt)* }) => {{
let mut row = $crate::widget::container::Row::new();
$(
row = $crate::__apply_prop!(row, $prop, $val);
)*
$(
row = row.child($crate::view!($child));
)*
row
}};
(Row { $($child:tt)* }) => {{
let mut row = $crate::widget::container::Row::new();
$(
row = row.child($crate::view!($child));
)*
row
}};
(Label($text:expr)) => {
$crate::widget::label::Label::new($text)
};
(Label($text:expr) . $method:ident ($($arg:expr),*)) => {
$crate::widget::label::Label::new($text).$method($($arg),*)
};
(Button($text:expr) { $($body:tt)* }) => {
$crate::widget::button::Button::new($text)
.on_click(move || { $($body)* })
};
(Button($text:expr)) => {
$crate::widget::button::Button::new($text)
};
(Button($text:expr) . $method:ident ($($arg:expr),*) $(. $rest_method:ident ($($rest_arg:expr),*))*) => {
$crate::widget::button::Button::new($text)
.$method($($arg),*)
$(.$rest_method($($rest_arg),*))*
};
(Checkbox) => {
$crate::widget::checkbox::Checkbox::new()
};
(Checkbox($label:expr)) => {
$crate::widget::checkbox::Checkbox::new().label($label)
};
(Checkbox($label:expr) { |$var:ident| $($body:tt)* }) => {
$crate::widget::checkbox::Checkbox::new()
.label($label)
.on_change(move |$var| { $($body)* })
};
(TextField) => {
$crate::widget::textfield::TextField::new()
};
(TextField($placeholder:expr)) => {
$crate::widget::textfield::TextField::new().placeholder($placeholder)
};
(TextField($placeholder:expr) { |$var:ident| $($body:tt)* }) => {
$crate::widget::textfield::TextField::new()
.placeholder($placeholder)
.on_change(move |$var| { $($body)* })
};
($expr:expr) => {
$expr
};
}
#[macro_export]
#[doc(hidden)]
macro_rules! __apply_prop {
($widget:expr, gap, $val:expr) => { $widget.gap($val) };
($widget:expr, padding, $val:expr) => { $widget.padding($val.into()) };
($widget:expr, align, $val:expr) => { $widget.align($val) };
($widget:expr, justify, $val:expr) => { $widget.justify($val) };
($widget:expr, class, $val:expr) => { $widget.class($val) };
($widget:expr, id, $val:expr) => { $widget.id($val) };
($widget:expr, $prop:ident, $val:expr) => { $widget.$prop($val) };
}
#[macro_export]
macro_rules! col {
($gap:expr; $($child:expr),* $(,)?) => {{
$crate::widget::container::Column::new()
.gap($gap as f32)
$(.child($child))*
}};
($($child:expr),* $(,)?) => {{
$crate::widget::container::Column::new()
$(.child($child))*
}};
}
#[macro_export]
macro_rules! row {
($gap:expr; $($child:expr),* $(,)?) => {{
$crate::widget::container::Row::new()
.gap($gap as f32)
$(.child($child))*
}};
($($child:expr),* $(,)?) => {{
$crate::widget::container::Row::new()
$(.child($child))*
}};
}
#[macro_export]
macro_rules! class {
($($class:expr),* $(,)?) => {{
let classes: Vec<&str> = vec![$($class),*];
classes.into_iter()
.filter(|c| !c.is_empty())
.collect::<Vec<_>>()
.join(" ")
}};
}
#[macro_export]
macro_rules! style {
($($prop:ident : $val:expr),* $(,)?) => {{
let mut styles = ::std::collections::HashMap::new();
$(
styles.insert(
stringify!($prop).replace("_", "-"),
format!("{}", $val)
);
)*
styles
}};
}
#[macro_export]
macro_rules! button {
($label:expr, { $($body:tt)* }) => {
$crate::widget::button::Button::new($label)
.on_click(move || { $($body)* })
};
($label:expr, $variant:ident, { $($body:tt)* }) => {
$crate::widget::button::Button::new($label)
.variant($crate::widget::button::ButtonVariant::$variant)
.on_click(move || { $($body)* })
};
($label:expr) => {
$crate::widget::button::Button::new($label)
};
($label:expr, $variant:ident) => {
$crate::widget::button::Button::new($label)
.variant($crate::widget::button::ButtonVariant::$variant)
};
}
#[macro_export]
macro_rules! checkbox {
($label:expr, |$var:ident| $($body:tt)*) => {
$crate::widget::checkbox::Checkbox::new()
.label($label)
.on_change(move |$var| { $($body)* })
};
($label:expr, $checked:expr, |$var:ident| $($body:tt)*) => {
$crate::widget::checkbox::Checkbox::new()
.label($label)
.checked($checked)
.on_change(move |$var| { $($body)* })
};
($label:expr) => {
$crate::widget::checkbox::Checkbox::new().label($label)
};
}
#[macro_export]
macro_rules! textfield {
($placeholder:expr, |$var:ident| $($body:tt)*) => {
$crate::widget::textfield::TextField::new()
.placeholder($placeholder)
.on_change(move |$var| { $($body)* })
};
($placeholder:expr, $value:expr, |$var:ident| $($body:tt)*) => {
$crate::widget::textfield::TextField::new()
.placeholder($placeholder)
.value($value)
.on_change(move |$var| { $($body)* })
};
($placeholder:expr) => {
$crate::widget::textfield::TextField::new().placeholder($placeholder)
};
() => {
$crate::widget::textfield::TextField::new()
};
}
#[macro_export]
macro_rules! label {
($text:expr) => {
$crate::widget::label::Label::new($text)
};
($text:expr, class: $class:expr) => {
$crate::widget::label::Label::new($text).class($class)
};
($text:expr, class: $class:expr, id: $id:expr) => {
$crate::widget::label::Label::new($text).class($class).id($id)
};
($text:expr, id: $id:expr) => {
$crate::widget::label::Label::new($text).id($id)
};
}
#[macro_export]
macro_rules! when {
($cond:expr => $widget:expr) => {
if $cond {
Some($widget)
} else {
None
}
};
}
#[macro_export]
macro_rules! match_widget {
($val:expr, $($pat:pat => $widget:expr),* $(,)?) => {
match $val {
$($pat => Box::new($widget) as Box<dyn $crate::widget::Widget>,)*
}
};
}
#[macro_export]
macro_rules! for_each {
($iter:expr, |$item:ident| $widget:expr) => {{
$iter.into_iter().map(|$item| $widget).collect::<Vec<_>>()
}};
($iter:expr, |$idx:ident, $item:ident| $widget:expr) => {{
$iter.into_iter().enumerate().map(|($idx, $item)| $widget).collect::<Vec<_>>()
}};
}
#[macro_export]
macro_rules! spacer {
() => {
$crate::widget::label::Label::new("").class("spacer")
};
($size:expr) => {
$crate::widget::label::Label::new("").class("spacer")
};
}
#[macro_export]
macro_rules! dbg_widget {
($widget:expr) => {{
let widget = $widget;
eprintln!(
"[{}:{}] Widget: {}",
file!(),
line!(),
stringify!($widget)
);
widget
}};
}
#[cfg(test)]
mod tests {
#[allow(unused_imports)]
use super::*;
use crate::widget::Widget;
#[test]
fn test_class_macro() {
let classes = class!["btn", "primary"];
assert_eq!(classes, "btn primary");
let classes = class!["btn", "", "active"];
assert_eq!(classes, "btn active");
}
#[test]
fn test_style_macro() {
let styles = style! {
background_color: "#fff",
padding: 16,
};
assert_eq!(styles.get("background-color"), Some(&"#fff".to_string()));
assert_eq!(styles.get("padding"), Some(&"16".to_string()));
}
#[test]
fn test_col_macro() {
use crate::widget::label::Label;
let col = col![16;
Label::new("A"),
Label::new("B"),
];
assert_eq!(col.children().len(), 2);
}
#[test]
fn test_row_macro() {
use crate::widget::label::Label;
let row = row![
Label::new("X"),
Label::new("Y"),
];
assert_eq!(row.children().len(), 2);
}
}