Skip to content

Commit

Permalink
xilem: Buttons with customisable text
Browse files Browse the repository at this point in the history
  • Loading branch information
Artyom Sinyugin committed Dec 18, 2024
1 parent c4d5950 commit 32f1277
Show file tree
Hide file tree
Showing 15 changed files with 62 additions and 54 deletions.
2 changes: 1 addition & 1 deletion xilem/examples/calc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -259,7 +259,7 @@ fn expanded_button(
text: &str,
callback: impl Fn(&mut Calculator) + Send + Sync + 'static,
) -> impl WidgetView<Calculator> + '_ {
sized_box(button(text, callback)).expand()
sized_box(button(label(text), callback)).expand()
}

/// Returns an expanded button that triggers the calculator's operator handler,
Expand Down
6 changes: 3 additions & 3 deletions xilem/examples/components.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,16 +20,16 @@ struct AppState {
fn modular_counter(count: &mut i32) -> impl WidgetView<i32> {
flex((
label(format!("modularized count: {count}")),
button("+", |count| *count += 1),
button("-", |count| *count -= 1),
button(label("+"), |count| *count += 1),
button(label("-"), |count| *count -= 1),
))
}

fn app_logic(state: &mut AppState) -> impl WidgetView<AppState> {
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,
),
))
Expand Down
12 changes: 6 additions & 6 deletions xilem/examples/elm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,8 @@ enum CountMessage {
fn elm_counter<T: 'static>(count: i32) -> impl WidgetView<T, CountMessage> {
flex((
label(format!("elm count: {count}")),
button("+", |_| CountMessage::Increment),
button("-", |_| CountMessage::Decrement),
button(label("+"), |_| CountMessage::Increment),
button(label("-"), |_| CountMessage::Decrement),
))
}

Expand All @@ -46,18 +46,18 @@ fn adapt_counter(count: i32) -> impl WidgetView<i32, AdaptMessage> {
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
}),
)),
Expand Down
4 changes: 2 additions & 2 deletions xilem/examples/external_event_loop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<ArcStr>,
text: impl Into<ArcStr>,
callback: impl Fn(&mut i32) + Send + Sync + 'static,
) -> impl WidgetView<i32> {
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<i32> {
Expand Down
4 changes: 2 additions & 2 deletions xilem/examples/flex.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,10 @@ use xilem::{EventLoop, WidgetView, Xilem};

/// A component to make a bigger than usual button
fn big_button(
label: impl Into<ArcStr>,
text: impl Into<ArcStr>,
callback: impl Fn(&mut i32) + Send + Sync + 'static,
) -> impl WidgetView<i32> {
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<i32> {
Expand Down
5 changes: 2 additions & 3 deletions xilem/examples/http_cats.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};

Expand Down Expand Up @@ -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),
Expand Down
16 changes: 8 additions & 8 deletions xilem/examples/mason.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ fn app_logic(data: &mut AppData) -> impl WidgetView<AppData> {
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 {
Expand All @@ -53,7 +53,7 @@ fn app_logic(data: &mut AppData) -> impl WidgetView<AppData> {

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()
Expand All @@ -79,7 +79,7 @@ fn app_logic(data: &mut AppData) -> impl WidgetView<AppData> {
))
.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,
Expand All @@ -90,8 +90,8 @@ fn app_logic(data: &mut AppData) -> impl WidgetView<AppData> {
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
Expand Down Expand Up @@ -119,10 +119,10 @@ fn toggleable(data: &mut AppData) -> impl WidgetView<AppData> {
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;
}),
))
Expand All @@ -131,7 +131,7 @@ fn toggleable(data: &mut AppData) -> impl WidgetView<AppData> {
)
.boxed()
} else {
button("Activate", |data: &mut AppData| data.active = true).boxed()
button(label("Activate"), |data: &mut AppData| data.active = true).boxed()
}
}

Expand Down
8 changes: 4 additions & 4 deletions xilem/examples/memoization.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -28,7 +28,7 @@ struct MemoizedArcView<D> {
fn increase_button(state: &mut AppState) -> Arc<AnyWidgetView<AppState>> {
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;
},
Expand All @@ -46,7 +46,7 @@ fn increase_button(state: &mut AppState) -> Arc<AnyWidgetView<AppState>> {
fn decrease_button(state: &AppState) -> impl WidgetView<AppState> {
memoize(state.count, |count| {
button(
format!("decrease the count: {count}"),
label(format!("decrease the count: {count}")),
|data: &mut AppState| data.count -= 1,
)
})
Expand All @@ -55,7 +55,7 @@ fn decrease_button(state: &AppState) -> impl WidgetView<AppState> {
fn reset_button() -> impl WidgetView<AppState> {
// 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<AppState> {
Expand Down
4 changes: 2 additions & 2 deletions xilem/examples/state_machine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,15 +53,15 @@ fn state_machine(app_data: &mut StateMachine) -> impl WidgetView<StateMachine> {
/// 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<StateMachine> {
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);
})
}

fn app_logic(app_data: &mut StateMachine) -> impl WidgetView<StateMachine> {
flex((
button("Reset", |app_data: &mut StateMachine| {
button(label("Reset"), |app_data: &mut StateMachine| {
app_data.history.clear();
app_data.state = IsEven::Initial;
}),
Expand Down
8 changes: 4 additions & 4 deletions xilem/examples/stopwatch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -178,23 +178,23 @@ fn single_lap(

fn start_stop_button(data: &mut Stopwatch) -> impl WidgetView<Stopwatch> {
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();
}))
}
}

fn lap_reset_button(data: &mut Stopwatch) -> impl WidgetView<Stopwatch> {
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();
}))
}
Expand Down
6 changes: 3 additions & 3 deletions xilem/examples/to_do_mvc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -45,7 +45,7 @@ fn app_logic(task_list: &mut TaskList) -> impl WidgetView<TaskList> {
});
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();
}),
))
Expand All @@ -63,7 +63,7 @@ fn app_logic(task_list: &mut TaskList) -> impl WidgetView<TaskList> {
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)
Expand Down
8 changes: 4 additions & 4 deletions xilem/examples/variable_clock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -91,16 +91,16 @@ fn local_time(data: &mut Clocks) -> impl WidgetView<Clocks> {
/// Controls for the variable font weight.
fn controls() -> impl WidgetView<Clocks> {
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.;
}),
))
Expand Down
4 changes: 2 additions & 2 deletions xilem/examples/widgets.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.;
Expand All @@ -36,7 +36,7 @@ fn progress_bar_view(data: Option<f64>) -> impl WidgetView<Option<f64>> {
}
},
),
button("change progress", |state: &mut Option<f64>| match state {
button(label("change progress"), |state: &mut Option<f64>| match state {
Some(ref mut v) => *v = (*v + 0.1).rem_euclid(1.),
None => *state = Some(0.5),
}),
Expand Down
27 changes: 18 additions & 9 deletions xilem/src/view/button.rs
Original file line number Diff line number Diff line change
@@ -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<State, Action>(
label: impl Into<ArcStr>,
label: Label,
callback: impl Fn(&mut State) -> Action + Send + 'static,
) -> Button<impl for<'a> Fn(&'a mut State, PointerButton) -> MessageResult<Action> + 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<State, Action>(
label: impl Into<ArcStr>,
label: Label,
callback: impl Fn(&mut State, PointerButton) -> Action + Send + 'static,
) -> Button<impl for<'a> Fn(&'a mut State, PointerButton) -> MessageResult<Action> + 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<F> {
label: ArcStr,
label: Label,
callback: F,
}

Expand All @@ -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(
Expand All @@ -60,8 +69,8 @@ where
_ctx: &mut ViewCtx,
mut element: Mut<Self::Element>,
) {
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());
}
}

Expand Down
Loading

0 comments on commit 32f1277

Please sign in to comment.