Skip to content

Give ToggleButton a style: neither native theme defined it - #5757

Merged
shai-almog merged 29 commits into
masterfrom
devguide-io-figures
Sep 12, 2026
Merged

Give ToggleButton a style: neither native theme defined it#5757
shai-almog merged 29 commits into
masterfrom
devguide-io-figures

Conversation

@shai-almog

Copy link
Copy Markdown
Collaborator

Button.setToggle(true) rewrites the UIID to ToggleButton (Button.java:1043-1044), and CheckBox.createToggle / RadioButton.createToggle go through the same path. No theme in the tree defined that UIID — not iOS Modern, not Android Material — so every toggle button fell through to the default style and rendered as bare text with no shape.

The Components chapter documents the feature and illustrated it with a 2015 screenshot from a theme that did define it, so the guide promised an appearance the shipped themes couldn't produce.

What each theme draws now

unselected selected
Android Material outlined pill, on-surface label filled with the secondary container, on-container label
iOS glass capsule, black label filled with the accent, white label

Selection changes the fill in both, not only the label color, so it doesn't rest on hue alone.

Why this needs no rasterized fallback

cn1-pill-border already carries a stroke: createRoundBorder reads border-top-color / border-top-width and calls RoundBorder.stroke(...), and usesRoundBorder short-circuits requiresBackgroundImageGeneration. FlatButton in both themes already relies on exactly that. Both themes compile clean under strictNoCef=true.

Two conventions from the files are followed deliberately:

  • Geometry is repeated in full on every state rather than inherited — the note at the top of each theme records that the per-state Style merge otherwise drifts the label by a pixel as the selection moves.
  • The dark blocks re-declare cn1-background-type and the border, for the reason the neighbouring dark button entries already give: the compiler doesn't carry either into the $Dark<UIID> entries.

Blast radius

Checked before touching the themes: fidelity-tests.yaml has no toggle entry, the Samples/ that use createToggle have no committed baselines, and the cn1ss harness doesn't install these themes. The change adds a previously-undefined UIID, so the only rendering it can alter is a control that had no style at all.

Guide side

The chapter's figure is regenerated from the sample that draws it, on both themes side by side, replacing the screenshot of a theme nobody ships. COVERAGE.md's two "segmented control — not started" rows now say the UIID is themed and say plainly it isn't in the fidelity suite, because no measurement backs a score for it.

Verification

Rendered both variants locally and looked at them. Guide gates: structure, xrefs, unused images, snippets, missing-code-blocks (34, unchanged), asciidoctor --failure-level WARN, asciidoctor-pdf, Vale 0/0/0, LanguageTool status: ok 0, paragraph capitalization, control characters, 16k alignment, copyright headers (4 files).

Note the committed baselines came from a local run; the four table-style figures are known to differ from CI by a few pixels of height (Java2D font-metric rounding), so if the new pair mismatches there I'll reseed from CI output.

🤖 Generated with Claude Code

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 10, 2026

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review Completed 2026-09-11T13:10:16.460733Z 21d75d6 New commits
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: ae2406c15e

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread native-themes/android-material/theme.css Outdated
Comment thread native-themes/android-material/theme.css
@shai-almog

shai-almog commented Sep 10, 2026

Copy link
Copy Markdown
Collaborator Author

Native fidelity (Android, Material 3)

54 pairs compared -- median 95.6%, worst 91.3% (FlatButton_pressed_dark), 25th pct 94.9%, mean 95.7%.

Distribution -- >=99%: 2 | 95-99%: 37 | 90-95%: 15 | <90%: 0

Component State Appearance Material Fidelity SSIM mean delta vs base Geometry
FlatButton pressed dark normal 91.3% 0.899 3.10 0.0 ok
Tabs normal light normal 92.3% 0.914 3.05 0.0 ok
Button pressed dark normal 92.6% 0.949 4.32 0.0 ok
FlatButton normal dark normal 93.2% 0.938 2.69 0.0 ok
Button disabled dark normal 93.3% 0.928 2.20 0.0 ok
Button pressed light normal 93.3% 0.952 3.68 0.0 ok
FlatButton normal light normal 93.7% 0.941 2.29 0.0 ok
FlatButton pressed light normal 93.8% 0.942 2.56 0.0 ok
FloatingActionButton pressed light normal 94.4% 0.927 2.82 0.0 OFF (h 0.88)
RadioButton normal dark normal 94.5% 0.963 2.19 0.0 ok
RadioButton normal light normal 94.7% 0.964 1.85 0.0 ok
CheckBox selected dark normal 94.7% 0.950 2.60 0.0 ok
RadioButton selected dark normal 94.8% 0.963 2.42 0.0 ok
CheckBox normal dark normal 94.9% 0.952 2.69 0.0 ok
Button normal dark normal 94.9% 0.949 3.16 0.0 ok
CheckBox normal light normal 95.0% 0.953 2.30 0.0 ok
Toolbar normal dark normal 95.1% 0.906 1.60 0.0 ok
RaisedButton pressed dark normal 95.2% 0.950 2.39 0.0 ok
CheckBox disabled dark normal 95.2% 0.954 1.47 0.0 ok
CheckBox disabled light normal 95.2% 0.956 1.34 0.0 ok
Tabs normal dark normal 95.2% 0.913 3.65 0.0 ok
RadioButton disabled dark normal 95.3% 0.963 1.18 0.0 ok
RadioButton selected light normal 95.3% 0.964 1.84 0.0 ok
CheckBox selected light normal 95.4% 0.952 2.06 0.0 ok
Switch selected light normal 95.4% 0.966 1.59 0.0 ok
Switch selected dark normal 95.5% 0.966 1.86 0.0 ok
RadioButton disabled light normal 95.5% 0.966 1.08 0.0 ok
Switch disabled dark normal 95.6% 0.961 0.85 0.0 ok
Dialog normal light normal 95.7% 0.930 2.30 0.0 ok
RaisedButton pressed light normal 95.8% 0.958 2.16 0.0 ok
Button normal light normal 95.8% 0.953 2.46 0.0 ok
Dialog normal dark normal 95.8% 0.932 2.30 0.0 ok
Switch normal light normal 96.0% 0.961 1.45 0.0 ok
FloatingActionButton normal light normal 96.1% 0.937 1.14 0.0 ok
FloatingActionButton pressed dark normal 96.2% 0.951 2.45 0.0 ok
Switch normal dark normal 96.2% 0.962 1.39 0.0 ok
TextField disabled dark normal 96.2% 0.965 0.76 0.0 ok
RaisedButton normal dark normal 96.4% 0.952 1.78 0.0 ok
Switch disabled light normal 96.4% 0.970 0.61 0.0 ok
Button disabled light normal 96.8% 0.960 1.06 0.0 ok
ProgressBar normal dark normal 96.9% 0.967 2.03 0.0 OFF (h 1.50)
RaisedButton disabled dark normal 97.0% 0.955 0.89 0.0 ok
FloatingActionButton normal dark normal 97.1% 0.952 1.43 0.0 ok
ProgressBar normal light normal 97.3% 0.974 1.53 0.0 OFF (h 1.50)
RaisedButton disabled light normal 97.3% 0.961 0.79 0.0 ok
TextField disabled light normal 97.3% 0.965 0.79 0.0 ok
RaisedButton normal light normal 97.3% 0.961 1.40 0.0 ok
TextField normal dark normal 97.6% 0.958 1.83 0.0 ok
TextField normal light normal 97.6% 0.958 1.63 0.0 ok
Slider normal dark normal 98.4% 0.990 0.87 0.0 ok
Toolbar normal light normal 98.7% 0.974 1.28 0.0 ok
Slider normal light normal 99.0% 0.991 0.47 0.0 ok
Slider disabled dark normal 99.6% 0.993 0.22 0.0 ok
Slider disabled light normal 99.6% 0.993 0.18 0.0 ok
Geometry vs native (bbox offset / size ratio / center offset / corner radius) -- gated separately from the visual score
Component State Appearance bbox dx,dy (px) w ratio h ratio center off (px) radius native->cn1 (px)
FloatingActionButton pressed light +0,+0 0.929 0.881 4.0 -
FloatingActionButton normal light +0,+0 0.946 0.912 2.9 -
Button pressed dark +0,+0 0.947 0.975 2.6 -
Button disabled dark +0,+0 0.947 0.975 2.6 -
Button pressed light +0,+0 0.947 0.975 2.6 -
Button normal dark +0,+0 0.947 0.975 2.6 -
RaisedButton pressed dark +0,+0 0.945 0.975 2.6 -
RaisedButton pressed light +0,+0 0.945 0.975 2.6 -
Button normal light +0,+0 0.947 0.975 2.6 -
RaisedButton normal dark +0,+0 0.945 0.975 2.6 -
Button disabled light +0,+0 0.947 0.975 2.6 -
RaisedButton disabled dark +0,+0 0.945 0.975 2.6 -
RaisedButton disabled light +0,+0 0.945 0.975 2.6 -
RaisedButton normal light +0,+0 0.945 0.975 2.6 -
RadioButton normal dark +1,+1 1.029 1.000 2.2 -
RadioButton normal light +1,+1 1.029 1.000 2.2 -
RadioButton selected dark +1,+1 1.029 1.000 2.2 -
RadioButton disabled dark +1,+1 1.029 1.000 2.2 -
RadioButton selected light +1,+1 1.029 1.000 2.2 -
Switch selected light +0,+0 1.080 1.063 2.2 -
RadioButton disabled light +1,+1 1.029 1.000 2.2 -
CheckBox selected dark +1,+1 1.013 1.000 1.8 -
CheckBox normal dark +1,+1 1.013 1.000 1.8 -
CheckBox normal light +1,+1 1.013 1.000 1.8 -
CheckBox disabled dark +1,+1 1.013 1.000 1.8 -
CheckBox disabled light +1,+1 1.013 1.000 1.8 -
CheckBox selected light +1,+1 1.013 1.000 1.8 -
Switch selected dark +0,+0 1.060 1.031 1.6 -
Switch disabled dark +0,-1 1.060 1.031 1.6 -
Dialog normal light +0,+0 1.007 0.982 1.6 -
Dialog normal dark +0,+0 1.007 0.982 1.6 -
Switch normal light +0,-1 1.060 1.031 1.6 -
Switch normal dark +0,-1 1.060 1.031 1.6 -
Switch disabled light +0,-1 1.060 1.031 1.6 -
FloatingActionButton pressed dark +0,+0 0.963 0.963 1.4 -
FloatingActionButton normal dark +0,+0 0.963 0.963 1.4 -
Toolbar normal light -1,+1 1.000 1.000 1.4 -
FlatButton pressed dark +0,+0 0.977 0.975 1.1 -
FlatButton normal dark +0,+0 0.977 0.975 1.1 -
FlatButton normal light +0,+0 0.977 0.975 1.1 -
FlatButton pressed light +0,+0 0.977 0.975 1.1 -
TextField normal dark +0,+0 1.015 1.036 1.1 -
TextField normal light +0,+0 1.015 1.036 1.1 -
Toolbar normal dark +0,+0 1.000 1.032 1.0 -
ProgressBar normal dark +0,+0 1.000 1.500 1.0 -
ProgressBar normal light +0,+0 1.000 1.500 1.0 -
TextField disabled dark +0,+0 1.015 1.018 0.7 -
TextField disabled light +0,+0 1.015 1.018 0.7 -
Tabs normal light +0,+1 1.000 0.984 0.5 -
Slider normal dark +1,+0 0.996 1.000 0.5 -
Slider normal light +1,+0 0.996 1.000 0.5 -
Tabs normal dark +0,+0 1.000 1.000 0.0 -
Slider disabled dark +0,+0 1.000 1.000 0.0 -
Slider disabled light +0,+0 1.000 1.000 0.0 -

Side-by-side comparisons (worst first)

  • FlatButton_pressed_dark -- 91.25% fidelity (SSIM 0.8985) (no change)

    native FlatButton_pressed_dark cn1 FlatButton_pressed_dark
    Left: native widget. Right: Codename One render.

  • Tabs_normal_light -- 92.28% fidelity (SSIM 0.9140) (no change)

    native Tabs_normal_light cn1 Tabs_normal_light
    Left: native widget. Right: Codename One render.

  • Button_pressed_dark -- 92.61% fidelity (SSIM 0.9485) (no change)

    native Button_pressed_dark cn1 Button_pressed_dark
    Left: native widget. Right: Codename One render.

  • FlatButton_normal_dark -- 93.24% fidelity (SSIM 0.9381) (no change)

    native FlatButton_normal_dark cn1 FlatButton_normal_dark
    Left: native widget. Right: Codename One render.

  • Button_disabled_dark -- 93.26% fidelity (SSIM 0.9278) (no change)

    native Button_disabled_dark cn1 Button_disabled_dark
    Left: native widget. Right: Codename One render.

  • Button_pressed_light -- 93.34% fidelity (SSIM 0.9521) (no change)

    native Button_pressed_light cn1 Button_pressed_light
    Left: native widget. Right: Codename One render.

  • FlatButton_normal_light -- 93.72% fidelity (SSIM 0.9410) (no change)

    native FlatButton_normal_light cn1 FlatButton_normal_light
    Left: native widget. Right: Codename One render.

  • FlatButton_pressed_light -- 93.77% fidelity (SSIM 0.9424) (no change)

    native FlatButton_pressed_light cn1 FlatButton_pressed_light
    Left: native widget. Right: Codename One render.

  • FloatingActionButton_pressed_light -- 94.44% fidelity (SSIM 0.9273) (no change)

    native FloatingActionButton_pressed_light cn1 FloatingActionButton_pressed_light
    Left: native widget. Right: Codename One render.

  • RadioButton_normal_dark -- 94.46% fidelity (SSIM 0.9630) (no change)

    native RadioButton_normal_dark cn1 RadioButton_normal_dark
    Left: native widget. Right: Codename One render.

  • RadioButton_normal_light -- 94.69% fidelity (SSIM 0.9643) (no change)

    native RadioButton_normal_light cn1 RadioButton_normal_light
    Left: native widget. Right: Codename One render.

  • CheckBox_selected_dark -- 94.71% fidelity (SSIM 0.9502) (no change)

    native CheckBox_selected_dark cn1 CheckBox_selected_dark
    Left: native widget. Right: Codename One render.

  • RadioButton_selected_dark -- 94.79% fidelity (SSIM 0.9630) (no change)

    native RadioButton_selected_dark cn1 RadioButton_selected_dark
    Left: native widget. Right: Codename One render.

  • CheckBox_normal_dark -- 94.93% fidelity (SSIM 0.9516) (no change)

    native CheckBox_normal_dark cn1 CheckBox_normal_dark
    Left: native widget. Right: Codename One render.

  • Button_normal_dark -- 94.94% fidelity (SSIM 0.9491) (no change)

    native Button_normal_dark cn1 Button_normal_dark
    Left: native widget. Right: Codename One render.

  • CheckBox_normal_light -- 95.03% fidelity (SSIM 0.9531) (no change)

    native CheckBox_normal_light cn1 CheckBox_normal_light
    Left: native widget. Right: Codename One render.

  • Toolbar_normal_dark -- 95.07% fidelity (SSIM 0.9059) (no change)

    native Toolbar_normal_dark cn1 Toolbar_normal_dark
    Left: native widget. Right: Codename One render.

  • RaisedButton_pressed_dark -- 95.19% fidelity (SSIM 0.9501) (no change)

    native RaisedButton_pressed_dark cn1 RaisedButton_pressed_dark
    Left: native widget. Right: Codename One render.

  • CheckBox_disabled_dark -- 95.20% fidelity (SSIM 0.9538) (no change)

    native CheckBox_disabled_dark cn1 CheckBox_disabled_dark
    Left: native widget. Right: Codename One render.

  • CheckBox_disabled_light -- 95.21% fidelity (SSIM 0.9562) (no change)

    native CheckBox_disabled_light cn1 CheckBox_disabled_light
    Left: native widget. Right: Codename One render.

  • Tabs_normal_dark -- 95.23% fidelity (SSIM 0.9125) (no change)

    native Tabs_normal_dark cn1 Tabs_normal_dark
    Left: native widget. Right: Codename One render.

  • RadioButton_disabled_dark -- 95.29% fidelity (SSIM 0.9630) (no change)

    native RadioButton_disabled_dark cn1 RadioButton_disabled_dark
    Left: native widget. Right: Codename One render.

  • RadioButton_selected_light -- 95.33% fidelity (SSIM 0.9642) (no change)

    native RadioButton_selected_light cn1 RadioButton_selected_light
    Left: native widget. Right: Codename One render.

  • CheckBox_selected_light -- 95.37% fidelity (SSIM 0.9524) (no change)

    native CheckBox_selected_light cn1 CheckBox_selected_light
    Left: native widget. Right: Codename One render.

  • Switch_selected_light -- 95.37% fidelity (SSIM 0.9658) (no change)

    native Switch_selected_light cn1 Switch_selected_light
    Left: native widget. Right: Codename One render.

  • Switch_selected_dark -- 95.45% fidelity (SSIM 0.9656) (no change)

    native Switch_selected_dark cn1 Switch_selected_dark
    Left: native widget. Right: Codename One render.

  • RadioButton_disabled_light -- 95.46% fidelity (SSIM 0.9657) (no change)

    native RadioButton_disabled_light cn1 RadioButton_disabled_light
    Left: native widget. Right: Codename One render.

  • Switch_disabled_dark -- 95.57% fidelity (SSIM 0.9613) (no change)

    native Switch_disabled_dark cn1 Switch_disabled_dark
    Left: native widget. Right: Codename One render.

  • Dialog_normal_light -- 95.65% fidelity (SSIM 0.9300) (no change)

    native Dialog_normal_light cn1 Dialog_normal_light
    Left: native widget. Right: Codename One render.

  • RaisedButton_pressed_light -- 95.78% fidelity (SSIM 0.9578) (no change)

    native RaisedButton_pressed_light cn1 RaisedButton_pressed_light
    Left: native widget. Right: Codename One render.

  • Button_normal_light -- 95.79% fidelity (SSIM 0.9528) (no change)

    native Button_normal_light cn1 Button_normal_light
    Left: native widget. Right: Codename One render.

  • Dialog_normal_dark -- 95.79% fidelity (SSIM 0.9322) (no change)

    native Dialog_normal_dark cn1 Dialog_normal_dark
    Left: native widget. Right: Codename One render.

  • Switch_normal_light -- 95.97% fidelity (SSIM 0.9612) (no change)

    native Switch_normal_light cn1 Switch_normal_light
    Left: native widget. Right: Codename One render.

  • FloatingActionButton_normal_light -- 96.09% fidelity (SSIM 0.9367) (no change)

    native FloatingActionButton_normal_light cn1 FloatingActionButton_normal_light
    Left: native widget. Right: Codename One render.

  • FloatingActionButton_pressed_dark -- 96.16% fidelity (SSIM 0.9513) (no change)

    native FloatingActionButton_pressed_dark cn1 FloatingActionButton_pressed_dark
    Left: native widget. Right: Codename One render.

  • Switch_normal_dark -- 96.17% fidelity (SSIM 0.9616) (no change)

    native Switch_normal_dark cn1 Switch_normal_dark
    Left: native widget. Right: Codename One render.

  • TextField_disabled_dark -- 96.21% fidelity (SSIM 0.9648) (no change)

    native TextField_disabled_dark cn1 TextField_disabled_dark
    Left: native widget. Right: Codename One render.

  • RaisedButton_normal_dark -- 96.41% fidelity (SSIM 0.9516) (no change)

    native RaisedButton_normal_dark cn1 RaisedButton_normal_dark
    Left: native widget. Right: Codename One render.

  • Switch_disabled_light -- 96.43% fidelity (SSIM 0.9698) (no change)

    native Switch_disabled_light cn1 Switch_disabled_light
    Left: native widget. Right: Codename One render.

  • Button_disabled_light -- 96.80% fidelity (SSIM 0.9596) (no change)

    native Button_disabled_light cn1 Button_disabled_light
    Left: native widget. Right: Codename One render.

  • ProgressBar_normal_dark -- 96.91% fidelity (SSIM 0.9669) (no change)

    native ProgressBar_normal_dark cn1 ProgressBar_normal_dark
    Left: native widget. Right: Codename One render.

  • RaisedButton_disabled_dark -- 97.02% fidelity (SSIM 0.9549) (no change)

    native RaisedButton_disabled_dark cn1 RaisedButton_disabled_dark
    Left: native widget. Right: Codename One render.

  • FloatingActionButton_normal_dark -- 97.07% fidelity (SSIM 0.9521) (no change)

    native FloatingActionButton_normal_dark cn1 FloatingActionButton_normal_dark
    Left: native widget. Right: Codename One render.

  • ProgressBar_normal_light -- 97.26% fidelity (SSIM 0.9739) (no change)

    native ProgressBar_normal_light cn1 ProgressBar_normal_light
    Left: native widget. Right: Codename One render.

  • RaisedButton_disabled_light -- 97.26% fidelity (SSIM 0.9611) (no change)

    native RaisedButton_disabled_light cn1 RaisedButton_disabled_light
    Left: native widget. Right: Codename One render.

  • TextField_disabled_light -- 97.29% fidelity (SSIM 0.9649) (no change)

    native TextField_disabled_light cn1 TextField_disabled_light
    Left: native widget. Right: Codename One render.

  • RaisedButton_normal_light -- 97.32% fidelity (SSIM 0.9613) (no change)

    native RaisedButton_normal_light cn1 RaisedButton_normal_light
    Left: native widget. Right: Codename One render.

  • TextField_normal_dark -- 97.61% fidelity (SSIM 0.9583) (no change)

    native TextField_normal_dark cn1 TextField_normal_dark
    Left: native widget. Right: Codename One render.

  • TextField_normal_light -- 97.62% fidelity (SSIM 0.9582) (no change)

    native TextField_normal_light cn1 TextField_normal_light
    Left: native widget. Right: Codename One render.

  • Slider_normal_dark -- 98.39% fidelity (SSIM 0.9900) (no change)

    native Slider_normal_dark cn1 Slider_normal_dark
    Left: native widget. Right: Codename One render.

  • Toolbar_normal_light -- 98.69% fidelity (SSIM 0.9739) (no change)

    native Toolbar_normal_light cn1 Toolbar_normal_light
    Left: native widget. Right: Codename One render.

  • Slider_normal_light -- 98.95% fidelity (SSIM 0.9908) (no change)

    native Slider_normal_light cn1 Slider_normal_light
    Left: native widget. Right: Codename One render.

  • Slider_disabled_dark -- 99.56% fidelity (SSIM 0.9927) (no change)

    native Slider_disabled_dark cn1 Slider_disabled_dark
    Left: native widget. Right: Codename One render.

  • Slider_disabled_light -- 99.59% fidelity (SSIM 0.9932) (no change)

    native Slider_disabled_light cn1 Slider_disabled_light
    Left: native widget. Right: Codename One render.

@github-actions

Copy link
Copy Markdown
Contributor

Cloudflare Preview

@github-actions

github-actions Bot commented Sep 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)
    • build-hint-catalog: 0 findings (no issues)
    • build-hint-tools: 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.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: a202cfe18f

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread native-themes/android-material/theme.css Outdated
@github-actions

github-actions Bot commented Sep 10, 2026

Copy link
Copy Markdown
Contributor

Developer Guide build artifacts are available for download from this workflow run:

Developer Guide quality checks:

  • AsciiDoc linter: No issues found (report)
  • Vale: No alerts found (report)
  • Paragraph capitalization: No paragraph capitalization issues (report)
  • LanguageTool: No grammar matches (report)
  • Image references: No unused images detected (report)

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 36ce34171f

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread native-themes/android-material/theme.css Outdated
Comment thread native-themes/android-material/theme.css Outdated

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 7edb34e4c4

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread native-themes/android-material/theme.css Outdated
Comment thread native-themes/android-material/theme.css Outdated
Comment thread native-themes/android-material/theme.css Outdated
Comment thread native-themes/android-material/theme.css Outdated

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: a82e157ee8

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread native-themes/android-material/theme.css Outdated

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 44c3478f9c

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread native-themes/android-material/theme.css Outdated
Comment thread native-themes/android-material/theme.css Outdated
Comment thread native-themes/android-material/theme.css Outdated

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 6e63432278

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread native-themes/android-material/theme.css Outdated
Comment thread native-themes/ios-modern/theme.css Outdated

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 1e20e36db1

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread native-themes/ios-modern/theme.css Outdated

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: d85e7ecdfc

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread native-themes/ios-modern/theme.css Outdated
Comment thread native-themes/android-material/theme.css Outdated

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 5bd6b2976e

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread native-themes/ios-modern/theme.css Outdated

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: a889c4ff4b

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread native-themes/android-material/theme.css Outdated
@shai-almog

shai-almog commented Sep 10, 2026

Copy link
Copy Markdown
Collaborator Author

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

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 4a69692520

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread native-themes/android-material/theme.css Outdated
@shai-almog

shai-almog commented Sep 10, 2026

Copy link
Copy Markdown
Collaborator Author

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

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 17fc52d2ad

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread native-themes/android-material/theme.css

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: de9ec003d8

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread native-themes/android-material/theme.css Outdated

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: cdfcdcab7c

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread native-themes/android-material/theme.css

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 364d35f3ae

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread native-themes/ios-modern/theme.css Outdated

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 1754b039b5

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread native-themes/android-material/theme.css

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 50f63a2c45

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread CodenameOne/src/com/codename1/ui/Button.java Outdated

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: d1cc25b766

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread CodenameOne/src/com/codename1/ui/Button.java Outdated

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: b6d2f22004

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread CodenameOne/src/com/codename1/ui/Button.java Outdated

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 1ff087588f

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread CodenameOne/src/com/codename1/ui/Button.java

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 22f499fa02

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread CodenameOne/src/com/codename1/ui/Button.java Outdated
@shai-almog

shai-almog commented Sep 10, 2026

Copy link
Copy Markdown
Collaborator Author

Compared 181 screenshots: 181 matched.
✅ JavaScript-port screenshot tests passed.

shaiblah and others added 22 commits September 11, 2026 06:16
The state audit in the previous commit only closed the light block. Its dark
pass skipped any UIID that had dark state overrides but no dark base entry, and
six did: the five media transport aliases and MenuButtonRight, each carrying a
dark pressed and disabled from an earlier round and nothing else. A dark state
that does not exist resolves to the LIGHT rule of the same name -- createStyle
falls back to the original id when no $Dark entry is found -- so focusing one of
those controls in dark mode picked up the light accent, #6750a4 against a dark
surface on Material.

All six now carry a dark base and a dark selected taken from the dark FlatButton
they derive from, and the audit is re-run over both themes: no UIID this change
adds has partial coverage in either block.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The previous audit asked the wrong question again. It looked for UIIDs with
partial dark coverage, which only finds names that already have some dark entry.
A name with no dark entry at all and a hard-coded light colour in its light
states is broken in exactly the same way and was invisible to that check: with
no $Dark rule the light one is what resolves, so PopupItem.selected painted
#1d1b20 on the dark popup and the disabled TimeSpinner labels sat at #49454f on
the dark surface -- both nearly unreadable, both reported.

The audit is now the right one: for every UIID this change adds, every state
whose light rule names a colour must have a dark rule. The 32 missing ones per
theme are generated by mapping the light body through the theme's own light-to-
dark token pairs, and any colour the map does not cover is printed rather than
passed through silently. Two came up and both are correct as they stand:
GlassTutorial's rgba(0,0,0,0.72) scrim, which is a dark overlay in both
appearances behind white text, and #000000, which only appears in dark rules
that predate this change.

Separately, MenuButtonRight no longer derives FlatButton. SideMenuBar drops that
icon-only button into the TitleArea's east slot, where a content button's
horizontal padding and pill widen the title bar and crowd the centred title. It
derives TitleCommand now, which is the compact treatment that slot expects.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Re-pointing MenuButtonRight at TitleCommand last commit meant deleting the
states that had been expanded from FlatButton, and I did not regenerate them, so
it went back to a base rule with nothing behind it -- the same defect the audit
had just closed, reintroduced by hand two commits later. Both audit scripts are
re-run here rather than the states being written out again by hand.

TODAY was derived from the wrong thing entirely. DateTimeRenderer extends
SpinnerRenderer and swaps its own UIID to TODAY for the current date, restoring
the original afterwards, so TODAY is a spinner row that happens to be today --
not a calendar cell. Deriving CalendarDate gave it different padding and
typography from the rows above and below it, and a transparent selected state
where the wheel expects its selection band, so the row changed size as it
scrolled past.

The two themes need different answers because only one of them themes the row.
iOS defines SpinnerRenderer, so TODAY derives it there and overrides nothing but
the accent colour and the weight. Android Material does not define
SpinnerRenderer at all -- its spinner rows take the default -- so styling only
today's row would make that one row the odd one out, which is the same defect
from the other side. TODAY is simply not defined in the Material theme.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Two bugs in the audit tooling, both of which hid work rather than doing it.

The driver read its UIID list from the Material theme's diff and applied it to
both files, so any name that exists in only one theme was never checked. TODAY
is now iOS-only, which is exactly how it came back with a base rule and no
states one commit after the audit reported zero gaps. The list is the union of
both diffs now.

The generator expanded cn1-derive from the base UIID's *base* rule for every
state, so TODAY.selected inherited SpinnerRenderer rather than
SpinnerRenderer.selected -- and SpinnerRenderer.selected is where the picker
wheel's selection band lives, the pill background and its margins. Today's row
would have scrolled through the wheel without the band the other rows get. The
expansion is state-aware now: X.selected takes B.selected when B defines one and
falls back to B otherwise.

While regenerating, the expanded bodies are also deduped -- a base declaration
followed by the derived UIID's override of the same property is correct CSS but
leaves a dead value in the rule, and one of those dead values was a #000000 that
kept surfacing in the unmapped-colour report and would eventually have masked a
real one.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…led itself

Both reported rules are symptoms of one bug in the generator. The palette map
was applied with a chain of str.replace calls, and Material maps #49454f to
#cac4d0 and #cac4d0 to #49454f. Run in order, the first substitution produces a
value the second turns straight back, so every on-surface-variant foreground
came out of the generator still holding its light value: the disabled TimeSpinner
labels at #49454f on the #141218 dark surface, and 35 rules like them. The map is
one simultaneous regex pass now, which is the only form that is correct when the
palette contains a swap.

The iOS half had a plain omission -- no entry for #000000, which is what Label
carries, so PopupItem's expanded states painted black text on the dark popup. I
had seen #000000 in the unmapped-colour report and dismissed it as belonging to
rules that predate this change. It did not; it was in the generated ones too.

Fixing the two values in place turned out to be the wrong instinct and I undid
it: replacing every #49454f in my dark rules also hit the 16 that were correct,
having come from a light #cac4d0, and put a light outline on the dark popup
panes. The dark side for these UIIDs is regenerated wholesale from the light
rules instead, so one rule produces it rather than a mix of hand edits and
patches.

Regenerating surfaced four more colours the map did not cover, all of them iOS
values reachable only through an expanded derive -- the picker wheel's selection
band, FlatButton's pressed fill and glass border. Each is mapped to the value the
theme's own dark block already uses for that surface. The report is empty now,
including GlassTutorial's scrim, which is declared an identity rather than left
to reappear every run: a permanently-noisy report is what let #000000 through.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The iOS selected toggle painted a hard-coded white label over a fill bound to
var(--accent-color). Both themes expose an on-accent token precisely so an app
can replace the accent at runtime through addThemeProps and have the text follow
it; the existing rules in each theme already pair the two. Hard-coding the
foreground means an app that supplies a light accent gets white on light.

Enumerating rather than fixing the reported rule found 36, evenly split. iOS: the
four ToggleButton names in selected and pressed, plus CalendarDate.selected, in
both appearances. Material had the same defect from the other direction --
ComboBoxFocus, CommandFocus and PopupFocus put a literal on-surface colour over
var(--accent-container-color), so overriding the accent container would have left
the focused row's text at the old value.

Each foreground now uses the token the theme itself pairs with that fill:
--accent-on-color over --accent-color, --accent-on-container-color over
--accent-container-color, and their dark counterparts where the theme declares
them. iOS declares one on-accent token and uses it in both appearances, which the
dark map now records as an identity so regeneration preserves it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The macOS UI suite went red on this branch: AppReviewDialog, ChatView light and
dark, ListTheme light and dark, and ValidatorLightweightPicker all differ from
their references, and master is green at the commit this branch is based on, so
the failures are mine. Those references are the project's own regression guard
for shipped screens, and 154 of the 160 tests still match, so this is not a
wholesale theme shift -- it is a handful of my definitions changing components
that already looked the way someone intended.

Two are visible in the diffs and neither is a bug fix. ListTheme's focused row
had a blue focus ring and my ListRendererFocus replaced it with a filled band,
which is a style opinion about an appearance that was working. AppReviewDialog's
command text changed because DialogCommandText, absent before, now centres and
accents it. ChatView's messages shift vertically and I could not attribute that
to a specific rule by reading the CSS -- which is the point: I was inferring
causes from test names rather than knowing them.

So the sweep is cut back to the names whose gap is demonstrable rather than
plausible: the ToggleButton family, which is the bug this PR was opened for;
FloatingHint and ErrorLabel, where the framework seeds ErrorLabel,
InputComponentAction and DescriptionLabel to derive from a UIID that nothing
defines; and TableCell and TableHeader, where the seed sets transparency and no
padding and the guide's own table figures show the difference. Twenty-nine other
names go back to being undefined.

They may well deserve styling. What they do not deserve is a sweep that changes
how shipped screens look with no before-and-after for any of them, which is what
this had become.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
ToggleButtonFirst, ToggleButtonLast and ToggleButtonOnly are written as pure
aliases -- cn1-derive: ToggleButton and nothing else -- and the dark generator
skipped exactly that shape on the reasoning that an alias inherits its base. It
does, but it inherits the LIGHT base: cn1-derive in the light block names the
light rule, and dark resolution falls back to the light entry when no $Dark one
exists. So an unselected edge segment of a group kept the light capsule on the
dark surface, #1d1b20 text on #141218 in Material.

All three now carry a dark base written out from the dark ToggleButton, and the
generator no longer treats an alias as safe to skip -- it looks for the base's
dark rule for that state and copies it.

The audit is tightened to match. It was asking whether every light rule that
names a colour has a dark counterpart, which an alias passes trivially by naming
no colour at all. It now requires all four states in both blocks for every UIID
the change adds, and reports zero.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Button.isPressedStyle() sends a checked toggle to press# when it has no focus
and to sel# when it does, and an unchecked toggle with focus lands on sel# too.
That one slot therefore says "focused" for either value, and cannot say which
value. An earlier round moved the checked fill into press#, correctly, but left
the same fill in sel# -- so on a keyboard or TV device, moving focus onto an
unchecked radio painted it exactly like the checked one beside it, and the
actual value disappeared.

sel# is now the base appearance with an accent ring: focus stays visible and
never claims a value the control does not have. The cost is that a focused
checked toggle shows the ring rather than the fill for as long as it holds
focus, which is a framework limit -- one style slot, two dimensions -- and the
lesser of the two, because it understates the focused control rather than
inventing a selection that is not there.

The figure needed the same care. Focus lands on the first focusable control, so
the sample's checked CheckBox came out ringed and looked unchecked. Trying to
render in pure touch mode did not help: shouldRenderSelection() also returns
true when the last interaction was a keypad. The figure focuses an unchecked
control instead, so the two filled rows read as checked and the ring reads as
focus, and the caption says which is which.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The focus ring changed both toggle figures, and the copies committed with it
were local renders. Java2D rounds font metrics differently here than on the
runner, so those never match CI byte for byte. Both are replaced with the
run's own artifact, and all 48 committed figures now match that run exactly,
none of them leaning on a tolerance sidecar.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The review is right that a focused checked toggle is now indistinguishable from
a focused unchecked one, and its own conclusion is the answer: preserving both
needs separate state resolution, which is not something a theme can express.
sel# is one slot and focus plus value are two dimensions, so every CSS
assignment loses one of them.

The two losses are not equal, and the choice between them is now written where
the next person will meet it rather than in a review thread. Putting the checked
fill in sel# makes a focused unchecked control claim to be selected -- arrowing
through a radio group lights up every control in turn. Putting the base plus a
ring there makes a focused checked control understate itself while it holds
focus. The second never asserts a value the control does not have, so it stays,
and the comment says explicitly not to move the fill back, because that trade
has been made once already in this branch.

The real fix belongs in Component.getStyle(): resolve focus separately from
state so a checked control keeps press# and focus is drawn some other way. That
is a framework change with its own blast radius, not a line in a theme.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…ts base

The disabled rules on FloatingHint and ErrorLabel carried a foreground and
nothing else. createStyle does not fill the rest in from the UIID's base -- a
state entry with no derivation is built from the global default style -- so
disabling a TextComponent dropped the hint's font size, padding and margin and
resized the field's layout as it went.

The generated states did not have this problem because they were expanded from
the base when they were written. These four were hand-written earlier in the
branch, and the audit only asked whether a state rule exists. Existing is not
the same as complete, which is the third distinct way this branch has managed
to report zero: a missing state, a state missing in only one theme, and now a
state that is present but says almost nothing.

Every state rule of every UIID this change adds is now merged against its base:
the base's declarations first, the state's own after, so a deliberate override
still wins. ToggleButton's accent ring survives that merge, which is the case
worth checking, and the audit is extended from "does the state exist" to "does
it carry its base's property set".

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Component.getStyle() returns getDisabledStyle() before it ever consults
isPressedStyle(), so a disabled toggle resolves dis# whether it is checked or
not. The review is right that disabling a form therefore hides which of its
toggles were on, and right again that a theme cannot fix it: dis# is one slot
and it is being asked to carry both enabled-ness and value.

The answer is the one already recorded for sel#, so it is recorded in the same
place rather than argued twice. Muting the unselected appearance declines to
state a value; muting the checked fill would state a wrong one on every
disabled unchecked control. Declining is the lesser failure.

Both cases want the same framework change and neither is patched here:
getStyle() would have to combine state with focus and with enabled, rather than
collapsing three dimensions onto one lookup, and that reaches every focusable
component in the framework.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
iOS dark FloatingHint and ErrorLabel came out at #38383a, which measures 1.45:1
against the theme's #1c1c1e surface -- the number in the review, and unreadable.
The cause is a limit of the light-to-dark table: the light value it came from,
#c6c6c8, is both a separator and a disabled label in this theme, and the table
maps by value, so a colour with two roles gets whichever mapping was written for
the other one.

#636366 is systemGray2 in dark and measures 2.84:1, nearly double, while staying
clearly below the #8e8e93 the enabled hint uses, so disabled still reads as
disabled. The table entry is corrected too, since no separator among these UIIDs
uses that light value any more.

Material was checked rather than assumed: its dark disabled sits at 2.73:1, the
same band, and matches the convention its own Button.disabled already sets, so
it is left alone.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Defining ToggleButton makes two long-standing bugs in Button.setToggle visible,
which is what the review is pointing at, so they are fixed with it.

setToggle(false) never restored the UIID. A CheckBox or RadioButton taken back
out of toggle mode kept ToggleButton, so it painted the toggle's pill and
padding while drawing its own state glyph again. Invisible while nothing defined
ToggleButton; not invisible now. The UIID it had before entering toggle mode is
remembered and put back, and setUIID clears preferredSize, so the size computed
without the glyph is recalculated on the next pass.

The condition was also mis-parenthesised: && binds tighter than ||, so

    if (toggle && "CheckBox".equals(getUIID()) || "RadioButton".equals(getUIID()))

reads (toggle && isCheckBox) || isRadioButton, and leaving toggle mode assigned
ToggleButton whenever the UIID was RadioButton at that moment -- the exact
opposite of the intent.

Both are covered by tests, and the tests were checked against the old code
rather than assumed: with the original condition restored, both fail. The first
version of the precedence test did not, because a control that was never a
toggle returns at setToggle's equality guard and never reaches the condition, so
asserting on a fresh RadioButton proved nothing. It now enters toggle mode and
sets its own UIID first, which is the path an application takes to get there.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
check-copyright-headers went red on RadioButtonTest. The file never had a
header, and neither does CheckBoxTest beside it, but the gate only looks at
files a PR touches -- so adding tests to it brought a pre-existing omission into
scope. It takes the Codename One GPLv2 with Classpath Exception header, not the
Oracle one, since the file is ours.

CheckBoxTest is left alone: it is not part of this change, and fixing it here
would be unrelated churn in a PR that has already grown once.

The gate was not re-run after the core commit, which is why CI found this rather
than I did.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
setHorizontal(true) on a ComponentGroup sets its element prefix to ToggleButton,
so its members are renamed ToggleButtonFirst, ToggleButton and ToggleButtonLast
-- or ToggleButtonOnly when there is one. The guard added with the restore
matched the bare name alone, so setToggle(false) silently skipped the restore
for any grouped toggle, which is the common case for the control this whole
change is about.

Worse, the group records the UIID it replaced in $origUIID and puts that back
when the control leaves it, so a control that had quietly failed to restore then
had ToggleButton reinstated on the way out. setToggle now updates $origUIID
alongside the UIID when one is present, so the group's own restore agrees with
the control's state.

Covered by a test that builds the horizontal group, checks the rename landed,
disables toggle mode and then removes the control -- and the test was probed by
narrowing the guard back to the bare name, which fails it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…erms

A vertical ComponentGroup keeps the default prefix, so its members are renamed
GroupElement, GroupElementFirst, GroupElementLast or GroupElementOnly -- names
with nothing toggle-ish about them -- while $origUIID still holds ToggleButton.
The previous guard matched the live UIID against a list of toggle names, so it
skipped that case entirely, and removing the control afterwards put ToggleButton
back on a plain radio.

Enumerating GroupElement* as well would have been the third revision of the same
list, so the list is no longer the mechanism. The toggle UIID can be held in two
places and each is now corrected on its own terms: the live UIID when it is
still a toggle name, and $origUIID when it holds one, whatever prefix the group
happens to use. Neither depends on knowing the group's orientation.

Both orientations are covered by tests, and each was probed against the
revision it fixes: the vertical test fails against the live-UIID-only guard this
commit replaces.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
A horizontal ComponentGroup renames every member -- toggle or not -- so the bar
gets its segmented edges, and it does not re-apply that when a member's UIID
changes underneath it. Resetting the live UIID on setToggle(false) therefore
stripped that member's styling for as long as it took some later structural or
theme update to put it back. Only $origUIID needs correcting while the control
is grouped; the restore then happens where it always happens, on removal.

$origUIID is not the test for "is grouped", which is the trap here: ComponentGroup
sets it on the first rename and never clears it, not even in
removeComponentImpl, so it means "was grouped at some point". The parent answers
the question directly and is used instead.

The horizontal test now asserts the group's alias survives setToggle(false) and
that removal yields the plain UIID, and it was probed against the previous
revision -- which resets the live UIID -- where it fails.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Only one direction was doing it. Leaving toggle mode moved $origUIID from
ToggleButton back to the plain UIID; entering it looked at the live UIID, saw a
group alias, recognised nothing and left $origUIID holding the plain name. So a
control toggled off and on again inside a group was handed a radio UIID and its
glyph on the way out -- the same defect as before, from the other side.

Both directions now treat the two holders the same way. The live UIID is
converted when it is a name this method owns, and the group's saved name is
converted whenever it is out of step with the mode, in either direction. That
also covers a plain radio added to a horizontal group and toggled afterwards,
which never passed through the bare-name branch at all.

Probed against the previous revision, where the round-trip test fails.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Three macOS screenshot tests kept failing on screens that use none of the UIIDs
this change adds -- ChatView in both appearances and ValidatorLightweightPicker.
Narrowing the theme from 58 UIIDs to 8 did not move them by a single pixel: both
runs produced byte-identical images, which is what finally ruled the CSS out.

build-native-themes.sh prefers maven/css-compiler/target and falls back to
~/.m2. I never built the module, so every theme build in this branch used a jar
some other checkout installed at some other commit. That jar does not merely
compile the rules that changed -- it re-emits the entire theme its own way. The
proof: compiling MASTER's own CSS with it produces a .res of 178627 bytes where
master has 178705 committed; compiling the same CSS with the jar built from this
tree reproduces master's file byte for byte.

So the committed .res carried a compiler's worth of unrelated differences, and
those are what moved the two screens. Rebuilt with the jar from this tree, the
theme now differs from master only in the UIIDs this change adds -- 658 entries
added, none removed, all of them ToggleButton*, FloatingHint, ErrorLabel,
TableCell and TableHeader -- and the guide's other 46 figures are untouched.

The script logs which jar it picked and I did not read it, but a silent fallback
that changes output this much deserves more than a log line, so it now warns
when the jar comes from ~/.m2 and says how to build the matching one.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Two findings, both correct, both from the same wrong assumption in the previous
commit: that a ComponentGroup parent means the group has taken the UIID.

It has not when the group is inactive. updateUIIDs() returns without renaming
anything unless ComponentGroupBool is on or the group is forced, and that
constant is off by default -- Android Material never sets it, only the iOS theme
does. A toggle in such a group still carries its own ToggleButton UIID, so
skipping the restore because the parent happened to be a group left a plain radio
wearing the toggle style. The test is now whether the group recorded what it
replaced, which is the thing that says it took ownership.

And the grouped path was converting any saved UIID, not just the defaults.
setToggle has only ever converted CheckBox and RadioButton -- an application that
assigned its own UIID keeps it, which is what the original condition did and what
the guide says. My version replaced a custom name with ToggleButton in the
group's record, so removal handed back ToggleButton instead of the application's
UIID. Only the two defaults are converted now.

Both are covered, and both tests fail against the previous revision's guards.

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

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 5128dcd526

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread CodenameOne/src/com/codename1/ui/Button.java Outdated
…moved

$origUIID outlives the ownership it records. refreshTheme with grouping switched
off calls restoreUIID, which hands the live UIID back to the control and leaves
that property set, so reading it as "the group owns this" kept answering yes
after the group had let go -- and setToggle(false) then skipped the restore,
leaving a plain radio painted as a toggle. I had noted that this property is
never cleared two commits before I started using it as the ownership test.

ComponentGroup now answers the question itself, with the same condition
updateUIIDs uses to decide whether to rename at all, so there is one definition
of "grouping is active" rather than a marker that approximates it. Covered by a
test that switches grouping off, refreshes, and then disables toggle mode; it
fails against the marker-based predicate.

The macOS ValidatorLightweightPicker golden is reseeded from the run's own
artifact. That screen builds PickerComponent.createDate(...).label("Birthdate"),
and a PickerComponent is an InputComponent whose label is a FloatingHint -- the
UIID that had been deriving from nothing -- so giving it a real style moves the
row and everything under it. The change is intended and the reference was stale.

Only that one golden moved. The same test has references for ten other ports,
and the iOS, JavaScript and Mac Catalyst suites all ran it on this branch and
matched, so the shift is specific to the AppKit port's layout of that row rather
than to the theme in general.

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

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 21d75d61ac

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +195 to +196
boolean isGroupingActive() {
return getUIManager().isThemeConstant(groupFlag, false) || forceGroup;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Track actual group UIID ownership

When setForceGroup(true) is called after a toggle has already been added to an inactive group, the setter only flips forceGroup and never calls updateUIIDs(), so the child still owns its live ToggleButton UIID and has no $origUIID; this method nevertheless reports that the group owns it. A subsequent setToggle(false) therefore skips restoring RadioButton/CheckBox, and removing the component leaves the non-toggle control permanently styled as ToggleButton. Fresh evidence beyond the earlier ownership comment is that the final setForceGroup() implementation does not apply the group aliases, so ownership must track an actual rename (or activation must update the members), not just the configuration flag.

Useful? React with 👍 / 👎.

@shai-almog

shai-almog commented Sep 11, 2026

Copy link
Copy Markdown
Collaborator Author

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

Benchmark Results

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

Detailed Performance Metrics

Metric Duration
SIMD kernel backend SSE2 (x64) / NEON (arm64) native kernels
SIMD int-add (64K x300) java 57ms / native 3ms = 19.0x speedup
SIMD float-mul (64K x300) java 55ms / native 3ms = 18.3x speedup
SIMD kernel correctness PASS (native result == scalar reference)
Base64 native bridge unavailable (CN1 + SIMD + image benchmarks only)
Base64 payload size 8192 bytes
Base64 benchmark iterations 6000
Base64 SIMD byte path active (NEON-accelerated)
Base64 CN1 encode 185.000 ms
Base64 CN1 decode 93.000 ms
Image encode benchmark iterations 100
Image createMask (SIMD off) 6.000 ms
Image createMask (SIMD on) 2.000 ms
Image createMask ratio (SIMD on/off) 0.333x (66.7% faster)
Image applyMask (SIMD off) 35.000 ms
Image applyMask (SIMD on) 38.000 ms
Image applyMask ratio (SIMD on/off) 1.086x (8.6% slower)
Image modifyAlpha (SIMD off) 31.000 ms
Image modifyAlpha (SIMD on) 23.000 ms
Image modifyAlpha ratio (SIMD on/off) 0.742x (25.8% faster)
Image modifyAlpha removeColor (SIMD off) 30.000 ms
Image modifyAlpha removeColor (SIMD on) 23.000 ms
Image modifyAlpha removeColor ratio (SIMD on/off) 0.767x (23.3% faster)

@shai-almog
shai-almog merged commit 9338ff0 into master Sep 12, 2026
45 checks passed
@shai-almog
shai-almog deleted the devguide-io-figures branch September 12, 2026 07:50
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.

2 participants