Skip to content

Commit 23f04ca

Browse files
Philipp-MDJMcNab
andauthored
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 <[email protected]>
1 parent 73d33ee commit 23f04ca

File tree

3 files changed

+87
-42
lines changed

3 files changed

+87
-42
lines changed

xilem_core/src/message.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,10 @@ pub enum MessageResult<Action, Message = DynMessage> {
1717
///
1818
/// This allows for sub-sections of your app to use an elm-like architecture
1919
Action(Action),
20-
// TODO: What does this mean?
21-
/// This message's handler needs a rebuild to happen.
22-
/// The exact semantics of this method haven't been determined.
20+
/// A view has requested a rebuild, even though its value hasn't changed.
21+
///
22+
/// This can happen for example by some kind of async action.
23+
/// An example would be an async virtualized list, which fetches new entries, and requires a rebuild for the new entries.
2324
RequestRebuild,
2425
#[default]
2526
/// This event had no impact on the app state, or the impact it did have

xilem_core/src/view.rs

Lines changed: 69 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
//! The primary view trait and associated trivial implementations.
55
66
use alloc::boxed::Box;
7+
use alloc::rc::Rc;
78
use alloc::sync::Arc;
89
use core::ops::Deref;
910

@@ -205,8 +206,12 @@ where
205206
}
206207

207208
#[allow(unnameable_types)] // reason: Implementation detail, public because of trait visibility rules
208-
pub struct ArcState<ViewState> {
209+
pub struct RcState<ViewState> {
209210
view_state: ViewState,
211+
/// This is a flag that is set, when an inner view signifies that it requires a rebuild (via [`MessageResult::RequestRebuild`]).
212+
/// 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),
213+
/// but e.g. through some kind of async action.
214+
/// An example would be an async virtualized list, which fetches new entries, and requires a rebuild for the new entries.
210215
dirty: bool,
211216
}
212217

@@ -218,13 +223,13 @@ where
218223
V: View<State, Action, Context, Message> + ?Sized,
219224
{
220225
type Element = V::Element;
221-
type ViewState = ArcState<V::ViewState>;
226+
type ViewState = RcState<V::ViewState>;
222227

223228
fn build(&self, ctx: &mut Context) -> (Self::Element, Self::ViewState) {
224229
let (element, view_state) = self.deref().build(ctx);
225230
(
226231
element,
227-
ArcState {
232+
RcState {
228233
view_state,
229234
dirty: false,
230235
},
@@ -238,7 +243,6 @@ where
238243
ctx: &mut Context,
239244
element: Mut<Self::Element>,
240245
) {
241-
// If this is the same value, or no rebuild was forced, there's no need to rebuild
242246
if core::mem::take(&mut view_state.dirty) || !Arc::ptr_eq(self, prev) {
243247
self.deref()
244248
.rebuild(prev, &mut view_state.view_state, ctx, element);
@@ -271,3 +275,64 @@ where
271275
message_result
272276
}
273277
}
278+
279+
impl<V: ?Sized> ViewMarker for Rc<V> {}
280+
/// An implementation of [`View`] which only runs rebuild if the states are different
281+
impl<State, Action, Context, Message, V> View<State, Action, Context, Message> for Rc<V>
282+
where
283+
Context: ViewPathTracker,
284+
V: View<State, Action, Context, Message> + ?Sized,
285+
{
286+
type Element = V::Element;
287+
type ViewState = RcState<V::ViewState>;
288+
289+
fn build(&self, ctx: &mut Context) -> (Self::Element, Self::ViewState) {
290+
let (element, view_state) = self.deref().build(ctx);
291+
(
292+
element,
293+
RcState {
294+
view_state,
295+
dirty: false,
296+
},
297+
)
298+
}
299+
300+
fn rebuild(
301+
&self,
302+
prev: &Self,
303+
view_state: &mut Self::ViewState,
304+
ctx: &mut Context,
305+
element: Mut<Self::Element>,
306+
) {
307+
if core::mem::take(&mut view_state.dirty) || !Rc::ptr_eq(self, prev) {
308+
self.deref()
309+
.rebuild(prev, &mut view_state.view_state, ctx, element);
310+
}
311+
}
312+
313+
fn teardown(
314+
&self,
315+
view_state: &mut Self::ViewState,
316+
ctx: &mut Context,
317+
element: Mut<Self::Element>,
318+
) {
319+
self.deref()
320+
.teardown(&mut view_state.view_state, ctx, element);
321+
}
322+
323+
fn message(
324+
&self,
325+
view_state: &mut Self::ViewState,
326+
id_path: &[ViewId],
327+
message: Message,
328+
app_state: &mut State,
329+
) -> MessageResult<Action, Message> {
330+
let message_result =
331+
self.deref()
332+
.message(&mut view_state.view_state, id_path, message, app_state);
333+
if matches!(message_result, MessageResult::RequestRebuild) {
334+
view_state.dirty = true;
335+
}
336+
message_result
337+
}
338+
}

xilem_web/src/templated.rs

Lines changed: 14 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -5,35 +5,29 @@ use crate::{
55
core::{MessageResult, Mut, View, ViewId, ViewMarker},
66
DomView, DynMessage, PodMut, ViewCtx,
77
};
8-
use std::{any::TypeId, ops::Deref as _, rc::Rc};
8+
use std::{any::TypeId, rc::Rc};
99
use wasm_bindgen::UnwrapThrowExt;
1010

1111
/// 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.
12-
pub struct Templated<E>(Rc<E>);
12+
pub struct Templated<V>(Rc<V>);
1313

14-
#[allow(unnameable_types)] // reason: Implementation detail, public because of trait visibility rules
15-
pub struct TemplatedState<ViewState> {
16-
view_state: ViewState,
17-
dirty: bool,
18-
}
19-
20-
impl<E> ViewMarker for Templated<E> {}
21-
impl<State, Action, E> View<State, Action, ViewCtx, DynMessage> for Templated<E>
14+
impl<V> ViewMarker for Templated<V> {}
15+
impl<State, Action, V> View<State, Action, ViewCtx, DynMessage> for Templated<V>
2216
where
2317
State: 'static,
2418
Action: 'static,
25-
E: DomView<State, Action>,
19+
V: DomView<State, Action>,
2620
{
27-
type Element = E::Element;
21+
type Element = V::Element;
2822

29-
type ViewState = TemplatedState<E::ViewState>;
23+
type ViewState = <Rc<V> as View<State, Action, ViewCtx, DynMessage>>::ViewState;
3024

3125
fn build(&self, ctx: &mut ViewCtx) -> (Self::Element, Self::ViewState) {
3226
let type_id = TypeId::of::<Self>();
3327
let (element, view_state) = if let Some((template_node, view)) = ctx.templates.get(&type_id)
3428
{
3529
let prev = view.clone();
36-
let prev = prev.downcast_ref::<E>().unwrap_throw();
30+
let prev = prev.downcast_ref::<Rc<V>>().unwrap_throw();
3731
let node = template_node.clone_node_with_deep(true).unwrap_throw();
3832
let (mut el, mut state) = ctx.with_hydration_node(node, |ctx| prev.build(ctx));
3933
el.apply_changes();
@@ -50,14 +44,11 @@ where
5044
.clone_node_with_deep(true)
5145
.unwrap_throw();
5246

53-
ctx.templates.insert(type_id, (template, self.0.clone()));
47+
ctx.templates
48+
.insert(type_id, (template, Rc::new(self.0.clone())));
5449
(element, state)
5550
};
56-
let state = TemplatedState {
57-
view_state,
58-
dirty: false,
59-
};
60-
(element, state)
51+
(element, view_state)
6152
}
6253

6354
fn rebuild(
@@ -67,12 +58,7 @@ where
6758
ctx: &mut ViewCtx,
6859
element: Mut<Self::Element>,
6960
) {
70-
// If this is the same value, or no rebuild was forced, there's no need to rebuild
71-
if core::mem::take(&mut view_state.dirty) || !Rc::ptr_eq(&self.0, &prev.0) {
72-
self.0
73-
.deref()
74-
.rebuild(&prev.0, &mut view_state.view_state, ctx, element);
75-
}
61+
self.0.rebuild(&prev.0, view_state, ctx, element);
7662
}
7763

7864
fn teardown(
@@ -81,7 +67,7 @@ where
8167
ctx: &mut ViewCtx,
8268
element: Mut<Self::Element>,
8369
) {
84-
self.0.teardown(&mut view_state.view_state, ctx, element);
70+
self.0.teardown(view_state, ctx, element);
8571
}
8672

8773
fn message(
@@ -91,14 +77,7 @@ where
9177
message: DynMessage,
9278
app_state: &mut State,
9379
) -> MessageResult<Action, DynMessage> {
94-
let message_result =
95-
self.0
96-
.deref()
97-
.message(&mut view_state.view_state, id_path, message, app_state);
98-
if matches!(message_result, MessageResult::RequestRebuild) {
99-
view_state.dirty = true;
100-
}
101-
message_result
80+
self.0.message(view_state, id_path, message, app_state)
10281
}
10382
}
10483

0 commit comments

Comments
 (0)