///|
priv struct ScrollTarget {
scroll_id : ElementId
viewport : Rect
target : Rect
}
///|
fn ElementNode::find_scroll_target(
self : ElementNode,
key : String,
layout : PlacedNode,
nearest_scroll : (ElementId, Rect)?,
) -> ScrollTarget? {
let nearest_scroll = if self.view_node.semantics().role == Some(ScrollView) {
Some((self.id, layout.frame))
} else {
nearest_scroll
}
if self.key == Some(Key::new(key)) {
return match nearest_scroll {
Some((scroll_id, viewport)) =>
Some({ scroll_id, viewport, target: layout.frame, })
None => None
}
}
for index, child in self.children {
if index < layout.children.length() {
match
child.find_scroll_target(key, layout.children[index], nearest_scroll) {
Some(found) => return Some(found)
None => ()
}
}
}
None
}
///|
fn PlacedNode::event_render_node(self : PlacedNode) -> RenderNode {
{
..RenderNode::empty(),
element_id: self.element_id,
frame: self.frame,
paint_bounds: self.frame,
children: self.children.map(child => child.event_render_node()),
}
}
///|
fn ElementNode::dispatch_event_to_target(
self : ElementNode,
target : ElementId,
event : AppEvent,
layout : PlacedNode,
environment~ : Environment,
style? : ViewStyle = ViewStyle::empty(),
text_system~ : TextSystem,
) -> ElementEventResult? {
if self.id == target {
Some(
self.dispatch_view_event(
self.view_node,
event,
layout.event_render_node(),
text_system,
environment~,
style~,
),
)
} else {
let child_environment = self.view_node.child_environment(environment)
let child_style = self.view_node.child_style(style)
for index, child in self.children {
if index < layout.children.length() {
match
child.dispatch_event_to_target(
target,
event,
layout.children[index],
environment=child_environment,
style=child_style,
text_system~,
) {
Some(result) => return Some(result)
None => ()
}
}
}
None
}
}
///|
fn RuntimeState::apply_targeted_event_result(
self : RuntimeState,
result : ElementEventResult,
) -> Unit {
match result.focused {
Some(target) => ignore(self.commit_focus(Some(target)))
None => ()
}
if result.captured {
self.animfocus.active_pointer_capture = match result.capture_target {
Some(target) => Some(target)
None => result.focused
}
}
self.dirty.needs_redraw = self.dirty.needs_redraw || result.changed
self.dirty.needs_rebuild = self.dirty.needs_rebuild || result.activated
self.dirty.needs_paint = self.dirty.needs_paint || result.changed
if result.changed || result.activated {
self.mark_current_dirty_elements()
self.mark_semantics_dirty(self.element_tree.id())
}
if result.activated {
self.request_activation_rebuild()
}
self.deliver_messages(result.deliveries)
}
///|
fn scroll_axis_delta(
start : Double,
end : Double,
viewport_start : Double,
viewport_end : Double,
alignment : ScrollAlignment,
) -> Double {
match alignment {
Start => start - viewport_start
Center => (start + end) / 2.0 - (viewport_start + viewport_end) / 2.0
End => end - viewport_end
Nearest =>
if start < viewport_start {
start - viewport_start
} else if end > viewport_end {
end - viewport_end
} else {
0.0
}
}
}
///|
fn RuntimeState::try_pending_scroll_from_current_layout(
self : RuntimeState,
) -> Bool {
match self.pending_scroll_request {
Some(request) =>
match
self.element_tree.root.find_scroll_target(
request.key,
self.layout_tree.root,
None,
) {
Some(found) => {
let dx = scroll_axis_delta(
found.target.origin.x,
found.target.origin.x + found.target.size.width,
found.viewport.origin.x,
found.viewport.origin.x + found.viewport.size.width,
request.alignment,
)
let dy = scroll_axis_delta(
found.target.origin.y,
found.target.origin.y + found.target.size.height,
found.viewport.origin.y,
found.viewport.origin.y + found.viewport.size.height,
request.alignment,
)
self.pending_scroll_request = None
if dx != 0.0 || dy != 0.0 {
match
self.element_tree.root.dispatch_event_to_target(
found.scroll_id,
Pointer(
PointerEvent::new(
position=Point::new(
x=found.viewport.origin.x +
found.viewport.size.width / 2.0,
y=found.viewport.origin.y +
found.viewport.size.height / 2.0,
),
phase=Move,
wheel_delta=Point::new(x=-dx, y=-dy),
),
),
self.layout_tree.root,
environment=self.environment,
text_system=self.text_system,
) {
Some(result) => self.apply_targeted_event_result(result)
None => ()
}
}
true
}
None => false
}
None => false
}
}
///|
fn RuntimeState::scroll_to_key(
self : RuntimeState,
key : String,
alignment : ScrollAlignment,
) -> Bool {
self.pending_scroll_request = Some({ key, alignment, })
self.ensure_layout_tree()
if self.pending_scroll_request is None {
true
} else {
self.try_pending_scroll_from_current_layout()
}
}
///|
pub fn AppRuntime::scroll_to_key(
self : AppRuntime,
key : String,
alignment? : @core.ScrollAlignment = Nearest,
) -> Bool {
self.state.scroll_to_key(key, alignment)
}