Skip to content
Open
Show file tree
Hide file tree
Changes from 32 commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
21fd041
Added scroll module, with scroll rules and the `TextViewport` type.
ickshonpe Jun 12, 2026
761fd4f
Added new `ScrollBy`, `ScrollTo`, and `ScrollByLines` `TextEdit`s
ickshonpe Jun 12, 2026
6b08d77
Added scrolling calculations and tests
ickshonpe Jun 12, 2026
e1105c6
Added `viewport: TextViewport` field to `EditableText`
ickshonpe Jun 13, 2026
bf8ebdb
Added `cursor_margin: Vec2` field to `EditableText`.
ickshonpe Jun 13, 2026
d931470
Added binds for the ScrollByLines TextEdit.
ickshonpe Jun 13, 2026
81b3403
Removed `TextScroll` component.
ickshonpe Jun 13, 2026
5f5afa1
Removed `TextScroll` component
ickshonpe Jun 13, 2026
dcf2031
updated testbed_ui
ickshonpe Jun 13, 2026
2f376a9
Combined minimal changes to move scrolling into bevy_text
ickshonpe Jun 15, 2026
d6bdf5d
Merge branch 'editable-text-viewport' into editable-text-scroll
ickshonpe Jun 15, 2026
2955c01
Merge branch 'main' into editable-text-scroll
ickshonpe Jun 15, 2026
b313462
Split borrow in update_editable_text_layout to avoid collecting lines.
ickshonpe Jun 15, 2026
c6dce10
Remove need to collect and smallvec dependency from scroll functions …
ickshonpe Jun 15, 2026
887e5f0
Moved IME composing guard back before local_pos calculated in on_poin…
ickshonpe Jun 15, 2026
877471c
Disable click propagation before the IME composing guard.
ickshonpe Jun 15, 2026
ce95792
Also always set focused if an `EditableText` found in `on_pointer_pre…
ickshonpe Jun 15, 2026
9275e82
Removed .chain() on single system.
ickshonpe Jun 15, 2026
a6316f4
Added failing test in `scroll` module with `LineBreak::WordBoundary`
ickshonpe Jun 15, 2026
ef43813
Extend scrollable_width with LineBreak::Boundary so cursor space adde…
ickshonpe Jun 15, 2026
fc1b140
Renamed scrollable_content_width to scrollable_text_layout_width.
ickshonpe Jun 15, 2026
53f029c
Remove Justify condition for `scrollable_text_width` cursor width.
ickshonpe Jun 15, 2026
97e1e56
Removed the unused justify parameter from scrollable_text_layout_width
ickshonpe Jun 15, 2026
c1bfd72
Removed redundant explicit link target
ickshonpe Jun 15, 2026
b8c00d4
Merge branch 'main' into editable-text-scroll
ickshonpe Jun 17, 2026
9dd71e9
used `Rect` `clamp_point` function instead of `Vec2` clamp
ickshonpe Jun 17, 2026
590fb41
Merge branch 'main' into editable-text-scroll
ickshonpe Jun 30, 2026
5af1c23
Modified dummy text to pass CI
ickshonpe Jun 30, 2026
5fcca3c
Removed TextScroll from number_input
ickshonpe Jun 30, 2026
c9ef4db
Added migration guide
ickshonpe Jun 30, 2026
0c9d1cf
Use US spellings so typos passes.
ickshonpe Jun 30, 2026
9564201
Merge branch 'main' into editable-text-scroll
ickshonpe Jul 9, 2026
a3df606
added backticks to doc comment item
ickshonpe Jul 9, 2026
6b97d85
Fix doc list's indentation
ickshonpe Jul 9, 2026
25024c4
use assign-op
ickshonpe Jul 9, 2026
99546f4
fixed using `.iter().next()` on an array
ickshonpe Jul 9, 2026
1366859
Removed trailing spaces from migration guide, changed title.
ickshonpe Jul 9, 2026
3bc0229
Fix for "field assignment outside of initializer for an instance crea…
ickshonpe Jul 9, 2026
e6c4a1d
moved import to tests
ickshonpe Jul 9, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions _release-content/migration-guides/editable_text_scrolling.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
---
title: "`EditableText` scrolling now uses `TextViewport`"
pull_requests: [24634]
---

`bevy_ui::widget::TextScroll` has been removed. Editable text scroll state is now stored in `EditableText::viewport`, using the new `bevy_text::TextViewport` type. `EditableText::viewport.offset` is the direct replacement for `TextScroll`.

The `scroll_editable_text` system has also been removed, cursor reveal behavior is now handled automatically when `TextEdit`s are applied.

A new system `sync_editable_text_viewports` in `bevy_ui` synchronizes each `EditableText`'s viewport size with the size of its respective `ComputedNode`.
14 changes: 5 additions & 9 deletions crates/bevy_feathers/src/controls/number_input.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,11 +38,9 @@ use bevy_text::{
TextLayout,
};
use bevy_ui::{
percent, px,
widget::{Text, TextScroll},
AlignItems, AlignSelf, BackgroundGradient, ColorStop, ComputedNode, ComputedUiRenderTargetInfo,
Display, Gradient, InteractionDisabled, InterpolationColorSpace, JustifyContent,
LinearGradient, Node, PositionType, UiGlobalTransform, UiRect, UiScale,
percent, px, widget::Text, AlignItems, AlignSelf, BackgroundGradient, ColorStop, ComputedNode,
ComputedUiRenderTargetInfo, Display, Gradient, InteractionDisabled, InterpolationColorSpace,
JustifyContent, LinearGradient, Node, PositionType, UiGlobalTransform, UiRect, UiScale,
};
use bevy_ui_widgets::ValueChange;

Expand Down Expand Up @@ -813,7 +811,6 @@ fn scrubber_on_release(
&ComputedNode,
&ComputedUiRenderTargetInfo,
&UiGlobalTransform,
&TextScroll,
)>,
q_scrubber: Query<(&ComputedNode, &UiGlobalTransform, &mut ScrubberDragState)>,
q_root: Query<Has<InteractionDisabled>>,
Expand All @@ -823,8 +820,7 @@ fn scrubber_on_release(
) {
if let Ok(&ChildOf(text_id)) = q_parent.get(release.event_target())
&& let Ok(&ChildOf(root_id)) = q_parent.get(text_id)
&& let Ok((mut editable_text, node, target, transform, text_scroll)) =
q_text.get_mut(text_id)
&& let Ok((mut editable_text, node, target, transform)) = q_text.get_mut(text_id)
&& let Ok((_, _, drag_state)) = q_scrubber.get(release.entity)
&& let Ok(disabled) = q_root.get(root_id)
{
Expand All @@ -850,7 +846,7 @@ fn scrubber_on_release(
inverse.transform_point2(
release.pointer_location.position * target.scale_factor() / ui_scale.0,
) - node.content_box().min
+ text_scroll.0
+ editable_text.viewport.offset
}) else {
return;
};
Expand Down
42 changes: 35 additions & 7 deletions crates/bevy_text/src/editing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,13 +73,15 @@
// and `bevy_ui`, such as text layout and font management.

use crate::{
text_edit::{poll_and_apply_paste, TextEdit},
scroll::TextViewport,
text_edit::{poll_and_apply_paste, reveal_cursor, TextEdit},
FontCx, FontHinting, LayoutCx, LineHeight, TextBrush, TextColor, TextFont, TextLayout,
};
use alloc::sync::Arc;
use bevy_clipboard::ClipboardRead;
use bevy_derive::{Deref, DerefMut};
use bevy_ecs::prelude::*;
use bevy_math::Vec2;
use core::time::Duration;
use parley::{FontContext, LayoutContext, PlainEditor, SplitString};

Expand Down Expand Up @@ -115,6 +117,8 @@ pub struct EditableText {
/// These operations should generally be batched together to avoid redundant layout work.
// The B: Brush generic here must match the brush used by `ComputedTextBlock` to ensure that the font system is compatible.
pub editor: PlainEditor<TextBrush>,
/// The bounds of the visible portion of the text layout.
pub viewport: TextViewport,
/// Text edit actions that have been requested but not yet applied.
///
/// These edits are processed in first-in, first-out order.
Expand All @@ -128,6 +132,11 @@ pub struct EditableText {
/// rather than draining further edits, so that everything after the paste stays correctly ordered *behind* it.
// TODO: this may cause unexpected stalls if the clipboard read takes too long. We may want to add a timeout.
pub pending_paste: Option<ClipboardRead>,
/// Cursor reveal margins as fractions of the viewport size.
///
/// Each component is applied to both edges of its axis. Values are clamped
/// to `0.0..=0.5`, and non-finite values are treated as zero.
pub cursor_margin: Vec2,
/// Cursor width, relative to font size
pub cursor_width: f32,
/// Cursor blink period in seconds.
Expand All @@ -151,6 +160,8 @@ impl Default for EditableText {
Self {
// Defaults selected to match `Text::default()`
editor: PlainEditor::new(100.),
viewport: TextViewport::default(),
cursor_margin: Vec2::splat(0.2),
pending_edits: Vec::new(),
pending_paste: None,
cursor_width: 0.2,
Expand Down Expand Up @@ -217,6 +228,8 @@ impl EditableText {
pending_edits,
pending_paste,
max_characters,
viewport,
cursor_margin,
..
} = self;

Expand All @@ -225,11 +238,15 @@ impl EditableText {
// First: resolve any paste carried over from a previous frame. If it's still
// pending, hold the remaining edits (untouched in `pending_edits`) for next frame
// so ordering relative to the paste is preserved.
if let Some(mut read) = pending_paste.take()
&& !poll_and_apply_paste(&mut read, &mut driver, *max_characters, &char_filter)
{
*pending_paste = Some(read);
return;
if let Some(mut read) = pending_paste.take() {
let generation = driver.editor.generation();
if !poll_and_apply_paste(&mut read, &mut driver, *max_characters, &char_filter) {
*pending_paste = Some(read);
return;
}
if generation != driver.editor.generation() {
reveal_cursor(&mut driver, viewport, *cursor_margin);
}
}

// Drain edits one at a time. A paste that resolves synchronously (always the case
Expand All @@ -239,15 +256,26 @@ impl EditableText {
while let Some(edit) = edits.next() {
match edit {
TextEdit::Paste => {
let generation = driver.editor.generation();
let mut read = clipboard.fetch_text();
if !poll_and_apply_paste(&mut read, &mut driver, *max_characters, &char_filter)
{
*pending_paste = Some(read);
pending_edits.extend(edits);
return;
}
if generation != driver.editor.generation() {
reveal_cursor(&mut driver, viewport, *cursor_margin);
}
}
other => other.apply(&mut driver, clipboard, *max_characters, &char_filter),
other => other.apply(
&mut driver,
viewport,
*cursor_margin,
clipboard,
*max_characters,
&char_filter,
),
}
}
}
Expand Down
2 changes: 2 additions & 0 deletions crates/bevy_text/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ mod font_loader;
mod glyph;
mod parley_context;
mod pipeline;
mod scroll;
mod text;
mod text_access;
mod text_edit;
Expand All @@ -57,6 +58,7 @@ pub use font_loader::*;
pub use glyph::*;
pub use parley_context::*;
pub use pipeline::*;
pub use scroll::*;
pub use text::*;
pub use text_access::*;
pub use text_edit::*;
Expand Down
Loading
Loading