From 9f7a22551ccd30650491a261737f706c747b3faf Mon Sep 17 00:00:00 2001 From: Martin Robinson Date: Thu, 31 Jul 2014 10:56:57 -0700 Subject: [PATCH] Make scrolling tick by device pixels Instead of converting a device offset into a page offset and then converting back when positioning layers, keep scroll offsets in device pixels. We still do conversions when calculating the scroll offset for pinch zoom, but we can remove those when pinch zoom and contents zoom are fully separated. --- src/components/compositing/compositor.rs | 54 ++++---- src/components/compositing/compositor_data.rs | 17 +-- src/components/compositing/events.rs | 116 +++++++----------- 3 files changed, 72 insertions(+), 115 deletions(-) diff --git a/src/components/compositing/compositor.rs b/src/components/compositing/compositor.rs index 54f0c637f2f..131b908c7df 100644 --- a/src/components/compositing/compositor.rs +++ b/src/components/compositing/compositor.rs @@ -478,11 +478,6 @@ impl IOCompositor { } } - /// The size of the content area in CSS px at the current zoom level - fn page_window(&self) -> TypedSize2D { - self.window_size.as_f32() / self.device_pixels_per_page_px() - } - fn send_window_size(&self) { let dppx = self.page_zoom * self.device_pixels_per_screen_px(); let initial_viewport = self.window_size.as_f32() / dppx; @@ -499,17 +494,17 @@ impl IOCompositor { fn scroll_layer_to_fragment_point_if_necessary(&mut self, pipeline_id: PipelineId, layer_id: LayerId) { - let page_window = self.page_window(); let device_pixels_per_page_px = self.device_pixels_per_page_px(); + let window_size = self.window_size.as_f32(); let needs_recomposite = match self.scene.root { Some(ref mut root_layer) => { self.fragment_point.take().map_or(false, |fragment_point| { + let fragment_point = fragment_point * device_pixels_per_page_px.get(); events::move(root_layer.clone(), pipeline_id, layer_id, fragment_point, - page_window, - device_pixels_per_page_px) + window_size) }) } None => fail!("Compositor: Tried to scroll to fragment without root layer."), @@ -589,17 +584,19 @@ impl IOCompositor { pipeline_id: PipelineId, layer_id: LayerId, point: Point2D) { - let page_window = self.page_window(); + let device_pixels_per_page_px = self.device_pixels_per_page_px(); + let device_point = point * device_pixels_per_page_px.get(); + let window_size = self.window_size.as_f32(); + let (ask, move): (bool, bool) = match self.scene.root { Some(ref layer) if layer.extra_data.borrow().pipeline.id == pipeline_id => { (true, events::move(layer.clone(), pipeline_id, layer_id, - point, - page_window, - device_pixels_per_page_px)) + device_point, + window_size)) } Some(_) | None => { self.fragment_point = Some(point); @@ -711,7 +708,7 @@ impl IOCompositor { MouseWindowMouseUpEvent(_, p) => p / scale, }; for layer in self.scene.root.iter() { - events::send_mouse_event(layer.clone(), mouse_window_event, point); + events::send_mouse_event(layer.clone(), mouse_window_event, point, scale); } } @@ -725,20 +722,14 @@ impl IOCompositor { fn on_scroll_window_event(&mut self, delta: TypedPoint2D, cursor: TypedPoint2D) { - let scale = self.device_pixels_per_page_px(); - // TODO: modify delta to snap scroll to pixels. - let page_delta = delta / scale; - let page_cursor = cursor.as_f32() / scale; - let page_window = self.page_window(); let mut scroll = false; - let device_pixels_per_page_px = self.device_pixels_per_page_px(); + let window_size = self.window_size.as_f32(); match self.scene.root { Some(ref mut layer) => { scroll = events::handle_scroll_event(layer.clone(), - page_delta, - page_cursor, - page_window, - device_pixels_per_page_px) || scroll; + delta, + cursor.as_f32(), + window_size) || scroll; } None => { } } @@ -775,7 +766,6 @@ impl IOCompositor { self.zoom_action = true; self.zoom_time = precise_time_s(); let old_viewport_zoom = self.viewport_zoom; - let window_size = self.window_size.as_f32(); self.viewport_zoom = ScaleFactor((self.viewport_zoom.get() * magnification).max(1.0)); let viewport_zoom = self.viewport_zoom; @@ -783,21 +773,19 @@ impl IOCompositor { self.update_zoom_transform(); // Scroll as needed - let page_delta = TypedPoint2D( + let window_size = self.window_size.as_f32(); + let page_delta: TypedPoint2D = TypedPoint2D( window_size.width.get() * (viewport_zoom.inv() - old_viewport_zoom.inv()).get() * 0.5, window_size.height.get() * (viewport_zoom.inv() - old_viewport_zoom.inv()).get() * 0.5); - // TODO: modify delta to snap scroll to pixels. - let page_cursor = TypedPoint2D(-1f32, -1f32); // Make sure this hits the base layer - let page_window = self.page_window(); - let device_pixels_per_page_px = self.device_pixels_per_page_px(); + let delta = page_delta * self.device_pixels_per_page_px(); + 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, - page_cursor, - page_window, - device_pixels_per_page_px); + delta, + cursor, + window_size); } None => { } } diff --git a/src/components/compositing/compositor_data.rs b/src/components/compositing/compositor_data.rs index 577afb3851f..62c915bd421 100644 --- a/src/components/compositing/compositor_data.rs +++ b/src/components/compositing/compositor_data.rs @@ -8,7 +8,6 @@ use pipeline::CompositionPipeline; use azure::azure_hl::Color; use geom::point::TypedPoint2D; -use geom::scale_factor::ScaleFactor; use geom::size::{Size2D, TypedSize2D}; use gfx::render_task::UnusedBufferMsg; use layers::layers::{Layer, LayerBufferSet}; @@ -16,7 +15,7 @@ use layers::platform::surface::NativeSurfaceMethods; use servo_msg::compositor_msg::{Epoch, LayerId}; use servo_msg::compositor_msg::ScrollPolicy; use servo_msg::constellation_msg::PipelineId; -use servo_util::geometry::PagePx; +use servo_util::geometry::DevicePixel; use std::rc::Rc; pub struct CompositorData { @@ -26,10 +25,6 @@ pub struct CompositorData { /// The ID of this layer within the pipeline. pub id: LayerId, - /// The offset of the page due to scrolling. (0,0) is when the window sees the - /// top left corner of the page. - pub scroll_offset: TypedPoint2D, - /// The behavior of this layer when a scroll message is received. pub wants_scroll_events: WantsScrollEventsFlag, @@ -59,7 +54,6 @@ impl CompositorData { let new_compositor_data = CompositorData { pipeline: pipeline, id: layer_properties.id, - scroll_offset: TypedPoint2D(0f32, 0f32), wants_scroll_events: wants_scroll_events, scroll_policy: layer_properties.scroll_policy, background_color: layer_properties.background_color, @@ -76,15 +70,12 @@ impl CompositorData { layer.contents_changed(); // 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. The - // scale factor does not matter here since we are scrolling to 0 offset and - // 0 * n == 0. - let size: TypedSize2D = Size2D::from_untyped(&layer.bounds.borrow().size); + // cursor position to make sure the scroll isn't propagated downwards. + let size: TypedSize2D = Size2D::from_untyped(&layer.bounds.borrow().size); events::handle_scroll_event(layer.clone(), TypedPoint2D(0f32, 0f32), TypedPoint2D(-1f32, -1f32), - size, - ScaleFactor(1.0)); + size); } pub fn find_layer_with_pipeline_and_layer_id(layer: Rc>, diff --git a/src/components/compositing/events.rs b/src/components/compositing/events.rs index 8ded49a5477..e4f57a3bb15 100644 --- a/src/components/compositing/events.rs +++ b/src/components/compositing/events.rs @@ -6,12 +6,10 @@ 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::scale_factor::ScaleFactor; -use geom::size::TypedSize2D; +use geom::size::{Size2D, TypedSize2D}; use layers::layers::Layer; use script::dom::event::{ClickEvent, MouseDownEvent, MouseMoveEvent, MouseUpEvent}; use script::script_task::{ScriptChan, SendEventMsg}; @@ -20,6 +18,9 @@ use servo_msg::constellation_msg::PipelineId; use servo_util::geometry::{DevicePixel, PagePx}; use std::rc::Rc; + +use geom::matrix::identity; + trait Clampable { fn clamp(&self, mn: &Self, mx: &Self) -> Self; } @@ -43,10 +44,9 @@ impl Clampable for f32 { /// 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>, - delta: TypedPoint2D, - cursor: TypedPoint2D, - window_size: TypedSize2D, - page_to_device_pixels_scale: ScaleFactor) + delta: TypedPoint2D, + cursor: TypedPoint2D, + window_size: TypedSize2D) -> bool { // If this layer doesn't want scroll events, neither it nor its children can handle scroll // events. @@ -55,80 +55,80 @@ pub fn handle_scroll_event(layer: Rc>, } // Allow children to scroll. - let cursor = cursor - layer.extra_data.borrow().scroll_offset; + let content_offset: TypedPoint2D = + Point2D::from_untyped(&*layer.content_offset.borrow()); + let cursor = cursor - content_offset; for child in layer.children().iter() { - let rect: TypedRect = Rect::from_untyped(&*child.bounds.borrow()); + let rect: TypedRect = Rect::from_untyped(&*child.bounds.borrow()); if rect.contains(&cursor) && handle_scroll_event(child.clone(), delta, cursor - rect.origin, - rect.size, - page_to_device_pixels_scale) { + 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; + clamp_scroll_offset_and_scroll_layer(layer, + content_offset.to_untyped() + delta.to_untyped(), + window_size.to_untyped()) - // bounds checking - let page_size = layer.bounds.borrow().size; - 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)); +pub fn clamp_scroll_offset_and_scroll_layer(layer: Rc>, + mut new_offset: Point2D, + window_size: Size2D) + -> bool { + let layer_size = layer.bounds.borrow().size; + let min_x = (window_size.width - layer_size.width).min(0.0); + new_offset.x = new_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)); + let min_y = (window_size.height - layer_size.height).min(0.0); + new_offset.y = new_offset.y.clamp(&min_y, &0.0); - if old_origin - layer.extra_data.borrow().scroll_offset == TypedPoint2D(0f32, 0f32) { + if *layer.content_offset.borrow() == new_offset { return false } - let offset = layer.extra_data.borrow().scroll_offset.clone(); - scroll(layer.clone(), offset, page_to_device_pixels_scale) + // FIXME: This allows the base layer to record the current content offset without + // updating its transform. This should be replaced with something less strange. + *layer.content_offset.borrow_mut() = new_offset; + scroll_layer_and_all_child_layers(layer.clone(), new_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>, - scroll_offset: TypedPoint2D, - page_to_device_pixels_scale: ScaleFactor) - -> bool { +fn scroll_layer_and_all_child_layers(layer: Rc>, + new_offset: Point2D) + -> 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); - *layer.content_offset.borrow_mut() = (scroll_offset * page_to_device_pixels_scale).to_untyped(); - + *layer.transform.borrow_mut() = identity().translate(new_offset.x, new_offset.y, 0.0); + *layer.content_offset.borrow_mut() = new_offset; result = true } for child in layer.children().iter() { - result = scroll(child.clone(), scroll_offset, page_to_device_pixels_scale) || result; + result |= scroll_layer_and_all_child_layers(child.clone(), new_offset); } - result + 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. pub fn send_mouse_event(layer: Rc>, - event: MouseWindowEvent, cursor: TypedPoint2D) { - let cursor = cursor - layer.extra_data.borrow().scroll_offset; + event: MouseWindowEvent, + cursor: TypedPoint2D, + device_pixels_per_page_px: ScaleFactor) { + let content_offset : TypedPoint2D = + Point2D::from_untyped(&*layer.content_offset.borrow()); + let cursor = cursor - (content_offset / device_pixels_per_page_px); for child in layer.children().iter() { let rect: TypedRect = Rect::from_untyped(&*child.bounds.borrow()); if rect.contains(&cursor) { - send_mouse_event(child.clone(), event, cursor - rect.origin); + send_mouse_event(child.clone(), event, cursor - rect.origin, device_pixels_per_page_px); return; } } @@ -154,8 +154,7 @@ pub fn move(layer: Rc>, pipeline_id: PipelineId, layer_id: LayerId, origin: Point2D, - window_size: TypedSize2D, - page_to_device_pixels_scale: ScaleFactor) + window_size: TypedSize2D) -> bool { // Search children for the right layer to move. if layer.extra_data.borrow().pipeline.id != pipeline_id || @@ -165,8 +164,7 @@ pub fn move(layer: Rc>, pipeline_id, layer_id, origin, - window_size, - page_to_device_pixels_scale) + window_size) }); } @@ -174,25 +172,5 @@ pub fn move(layer: Rc>, 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 = layer.bounds.borrow().size; - 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, page_to_device_pixels_scale) + clamp_scroll_offset_and_scroll_layer(layer, origin * -1.0, window_size.to_untyped()) }