emilk / emilk/egui

ScrollArea: pointer.delta() in the first frame of a new drag equals `new_DOWN_pos - old_UP_pos`, causing scroll jump on touch after fling

Open
#8,285 3 comments 0 reactions 0 assignees View on GitHub
bug
Dominant language
Rust
Stars
30.6k
Forks
2.1k
Avg merge
1d 9h
Merged PRs (30d)
72

Description

### Platform
- **Device:** Xiaomi MIUI, Android 14
- **egui version:** 0.31 (via `[patch.crates-io]` in our project)
- **Architecture:** egui through NativeActivity (android-activity), without eframe, only `egui_glow`
- **Input:** touch only (MotionEvent from Android NDK), no mouse

### Description
Using `ScrollArea` with `drag_to_scroll(true)` on a touch screen:
1. User swipes up — fling (kinetic scrolling) starts
2. Fling stops
3. User touches the screen at a lower position
4. **Result:** content instantly jumps 100-200pt downward

### Root cause
In `PointerState::begin_pass()` the delta is computed as the difference between `latest_pos` after event processing and `old_pos` (which equals `latest_pos` before processing):

```rust
self.delta = if let (Some(old_pos), Some(new_pos)) = (old_pos, self.latest_pos) {
new_pos - old_pos // old_pos from previous UP, new_pos from new DOWN
} else {
Vec2::ZERO
};
```

On the first frame of a new drag:
- `old_pos = Some(pos_from_UP)` — last position from the previous gesture
- `self.latest_pos = Some(pos_of_new_DOWN)` — set when processing `PointerButton { pressed: true }`
- `delta = pos_of_new_DOWN - pos_from_UP` — can be 100-200pt

ScrollArea applies this delta via `state.offset -= pointer.delta()`, causing an instant jump.

### Fix (2 changes in egui + batching in backend)

#### 1. `input_state/mod.rs` — reset `old_pos` on `PointerButton { pressed: true }`
```diff
if pressed {
+ old_pos = Some(pos); // reset old_pos so pointer.delta() is zero
self.pos_history.clear();
}
```

#### 2. `containers/scroll_area.rs` — reset `state.vel` and `smooth_scroll_delta` at `drag_started()`

#### 3. Backend batching — only `PointerButton { pressed: true }` enters the frame on DOWN; all Move events are deferred

### References to our fork
- **Commit with the patch:** https://github.com/toffeantyri/egui-android-framework/tree/cb4debc
- **PATCH_NOTES.md (Russian):** https://github.com/toffeantyri/egui-android-framework/blob/main/patches/egui/PATCH_NOTES.md
- **Reproduction screen:** "Modifier Value" in our `examples/showcase`

### Files to patch in egui
- `crates/egui/src/input_state/mod.rs` — `PointerState::begin_pass()`, lines ~951-955
- `crates/egui/src/containers/scroll_area.rs` — `drag_started()` block, lines ~642-646

Contributor guide

Open the contributing guide

Research direction

Start in crates/egui/src/input_state/mod.rs at PointerState::begin_pass() and inspect the PointerButton press handling, then review the drag_started() block in crates/egui/src/containers/scroll_area.rs. Reproduce the touch-after-fling case using the reported Modifier Value showcase and check the Android backend’s MotionEvent batching. Done means a new touch does not jump and starts without stale scroll velocity or smooth-scroll delta.

Written by the indexing model from the issue text.

Assessment

Tech stack
rust
Domain
frontend, mobile-dev
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Quiet
Clarity
Clearly specified
Newbie friendliness
68/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.