Skip to content

Commit

Permalink
Implement update_scrolls pass
Browse files Browse the repository at this point in the history
Note: This PRs comes with a lot of new TODO items.
Addressing most of these items is difficult without major refactors.
This pass is still mostly functional.
  • Loading branch information
PoignardAzur committed Aug 25, 2024
1 parent 1d87fce commit 54b607b
Show file tree
Hide file tree
Showing 6 changed files with 83 additions and 48 deletions.
17 changes: 14 additions & 3 deletions masonry/src/contexts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,6 @@ pub struct EventCtx<'a> {
pub(crate) widget_children: ArenaMutChildren<'a, Box<dyn Widget>>,
pub(crate) allow_pointer_capture: bool,
pub(crate) is_handled: bool,
pub(crate) request_pan_to_child: Option<Rect>,
}

/// A context provided to the [`lifecycle`] method on widgets.
Expand Down Expand Up @@ -604,8 +603,20 @@ impl EventCtx<'_> {
}

/// Send a signal to parent widgets to scroll this widget into view.
pub fn request_pan_to_this(&mut self) {
self.request_pan_to_child = Some(self.widget_state.layout_rect());
pub fn request_scroll_to_this(&mut self) {
let rect = self.widget_state.layout_rect();
self.global_state
.scroll_request_targets
.push((self.widget_state.id, rect));
}

/// Send a signal to parent widgets to scroll this area into view.
///
/// `rect` is in local coordinates.
pub fn request_scroll_to(&mut self, rect: Rect) {
self.global_state
.scroll_request_targets
.push((self.widget_state.id, rect));
}

// TODO - Remove
Expand Down
39 changes: 0 additions & 39 deletions masonry/src/passes/event.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,6 @@ fn run_event_pass<E>(
widget_children: widget_mut.children,
allow_pointer_capture,
is_handled: false,
request_pan_to_child: None,
};
let widget = widget_mut.item;

Expand Down Expand Up @@ -194,41 +193,3 @@ pub(crate) fn root_on_access_event(

handled
}

// These functions were carved out of WidgetPod code during a previous refactor
// The general "pan to child" logic needs to be added back in.
#[cfg(FALSE)]
fn pan_to_child() {
// TODO - there's some dubious logic here
if let Some(target_rect) = inner_ctx.request_pan_to_child {
self.pan_to_child(parent_ctx, target_rect);
let (state, _) = parent_ctx
.widget_state_children
.get_child_mut(id)
.expect("WidgetPod: inner widget not found in widget tree");
let new_rect = target_rect.with_origin(target_rect.origin() + state.origin.to_vec2());
parent_ctx.request_pan_to_child = Some(new_rect);
}
}

#[cfg(FALSE)]
fn pan_to_child(&mut self, parent_ctx: &mut EventCtx, rect: Rect) {
let id = self.id().to_raw();
let (widget, widget_token) = parent_ctx
.widget_children
.get_child_mut(id)
.expect("WidgetPod: inner widget not found in widget tree");
let (state, state_token) = parent_ctx
.widget_state_children
.get_child_mut(id)
.expect("WidgetPod: inner widget not found in widget tree");
let mut inner_ctx = LifeCycleCtx {
global_state: parent_ctx.global_state,
widget_state: state,
widget_state_children: state_token,
widget_children: widget_token,
};
let event = LifeCycle::RequestPanToChild(rect);

widget.lifecycle(&mut inner_ctx, &event);
}
27 changes: 25 additions & 2 deletions masonry/src/passes/update.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@
use std::collections::HashSet;

use cursor_icon::CursorIcon;
use tracing::trace;
use tracing::{info_span, trace};

use crate::passes::merge_state_up;
use crate::render_root::{RenderRoot, RenderRootSignal};
use crate::{LifeCycleCtx, StatusChange, Widget, WidgetId, WidgetState};
use crate::{LifeCycle, LifeCycleCtx, StatusChange, Widget, WidgetId, WidgetState};

fn get_id_path(root: &RenderRoot, widget_id: Option<WidgetId>) -> Vec<WidgetId> {
let Some(widget_id) = widget_id else {
Expand Down Expand Up @@ -145,3 +145,26 @@ pub(crate) fn run_update_pointer_pass(root: &mut RenderRoot, root_state: &mut Wi
// Pass root widget state to synthetic state create at beginning of pass
root_state.merge_up(root.widget_arena.get_state_mut(root.root.id()).item);
}

// ----------------

pub(crate) fn run_update_scroll_pass(root: &mut RenderRoot) {
let _span = info_span!("update_scroll").entered();

let scroll_request_targets = std::mem::take(&mut root.state.scroll_request_targets);
for (target, rect) in scroll_request_targets {
let mut target_rect = rect;

run_targeted_update_pass(root, Some(target), |widget, ctx| {
let event = LifeCycle::RequestPanToChild(rect);
widget.lifecycle(ctx, &event);

// TODO - We should run the compose method after this, so
// translations are updated and the rect passed to parents
// is more accurate.

let state = &ctx.widget_state;
target_rect = target_rect + state.translation + state.origin.to_vec2();
});
}
}
11 changes: 9 additions & 2 deletions masonry/src/render_root.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use accesskit::{ActionRequest, Tree, TreeUpdate};
use parley::fontique::{self, Collection, CollectionOptions};
use parley::{FontContext, LayoutContext};
use tracing::{info_span, warn};
use vello::kurbo::{self, Point};
use vello::kurbo::{self, Point, Rect};
use vello::Scene;

#[cfg(not(target_arch = "wasm32"))]
Expand All @@ -24,7 +24,7 @@ use crate::passes::compose::root_compose;
use crate::passes::event::{root_on_access_event, root_on_pointer_event, root_on_text_event};
use crate::passes::mutate::{mutate_widget, run_mutate_pass};
use crate::passes::paint::root_paint;
use crate::passes::update::run_update_pointer_pass;
use crate::passes::update::{run_update_pointer_pass, run_update_scroll_pass};
use crate::text::TextBrush;
use crate::tree_arena::TreeArena;
use crate::widget::WidgetArena;
Expand Down Expand Up @@ -55,11 +55,13 @@ pub struct RenderRoot {
pub(crate) widget_arena: WidgetArena,
}

// TODO - Document these fields.
pub(crate) struct RenderRootState {
pub(crate) debug_logger: DebugLogger,
pub(crate) signal_queue: VecDeque<RenderRootSignal>,
pub(crate) focused_widget: Option<WidgetId>,
pub(crate) next_focused_widget: Option<WidgetId>,
pub(crate) scroll_request_targets: Vec<(WidgetId, Rect)>,
pub(crate) hovered_path: Vec<WidgetId>,
pub(crate) pointer_capture_target: Option<WidgetId>,
pub(crate) cursor_icon: CursorIcon,
Expand Down Expand Up @@ -132,6 +134,7 @@ impl RenderRoot {
signal_queue: VecDeque::new(),
focused_widget: None,
next_focused_widget: None,
scroll_request_targets: Vec::new(),
hovered_path: Vec::new(),
pointer_capture_target: None,
cursor_icon: CursorIcon::Default,
Expand Down Expand Up @@ -547,6 +550,10 @@ impl RenderRoot {
self.state.debug_logger.layout_tree.root = Some(self.root.id().to_raw() as u32);
}

if !self.state.scroll_request_targets.is_empty() {
run_update_scroll_pass(self);
}

if self.root_state().needs_compose && !self.root_state().needs_layout {
root_compose(self, widget_state);
}
Expand Down
35 changes: 33 additions & 2 deletions masonry/src/widget/portal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,31 @@ impl<W: Widget> Portal<W> {
false
}
}

// Note - Rect is in child coordinates
// TODO - Merge with pan_viewport_to
// Right now these functions are just different enough to be a pain to merge.
pub fn pan_viewport_to_raw(
&mut self,
portal_size: Size,
content_size: Size,
target: Rect,
) -> bool {
let viewport = Rect::from_origin_size(self.viewport_pos, portal_size);

let new_pos_x = compute_pan_range(
viewport.min_x()..viewport.max_x(),
target.min_x()..target.max_x(),
)
.start;
let new_pos_y = compute_pan_range(
viewport.min_y()..viewport.max_y(),
target.min_y()..target.max_y(),
)
.start;

self.set_viewport_pos_raw(portal_size, content_size, Point::new(new_pos_x, new_pos_y))
}
}

// --- MARK: WIDGETMUT ---
Expand Down Expand Up @@ -311,8 +336,14 @@ impl<W: Widget> Widget for Portal<W> {
LifeCycle::WidgetAdded => {
ctx.register_as_portal();
}
//TODO
//LifeCycle::RequestPanToChild(target_rect) => {}
LifeCycle::RequestPanToChild(target) => {
let portal_size = ctx.size();
let content_size = ctx.get_raw_ref(&mut self.child).ctx().layout_rect().size();

// FIXME - Update scrollbars
self.pan_viewport_to_raw(portal_size, content_size, *target);
ctx.request_compose();
}
_ => {}
}

Expand Down
2 changes: 2 additions & 0 deletions masonry/src/widget/textbox.rs
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,8 @@ impl Widget for Textbox {
let result = self.editor.text_event(ctx, event);
// If focused on a link and enter pressed, follow it?
if result.is_handled() {
// TODO - Use request_scroll_to with cursor rect
ctx.request_scroll_to_this();
ctx.set_handled();
// TODO: only some handlers need this repaint
ctx.request_layout();
Expand Down

0 comments on commit 54b607b

Please sign in to comment.