Skip to content

Fix caret position with centered text - #22340

Merged
Gillibald merged 3 commits into
AvaloniaUI:mainfrom
chenjt2001:fix/text-presenter-centered-caret
Oct 6, 2026
Merged

Gillibald merged 3 commits into
AvaloniaUI:mainfrom
chenjt2001:fix/text-presenter-centered-caret

Conversation

@chenjt2001

Copy link
Copy Markdown
Contributor

What does the pull request do?

Fixes incorrect caret positioning in TextBox when TextAlignment is set to Center and the final arranged width differs from the width used during measure.

Adds a regression test covering the case where the TextLayout is recreated during arrange.

What is the current behavior?

<Window xmlns="https://github.com/avaloniaui"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
        x:Class="TextBoxCaretRepro.MainWindow"
        Title="TextBoxCaretRepro">
    <StackPanel Margin="40" Spacing="20">

        <TextBlock Text="Type into the centered TextBox:" FontSize="18" />

        <TextBox Width="400"
                 FontSize="32"
                 TextAlignment="Center"
                 HorizontalAlignment="Center" />

    </StackPanel>
</Window>
TextBoxCaretRepro.2026-09-29.11-00-59.mp4

What is the updated/expected behavior with this PR?

The caret position is recalculated against the same TextLayout that is used to render the text.

As a result, the caret remains correctly positioned when the text is centered, including after the layout is recreated with a different arranged width.

How was the solution implemented (if it's not obvious)?

TextPresenter already marks the caret bounds as dirty whenever a new TextLayout is created.

Previously, when ArrangeOverride invalidated and recreated the layout due to a changed final width, the dirty caret bounds were not recalculated before the caret was rendered.

The fix ensures that the caret bounds are recalculated against the newly created layout after the arranged width changes.

Checklist

Breaking changes

None.

Obsoletions / Deprecations

None.

Fixed issues

Regression introduced by PR #22176.

@MrJul MrJul added bug backport-candidate-12.1.x Consider this PR for backporting to 12.1 branch area-controls labels Sep 29, 2026
Gillibald
Gillibald previously approved these changes Oct 1, 2026

@Gillibald Gillibald left a comment

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.

LGTM with some small nit mentioned in the comments

- Arrange already recreates the layout when the constraint changes, so the explicit getter call added nothing
@Gillibald
Gillibald enabled auto-merge October 6, 2026 09:26
@github-actions

github-actions Bot commented Oct 6, 2026

Copy link
Copy Markdown

NuGet packages

The packages for this pull request have been published (workflow run):

@Gillibald
Gillibald added this pull request to the merge queue Oct 6, 2026
Merged via the queue into AvaloniaUI:main with commit b2b943e Oct 6, 2026
10 checks passed
@fholmquist

Copy link
Copy Markdown

Thanks for this. The same regression from AvaloniaUI/Avalonia#22176 shows up with left-aligned, wrapped text too (TextWrapping="Wrap"), not only centered text. When a line's last word fits in Math.Ceiling(finalWidth) but not in the measure width, the drawn layout and the caret's layout wrap differently. Every caret on the following lines is then drawn a word to the right, and Up/Down move from the wrong spot. Seen on 12.1.3 on Windows, with a TextBox inside a LayoutTransformControl at 0.85 scale and 225% DPI: a caret at the start of line 2 was drawn after the first word. Re-placing the caret on the drawn layout after layout (what this PR does in ArrangeOverride) fixes it in our app. We'd welcome the 12.1.x backport.

@cla-avalonia

cla-avalonia commented Oct 6, 2026 •

Copy link
Copy Markdown
Collaborator
  • All contributors have signed the CLA.

grokys pushed a commit to Evan260/Avalonia that referenced this pull request Oct 7, 2026
* Update caret bounds after TextLayout recreation

* Drop redundant TextLayout access from the centered caret test

- Arrange already recreates the layout when the constraint changes, so the explicit getter call added nothing

---------

Co-authored-by: Benedikt Stebner <Gillibald@users.noreply.github.com>
@chenjt2001

Copy link
Copy Markdown
Contributor Author

@cla-avalonia agree

MrJul pushed a commit that referenced this pull request Oct 9, 2026
* Update caret bounds after TextLayout recreation

* Drop redundant TextLayout access from the centered caret test

- Arrange already recreates the layout when the constraint changes, so the explicit getter call added nothing

---------

Co-authored-by: Benedikt Stebner <Gillibald@users.noreply.github.com>
@MrJul MrJul added backported-12.1.x and removed backport-candidate-12.1.x Consider this PR for backporting to 12.1 branch labels Oct 9, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants