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

Add an AnyViewSequence which is analogous to the AnyView for ViewSequences #158

Draft
wants to merge 2 commits into
base: main
Choose a base branch
from
Draft
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
150 changes: 142 additions & 8 deletions crates/xilem_core/src/sequence.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ macro_rules! impl_view_tuple {

#[macro_export]
macro_rules! generate_viewsequence_trait {
($viewseq:ident, $view:ident, $viewmarker: ident, $bound:ident, $cx:ty, $changeflags:ty, $pod:ty; $( $ss:tt )* ) => {
($viewseq:ident, $anyviewseq:ident, $boxedviewseq:ident, $view:ident, $viewmarker: ident, $bound:ident, $cx:ty, $changeflags:ty, $pod:ty; ($( $ss:tt )*); ($( $ss_bounds:tt )*) ) => {
/// This trait represents a (possibly empty) sequence of views.
///
/// It is up to the parent view how to lay out and display them.
Expand All @@ -75,7 +75,7 @@ macro_rules! generate_viewsequence_trait {
cx: &mut $cx,
prev: &Self,
state: &mut Self::State,
element: &mut $crate::VecSplice<$pod>,
elements: &mut $crate::VecSplice<$pod>,
) -> $changeflags;

/// Propagate a message.
Expand All @@ -94,6 +94,140 @@ macro_rules! generate_viewsequence_trait {
fn count(&self, state: &Self::State) -> usize;
}

/// A trait enabling type erasure of view sequences.
pub trait $anyviewseq<T, A = ()> $( $ss )* {
fn as_any(&self) -> &dyn std::any::Any;

/// Build the associated widgets and initialize all states.
fn dyn_build(&self, cx: &mut $cx, elements: &mut Vec<$pod>) -> Box<dyn std::any::Any $( $ss_bounds )* >;

/// Update the associated widget.
///
/// Returns `true` when anything has changed.
fn dyn_rebuild(
&self,
cx: &mut $cx,
prev: &dyn $anyviewseq<T, A>,
state: &mut Box<dyn std::any::Any $( $ss_bounds )* >,
elements: &mut $crate::VecSplice<$pod>,
) -> $changeflags;

/// Propagate a message.
///
/// Handle a message, propagating to elements if needed. Here, `id_path` is a slice
/// of ids beginning at an element of this view_sequence.
fn dyn_message(
&self,
id_path: &[$crate::Id],
state: &mut dyn std::any::Any,
message: Box<dyn std::any::Any>,
app_state: &mut T,
) -> $crate::MessageResult<A>;

/// Returns the current amount of widgets built by this sequence.
fn dyn_count(&self, state: &dyn std::any::Any) -> usize;
}


impl<T, A, VS: $viewseq<T, A> + 'static> $anyviewseq<T, A> for VS
where
VS::State: 'static,
{
fn as_any(&self) -> &dyn std::any::Any {
self
}

fn dyn_build(&self, cx: &mut $cx, elements: &mut Vec<$pod>) -> Box<dyn std::any::Any $( $ss_bounds )* > {
Box::new(self.build(cx, elements))
}

fn dyn_rebuild(
&self,
cx: &mut $cx,
prev: &dyn $anyviewseq<T, A>,
state: &mut Box<dyn std::any::Any $( $ss_bounds )* >,
elements: &mut $crate::VecSplice<$pod>,
) -> $changeflags
{
use std::ops::DerefMut;
if let Some(prev) = prev.as_any().downcast_ref() {
if let Some(state) = state.downcast_mut() {
self.rebuild(cx, prev, state, elements)
} else {
eprintln!("downcast of state failed in dyn_rebuild");
<$changeflags>::default()
}
} else {
let new_state = elements.as_vec(|vec| self.build(cx, vec));
*state = Box::new(new_state);
<$changeflags>::tree_structure()
}
}

fn dyn_message(
&self,
id_path: &[$crate::Id],
state: &mut dyn std::any::Any,
message: Box<dyn std::any::Any>,
app_state: &mut T,
) -> $crate::MessageResult<A> {
if let Some(state) = state.downcast_mut() {
self.message(id_path, state, message, app_state)
} else {
// Possibly softer failure?
panic!("downcast error in dyn_event");
}
}

fn dyn_count(&self, state: &dyn std::any::Any) -> usize {
if let Some(state) = state.downcast_ref() {
self.count(state)
} else {
// Possibly softer failure?
panic!("downcast error in dyn_count");
}
}
}

pub type $boxedviewseq<T, A = ()> = Box<dyn $anyviewseq<T, A> $( $ss_bounds )* >;

impl<T, A> $viewseq<T, A> for $boxedviewseq<T, A> {
type State = Box<dyn std::any::Any $( $ss_bounds )* >;

fn build(&self, cx: &mut $cx, elements: &mut Vec<$pod>) -> Self::State {
use std::ops::Deref;
self.deref().dyn_build(cx, elements)
}

fn rebuild(
&self,
cx: &mut $cx,
prev: &Self,
state: &mut Self::State,
elements: &mut $crate::VecSplice<$pod>,
) -> $changeflags {
use std::ops::Deref;
self.deref().dyn_rebuild(cx, prev.deref(), state, elements)
}

fn message(
&self,
id_path: &[$crate::Id],
state: &mut Self::State,
message: Box<dyn std::any::Any>,
app_state: &mut T,
) -> $crate::MessageResult<A> {
use std::ops::{Deref, DerefMut};
self.deref()
.dyn_message(id_path, state.deref_mut(), message, app_state)
}

fn count(&self, state: &Self::State) -> usize {
use std::ops::Deref;
self.deref().dyn_count(state.deref())
}
}

impl<T, A, V: $view<T, A> + $viewmarker> $viewseq<T, A> for V
where
V::Element: $bound + 'static,
Expand All @@ -111,9 +245,9 @@ macro_rules! generate_viewsequence_trait {
cx: &mut $cx,
prev: &Self,
state: &mut Self::State,
element: &mut $crate::VecSplice<$pod>,
elements: &mut $crate::VecSplice<$pod>,
) -> $changeflags {
let el = element.mutate();
let el = elements.mutate();
let downcast = el.downcast_mut().unwrap();
let flags = <V as $view<T, A>>::rebuild(
self,
Expand Down Expand Up @@ -171,19 +305,19 @@ macro_rules! generate_viewsequence_trait {
cx: &mut $cx,
prev: &Self,
state: &mut Self::State,
element: &mut $crate::VecSplice<$pod>,
elements: &mut $crate::VecSplice<$pod>,
) -> $changeflags {
match (self, &mut *state, prev) {
(Some(this), Some(state), Some(prev)) => this.rebuild(cx, prev, state, element),
(Some(this), Some(state), Some(prev)) => this.rebuild(cx, prev, state, elements),
(None, Some(seq_state), Some(prev)) => {
let count = prev.count(&seq_state);
element.delete(count);
elements.delete(count);
*state = None;

<$changeflags>::tree_structure()
}
(Some(this), None, None) => {
let seq_state = element.as_vec(|vec| this.build(cx, vec));
let seq_state = elements.as_vec(|vec| this.build(cx, vec));
*state = Some(seq_state);

<$changeflags>::tree_structure()
Expand Down
67 changes: 33 additions & 34 deletions crates/xilem_html/src/elements.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ use wasm_bindgen::{JsCast, UnwrapThrowExt};
use xilem_core::{Id, MessageResult, VecSplice};

use crate::{
interfaces::sealed::Sealed, vecmap::VecMap, view::DomNode, AttributeValue, ChangeFlags, Cx,
Pod, View, ViewMarker, ViewSequence, HTML_NS,
interfaces::sealed::Sealed, vecmap::VecMap, view::DomNode, AttributeValue, BoxedViewSequence,
ChangeFlags, Cx, Pod, View, ViewMarker, ViewSequence, HTML_NS,
};

use super::interfaces::Element;
Expand All @@ -24,9 +24,9 @@ pub struct ElementState<ViewSeqState> {

// TODO something like the `after_update` of the former `Element` view (likely as a wrapper view instead)

pub struct CustomElement<T, A = (), Children = ()> {
pub struct CustomElement<T, A = ()> {
name: CowStr,
children: Children,
children: BoxedViewSequence<T, A>,
#[allow(clippy::type_complexity)]
phantom: PhantomData<fn() -> (T, A)>,
}
Expand All @@ -35,28 +35,29 @@ pub struct CustomElement<T, A = (), Children = ()> {
pub fn custom_element<T, A, Children: ViewSequence<T, A>>(
name: impl Into<CowStr>,
children: Children,
) -> CustomElement<T, A, Children> {
) -> CustomElement<T, A>
where
Children: 'static,
Children::State: 'static,
{
CustomElement {
name: name.into(),
children,
children: Box::new(children),
phantom: PhantomData,
}
}

impl<T, A, Children> CustomElement<T, A, Children> {
impl<T, A> CustomElement<T, A> {
fn node_name(&self) -> &str {
&self.name
}
}

impl<T, A, Children> ViewMarker for CustomElement<T, A, Children> {}
impl<T, A, Children> Sealed for CustomElement<T, A, Children> {}
impl<T, A> ViewMarker for CustomElement<T, A> {}
impl<T, A> Sealed for CustomElement<T, A> {}

impl<T, A, Children> View<T, A> for CustomElement<T, A, Children>
where
Children: ViewSequence<T, A>,
{
type State = ElementState<Children::State>;
impl<T, A> View<T, A> for CustomElement<T, A> {
type State = ElementState<Box<dyn std::any::Any>>;

// This is mostly intended for Autonomous custom elements,
// TODO: Custom builtin components need some special handling (`document.createElement("p", { is: "custom-component" })`)
Expand Down Expand Up @@ -153,18 +154,12 @@ where
}
}

impl<T, A, Children: ViewSequence<T, A>> Element<T, A> for CustomElement<T, A, Children> {}
impl<T, A, Children: ViewSequence<T, A>> crate::interfaces::HtmlElement<T, A>
for CustomElement<T, A, Children>
{
}
impl<T, A> Element<T, A> for CustomElement<T, A> {}
impl<T, A> crate::interfaces::HtmlElement<T, A> for CustomElement<T, A> {}

macro_rules! generate_dom_interface_impl {
($dom_interface:ident, ($ty_name:ident, $t:ident, $a:ident, $vs:ident)) => {
impl<$t, $a, $vs> $crate::interfaces::$dom_interface<$t, $a> for $ty_name<$t, $a, $vs> where
$vs: $crate::view::ViewSequence<$t, $a>
{
}
($dom_interface:ident, ($ty_name:ident, $t:ident, $a:ident)) => {
impl<$t, $a> $crate::interfaces::$dom_interface<$t, $a> for $ty_name<$t, $a> {}
};
}

Expand Down Expand Up @@ -194,13 +189,13 @@ macro_rules! define_element {
));
};
($ns:expr, ($ty_name:ident, $name:ident, $dom_interface:ident, $tag_name:expr, $t:ident, $a: ident, $vs: ident)) => {
pub struct $ty_name<$t, $a = (), $vs = ()>($vs, PhantomData<fn() -> ($t, $a)>);
pub struct $ty_name<$t, $a = ()>(BoxedViewSequence<$t, $a>, PhantomData<fn() -> ($t, $a)>);

impl<$t, $a, $vs> ViewMarker for $ty_name<$t, $a, $vs> {}
impl<$t, $a, $vs> Sealed for $ty_name<$t, $a, $vs> {}
impl<$t, $a> ViewMarker for $ty_name<$t, $a> {}
impl<$t, $a> Sealed for $ty_name<$t, $a> {}

impl<$t, $a, $vs: ViewSequence<$t, $a>> View<$t, $a> for $ty_name<$t, $a, $vs> {
type State = ElementState<$vs::State>;
impl<$t, $a> View<$t, $a> for $ty_name<$t, $a> {
type State = ElementState<Box<dyn std::any::Any>>;
type Element = web_sys::$dom_interface;

fn build(&self, cx: &mut Cx) -> (Id, Self::State, Self::Element) {
Expand Down Expand Up @@ -277,14 +272,18 @@ macro_rules! define_element {
/// Builder function for a
#[doc = concat!("`", $tag_name, "`")]
/// element view.
pub fn $name<$t, $a, $vs: ViewSequence<$t, $a>>(children: $vs) -> $ty_name<$t, $a, $vs> {
$ty_name(children, PhantomData)
pub fn $name<$t, $a, $vs: ViewSequence<$t, $a>>(children: $vs) -> $ty_name<$t, $a>
where
$vs: 'static,
$vs::State: 'static,
{
$ty_name(Box::new(children), PhantomData)
}

generate_dom_interface_impl!($dom_interface, ($ty_name, $t, $a, $vs));
generate_dom_interface_impl!($dom_interface, ($ty_name, $t, $a));

paste::paste! {
$crate::interfaces::[<for_all_ $dom_interface:snake _ancestors>]!(generate_dom_interface_impl, ($ty_name, $t, $a, $vs));
$crate::interfaces::[<for_all_ $dom_interface:snake _ancestors>]!(generate_dom_interface_impl, ($ty_name, $t, $a));
}
};
}
Expand All @@ -298,7 +297,7 @@ macro_rules! define_elements {

use crate::{
interfaces::sealed::Sealed, view::DomNode,
ChangeFlags, Cx, View, ViewMarker, ViewSequence,
ChangeFlags, Cx, View, ViewMarker, ViewSequence, BoxedViewSequence
};

$(define_element!(crate::$ns, $element_def);)*
Expand Down
4 changes: 2 additions & 2 deletions crates/xilem_html/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,8 @@ pub use one_of::{
};
pub use optional_action::{Action, OptionalAction};
pub use view::{
memoize, static_view, Adapt, AdaptState, AdaptThunk, AnyView, Memoize, MemoizeState, Pod, View,
ViewMarker, ViewSequence,
memoize, static_view, Adapt, AdaptState, AdaptThunk, AnyView, AnyViewSequence, BoxedView,
BoxedViewSequence, Memoize, MemoizeState, Pod, View, ViewMarker, ViewSequence,
};
pub use view_ext::ViewExt;

Expand Down
2 changes: 1 addition & 1 deletion crates/xilem_html/src/view.rs
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ impl Pod {
}

xilem_core::generate_view_trait! {View, DomNode, Cx, ChangeFlags;}
xilem_core::generate_viewsequence_trait! {ViewSequence, View, ViewMarker, DomNode, Cx, ChangeFlags, Pod;}
xilem_core::generate_viewsequence_trait! {ViewSequence, AnyViewSequence, BoxedViewSequence, View, ViewMarker, DomNode, Cx, ChangeFlags, Pod;();()}
xilem_core::generate_anyview_trait! {AnyView, View, ViewMarker, Cx, ChangeFlags, AnyNode, BoxedView;}
xilem_core::generate_memoize_view! {Memoize, MemoizeState, View, ViewMarker, Cx, ChangeFlags, static_view, memoize;}
xilem_core::generate_adapt_view! {View, Cx, ChangeFlags;}
Expand Down
2 changes: 1 addition & 1 deletion crates/xilem_svg/src/view.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ impl Pod {
}

xilem_core::generate_view_trait! {View, DomElement, Cx, ChangeFlags;}
xilem_core::generate_viewsequence_trait! {ViewSequence, View, ViewMarker, DomElement, Cx, ChangeFlags, Pod;}
xilem_core::generate_viewsequence_trait! {ViewSequence, AnyViewSequence, BoxedViewSequence, View, ViewMarker, DomElement, Cx, ChangeFlags, Pod;();()}
xilem_core::generate_anyview_trait! {AnyView, View, ViewMarker, Cx, ChangeFlags, AnyElement, BoxedView;}
xilem_core::generate_memoize_view! {Memoize, MemoizeState, View, ViewMarker, Cx, ChangeFlags, s, memoize;}
xilem_core::generate_adapt_view! {View, Cx, ChangeFlags;}
Expand Down
5 changes: 4 additions & 1 deletion src/view/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,10 @@ pub use button::button;
pub use linear_layout::{h_stack, v_stack, LinearLayout};
pub use list::{list, List};
pub use switch::switch;
pub use view::{Adapt, AdaptState, Cx, Memoize, View, ViewMarker, ViewSequence};
pub use view::{
Adapt, AdaptState, AnyView, AnyViewSequence, BoxedView, BoxedViewSequence, Cx, Memoize, View,
ViewMarker, ViewSequence,
};

#[cfg(feature = "taffy")]
mod taffy_layout;
Expand Down
2 changes: 1 addition & 1 deletion src/view/view.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ use xilem_core::{Id, IdPath};
use crate::widget::{AnyWidget, ChangeFlags, Pod, Widget};

xilem_core::generate_view_trait! {View, Widget, Cx, ChangeFlags; : Send}
xilem_core::generate_viewsequence_trait! {ViewSequence, View, ViewMarker, Widget, Cx, ChangeFlags, Pod; : Send}
xilem_core::generate_viewsequence_trait! {ViewSequence, AnyViewSequence, BoxedViewSequence, View, ViewMarker, Widget, Cx, ChangeFlags, Pod; (: Send); (+ Send)}
xilem_core::generate_anyview_trait! {AnyView, View, ViewMarker, Cx, ChangeFlags, AnyWidget, BoxedView; + Send}
xilem_core::generate_memoize_view! {Memoize, MemoizeState, View, ViewMarker, Cx, ChangeFlags, s, memoize; + Send}
xilem_core::generate_adapt_view! {View, Cx, ChangeFlags; + Send}
Expand Down