Skip to content

Fix #5019: revalidate lightweight Picker after shortcut button action#5020

Merged
shai-almog merged 1 commit into
masterfrom
fix-5019-lightweight-picker-shortcut-repaint
May 24, 2026
Merged

Fix #5019: revalidate lightweight Picker after shortcut button action#5020
shai-almog merged 1 commit into
masterfrom
fix-5019-lightweight-picker-shortcut-repaint

Conversation

@shai-almog
Copy link
Copy Markdown
Collaborator

Summary

  • On Android, the lightweight Picker spinner wheels could stay empty/stale after a custom addLightweightPopupButton callback called setDate / setTime / setSelectedString / setDuration while the popup was on screen. The user had to physically touch the wheel before the new value rendered.
  • Spinner3D.setModel only flags the scroller as needing a new preferred size; the implicit getScene().repaint() in SpinnerNode.setScrollY didn't drive a relayout on Android, so the wheels never picked up the new model dimensions.
  • PopupButtonActionListener now revalidates and repaints the spinner container after running the user's Runnable, which fulfils the documented contract on setDate that "if the lightweight popup is currently on screen the visible scroll wheels are also moved to the new value".

Why the screenshot test didn't catch it

LightweightPickerButtonsScreenshotTest exercises the live-propagation path, but two things masked the bug:

  1. It calls picker.setDate(fixedDate) directly from a UITimer, not from inside a shortcut-button callback. The bug specifically lives in the PopupButtonActionListener → user Runnable path. A bare picker.setDate(...) running on the EDT doesn't run alongside the button's press/release/repaint dispatch and apparently survives the next paint cycle on Android.
  2. The 400ms wait between setDate and screenshot is enough for any latent paint to run; even though PixelCopy captures actual on-screen pixels, the scenario the test exercises never enters the failing state in the first place.

Follow-up worth doing in a separate PR: extend the screenshot test to programmatically tap one of the shortcut buttons (so the capture flows through PopupButtonActionListener), which would have caught this and would guard against regressions in the fix.

Test plan

  • Existing LightweightPickerButtonsScreenshotTest still passes on Android (the test path is unchanged behaviour).
  • Manual: build the Android sample, open a date Picker with setUseLightweightPopup(true) and shortcut buttons that call setDate, tap a button, confirm wheels update without needing a touch.
  • Manual: same flow on the simulator (regression check that the new revalidate() doesn't introduce flicker).

Fixes #5019

🤖 Generated with Claude Code

On Android the spinner wheels could stay stale after a custom
addLightweightPopupButton callback called setDate/setTime/etc. while
the popup was on screen. Spinner3D.setModel only flags the scroller
as needing a new preferred size; the implicit getScene().repaint()
in SpinnerNode.setScrollY wasn't enough to drive a relayout, so the
wheels showed an empty/stale value until the user touched them.

PopupButtonActionListener now revalidates+repaints the spinner
container after running the user's Runnable, which fulfils the
documented contract on setDate that "if the lightweight popup is
currently on screen the visible scroll wheels are also moved to the
new value." The simulator was unaffected because its paint pipeline
already picked up the change.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@github-actions
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
Copy link
Copy Markdown
Collaborator Author

shai-almog commented May 23, 2026

Compared 110 screenshots: 110 matched.

Native Android coverage

  • 📊 Line coverage: 11.99% (6812/56827 lines covered) [HTML preview] (artifact android-coverage-report, jacocoAndroidReport/html/index.html)
    • Other counters: instruction 9.72% (34181/351812), branch 4.20% (1403/33371), complexity 5.25% (1682/32020), method 9.12% (1366/14980), class 14.81% (304/2052)
    • 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)
      • 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)
      • kotlinx.coroutines.kotlinx.coroutines.JobSupport – 0.00% (0/423 lines covered)

✅ Native Android screenshot tests passed.

Native Android coverage

  • 📊 Line coverage: 11.99% (6812/56827 lines covered) [HTML preview] (artifact android-coverage-report, jacocoAndroidReport/html/index.html)
    • Other counters: instruction 9.72% (34181/351812), branch 4.20% (1403/33371), complexity 5.25% (1682/32020), method 9.12% (1366/14980), class 14.81% (304/2052)
    • 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)
      • 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)
      • kotlinx.coroutines.kotlinx.coroutines.JobSupport – 0.00% (0/423 lines covered)

Benchmark Results

Detailed Performance Metrics

Metric Duration
Base64 payload size 8192 bytes
Base64 benchmark iterations 6000
Base64 native encode 1332.000 ms
Base64 CN1 encode 185.000 ms
Base64 encode ratio (CN1/native) 0.139x (86.1% faster)
Base64 native decode 1159.000 ms
Base64 CN1 decode 205.000 ms
Base64 decode ratio (CN1/native) 0.177x (82.3% faster)
Image encode benchmark status skipped (SIMD unsupported)

@shai-almog
Copy link
Copy Markdown
Collaborator Author

shai-almog commented May 23, 2026

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

Benchmark Results

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

Build and Run Timing

Metric Duration
Simulator Boot 77000 ms
Simulator Boot (Run) 1000 ms
App Install 10000 ms
App Launch 8000 ms
Test Execution 311000 ms

Detailed Performance Metrics

Metric Duration
Base64 payload size 8192 bytes
Base64 benchmark iterations 6000
Base64 native encode 1872.000 ms
Base64 CN1 encode 1391.000 ms
Base64 encode ratio (CN1/native) 0.743x (25.7% faster)
Base64 native decode 301.000 ms
Base64 CN1 decode 1109.000 ms
Base64 decode ratio (CN1/native) 3.684x (268.4% slower)
Base64 SIMD encode 460.000 ms
Base64 encode ratio (SIMD/native) 0.246x (75.4% faster)
Base64 encode ratio (SIMD/CN1) 0.331x (66.9% faster)
Base64 SIMD decode 374.000 ms
Base64 decode ratio (SIMD/native) 1.243x (24.3% slower)
Base64 decode ratio (SIMD/CN1) 0.337x (66.3% faster)
Image encode benchmark iterations 100
Image createMask (SIMD off) 58.000 ms
Image createMask (SIMD on) 9.000 ms
Image createMask ratio (SIMD on/off) 0.155x (84.5% faster)
Image applyMask (SIMD off) 119.000 ms
Image applyMask (SIMD on) 57.000 ms
Image applyMask ratio (SIMD on/off) 0.479x (52.1% faster)
Image modifyAlpha (SIMD off) 141.000 ms
Image modifyAlpha (SIMD on) 74.000 ms
Image modifyAlpha ratio (SIMD on/off) 0.525x (47.5% faster)
Image modifyAlpha removeColor (SIMD off) 196.000 ms
Image modifyAlpha removeColor (SIMD on) 67.000 ms
Image modifyAlpha removeColor ratio (SIMD on/off) 0.342x (65.8% faster)
Image PNG encode (SIMD off) 1045.000 ms
Image PNG encode (SIMD on) 916.000 ms
Image PNG encode ratio (SIMD on/off) 0.877x (12.3% faster)
Image JPEG encode 479.000 ms

@shai-almog
Copy link
Copy Markdown
Collaborator Author

shai-almog commented May 23, 2026

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

Benchmark Results

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

Build and Run Timing

Metric Duration
Simulator Boot 98000 ms
Simulator Boot (Run) 2000 ms
App Install 20000 ms
App Launch 11000 ms
Test Execution 321000 ms

Detailed Performance Metrics

Metric Duration
Base64 payload size 8192 bytes
Base64 benchmark iterations 6000
Base64 native encode 798.000 ms
Base64 CN1 encode 1850.000 ms
Base64 encode ratio (CN1/native) 2.318x (131.8% slower)
Base64 native decode 390.000 ms
Base64 CN1 decode 1412.000 ms
Base64 decode ratio (CN1/native) 3.621x (262.1% slower)
Base64 SIMD encode 1530.000 ms
Base64 encode ratio (SIMD/native) 1.917x (91.7% slower)
Base64 encode ratio (SIMD/CN1) 0.827x (17.3% faster)
Base64 SIMD decode 773.000 ms
Base64 decode ratio (SIMD/native) 1.982x (98.2% slower)
Base64 decode ratio (SIMD/CN1) 0.547x (45.3% faster)
Image encode benchmark iterations 100
Image createMask (SIMD off) 81.000 ms
Image createMask (SIMD on) 9.000 ms
Image createMask ratio (SIMD on/off) 0.111x (88.9% faster)
Image applyMask (SIMD off) 172.000 ms
Image applyMask (SIMD on) 90.000 ms
Image applyMask ratio (SIMD on/off) 0.523x (47.7% faster)
Image modifyAlpha (SIMD off) 206.000 ms
Image modifyAlpha (SIMD on) 96.000 ms
Image modifyAlpha ratio (SIMD on/off) 0.466x (53.4% faster)
Image modifyAlpha removeColor (SIMD off) 218.000 ms
Image modifyAlpha removeColor (SIMD on) 114.000 ms
Image modifyAlpha removeColor ratio (SIMD on/off) 0.523x (47.7% faster)
Image PNG encode (SIMD off) 1605.000 ms
Image PNG encode (SIMD on) 1294.000 ms
Image PNG encode ratio (SIMD on/off) 0.806x (19.4% faster)
Image JPEG encode 731.000 ms

@shai-almog shai-almog merged commit b7157a1 into master May 24, 2026
19 checks passed
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.

Lightweight Picker popup does not repaint after shortcut button changes value on Android

1 participant