Skip to content

Commit

Permalink
Restore some hit testing APIs
Browse files Browse the repository at this point in the history
These APIs are still used by Servo, so keep them around until Servo
can migrate off of them.
  • Loading branch information
mrobinson committed Mar 1, 2024
1 parent d5ce6cf commit b296300
Show file tree
Hide file tree
Showing 7 changed files with 84 additions and 14 deletions.
2 changes: 2 additions & 0 deletions examples/scrolling.rs
Original file line number Diff line number Diff line change
Expand Up @@ -271,7 +271,9 @@ impl Example for App {
winit::event::WindowEvent::MouseInput { .. } => {
let results = api.hit_test(
document_id,
None,
self.cursor_position,
HitTestFlags::empty(),
);

println!("Hit test results:");
Expand Down
61 changes: 55 additions & 6 deletions webrender/src/hit_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use api::{BorderRadius, ClipMode, HitTestResultItem, HitTestResult, ItemTag, PrimitiveFlags};
use api::{PipelineId, ApiHitTester};
use api::{BorderRadius, ClipMode, HitTestFlags, HitTestResultItem, HitTestResult, ItemTag, PrimitiveFlags};
use api::{ApiHitTester, PipelineId};
use api::units::*;
use crate::clip::{rounded_rectangle_contains_point, ClipNodeId, ClipTreeBuilder};
use crate::clip::{polygon_contains_point, ClipItemKey, ClipItemKeyKind};
Expand Down Expand Up @@ -41,10 +41,13 @@ impl SharedHitTester {
}

impl ApiHitTester for SharedHitTester {
fn hit_test(&self,
fn hit_test(
&self,
pipeline_id: Option<PipelineId>,
point: WorldPoint,
flags: HitTestFlags,
) -> HitTestResult {
self.get_ref().hit_test(HitTest::new(point))
self.get_ref().hit_test(HitTest::new(pipeline_id, point, flags))
}
}

Expand Down Expand Up @@ -280,13 +283,15 @@ pub struct HitTester {
#[ignore_malloc_size_of = "Arc"]
scene: Arc<HitTestingScene>,
spatial_nodes: FastHashMap<SpatialNodeIndex, HitTestSpatialNode>,
pipeline_root_nodes: FastHashMap<PipelineId, SpatialNodeIndex>,
}

impl HitTester {
pub fn empty() -> Self {
HitTester {
scene: Arc::new(HitTestingScene::new(&HitTestingSceneStats::empty())),
spatial_nodes: FastHashMap::default(),
pipeline_root_nodes: FastHashMap::default(),
}
}

Expand All @@ -297,6 +302,7 @@ impl HitTester {
let mut hit_tester = HitTester {
scene,
spatial_nodes: FastHashMap::default(),
pipeline_root_nodes: FastHashMap::default(),
};
hit_tester.read_spatial_tree(spatial_tree);
hit_tester
Expand All @@ -309,7 +315,13 @@ impl HitTester {
self.spatial_nodes.clear();
self.spatial_nodes.reserve(spatial_tree.spatial_node_count());

self.pipeline_root_nodes.clear();

spatial_tree.visit_nodes(|index, node| {
// If we haven't already seen a node for this pipeline, record this one as the root
// node.
self.pipeline_root_nodes.entry(node.pipeline_id).or_insert(index);

//TODO: avoid inverting more than necessary:
// - if the coordinate system is non-invertible, no need to try any of these concrete transforms
// - if there are other places where inversion is needed, let's not repeat the step
Expand All @@ -328,6 +340,8 @@ impl HitTester {
}

pub fn hit_test(&self, test: HitTest) -> HitTestResult {
let point = test.get_absolute_point(self);

let mut result = HitTestResult::default();

let mut current_spatial_node_index = SpatialNodeIndex::INVALID;
Expand All @@ -344,7 +358,7 @@ impl HitTester {
point_in_layer = scroll_node
.world_content_transform
.inverse()
.and_then(|inverted| inverted.project_point2d(test.point));
.and_then(|inverted| inverted.project_point2d(point));
current_spatial_node_index = item.spatial_node_index;
}

Expand Down Expand Up @@ -372,7 +386,7 @@ impl HitTester {
.world_content_transform;
if let Some(transformed_point) = transform
.inverse()
.and_then(|inverted| inverted.project_point2d(test.point))
.and_then(|inverted| inverted.project_point2d(point))
{
if !clip_node.region.contains(&transformed_point) {
is_valid = false;
Expand All @@ -397,24 +411,59 @@ impl HitTester {
tag: item.tag,
animation_id: item.animation_id,
});

if !test.flags.contains(HitTestFlags::FIND_ALL) {
return result;
}
}

result.items.dedup();
result
}

fn get_pipeline_root(&self, pipeline_id: PipelineId) -> &HitTestSpatialNode {
&self.spatial_nodes[&self.pipeline_root_nodes[&pipeline_id]]
}

}

#[derive(MallocSizeOf)]
pub struct HitTest {
pipeline_id: Option<PipelineId>,
point: WorldPoint,
#[ignore_malloc_size_of = "bitflags"]
flags: HitTestFlags,
}

impl HitTest {
pub fn new(
pipeline_id: Option<PipelineId>,
point: WorldPoint,
flags: HitTestFlags,
) -> HitTest {
HitTest {
pipeline_id,
point,
flags
}
}

fn get_absolute_point(&self, hit_tester: &HitTester) -> WorldPoint {
if !self.flags.contains(HitTestFlags::POINT_RELATIVE_TO_PIPELINE_VIEWPORT) {
return self.point;
}

let point = LayoutPoint::new(self.point.x, self.point.y);
self.pipeline_id
.and_then(|id|
hit_tester
.get_pipeline_root(id)
.world_viewport_transform
.transform_point2d(point)
)
.unwrap_or_else(|| {
WorldPoint::new(self.point.x, self.point.y)
})
}

}
8 changes: 5 additions & 3 deletions webrender/src/render_api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use std::marker::PhantomData;
use std::path::PathBuf;
use std::sync::Arc;
use std::u32;
use api::MinimapData;
use api::{HitTestFlags, MinimapData};
use time::precise_time_ns;
use crate::api::channel::{Sender, single_msg_channel, unbounded_channel};
use crate::api::{BuiltDisplayList, IdNamespace, ExternalScrollId, Parameter, BoolParameter};
Expand Down Expand Up @@ -792,7 +792,7 @@ pub enum FrameMsg {
///
UpdateEpoch(PipelineId, Epoch),
///
HitTest(WorldPoint, Sender<HitTestResult>),
HitTest(Option<PipelineId>, WorldPoint, HitTestFlags, Sender<HitTestResult>),
///
RequestHitTester(Sender<Arc<dyn ApiHitTester>>),
///
Expand Down Expand Up @@ -1276,13 +1276,15 @@ impl RenderApi {
/// front to back.
pub fn hit_test(&self,
document_id: DocumentId,
pipeline_id: Option<PipelineId>,
point: WorldPoint,
flags: HitTestFlags,
) -> HitTestResult {
let (tx, rx) = single_msg_channel();

self.send_frame_msg(
document_id,
FrameMsg::HitTest(point, tx)
FrameMsg::HitTest(pipeline_id, point, flags, tx)
);
rx.recv().unwrap()
}
Expand Down
4 changes: 2 additions & 2 deletions webrender/src/render_backend.rs
Original file line number Diff line number Diff line change
Expand Up @@ -443,14 +443,14 @@ impl Document {
FrameMsg::UpdateEpoch(pipeline_id, epoch) => {
self.scene.pipeline_epochs.insert(pipeline_id, epoch);
}
FrameMsg::HitTest(point, tx) => {
FrameMsg::HitTest(pipeline_id, point, flags, tx) => {
if !self.hit_tester_is_valid {
self.rebuild_hit_tester();
}

let result = match self.hit_tester {
Some(ref hit_tester) => {
hit_tester.hit_test(HitTest::new(point))
hit_tester.hit_test(HitTest::new(pipeline_id, point, flags))
}
None => HitTestResult { items: Vec::new() },
};
Expand Down
19 changes: 16 additions & 3 deletions webrender_api/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -336,9 +336,11 @@ impl NotificationRequest {
/// the RenderBackendThread.
pub trait ApiHitTester: Send + Sync {
/// Does a hit test on display items in the specified document, at the given
/// point. The vector of hit results will contain all display items that match,
/// ordered from front to back.
fn hit_test(&self, point: WorldPoint) -> HitTestResult;
/// point. If a pipeline_id is specified, it is used to further restrict the
/// hit results so that only items inside that pipeline are matched. The vector
/// of hit results will contain all display items that match, ordered from
/// front to back.
fn hit_test(&self, pipeline_id: Option<PipelineId>, point: WorldPoint, flags: HitTestFlags) -> HitTestResult;
}

/// A hit tester requested to the render backend thread but not necessarily ready yet.
Expand Down Expand Up @@ -376,6 +378,17 @@ pub struct HitTestResult {
pub items: Vec<HitTestResultItem>,
}

bitflags! {
#[derive(Deserialize, Serialize)]
///
pub struct HitTestFlags: u8 {
///
const FIND_ALL = 0b00000001;
///
const POINT_RELATIVE_TO_PIPELINE_VIEWPORT = 0b00000010;
}
}

impl Drop for NotificationRequest {
fn drop(&mut self) {
if let Some(ref mut handler) = self.handler {
Expand Down
2 changes: 2 additions & 0 deletions wrench/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -984,7 +984,9 @@ fn render<'a>(
VirtualKeyCode::X => {
let results = wrench.api.hit_test(
wrench.document_id,
None,
cursor_position,
HitTestFlags::empty(),
);

println!("Hit test results:");
Expand Down
2 changes: 2 additions & 0 deletions wrench/src/rawtest.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1387,7 +1387,9 @@ impl<'a> RawtestHarness<'a> {
let hit_test = |point: WorldPoint| -> HitTestResult {
self.wrench.api.hit_test(
self.wrench.document_id,
None,
point,
HitTestFlags::empty(),
)
};

Expand Down

0 comments on commit b296300

Please sign in to comment.