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 support for impl View for Arc/Rc<impl View> #164

Closed
wants to merge 10 commits into from
1 change: 1 addition & 0 deletions crates/xilem_core/src/view/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

mod adapt;
mod memoize;
mod rc;

/// Create the `View` trait for a particular xilem context (e.g. html, native, ...).
///
Expand Down
44 changes: 44 additions & 0 deletions crates/xilem_core/src/view/rc.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
// Copyright 2024 the Xilem Authors.
// SPDX-License-Identifier: Apache-2.0

#[macro_export]
macro_rules! generate_rc_view {
($($rc_ty:ident)::*, $viewtrait:ident, $viewmarker:ty, $cx:ty, $changeflags:ty; $($ss:tt)*) => {
impl<V> $viewmarker for $(::$rc_ty)*<V> {}

impl<T, A, V: $viewtrait<T, A> $( $ss )*> $viewtrait<T, A> for $(::$rc_ty)*<V> {
type State = V::State;

type Element = V::Element;

fn build(&self, cx: &mut Cx) -> ($crate::Id, Self::State, Self::Element) {
V::build(self, cx)
}

fn rebuild(
&self,
cx: &mut $cx,
prev: &Self,
id: &mut $crate::Id,
state: &mut Self::State,
element: &mut Self::Element,
) -> ChangeFlags {
if !$(::$rc_ty)*::ptr_eq(self, prev) {
V::rebuild(self, cx, prev, id, state, element)
} else {
ChangeFlags::empty()
}
}

fn message(
&self,
id_path: &[$crate::Id],
state: &mut Self::State,
message: Box<dyn std::any::Any>,
app_state: &mut T,
) -> $crate::MessageResult<A> {
V::message(self, id_path, state, message, app_state)
}
}
};
}
2 changes: 2 additions & 0 deletions crates/xilem_web/src/view.rs
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,8 @@ xilem_core::generate_anyview_trait! {AnyView, View, ViewMarker, Cx, ChangeFlags,
xilem_core::generate_memoize_view! {Memoize, MemoizeState, View, ViewMarker, Cx, ChangeFlags, static_view, memoize;}
xilem_core::generate_adapt_view! {View, Cx, ChangeFlags;}
xilem_core::generate_adapt_state_view! {View, Cx, ChangeFlags;}
xilem_core::generate_rc_view! {std::rc::Rc, View, ViewMarker, Cx, ChangeFlags; }
xilem_core::generate_rc_view! {std::sync::Arc, View, ViewMarker, Cx, ChangeFlags; + Sync}

// strings -> text nodes

Expand Down
65 changes: 65 additions & 0 deletions examples/memoization.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
use std::sync::Arc;
use xilem::view::{button, memoize, v_stack, BoxedView};
use xilem::{view::View, App, AppLauncher};

// There are currently two ways to do memoization

fn app_logic(state: &mut AppState) -> impl View<AppState> {
// The following is an example to do memoization with an Arc
let increase_button = if let Some(view) = &state.count_view {
view.clone()
} else {
let view = state.make_increase_button();
state.count_view = Some(view.clone());
view
};

v_stack((
increase_button,
// This is the alternative with Memoize
// Note how this requires a closure that returns the memoized view, while Arc does not
memoize(state.count, |count| {
button(
format!("decrease the count: {count}"),
|data: &mut AppState| {
data.count_view = None;
data.count -= 1;
},
)
}),
button("reset", |data: &mut AppState| {
if data.count != 0 {
data.count_view = None;
}
data.count = 0;
}),
))
.with_spacing(20.0)
}

struct AppState {
count: i32,
// TODO Maybe support `impl View for Arc<dyn AnyView>` to avoid double indirection
count_view: Option<Arc<BoxedView<AppState>>>,
}

impl AppState {
fn make_increase_button(&self) -> Arc<BoxedView<AppState>> {
Arc::new(Box::new(button(
format!("current count is {}", self.count),
|state: &mut AppState| {
state.count += 1;
state.count_view = None;
},
)))
}
}

fn main() {
let data = AppState {
count: 0,
count_view: None,
};

AppLauncher::new(App::new(data, app_logic)).run()
}
9 changes: 6 additions & 3 deletions src/view/button.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,18 +23,21 @@ use super::{Cx, View};
pub struct Button<T, A> {
label: String,
// consider not boxing
callback: Box<dyn Fn(&mut T) -> A + Send>,
callback: Box<dyn Fn(&mut T) -> A + Send + Sync>,
}

pub fn button<T, A>(
label: impl Into<String>,
clicked: impl Fn(&mut T) -> A + Send + 'static,
clicked: impl Fn(&mut T) -> A + Send + Sync + 'static,
) -> Button<T, A> {
Button::new(label, clicked)
}

impl<T, A> Button<T, A> {
pub fn new(label: impl Into<String>, clicked: impl Fn(&mut T) -> A + Send + 'static) -> Self {
pub fn new(
label: impl Into<String>,
clicked: impl Fn(&mut T) -> A + Send + Sync + 'static,
) -> Self {
Button {
label: label.into(),
callback: Box::new(clicked),
Expand Down
2 changes: 1 addition & 1 deletion src/view/list.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ pub fn list<T, A, VT: ViewSequence<T, A>, F: Fn(usize) -> VT + Send>(
}
}

impl<T, A, VT: ViewSequence<T, A>, F: Fn(usize) -> VT + Send> ViewSequence<T, A>
impl<T, A, VT: ViewSequence<T, A>, F: Fn(usize) -> VT + Send + Sync> ViewSequence<T, A>
for List<T, A, VT, F>
{
type State = ListState<T, A, VT>;
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::{
memoize, static_view, Adapt, AdaptState, AnyView, BoxedView, Cx, Memoize, MemoizeState, View,
ViewMarker, ViewSequence,
};

#[cfg(feature = "taffy")]
mod taffy_layout;
Expand Down
6 changes: 3 additions & 3 deletions src/view/switch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,18 +22,18 @@ use super::{Cx, View};
pub struct Switch<T, A> {
is_on: bool,
#[allow(clippy::type_complexity)]
callback: Box<dyn Fn(&mut T, bool) -> A + Send>,
callback: Box<dyn Fn(&mut T, bool) -> A + Send + Sync>,
}

pub fn switch<T, A>(
is_on: bool,
clicked: impl Fn(&mut T, bool) -> A + Send + 'static,
clicked: impl Fn(&mut T, bool) -> A + Send + Sync + 'static,
) -> Switch<T, A> {
Switch::new(is_on, clicked)
}

impl<T, A> Switch<T, A> {
pub fn new(is_on: bool, clicked: impl Fn(&mut T, bool) -> A + Send + 'static) -> Self {
pub fn new(is_on: bool, clicked: impl Fn(&mut T, bool) -> A + Send + Sync + 'static) -> Self {
Switch {
is_on,
callback: Box::new(clicked),
Expand Down
13 changes: 7 additions & 6 deletions src/view/view.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,13 @@ 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_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}
xilem_core::generate_adapt_state_view! {View, Cx, ChangeFlags; + Send}
xilem_core::generate_view_trait! {View, Widget, Cx, ChangeFlags; : Send + Sync}
xilem_core::generate_viewsequence_trait! {ViewSequence, View, ViewMarker, Widget, Cx, ChangeFlags, Pod; : Send + Sync}
xilem_core::generate_anyview_trait! {AnyView, View, ViewMarker, Cx, ChangeFlags, AnyWidget, BoxedView; + Send + Sync}
xilem_core::generate_memoize_view! {Memoize, MemoizeState, View, ViewMarker, Cx, ChangeFlags, static_view, memoize; + Send + Sync}
xilem_core::generate_adapt_view! {View, Cx, ChangeFlags; + Send + Sync}
xilem_core::generate_adapt_state_view! {View, Cx, ChangeFlags; + Send + Sync}
xilem_core::generate_rc_view! {std::sync::Arc, View, ViewMarker, Cx, ChangeFlags; + Sync + Sync}

#[derive(Clone)]
pub struct Cx {
Expand Down