Skip to content

EditableText improved scrolling#24268

Open
ickshonpe wants to merge 24 commits into
bevyengine:mainfrom
ickshonpe:text-input-scroll-with-inset
Open

EditableText improved scrolling#24268
ickshonpe wants to merge 24 commits into
bevyengine:mainfrom
ickshonpe:text-input-scroll-with-inset

Conversation

@ickshonpe
Copy link
Copy Markdown
Contributor

@ickshonpe ickshonpe commented May 12, 2026

Objective

Fix problems:

  • Add adjustable insets to the scrolling view to support look ahead when scrolling.
  • The drag observer shouldn't select allow selection outside the view of the input text.
  • Pointer edits shouldn't automatically trigger scrolling, as it moves the view relative to the pointer.
  • Dragging should scroll at a consistant and configurable speed.
  • Dragging inside the text input's bounds shouldn't scroll the input.
  • Scrolling should clamp the view to the bounds of the line on the view boundary in the scrolling direction. There should never be a situation where half the cursor is clipped out of view.

Solution

  • New component EditableTextNeedsScroll, newtypes bool. If set to true the editor view scrolls next update.
  • Don't scroll on pointer edits.
  • Scroll if dragging outside the text.
  • New scrolling view calculations in bevy_ui::widget::text_input_layout::scroll_axis_with_inset.
  • New scroll_inset field on EditableText controls the size of the view inset (is normalized).
  • New resource TextInputScrollSpeed, sets scrolling speed relative to the text's line height.
  • New system drag_scroll_text_inputs updates drag scrolling for current input focused text input.
  • on_pointer_drag clamps the pointer position to within the input's bounds. Drag selecting outside of the input's bounds no longer selects text out of view.

Fixes #24293

pasta

Testing

cargo run --example multiline_text_input

ickshonpe added 10 commits May 12, 2026 13:03
`scroll_editable_text` scrolling calculations changed to support the inset.
…oint` when dragging. So each time the mouse moves during a drag, the current selection is cleared and a new selection is created. As long as the local drag start position is constant this is seamless, but if you scroll the text input view, the start of the drag is now at a different position relative to the text layout, and the start of the selection range changes.

Solution: Don't queue the `MoveToPoint` edit.
@ickshonpe ickshonpe added C-Bug An unexpected or incorrect behavior C-Feature A new feature, making something new possible A-UI Graphical user interfaces, styles, layouts, and widgets A-Text Rendering and layout for characters S-Needs-Review Needs reviewer attention (from anyone!) to move forward labels May 12, 2026
@github-project-automation github-project-automation Bot moved this to Needs SME Triage in UI May 12, 2026
@ickshonpe ickshonpe added the C-Refinement Improves output quality, without fixing a clear bug or adding new functionality. label May 13, 2026
@ickshonpe ickshonpe changed the title EditableText scroll with inset EditableText improved scrolling May 13, 2026
@ickshonpe ickshonpe added the D-Complex Quite challenging from either a design or technical perspective. Ask for help! label May 14, 2026
Copy link
Copy Markdown
Contributor

@eswartz eswartz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(btw, the sample name in the instructions is misspelled -- it's one letter from "multiline_text_input", which I actually tested before seeing the bug refers to "multiple_text_inputs"!)

It looks nice and feels pretty good. Pasting a long blob leaves the cursor where expected, at the end of the blob.

In the "multiline_text_input" case, which I mostly tested, it seems like this might introduce a regression. When dragging a selection up or down in the window, at some instances the selection outline becomes one pixel tall, either at the top or the bottom depending on scroll direction. I'd expect at least a full text line to be visible.

The keeping of at least a single selected row when scrolling seems to work as expected on main (a7a456b), though it also sometimes shows a single-pixel selection row above that. (Forgive me for testing this PR without seeing any other work on this widget, so I don't know if that's already tracked!)

video from scrolling:
https://github.com/user-attachments/assets/a2ae3a95-adbb-4916-b398-0ffc0815b500

let max_scroll_y = (info.size.y - view_size.y).max(0.);

let y = scroll_axis_with_inset(
editable_text.scroll_inset.y.clamp(0., 0.49) * view_size.y,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The use of 0.49 here and on line 568 seems a little magical. Why not 0.5 exactly? Would line 627 then catch the original intended case via e.g.
let new_v_min = if v_size - 2. * inset <= t_size { ?

Copy link
Copy Markdown
Contributor Author

@ickshonpe ickshonpe May 14, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The use of 0.49 here and on line 568 seems a little magical. Why not 0.5 exactly? Would line 627 then catch the original intended case via e.g. let new_v_min = if v_size - 2. * inset <= t_size { ?

0.5 is probably fine, clamping it below 0.49 felt safer though. It's just a defensive measure to preemptively head off the inevitable rounding bugs that pop up everywhere.

@ickshonpe
Copy link
Copy Markdown
Contributor Author

ickshonpe commented May 14, 2026

(btw, the sample name in the instructions is misspelled -- it's one letter from "multiline_text_input", which I actually tested before seeing the bug refers to "multiple_text_inputs"!)

It looks nice and feels pretty good. Pasting a long blob leaves the cursor where expected, at the end of the blob.

In the "multiline_text_input" case, which I mostly tested, it seems like this might introduce a regression. When dragging a selection up or down in the window, at some instances the selection outline becomes one pixel tall, either at the top or the bottom depending on scroll direction. I'd expect at least a full text line to be visible.

The keeping of at least a single selected row when scrolling seems to work as expected on main (a7a456b), though it also sometimes shows a single-pixel selection row above that. (Forgive me for testing this PR without seeing any other work on this widget, so I don't know if that's already tracked!)

video from scrolling: https://github.com/user-attachments/assets/a2ae3a95-adbb-4916-b398-0ffc0815b500

No this is really helpful, thank you, I hadn't noticed it losing a line draggging downwards.

The single-pixel selection region bleed is down to trusting the selection geometry from Parley too much. Sometimes it's out by a pixel due to its metric quantization, sometimes leading to extra pixel tall selection rects or pixel wide gaps between selection rects on adjacent lines.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

A-Text Rendering and layout for characters A-UI Graphical user interfaces, styles, layouts, and widgets C-Bug An unexpected or incorrect behavior C-Feature A new feature, making something new possible C-Refinement Improves output quality, without fixing a clear bug or adding new functionality. D-Complex Quite challenging from either a design or technical perspective. Ask for help! S-Needs-Review Needs reviewer attention (from anyone!) to move forward

Projects

Status: Needs SME Triage

Development

Successfully merging this pull request may close these issues.

Incorrect text scroll offset after pasting into a text input

2 participants