Skip to content

Commit

Permalink
xilem_core: Add a WithoutElements ViewSequence
Browse files Browse the repository at this point in the history
  • Loading branch information
Philipp-M committed Sep 23, 2024
1 parent ecc9468 commit e92614f
Show file tree
Hide file tree
Showing 6 changed files with 154 additions and 45 deletions.
26 changes: 13 additions & 13 deletions xilem/examples/mason.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
use std::time::Duration;

use xilem::{
core::{fork, run_once, without_elements},
tokio::time,
view::{
button, button_any_pointer, checkbox, flex, label, prose, task, textbox, Axis,
Expand Down Expand Up @@ -115,18 +116,18 @@ fn app_logic(data: &mut AppData) -> impl WidgetView<AppData> {

fn toggleable(data: &mut AppData) -> impl WidgetView<AppData> {
if data.active {
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")),
)
flex((
button("Deactivate", |data: &mut AppData| {
data.active = false;
}),
button("Unlimited Power", |data: &mut AppData| {
data.count = -1_000_000;
}),
without_elements(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 Expand Up @@ -165,7 +166,6 @@ fn main() {

#[cfg(target_os = "android")]
use winit::platform::android::activity::AndroidApp;
use xilem_core::{fork, run_once};

#[cfg(target_os = "android")]
// Safety: We are following `android_activity`'s docs here
Expand Down
4 changes: 2 additions & 2 deletions xilem/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -224,12 +224,12 @@ where
/// }
/// ```
pub trait WidgetViewSequence<State, Action = ()>:
ViewSequence<State, Action, ViewCtx, Pod<any_view::DynWidget>>
ViewSequence<State, Action, ViewCtx, Pod<Box<dyn Widget>>>
{
}

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

Expand Down
28 changes: 26 additions & 2 deletions xilem_core/src/docs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
//! # struct InterestingPrimitive;
//! ```
use crate::{run_once, View, ViewPathTracker};
use crate::{run_once, NoElement, View, ViewPathTracker, ViewSequence};

/// A type used for documentation
pub enum Fake {}
Expand All @@ -62,11 +62,35 @@ impl ViewPathTracker for Fake {
pub trait DocsView<State, Action = ()>: View<State, Action, Fake> {}
impl<V, State, Action> DocsView<State, Action> for V where V: View<State, Action, Fake> {}

/// A version of [`ViewSequence`] used for documentation.
///
/// This will often be imported by a different name in a hidden use item.
///
/// In most cases, that name will be `WidgetViewSequence`, as Xilem Core's documentation is
/// primarily targeted at users of [Xilem](https://crates.io/crates/xilem/).
pub trait DocsViewSequence<State, Action = ()>:
ViewSequence<State, Action, Fake, NoElement>
{
}

impl<Seq, State, Action> DocsViewSequence<State, Action> for Seq where
Seq: ViewSequence<State, Action, Fake, NoElement>
{
}

/// A state type usable in a component
pub struct State;

/// A minimal component.
pub fn some_component<Action>(_: &mut State) -> impl DocsView<State, Action> {
pub fn some_component<Action>(_: &mut State) -> impl DocsView<State, Action, Element = NoElement> {
// The view which does nothing already exists in `run_once`.
run_once(|| {})
}

/// A minimal component with generic State.
pub fn some_component_generic<State, Action>(
_: &mut State,
) -> impl DocsView<State, Action, Element = NoElement> {
// The view which does nothing already exists in `run_once`.
run_once(|| {})
}
2 changes: 1 addition & 1 deletion xilem_core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,6 @@ mod any_view;
pub use any_view::AnyView;

mod sequence;
pub use sequence::{AppendVec, ElementSplice, ViewSequence};
pub use sequence::{without_elements, AppendVec, ElementSplice, ViewSequence, WithoutElements};

pub mod docs;
112 changes: 111 additions & 1 deletion xilem_core/src/sequence.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,14 @@

//! Support for sequences of views with a shared element type.
use core::marker::PhantomData;
use core::sync::atomic::AtomicBool;
use core::sync::atomic::Ordering;

use alloc::vec::Drain;
use alloc::vec::Vec;

// use crate::element::NoElement;
use crate::element::NoElement;
use crate::{
DynMessage, MessageResult, SuperElement, View, ViewElement, ViewId, ViewMarker, ViewPathTracker,
};
Expand Down Expand Up @@ -785,3 +786,112 @@ impl_view_tuple!(M0, Seq0, 0; M1, Seq1, 1; M2, Seq2, 2; M3, Seq3, 3; M4, Seq4, 4
impl_view_tuple!(M0, Seq0, 0; M1, Seq1, 1; M2, Seq2, 2; M3, Seq3, 3; M4, Seq4, 4; M5, Seq5, 5; M6, Seq6, 6; M7, Seq7, 7; M8, Seq8, 8; M9, Seq9, 9; M10, Seq10, 10; M11, Seq11, 11; M12, Seq12, 12; M13, Seq13, 13);
impl_view_tuple!(M0, Seq0, 0; M1, Seq1, 1; M2, Seq2, 2; M3, Seq3, 3; M4, Seq4, 4; M5, Seq5, 5; M6, Seq6, 6; M7, Seq7, 7; M8, Seq8, 8; M9, Seq9, 9; M10, Seq10, 10; M11, Seq11, 11; M12, Seq12, 12; M13, Seq13, 13; M14, Seq14, 14);
impl_view_tuple!(M0, Seq0, 0; M1, Seq1, 1; M2, Seq2, 2; M3, Seq3, 3; M4, Seq4, 4; M5, Seq5, 5; M6, Seq6, 6; M7, Seq7, 7; M8, Seq8, 8; M9, Seq9, 9; M10, Seq10, 10; M11, Seq11, 11; M12, Seq12, 12; M13, Seq13, 13; M14, Seq14, 14; M15, Seq15, 15);

/// A stub `ElementSplice` implementation for `NoElement`.
///
/// It is technically possible for someone to create an implementation of `ViewSequence`
/// which uses a `NoElement` `ElementSplice`. But we don't think that sequence could be meaningful.
pub(crate) struct NoElements;

impl ElementSplice<NoElement> for NoElements {
fn with_scratch<R>(&mut self, f: impl FnOnce(&mut AppendVec<NoElement>) -> R) -> R {
let mut append_vec = AppendVec::default();
f(&mut append_vec)
}

fn insert(&mut self, _: NoElement) {}

fn mutate<R>(&mut self, f: impl FnOnce(<NoElement as crate::ViewElement>::Mut<'_>) -> R) -> R {
f(())
}

fn skip(&mut self, _: usize) {}

fn delete<R>(&mut self, f: impl FnOnce(<NoElement as crate::ViewElement>::Mut<'_>) -> R) -> R {
f(())
}
}

/// The [`ViewSequence`] for [`without_elements`], see its documentation for more context.
pub struct WithoutElements<Seq, State, Action, Context, Message> {
seq: Seq,
phantom: PhantomData<fn() -> (State, Action, Context, Message)>,
}

/// A [`ViewSequence`] that doesn't contain any elements, it can be used everywhere where a view sequence (with or without elements) is expected.
///
/// # Examples
///
/// ```
/// # use xilem_core::docs::{DocsViewSequence as WidgetViewSequence, some_component_generic as component};
/// use xilem_core::{without_elements, run_once};
///
/// fn app_logic(state: &mut AppState) -> impl WidgetViewSequence<AppState> {
/// (component(state), without_elements(run_once(|| {})))
/// }
///
/// struct AppState;
/// ```
pub fn without_elements<State, Action, Context, Message, Seq>(
seq: Seq,
) -> WithoutElements<Seq, State, Action, Context, Message>
where
State: 'static,
Action: 'static,
Message: 'static,
Context: ViewPathTracker + 'static,
Seq: ViewSequence<State, Action, Context, NoElement, Message>,
{
WithoutElements {
seq,
phantom: PhantomData,
}
}

impl<State, Action, Context, Element, Message, Seq>
ViewSequence<State, Action, Context, Element, Message>
for WithoutElements<Seq, State, Action, Context, Message>
where
State: 'static,
Action: 'static,
Message: 'static,
Element: ViewElement,
Context: ViewPathTracker + 'static,
Seq: ViewSequence<State, Action, Context, NoElement, Message>,
{
type SeqState = Seq::SeqState;

fn seq_build(&self, ctx: &mut Context, _elements: &mut AppendVec<Element>) -> Self::SeqState {
self.seq.seq_build(ctx, &mut AppendVec::default())
}

fn seq_rebuild(
&self,
prev: &Self,
seq_state: &mut Self::SeqState,
ctx: &mut Context,
_elements: &mut impl ElementSplice<Element>,
) {
self.seq
.seq_rebuild(&prev.seq, seq_state, ctx, &mut NoElements);
}

fn seq_teardown(
&self,
seq_state: &mut Self::SeqState,
ctx: &mut Context,
_elements: &mut impl ElementSplice<Element>,
) {
self.seq.seq_teardown(seq_state, ctx, &mut NoElements);
}

fn seq_message(
&self,
seq_state: &mut Self::SeqState,
id_path: &[ViewId],
message: Message,
app_state: &mut State,
) -> crate::MessageResult<Action, Message> {
self.seq.seq_message(seq_state, id_path, message, app_state)
}
}
27 changes: 1 addition & 26 deletions xilem_core/src/views/fork.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
// SPDX-License-Identifier: Apache-2.0

use crate::{
AppendVec, ElementSplice, Mut, NoElement, View, ViewId, ViewMarker, ViewPathTracker,
sequence::NoElements, AppendVec, Mut, NoElement, View, ViewId, ViewMarker, ViewPathTracker,
ViewSequence,
};

Expand Down Expand Up @@ -105,28 +105,3 @@ where
}
}
}

/// A stub `ElementSplice` implementation for `NoElement`.
///
/// It is technically possible for someone to create an implementation of `ViewSequence`
/// which uses a `NoElement` `ElementSplice`. But we don't think that sequence could be meaningful.
struct NoElements;

impl ElementSplice<NoElement> for NoElements {
fn with_scratch<R>(&mut self, f: impl FnOnce(&mut AppendVec<NoElement>) -> R) -> R {
let mut append_vec = AppendVec::default();
f(&mut append_vec)
}

fn insert(&mut self, _: NoElement) {}

fn mutate<R>(&mut self, f: impl FnOnce(<NoElement as crate::ViewElement>::Mut<'_>) -> R) -> R {
f(())
}

fn skip(&mut self, _: usize) {}

fn delete<R>(&mut self, f: impl FnOnce(<NoElement as crate::ViewElement>::Mut<'_>) -> R) -> R {
f(())
}
}

0 comments on commit e92614f

Please sign in to comment.