diff --git a/xilem/examples/calc.rs b/xilem/examples/calc.rs index 1e1f082f6..b03f6f5c6 100644 --- a/xilem/examples/calc.rs +++ b/xilem/examples/calc.rs @@ -259,7 +259,7 @@ fn expanded_button( text: &str, callback: impl Fn(&mut Calculator) + Send + Sync + 'static, ) -> impl WidgetView + '_ { - sized_box(button(text, callback)).expand() + sized_box(button(label(text), callback)).expand() } /// Returns an expanded button that triggers the calculator's operator handler, diff --git a/xilem/examples/components.rs b/xilem/examples/components.rs index cab739c93..3b9dbda98 100644 --- a/xilem/examples/components.rs +++ b/xilem/examples/components.rs @@ -20,8 +20,8 @@ struct AppState { fn modular_counter(count: &mut i32) -> impl WidgetView { flex(( label(format!("modularized count: {count}")), - button("+", |count| *count += 1), - button("-", |count| *count -= 1), + button(label("+"), |count| *count += 1), + button(label("-"), |count| *count -= 1), )) } @@ -29,7 +29,7 @@ fn app_logic(state: &mut AppState) -> impl WidgetView { flex(( lens(modular_counter, state, |state| &mut state.modularized_count), button( - format!("clicked {} times", state.global_count), + label(format!("clicked {} times", state.global_count)), |state: &mut AppState| state.global_count += 1, ), )) diff --git a/xilem/examples/elm.rs b/xilem/examples/elm.rs index 0eddc643d..ad8753950 100644 --- a/xilem/examples/elm.rs +++ b/xilem/examples/elm.rs @@ -29,8 +29,8 @@ enum CountMessage { fn elm_counter(count: i32) -> impl WidgetView { flex(( label(format!("elm count: {count}")), - button("+", |_| CountMessage::Increment), - button("-", |_| CountMessage::Decrement), + button(label("+"), |_| CountMessage::Increment), + button(label("-"), |_| CountMessage::Decrement), )) } @@ -46,18 +46,18 @@ fn adapt_counter(count: i32) -> impl WidgetView { flex(( flex(( label(format!("adapt count: {count}")), - button("+", |count| { + button(label("+"), |count| { *count += 1; AdaptMessage::Changed }), - button("-", |count| { + button(label("-"), |count| { *count -= 1; AdaptMessage::Changed }), )), flex(( - button("reset all", |_| AdaptMessage::Reset), - button("do nothing (and don't rebuild the view tree)", |_| { + button(label("reset all"), |_| AdaptMessage::Reset), + button(label("do nothing (and don't rebuild the view tree)"), |_| { AdaptMessage::Nop }), )), diff --git a/xilem/examples/external_event_loop.rs b/xilem/examples/external_event_loop.rs index 1f22ffbf8..776c7eae0 100644 --- a/xilem/examples/external_event_loop.rs +++ b/xilem/examples/external_event_loop.rs @@ -22,10 +22,10 @@ use xilem::{EventLoop, MasonryProxy, WidgetView, Xilem}; /// A component to make a bigger than usual button fn big_button( - label: impl Into, + text: impl Into, callback: impl Fn(&mut i32) + Send + Sync + 'static, ) -> impl WidgetView { - sized_box(button(label, callback)).width(40.).height(40.) + sized_box(button(label(text), callback)).width(40.).height(40.) } fn app_logic(data: &mut i32) -> impl WidgetView { diff --git a/xilem/examples/flex.rs b/xilem/examples/flex.rs index 003513cd0..1d001fac3 100644 --- a/xilem/examples/flex.rs +++ b/xilem/examples/flex.rs @@ -12,10 +12,10 @@ use xilem::{EventLoop, WidgetView, Xilem}; /// A component to make a bigger than usual button fn big_button( - label: impl Into, + text: impl Into, callback: impl Fn(&mut i32) + Send + Sync + 'static, ) -> impl WidgetView { - sized_box(button(label, callback)).width(40.).height(40.) + sized_box(button(label(text), callback)).width(40.).height(40.) } fn app_logic(data: &mut i32) -> impl WidgetView { diff --git a/xilem/examples/http_cats.rs b/xilem/examples/http_cats.rs index f3dd486dd..d2e75992d 100644 --- a/xilem/examples/http_cats.rs +++ b/xilem/examples/http_cats.rs @@ -17,8 +17,7 @@ use winit::window::Window; use xilem::core::fork; use xilem::core::one_of::OneOf3; use xilem::view::{ - button, flex, image, inline_prose, portal, prose, sized_box, spinner, worker, Axis, FlexExt, - FlexSpacer, Padding, + button, flex, image, inline_prose, label, portal, prose, sized_box, spinner, worker, Axis, FlexExt, FlexSpacer, Padding }; use xilem::{Color, EventLoop, EventLoopBuilder, TextAlignment, WidgetView, Xilem}; @@ -173,7 +172,7 @@ impl Status { FlexSpacer::Flex(1.), // TODO: Spinner if image pending? // TODO: Tick if image loaded? - button("Select", move |state: &mut HttpCats| { + button(label("Select"), move |state: &mut HttpCats| { state.selected_code = Some(code); }), FlexSpacer::Fixed(masonry::theme::SCROLLBAR_WIDTH), diff --git a/xilem/examples/mason.rs b/xilem/examples/mason.rs index 41083f075..5aead344f 100644 --- a/xilem/examples/mason.rs +++ b/xilem/examples/mason.rs @@ -41,7 +41,7 @@ fn app_logic(data: &mut AppData) -> impl WidgetView { let flex_sequence = (0..count) .map(|x| { ( - button(format!("+{x}"), move |data: &mut AppData| data.count += x), + button(label(format!("+{x}")), move |data: &mut AppData| data.count += x), if data.active { FlexSpacer::Flex(x as f64) } else { @@ -53,7 +53,7 @@ fn app_logic(data: &mut AppData) -> impl WidgetView { let fizz_buzz_flex_sequence = [(3, "Fizz"), (5, "Buzz")].map(|c| { if data.count.abs() % c.0 == 0 { - button(c.1, move |data: &mut AppData| { + button(label(c.1), move |data: &mut AppData| { data.count += 1; }) .into_any_flex() @@ -79,7 +79,7 @@ fn app_logic(data: &mut AppData) -> impl WidgetView { )) .direction(Axis::Horizontal), prose(LOREM).alignment(TextAlignment::Middle).text_size(18.), - button_any_pointer(button_label, |data: &mut AppData, button| match button { + button_any_pointer(label(button_label), |data: &mut AppData, button| match button { masonry::PointerButton::None => tracing::warn!("Got unexpected None from button"), masonry::PointerButton::Primary => data.count += 1, masonry::PointerButton::Secondary => data.count -= 1, @@ -90,8 +90,8 @@ fn app_logic(data: &mut AppData) -> impl WidgetView { data.active = checked; }), toggleable(data), - button("Decrement", |data: &mut AppData| data.count -= 1), - button("Reset", |data: &mut AppData| data.count = 0), + button(label("Decrement"), |data: &mut AppData| data.count -= 1), + button(label("Reset"), |data: &mut AppData| data.count = 0), flex((fizz_buzz_flex_sequence, flex_sequence)).direction(axis), )), // The following `task` view only exists whilst the example is in the "active" state, so @@ -119,10 +119,10 @@ fn toggleable(data: &mut AppData) -> impl WidgetView { if data.active { fork( flex(( - button("Deactivate", |data: &mut AppData| { + button(label("Deactivate"), |data: &mut AppData| { data.active = false; }), - button("Unlimited Power", |data: &mut AppData| { + button(label("Unlimited Power"), |data: &mut AppData| { data.count = -1_000_000; }), )) @@ -131,7 +131,7 @@ fn toggleable(data: &mut AppData) -> impl WidgetView { ) .boxed() } else { - button("Activate", |data: &mut AppData| data.active = true).boxed() + button(label("Activate"), |data: &mut AppData| data.active = true).boxed() } } diff --git a/xilem/examples/memoization.rs b/xilem/examples/memoization.rs index 97f9e5c2b..cd0599ccc 100644 --- a/xilem/examples/memoization.rs +++ b/xilem/examples/memoization.rs @@ -7,7 +7,7 @@ use std::sync::Arc; use xilem::core::{frozen, memoize}; -use xilem::view::{button, flex}; +use xilem::view::{button, flex, label}; use xilem::{AnyWidgetView, EventLoop, WidgetView, Xilem}; // There are currently two ways to do memoization @@ -28,7 +28,7 @@ struct MemoizedArcView { fn increase_button(state: &mut AppState) -> Arc> { if state.count != state.increase_button.data || state.increase_button.view.is_none() { let view = Arc::new(button( - format!("current count is {}", state.count), + label(format!("current count is {}", state.count)), |state: &mut AppState| { state.count += 1; }, @@ -46,7 +46,7 @@ fn increase_button(state: &mut AppState) -> Arc> { fn decrease_button(state: &AppState) -> impl WidgetView { memoize(state.count, |count| { button( - format!("decrease the count: {count}"), + label(format!("decrease the count: {count}")), |data: &mut AppState| data.count -= 1, ) }) @@ -55,7 +55,7 @@ fn decrease_button(state: &AppState) -> impl WidgetView { fn reset_button() -> impl WidgetView { // The contents of this view never changes, so we use `frozen` to avoid unnecessary rebuilds. // This is a special case of memoization for when the view doesn't depend on any data. - frozen(|| button("reset", |data: &mut AppState| data.count = 0)) + frozen(|| button(label("reset"), |data: &mut AppState| data.count = 0)) } fn app_logic(state: &mut AppState) -> impl WidgetView { diff --git a/xilem/examples/state_machine.rs b/xilem/examples/state_machine.rs index 22007fc8a..f5d5b1e2d 100644 --- a/xilem/examples/state_machine.rs +++ b/xilem/examples/state_machine.rs @@ -53,7 +53,7 @@ fn state_machine(app_data: &mut StateMachine) -> impl WidgetView { /// A button component which transitions to a specified `target_state` /// and appends its value to the history when pressed. fn sequence_button(value: &'static str, target_state: IsEven) -> impl WidgetView { - button(value, move |app_data: &mut StateMachine| { + button(label(value), move |app_data: &mut StateMachine| { app_data.state = target_state; app_data.history.push_str(value); }) @@ -61,7 +61,7 @@ fn sequence_button(value: &'static str, target_state: IsEven) -> impl WidgetView fn app_logic(app_data: &mut StateMachine) -> impl WidgetView { flex(( - button("Reset", |app_data: &mut StateMachine| { + button(label("Reset"), |app_data: &mut StateMachine| { app_data.history.clear(); app_data.state = IsEven::Initial; }), diff --git a/xilem/examples/stopwatch.rs b/xilem/examples/stopwatch.rs index 05defcfc2..1488d8940 100644 --- a/xilem/examples/stopwatch.rs +++ b/xilem/examples/stopwatch.rs @@ -178,11 +178,11 @@ fn single_lap( fn start_stop_button(data: &mut Stopwatch) -> impl WidgetView { if data.active { - Either::A(button("Stop", |data: &mut Stopwatch| { + Either::A(button(label("Stop"), |data: &mut Stopwatch| { data.stop(); })) } else { - Either::B(button("Start", |data: &mut Stopwatch| { + Either::B(button(label("Start"), |data: &mut Stopwatch| { data.start(); })) } @@ -190,11 +190,11 @@ fn start_stop_button(data: &mut Stopwatch) -> impl WidgetView { fn lap_reset_button(data: &mut Stopwatch) -> impl WidgetView { if data.active { - Either::A(button(" Lap ", |data: &mut Stopwatch| { + Either::A(button(label(" Lap "), |data: &mut Stopwatch| { data.lap(); })) } else { - Either::B(button("Reset", |data: &mut Stopwatch| { + Either::B(button(label("Reset"), |data: &mut Stopwatch| { data.reset(); })) } diff --git a/xilem/examples/to_do_mvc.rs b/xilem/examples/to_do_mvc.rs index 0f51f8b06..ab3cb05e8 100644 --- a/xilem/examples/to_do_mvc.rs +++ b/xilem/examples/to_do_mvc.rs @@ -8,7 +8,7 @@ #![expect(clippy::shadow_unrelated, reason = "Idiomatic for Xilem users")] use winit::error::EventLoopError; -use xilem::view::{button, checkbox, flex, textbox, Axis, FlexSpacer}; +use xilem::view::{button, checkbox, flex, label, textbox, Axis, FlexSpacer}; use xilem::{EventLoop, EventLoopBuilder, WidgetView, Xilem}; struct Task { @@ -45,7 +45,7 @@ fn app_logic(task_list: &mut TaskList) -> impl WidgetView { }); let first_line = flex(( input_box, - button("Add task".to_string(), |task_list: &mut TaskList| { + button(label("Add task"), |task_list: &mut TaskList| { task_list.add_task(); }), )) @@ -63,7 +63,7 @@ fn app_logic(task_list: &mut TaskList) -> impl WidgetView { data.tasks[i].done = checked; }, ); - let delete_button = button("Delete", move |data: &mut TaskList| { + let delete_button = button(label("Delete"), move |data: &mut TaskList| { data.tasks.remove(i); }); flex((checkbox, delete_button)).direction(Axis::Horizontal) diff --git a/xilem/examples/variable_clock.rs b/xilem/examples/variable_clock.rs index c95fbd9bd..fbdd691c5 100644 --- a/xilem/examples/variable_clock.rs +++ b/xilem/examples/variable_clock.rs @@ -91,16 +91,16 @@ fn local_time(data: &mut Clocks) -> impl WidgetView { /// Controls for the variable font weight. fn controls() -> impl WidgetView { flex(( - button("Increase", |data: &mut Clocks| { + button(label("change progress"), |data: &mut Clocks| { data.weight = (data.weight + 100.).clamp(1., 1000.); }), - button("Decrease", |data: &mut Clocks| { + button(label("Decrease"), |data: &mut Clocks| { data.weight = (data.weight - 100.).clamp(1., 1000.); }), - button("Minimum", |data: &mut Clocks| { + button(label("Minimum"), |data: &mut Clocks| { data.weight = 1.; }), - button("Maximum", |data: &mut Clocks| { + button(label("Maximum"), |data: &mut Clocks| { data.weight = 1000.; }), )) diff --git a/xilem/examples/widgets.rs b/xilem/examples/widgets.rs index 846938e4c..84acb849c 100644 --- a/xilem/examples/widgets.rs +++ b/xilem/examples/widgets.rs @@ -9,7 +9,7 @@ use masonry::event_loop_runner::{EventLoop, EventLoopBuilder}; use winit::error::EventLoopError; use winit::window::Window; use xilem::core::adapt; -use xilem::view::{button, checkbox, flex, flex_item, progress_bar, sized_box, Axis, FlexSpacer}; +use xilem::view::{button, checkbox, flex, flex_item, label, progress_bar, sized_box, Axis, FlexSpacer}; use xilem::{Color, WidgetView, Xilem}; const SPACER_WIDTH: f64 = 10.; @@ -36,7 +36,7 @@ fn progress_bar_view(data: Option) -> impl WidgetView> { } }, ), - button("change progress", |state: &mut Option| match state { + button(label("change progress"), |state: &mut Option| match state { Some(ref mut v) => *v = (*v + 0.1).rem_euclid(1.), None => *state = Some(0.5), }), diff --git a/xilem/src/view/button.rs b/xilem/src/view/button.rs index e44895f16..8ef892966 100644 --- a/xilem/src/view/button.rs +++ b/xilem/src/view/button.rs @@ -1,43 +1,45 @@ // Copyright 2024 the Xilem Authors // SPDX-License-Identifier: Apache-2.0 -use masonry::text::ArcStr; +use masonry::text::StyleProperty; use masonry::widget; pub use masonry::PointerButton; use crate::core::{DynMessage, Mut, View, ViewMarker}; use crate::{MessageResult, Pod, ViewCtx, ViewId}; +use crate::view::Label; /// A button which calls `callback` when the primary mouse button (normally left) is pressed. pub fn button( - label: impl Into, + label: Label, callback: impl Fn(&mut State) -> Action + Send + 'static, ) -> Button Fn(&'a mut State, PointerButton) -> MessageResult + Send + 'static> { Button { - label: label.into(), + label, callback: move |state: &mut State, button| match button { PointerButton::Primary => MessageResult::Action(callback(state)), _ => MessageResult::Nop, }, } } +// button(label("Text"), callback) /// A button which calls `callback` when pressed. pub fn button_any_pointer( - label: impl Into, + label: Label, callback: impl Fn(&mut State, PointerButton) -> Action + Send + 'static, ) -> Button Fn(&'a mut State, PointerButton) -> MessageResult + Send + 'static> { Button { - label: label.into(), + label, callback: move |state: &mut State, button| MessageResult::Action(callback(state, button)), } } #[must_use = "View values do nothing unless provided to Xilem."] pub struct Button { - label: ArcStr, + label: Label, callback: F, } @@ -50,7 +52,14 @@ where type ViewState = (); fn build(&self, ctx: &mut ViewCtx) -> (Self::Element, Self::ViewState) { - ctx.with_leaf_action_widget(|ctx| ctx.new_pod(widget::Button::new(self.label.clone()))) + ctx.with_leaf_action_widget(|ctx| ctx.new_pod( + widget::Button::from_label(widget::Label::new(self.label.label.clone()) + .with_brush(self.label.text_brush.clone()) + .with_alignment(self.label.alignment) + .with_style(StyleProperty::FontSize(self.label.text_size)) + .with_style(StyleProperty::FontWeight(self.label.weight)) + .with_style(StyleProperty::FontStack(self.label.font.clone())), + ))) } fn rebuild( @@ -60,8 +69,8 @@ where _ctx: &mut ViewCtx, mut element: Mut, ) { - if prev.label != self.label { - widget::Button::set_text(&mut element, self.label.clone()); + if prev.label.label != self.label.label { + widget::Button::set_text(&mut element, self.label.label.clone()); } } diff --git a/xilem/src/view/label.rs b/xilem/src/view/label.rs index 4abfd8325..43ce9b74d 100644 --- a/xilem/src/view/label.rs +++ b/xilem/src/view/label.rs @@ -22,7 +22,7 @@ pub fn label(label: impl Into) -> Label { #[must_use = "View values do nothing unless provided to Xilem."] pub struct Label { - label: ArcStr, + pub(in crate::view) label: ArcStr, // Public for variable_label as a semi-interims state. pub(in crate::view) text_brush: Brush,