Skip to content

Commit

Permalink
Use ViewMarker trait instead of the generic Marker to allow `impl…
Browse files Browse the repository at this point in the history
… ViewSequence` as return type (#472)

This allows returning `impl ViewSequence` as this was previously not
possibly as the `Marker` generic parameter couldn't be named (easily) in
`ViewSequence<..., Marker>`.

This also provides specialized `ViewSequence`s for xilem
(`WidgetViewSequence` and `FlexSequence`) as well as for xilem_web
(`DomFragment`). Additional doc-tests/documentation and a small example
(in `counter`) for xilem_web is provided as well.

This has the drawback to not being able to reeimplement `ViewSequence`
for types that already implement `View`, which was previously possible
(as seen by the now removed `NoElementView` `ViewSequence`
implementation).
And additionally by introducing more boilerplate by having to implement
`ViewMarker` for every type that implements `View`.

---------

Co-authored-by: Daniel McNab <[email protected]>
  • Loading branch information
Philipp-M and DJMcNab authored Aug 5, 2024
1 parent 544a4a1 commit 24427bb
Show file tree
Hide file tree
Showing 38 changed files with 316 additions and 232 deletions.
6 changes: 3 additions & 3 deletions xilem/examples/calc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use masonry::widget::{CrossAxisAlignment, MainAxisAlignment};
use winit::dpi::LogicalSize;
use winit::error::EventLoopError;
use winit::window::Window;
use xilem::view::Flex;
use xilem::view::{Flex, FlexSequence};
use xilem::EventLoopBuilder;
use xilem::{
view::{button, flex, label, sized_box, Axis, FlexExt as _, FlexSpacer},
Expand Down Expand Up @@ -254,7 +254,7 @@ fn app_logic(data: &mut Calculator) -> impl WidgetView<Calculator> {
}

/// Creates a horizontal centered flex row designed for the display portion of the calculator.
pub fn centered_flex_row<Seq, Marker>(sequence: Seq) -> Flex<Seq, Marker> {
pub fn centered_flex_row<State, Seq: FlexSequence<State>>(sequence: Seq) -> Flex<Seq, State> {
flex(sequence)
.direction(Axis::Horizontal)
.cross_axis_alignment(CrossAxisAlignment::Center)
Expand All @@ -263,7 +263,7 @@ pub fn centered_flex_row<Seq, Marker>(sequence: Seq) -> Flex<Seq, Marker> {
}

/// Creates a horizontal filled flex row designed to be used in a grid.
pub fn flex_row<Seq, Marker>(sequence: Seq) -> Flex<Seq, Marker> {
pub fn flex_row<State, Seq: FlexSequence<State>>(sequence: Seq) -> Flex<Seq, State> {
flex(sequence)
.direction(Axis::Horizontal)
.cross_axis_alignment(CrossAxisAlignment::Fill)
Expand Down
2 changes: 1 addition & 1 deletion xilem/examples/elm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ enum CountMessage {

// `map_action()` is basically how elm works, i.e. provide a message that the parent view has to handle to update the state.
// In this case the parent adjusts the count that is given to this view according to the message
fn elm_counter<T>(count: i32) -> impl WidgetView<T, CountMessage> {
fn elm_counter<T: 'static>(count: i32) -> impl WidgetView<T, CountMessage> {
flex((
label(format!("elm count: {count}")),
button("+", |_| CountMessage::Increment),
Expand Down
20 changes: 11 additions & 9 deletions xilem/examples/mason.rs
Original file line number Diff line number Diff line change
Expand Up @@ -109,16 +109,18 @@ fn app_logic(data: &mut AppData) -> impl WidgetView<AppData> {

fn toggleable(data: &mut AppData) -> impl WidgetView<AppData> {
if data.active {
flex((
button("Deactivate", |data: &mut AppData| {
data.active = false;
}),
button("Unlimited Power", |data: &mut AppData| {
data.count = -1_000_000;
}),
fork(
flex((
button("Deactivate", |data: &mut AppData| {
data.active = false;
}),
button("Unlimited Power", |data: &mut AppData| {
data.count = -1_000_000;
}),
))
.direction(Axis::Horizontal),
run_once(|| tracing::warn!("The pathway to unlimited power has been revealed")),
))
.direction(Axis::Horizontal)
)
.boxed()
} else {
button("Activate", |data: &mut AppData| data.active = true).boxed()
Expand Down
25 changes: 25 additions & 0 deletions xilem/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ use winit::{
};
use xilem_core::{
AsyncCtx, MessageResult, RawProxy, SuperElement, View, ViewElement, ViewId, ViewPathTracker,
ViewSequence,
};

pub use masonry::{
Expand Down Expand Up @@ -209,6 +210,30 @@ where
type Widget = W;
}

/// An ordered sequence of widget views, it's used for `0..N` views.
/// See [`ViewSequence`] for more technical details.
///
/// # Examples
///
/// ```
/// use xilem::{view::prose, WidgetViewSequence};
///
/// fn prose_sequence<State: 'static>(
/// texts: impl Iterator<Item = &'static str>,
/// ) -> impl WidgetViewSequence<State> {
/// texts.map(prose).collect::<Vec<_>>()
/// }
/// ```
pub trait WidgetViewSequence<State, Action = ()>:
ViewSequence<State, Action, ViewCtx, Pod<any_view::DynWidget>>
{
}

impl<Seq, State, Action> WidgetViewSequence<State, Action> for Seq where
Seq: ViewSequence<State, Action, ViewCtx, Pod<any_view::DynWidget>>
{
}

type WidgetMap = HashMap<WidgetId, Vec<ViewId>>;

pub struct ViewCtx {
Expand Down
5 changes: 4 additions & 1 deletion xilem/src/view/async_repeat.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@
use std::{future::Future, marker::PhantomData, sync::Arc};

use tokio::task::JoinHandle;
use xilem_core::{DynMessage, Message, MessageProxy, NoElement, View, ViewId, ViewPathTracker};
use xilem_core::{
DynMessage, Message, MessageProxy, NoElement, View, ViewId, ViewMarker, ViewPathTracker,
};

use crate::ViewCtx;

Expand Down Expand Up @@ -70,6 +72,7 @@ pub struct AsyncRepeat<F, H, M> {
message: PhantomData<fn() -> M>,
}

impl<F, H, M> ViewMarker for AsyncRepeat<F, H, M> {}
impl<State, Action, F, H, M, Fut> View<State, Action, ViewCtx> for AsyncRepeat<F, H, M>
where
F: Fn(MessageProxy<M>) -> Fut + 'static,
Expand Down
3 changes: 2 additions & 1 deletion xilem/src/view/button.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

use crate::{core::View, Pod};
use masonry::{widget, ArcStr};
use xilem_core::Mut;
use xilem_core::{Mut, ViewMarker};

pub use masonry::PointerButton;

Expand Down Expand Up @@ -41,6 +41,7 @@ pub struct Button<F> {
callback: F,
}

impl<F> ViewMarker for Button<F> {}
impl<F, State, Action> View<State, Action, ViewCtx> for Button<F>
where
F: Fn(&mut State, PointerButton) -> MessageResult<Action> + Send + Sync + 'static,
Expand Down
3 changes: 2 additions & 1 deletion xilem/src/view/checkbox.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
// SPDX-License-Identifier: Apache-2.0

use masonry::{widget, ArcStr};
use xilem_core::Mut;
use xilem_core::{Mut, ViewMarker};

use crate::{MessageResult, Pod, View, ViewCtx, ViewId};

Expand All @@ -27,6 +27,7 @@ pub struct Checkbox<F> {
callback: F,
}

impl<F> ViewMarker for Checkbox<F> {}
impl<F, State, Action> View<State, Action, ViewCtx> for Checkbox<F>
where
F: Fn(&mut State, bool) -> Action + Send + Sync + 'static,
Expand Down
49 changes: 41 additions & 8 deletions xilem/src/view/flex.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,36 +9,38 @@ use masonry::{
};
use xilem_core::{
AppendVec, DynMessage, ElementSplice, MessageResult, Mut, SuperElement, View, ViewElement,
ViewId, ViewPathTracker, ViewSequence,
ViewId, ViewMarker, ViewPathTracker, ViewSequence,
};

use crate::{AnyWidgetView, Pod, ViewCtx, WidgetView};

pub use masonry::widget::{Axis, CrossAxisAlignment, FlexParams, MainAxisAlignment};

pub fn flex<Seq, Marker>(sequence: Seq) -> Flex<Seq, Marker> {
pub fn flex<State, Action, Seq: FlexSequence<State, Action>>(
sequence: Seq,
) -> Flex<Seq, State, Action> {
Flex {
phantom: PhantomData,
sequence,
axis: Axis::Vertical,
cross_axis_alignment: CrossAxisAlignment::Center,
main_axis_alignment: MainAxisAlignment::Start,
fill_major_axis: false,
gap: None,
phantom: PhantomData,
}
}

pub struct Flex<Seq, Marker> {
pub struct Flex<Seq, State, Action = ()> {
sequence: Seq,
axis: Axis,
cross_axis_alignment: CrossAxisAlignment,
main_axis_alignment: MainAxisAlignment,
fill_major_axis: bool,
phantom: PhantomData<fn() -> Marker>,
gap: Option<f64>,
phantom: PhantomData<fn() -> (State, Action)>,
}

impl<Seq, Marker> Flex<Seq, Marker> {
impl<Seq, State, Action> Flex<Seq, State, Action> {
pub fn direction(mut self, axis: Axis) -> Self {
self.axis = axis;
self
Expand Down Expand Up @@ -85,9 +87,12 @@ impl<Seq, Marker> Flex<Seq, Marker> {
}
}

impl<State, Action, Seq, Marker: 'static> View<State, Action, ViewCtx> for Flex<Seq, Marker>
impl<Seq, State, Action> ViewMarker for Flex<Seq, State, Action> {}
impl<State, Action, Seq> View<State, Action, ViewCtx> for Flex<Seq, State, Action>
where
Seq: ViewSequence<State, Action, ViewCtx, FlexElement, Marker>,
State: 'static,
Action: 'static,
Seq: FlexSequence<State, Action>,
{
type Element = Pod<widget::Flex>;

Expand Down Expand Up @@ -303,6 +308,31 @@ impl ElementSplice<FlexElement> for FlexSplice<'_> {
}
}

/// An ordered sequence of views for a [`Flex`] view.
/// See [`ViewSequence`] for more technical details.
///
/// # Examples
///
/// ```
/// use xilem::view::{label, FlexSequence, FlexExt as _};
///
/// fn label_sequence<State: 'static>(
/// labels: impl Iterator<Item = &'static str>,
/// flex: f64,
/// ) -> impl FlexSequence<State> {
/// labels.map(|l| label(l).flex(flex)).collect::<Vec<_>>()
/// }
/// ```
pub trait FlexSequence<State, Action = ()>:
ViewSequence<State, Action, ViewCtx, FlexElement>
{
}

impl<Seq, State, Action> FlexSequence<State, Action> for Seq where
Seq: ViewSequence<State, Action, ViewCtx, FlexElement>
{
}

/// A trait which extends a [`WidgetView`] with methods to provide parameters for a flex item, or being able to use it interchangeably with a spacer
pub trait FlexExt<State, Action>: WidgetView<State, Action> {
/// Applies [`impl Into<FlexParams>`](`FlexParams`) to this view, can be used as child of a [`Flex`] [`View`]
Expand Down Expand Up @@ -407,6 +437,7 @@ where
}
}

impl<V, State, Action> ViewMarker for FlexItem<V, State, Action> {}
impl<State, Action, V> View<State, Action, ViewCtx> for FlexItem<V, State, Action>
where
State: 'static,
Expand Down Expand Up @@ -485,6 +516,7 @@ impl<State, Action> From<FlexSpacer> for AnyFlexChild<State, Action> {
}
}

impl ViewMarker for FlexSpacer {}
// This impl doesn't require a view id, as it neither receives, nor sends any messages
// If this should ever change, it's necessary to adjust the `AnyFlexChild` `View` impl
impl<State, Action> View<State, Action, ViewCtx> for FlexSpacer {
Expand Down Expand Up @@ -593,6 +625,7 @@ pub struct AnyFlexChildState<State: 'static, Action: 'static> {
generation: u64,
}

impl<State, Action> ViewMarker for AnyFlexChild<State, Action> {}
impl<State, Action> View<State, Action, ViewCtx> for AnyFlexChild<State, Action>
where
State: 'static,
Expand Down
3 changes: 2 additions & 1 deletion xilem/src/view/label.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
// SPDX-License-Identifier: Apache-2.0

use masonry::{text::TextBrush, widget, ArcStr};
use xilem_core::Mut;
use xilem_core::{Mut, ViewMarker};

use crate::{Color, MessageResult, Pod, TextAlignment, View, ViewCtx, ViewId};

Expand Down Expand Up @@ -43,6 +43,7 @@ impl Label {
}
}

impl ViewMarker for Label {}
impl<State, Action> View<State, Action, ViewCtx> for Label {
type Element = Pod<widget::Label>;
type ViewState = ();
Expand Down
3 changes: 2 additions & 1 deletion xilem/src/view/prose.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
// SPDX-License-Identifier: Apache-2.0

use masonry::{text::TextBrush, widget, ArcStr};
use xilem_core::Mut;
use xilem_core::{Mut, ViewMarker};

use crate::{Color, MessageResult, Pod, TextAlignment, View, ViewCtx, ViewId};

Expand Down Expand Up @@ -44,6 +44,7 @@ impl Prose {
}
}

impl ViewMarker for Prose {}
impl<State, Action> View<State, Action, ViewCtx> for Prose {
type Element = Pod<widget::Prose>;
type ViewState = ();
Expand Down
2 changes: 2 additions & 0 deletions xilem/src/view/sized_box.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// SPDX-License-Identifier: Apache-2.0

use masonry::widget;
use xilem_core::ViewMarker;

use crate::{
core::{Mut, View, ViewId},
Expand Down Expand Up @@ -74,6 +75,7 @@ impl<V> SizedBox<V> {
}
}

impl<V> ViewMarker for SizedBox<V> {}
impl<V, State, Action> View<State, Action, ViewCtx> for SizedBox<V>
where
V: WidgetView<State, Action>,
Expand Down
3 changes: 2 additions & 1 deletion xilem/src/view/textbox.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
// SPDX-License-Identifier: Apache-2.0

use masonry::{text::TextBrush, widget};
use xilem_core::{Mut, View};
use xilem_core::{Mut, View, ViewMarker};

use crate::{Color, MessageResult, Pod, TextAlignment, ViewCtx, ViewId};

Expand Down Expand Up @@ -63,6 +63,7 @@ impl<State, Action> Textbox<State, Action> {
}
}

impl<State, Action> ViewMarker for Textbox<State, Action> {}
impl<State: 'static, Action: 'static> View<State, Action, ViewCtx> for Textbox<State, Action> {
type Element = Pod<widget::Textbox>;
type ViewState = ();
Expand Down
3 changes: 2 additions & 1 deletion xilem_core/examples/filesystem.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
use std::{io::stdin, path::PathBuf};

use xilem_core::{
AnyElement, AnyView, Mut, SuperElement, View, ViewElement, ViewId, ViewPathTracker,
AnyElement, AnyView, Mut, SuperElement, View, ViewElement, ViewId, ViewMarker, ViewPathTracker,
};

#[derive(Debug)]
Expand Down Expand Up @@ -145,6 +145,7 @@ impl ViewElement for FsPath {
type Mut<'a> = &'a mut PathBuf;
}

impl ViewMarker for File {}
impl<State, Action> View<State, Action, ViewCtx> for File {
type Element = FsPath;
type ViewState = ();
Expand Down
4 changes: 3 additions & 1 deletion xilem_core/examples/user_interface.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@
use core::any::Any;

use xilem_core::{
DynMessage, MessageResult, Mut, SuperElement, View, ViewElement, ViewId, ViewPathTracker,
DynMessage, MessageResult, Mut, SuperElement, View, ViewElement, ViewId, ViewMarker,
ViewPathTracker,
};

pub fn app_logic(_: &mut u32) -> impl WidgetView<u32> {
Expand Down Expand Up @@ -44,6 +45,7 @@ impl<W: Widget> ViewElement for WidgetPod<W> {
type Mut<'a> = WidgetMut<'a, W>;
}

impl ViewMarker for Button {}
impl<State, Action> View<State, Action, ViewCtx> for Button {
type Element = WidgetPod<ButtonWidget>;
type ViewState = ();
Expand Down
Loading

0 comments on commit 24427bb

Please sign in to comment.