From e92614f98d8d858c29995e75f2b7aaa79b1ec361 Mon Sep 17 00:00:00 2001 From: Philipp Mildenberger Date: Mon, 23 Sep 2024 21:55:31 +0200 Subject: [PATCH] xilem_core: Add a `WithoutElements` `ViewSequence` --- xilem/examples/mason.rs | 26 ++++---- xilem/src/lib.rs | 4 +- xilem_core/src/docs.rs | 28 ++++++++- xilem_core/src/lib.rs | 2 +- xilem_core/src/sequence.rs | 112 ++++++++++++++++++++++++++++++++++- xilem_core/src/views/fork.rs | 27 +-------- 6 files changed, 154 insertions(+), 45 deletions(-) diff --git a/xilem/examples/mason.rs b/xilem/examples/mason.rs index c13120f43..af3185e1b 100644 --- a/xilem/examples/mason.rs +++ b/xilem/examples/mason.rs @@ -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, @@ -115,18 +116,18 @@ fn app_logic(data: &mut AppData) -> impl WidgetView { fn toggleable(data: &mut AppData) -> impl WidgetView { 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() @@ -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 diff --git a/xilem/src/lib.rs b/xilem/src/lib.rs index c9f4bf9c7..8929bc6b5 100644 --- a/xilem/src/lib.rs +++ b/xilem/src/lib.rs @@ -224,12 +224,12 @@ where /// } /// ``` pub trait WidgetViewSequence: - ViewSequence> + ViewSequence>> { } impl WidgetViewSequence for Seq where - Seq: ViewSequence> + Seq: ViewSequence>> { } diff --git a/xilem_core/src/docs.rs b/xilem_core/src/docs.rs index b1ff5801c..2c309aea6 100644 --- a/xilem_core/src/docs.rs +++ b/xilem_core/src/docs.rs @@ -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 {} @@ -62,11 +62,35 @@ impl ViewPathTracker for Fake { pub trait DocsView: View {} impl DocsView for V where V: View {} +/// 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: + ViewSequence +{ +} + +impl DocsViewSequence for Seq where + Seq: ViewSequence +{ +} + /// A state type usable in a component pub struct State; /// A minimal component. -pub fn some_component(_: &mut State) -> impl DocsView { +pub fn some_component(_: &mut State) -> impl DocsView { + // The view which does nothing already exists in `run_once`. + run_once(|| {}) +} + +/// A minimal component with generic State. +pub fn some_component_generic( + _: &mut State, +) -> impl DocsView { // The view which does nothing already exists in `run_once`. run_once(|| {}) } diff --git a/xilem_core/src/lib.rs b/xilem_core/src/lib.rs index f8d77d1e4..e1ff01445 100644 --- a/xilem_core/src/lib.rs +++ b/xilem_core/src/lib.rs @@ -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; diff --git a/xilem_core/src/sequence.rs b/xilem_core/src/sequence.rs index a6b1fb404..034389ed5 100644 --- a/xilem_core/src/sequence.rs +++ b/xilem_core/src/sequence.rs @@ -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, }; @@ -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 for NoElements { + fn with_scratch(&mut self, f: impl FnOnce(&mut AppendVec) -> R) -> R { + let mut append_vec = AppendVec::default(); + f(&mut append_vec) + } + + fn insert(&mut self, _: NoElement) {} + + fn mutate(&mut self, f: impl FnOnce(::Mut<'_>) -> R) -> R { + f(()) + } + + fn skip(&mut self, _: usize) {} + + fn delete(&mut self, f: impl FnOnce(::Mut<'_>) -> R) -> R { + f(()) + } +} + +/// The [`ViewSequence`] for [`without_elements`], see its documentation for more context. +pub struct WithoutElements { + seq: Seq, + phantom: PhantomData (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 { +/// (component(state), without_elements(run_once(|| {}))) +/// } +/// +/// struct AppState; +/// ``` +pub fn without_elements( + seq: Seq, +) -> WithoutElements +where + State: 'static, + Action: 'static, + Message: 'static, + Context: ViewPathTracker + 'static, + Seq: ViewSequence, +{ + WithoutElements { + seq, + phantom: PhantomData, + } +} + +impl + ViewSequence + for WithoutElements +where + State: 'static, + Action: 'static, + Message: 'static, + Element: ViewElement, + Context: ViewPathTracker + 'static, + Seq: ViewSequence, +{ + type SeqState = Seq::SeqState; + + fn seq_build(&self, ctx: &mut Context, _elements: &mut AppendVec) -> 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, + ) { + 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, + ) { + 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 { + self.seq.seq_message(seq_state, id_path, message, app_state) + } +} diff --git a/xilem_core/src/views/fork.rs b/xilem_core/src/views/fork.rs index 07ad89054..a58b7e39c 100644 --- a/xilem_core/src/views/fork.rs +++ b/xilem_core/src/views/fork.rs @@ -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, }; @@ -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 for NoElements { - fn with_scratch(&mut self, f: impl FnOnce(&mut AppendVec) -> R) -> R { - let mut append_vec = AppendVec::default(); - f(&mut append_vec) - } - - fn insert(&mut self, _: NoElement) {} - - fn mutate(&mut self, f: impl FnOnce(::Mut<'_>) -> R) -> R { - f(()) - } - - fn skip(&mut self, _: usize) {} - - fn delete(&mut self, f: impl FnOnce(::Mut<'_>) -> R) -> R { - f(()) - } -}