Create a `View` for `SizedBox` (#398)

![image](https://github.com/linebender/xilem/assets/36049421/9b1cccc2-15d9-48dc-aca8-eddeb32dfac5)

I have chosen not to set the background and border properties, as those
are moreso styling properties - in our current state, we seem to be much
preferring only specifying layout values
This commit is contained in:
Daniel McNab 2024-06-14 18:04:39 +01:00 committed by GitHub
parent d6af6a6ef7
commit 7d62b16a19
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 184 additions and 4 deletions

View File

@ -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<Box<dyn Widget>>) -> 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<f64>) -> Self {
self.width = value;
self
}
/// Set the height directly. Intended for toolkits abstracting over `SizedBox`
pub fn raw_height(mut self, value: Option<f64>) -> Self {
self.height = value;
self
}
// TODO - child()
}

View File

@ -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<ArcStr>,
callback: impl Fn(&mut i32) + Send + Sync + 'static,
) -> impl WidgetView<i32> {
sized_box(button(label, callback)).width(40.).height(40.)
}
fn app_logic(data: &mut i32) -> impl WidgetView<i32> {
flex((
button("-", |data| {
big_button("-", |data| {
*data -= 1;
}),
label(format!("count: {}", data)),
button("+", |data| {
big_button("+", |data| {
*data += 1;
}),
))

View File

@ -10,6 +10,9 @@ pub use checkbox::*;
mod flex;
pub use flex::*;
mod sized_box;
pub use sized_box::*;
mod label;
pub use label::*;

142
xilem/src/view/sized_box.rs Normal file
View File

@ -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<State, Action, V>(inner: V) -> SizedBox<V>
where
V: WidgetView<State, Action>,
{
SizedBox {
inner,
height: None,
width: None,
}
}
pub struct SizedBox<V> {
inner: V,
width: Option<f64>,
height: Option<f64>,
}
impl<V> SizedBox<V> {
/// 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<V, State, Action> View<State, Action, ViewCtx> for SizedBox<V>
where
V: WidgetView<State, Action>,
{
type Element = Pod<widget::SizedBox>;
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<Action> {
self.inner.message(view_state, id_path, message, app_state)
}
}