Skip to content

Commit

Permalink
xilem: Button with customisable label(text) (#797)
Browse files Browse the repository at this point in the history
Co-authored-by: Artyom Sinyugin <[email protected]>
  • Loading branch information
ArtyomSinyugin and Artyom Sinyugin authored Dec 20, 2024
1 parent 8e12858 commit 8f7ef7d
Show file tree
Hide file tree
Showing 5 changed files with 58 additions and 25 deletions.
24 changes: 18 additions & 6 deletions xilem/examples/calc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@ use winit::error::EventLoopError;
use winit::window::Window;
use xilem::view::{
button, flex, grid, label, sized_box, Axis, Flex, FlexSequence, FlexSpacer, GridExt,
GridSequence,
GridSequence, Label,
};
use xilem::{EventLoop, EventLoopBuilder, WidgetView, Xilem};
use xilem::{Color, EventLoop, EventLoopBuilder, WidgetView, Xilem};

#[derive(Copy, Clone)]
enum MathOperator {
Expand Down Expand Up @@ -53,7 +53,11 @@ struct Calculator {

impl Calculator {
fn get_current_number(&self) -> String {
self.numbers[self.current_num_index].clone()
self.current_number().to_string()
}

fn current_number(&self) -> &str {
&self.numbers[self.current_num_index]
}

fn set_current_number(&mut self, new_num: String) {
Expand Down Expand Up @@ -215,7 +219,15 @@ fn app_logic(data: &mut Calculator) -> impl WidgetView<Calculator> {
))
.grid_item(GridParams::new(0, 0, 4, 1)),
// Top row
expanded_button("CE", Calculator::clear_entry).grid_pos(0, 1),
expanded_button(
label("CE").brush(if data.get_current_number().is_empty() {
Color::MEDIUM_VIOLET_RED
} else {
Color::WHITE
}),
Calculator::clear_entry,
)
.grid_pos(0, 1),
expanded_button("C", Calculator::clear_all).grid_pos(1, 1),
expanded_button("DEL", Calculator::on_delete).grid_pos(2, 1),
operator_button(MathOperator::Divide).grid_pos(3, 1),
Expand Down Expand Up @@ -255,9 +267,9 @@ fn display_label(text: &str) -> impl WidgetView<Calculator> {
/// Returns a button contained in an expanded box. Useful for the buttons so that
/// they take up all available space in flex containers.
fn expanded_button(
text: &str,
text: impl Into<Label>,
callback: impl Fn(&mut Calculator) + Send + Sync + 'static,
) -> impl WidgetView<Calculator> + '_ {
) -> impl WidgetView<Calculator> {
sized_box(button(text, callback)).expand()
}

Expand Down
5 changes: 2 additions & 3 deletions xilem/examples/external_event_loop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,19 +10,18 @@
use std::sync::Arc;

use masonry::event_loop_runner::MasonryUserEvent;
use masonry::text::ArcStr;
use masonry::widget::{CrossAxisAlignment, MainAxisAlignment};
use masonry::{AppDriver, Color};
use winit::application::ApplicationHandler;
use winit::error::EventLoopError;
use winit::event::ElementState;
use winit::keyboard::{KeyCode, PhysicalKey};
use xilem::view::{button, flex, label, sized_box, Axis};
use xilem::view::{button, flex, label, sized_box, Axis, Label};
use xilem::{EventLoop, MasonryProxy, WidgetView, Xilem};

/// A component to make a bigger than usual button
fn big_button(
label: impl Into<ArcStr>,
label: impl Into<Label>,
callback: impl Fn(&mut i32) + Send + Sync + 'static,
) -> impl WidgetView<i32> {
sized_box(button(label, callback)).width(40.).height(40.)
Expand Down
5 changes: 2 additions & 3 deletions xilem/examples/flex.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,14 @@
//! Flex properties can be set in Xilem.
#![expect(clippy::shadow_unrelated, reason = "Idiomatic for Xilem users")]
use masonry::text::ArcStr;
use masonry::widget::{CrossAxisAlignment, MainAxisAlignment};
use winit::error::EventLoopError;
use xilem::view::{button, flex, label, sized_box, Axis, FlexExt as _, FlexSpacer};
use xilem::view::{button, flex, label, sized_box, Axis, FlexExt as _, FlexSpacer, Label};
use xilem::{EventLoop, WidgetView, Xilem};

/// A component to make a bigger than usual button
fn big_button(
label: impl Into<ArcStr>,
label: impl Into<Label>,
callback: impl Fn(&mut i32) + Send + Sync + 'static,
) -> impl WidgetView<i32> {
sized_box(button(label, callback)).width(40.).height(40.)
Expand Down
35 changes: 25 additions & 10 deletions xilem/src/view/button.rs
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
// 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::view::Label;
use crate::{MessageResult, Pod, ViewCtx, ViewId};

/// A button which calls `callback` when the primary mouse button (normally left) is pressed.
pub fn button<State, Action>(
label: impl Into<ArcStr>,
label: impl Into<Label>,
callback: impl Fn(&mut State) -> Action + Send + 'static,
) -> Button<impl for<'a> Fn(&'a mut State, PointerButton) -> MessageResult<Action> + Send + 'static>
{
Expand All @@ -25,7 +26,7 @@ pub fn button<State, Action>(

/// A button which calls `callback` when pressed.
pub fn button_any_pointer<State, Action>(
label: impl Into<ArcStr>,
label: impl Into<Label>,
callback: impl Fn(&mut State, PointerButton) -> Action + Send + 'static,
) -> Button<impl for<'a> Fn(&'a mut State, PointerButton) -> MessageResult<Action> + Send + 'static>
{
Expand All @@ -37,7 +38,7 @@ pub fn button_any_pointer<State, Action>(

#[must_use = "View values do nothing unless provided to Xilem."]
pub struct Button<F> {
label: ArcStr,
label: Label,
callback: F,
}

Expand All @@ -50,19 +51,33 @@ 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(
// TODO: Use `Label::build` here - currently impossible because `Pod` uses `WidgetPod` internally
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(
&self,
prev: &Self,
_: &mut Self::ViewState,
_ctx: &mut ViewCtx,
state: &mut Self::ViewState,
ctx: &mut ViewCtx,
mut element: Mut<Self::Element>,
) {
if prev.label != self.label {
widget::Button::set_text(&mut element, self.label.clone());
}
<Label as View<State, Action, ViewCtx>>::rebuild(
&self.label,
&prev.label,
state,
ctx,
widget::Button::label_mut(&mut element),
);
}

fn teardown(&self, _: &mut Self::ViewState, ctx: &mut ViewCtx, element: Mut<Self::Element>) {
Expand Down
14 changes: 11 additions & 3 deletions xilem/src/view/label.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,8 @@ pub fn label(label: impl Into<ArcStr>) -> Label {

#[must_use = "View values do nothing unless provided to Xilem."]
pub struct Label {
label: ArcStr,

// Public for variable_label as a semi-interims state.
// Public for button and variable_label as a semi-interim state.
pub(in crate::view) label: ArcStr,
pub(in crate::view) text_brush: Brush,
pub(in crate::view) alignment: TextAlignment,
pub(in crate::view) text_size: f32,
Expand Down Expand Up @@ -65,6 +64,15 @@ impl Label {
}
}

impl<T> From<T> for Label
where
T: Into<ArcStr>,
{
fn from(text: T) -> Self {
label(text)
}
}

impl ViewMarker for Label {}
impl<State, Action> View<State, Action, ViewCtx> for Label {
type Element = Pod<widget::Label>;
Expand Down

0 comments on commit 8f7ef7d

Please sign in to comment.