diff --git a/masonry/src/widget/sized_box.rs b/masonry/src/widget/sized_box.rs index cb78f15b..d7530984 100644 --- a/masonry/src/widget/sized_box.rs +++ b/masonry/src/widget/sized_box.rs @@ -83,6 +83,18 @@ impl SizedBox { } } + /// Construct container with child in a pod, and both width and height not set. + pub fn new_pod(child: WidgetPod>) -> Self { + Self { + child: Some(child), + width: None, + height: None, + background: None, + border: None, + corner_radius: RoundedRectRadii::from_single_radius(0.0), + } + } + /// Construct container without child, and both width and height not set. /// /// If the widget is unchanged, it will render nothing, which can be useful if you want to draw a @@ -165,6 +177,18 @@ impl SizedBox { self } + /// Set the width directly. Intended for toolkits abstracting over `SizedBox` + pub fn raw_width(mut self, value: Option) -> Self { + self.width = value; + self + } + + /// Set the height directly. Intended for toolkits abstracting over `SizedBox` + pub fn raw_height(mut self, value: Option) -> Self { + self.height = value; + self + } + // TODO - child() } diff --git a/xilem/examples/flex.rs b/xilem/examples/flex.rs index bf22e9ba..1be88a57 100644 --- a/xilem/examples/flex.rs +++ b/xilem/examples/flex.rs @@ -1,20 +1,31 @@ // Copyright 2024 the Xilem Authors // SPDX-License-Identifier: Apache-2.0 -use masonry::widget::{CrossAxisAlignment, MainAxisAlignment}; +use masonry::{ + widget::{CrossAxisAlignment, MainAxisAlignment}, + ArcStr, +}; use winit::error::EventLoopError; use xilem::{ - view::{button, flex, label}, + view::{button, flex, label, sized_box}, EventLoop, WidgetView, Xilem, }; +/// A component to make a bigger than usual button +fn big_button( + label: impl Into, + callback: impl Fn(&mut i32) + Send + Sync + 'static, +) -> impl WidgetView { + sized_box(button(label, callback)).width(40.).height(40.) +} + fn app_logic(data: &mut i32) -> impl WidgetView { flex(( - button("-", |data| { + big_button("-", |data| { *data -= 1; }), label(format!("count: {}", data)), - button("+", |data| { + big_button("+", |data| { *data += 1; }), )) diff --git a/xilem/src/view/mod.rs b/xilem/src/view/mod.rs index c7bda113..3ba6ca31 100644 --- a/xilem/src/view/mod.rs +++ b/xilem/src/view/mod.rs @@ -10,6 +10,9 @@ pub use checkbox::*; mod flex; pub use flex::*; +mod sized_box; +pub use sized_box::*; + mod label; pub use label::*; diff --git a/xilem/src/view/sized_box.rs b/xilem/src/view/sized_box.rs new file mode 100644 index 00000000..fafa6f1d --- /dev/null +++ b/xilem/src/view/sized_box.rs @@ -0,0 +1,142 @@ +// Copyright 2024 the Xilem Authors +// SPDX-License-Identifier: Apache-2.0 + +use masonry::widget; + +use crate::{ + core::{Mut, View, ViewId}, + Pod, ViewCtx, WidgetView, +}; + +/// A widget with predefined size. +/// +/// This widget forces its child to have a specific width and/or height (assuming values are permitted by +/// this widget's parent). If either the width or height is not set, this widget will size itself +/// to match the child's size in that dimension. +pub fn sized_box(inner: V) -> SizedBox +where + V: WidgetView, +{ + SizedBox { + inner, + height: None, + width: None, + } +} + +pub struct SizedBox { + inner: V, + width: Option, + height: Option, +} + +impl SizedBox { + /// Set container's width. + pub fn width(mut self, width: f64) -> Self { + self.width = Some(width); + self + } + + /// Set container's height. + pub fn height(mut self, height: f64) -> Self { + self.height = Some(height); + self + } + + /// Expand container to fit the parent. + /// + /// Only call this method if you want your widget to occupy all available + /// space. If you only care about expanding in one of width or height, use + /// [`expand_width`] or [`expand_height`] instead. + /// + /// [`expand_height`]: Self::expand_height + /// [`expand_width`]: Self::expand_width + pub fn expand(mut self) -> Self { + self.width = Some(f64::INFINITY); + self.height = Some(f64::INFINITY); + self + } + + /// Expand the container on the x-axis. + /// + /// This will force the child to have maximum width. + pub fn expand_width(mut self) -> Self { + self.width = Some(f64::INFINITY); + self + } + + /// Expand the container on the y-axis. + /// + /// This will force the child to have maximum height. + pub fn expand_height(mut self) -> Self { + self.height = Some(f64::INFINITY); + self + } +} + +impl View for SizedBox +where + V: WidgetView, +{ + type Element = Pod; + type ViewState = V::ViewState; + + fn build(&self, ctx: &mut ViewCtx) -> (Self::Element, Self::ViewState) { + let (child, child_state) = self.inner.build(ctx); + let widget = widget::SizedBox::new_pod(child.inner.boxed()) + .raw_width(self.width) + .raw_height(self.height); + (Pod::new(widget), child_state) + } + + fn rebuild<'el>( + &self, + prev: &Self, + view_state: &mut Self::ViewState, + ctx: &mut ViewCtx, + mut element: Mut<'el, Self::Element>, + ) -> Mut<'el, Self::Element> { + if self.width != prev.width { + match self.width { + Some(width) => element.set_width(width), + None => element.unset_width(), + } + } + if self.height != prev.height { + match self.height { + Some(height) => element.set_height(height), + None => element.unset_height(), + } + } + { + let mut child = element + .child_mut() + .expect("We only create SizedBox with a child"); + self.inner + .rebuild(&prev.inner, view_state, ctx, child.downcast()); + } + element + } + + fn teardown( + &self, + view_state: &mut Self::ViewState, + ctx: &mut ViewCtx, + mut element: Mut<'_, Self::Element>, + ) { + let mut child = element + .child_mut() + .expect("We only create SizedBox with a child"); + self.inner.teardown(view_state, ctx, child.downcast()); + } + + fn message( + &self, + view_state: &mut Self::ViewState, + id_path: &[ViewId], + message: xilem_core::DynMessage, + app_state: &mut State, + ) -> crate::MessageResult { + self.inner.message(view_state, id_path, message, app_state) + } +}