Skip to content

Commit

Permalink
Add the Portal view to Xilem (#561)
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
DJMcNab authored Aug 29, 2024
1 parent 251f42a commit ac95f25
Show file tree
Hide file tree
Showing 4 changed files with 95 additions and 5 deletions.
8 changes: 6 additions & 2 deletions masonry/src/widget/portal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)),
Expand Down
10 changes: 7 additions & 3 deletions xilem/examples/variable_clock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
};
Expand Down Expand Up @@ -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,
Expand Down
3 changes: 3 additions & 0 deletions xilem/src/view/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,6 @@ pub use prose::*;

mod textbox;
pub use textbox::*;

mod portal;
pub use portal::*;
79 changes: 79 additions & 0 deletions xilem/src/view/portal.rs
Original file line number Diff line number Diff line change
@@ -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)
}
}

0 comments on commit ac95f25

Please sign in to comment.