Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make xilem::Pod more flexible #806

Merged
merged 7 commits into from
Jan 15, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions masonry/src/widget/button.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,13 @@ impl Button {
label: WidgetPod::new(label),
}
}

/// Create a new button with the provided [`Label`] with a predetermined id.
///
/// This constructor is useful for toolkits which use Masonry (such as Xilem).
pub fn from_label_pod(label: WidgetPod<Label>) -> Self {
Self { label }
}
}

// --- MARK: WIDGETMUT ---
Expand Down
1 change: 0 additions & 1 deletion masonry/src/widget/root_widget.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ impl<W: Widget> RootWidget<W> {
}
}

// TODO - This help works around impedance mismatch between the types of Xilem and Masonry
pub fn from_pod(pod: WidgetPod<W>) -> Self {
Self { pod }
}
Expand Down
2 changes: 1 addition & 1 deletion xilem/examples/variable_clock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ impl TimeZone {
)
.text_size(48.)
// Use the roboto flex we have just loaded.
.with_font("Roboto Flex")
.font("Roboto Flex")
.target_weight(data.weight, 400.),
FlexSpacer::Flex(1.0),
(data.local_now().date() != date_time_in_self.date()).then(|| {
Expand Down
5 changes: 2 additions & 3 deletions xilem/src/any_view.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,8 @@ pub type AnyWidgetView<State, Action = ()> =

impl<W: Widget> SuperElement<Pod<W>, ViewCtx> for Pod<DynWidget> {
fn upcast(ctx: &mut ViewCtx, child: Pod<W>) -> Self {
let boxed_pod = ctx.boxed_pod(child);
ctx.new_pod(DynWidget {
inner: boxed_pod.inner.boxed(),
inner: child.boxed_widget_pod(),
})
}

Expand All @@ -49,7 +48,7 @@ impl<W: Widget> SuperElement<Pod<W>, ViewCtx> for Pod<DynWidget> {

impl<W: Widget> AnyElement<Pod<W>, ViewCtx> for Pod<DynWidget> {
fn replace_inner(mut this: Self::Mut<'_>, child: Pod<W>) -> Self::Mut<'_> {
DynWidget::replace_inner(&mut this, child.inner.boxed());
DynWidget::replace_inner(&mut this, child.boxed_widget_pod());
this
}
}
Expand Down
65 changes: 43 additions & 22 deletions xilem/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,7 @@ where
runtime: self.runtime,
};
let (pod, view_state) = first_view.build(&mut ctx);
let root_widget = RootWidget::from_pod(pod.inner);
let root_widget = RootWidget::from_pod(pod.into_widget_pod());
let driver = MasonryDriver {
current_view: first_view,
logic: self.logic,
Expand All @@ -174,21 +174,52 @@ where
}
}

/// A container for a [Masonry](masonry) widget to be used with Xilem.
/// A container for a yet to be inserted [Masonry](masonry) widget
/// to be used with Xilem.
///
/// Equivalent to [`WidgetPod<W>`], but in the [`xilem`](crate) crate to work around the orphan rule.
/// This exists for two reasons:
/// 1) The nearest equivalent type in Masonry, [`WidgetPod`], can't have
/// [Xilem Core](xilem_core) traits implemented on it due to Rust's orphan rules.
/// 2) `WidgetPod` is also used during a Widget's lifetime to contain its children,
/// and so might not actually own the underlying widget value.
/// When creating widgets in Xilem, layered views all want access to the - using
/// `WidgetPod` for this purpose would require fallible unwrapping.
pub struct Pod<W: Widget> {
pub inner: WidgetPod<W>,
// TODO: Maybe this should just be a (WidgetId, W) pair.
pub widget: W,
pub id: WidgetId,
pub transform: Affine,
}

impl<W: Widget> Pod<W> {
fn new(widget: W) -> Self {
Self {
widget,
id: WidgetId::next(),
transform: Default::default(),
}
}
fn into_widget_pod(self) -> WidgetPod<W> {
WidgetPod::new_with_id_and_transform(self.widget, self.id, self.transform)
}
fn boxed_widget_pod(self) -> WidgetPod<Box<dyn Widget>> {
WidgetPod::new_with_id_and_transform(Box::new(self.widget), self.id, self.transform)
}
fn boxed(self) -> Pod<Box<dyn Widget>> {
Pod {
widget: Box::new(self.widget),
id: self.id,
transform: self.transform,
}
}
}

impl<W: Widget> ViewElement for Pod<W> {
type Mut<'a> = WidgetMut<'a, W>;
}

impl<W: Widget> SuperElement<Pod<W>, ViewCtx> for Pod<Box<dyn Widget>> {
fn upcast(ctx: &mut ViewCtx, child: Pod<W>) -> Self {
ctx.boxed_pod(child)
fn upcast(_: &mut ViewCtx, child: Pod<W>) -> Self {
child.boxed()
}

fn with_downcast_val<R>(
Expand Down Expand Up @@ -287,21 +318,12 @@ impl ViewPathTracker for ViewCtx {

impl ViewCtx {
pub fn new_pod<W: Widget>(&mut self, widget: W) -> Pod<W> {
Pod {
inner: WidgetPod::new(widget),
}
Pod::new(widget)
}

pub fn new_pod_with_transform<W: Widget>(&mut self, widget: W, transform: Affine) -> Pod<W> {
Pod {
inner: WidgetPod::new_with_transform(widget, transform),
}
}

pub fn boxed_pod<W: Widget>(&mut self, pod: Pod<W>) -> Pod<Box<dyn Widget>> {
Pod {
inner: pod.inner.boxed(),
}
let mut pod = Pod::new(widget);
pod.transform = transform;
pod
}

pub fn with_leaf_action_widget<E: Widget>(
Expand All @@ -313,8 +335,7 @@ impl ViewCtx {

pub fn with_action_widget<E: Widget>(&mut self, f: impl FnOnce(&mut Self) -> Pod<E>) -> Pod<E> {
let value = f(self);
let id = value.inner.id();
self.record_action(id);
self.record_action(value.id);
value
}

Expand Down
36 changes: 18 additions & 18 deletions xilem/src/one_of.rs
Original file line number Diff line number Diff line change
Expand Up @@ -100,15 +100,15 @@ impl<
elem: OneOf<Pod<A>, Pod<B>, Pod<C>, Pod<D>, Pod<E>, Pod<F>, Pod<G>, Pod<H>, Pod<I>>,
) -> Self::OneOfElement {
match elem {
OneOf::A(w) => self.new_pod(OneOfWidget::A(w.inner)),
OneOf::B(w) => self.new_pod(OneOfWidget::B(w.inner)),
OneOf::C(w) => self.new_pod(OneOfWidget::C(w.inner)),
OneOf::D(w) => self.new_pod(OneOfWidget::D(w.inner)),
OneOf::E(w) => self.new_pod(OneOfWidget::E(w.inner)),
OneOf::F(w) => self.new_pod(OneOfWidget::F(w.inner)),
OneOf::G(w) => self.new_pod(OneOfWidget::G(w.inner)),
OneOf::H(w) => self.new_pod(OneOfWidget::H(w.inner)),
OneOf::I(w) => self.new_pod(OneOfWidget::I(w.inner)),
OneOf::A(w) => self.new_pod(OneOfWidget::A(w.into_widget_pod())),
OneOf::B(w) => self.new_pod(OneOfWidget::B(w.into_widget_pod())),
OneOf::C(w) => self.new_pod(OneOfWidget::C(w.into_widget_pod())),
OneOf::D(w) => self.new_pod(OneOfWidget::D(w.into_widget_pod())),
OneOf::E(w) => self.new_pod(OneOfWidget::E(w.into_widget_pod())),
OneOf::F(w) => self.new_pod(OneOfWidget::F(w.into_widget_pod())),
OneOf::G(w) => self.new_pod(OneOfWidget::G(w.into_widget_pod())),
OneOf::H(w) => self.new_pod(OneOfWidget::H(w.into_widget_pod())),
OneOf::I(w) => self.new_pod(OneOfWidget::I(w.into_widget_pod())),
}
}

Expand All @@ -117,15 +117,15 @@ impl<
new_elem: OneOf<Pod<A>, Pod<B>, Pod<C>, Pod<D>, Pod<E>, Pod<F>, Pod<G>, Pod<H>, Pod<I>>,
) {
let new_inner = match new_elem {
OneOf::A(w) => OneOfWidget::A(w.inner),
OneOf::B(w) => OneOfWidget::B(w.inner),
OneOf::C(w) => OneOfWidget::C(w.inner),
OneOf::D(w) => OneOfWidget::D(w.inner),
OneOf::E(w) => OneOfWidget::E(w.inner),
OneOf::F(w) => OneOfWidget::F(w.inner),
OneOf::G(w) => OneOfWidget::G(w.inner),
OneOf::H(w) => OneOfWidget::H(w.inner),
OneOf::I(w) => OneOfWidget::I(w.inner),
OneOf::A(w) => OneOfWidget::A(w.into_widget_pod()),
OneOf::B(w) => OneOfWidget::B(w.into_widget_pod()),
OneOf::C(w) => OneOfWidget::C(w.into_widget_pod()),
OneOf::D(w) => OneOfWidget::D(w.into_widget_pod()),
OneOf::E(w) => OneOfWidget::E(w.into_widget_pod()),
OneOf::F(w) => OneOfWidget::F(w.into_widget_pod()),
OneOf::G(w) => OneOfWidget::G(w.into_widget_pod()),
OneOf::H(w) => OneOfWidget::H(w.into_widget_pod()),
OneOf::I(w) => OneOfWidget::I(w.into_widget_pod()),
};
let old_inner = std::mem::replace(elem_mut.widget, new_inner);
match old_inner {
Expand Down
81 changes: 49 additions & 32 deletions xilem/src/view/button.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
// Copyright 2024 the Xilem Authors
// SPDX-License-Identifier: Apache-2.0

use masonry::text::StyleProperty;
use masonry::widget;
pub use masonry::PointerButton;
use xilem_core::ViewPathTracker;

use crate::core::{DynMessage, Mut, View, ViewMarker};
use crate::view::Label;
Expand Down Expand Up @@ -42,6 +42,8 @@ pub fn button_any_pointer<State, Action>(

#[must_use = "View values do nothing unless provided to Xilem."]
pub struct Button<F> {
// N.B. This widget is *implemented* to handle any kind of view with an element
// type of `Label` even though it currently does not do so.
label: Label,
transform: Affine,
callback: F,
Expand All @@ -53,6 +55,8 @@ impl<F> Transformable for Button<F> {
}
}

const LABEL_VIEW_ID: ViewId = ViewId::new(0);

impl<F> ViewMarker for Button<F> {}
impl<F, State, Action> View<State, Action, ViewCtx> for Button<F>
where
Expand All @@ -62,17 +66,12 @@ where
type ViewState = ();

fn build(&self, ctx: &mut ViewCtx) -> (Self::Element, Self::ViewState) {
let (child, ()) = ctx.with_id(LABEL_VIEW_ID, |ctx| {
View::<State, Action, _>::build(&self.label, ctx)
});
ctx.with_leaf_action_widget(|ctx| {
ctx.new_pod_with_transform(
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())),
),
widget::Button::from_label_pod(child.into_widget_pod()),
self.transform,
)
})
Expand All @@ -89,16 +88,31 @@ where
element.set_transform(self.transform);
}

<Label as View<State, Action, ViewCtx>>::rebuild(
&self.label,
&prev.label,
state,
ctx,
widget::Button::label_mut(&mut element),
);
ctx.with_id(LABEL_VIEW_ID, |ctx| {
View::<State, Action, _>::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>) {
fn teardown(
&self,
_: &mut Self::ViewState,
ctx: &mut ViewCtx,
mut element: Mut<Self::Element>,
) {
ctx.with_id(LABEL_VIEW_ID, |ctx| {
View::<State, Action, _>::teardown(
&self.label,
&mut (),
ctx,
widget::Button::label_mut(&mut element),
);
});
ctx.teardown_leaf(element);
}

Expand All @@ -109,21 +123,24 @@ where
message: DynMessage,
app_state: &mut State,
) -> MessageResult<Action> {
debug_assert!(
id_path.is_empty(),
"id path should be empty in Button::message"
);
match message.downcast::<masonry::Action>() {
Ok(action) => {
if let masonry::Action::ButtonPressed(button) = *action {
(self.callback)(app_state, button)
} else {
tracing::error!("Wrong action type in Button::message: {action:?}");
MessageResult::Stale(action)
match id_path.split_first() {
Some((&LABEL_VIEW_ID, rest)) => self.label.message(&mut (), rest, message, app_state),
None => match message.downcast::<masonry::Action>() {
Ok(action) => {
if let masonry::Action::ButtonPressed(button) = *action {
(self.callback)(app_state, button)
} else {
tracing::error!("Wrong action type in Button::message: {action:?}");
MessageResult::Stale(action)
}
}
}
Err(message) => {
tracing::error!("Wrong message type in Button::message: {message:?}");
Err(message) => {
tracing::error!("Wrong message type in Button::message: {message:?}");
MessageResult::Stale(message)
}
},
_ => {
tracing::warn!("Got unexpected id path in Button::message");
MessageResult::Stale(message)
}
}
Expand Down
14 changes: 7 additions & 7 deletions xilem/src/view/flex.rs
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ where
for child in elements.into_inner() {
widget = match child {
FlexElement::Child(child, params) => {
widget.with_flex_child_pod(child.inner, params)
widget.with_flex_child_pod(child.into_widget_pod(), params)
}
FlexElement::FixedSpacer(size) => widget.with_spacer(size),
FlexElement::FlexSpacer(flex) => widget.with_flex_spacer(flex),
Expand Down Expand Up @@ -233,8 +233,8 @@ impl SuperElement<Self, ViewCtx> for FlexElement {
}

impl<W: Widget> SuperElement<Pod<W>, ViewCtx> for FlexElement {
fn upcast(ctx: &mut ViewCtx, child: Pod<W>) -> Self {
Self::Child(ctx.boxed_pod(child), FlexParams::default())
fn upcast(_: &mut ViewCtx, child: Pod<W>) -> Self {
Self::Child(child.boxed(), FlexParams::default())
}

fn with_downcast_val<R>(
Expand All @@ -259,7 +259,7 @@ impl ElementSplice<FlexElement> for FlexSplice<'_> {
widget::Flex::insert_flex_child_pod(
&mut self.element,
self.idx,
child.inner,
child.into_widget_pod(),
params,
);
}
Expand All @@ -281,7 +281,7 @@ impl ElementSplice<FlexElement> for FlexSplice<'_> {
widget::Flex::insert_flex_child_pod(
&mut self.element,
self.idx,
child.inner,
child.into_widget_pod(),
params,
);
}
Expand Down Expand Up @@ -466,7 +466,7 @@ where

fn build(&self, ctx: &mut ViewCtx) -> (Self::Element, Self::ViewState) {
let (pod, state) = self.view.build(ctx);
(FlexElement::Child(ctx.boxed_pod(pod), self.params), state)
(FlexElement::Child(pod.boxed(), self.params), state)
}

fn rebuild(
Expand Down Expand Up @@ -753,7 +753,7 @@ where
widget::Flex::insert_flex_child_pod(
&mut element.parent,
element.idx,
child.inner,
child.boxed_widget_pod(),
params,
);
} else {
Expand Down
Loading
Loading