From ac95f2524ac33fccd02f444b8a75ae0daaf98c3c Mon Sep 17 00:00:00 2001 From: Daniel McNab <36049421+DJMcNab@users.noreply.github.com> Date: Thu, 29 Aug 2024 06:53:50 +0100 Subject: [PATCH] Add the `Portal` view to Xilem (#561) This is also added to `variable_clocks` as needed. This could also be added to `mason`, but I see a worrying amount of lag if I do... I could do with some help tracking this down. --- masonry/src/widget/portal.rs | 8 +++- xilem/examples/variable_clock.rs | 10 ++-- xilem/src/view/mod.rs | 3 ++ xilem/src/view/portal.rs | 79 ++++++++++++++++++++++++++++++++ 4 files changed, 95 insertions(+), 5 deletions(-) create mode 100644 xilem/src/view/portal.rs diff --git a/masonry/src/widget/portal.rs b/masonry/src/widget/portal.rs index fb19e593..19d9f8ee 100644 --- a/masonry/src/widget/portal.rs +++ b/masonry/src/widget/portal.rs @@ -41,13 +41,17 @@ pub struct Portal { // --- MARK: BUILDERS --- impl Portal { pub fn new(child: W) -> Self { + Self::new_pod(WidgetPod::new(child)) + } + + pub fn new_pod(child: WidgetPod) -> Self { Portal { - child: WidgetPod::new(child), + child, viewport_pos: Point::ORIGIN, constrain_horizontal: false, constrain_vertical: false, must_fill: false, - // TODO - remove + // TODO - remove (TODO: why?) scrollbar_horizontal: WidgetPod::new(ScrollBar::new(Axis::Horizontal, 1.0, 1.0)), scrollbar_horizontal_visible: false, scrollbar_vertical: WidgetPod::new(ScrollBar::new(Axis::Vertical, 1.0, 1.0)), diff --git a/xilem/examples/variable_clock.rs b/xilem/examples/variable_clock.rs index 8bead4d8..c9154adf 100644 --- a/xilem/examples/variable_clock.rs +++ b/xilem/examples/variable_clock.rs @@ -13,7 +13,8 @@ use time::{error::IndeterminateOffset, macros::format_description, OffsetDateTim use winit::error::EventLoopError; use xilem::{ view::{ - button, flex, label, prose, sized_box, task, variable_label, Axis, FlexExt, FlexSpacer, + button, flex, label, portal, prose, sized_box, task, variable_label, Axis, FlexExt, + FlexSpacer, }, Color, EventLoop, EventLoopBuilder, WidgetView, Xilem, }; @@ -43,8 +44,11 @@ fn app_logic(data: &mut Clocks) -> impl WidgetView { FlexSpacer::Fixed(40.), local_time(data), controls(), - // TODO: When we get responsive layouts, move this into a two-column view. - TIMEZONES.iter().map(|it| it.view(data)).collect::>(), + portal(flex( + // TODO: When we get responsive layouts, move this into a two-column view on desktop. + TIMEZONES.iter().map(|it| it.view(data)).collect::>(), + )) + .flex(1.), )); fork( view, diff --git a/xilem/src/view/mod.rs b/xilem/src/view/mod.rs index eccc2923..9cafb201 100644 --- a/xilem/src/view/mod.rs +++ b/xilem/src/view/mod.rs @@ -30,3 +30,6 @@ pub use prose::*; mod textbox; pub use textbox::*; + +mod portal; +pub use portal::*; diff --git a/xilem/src/view/portal.rs b/xilem/src/view/portal.rs new file mode 100644 index 00000000..91077684 --- /dev/null +++ b/xilem/src/view/portal.rs @@ -0,0 +1,79 @@ +// Copyright 2024 the Xilem Authors +// SPDX-License-Identifier: Apache-2.0 + +use std::marker::PhantomData; + +use masonry::widget; +use xilem_core::{Mut, ViewMarker}; + +use crate::{Pod, View, ViewCtx, ViewId, WidgetView}; + +/// A view which puts `child` into a scrollable region. +/// +/// This corresponds to the Masonry [`Portal`](masonry::widget::Portal) widget. +pub fn portal(child: Child) -> Portal +where + Child: WidgetView, +{ + Portal { + child, + phantom: PhantomData, + } +} + +pub struct Portal { + child: V, + phantom: PhantomData<(State, Action)>, +} + +impl ViewMarker for Portal {} +impl View for Portal +where + Child: WidgetView, + State: 'static, + Action: 'static, +{ + type Element = Pod>; + type ViewState = Child::ViewState; + + fn build(&self, ctx: &mut ViewCtx) -> (Self::Element, Self::ViewState) { + // The Portal `View` doesn't get any messages directly (yet - scroll events?), so doesn't need to + // use ctx.with_id. + let (child, child_state) = self.child.build(ctx); + let widget_pod = Pod::new(widget::Portal::new_pod(child.inner)); + (widget_pod, child_state) + } + + fn rebuild<'el>( + &self, + prev: &Self, + view_state: &mut Self::ViewState, + ctx: &mut ViewCtx, + mut element: Mut<'el, Self::Element>, + ) -> Mut<'el, Self::Element> { + let child_element = element.child_mut(); + self.child + .rebuild(&prev.child, view_state, ctx, child_element); + element + } + + fn teardown( + &self, + view_state: &mut Self::ViewState, + ctx: &mut ViewCtx, + mut element: Mut<'_, Self::Element>, + ) { + let child_element = element.child_mut(); + self.child.teardown(view_state, ctx, child_element); + } + + fn message( + &self, + view_state: &mut Self::ViewState, + id_path: &[ViewId], + message: xilem_core::DynMessage, + app_state: &mut State, + ) -> crate::MessageResult { + self.child.message(view_state, id_path, message, app_state) + } +}