mirror of https://github.com/linebender/xilem
xilem_core: implement `View` for `Rc<impl View>` (#732)
I needed this in xilem_web, I could've used `Arc` as well, but I think it's fine to add this for consistency when an `Arc` is not needed. It's basically copy-pasta of the `Arc`. (I don't think a macro is worth it here?). Had to fix some resulting issues in the `Templated` view (due to ambiguity?). --------- Co-authored-by: Daniel McNab <36049421+DJMcNab@users.noreply.github.com>
This commit is contained in:
parent
73d33ee1c9
commit
23f04ca370
|
@ -17,9 +17,10 @@ pub enum MessageResult<Action, Message = DynMessage> {
|
|||
///
|
||||
/// This allows for sub-sections of your app to use an elm-like architecture
|
||||
Action(Action),
|
||||
// TODO: What does this mean?
|
||||
/// This message's handler needs a rebuild to happen.
|
||||
/// The exact semantics of this method haven't been determined.
|
||||
/// A view has requested a rebuild, even though its value hasn't changed.
|
||||
///
|
||||
/// This can happen for example by some kind of async action.
|
||||
/// An example would be an async virtualized list, which fetches new entries, and requires a rebuild for the new entries.
|
||||
RequestRebuild,
|
||||
#[default]
|
||||
/// This event had no impact on the app state, or the impact it did have
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
//! The primary view trait and associated trivial implementations.
|
||||
|
||||
use alloc::boxed::Box;
|
||||
use alloc::rc::Rc;
|
||||
use alloc::sync::Arc;
|
||||
use core::ops::Deref;
|
||||
|
||||
|
@ -205,8 +206,12 @@ where
|
|||
}
|
||||
|
||||
#[allow(unnameable_types)] // reason: Implementation detail, public because of trait visibility rules
|
||||
pub struct ArcState<ViewState> {
|
||||
pub struct RcState<ViewState> {
|
||||
view_state: ViewState,
|
||||
/// This is a flag that is set, when an inner view signifies that it requires a rebuild (via [`MessageResult::RequestRebuild`]).
|
||||
/// This can happen, e.g. when an inner view wasn't changed by the app-developer directly (i.e. it points to the same view),
|
||||
/// but e.g. through some kind of async action.
|
||||
/// An example would be an async virtualized list, which fetches new entries, and requires a rebuild for the new entries.
|
||||
dirty: bool,
|
||||
}
|
||||
|
||||
|
@ -218,13 +223,13 @@ where
|
|||
V: View<State, Action, Context, Message> + ?Sized,
|
||||
{
|
||||
type Element = V::Element;
|
||||
type ViewState = ArcState<V::ViewState>;
|
||||
type ViewState = RcState<V::ViewState>;
|
||||
|
||||
fn build(&self, ctx: &mut Context) -> (Self::Element, Self::ViewState) {
|
||||
let (element, view_state) = self.deref().build(ctx);
|
||||
(
|
||||
element,
|
||||
ArcState {
|
||||
RcState {
|
||||
view_state,
|
||||
dirty: false,
|
||||
},
|
||||
|
@ -238,7 +243,6 @@ where
|
|||
ctx: &mut Context,
|
||||
element: Mut<Self::Element>,
|
||||
) {
|
||||
// If this is the same value, or no rebuild was forced, there's no need to rebuild
|
||||
if core::mem::take(&mut view_state.dirty) || !Arc::ptr_eq(self, prev) {
|
||||
self.deref()
|
||||
.rebuild(prev, &mut view_state.view_state, ctx, element);
|
||||
|
@ -271,3 +275,64 @@ where
|
|||
message_result
|
||||
}
|
||||
}
|
||||
|
||||
impl<V: ?Sized> ViewMarker for Rc<V> {}
|
||||
/// An implementation of [`View`] which only runs rebuild if the states are different
|
||||
impl<State, Action, Context, Message, V> View<State, Action, Context, Message> for Rc<V>
|
||||
where
|
||||
Context: ViewPathTracker,
|
||||
V: View<State, Action, Context, Message> + ?Sized,
|
||||
{
|
||||
type Element = V::Element;
|
||||
type ViewState = RcState<V::ViewState>;
|
||||
|
||||
fn build(&self, ctx: &mut Context) -> (Self::Element, Self::ViewState) {
|
||||
let (element, view_state) = self.deref().build(ctx);
|
||||
(
|
||||
element,
|
||||
RcState {
|
||||
view_state,
|
||||
dirty: false,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
fn rebuild(
|
||||
&self,
|
||||
prev: &Self,
|
||||
view_state: &mut Self::ViewState,
|
||||
ctx: &mut Context,
|
||||
element: Mut<Self::Element>,
|
||||
) {
|
||||
if core::mem::take(&mut view_state.dirty) || !Rc::ptr_eq(self, prev) {
|
||||
self.deref()
|
||||
.rebuild(prev, &mut view_state.view_state, ctx, element);
|
||||
}
|
||||
}
|
||||
|
||||
fn teardown(
|
||||
&self,
|
||||
view_state: &mut Self::ViewState,
|
||||
ctx: &mut Context,
|
||||
element: Mut<Self::Element>,
|
||||
) {
|
||||
self.deref()
|
||||
.teardown(&mut view_state.view_state, ctx, element);
|
||||
}
|
||||
|
||||
fn message(
|
||||
&self,
|
||||
view_state: &mut Self::ViewState,
|
||||
id_path: &[ViewId],
|
||||
message: Message,
|
||||
app_state: &mut State,
|
||||
) -> MessageResult<Action, Message> {
|
||||
let message_result =
|
||||
self.deref()
|
||||
.message(&mut view_state.view_state, id_path, message, app_state);
|
||||
if matches!(message_result, MessageResult::RequestRebuild) {
|
||||
view_state.dirty = true;
|
||||
}
|
||||
message_result
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,35 +5,29 @@ use crate::{
|
|||
core::{MessageResult, Mut, View, ViewId, ViewMarker},
|
||||
DomView, DynMessage, PodMut, ViewCtx,
|
||||
};
|
||||
use std::{any::TypeId, ops::Deref as _, rc::Rc};
|
||||
use std::{any::TypeId, rc::Rc};
|
||||
use wasm_bindgen::UnwrapThrowExt;
|
||||
|
||||
/// This view creates an internally cached deep-clone of the underlying DOM node. When the inner view is created again, this will be done more efficiently.
|
||||
pub struct Templated<E>(Rc<E>);
|
||||
pub struct Templated<V>(Rc<V>);
|
||||
|
||||
#[allow(unnameable_types)] // reason: Implementation detail, public because of trait visibility rules
|
||||
pub struct TemplatedState<ViewState> {
|
||||
view_state: ViewState,
|
||||
dirty: bool,
|
||||
}
|
||||
|
||||
impl<E> ViewMarker for Templated<E> {}
|
||||
impl<State, Action, E> View<State, Action, ViewCtx, DynMessage> for Templated<E>
|
||||
impl<V> ViewMarker for Templated<V> {}
|
||||
impl<State, Action, V> View<State, Action, ViewCtx, DynMessage> for Templated<V>
|
||||
where
|
||||
State: 'static,
|
||||
Action: 'static,
|
||||
E: DomView<State, Action>,
|
||||
V: DomView<State, Action>,
|
||||
{
|
||||
type Element = E::Element;
|
||||
type Element = V::Element;
|
||||
|
||||
type ViewState = TemplatedState<E::ViewState>;
|
||||
type ViewState = <Rc<V> as View<State, Action, ViewCtx, DynMessage>>::ViewState;
|
||||
|
||||
fn build(&self, ctx: &mut ViewCtx) -> (Self::Element, Self::ViewState) {
|
||||
let type_id = TypeId::of::<Self>();
|
||||
let (element, view_state) = if let Some((template_node, view)) = ctx.templates.get(&type_id)
|
||||
{
|
||||
let prev = view.clone();
|
||||
let prev = prev.downcast_ref::<E>().unwrap_throw();
|
||||
let prev = prev.downcast_ref::<Rc<V>>().unwrap_throw();
|
||||
let node = template_node.clone_node_with_deep(true).unwrap_throw();
|
||||
let (mut el, mut state) = ctx.with_hydration_node(node, |ctx| prev.build(ctx));
|
||||
el.apply_changes();
|
||||
|
@ -50,14 +44,11 @@ where
|
|||
.clone_node_with_deep(true)
|
||||
.unwrap_throw();
|
||||
|
||||
ctx.templates.insert(type_id, (template, self.0.clone()));
|
||||
ctx.templates
|
||||
.insert(type_id, (template, Rc::new(self.0.clone())));
|
||||
(element, state)
|
||||
};
|
||||
let state = TemplatedState {
|
||||
view_state,
|
||||
dirty: false,
|
||||
};
|
||||
(element, state)
|
||||
(element, view_state)
|
||||
}
|
||||
|
||||
fn rebuild(
|
||||
|
@ -67,12 +58,7 @@ where
|
|||
ctx: &mut ViewCtx,
|
||||
element: Mut<Self::Element>,
|
||||
) {
|
||||
// If this is the same value, or no rebuild was forced, there's no need to rebuild
|
||||
if core::mem::take(&mut view_state.dirty) || !Rc::ptr_eq(&self.0, &prev.0) {
|
||||
self.0
|
||||
.deref()
|
||||
.rebuild(&prev.0, &mut view_state.view_state, ctx, element);
|
||||
}
|
||||
self.0.rebuild(&prev.0, view_state, ctx, element);
|
||||
}
|
||||
|
||||
fn teardown(
|
||||
|
@ -81,7 +67,7 @@ where
|
|||
ctx: &mut ViewCtx,
|
||||
element: Mut<Self::Element>,
|
||||
) {
|
||||
self.0.teardown(&mut view_state.view_state, ctx, element);
|
||||
self.0.teardown(view_state, ctx, element);
|
||||
}
|
||||
|
||||
fn message(
|
||||
|
@ -91,14 +77,7 @@ where
|
|||
message: DynMessage,
|
||||
app_state: &mut State,
|
||||
) -> MessageResult<Action, DynMessage> {
|
||||
let message_result =
|
||||
self.0
|
||||
.deref()
|
||||
.message(&mut view_state.view_state, id_path, message, app_state);
|
||||
if matches!(message_result, MessageResult::RequestRebuild) {
|
||||
view_state.dirty = true;
|
||||
}
|
||||
message_result
|
||||
self.0.message(view_state, id_path, message, app_state)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue