mirror of
https://github.com/servo/servo.git
synced 2025-06-21 07:38:59 +01:00
Add a compositor-side scroll tree
This will allow the compositor to properly chain scrolling requests up when a node has reached the extent of the scroll area. This fixes scrolling on servo.org.
This commit is contained in:
parent
918557ad6d
commit
c56a814806
12 changed files with 677 additions and 102 deletions
181
components/script_traits/tests/compositor.rs
Normal file
181
components/script_traits/tests/compositor.rs
Normal file
|
@ -0,0 +1,181 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||
|
||||
use euclid::Size2D;
|
||||
use script_traits::compositor::{ScrollTree, ScrollTreeNodeId, ScrollableNodeInfo};
|
||||
use webrender_api::{
|
||||
units::LayoutVector2D, ExternalScrollId, PipelineId, ScrollLocation, ScrollSensitivity,
|
||||
SpatialId,
|
||||
};
|
||||
|
||||
fn add_mock_scroll_node(tree: &mut ScrollTree) -> ScrollTreeNodeId {
|
||||
let pipeline_id = PipelineId(0, 0);
|
||||
let num_nodes = tree.nodes.len();
|
||||
let parent = if num_nodes > 0 {
|
||||
Some(ScrollTreeNodeId {
|
||||
index: num_nodes - 1,
|
||||
spatial_id: SpatialId::new(num_nodes - 1, pipeline_id),
|
||||
})
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
tree.add_scroll_tree_node(
|
||||
parent.as_ref(),
|
||||
SpatialId::new(num_nodes, pipeline_id),
|
||||
Some(ScrollableNodeInfo {
|
||||
external_id: ExternalScrollId(num_nodes as u64, pipeline_id),
|
||||
scrollable_size: Size2D::new(100.0, 100.0),
|
||||
scroll_sensitivity: ScrollSensitivity::ScriptAndInputEvents,
|
||||
offset: LayoutVector2D::zero(),
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_scroll_tree_simple_scroll() {
|
||||
let mut scroll_tree = ScrollTree::default();
|
||||
let pipeline_id = PipelineId(0, 0);
|
||||
let id = add_mock_scroll_node(&mut scroll_tree);
|
||||
|
||||
let (scrolled_id, offset) = scroll_tree
|
||||
.scroll_node_or_ancestor(
|
||||
&id,
|
||||
ScrollLocation::Delta(LayoutVector2D::new(-20.0, -40.0)),
|
||||
)
|
||||
.unwrap();
|
||||
let expected_offset = LayoutVector2D::new(-20.0, -40.0);
|
||||
assert_eq!(scrolled_id, ExternalScrollId(0, pipeline_id));
|
||||
assert_eq!(offset, expected_offset);
|
||||
assert_eq!(scroll_tree.get_node(&id).offset(), Some(expected_offset));
|
||||
|
||||
let (scrolled_id, offset) = scroll_tree
|
||||
.scroll_node_or_ancestor(&id, ScrollLocation::Delta(LayoutVector2D::new(20.0, 40.0)))
|
||||
.unwrap();
|
||||
let expected_offset = LayoutVector2D::new(0.0, 0.0);
|
||||
assert_eq!(scrolled_id, ExternalScrollId(0, pipeline_id));
|
||||
assert_eq!(offset, expected_offset);
|
||||
assert_eq!(scroll_tree.get_node(&id).offset(), Some(expected_offset));
|
||||
|
||||
// Scroll offsets must be negative.
|
||||
let result = scroll_tree
|
||||
.scroll_node_or_ancestor(&id, ScrollLocation::Delta(LayoutVector2D::new(20.0, 40.0)));
|
||||
assert!(result.is_none());
|
||||
assert_eq!(
|
||||
scroll_tree.get_node(&id).offset(),
|
||||
Some(LayoutVector2D::new(0.0, 0.0))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_scroll_tree_simple_scroll_chaining() {
|
||||
let mut scroll_tree = ScrollTree::default();
|
||||
|
||||
let pipeline_id = PipelineId(0, 0);
|
||||
let parent_id = add_mock_scroll_node(&mut scroll_tree);
|
||||
let unscrollable_child_id =
|
||||
scroll_tree.add_scroll_tree_node(Some(&parent_id), SpatialId::new(1, pipeline_id), None);
|
||||
|
||||
let (scrolled_id, offset) = scroll_tree
|
||||
.scroll_node_or_ancestor(
|
||||
&unscrollable_child_id,
|
||||
ScrollLocation::Delta(LayoutVector2D::new(-20.0, -40.0)),
|
||||
)
|
||||
.unwrap();
|
||||
let expected_offset = LayoutVector2D::new(-20.0, -40.0);
|
||||
assert_eq!(scrolled_id, ExternalScrollId(0, pipeline_id));
|
||||
assert_eq!(offset, expected_offset);
|
||||
assert_eq!(
|
||||
scroll_tree.get_node(&parent_id).offset(),
|
||||
Some(expected_offset)
|
||||
);
|
||||
|
||||
let (scrolled_id, offset) = scroll_tree
|
||||
.scroll_node_or_ancestor(
|
||||
&unscrollable_child_id,
|
||||
ScrollLocation::Delta(LayoutVector2D::new(-10.0, -15.0)),
|
||||
)
|
||||
.unwrap();
|
||||
let expected_offset = LayoutVector2D::new(-30.0, -55.0);
|
||||
assert_eq!(scrolled_id, ExternalScrollId(0, pipeline_id));
|
||||
assert_eq!(offset, expected_offset);
|
||||
assert_eq!(
|
||||
scroll_tree.get_node(&parent_id).offset(),
|
||||
Some(expected_offset)
|
||||
);
|
||||
assert_eq!(scroll_tree.get_node(&unscrollable_child_id).offset(), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_scroll_tree_chain_when_at_extent() {
|
||||
let mut scroll_tree = ScrollTree::default();
|
||||
|
||||
let pipeline_id = PipelineId(0, 0);
|
||||
let parent_id = add_mock_scroll_node(&mut scroll_tree);
|
||||
let child_id = add_mock_scroll_node(&mut scroll_tree);
|
||||
|
||||
let (scrolled_id, offset) = scroll_tree
|
||||
.scroll_node_or_ancestor(&child_id, ScrollLocation::End)
|
||||
.unwrap();
|
||||
|
||||
let expected_offset = LayoutVector2D::new(0.0, -100.0);
|
||||
assert_eq!(scrolled_id, ExternalScrollId(1, pipeline_id));
|
||||
assert_eq!(offset, expected_offset);
|
||||
assert_eq!(
|
||||
scroll_tree.get_node(&child_id).offset(),
|
||||
Some(expected_offset)
|
||||
);
|
||||
|
||||
// The parent will have scrolled because the child is already at the extent
|
||||
// of its scroll area in the y axis.
|
||||
let (scrolled_id, offset) = scroll_tree
|
||||
.scroll_node_or_ancestor(
|
||||
&child_id,
|
||||
ScrollLocation::Delta(LayoutVector2D::new(0.0, -10.0)),
|
||||
)
|
||||
.unwrap();
|
||||
let expected_offset = LayoutVector2D::new(0.0, -10.0);
|
||||
assert_eq!(scrolled_id, ExternalScrollId(0, pipeline_id));
|
||||
assert_eq!(offset, expected_offset);
|
||||
assert_eq!(
|
||||
scroll_tree.get_node(&parent_id).offset(),
|
||||
Some(expected_offset)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_scroll_tree_chain_through_overflow_hidden() {
|
||||
let mut scroll_tree = ScrollTree::default();
|
||||
|
||||
// Create a tree with a scrollable leaf, but make its `scroll_sensitivity`
|
||||
// reflect `overflow: hidden` ie not responsive to non-script scroll events.
|
||||
let pipeline_id = PipelineId(0, 0);
|
||||
let parent_id = add_mock_scroll_node(&mut scroll_tree);
|
||||
let overflow_hidden_id = add_mock_scroll_node(&mut scroll_tree);
|
||||
scroll_tree
|
||||
.get_node_mut(&overflow_hidden_id)
|
||||
.scroll_info
|
||||
.as_mut()
|
||||
.map(|mut info| {
|
||||
info.scroll_sensitivity = ScrollSensitivity::Script;
|
||||
});
|
||||
|
||||
let (scrolled_id, offset) = scroll_tree
|
||||
.scroll_node_or_ancestor(
|
||||
&overflow_hidden_id,
|
||||
ScrollLocation::Delta(LayoutVector2D::new(-20.0, -40.0)),
|
||||
)
|
||||
.unwrap();
|
||||
let expected_offset = LayoutVector2D::new(-20.0, -40.0);
|
||||
assert_eq!(scrolled_id, ExternalScrollId(0, pipeline_id));
|
||||
assert_eq!(offset, expected_offset);
|
||||
assert_eq!(
|
||||
scroll_tree.get_node(&parent_id).offset(),
|
||||
Some(expected_offset)
|
||||
);
|
||||
assert_eq!(
|
||||
scroll_tree.get_node(&overflow_hidden_id).offset(),
|
||||
Some(LayoutVector2D::new(0.0, 0.0))
|
||||
);
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue