Skip to content

Fix #5345: multiline TextArea text/hint jumps vertically when editing starts - #5358

Merged
shai-almog merged 6 commits into
masterfrom
fix/5345-multiline-textarea-vertical-shift
Jul 11, 2026
Merged

Fix #5345: multiline TextArea text/hint jumps vertically when editing starts#5358
shai-almog merged 6 commits into
masterfrom
fix/5345-multiline-textarea-vertical-shift

Conversation

@shai-almog

Copy link
Copy Markdown
Collaborator

Fixes #5345.

Problem

Editing the first field in the reporter's snippet makes the text shift up when editing starts and back down when it ends. With a hint on an empty field, the hint sits vertically centered while the cursor lands on the first line.

Root cause

Themes set a default vertical alignment for text components via the textCmpVAlignInt theme constant (commonly CENTER, so single-line TextFields center their text vertically). LookAndFeel applies that default to every TextArea through TextArea.setDefaultValign(), so multi-line areas inherit CENTER too.

Commit eefac9657a ("Align multiline textarea rendering") removed the guard that used to force editable multi-line areas to TOP, so the lightweight rendering started honoring CENTER/BOTTOM. But the native editors (JTextArea / UITextView / EditText / …) top-align their content on every platform (supportsNativeTextAreaVerticalAlignment() is false everywhere). The lightweight render therefore centered the text/cursor/hint while idle and the native editor top-aligned it during editing — hence the jump.

Fix

Reinstate the pre-regression behavior, but only in the rendering path via a new TextArea.getVerticalAlignmentForRendering(): an editable multi-line text area renders top-aligned on platforms whose native editor can't vertically align, so the lightweight rendering matches the native editor and nothing shifts.

  • getVerticalAlignment() still returns the logical value that was set, preserving the API contract and the unit tests added in eefac9657a.
  • Non-editable multi-line areas keep honoring CENTER/BOTTOM (the feature that commit intended), so the existing TextAreaAlignmentScreenshotTest/testVerticalAlignmentScreenshotStates references stay valid — they use setEditable(false).
  • The hint label is aligned the same way, so an empty field's hint appears where the cursor will be.

Tests

Added TextAreaTest cases covering editable multi-line (falls back to TOP), non-editable multi-line (honors CENTER/BOTTOM), and single-line (honors alignment). Full TextAreaTest suite: 46/46 green. Core compiles on JDK 8.

🤖 Generated with Claude Code

Themes set a default vertical alignment for text components via the
`textCmpVAlignInt` theme constant (commonly CENTER so single-line fields
center vertically). That default is applied to every TextArea, including
multi-line ones.

Commit eefac96 made the lightweight rendering honor that alignment for
multi-line text areas, but native editors (on every platform) top-align
their content. The result: an editable multi-line TextArea drew its text,
cursor and hint centered while idle, then jumped to the top the moment
editing started and back down when it ended.

Restore the pre-regression behavior only in the rendering path: editable
multi-line text areas render top-aligned on platforms whose native editor
doesn't support vertical alignment, so the lightweight rendering matches
the native editor and nothing shifts. getVerticalAlignment() still returns
the logical value that was set, so non-editable multi-line areas keep
honoring CENTER/BOTTOM and the existing API contract is preserved.

The hint label is aligned the same way so an empty field's hint sits where
the cursor will appear instead of floating in the middle.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@github-actions

github-actions Bot commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

✅ Continuous Quality Report

Test & Coverage

Static Analysis

  • SpotBugs [Report archive]
    • ByteCodeTranslator: 0 findings (no issues)
    • android: 0 findings (no issues)
    • codenameone-maven-plugin: 0 findings (no issues)
    • core-unittests: 0 findings (no issues)
    • ios: 0 findings (no issues)
  • PMD: 0 findings (no issues) [Report archive]
  • Checkstyle: 0 findings (no issues) [Report archive]

Generated automatically by the PR CI workflow.

@shai-almog

shai-almog commented Jul 10, 2026

Copy link
Copy Markdown
Collaborator Author

Compared 144 screenshots: 144 matched.

Native Android coverage

  • 📊 Line coverage: 10.12% (10320/101985 lines covered) [HTML preview] (artifact android-coverage-report, jacocoAndroidReport/html/index.html)
    • Other counters: instruction 8.94% (50626/566413), branch 4.46% (2304/51608), complexity 4.48% (2445/54551), method 6.81% (1925/28286), class 10.87% (437/4021)
    • Lowest covered classes
      • kotlin.collections.kotlin.collections.ArraysKt___ArraysKt – 0.00% (0/6327 lines covered)
      • kotlin.collections.unsigned.kotlin.collections.unsigned.UArraysKt___UArraysKt – 0.00% (0/2384 lines covered)
      • org.jacoco.agent.rt.internal_b6258fc.asm.org.jacoco.agent.rt.internal_b6258fc.asm.ClassReader – 0.00% (0/1519 lines covered)
      • kotlin.collections.kotlin.collections.CollectionsKt___CollectionsKt – 0.00% (0/1148 lines covered)
      • org.jacoco.agent.rt.internal_b6258fc.asm.org.jacoco.agent.rt.internal_b6258fc.asm.MethodWriter – 0.00% (0/923 lines covered)
      • kotlin.sequences.kotlin.sequences.SequencesKt___SequencesKt – 0.00% (0/730 lines covered)
      • com.google.common.cache.com.google.common.cache.LocalCache$Segment – 0.00% (0/726 lines covered)
      • kotlin.text.kotlin.text.StringsKt___StringsKt – 0.00% (0/623 lines covered)
      • org.jacoco.agent.rt.internal_b6258fc.asm.org.jacoco.agent.rt.internal_b6258fc.asm.Frame – 0.00% (0/564 lines covered)
      • kotlin.collections.kotlin.collections.ArraysKt___ArraysJvmKt – 0.00% (0/495 lines covered)

✅ Native Android screenshot tests passed.

Native Android coverage

  • 📊 Line coverage: 10.12% (10320/101985 lines covered) [HTML preview] (artifact android-coverage-report, jacocoAndroidReport/html/index.html)
    • Other counters: instruction 8.94% (50626/566413), branch 4.46% (2304/51608), complexity 4.48% (2445/54551), method 6.81% (1925/28286), class 10.87% (437/4021)
    • Lowest covered classes
      • kotlin.collections.kotlin.collections.ArraysKt___ArraysKt – 0.00% (0/6327 lines covered)
      • kotlin.collections.unsigned.kotlin.collections.unsigned.UArraysKt___UArraysKt – 0.00% (0/2384 lines covered)
      • org.jacoco.agent.rt.internal_b6258fc.asm.org.jacoco.agent.rt.internal_b6258fc.asm.ClassReader – 0.00% (0/1519 lines covered)
      • kotlin.collections.kotlin.collections.CollectionsKt___CollectionsKt – 0.00% (0/1148 lines covered)
      • org.jacoco.agent.rt.internal_b6258fc.asm.org.jacoco.agent.rt.internal_b6258fc.asm.MethodWriter – 0.00% (0/923 lines covered)
      • kotlin.sequences.kotlin.sequences.SequencesKt___SequencesKt – 0.00% (0/730 lines covered)
      • com.google.common.cache.com.google.common.cache.LocalCache$Segment – 0.00% (0/726 lines covered)
      • kotlin.text.kotlin.text.StringsKt___StringsKt – 0.00% (0/623 lines covered)
      • org.jacoco.agent.rt.internal_b6258fc.asm.org.jacoco.agent.rt.internal_b6258fc.asm.Frame – 0.00% (0/564 lines covered)
      • kotlin.collections.kotlin.collections.ArraysKt___ArraysJvmKt – 0.00% (0/495 lines covered)

Benchmark Results

Detailed Performance Metrics

Metric Duration
SIMD kernel backend scalar fallback (no native SIMD)
SIMD int-add (64K x300) java 201ms / native 134ms = 1.5x speedup
SIMD float-mul (64K x300) java 104ms / native 129ms = 0.8x speedup
SIMD kernel correctness PASS (native result == scalar reference)
Base64 payload size 8192 bytes
Base64 benchmark iterations 6000
Base64 SIMD byte path gated to scalar (CPU autovectorizes scalar; explicit SIMD not beneficial here)
Base64 CN1 encode 279.000 ms
Base64 CN1 decode 333.000 ms
Base64 native encode 700.000 ms
Base64 encode ratio (CN1/native) 0.399x (60.1% faster)
Base64 native decode 1260.000 ms
Base64 decode ratio (CN1/native) 0.264x (73.6% faster)
Image encode benchmark status skipped (SIMD unsupported)

@github-actions

Copy link
Copy Markdown
Contributor

Cloudflare Preview

@shai-almog

shai-almog commented Jul 10, 2026

Copy link
Copy Markdown
Collaborator Author

Compared 142 screenshots: 142 matched.
✅ Native Mac screenshot tests passed.

Benchmark Results

  • VM Translation Time: 0 seconds
  • Compilation Time: 311 seconds

Detailed Performance Metrics

Metric Duration
SIMD kernel backend SSE2 (x64) / NEON (arm64) native kernels
SIMD int-add (64K x300) java 73ms / native 3ms = 24.3x speedup
SIMD float-mul (64K x300) java 64ms / native 3ms = 21.3x speedup
SIMD kernel correctness PASS (native result == scalar reference)
Base64 payload size 8192 bytes
Base64 benchmark iterations 6000
Base64 SIMD byte path active (NEON-accelerated)
Base64 CN1 encode 262.000 ms
Base64 CN1 decode 225.000 ms
Base64 native encode 45263.000 ms
Base64 encode ratio (CN1/native) 0.006x (99.4% faster)
Base64 native decode 36732.000 ms
Base64 decode ratio (CN1/native) 0.006x (99.4% faster)
Base64 SIMD encode 79.000 ms
Base64 encode ratio (SIMD/CN1) 0.302x (69.8% faster)
Base64 SIMD decode 80.000 ms
Base64 decode ratio (SIMD/CN1) 0.356x (64.4% faster)
Base64 encode ratio (SIMD/native) 0.002x (99.8% faster)
Base64 decode ratio (SIMD/native) 0.002x (99.8% faster)
Image encode benchmark iterations 100
Image createMask (SIMD off) 201.000 ms
Image createMask (SIMD on) 161.000 ms
Image createMask ratio (SIMD on/off) 0.801x (19.9% faster)
Image applyMask (SIMD off) 588.000 ms
Image applyMask (SIMD on) 804.000 ms
Image applyMask ratio (SIMD on/off) 1.367x (36.7% slower)
Image modifyAlpha (SIMD off) 562.000 ms
Image modifyAlpha (SIMD on) 610.000 ms
Image modifyAlpha ratio (SIMD on/off) 1.085x (8.5% slower)
Image modifyAlpha removeColor (SIMD off) 609.000 ms
Image modifyAlpha removeColor (SIMD on) 506.000 ms
Image modifyAlpha removeColor ratio (SIMD on/off) 0.831x (16.9% faster)

@shai-almog

shai-almog commented Jul 10, 2026

Copy link
Copy Markdown
Collaborator Author

Compared 143 screenshots: 143 matched.
✅ Native iOS Metal screenshot tests passed.

Benchmark Results

  • VM Translation Time: 0 seconds
  • Compilation Time: 429 seconds

Build and Run Timing

Metric Duration
Simulator Boot 83000 ms
Simulator Boot (Run) 1000 ms
App Install 16000 ms
App Launch 4000 ms
Test Execution 443000 ms

Detailed Performance Metrics

Metric Duration
SIMD kernel backend SSE2 (x64) / NEON (arm64) native kernels
SIMD int-add (64K x300) java 55ms / native 4ms = 13.7x speedup
SIMD float-mul (64K x300) java 54ms / native 3ms = 18.0x speedup
SIMD kernel correctness PASS (native result == scalar reference)
Base64 payload size 8192 bytes
Base64 benchmark iterations 6000
Base64 SIMD byte path active (NEON-accelerated)
Base64 CN1 encode 209.000 ms
Base64 CN1 decode 332.000 ms
Base64 native encode 9881.000 ms
Base64 encode ratio (CN1/native) 0.021x (97.9% faster)
Base64 native decode 759.000 ms
Base64 decode ratio (CN1/native) 0.437x (56.3% faster)
Base64 SIMD encode 122.000 ms
Base64 encode ratio (SIMD/CN1) 0.584x (41.6% faster)
Base64 SIMD decode 158.000 ms
Base64 decode ratio (SIMD/CN1) 0.476x (52.4% faster)
Base64 encode ratio (SIMD/native) 0.012x (98.8% faster)
Base64 decode ratio (SIMD/native) 0.208x (79.2% faster)
Image encode benchmark iterations 100
Image createMask (SIMD off) 34.000 ms
Image createMask (SIMD on) 42.000 ms
Image createMask ratio (SIMD on/off) 1.235x (23.5% slower)
Image applyMask (SIMD off) 197.000 ms
Image applyMask (SIMD on) 323.000 ms
Image applyMask ratio (SIMD on/off) 1.640x (64.0% slower)
Image modifyAlpha (SIMD off) 235.000 ms
Image modifyAlpha (SIMD on) 168.000 ms
Image modifyAlpha ratio (SIMD on/off) 0.715x (28.5% faster)
Image modifyAlpha removeColor (SIMD off) 126.000 ms
Image modifyAlpha removeColor (SIMD on) 131.000 ms
Image modifyAlpha removeColor ratio (SIMD on/off) 1.040x (4.0% slower)

@shai-almog

shai-almog commented Jul 10, 2026

Copy link
Copy Markdown
Collaborator Author

Compared 216 screenshots: 216 matched.
✅ Native Apple Watch (watchOS, Core Graphics) screenshot tests passed.

The previous commit aligned the hint of every text component to its
rendering alignment. For single-line fields that shifted the hint a few
pixels (they were not previously centered), which perturbed unrelated
TextField hint screenshots (TextFieldTheme, kotlin) on every platform for
no functional benefit.

Restrict the adjustment to multi-line text areas, which is the only case
issue #5345 is about. Single-line fields keep their existing hint behavior,
so the device screenshot goldens are unaffected.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@shai-almog

shai-almog commented Jul 10, 2026

Copy link
Copy Markdown
Collaborator Author

Compared 139 screenshots: 139 matched.
✅ Native Apple TV (tvOS, Metal) screenshot tests passed.

@shai-almog

shai-almog commented Jul 10, 2026

Copy link
Copy Markdown
Collaborator Author

Compared 137 screenshots: 137 matched.
✅ Native iOS screenshot tests passed.

Benchmark Results

  • VM Translation Time: 0 seconds
  • Compilation Time: 367 seconds

Build and Run Timing

Metric Duration
Simulator Boot 61000 ms
Simulator Boot (Run) 0 ms
App Install 12000 ms
App Launch 7000 ms
Test Execution 538000 ms

Detailed Performance Metrics

Metric Duration
SIMD kernel backend SSE2 (x64) / NEON (arm64) native kernels
SIMD int-add (64K x300) java 61ms / native 3ms = 20.3x speedup
SIMD float-mul (64K x300) java 189ms / native 2ms = 94.5x speedup
SIMD kernel correctness PASS (native result == scalar reference)
Base64 payload size 8192 bytes
Base64 benchmark iterations 6000
Base64 SIMD byte path active (NEON-accelerated)
Base64 CN1 encode 167.000 ms
Base64 CN1 decode 119.000 ms
Base64 native encode 1250.000 ms
Base64 encode ratio (CN1/native) 0.134x (86.6% faster)
Base64 native decode 406.000 ms
Base64 decode ratio (CN1/native) 0.293x (70.7% faster)
Base64 SIMD encode 51.000 ms
Base64 encode ratio (SIMD/CN1) 0.305x (69.5% faster)
Base64 SIMD decode 48.000 ms
Base64 decode ratio (SIMD/CN1) 0.403x (59.7% faster)
Base64 encode ratio (SIMD/native) 0.041x (95.9% faster)
Base64 decode ratio (SIMD/native) 0.118x (88.2% faster)
Image encode benchmark iterations 100
Image createMask (SIMD off) 35.000 ms
Image createMask (SIMD on) 34.000 ms
Image createMask ratio (SIMD on/off) 0.971x (2.9% faster)
Image applyMask (SIMD off) 172.000 ms
Image applyMask (SIMD on) 154.000 ms
Image applyMask ratio (SIMD on/off) 0.895x (10.5% faster)
Image modifyAlpha (SIMD off) 89.000 ms
Image modifyAlpha (SIMD on) 178.000 ms
Image modifyAlpha ratio (SIMD on/off) 2.000x (100.0% slower)
Image modifyAlpha removeColor (SIMD off) 161.000 ms
Image modifyAlpha removeColor (SIMD on) 213.000 ms
Image modifyAlpha removeColor ratio (SIMD on/off) 1.323x (32.3% slower)

shai-almog and others added 4 commits July 10, 2026 20:54
Only reposition the hint when the field actually spans multiple rows
(getActualRows() > 1). A one-row growable field such as ChatInput's
ChatInputField (a TextField with setSingleLineTextArea(false)) has no
vertical gap, so its hint stays centered as before -- this keeps the
ChatInput watch screenshots unchanged.

On themes that vertically center text (the native iOS and mac themes, via
textCmpVAlignInt), an editable multi-line TextArea previously rendered its
text centered while idle and jumped to the top when the native editor took
over. The fix top-aligns it consistently, so the TextFieldTheme device
goldens for iOS (regular + metal) and mac-native are refreshed to the
corrected rendering. The Android theme is top-aligned already, so those
goldens are unchanged; the watch/tv suites are unaffected.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Replaces the separate getVerticalAlignmentForRendering() with a plain
default: an editable multi-line text area reports TOP from
getVerticalAlignment() instead of the CENTER theme default, so its
lightweight text/cursor/hint line up with the top-aligning native editor
and don't jump when editing starts/ends (#5345).

Why this is scoped to editable multi-line rather than a blanket field
default: the "must be TOP" constraint comes from the native editor, which
only exists for editable fields. A blanket default would also top-align
every non-editable multi-line area -- notably the TextArea inside every
SpanLabel/SpanButton -- changing unrelated rendering. Single-line fields
and non-editable areas therefore keep the theme default and still honor
CENTER/BOTTOM.

An explicit setVerticalAlignment() sets a flag and is always honored, so
the value is retained (matching the getter/setter symmetry) even for an
editable multi-line area. Rendering and the hint now read
getVerticalAlignment() directly; behaviour is identical to the previous
commit for every case exercised by a screenshot, so the refreshed iOS/mac
goldens stay valid.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The theme's textCmpVAlignInt default is meant for single-line fields;
applying it to multi-line areas is what caused the centered-then-jump
rendering (#5345). Make every multi-line text area default to TOP -- the
original pre-regression behavior -- instead of scoping the fix to editable
areas. Non-editable multi-line areas (e.g. the text inside SpanLabel /
SpanButton) are top-aligned again too; an explicit setVerticalAlignment()
is still honored for display text that wants CENTER/BOTTOM.

This drops the isEditable()/supportsNativeTextAreaVerticalAlignment()
scoping from getVerticalAlignment(); the earlier version kept those partly
to avoid refreshing SpanLabel-based goldens, which was the wrong trade-off.
Device goldens that render a non-editable multi-line area with vertical
slack are refreshed accordingly.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Non-editable multi-line text now top-aligns (SpanLabel/SpanButton captions
and body text), so refresh the device goldens on the center-valign themes.
Only SpanLabel-based text shifts (a few px, from centered to top); the
interactive components and layouts are unchanged. Android keeps its
top-aligned theme so its goldens are untouched.

Verified each changed category visually (SpanLabelTheme, ButtonTheme,
SwitchTheme, ShowcaseTheme, ChatView, DialogTheme, AppReviewDialog).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@shai-almog
shai-almog merged commit 78eb3a7 into master Jul 11, 2026
33 of 35 checks passed
@shai-almog
shai-almog deleted the fix/5345-multiline-textarea-vertical-shift branch July 11, 2026 11:39
shai-almog added a commit that referenced this pull request Jul 11, 2026
…rk MultiButton

The merged tree renders #5358's corrected vertical TextArea/text
alignment (shifts every screen with text components, including the
measurement-overlay labels on ButtonTheme/SwitchTheme) and the dark
MultiButton rounded-rect fix. Verified MultiButtonTheme_ios_dark now
renders rounded dark cells; accepted all 16 mac + 30 JS flagged
renders. iOS-family (GL/Metal/tv/watch) churn lands with their runs.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
shai-almog added a commit that referenced this pull request Jul 11, 2026
…ty-suite

Conflicts resolved by keeping the branch side:
- scripts/cn1playground/pom.xml: keep the deliberate 8.0-SNAPSHOT pin
  (master's 7.0.258 release bump must not touch the playground pin).
- 14 scripts/javascript goldens: master's #5361 regenerated them on a
  tree without this branch's theme changes; ours were captured from the
  merged tree post-#5358 and remain correct for this PR.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
shai-almog added a commit that referenced this pull request Jul 11, 2026
… shift + rounded dark MultiButton

GL (16), Metal (16), tv (5), watch (9) renders reviewed against the
mac goldens from the merged tree. The old GL/Metal goldens carried
pre-#5351 renders where the Metal/GL foreground erase dropped button
pills and text-field glass cells (ButtonTheme_dark golden showed bare
blue text and H=7mm vs the correct glass pills at H=5mm); the new
renders restore them. Remaining churn is the #5358 TextArea vertical
alignment shift plus the rounded dark MultiButton fix (74599f4),
now confirmed rounded on metal, tv and watch.

Deliberately NOT accepted: AnimateLayoutScreenshotTest-family goldens
(their committed renders lost the tile text in the final frames on all
ParparVM runtimes -- iOS GL/Metal/JS -- while JVM/ART runtimes keep it;
that regression is being tracked down separately and the goldens will
be refreshed when it is fixed).

Co-Authored-By: Claude Fable 5 <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.

Multiline TextArea shifts vertically when editing starts

1 participant