mirror of
https://github.com/priyanshujain/sanderling.git
synced 2026-10-02 19:17:10 +00:00
fix(runner): translate gesture to safe area so near-top scrolls keep direction
Clamping the swipe origin to the top margin while leaving the destination on the full screen used two reference frames: a scrollable container pinned in the top strip had its origin pushed past the destination, reversing the gesture. Translate the whole from->to segment down by the same delta so the origin clears the shade strip without flipping direction. Adds a scroll-near-top test that fails under the old origin-only clamp.
This commit is contained in:
1 parent
84c0041d84
commit
72a0c1c26e
2 files changed
+65
-9
No files matched your search
@@ -780,9 +780,20 @@ func clampGestureToSafeArea(fromX, fromY, toX, toY int, screen hierarchy.Bounds)
|
|||||||
if width <= 0 || height <= 0 {
|
if width <= 0 || height <= 0 {
|
||||||
return fromX, fromY, toX, toY
|
return fromX, fromY, toX, toY
|
||||||
}
|
}
|
||||||
// The top margin clears the status bar; calibrated as a fraction of height
|
// Only the touch-down point opens the shade, and only when it lands in the
|
||||||
// so swipes from below ~8% of height no longer open the shade.
|
// top status strip; the margin clears it (calibrated as a fraction of
|
||||||
|
// height so origins below ~8% of height no longer open the shade). When the
|
||||||
|
// origin is too high, translate the whole segment down by the same delta
|
||||||
|
// rather than clamping the origin alone, which could push it past the
|
||||||
|
// destination and reverse the gesture (a near-top scroll would then flip
|
||||||
|
// direction). Side and bottom edge gestures are disabled by
|
||||||
|
// ForceThreeButtonNav, so only the top needs a margin; endpoints are
|
||||||
|
// otherwise just kept on screen.
|
||||||
marginY := height / 12
|
marginY := height / 12
|
||||||
|
if shortfall := (screen.Top + marginY) - fromY; shortfall > 0 {
|
||||||
|
fromY += shortfall
|
||||||
|
toY += shortfall
|
||||||
|
}
|
||||||
clamp := func(value, low, high int) int {
|
clamp := func(value, low, high int) int {
|
||||||
if value < low {
|
if value < low {
|
||||||
return low
|
return low
|
||||||
@@ -793,7 +804,7 @@ func clampGestureToSafeArea(fromX, fromY, toX, toY int, screen hierarchy.Bounds)
|
|||||||
return value
|
return value
|
||||||
}
|
}
|
||||||
fromX = clamp(fromX, screen.Left, screen.Right)
|
fromX = clamp(fromX, screen.Left, screen.Right)
|
||||||
fromY = clamp(fromY, screen.Top+marginY, screen.Bottom)
|
fromY = clamp(fromY, screen.Top, screen.Bottom)
|
||||||
toX = clamp(toX, screen.Left, screen.Right)
|
toX = clamp(toX, screen.Left, screen.Right)
|
||||||
toY = clamp(toY, screen.Top, screen.Bottom)
|
toY = clamp(toY, screen.Top, screen.Bottom)
|
||||||
return fromX, fromY, toX, toY
|
return fromX, fromY, toX, toY
|
||||||
|
|||||||
@@ -909,6 +909,48 @@ func TestApplyAction_ScrollDirectionUsesInversion(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestApplyAction_ScrollNearTopKeepsDirectionAfterClamp pins the clamp fix
|
||||||
|
// against a scrollable container pinned in the top status strip: the safe-area
|
||||||
|
// clamp must translate the whole gesture down, not push the origin past the
|
||||||
|
// destination. With the old origin-only clamp the downward finger (scroll up)
|
||||||
|
// flipped to an upward finger, scrolling the opposite way.
|
||||||
|
func TestApplyAction_ScrollNearTopKeepsDirectionAfterClamp(t *testing.T) {
|
||||||
|
driverMock := mockdriver.New()
|
||||||
|
// A full-screen root establishes the 1080x2400 screen (marginY=200); the
|
||||||
|
// scrollable list sits entirely inside the top margin (y 20..180).
|
||||||
|
treeJSON := `{"attributes":{"bounds":"[0,0,1080,2400]"},"children":[
|
||||||
|
{"attributes":{"resource-id":"com.fixture:id/toplist","scrollable":"true","bounds":"[0,20,1080,180]"},"children":[],"enabled":true}
|
||||||
|
]}`
|
||||||
|
tree, err := hierarchy.Parse(treeJSON)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("parse tree: %v", err)
|
||||||
|
}
|
||||||
|
action := verifier.Action{Kind: verifier.ActionKindScroll, Direction: "up", On: "id:toplist"}
|
||||||
|
|
||||||
|
if err := applyAction(context.Background(), driverMock, action, tree); err != nil {
|
||||||
|
t.Fatalf("apply action: %v", err)
|
||||||
|
}
|
||||||
|
var swipe *mockdriver.Action
|
||||||
|
for i := range driverMock.Actions() {
|
||||||
|
if driverMock.Actions()[i].Kind == mockdriver.ActionSwipe {
|
||||||
|
a := driverMock.Actions()[i]
|
||||||
|
swipe = &a
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if swipe == nil {
|
||||||
|
t.Fatalf("expected a Swipe, got %v", driverMock.Actions())
|
||||||
|
}
|
||||||
|
// The origin must clear the top margin (the clamp fired)...
|
||||||
|
if swipe.FromY != 200 {
|
||||||
|
t.Errorf("origin not pushed below the shade strip, got fromY=%d want 200", swipe.FromY)
|
||||||
|
}
|
||||||
|
// ...and "up" must still drag the finger DOWN (toY > fromY). The old clamp
|
||||||
|
// reversed this.
|
||||||
|
if swipe.ToY <= swipe.FromY {
|
||||||
|
t.Errorf("scroll up reversed by the clamp: from=%d to=%d (want toY > fromY)", swipe.FromY, swipe.ToY)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestApplyAction_ScrollScreenFallback(t *testing.T) {
|
func TestApplyAction_ScrollScreenFallback(t *testing.T) {
|
||||||
driverMock := mockdriver.New()
|
driverMock := mockdriver.New()
|
||||||
treeJSON := `{"attributes":{"bounds":"[0,0,400,800]"},"children":[],"enabled":true}`
|
treeJSON := `{"attributes":{"bounds":"[0,0,400,800]"},"children":[],"enabled":true}`
|
||||||
@@ -1808,18 +1850,21 @@ func TestAwaitForeground_RelaunchesThenWaitsForWindow(t *testing.T) {
|
|||||||
// TestClampGestureToSafeArea_KeepsOriginBelowShadeStrip locks the swipe fix: a
|
// TestClampGestureToSafeArea_KeepsOriginBelowShadeStrip locks the swipe fix: a
|
||||||
// gesture origin in the top status-bar strip pulls down the notification shade
|
// gesture origin in the top status-bar strip pulls down the notification shade
|
||||||
// and drags the fuzzer out of the app. The origin must be pushed below the top
|
// and drags the fuzzer out of the app. The origin must be pushed below the top
|
||||||
// margin. The side and bottom strips are NOT clamped: runs force 3-button
|
// margin, and the WHOLE segment translates with it so the gesture direction is
|
||||||
// navigation, which disables the back and home gestures, so (verified on device)
|
// preserved (clamping the origin alone could push it past the destination and
|
||||||
// those origins no longer drift.
|
// reverse a near-top scroll). The side and bottom strips are NOT clamped: runs
|
||||||
|
// force 3-button navigation, which disables the back and home gestures, so
|
||||||
|
// (verified on device) those origins no longer drift.
|
||||||
func TestClampGestureToSafeArea_KeepsOriginBelowShadeStrip(t *testing.T) {
|
func TestClampGestureToSafeArea_KeepsOriginBelowShadeStrip(t *testing.T) {
|
||||||
screen := hierarchy.Bounds{Left: 0, Top: 0, Right: 1080, Bottom: 2400}
|
screen := hierarchy.Bounds{Left: 0, Top: 0, Right: 1080, Bottom: 2400}
|
||||||
// marginY = 2400/12 = 200.
|
// marginY = 2400/12 = 200.
|
||||||
|
|
||||||
// The real shade-opening swipe captured on device: origin y=62 is pushed to
|
// The real shade-opening swipe captured on device: origin y=62 is pushed to
|
||||||
// the top margin (200); the in-range destination is untouched.
|
// the top margin (200), and the destination shifts down by the same 138 so
|
||||||
|
// the downward gesture stays downward (447 -> 585), not reversed.
|
||||||
fromX, fromY, toX, toY := clampGestureToSafeArea(802, 62, 802, 447, screen)
|
fromX, fromY, toX, toY := clampGestureToSafeArea(802, 62, 802, 447, screen)
|
||||||
if fromX != 802 || fromY != 200 || toX != 802 || toY != 447 {
|
if fromX != 802 || fromY != 200 || toX != 802 || toY != 585 {
|
||||||
t.Errorf("origin not pushed below the shade strip: from=(%d,%d) to=(%d,%d), want from=(802,200) to=(802,447)", fromX, fromY, toX, toY)
|
t.Errorf("segment not translated below the shade strip: from=(%d,%d) to=(%d,%d), want from=(802,200) to=(802,585)", fromX, fromY, toX, toY)
|
||||||
}
|
}
|
||||||
|
|
||||||
// A side origin (left/right edge) passes through untouched: 3-button nav
|
// A side origin (left/right edge) passes through untouched: 3-button nav
|
||||||
|
|||||||
Reference in new issue
Block a user