Skip to content

text_view: Restrict markdown table wheel scrolling - #2468

Merged
huacnlee merged 1 commit into
mainfrom
fix-markdown-table-scroll
Jun 15, 2026
Merged

huacnlee merged 1 commit into
mainfrom
fix-markdown-table-scroll

Conversation

@huacnlee

Copy link
Copy Markdown
Member

Summary

  • Add a horizontal scroll area helper that ignores vertical wheel input while preserving horizontal scrolling.
  • Use it for markdown table scroll mode so TextView vertical scrolling does not move tables sideways.
  • Add tests covering vertical wheel ignored and horizontal wheel accepted.

Test Plan

  • cargo test -p gpui-component --lib

Co-authored-by: Codex <codex@openai.com>
@huacnlee huacnlee changed the title fix: restrict markdown table wheel scrolling text_view: Restrict markdown table wheel scrolling Jun 15, 2026
@huacnlee
huacnlee merged commit be390cd into main Jun 15, 2026
3 checks passed
@huacnlee
huacnlee deleted the fix-markdown-table-scroll branch June 15, 2026 07:15
jonx pushed a commit to jonx/gpui-component-aros that referenced this pull request Jul 17, 2026
## Summary
- Add a horizontal scroll area helper that ignores vertical wheel input
while preserving horizontal scrolling.
- Use it for markdown table scroll mode so TextView vertical scrolling
does not move tables sideways.
- Add tests covering vertical wheel ignored and horizontal wheel
accepted.

## Test Plan
- cargo test -p gpui-component --lib

Co-authored-by: Codex <codex@openai.com>
madcodelife added a commit that referenced this pull request Jul 28, 2026
…dy scroll (#2608)

## Description

With a trackpad, horizontal swipes over a horizontally scrollable
markdown table (`TextViewStyle.table` with `overflow_x: scroll`)
scrolled the whole markdown body vertically instead, making the table
nearly impossible to scroll. Two causes:

1. With `scrollable(true)` the body is a `gpui::list`, which registers
its wheel listener after its items paint, so in the bubble phase
(reverse registration order) it consumed `delta.y` before
`ScrollableMask` could stop the propagation. The mask now consumes
axis-dominant wheel events in the capture phase; vertical-dominant
events keep bubbling to the body (preserves #2468).

2. The mask was a child of the scrolled element and slid away from the
viewport as the table scrolled, leaving the uncovered part leaking to
the parent scroller. It is now a sibling of the scrolled element, same
as `Table`.

The mask also checks `HitboxId::should_handle_scroll` now, staying inert
when occluded (dialogs, context menus). Since capture runs in
registration order, nested masks resolve innermost-first.

Added 4 tests reproducing the `gpui::list` structure.

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
madcodelife added a commit that referenced this pull request Jul 29, 2026
…2611)

https://github.com/user-attachments/assets/71d453a9-e75e-469d-8c14-0d608a060a8d

## Description

When a `DataTable` is nested inside a scrollable ancestor, wheel
scrolling over the table scrolled both the table and the ancestor at
once: the table body relies on gpui's `uniform_list` wheel listener,
which updates the offset but never stops propagation. The horizontal
axis was already protected by a `ScrollableMask` (#2608); the vertical
axis had no equivalent.

- Add a vertical `ScrollableMask` sibling to the table (skipped when the
table is empty, where the handle state is stale).
- `ScrollableMask` edge semantics now differ per axis, matching platform
scrollers (macOS responder-chain forwarding, CSS `overscroll-behavior:
auto`): a vertical mask consumes axis-dominant wheel events only while
the content can still move and hands them back to the ancestor at the
edge or when there is no overflow; a horizontal mask keeps consuming
even at the edge, since a bubbled horizontal delta would be axis-mapped
onto a vertical-only ancestor by gpui's wheel listener (#2468).
- The vertical comparison uses clamped offsets so transient unclamped
overscroll from gpui's own listener doesn't swallow events during a
fling at the edge.
- Behavior note: a vertical wheel over the table header now scrolls the
table body instead of the ancestor, matching browser behavior.
- Add a minimal repro example: `cargo run -p table_in_scrollable`.

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
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>
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