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.
This commit is contained in:
Daniel McNab 2024-08-29 06:53:50 +01:00 committed by GitHub
parent 251f42ae87
commit ac95f2524a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 95 additions and 5 deletions

View File

@ -41,13 +41,17 @@ pub struct Portal<W: Widget> {
// --- MARK: BUILDERS ---
impl<W: Widget> Portal<W> {
pub fn new(child: W) -> Self {
Self::new_pod(WidgetPod::new(child))
}
pub fn new_pod(child: WidgetPod<W>) -> 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)),

View File

@ -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<Clocks> {
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::<Vec<_>>(),
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::<Vec<_>>(),
))
.flex(1.),
));
fork(
view,

View File

@ -30,3 +30,6 @@ pub use prose::*;
mod textbox;
pub use textbox::*;
mod portal;
pub use portal::*;

79
xilem/src/view/portal.rs Normal file
View File

@ -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, State, Action>(child: Child) -> Portal<Child, State, Action>
where
Child: WidgetView<State, Action>,
{
Portal {
child,
phantom: PhantomData,
}
}
pub struct Portal<V, State, Action> {
child: V,
phantom: PhantomData<(State, Action)>,
}
impl<V, State, Action> ViewMarker for Portal<V, State, Action> {}
impl<Child, State, Action> View<State, Action, ViewCtx> for Portal<Child, State, Action>
where
Child: WidgetView<State, Action>,
State: 'static,
Action: 'static,
{
type Element = Pod<widget::Portal<Child::Widget>>;
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<Action> {
self.child.message(view_state, id_path, message, app_state)
}
}