From 96f6c53b52d53cdb5d4b4105c4bb901002a34d08 Mon Sep 17 00:00:00 2001 From: Martin Robinson Date: Mon, 27 Oct 2014 09:45:33 -0700 Subject: [PATCH] Make compositor event handling a Layer trait This simplifies the way that event handling interacts with the compositor. --- components/compositing/compositor.rs | 58 +++-- components/compositing/compositor_data.rs | 6 +- components/compositing/events.rs | 245 ++++++++++------------ 3 files changed, 161 insertions(+), 148 deletions(-) diff --git a/components/compositing/compositor.rs b/components/compositing/compositor.rs index ab959f17a3f..11b5b5346e9 100644 --- a/components/compositing/compositor.rs +++ b/components/compositing/compositor.rs @@ -8,8 +8,7 @@ use compositor_task::{GetGraphicsMetadata, CreateOrUpdateRootLayer, CreateOrUpda use compositor_task::{SetLayerOrigin, Paint, ScrollFragmentPoint, LoadComplete}; use compositor_task::{ShutdownComplete, ChangeRenderState, RenderMsgDiscarded}; use constellation::SendableFrameTree; -use events; -use events::ScrollPositionChanged; +use events::{LayerEventHandling, ScrollPositionChanged}; use pipeline::CompositionPipeline; use windowing; use windowing::{FinishedWindowEvent, IdleWindowEvent, LoadUrlWindowEvent, MouseWindowClickEvent}; @@ -132,6 +131,11 @@ enum ShutdownState { FinishedShuttingDown, } +struct HitTestResult { + layer: Rc>, + point: TypedPoint2D, +} + impl IOCompositor { fn new(window: Rc, port: Receiver, @@ -548,8 +552,7 @@ impl IOCompositor { match self.find_layer_with_pipeline_and_layer_id(pipeline_id, layer_id) { Some(ref layer) => { if layer.extra_data.borrow().wants_scroll_events == WantsScrollEvents { - events::clamp_scroll_offset_and_scroll_layer(layer.clone(), - TypedPoint2D(0f32, 0f32) - origin); + layer.clamp_scroll_offset_and_scroll_layer(TypedPoint2D(0f32, 0f32) - origin); } true } @@ -720,14 +723,16 @@ impl IOCompositor { MouseWindowMouseDownEvent(_, p) => p, MouseWindowMouseUpEvent(_, p) => p, }; - for layer in self.scene.root.iter() { - events::send_mouse_event(layer.clone(), mouse_window_event, point / self.scene.scale); + match self.find_topmost_layer_at_point(point / self.scene.scale) { + Some(result) => result.layer.send_mouse_event(mouse_window_event, result.point), + None => {}, } } fn on_mouse_window_move_event_class(&self, cursor: TypedPoint2D) { - for layer in self.scene.root.iter() { - events::send_mouse_move_event(layer.clone(), cursor / self.scene.scale); + match self.find_topmost_layer_at_point(cursor / self.scene.scale) { + Some(result) => result.layer.send_mouse_move_event(result.point), + None => {}, } } @@ -740,9 +745,7 @@ impl IOCompositor { let mut scroll = false; match self.scene.root { Some(ref mut layer) => { - scroll = events::handle_scroll_event(layer.clone(), - delta, - cursor) == ScrollPositionChanged; + scroll = layer.handle_scroll_event(delta, cursor) == ScrollPositionChanged; } None => { } } @@ -798,9 +801,7 @@ impl IOCompositor { let cursor = TypedPoint2D(-1f32, -1f32); // Make sure this hits the base layer. match self.scene.root { Some(ref mut layer) => { - events::handle_scroll_event(layer.clone(), - page_delta, - cursor); + layer.handle_scroll_event(page_delta, cursor); } None => { } } @@ -1002,4 +1003,33 @@ impl IOCompositor { fn recomposite_if(&mut self, result: bool) { self.recomposite = result || self.recomposite; } + + fn find_topmost_layer_at_point_for_layer(&self, + layer: Rc>, + point: TypedPoint2D) + -> Option { + let child_point = point - layer.bounds.borrow().origin; + for child in layer.children().iter().rev() { + let result = self.find_topmost_layer_at_point_for_layer(child.clone(), child_point); + if result.is_some() { + return result; + } + } + + let point = point - *layer.content_offset.borrow(); + if !layer.bounds.borrow().contains(&point) { + return None; + } + + return Some(HitTestResult { layer: layer, point: point }); + } + + fn find_topmost_layer_at_point(&self, + point: TypedPoint2D) + -> Option { + match self.scene.root { + Some(ref layer) => self.find_topmost_layer_at_point_for_layer(layer.clone(), point), + None => None, + } + } } diff --git a/components/compositing/compositor_data.rs b/components/compositing/compositor_data.rs index 13e3c3a4e6b..5824d4a6785 100644 --- a/components/compositing/compositor_data.rs +++ b/components/compositing/compositor_data.rs @@ -3,7 +3,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use compositor_task::LayerProperties; -use events; +use events::LayerEventHandling; use pipeline::CompositionPipeline; use azure::azure_hl; @@ -88,9 +88,7 @@ impl CompositorData { // Call scroll for bounds checking if the page shrunk. Use (-1, -1) as the // cursor position to make sure the scroll isn't propagated downwards. - events::handle_scroll_event(layer.clone(), - TypedPoint2D(0f32, 0f32), - TypedPoint2D(-1f32, -1f32)); + layer.handle_scroll_event(TypedPoint2D(0f32, 0f32), TypedPoint2D(-1f32, -1f32)); CompositorData::update_layer_except_size(layer, layer_properties); } diff --git a/components/compositing/events.rs b/components/compositing/events.rs index 371f033b724..fcab77c7eaa 100644 --- a/components/compositing/events.rs +++ b/components/compositing/events.rs @@ -5,8 +5,10 @@ use compositor_data::{CompositorData, WantsScrollEvents}; use windowing::{MouseWindowEvent, MouseWindowClickEvent, MouseWindowMouseDownEvent}; use windowing::MouseWindowMouseUpEvent; +use windowing::WindowMethods; use geom::length::Length; +use geom::matrix::identity; use geom::point::{Point2D, TypedPoint2D}; use geom::rect::Rect; use geom::size::TypedSize2D; @@ -15,10 +17,6 @@ use layers::layers::Layer; use script_traits::{ClickEvent, MouseDownEvent, MouseMoveEvent, MouseUpEvent, SendEventMsg}; use script_traits::{ScriptControlChan}; use servo_msg::compositor_msg::FixedPosition; -use std::rc::Rc; - - -use geom::matrix::identity; trait Clampable { fn clamp(&self, mn: &Self, mx: &Self) -> Self; @@ -45,157 +43,144 @@ pub enum ScrollEventResult { ScrollPositionUnchanged, } -/// Move the layer's descendants that don't want scroll events and scroll by a relative -/// specified amount in page coordinates. This also takes in a cursor position to see if the -/// mouse is over child layers first. If a layer successfully scrolled, returns true; otherwise -/// returns false, so a parent layer can scroll instead. -pub fn handle_scroll_event(layer: Rc>, +pub trait LayerEventHandling { + /// Move the layer's descendants that don't want scroll events and scroll by a relative + /// specified amount in page coordinates. This also takes in a cursor position to see if the + /// mouse is over child layers first. If a layer successfully scrolled returns either + /// ScrollPositionUnchanged or ScrollPositionChanged. If no layer was targeted by the event + /// returns ScrollEventUnhandled. + fn handle_scroll_event(&self, delta: TypedPoint2D, cursor: TypedPoint2D) - -> ScrollEventResult { - // If this layer doesn't want scroll events, neither it nor its children can handle scroll - // events. - if layer.extra_data.borrow().wants_scroll_events != WantsScrollEvents { - return ScrollEventUnhandled; - } + -> ScrollEventResult; - // Allow children to scroll. - let scroll_offset = layer.extra_data.borrow().scroll_offset; - let new_cursor = cursor - scroll_offset; - for child in layer.children().iter() { - let child_bounds = child.bounds.borrow(); - if child_bounds.contains(&new_cursor) { - let result = handle_scroll_event(child.clone(), - delta, - new_cursor - child_bounds.origin); - if result != ScrollEventUnhandled { - return result; - } - } - } + // Takes in a MouseWindowEvent, determines if it should be passed to children, and + // sends the event off to the appropriate pipeline. NB: the cursor position is in + // page coordinates. + fn send_mouse_event(&self, + event: MouseWindowEvent, + cursor: TypedPoint2D); - clamp_scroll_offset_and_scroll_layer(layer, scroll_offset + delta) + fn send_mouse_move_event(&self, + cursor: TypedPoint2D); + + fn clamp_scroll_offset_and_scroll_layer(&self, + new_offset: TypedPoint2D) + -> ScrollEventResult; + fn scroll_layer_and_all_child_layers(&self, + new_offset: TypedPoint2D) + -> bool; } -pub fn calculate_content_size_for_layer(layer: Rc>) - -> TypedSize2D { +fn calculate_content_size_for_layer(layer: &Layer) + -> TypedSize2D { layer.children().iter().fold(Rect::zero(), |unioned_rect, child_rect| { unioned_rect.union(&*child_rect.bounds.borrow()) }).size } -pub fn clamp_scroll_offset_and_scroll_layer(layer: Rc>, +impl LayerEventHandling for Layer { + fn handle_scroll_event(&self, + delta: TypedPoint2D, + cursor: TypedPoint2D) + -> ScrollEventResult { + // If this layer doesn't want scroll events, neither it nor its children can handle scroll + // events. + if self.extra_data.borrow().wants_scroll_events != WantsScrollEvents { + return ScrollEventUnhandled; + } + + //// Allow children to scroll. + let scroll_offset = self.extra_data.borrow().scroll_offset; + let new_cursor = cursor - scroll_offset; + for child in self.children().iter() { + let child_bounds = child.bounds.borrow(); + if child_bounds.contains(&new_cursor) { + let result = child.handle_scroll_event(delta, new_cursor - child_bounds.origin); + if result != ScrollEventUnhandled { + return result; + } + } + } + + self.clamp_scroll_offset_and_scroll_layer(scroll_offset + delta) + } + + fn clamp_scroll_offset_and_scroll_layer(&self, new_offset: TypedPoint2D) -> ScrollEventResult { - let layer_size = layer.bounds.borrow().size; - let content_size = calculate_content_size_for_layer(layer.clone()); - let min_x = (layer_size.width - content_size.width).get().min(0.0); - let min_y = (layer_size.height - content_size.height).get().min(0.0); - let new_offset : TypedPoint2D = - Point2D(Length(new_offset.x.get().clamp(&min_x, &0.0)), - Length(new_offset.y.get().clamp(&min_y, &0.0))); + let layer_size = self.bounds.borrow().size; + let content_size = calculate_content_size_for_layer(self); + let min_x = (layer_size.width - content_size.width).get().min(0.0); + let min_y = (layer_size.height - content_size.height).get().min(0.0); + let new_offset : TypedPoint2D = + Point2D(Length(new_offset.x.get().clamp(&min_x, &0.0)), + Length(new_offset.y.get().clamp(&min_y, &0.0))); - if layer.extra_data.borrow().scroll_offset == new_offset { - return ScrollPositionUnchanged; - } + if self.extra_data.borrow().scroll_offset == new_offset { + return ScrollPositionUnchanged; + } - // The scroll offset is just a record of the scroll position of this scrolling root, - // but scroll_layer_and_all_child_layers actually moves the child layers. - layer.extra_data.borrow_mut().scroll_offset = new_offset; + // The scroll offset is just a record of the scroll position of this scrolling root, + // but scroll_layer_and_all_child_layers actually moves the child layers. + self.extra_data.borrow_mut().scroll_offset = new_offset; - let mut result = false; - for child in layer.children().iter() { - result |= scroll_layer_and_all_child_layers(child.clone(), new_offset); - } + let mut result = false; + for child in self.children().iter() { + result |= child.scroll_layer_and_all_child_layers(new_offset); + } - if result { - return ScrollPositionChanged; - } else { - return ScrollPositionUnchanged; - } -} - -fn scroll_layer_and_all_child_layers(layer: Rc>, - new_offset: TypedPoint2D) - -> bool { - let mut result = false; - - // Only scroll this layer if it's not fixed-positioned. - if layer.extra_data.borrow().scroll_policy != FixedPosition { - let new_offset = new_offset.to_untyped(); - *layer.transform.borrow_mut() = identity().translate(new_offset.x, - new_offset.y, - 0.0); - *layer.content_offset.borrow_mut() = Point2D::from_untyped(&new_offset); - result = true - } - - let offset_for_children = new_offset + layer.extra_data.borrow().scroll_offset; - for child in layer.children().iter() { - result |= scroll_layer_and_all_child_layers(child.clone(), offset_for_children); - } - - return result; -} - -struct HitTestResult { - layer: Rc>, - point: TypedPoint2D, -} - -pub fn find_topmost_layer_at_point(layer: Rc>, - point: TypedPoint2D) - -> Option { - let child_point = point - layer.bounds.borrow().origin; - for child in layer.children().iter().rev() { - let result = find_topmost_layer_at_point(child.clone(), child_point); - if result.is_some() { - return result; + if result { + return ScrollPositionChanged; + } else { + return ScrollPositionUnchanged; } } - let point = point - *layer.content_offset.borrow(); - if !layer.bounds.borrow().contains(&point) { - return None; - } - - return Some(HitTestResult { layer: layer, point: point }); -} - -// Takes in a MouseWindowEvent, determines if it should be passed to children, and -// sends the event off to the appropriate pipeline. NB: the cursor position is in -// page coordinates. -pub fn send_mouse_event(layer: Rc>, + fn send_mouse_event(&self, event: MouseWindowEvent, cursor: TypedPoint2D) { - match find_topmost_layer_at_point(layer.clone(), cursor) { - Some(result) => { - let event_point = result.point.to_untyped(); - let message = match event { - MouseWindowClickEvent(button, _) => ClickEvent(button, event_point), - MouseWindowMouseDownEvent(button, _) => MouseDownEvent(button, event_point), - MouseWindowMouseUpEvent(button, _) => MouseUpEvent(button, event_point), - }; - let pipeline = &result.layer.extra_data.borrow().pipeline; - let ScriptControlChan(ref chan) = pipeline.script_chan; - let _ = chan.send_opt(SendEventMsg(pipeline.id.clone(), message)); - }, - None => {}, + let event_point = cursor.to_untyped(); + let message = match event { + MouseWindowClickEvent(button, _) => ClickEvent(button, event_point), + MouseWindowMouseDownEvent(button, _) => MouseDownEvent(button, event_point), + MouseWindowMouseUpEvent(button, _) => MouseUpEvent(button, event_point), + }; + let pipeline = &self.extra_data.borrow().pipeline; + let ScriptControlChan(ref chan) = pipeline.script_chan; + let _ = chan.send_opt(SendEventMsg(pipeline.id.clone(), message)); } -} - -pub fn send_mouse_move_event(layer: Rc>, + fn send_mouse_move_event(&self, cursor: TypedPoint2D) { - match find_topmost_layer_at_point(layer.clone(), cursor) { - Some(result) => { - let message = MouseMoveEvent(result.point.to_untyped()); - let pipeline = &result.layer.extra_data.borrow().pipeline; - let ScriptControlChan(ref chan) = pipeline.script_chan; - let _ = chan.send_opt(SendEventMsg(pipeline.id.clone(), message)); - }, - None => {}, + let message = MouseMoveEvent(cursor.to_untyped()); + let pipeline = &self.extra_data.borrow().pipeline; + let ScriptControlChan(ref chan) = pipeline.script_chan; + let _ = chan.send_opt(SendEventMsg(pipeline.id.clone(), message)); } -} + fn scroll_layer_and_all_child_layers(&self, + new_offset: TypedPoint2D) + -> bool { + let mut result = false; + + // Only scroll this layer if it's not fixed-positioned. + if self.extra_data.borrow().scroll_policy != FixedPosition { + let new_offset = new_offset.to_untyped(); + *self.transform.borrow_mut() = identity().translate(new_offset.x, + new_offset.y, + 0.0); + *self.content_offset.borrow_mut() = Point2D::from_untyped(&new_offset); + result = true + } + + let offset_for_children = new_offset + self.extra_data.borrow().scroll_offset; + for child in self.children().iter() { + result |= child.scroll_layer_and_all_child_layers(offset_for_children); + } + + return result; + } + +}