From 63b970879b4e7ed930e217f5eaa6d8d81b32ff95 Mon Sep 17 00:00:00 2001 From: Gregory Date: Sun, 6 Nov 2016 22:36:18 +0800 Subject: [PATCH 1/3] when a non_root layer overscrolls, and a new gesture starts, scroll the parent --- webrender/src/frame.rs | 56 +++++++++++++++++++++++++++++++++--------- webrender/src/layer.rs | 3 ++- 2 files changed, 47 insertions(+), 12 deletions(-) diff --git a/webrender/src/frame.rs b/webrender/src/frame.rs index d605f87a29..5a0f545560 100644 --- a/webrender/src/frame.rs +++ b/webrender/src/frame.rs @@ -331,7 +331,7 @@ impl Frame { /// Returns true if any layers actually changed position or false otherwise. pub fn scroll(&mut self, - mut delta: Point2D, + delta: Point2D, cursor: Point2D, phase: ScrollEventPhase) -> bool { @@ -345,13 +345,49 @@ impl Frame { None => return false, }; - let scroll_root_id = match scroll_layer_id.info { - ScrollLayerInfo::Scrollable(_, scroll_root_id) => scroll_root_id, - ScrollLayerInfo::Fixed => unreachable!("Tried to scroll a fixed position layer."), + let non_root_overscroll = if scroll_layer_id != root_scroll_layer_id { + // true if the current layer is overscrolling, + // and it is not the root scroll layer. + let child_layer = self.layers.get(&scroll_layer_id).unwrap(); + let overscroll_amount = child_layer.overscroll_amount(); + overscroll_amount.width != 0.0 || overscroll_amount.height != 0.0 + } else { + false + }; + + let switch_layer = match phase { + ScrollEventPhase::Start => { + // if this is a new gesture, we do not switch layer, + // however we do save the state of non_root_overscroll, + // for use in the subsequent Move phase. + let mut current_layer = self.layers.get_mut(&scroll_layer_id).unwrap(); + current_layer.scrolling.should_handoff_scroll = non_root_overscroll; + false + }, + ScrollEventPhase::Move(_) => { + // Switch layer if movement originated in a new gesture, + // from a non root layer in overscroll. + let current_layer = self.layers.get_mut(&scroll_layer_id).unwrap(); + current_layer.scrolling.should_handoff_scroll + }, + ScrollEventPhase::End => { + // clean-up when gesture ends. + let mut current_layer = self.layers.get_mut(&scroll_layer_id).unwrap(); + current_layer.scrolling.should_handoff_scroll = false; + false + }, + }; + + let scroll_root_id = match (switch_layer, scroll_layer_id.info, root_scroll_layer_id.info) { + (true, _, ScrollLayerInfo::Scrollable(_, scroll_root_id)) | + (true, ScrollLayerInfo::Scrollable(_, scroll_root_id), ScrollLayerInfo::Fixed) | + (false, ScrollLayerInfo::Scrollable(_, scroll_root_id), _) => scroll_root_id, + (_, ScrollLayerInfo::Fixed, _) => unreachable!("Tried to scroll a fixed position layer."), }; let mut scrolled_a_layer = false; for (layer_id, layer) in self.layers.iter_mut() { + let mut layer_delta = delta; if layer_id.pipeline_id != scroll_layer_id.pipeline_id { continue; } @@ -371,10 +407,10 @@ impl Frame { overscroll_amount.height != 0.0); if overscrolling { if overscroll_amount.width != 0.0 { - delta.x /= overscroll_amount.width.abs() + layer_delta.x /= overscroll_amount.width.abs() } if overscroll_amount.height != 0.0 { - delta.y /= overscroll_amount.height.abs() + layer_delta.y /= overscroll_amount.height.abs() } } @@ -385,7 +421,7 @@ impl Frame { let original_layer_scroll_offset = layer.scrolling.offset; if layer.content_size.width > layer.local_viewport_rect.size.width { - layer.scrolling.offset.x = layer.scrolling.offset.x + delta.x; + layer.scrolling.offset.x = layer.scrolling.offset.x + layer_delta.x; if is_unscrollable || !CAN_OVERSCROLL { layer.scrolling.offset.x = layer.scrolling.offset.x.min(0.0); layer.scrolling.offset.x = @@ -395,7 +431,7 @@ impl Frame { } if layer.content_size.height > layer.local_viewport_rect.size.height { - layer.scrolling.offset.y = layer.scrolling.offset.y + delta.y; + layer.scrolling.offset.y = layer.scrolling.offset.y + layer_delta.y; if is_unscrollable || !CAN_OVERSCROLL { layer.scrolling.offset.y = layer.scrolling.offset.y.min(0.0); layer.scrolling.offset.y = @@ -407,7 +443,7 @@ impl Frame { if phase == ScrollEventPhase::Start || phase == ScrollEventPhase::Move(true) { layer.scrolling.started_bouncing_back = false } else if overscrolling && - ((delta.x < 1.0 && delta.y < 1.0) || phase == ScrollEventPhase::End) { + ((layer_delta.x < 1.0 && layer_delta.y < 1.0) || phase == ScrollEventPhase::End) { layer.scrolling.started_bouncing_back = true; layer.scrolling.bouncing_back = true } @@ -418,12 +454,10 @@ impl Frame { if CAN_OVERSCROLL { layer.stretch_overscroll_spring(); } - scrolled_a_layer = scrolled_a_layer || layer.scrolling.offset != original_layer_scroll_offset || layer.scrolling.started_bouncing_back; } - scrolled_a_layer } diff --git a/webrender/src/layer.rs b/webrender/src/layer.rs index f16509620e..c88adc9aba 100644 --- a/webrender/src/layer.rs +++ b/webrender/src/layer.rs @@ -128,6 +128,7 @@ pub struct ScrollingState { pub spring: Spring, pub started_bouncing_back: bool, pub bouncing_back: bool, + pub should_handoff_scroll: bool } impl ScrollingState { @@ -137,7 +138,7 @@ impl ScrollingState { spring: Spring::at(LayerPoint::zero(), STIFFNESS, DAMPING), started_bouncing_back: false, bouncing_back: false, + should_handoff_scroll: false } } } - From a68baf318ea80de550edf4cea70dda31adb209a2 Mon Sep 17 00:00:00 2001 From: Gregory Date: Sun, 6 Nov 2016 23:38:35 +0800 Subject: [PATCH 2/3] keeping the currently scrolling layer --- webrender/src/frame.rs | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/webrender/src/frame.rs b/webrender/src/frame.rs index 5a0f545560..9c5f0c413c 100644 --- a/webrender/src/frame.rs +++ b/webrender/src/frame.rs @@ -53,6 +53,7 @@ pub struct Frame { BuildHasherDefault>, pub root_scroll_layer_id: Option, pending_scroll_offsets: HashMap<(PipelineId, ServoScrollRootId), LayerPoint>, + current_scroll_layer_id: Option, id: FrameId, debug: bool, frame_builder_config: FrameBuilderConfig, @@ -212,6 +213,7 @@ impl Frame { layers: HashMap::with_hasher(Default::default()), root_scroll_layer_id: None, pending_scroll_offsets: HashMap::new(), + current_scroll_layer_id: None, id: FrameId(0), debug: debug, frame_builder: None, @@ -340,9 +342,15 @@ impl Frame { None => return false, }; - let scroll_layer_id = match self.get_scroll_layer(&cursor, root_scroll_layer_id) { - Some(scroll_layer_id) => scroll_layer_id, - None => return false, + let scroll_layer_id = match (phase, self.get_scroll_layer(&cursor, root_scroll_layer_id), + self.current_scroll_layer_id) { + (ScrollEventPhase::Start, Some(scroll_layer_id), _) => { + self.current_scroll_layer_id = Some(scroll_layer_id); + scroll_layer_id + }, + (ScrollEventPhase::Start, None, _) => return false, + (_, _, Some(scroll_layer_id)) => scroll_layer_id, + (_, _, None) => return false, }; let non_root_overscroll = if scroll_layer_id != root_scroll_layer_id { From 26a3b056d0a8d6f2df3be0c19bf539329d4583d5 Mon Sep 17 00:00:00 2001 From: Gregory Date: Tue, 22 Nov 2016 22:50:01 +0800 Subject: [PATCH 3/3] fixes to switch layer - to be squashed later --- webrender/src/frame.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/webrender/src/frame.rs b/webrender/src/frame.rs index 9c5f0c413c..109aa098f2 100644 --- a/webrender/src/frame.rs +++ b/webrender/src/frame.rs @@ -372,14 +372,14 @@ impl Frame { current_layer.scrolling.should_handoff_scroll = non_root_overscroll; false }, - ScrollEventPhase::Move(_) => { + ScrollEventPhase::Move(true) => { // Switch layer if movement originated in a new gesture, // from a non root layer in overscroll. let current_layer = self.layers.get_mut(&scroll_layer_id).unwrap(); - current_layer.scrolling.should_handoff_scroll + current_layer.scrolling.should_handoff_scroll && non_root_overscroll }, - ScrollEventPhase::End => { - // clean-up when gesture ends. + ScrollEventPhase::End | ScrollEventPhase::Move(false) => { + // clean-up when gesture ends, or we're not moving. let mut current_layer = self.layers.get_mut(&scroll_layer_id).unwrap(); current_layer.scrolling.should_handoff_scroll = false; false