From 3470aa6afcddaaffe7d9917b4fc42af9deb51dce Mon Sep 17 00:00:00 2001 From: Philipp Mildenberger Date: Tue, 27 Aug 2024 00:03:45 +0200 Subject: [PATCH] xilem: Add a way to allow `impl WidgetViewSequence` as param for `flex()` --- xilem/src/lib.rs | 11 +++- xilem/src/view/flex.rs | 129 ++++++++++++++++++++++++++++++++++++++++- 2 files changed, 137 insertions(+), 3 deletions(-) diff --git a/xilem/src/lib.rs b/xilem/src/lib.rs index 5cb2134c8..59f3cc69f 100644 --- a/xilem/src/lib.rs +++ b/xilem/src/lib.rs @@ -13,6 +13,7 @@ use masonry::{ widget::{RootWidget, WidgetMut}, Widget, WidgetId, WidgetPod, }; +use view::{into_flex_seq, AsFlexSequence}; use winit::{ error::EventLoopError, window::{Window, WindowAttributes}, @@ -237,12 +238,18 @@ where /// } /// ``` pub trait WidgetViewSequence: - ViewSequence> + ViewSequence>> { + fn into_flex_seq(self) -> AsFlexSequence + where + Self: Sized, + { + into_flex_seq(self) + } } impl WidgetViewSequence for Seq where - Seq: ViewSequence> + Seq: ViewSequence>> { } diff --git a/xilem/src/view/flex.rs b/xilem/src/view/flex.rs index ceb7d79ad..af4697ca0 100644 --- a/xilem/src/view/flex.rs +++ b/xilem/src/view/flex.rs @@ -12,7 +12,7 @@ use xilem_core::{ ViewId, ViewMarker, ViewPathTracker, ViewSequence, }; -use crate::{AnyWidgetView, Pod, ViewCtx, WidgetView}; +use crate::{AnyWidgetView, Pod, ViewCtx, WidgetView, WidgetViewSequence}; pub use masonry::widget::{Axis, CrossAxisAlignment, FlexParams, MainAxisAlignment}; @@ -776,3 +776,130 @@ where flex_item.message(view_state.inner.as_mut().unwrap(), rest, message, app_state) } } + +pub struct AsFlexSequence { + inner: Seq, + phantom: PhantomData (State, Action)>, +} + +pub fn into_flex_seq>( + seq: Seq, +) -> AsFlexSequence { + AsFlexSequence { + inner: seq, + phantom: PhantomData, + } +} + +struct FlexConversionSplice<'s, FlexElementSplice> { + scratch: &'s mut AppendVec>>, + flex_splice: &'s mut FlexElementSplice, +} + +// intentionally doesn't use `SuperElement::with_downcast` as it results in unnecessary double-boxing +impl<'s, FlexElementSplice: ElementSplice> ElementSplice>> + for FlexConversionSplice<'s, FlexElementSplice> +{ + fn with_scratch(&mut self, f: impl FnOnce(&mut AppendVec>>) -> R) -> R { + let r = f(self.scratch); + self.flex_splice.with_scratch(|scratch| { + for element in self.scratch.drain() { + scratch.push(FlexElement::Child(element, FlexParams::default())); + } + }); + r + } + + fn insert(&mut self, element: Pod>) { + self.flex_splice + .insert(FlexElement::Child(element, FlexParams::default())); + } + + fn mutate(&mut self, f: impl FnOnce(WidgetMut<'_, Box>) -> R) -> R { + self.flex_splice.mutate(|mut e| { + f(e.parent + .child_mut(e.idx) + .expect("Guaranteed to be a widget")) + }) + } + + fn skip(&mut self, n: usize) { + self.flex_splice.skip(n); + } + + fn delete( + &mut self, + f: impl FnOnce(> as ViewElement>::Mut<'_>) -> R, + ) -> R { + self.flex_splice.delete(|mut e| { + f(e.parent + .child_mut(e.idx) + .expect("Guaranteed to be a widget")) + }) + } +} + +impl ViewSequence + for AsFlexSequence +where + State: 'static, + Action: 'static, + Seq: ViewSequence>>, +{ + type SeqState = (AppendVec>>, Seq::SeqState); + + fn seq_build( + &self, + ctx: &mut ViewCtx, + elements: &mut AppendVec, + ) -> Self::SeqState { + let mut inner_elements = AppendVec::default(); + // It would be nice to avoid extra allocations... + let state = self.inner.seq_build(ctx, &mut inner_elements); + for element in inner_elements.drain() { + elements.push(FlexElement::Child(element, FlexParams::default())); + } + (inner_elements, state) + } + + fn seq_rebuild( + &self, + prev: &Self, + (inner_elements, seq_state): &mut Self::SeqState, + ctx: &mut ViewCtx, + elements: &mut impl ElementSplice, + ) { + let mut inner_splice = FlexConversionSplice { + scratch: inner_elements, + flex_splice: elements, + }; + self.inner + .seq_rebuild(&prev.inner, seq_state, ctx, &mut inner_splice); + debug_assert!(inner_elements.is_empty()); + } + + fn seq_teardown( + &self, + (inner_elements, seq_state): &mut Self::SeqState, + ctx: &mut ViewCtx, + elements: &mut impl ElementSplice, + ) { + let mut inner_splice = FlexConversionSplice { + scratch: inner_elements, + flex_splice: elements, + }; + self.inner.seq_teardown(seq_state, ctx, &mut inner_splice); + debug_assert!(inner_elements.is_empty()); + } + + fn seq_message( + &self, + (_, seq_state): &mut Self::SeqState, + id_path: &[ViewId], + message: DynMessage, + app_state: &mut State, + ) -> MessageResult { + self.inner + .seq_message(seq_state, id_path, message, app_state) + } +}