Skip to content

scroll: Lock trackpad wheel gestures to their starting axis - #2648

Merged
madcodelife merged 1 commit into
mainfrom
scroll-gesture-axis-lock
Aug 4, 2026
Merged

madcodelife merged 1 commit into
mainfrom
scroll-gesture-axis-lock

Conversation

@madcodelife

@madcodelife madcodelife commented Aug 4, 2026

Copy link
Copy Markdown
Member

Wireup zed-industries/zed#61797.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@madcodelife
madcodelife merged commit cd86cf0 into main Aug 4, 2026
6 checks passed
@madcodelife
madcodelife deleted the scroll-gesture-axis-lock branch August 4, 2026 07:00
madcodelife added a commit that referenced this pull request Aug 4, 2026
madcodelife added a commit that referenced this pull request Aug 31, 2026
## Description

A trackpad swipe over a horizontally scrollable Markdown table scrolled
the
document instead, and scrolling the document slid the table sideways.

#2881 moved the table renderer into `gpui-base` and replaced
`horizontal_scroll_area()` — an `overflow_hidden` viewport plus a
sibling
`ScrollableMask` — with a plain `div().overflow_x_scroll()`, dropping
#2468,
#2608 and #2648 in one step. The only trace was an `#[allow(dead_code)]`
added
to the now-unreachable helper, so CI stayed green: the mask's tests
cover the
mask, not whether the table uses it.

`ScrollableMask` moves to `gpui-base` with its 16 tests (its
dependencies —
`ScrollbarHandle`, `AxisExt`, `StyledExt`, `OngoingScrollExt` — already
lived
there), and the table calls `horizontal_scroll_area` again. The viewport
keeps
the border and background #2881 put on it, folded into the refinement.

`gpui-component`'s public API is unchanged:
`gpui_component::scroll::ScrollableMask`
still resolves, now re-exported from Base.

## Why the mask, and not `restrict_scroll_to_axis`

`.lock_scroll_axis()` alone fixes only half of it. GPUI's overflow
listener runs
in the bubble phase and never stops propagation, and `gpui::list` — the
body
under `TextView::scrollable(true)` — registers its listener after its
items
paint, so bubble's reverse order runs the list first. A diagonal swipe
therefore
loses its vertical component to the document no matter what the table
does.
Winning requires consuming the event in the capture phase, which is what
the
mask is for.

## Test plan

- `cargo test -p gpui-base --lib scrollable_mask` — 16 tests, including
`horizontal_scroll_area_in_list_keeps_horizontal_dominant_wheel`, which
is the
  regression this PR fixes
- `cargo test --workspace --exclude gpui-shell`
- `cargo test -p gpui-component --doc`
- `cargo check -p gpui-component --no-default-features`
- `cargo clippy --workspace --exclude gpui-shell --all-targets -- --deny
warnings`
- `cargo fmt --check`
- By hand with a trackpad: `cargo run -p markdown_table`, and at several
frame
  widths via `WIN_W=<px>`

---

Parts of this PR were written with Claude Code.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
huacnlee added a commit that referenced this pull request Sep 14, 2026
- Lock wheel gestures to their starting axis so a diagonal wobble cannot
  fall out of the stretch mid-gesture, matching ScrollableMask (#2648)
- Keep a displaced edge's extent across begin() so re-grabbing a
  returning edge after the viewport shrinks stays continuous
- Share the max scroll extent between both dispatch phases
- Expose ElasticScrollMotion (tracking, response) instead of hard-coded
  spring constants, per the motion ownership rules

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JYFLtQZbywTsaT9cyuv1Es
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant