diff --git a/crates/xilem_core/src/lib.rs b/crates/xilem_core/src/lib.rs index 843f6a424..9395809e4 100644 --- a/crates/xilem_core/src/lib.rs +++ b/crates/xilem_core/src/lib.rs @@ -17,6 +17,7 @@ mod any_view; mod id; +mod memoize; mod message; mod sequence; mod vec_splice; diff --git a/crates/xilem_core/src/memoize.rs b/crates/xilem_core/src/memoize.rs new file mode 100644 index 000000000..f151394e1 --- /dev/null +++ b/crates/xilem_core/src/memoize.rs @@ -0,0 +1,104 @@ +// Copyright 2023 the Xilem Authors. +// SPDX-License-Identifier: Apache-2.0 + +#[macro_export] +macro_rules! generate_memoize_view { + ($memoizeview:ident, + $memoizestate:ident, + $viewtrait:ident, + $viewmarker:ty, + $cx:ty, + $changeflags:ty, + $staticviewfunction:ident, + $memoizeviewfunction:ident + ) => { + pub struct $memoizeview { + data: D, + child_cb: F, + } + + pub struct $memoizestate> { + view: V, + view_state: V::State, + dirty: bool, + } + + impl V> $memoizeview { + pub fn new(data: D, child_cb: F) -> Self { + $memoizeview { data, child_cb } + } + } + + impl $viewmarker for $memoizeview {} + + impl< + T, + A, + D: PartialEq + Clone + Send + 'static, + V: $viewtrait, + F: Fn(&D) -> V + Send, + > $viewtrait for $memoizeview + { + type State = $memoizestate; + + type Element = V::Element; + + fn build(&self, cx: &mut $cx) -> ($crate::Id, Self::State, Self::Element) { + let view = (self.child_cb)(&self.data); + let (id, view_state, element) = view.build(cx); + let memoize_state = $memoizestate { + view, + view_state, + dirty: false, + }; + (id, memoize_state, element) + } + + fn rebuild( + &self, + cx: &mut $cx, + prev: &Self, + id: &mut $crate::Id, + state: &mut Self::State, + element: &mut Self::Element, + ) -> $changeflags { + if std::mem::take(&mut state.dirty) || prev.data != self.data { + let view = (self.child_cb)(&self.data); + let changed = view.rebuild(cx, &state.view, id, &mut state.view_state, element); + state.view = view; + changed + } else { + <$changeflags>::empty() + } + } + + fn message( + &self, + id_path: &[$crate::Id], + state: &mut Self::State, + event: Box, + app_state: &mut T, + ) -> $crate::MessageResult { + let r = state + .view + .message(id_path, &mut state.view_state, event, app_state); + if matches!(r, $crate::MessageResult::RequestRebuild) { + state.dirty = true; + } + r + } + } + + /// A static view, all of the content of the `view` should be constant, as this function is only run once + pub fn $staticviewfunction V + 'static>( + view: F, + ) -> $memoizeview<(), impl Fn(&()) -> V> { + $memoizeview::new((), move |_: &()| view()) + } + + /// Memoize the view, until the `data` changes (in which case `view` is called again) + pub fn $memoizeviewfunction V>(data: D, view: F) -> $memoizeview { + $memoizeview::new(data, view) + } + }; +} diff --git a/src/view/memoize.rs b/src/view/memoize.rs deleted file mode 100644 index c7f3d73b3..000000000 --- a/src/view/memoize.rs +++ /dev/null @@ -1,89 +0,0 @@ -// Copyright 2022 The Druid Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use std::any::Any; - -use crate::{event::EventResult, id::Id}; - -use super::{Cx, View}; - -pub struct Memoize { - data: D, - child_cb: F, -} - -pub struct MemoizeState> { - view: V, - view_state: V::State, - dirty: bool, -} - -impl V> Memoize { - pub fn new(data: D, child_cb: F) -> Self { - Memoize { data, child_cb } - } -} - -impl, F: Fn(&D) -> V + Send> View - for Memoize -{ - type State = MemoizeState; - - type Element = V::Element; - - fn build(&self, cx: &mut Cx) -> (Id, Self::State, Self::Element) { - let view = (self.child_cb)(&self.data); - let (id, view_state, element) = view.build(cx); - let memoize_state = MemoizeState { - view, - view_state, - dirty: false, - }; - (id, memoize_state, element) - } - - fn rebuild( - &self, - cx: &mut Cx, - prev: &Self, - id: &mut Id, - state: &mut Self::State, - element: &mut Self::Element, - ) -> bool { - if std::mem::take(&mut state.dirty) || prev.data != self.data { - let view = (self.child_cb)(&self.data); - let changed = view.rebuild(cx, &state.view, id, &mut state.view_state, element); - state.view = view; - changed - } else { - false - } - } - - fn event( - &self, - id_path: &[Id], - state: &mut Self::State, - event: Box, - app_state: &mut T, - ) -> EventResult { - let r = state - .view - .event(id_path, &mut state.view_state, event, app_state); - if matches!(r, EventResult::RequestRebuild) { - state.dirty = true; - } - r - } -} diff --git a/src/view/mod.rs b/src/view/mod.rs index 6c13c194e..b3a7425fb 100644 --- a/src/view/mod.rs +++ b/src/view/mod.rs @@ -17,7 +17,6 @@ mod button; // mod layout_observer; // mod list; -// mod memoize; // mod scroll_view; // mod text; // mod use_state; diff --git a/src/view/view.rs b/src/view/view.rs index a9a999bb9..8865074ab 100644 --- a/src/view/view.rs +++ b/src/view/view.rs @@ -26,6 +26,7 @@ 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} #[derive(Clone)] pub struct Cx {