From 301a808660a83e9213ec414acefb8af0d461da64 Mon Sep 17 00:00:00 2001 From: webbeef Date: Mon, 10 Aug 2026 11:53:37 -0700 Subject: [PATCH] paint: fix pan axis lock rebase Signed-off-by: webbeef --- patches/components/paint/touch.rs.patch | 93 ++++++++++++++++--------- 1 file changed, 60 insertions(+), 33 deletions(-) diff --git a/patches/components/paint/touch.rs.patch b/patches/components/paint/touch.rs.patch index 0429727..d1b650b 100644 --- a/patches/components/paint/touch.rs.patch +++ b/patches/components/paint/touch.rs.patch @@ -157,12 +157,12 @@ +fn fling_params() -> FlingParams { + FLING_PARAMS.with(|params| params.get()) +} -+ + +/// Re-read `$SERVO_FLING_PARAMS` if set, so edits apply to the next gesture. +fn reload_fling_params() { + FLING_PARAMS.with(|params| params.set(FlingParams::load())); +} - ++ +/// Fold carried fling momentum into one axis of a new fling, but only when this +/// axis was flicked in the same direction (an opposite or unflicked axis is left +/// alone). @@ -187,7 +187,25 @@ } /// Whether the default move action is allowed or not. -@@ -96,6 +248,14 @@ +@@ -88,6 +240,17 @@ + Vertical, + } + ++impl PanAxis { ++ /// Project `v` onto this axis, zeroing the other component, so a diagonal ++ /// gesture cannot scroll or fling off the axis the pan locked to. ++ fn project(self, v: Vector2D) -> Vector2D { ++ match self { ++ PanAxis::Horizontal => Vector2D::new(v.x, 0.0), ++ PanAxis::Vertical => Vector2D::new(0.0, v.y), ++ } ++ } ++} ++ + /// A cached [`PaintHitTestResult`] to use during a touch sequence. This + /// is kept so that the renderer doesn't have to constantly keep making hit tests + /// while during panning and flinging actions. +@@ -96,6 +259,14 @@ device_pixels_per_page: Scale, } @@ -202,7 +220,7 @@ pub struct TouchSequenceInfo { /// touch sequence state pub(crate) state: TouchSequenceState, -@@ -127,6 +287,18 @@ +@@ -127,6 +298,18 @@ pending_touch_move_actions: Vec, /// Cache for the last touch hit test result. hit_test_result_cache: Option, @@ -221,7 +239,7 @@ } impl TouchSequenceInfo { -@@ -134,6 +306,43 @@ +@@ -134,6 +317,43 @@ self.active_touch_points.len() } @@ -265,7 +283,7 @@ fn pinch_distance_and_center(&self) -> (f32, Point2D) { debug_assert_eq!(self.touch_count(), 2); let p0 = self.active_touch_points[0].point; -@@ -187,11 +396,12 @@ +@@ -187,11 +407,12 @@ pub(crate) enum TouchSequenceState { /// touch point is active but does not start moving Touching, @@ -280,7 +298,7 @@ }, /// A two-finger pinch zoom gesture is active. Pinching, -@@ -231,6 +441,9 @@ +@@ -231,6 +452,9 @@ prevent_move: TouchMoveAllowed::Pending, pending_touch_move_actions: vec![], hit_test_result_cache: None, @@ -290,7 +308,7 @@ }; // We insert a simulated initial touch sequence, which is already finished, // so that we always have one element in the map, which simplifies creating -@@ -243,6 +456,7 @@ +@@ -243,6 +467,7 @@ touch_sequence_map, pending_touch_input_events: Default::default(), observing_frames_for_fling: Default::default(), @@ -298,7 +316,7 @@ } } -@@ -339,6 +553,31 @@ +@@ -339,6 +564,31 @@ self.touch_sequence_map.get(&self.current_sequence_id) } @@ -330,7 +348,7 @@ fn try_get_current_touch_sequence_mut(&mut self) -> Option<&mut TouchSequenceInfo> { self.touch_sequence_map.get_mut(&self.current_sequence_id) } -@@ -357,6 +596,32 @@ +@@ -357,6 +607,32 @@ } pub(crate) fn on_touch_down(&mut self, touch_id: TouchId, point: Point2D) { @@ -363,7 +381,7 @@ // if the current sequence ID does not exist in the map, then it was already handled if !self .touch_sequence_map -@@ -366,6 +631,10 @@ +@@ -366,6 +642,10 @@ { self.current_sequence_id.next(); debug!("Entered new touch sequence: {:?}", self.current_sequence_id); @@ -374,7 +392,7 @@ let active_touch_points = vec![TouchPoint::new(touch_id, point)]; self.touch_sequence_map.insert( self.current_sequence_id, -@@ -377,6 +646,9 @@ +@@ -377,6 +657,9 @@ prevent_move: TouchMoveAllowed::Pending, pending_touch_move_actions: vec![], hit_test_result_cache: None, @@ -384,7 +402,7 @@ }, ); } else { -@@ -395,6 +667,12 @@ +@@ -395,6 +678,12 @@ } // Multiple fingers prevent a click. touch_sequence.prevent_click = true; @@ -397,7 +415,7 @@ } } -@@ -401,33 +679,51 @@ +@@ -401,33 +690,51 @@ pub(crate) fn notify_new_frame_start(&mut self) -> Option { let touch_sequence = self.touch_sequence_map.get_mut(&self.current_sequence_id)?; @@ -466,7 +484,7 @@ } pub(crate) fn stop_fling_if_needed(&mut self) { -@@ -438,11 +734,14 @@ +@@ -438,11 +745,14 @@ ); return; }; @@ -483,7 +501,7 @@ touch_sequence.state = Finished; // If we were flinging previously, there could still be a touch_up event result // coming in after we stopped flinging -@@ -459,7 +758,12 @@ +@@ -459,7 +769,12 @@ // As `TouchHandler` is per `WebViewRenderer` which is per `WebView` we might get a Touch Sequence Move that // started with a down on a different webview. As the touch_sequence id is only changed on touch_down this // move event gets a touch id which is already cleaned up. @@ -497,7 +515,7 @@ let idx = match touch_sequence .active_touch_points .iter_mut() -@@ -467,82 +771,77 @@ +@@ -467,66 +782,68 @@ { Some(i) => i, None => { @@ -544,7 +562,7 @@ + // Record the finger position for the time-based release-velocity + // estimate consumed when this pan ends (see on_touch_up). + touch_sequence.record_pan_sample(Instant::now(), point); -+ let is_panning = matches!(touch_sequence.state, Panning); ++ let is_panning = matches!(touch_sequence.state, Panning { .. }); + if is_panning || delta.x.abs() > pan_min || delta.y.abs() > pan_min { + if !is_panning { + debug!( @@ -556,7 +574,8 @@ + delta = ?delta, + ) + .entered(); -+ let axis = if delta.x > delta.y { ++ // Dominant axis by magnitude. ++ let axis = if delta.y.abs() > delta.x.abs() { + PanAxis::Vertical + } else { + PanAxis::Horizontal @@ -568,8 +587,8 @@ // update the touch point every time when panning. touch_sequence.active_touch_points[idx].point = point; - // Scroll offsets are opposite to the direction of finger motion. - Some(ScrollZoomEvent::Scroll(ScrollEvent { +- // Scroll offsets are opposite to the direction of finger motion. +- Some(ScrollZoomEvent::Scroll(ScrollEvent { - scroll: Scroll::Delta((-pan_delta).into()), - point, - })) @@ -596,18 +615,23 @@ - touch_sequence.state = Panning { - axis, - velocity: pan_delta, -- }; ++ // Only scroll along the axis that was dominant when panning ++ // started, so the gesture cannot switch between horizontal and ++ // vertical mid-sequence. ++ let pan_delta = match touch_sequence.state { ++ Panning { axis } => axis.project(delta), ++ // Unreachable: set to Panning just above when it wasn't ++ // already. ++ _ => PanAxis::Vertical.project(delta), + }; - // No clicks should be issued after we transitioned to move. - touch_sequence.prevent_click = true; - // update the touch point - touch_sequence.active_touch_points[idx].point = point; -- -- // Scroll offsets are opposite to the direction of finger motion. -- Some(ScrollZoomEvent::Scroll(ScrollEvent { -- scroll: Scroll::Delta((-pan_delta).into()), -+ scroll: Scroll::Delta((-delta).into()), - point, - })) + + // Scroll offsets are opposite to the direction of finger motion. + Some(ScrollZoomEvent::Scroll(ScrollEvent { +@@ -536,13 +853,17 @@ } else { // We don't update the touchpoint, so multiple small moves can // accumulate and merge into a larger move. @@ -627,7 +651,7 @@ { touch_sequence.state = Pinching; let (d0, _) = touch_sequence.pinch_distance_and_center(); -@@ -576,45 +875,105 @@ +@@ -576,45 +897,108 @@ } pub(crate) fn on_touch_up(&mut self, touch_id: TouchId, point: Point2D) { @@ -679,14 +703,17 @@ }, - Panning { velocity, .. } => { - if velocity.length().abs() >= FLING_MIN_SCREEN_PX { -+ Panning => { ++ Panning { axis } => { + // Estimate the release velocity (device px / s) from the recent + // sample window, dropping stale samples so a pause before lifting + // yields a near-zero velocity and no fling. + let params = fling_params(); + let now = Instant::now(); + touch_sequence.trim_pan_samples(now); -+ let release_velocity = touch_sequence.windowed_velocity(); ++ // The samples are raw finger positions, so a diagonal swipe would ++ // fling off-axis even though the pan itself was locked. Constrain ++ // the launch velocity to the same axis the pan was locked to. ++ let release_velocity = axis.project(touch_sequence.windowed_velocity()); + let release_speed = release_velocity.length(); + if release_speed >= params.min_speed_px_s { + // Snap boost, then fold in any carried momentum from a fling this @@ -751,7 +778,7 @@ match touch_sequence.prevent_move { TouchMoveAllowed::Allowed => { touch_sequence.state = Flinging { velocity, point } -@@ -627,6 +986,10 @@ +@@ -627,6 +1011,10 @@ TouchMoveAllowed::Prevented => touch_sequence.state = Finished, } } else { -- 2.51.2