mirror of
https://github.com/servo/servo.git
synced 2025-08-04 21:20:23 +01:00
Merge pull request #2789 from zwarich/compositor-events
Move compositor event handling code to a new `events` module
This commit is contained in:
commit
cb7828c76e
4 changed files with 242 additions and 223 deletions
|
@ -50,6 +50,7 @@ pub use constellation::Constellation;
|
||||||
pub mod compositor_task;
|
pub mod compositor_task;
|
||||||
|
|
||||||
mod compositor_data;
|
mod compositor_data;
|
||||||
|
mod events;
|
||||||
|
|
||||||
mod compositor;
|
mod compositor;
|
||||||
mod headless;
|
mod headless;
|
||||||
|
|
|
@ -8,6 +8,7 @@ use compositor_task::{GetGraphicsMetadata, CreateOrUpdateRootLayer, CreateOrUpda
|
||||||
use compositor_task::{SetLayerClipRect, Paint, ScrollFragmentPoint, LoadComplete};
|
use compositor_task::{SetLayerClipRect, Paint, ScrollFragmentPoint, LoadComplete};
|
||||||
use compositor_task::{ShutdownComplete, ChangeRenderState};
|
use compositor_task::{ShutdownComplete, ChangeRenderState};
|
||||||
use constellation::SendableFrameTree;
|
use constellation::SendableFrameTree;
|
||||||
|
use events;
|
||||||
use pipeline::CompositionPipeline;
|
use pipeline::CompositionPipeline;
|
||||||
use platform::{Application, Window};
|
use platform::{Application, Window};
|
||||||
use windowing;
|
use windowing;
|
||||||
|
@ -444,7 +445,7 @@ impl IOCompositor {
|
||||||
let needs_recomposite = match self.scene.root {
|
let needs_recomposite = match self.scene.root {
|
||||||
Some(ref mut root_layer) => {
|
Some(ref mut root_layer) => {
|
||||||
self.fragment_point.take().map_or(false, |fragment_point| {
|
self.fragment_point.take().map_or(false, |fragment_point| {
|
||||||
CompositorData::move(root_layer.clone(),
|
events::move(root_layer.clone(),
|
||||||
pipeline_id,
|
pipeline_id,
|
||||||
layer_id,
|
layer_id,
|
||||||
fragment_point,
|
fragment_point,
|
||||||
|
@ -528,7 +529,7 @@ impl IOCompositor {
|
||||||
Some(ref layer) if layer.extra_data.borrow().pipeline.id == pipeline_id &&
|
Some(ref layer) if layer.extra_data.borrow().pipeline.id == pipeline_id &&
|
||||||
!layer.extra_data.borrow().hidden => {
|
!layer.extra_data.borrow().hidden => {
|
||||||
(true,
|
(true,
|
||||||
CompositorData::move(layer.clone(), pipeline_id, layer_id, point, page_window))
|
events::move(layer.clone(), pipeline_id, layer_id, point, page_window))
|
||||||
}
|
}
|
||||||
Some(_) | None => {
|
Some(_) | None => {
|
||||||
self.fragment_point = Some(point);
|
self.fragment_point = Some(point);
|
||||||
|
@ -640,14 +641,14 @@ impl IOCompositor {
|
||||||
MouseWindowMouseUpEvent(_, p) => p / scale,
|
MouseWindowMouseUpEvent(_, p) => p / scale,
|
||||||
};
|
};
|
||||||
for layer in self.scene.root.iter() {
|
for layer in self.scene.root.iter() {
|
||||||
CompositorData::send_mouse_event(layer.clone(), mouse_window_event, point);
|
events::send_mouse_event(layer.clone(), mouse_window_event, point);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn on_mouse_window_move_event_class(&self, cursor: TypedPoint2D<DevicePixel, f32>) {
|
fn on_mouse_window_move_event_class(&self, cursor: TypedPoint2D<DevicePixel, f32>) {
|
||||||
let scale = self.device_pixels_per_page_px();
|
let scale = self.device_pixels_per_page_px();
|
||||||
for layer in self.scene.root.iter() {
|
for layer in self.scene.root.iter() {
|
||||||
CompositorData::send_mouse_move_event(layer.clone(), cursor / scale);
|
events::send_mouse_move_event(layer.clone(), cursor / scale);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -662,7 +663,7 @@ impl IOCompositor {
|
||||||
let mut scroll = false;
|
let mut scroll = false;
|
||||||
match self.scene.root {
|
match self.scene.root {
|
||||||
Some(ref mut layer) => {
|
Some(ref mut layer) => {
|
||||||
scroll = CompositorData::handle_scroll_event(layer.clone(),
|
scroll = events::handle_scroll_event(layer.clone(),
|
||||||
page_delta,
|
page_delta,
|
||||||
page_cursor,
|
page_cursor,
|
||||||
page_window) || scroll;
|
page_window) || scroll;
|
||||||
|
@ -719,7 +720,7 @@ impl IOCompositor {
|
||||||
|
|
||||||
match self.scene.root {
|
match self.scene.root {
|
||||||
Some(ref mut layer) => {
|
Some(ref mut layer) => {
|
||||||
CompositorData::handle_scroll_event(layer.clone(),
|
events::handle_scroll_event(layer.clone(),
|
||||||
page_delta,
|
page_delta,
|
||||||
page_cursor,
|
page_cursor,
|
||||||
page_window);
|
page_window);
|
||||||
|
|
|
@ -3,23 +3,19 @@
|
||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
use compositor_task::LayerProperties;
|
use compositor_task::LayerProperties;
|
||||||
|
use events;
|
||||||
use pipeline::CompositionPipeline;
|
use pipeline::CompositionPipeline;
|
||||||
use windowing::{MouseWindowEvent, MouseWindowClickEvent, MouseWindowMouseDownEvent};
|
|
||||||
use windowing::{MouseWindowMouseUpEvent};
|
|
||||||
|
|
||||||
use azure::azure_hl::Color;
|
use azure::azure_hl::Color;
|
||||||
use geom::length::Length;
|
|
||||||
use geom::matrix::identity;
|
use geom::matrix::identity;
|
||||||
use geom::point::{Point2D, TypedPoint2D};
|
use geom::point::{Point2D, TypedPoint2D};
|
||||||
use geom::rect::{Rect, TypedRect};
|
use geom::rect::Rect;
|
||||||
use geom::size::{Size2D, TypedSize2D};
|
use geom::size::{Size2D, TypedSize2D};
|
||||||
use gfx::render_task::{ReRenderMsg, UnusedBufferMsg};
|
use gfx::render_task::{ReRenderMsg, UnusedBufferMsg};
|
||||||
use layers::layers::{Layer, Flip, LayerBuffer, LayerBufferSet, NoFlip, TextureLayer};
|
use layers::layers::{Layer, Flip, LayerBuffer, LayerBufferSet, NoFlip, TextureLayer};
|
||||||
use layers::quadtree::{Tile, Normal, Hidden};
|
use layers::quadtree::{Tile, Normal, Hidden};
|
||||||
use layers::platform::surface::{NativeCompositingGraphicsContext, NativeSurfaceMethods};
|
use layers::platform::surface::{NativeCompositingGraphicsContext, NativeSurfaceMethods};
|
||||||
use layers::texturegl::{Texture, TextureTarget};
|
use layers::texturegl::{Texture, TextureTarget};
|
||||||
use script::dom::event::{ClickEvent, MouseDownEvent, MouseMoveEvent, MouseUpEvent};
|
|
||||||
use script::script_task::{ScriptChan, SendEventMsg};
|
|
||||||
use servo_msg::compositor_msg::{Epoch, FixedPosition, LayerId};
|
use servo_msg::compositor_msg::{Epoch, FixedPosition, LayerId};
|
||||||
use servo_msg::compositor_msg::ScrollPolicy;
|
use servo_msg::compositor_msg::ScrollPolicy;
|
||||||
use servo_msg::constellation_msg::PipelineId;
|
use servo_msg::constellation_msg::PipelineId;
|
||||||
|
@ -82,24 +78,6 @@ pub enum WantsScrollEventsFlag {
|
||||||
DoesntWantScrollEvents,
|
DoesntWantScrollEvents,
|
||||||
}
|
}
|
||||||
|
|
||||||
trait Clampable {
|
|
||||||
fn clamp(&self, mn: &Self, mx: &Self) -> Self;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Clampable for f32 {
|
|
||||||
/// Returns the number constrained within the range `mn <= self <= mx`.
|
|
||||||
/// If any of the numbers are `NAN` then `NAN` is returned.
|
|
||||||
#[inline]
|
|
||||||
fn clamp(&self, mn: &f32, mx: &f32) -> f32 {
|
|
||||||
match () {
|
|
||||||
_ if self.is_nan() => *self,
|
|
||||||
_ if !(*self <= *mx) => *mx,
|
|
||||||
_ if !(*self >= *mn) => *mn,
|
|
||||||
_ => *self,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl CompositorData {
|
impl CompositorData {
|
||||||
pub fn new(pipeline: CompositionPipeline,
|
pub fn new(pipeline: CompositionPipeline,
|
||||||
layer_id: LayerId,
|
layer_id: LayerId,
|
||||||
|
@ -169,141 +147,6 @@ impl CompositorData {
|
||||||
Layer::add_child(layer.clone(), new_kid.clone());
|
Layer::add_child(layer.clone(), new_kid.clone());
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 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<Layer<CompositorData>>,
|
|
||||||
delta: TypedPoint2D<PagePx, f32>,
|
|
||||||
cursor: TypedPoint2D<PagePx, f32>,
|
|
||||||
window_size: TypedSize2D<PagePx, f32>)
|
|
||||||
-> bool {
|
|
||||||
// If this layer is hidden, neither it nor its children will scroll.
|
|
||||||
if layer.extra_data.borrow().hidden {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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 false
|
|
||||||
}
|
|
||||||
|
|
||||||
// Allow children to scroll.
|
|
||||||
let cursor = cursor - layer.extra_data.borrow().scroll_offset;
|
|
||||||
for child in layer.children().iter() {
|
|
||||||
match child.extra_data.borrow().scissor {
|
|
||||||
None => {
|
|
||||||
error!("CompositorData: unable to perform cursor hit test for layer");
|
|
||||||
}
|
|
||||||
Some(rect) => {
|
|
||||||
let rect: TypedRect<PagePx, f32> = Rect::from_untyped(&rect);
|
|
||||||
if rect.contains(&cursor) &&
|
|
||||||
CompositorData::handle_scroll_event(child.clone(),
|
|
||||||
delta,
|
|
||||||
cursor - rect.origin,
|
|
||||||
rect.size) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// This scroll event is mine!
|
|
||||||
// Scroll this layer!
|
|
||||||
let old_origin = layer.extra_data.borrow().scroll_offset.clone();
|
|
||||||
layer.extra_data.borrow_mut().scroll_offset = old_origin + delta;
|
|
||||||
|
|
||||||
// bounds checking
|
|
||||||
let page_size = match layer.extra_data.borrow().page_size {
|
|
||||||
Some(size) => size,
|
|
||||||
None => fail!("CompositorData: tried to scroll with no page size set"),
|
|
||||||
};
|
|
||||||
|
|
||||||
let window_size = window_size.to_untyped();
|
|
||||||
let scroll_offset = layer.extra_data.borrow().scroll_offset.to_untyped();
|
|
||||||
|
|
||||||
let min_x = (window_size.width - page_size.width).min(0.0);
|
|
||||||
layer.extra_data.borrow_mut().scroll_offset.x = Length(scroll_offset.x.clamp(&min_x, &0.0));
|
|
||||||
|
|
||||||
let min_y = (window_size.height - page_size.height).min(0.0);
|
|
||||||
layer.extra_data.borrow_mut().scroll_offset.y = Length(scroll_offset.y.clamp(&min_y, &0.0));
|
|
||||||
|
|
||||||
if old_origin - layer.extra_data.borrow().scroll_offset == TypedPoint2D(0f32, 0f32) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
let offset = layer.extra_data.borrow().scroll_offset.clone();
|
|
||||||
CompositorData::scroll(layer.clone(), offset)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Actually scrolls the descendants of a layer that scroll. This is called by
|
|
||||||
/// `handle_scroll_event` above when it determines that a layer wants to scroll.
|
|
||||||
fn scroll(layer: Rc<Layer<CompositorData>>,
|
|
||||||
scroll_offset: TypedPoint2D<PagePx, f32>)
|
|
||||||
-> bool {
|
|
||||||
let mut result = false;
|
|
||||||
|
|
||||||
// Only scroll this layer if it's not fixed-positioned.
|
|
||||||
if layer.extra_data.borrow().scroll_policy != FixedPosition {
|
|
||||||
// Scroll this layer!
|
|
||||||
layer.extra_data.borrow_mut().scroll_offset = scroll_offset;
|
|
||||||
|
|
||||||
let scroll_offset = layer.extra_data.borrow().scroll_offset.clone();
|
|
||||||
*layer.transform.borrow_mut() = identity().translate(scroll_offset.x.get(), scroll_offset.y.get(), 0.0);
|
|
||||||
|
|
||||||
result = true
|
|
||||||
}
|
|
||||||
|
|
||||||
for child in layer.children().iter() {
|
|
||||||
result = CompositorData::scroll(child.clone(), scroll_offset) || result;
|
|
||||||
}
|
|
||||||
|
|
||||||
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.
|
|
||||||
pub fn send_mouse_event(layer: Rc<Layer<CompositorData>>,
|
|
||||||
event: MouseWindowEvent, cursor: TypedPoint2D<PagePx, f32>) {
|
|
||||||
let cursor = cursor - layer.extra_data.borrow().scroll_offset;
|
|
||||||
for child in layer.children().iter() {
|
|
||||||
if child.extra_data.borrow().hidden {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
match child.extra_data.borrow().scissor {
|
|
||||||
None => {
|
|
||||||
error!("CompositorData: unable to perform cursor hit test for layer");
|
|
||||||
}
|
|
||||||
Some(rect) => {
|
|
||||||
let rect: TypedRect<PagePx, f32> = Rect::from_untyped(&rect);
|
|
||||||
if rect.contains(&cursor) {
|
|
||||||
CompositorData::send_mouse_event(child.clone(), event, cursor - rect.origin);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// This mouse event is mine!
|
|
||||||
let message = match event {
|
|
||||||
MouseWindowClickEvent(button, _) => ClickEvent(button, cursor.to_untyped()),
|
|
||||||
MouseWindowMouseDownEvent(button, _) => MouseDownEvent(button, cursor.to_untyped()),
|
|
||||||
MouseWindowMouseUpEvent(button, _) => MouseUpEvent(button, cursor.to_untyped()),
|
|
||||||
};
|
|
||||||
let ScriptChan(ref chan) = layer.extra_data.borrow().pipeline.script_chan;
|
|
||||||
let _ = chan.send_opt(SendEventMsg(layer.extra_data.borrow().pipeline.id.clone(), message));
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn send_mouse_move_event(layer: Rc<Layer<CompositorData>>,
|
|
||||||
cursor: TypedPoint2D<PagePx, f32>) {
|
|
||||||
let message = MouseMoveEvent(cursor.to_untyped());
|
|
||||||
let ScriptChan(ref chan) = layer.extra_data.borrow().pipeline.script_chan;
|
|
||||||
let _ = chan.send_opt(SendEventMsg(layer.extra_data.borrow().pipeline.id.clone(), message));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Given the current window size, determine which tiles need to be (re-)rendered and sends them
|
// Given the current window size, determine which tiles need to be (re-)rendered and sends them
|
||||||
// off the the appropriate renderer. Returns true if and only if the scene should be repainted.
|
// off the the appropriate renderer. Returns true if and only if the scene should be repainted.
|
||||||
pub fn send_buffer_requests_recursively(layer: Rc<Layer<CompositorData>>,
|
pub fn send_buffer_requests_recursively(layer: Rc<Layer<CompositorData>>,
|
||||||
|
@ -439,7 +282,7 @@ impl CompositorData {
|
||||||
// Call scroll for bounds checking if the page shrunk. Use (-1, -1) as the
|
// 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.
|
// cursor position to make sure the scroll isn't propagated downwards.
|
||||||
let size: TypedSize2D<PagePx, f32> = Size2D::from_untyped(&scissor.size);
|
let size: TypedSize2D<PagePx, f32> = Size2D::from_untyped(&scissor.size);
|
||||||
CompositorData::handle_scroll_event(layer.clone(),
|
events::handle_scroll_event(layer.clone(),
|
||||||
TypedPoint2D(0f32, 0f32),
|
TypedPoint2D(0f32, 0f32),
|
||||||
TypedPoint2D(-1f32, -1f32),
|
TypedPoint2D(-1f32, -1f32),
|
||||||
size);
|
size);
|
||||||
|
@ -451,50 +294,6 @@ impl CompositorData {
|
||||||
CompositorData::set_occlusions(layer.clone());
|
CompositorData::set_occlusions(layer.clone());
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn move(layer: Rc<Layer<CompositorData>>,
|
|
||||||
pipeline_id: PipelineId,
|
|
||||||
layer_id: LayerId,
|
|
||||||
origin: Point2D<f32>,
|
|
||||||
window_size: TypedSize2D<PagePx, f32>)
|
|
||||||
-> bool {
|
|
||||||
// Search children for the right layer to move.
|
|
||||||
if layer.extra_data.borrow().pipeline.id != pipeline_id ||
|
|
||||||
layer.extra_data.borrow().id != layer_id {
|
|
||||||
return layer.children().iter().any(|kid| {
|
|
||||||
CompositorData::move(kid.clone(), pipeline_id, layer_id, origin, window_size)
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if layer.extra_data.borrow().wants_scroll_events != WantsScrollEvents {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// Scroll this layer!
|
|
||||||
let old_origin = layer.extra_data.borrow().scroll_offset;
|
|
||||||
layer.extra_data.borrow_mut().scroll_offset = Point2D::from_untyped(&(origin * -1.0));
|
|
||||||
|
|
||||||
// bounds checking
|
|
||||||
let page_size = match layer.extra_data.borrow().page_size {
|
|
||||||
Some(size) => size,
|
|
||||||
None => fail!("CompositorData: tried to scroll with no page size set"),
|
|
||||||
};
|
|
||||||
let window_size = window_size.to_untyped();
|
|
||||||
let scroll_offset = layer.extra_data.borrow().scroll_offset.to_untyped();
|
|
||||||
|
|
||||||
let min_x = (window_size.width - page_size.width).min(0.0);
|
|
||||||
layer.extra_data.borrow_mut().scroll_offset.x = Length(scroll_offset.x.clamp(&min_x, &0.0));
|
|
||||||
let min_y = (window_size.height - page_size.height).min(0.0);
|
|
||||||
layer.extra_data.borrow_mut().scroll_offset.y = Length(scroll_offset.y.clamp(&min_y, &0.0));
|
|
||||||
|
|
||||||
// check to see if we scrolled
|
|
||||||
if old_origin - layer.extra_data.borrow().scroll_offset == TypedPoint2D(0f32, 0f32) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
let offset = layer.extra_data.borrow().scroll_offset.clone();
|
|
||||||
CompositorData::scroll(layer.clone(), offset)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns whether the layer should be vertically flipped.
|
// Returns whether the layer should be vertically flipped.
|
||||||
#[cfg(target_os="macos")]
|
#[cfg(target_os="macos")]
|
||||||
fn texture_flip_and_target(cpu_painting: bool, size: Size2D<uint>) -> (Flip, TextureTarget) {
|
fn texture_flip_and_target(cpu_painting: bool, size: Size2D<uint>) -> (Flip, TextureTarget) {
|
||||||
|
|
218
src/components/compositing/events.rs
Normal file
218
src/components/compositing/events.rs
Normal file
|
@ -0,0 +1,218 @@
|
||||||
|
/* 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 http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
use compositor_data::{CompositorData, WantsScrollEvents};
|
||||||
|
use windowing::{MouseWindowEvent, MouseWindowClickEvent, MouseWindowMouseDownEvent};
|
||||||
|
use windowing::MouseWindowMouseUpEvent;
|
||||||
|
|
||||||
|
use geom::length::Length;
|
||||||
|
use geom::matrix::identity;
|
||||||
|
use geom::point::{Point2D, TypedPoint2D};
|
||||||
|
use geom::rect::{Rect, TypedRect};
|
||||||
|
use geom::size::TypedSize2D;
|
||||||
|
use layers::layers::Layer;
|
||||||
|
use script::dom::event::{ClickEvent, MouseDownEvent, MouseMoveEvent, MouseUpEvent};
|
||||||
|
use script::script_task::{ScriptChan, SendEventMsg};
|
||||||
|
use servo_msg::compositor_msg::{FixedPosition, LayerId};
|
||||||
|
use servo_msg::constellation_msg::PipelineId;
|
||||||
|
use servo_util::geometry::PagePx;
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
trait Clampable {
|
||||||
|
fn clamp(&self, mn: &Self, mx: &Self) -> Self;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Clampable for f32 {
|
||||||
|
/// Returns the number constrained within the range `mn <= self <= mx`.
|
||||||
|
/// If any of the numbers are `NAN` then `NAN` is returned.
|
||||||
|
#[inline]
|
||||||
|
fn clamp(&self, mn: &f32, mx: &f32) -> f32 {
|
||||||
|
match () {
|
||||||
|
_ if self.is_nan() => *self,
|
||||||
|
_ if !(*self <= *mx) => *mx,
|
||||||
|
_ if !(*self >= *mn) => *mn,
|
||||||
|
_ => *self,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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<Layer<CompositorData>>,
|
||||||
|
delta: TypedPoint2D<PagePx, f32>,
|
||||||
|
cursor: TypedPoint2D<PagePx, f32>,
|
||||||
|
window_size: TypedSize2D<PagePx, f32>)
|
||||||
|
-> bool {
|
||||||
|
// If this layer is hidden, neither it nor its children will scroll.
|
||||||
|
if layer.extra_data.borrow().hidden {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Allow children to scroll.
|
||||||
|
let cursor = cursor - layer.extra_data.borrow().scroll_offset;
|
||||||
|
for child in layer.children().iter() {
|
||||||
|
match child.extra_data.borrow().scissor {
|
||||||
|
None => {
|
||||||
|
error!("CompositorData: unable to perform cursor hit test for layer");
|
||||||
|
}
|
||||||
|
Some(rect) => {
|
||||||
|
let rect: TypedRect<PagePx, f32> = Rect::from_untyped(&rect);
|
||||||
|
if rect.contains(&cursor) &&
|
||||||
|
handle_scroll_event(child.clone(),
|
||||||
|
delta,
|
||||||
|
cursor - rect.origin,
|
||||||
|
rect.size) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// This scroll event is mine!
|
||||||
|
// Scroll this layer!
|
||||||
|
let old_origin = layer.extra_data.borrow().scroll_offset.clone();
|
||||||
|
layer.extra_data.borrow_mut().scroll_offset = old_origin + delta;
|
||||||
|
|
||||||
|
// bounds checking
|
||||||
|
let page_size = match layer.extra_data.borrow().page_size {
|
||||||
|
Some(size) => size,
|
||||||
|
None => fail!("CompositorData: tried to scroll with no page size set"),
|
||||||
|
};
|
||||||
|
|
||||||
|
let window_size = window_size.to_untyped();
|
||||||
|
let scroll_offset = layer.extra_data.borrow().scroll_offset.to_untyped();
|
||||||
|
|
||||||
|
let min_x = (window_size.width - page_size.width).min(0.0);
|
||||||
|
layer.extra_data.borrow_mut().scroll_offset.x = Length(scroll_offset.x.clamp(&min_x, &0.0));
|
||||||
|
|
||||||
|
let min_y = (window_size.height - page_size.height).min(0.0);
|
||||||
|
layer.extra_data.borrow_mut().scroll_offset.y = Length(scroll_offset.y.clamp(&min_y, &0.0));
|
||||||
|
|
||||||
|
if old_origin - layer.extra_data.borrow().scroll_offset == TypedPoint2D(0f32, 0f32) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
let offset = layer.extra_data.borrow().scroll_offset.clone();
|
||||||
|
scroll(layer.clone(), offset)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Actually scrolls the descendants of a layer that scroll. This is called by
|
||||||
|
/// `handle_scroll_event` above when it determines that a layer wants to scroll.
|
||||||
|
fn scroll(layer: Rc<Layer<CompositorData>>,
|
||||||
|
scroll_offset: TypedPoint2D<PagePx, f32>)
|
||||||
|
-> bool {
|
||||||
|
let mut result = false;
|
||||||
|
|
||||||
|
// Only scroll this layer if it's not fixed-positioned.
|
||||||
|
if layer.extra_data.borrow().scroll_policy != FixedPosition {
|
||||||
|
// Scroll this layer!
|
||||||
|
layer.extra_data.borrow_mut().scroll_offset = scroll_offset;
|
||||||
|
|
||||||
|
let scroll_offset = layer.extra_data.borrow().scroll_offset.clone();
|
||||||
|
*layer.transform.borrow_mut() = identity().translate(scroll_offset.x.get(), scroll_offset.y.get(), 0.0);
|
||||||
|
|
||||||
|
result = true
|
||||||
|
}
|
||||||
|
|
||||||
|
for child in layer.children().iter() {
|
||||||
|
result = scroll(child.clone(), scroll_offset) || result;
|
||||||
|
}
|
||||||
|
|
||||||
|
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.
|
||||||
|
pub fn send_mouse_event(layer: Rc<Layer<CompositorData>>,
|
||||||
|
event: MouseWindowEvent, cursor: TypedPoint2D<PagePx, f32>) {
|
||||||
|
let cursor = cursor - layer.extra_data.borrow().scroll_offset;
|
||||||
|
for child in layer.children().iter() {
|
||||||
|
if child.extra_data.borrow().hidden {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
match child.extra_data.borrow().scissor {
|
||||||
|
None => {
|
||||||
|
error!("CompositorData: unable to perform cursor hit test for layer");
|
||||||
|
}
|
||||||
|
Some(rect) => {
|
||||||
|
let rect: TypedRect<PagePx, f32> = Rect::from_untyped(&rect);
|
||||||
|
if rect.contains(&cursor) {
|
||||||
|
send_mouse_event(child.clone(), event, cursor - rect.origin);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// This mouse event is mine!
|
||||||
|
let message = match event {
|
||||||
|
MouseWindowClickEvent(button, _) => ClickEvent(button, cursor.to_untyped()),
|
||||||
|
MouseWindowMouseDownEvent(button, _) => MouseDownEvent(button, cursor.to_untyped()),
|
||||||
|
MouseWindowMouseUpEvent(button, _) => MouseUpEvent(button, cursor.to_untyped()),
|
||||||
|
};
|
||||||
|
let ScriptChan(ref chan) = layer.extra_data.borrow().pipeline.script_chan;
|
||||||
|
let _ = chan.send_opt(SendEventMsg(layer.extra_data.borrow().pipeline.id.clone(), message));
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn send_mouse_move_event(layer: Rc<Layer<CompositorData>>,
|
||||||
|
cursor: TypedPoint2D<PagePx, f32>) {
|
||||||
|
let message = MouseMoveEvent(cursor.to_untyped());
|
||||||
|
let ScriptChan(ref chan) = layer.extra_data.borrow().pipeline.script_chan;
|
||||||
|
let _ = chan.send_opt(SendEventMsg(layer.extra_data.borrow().pipeline.id.clone(), message));
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn move(layer: Rc<Layer<CompositorData>>,
|
||||||
|
pipeline_id: PipelineId,
|
||||||
|
layer_id: LayerId,
|
||||||
|
origin: Point2D<f32>,
|
||||||
|
window_size: TypedSize2D<PagePx, f32>)
|
||||||
|
-> bool {
|
||||||
|
// Search children for the right layer to move.
|
||||||
|
if layer.extra_data.borrow().pipeline.id != pipeline_id ||
|
||||||
|
layer.extra_data.borrow().id != layer_id {
|
||||||
|
return layer.children().iter().any(|kid| {
|
||||||
|
move(kid.clone(), pipeline_id, layer_id, origin, window_size)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if layer.extra_data.borrow().wants_scroll_events != WantsScrollEvents {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Scroll this layer!
|
||||||
|
let old_origin = layer.extra_data.borrow().scroll_offset;
|
||||||
|
layer.extra_data.borrow_mut().scroll_offset = Point2D::from_untyped(&(origin * -1.0));
|
||||||
|
|
||||||
|
// bounds checking
|
||||||
|
let page_size = match layer.extra_data.borrow().page_size {
|
||||||
|
Some(size) => size,
|
||||||
|
None => fail!("CompositorData: tried to scroll with no page size set"),
|
||||||
|
};
|
||||||
|
let window_size = window_size.to_untyped();
|
||||||
|
let scroll_offset = layer.extra_data.borrow().scroll_offset.to_untyped();
|
||||||
|
|
||||||
|
let min_x = (window_size.width - page_size.width).min(0.0);
|
||||||
|
layer.extra_data.borrow_mut().scroll_offset.x = Length(scroll_offset.x.clamp(&min_x, &0.0));
|
||||||
|
let min_y = (window_size.height - page_size.height).min(0.0);
|
||||||
|
layer.extra_data.borrow_mut().scroll_offset.y = Length(scroll_offset.y.clamp(&min_y, &0.0));
|
||||||
|
|
||||||
|
// check to see if we scrolled
|
||||||
|
if old_origin - layer.extra_data.borrow().scroll_offset == TypedPoint2D(0f32, 0f32) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
let offset = layer.extra_data.borrow().scroll_offset.clone();
|
||||||
|
scroll(layer.clone(), offset)
|
||||||
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue