Skip to content

Native desktop windows - #5556

Open
shai-almog wants to merge 291 commits into
masterfrom
feat-desktop-windows
Open

Native desktop windows#5556
shai-almog wants to merge 291 commits into
masterfrom
feat-desktop-windows

Conversation

@shai-almog

Copy link
Copy Markdown
Collaborator

Codename One has no windowing API. Even on JavaSE, Mac, Win32 and Linux, where the OS has real windows, an app gets exactly one, welded to a single global "current Form": CodenameOneImplementation holds one currentForm, Display.edtLoopImpl paints one surface per tick, paintDirty uses one global paint queue clipped to getDisplayWidth()/getDisplayHeight(), and handleEvent routes every input event to one form. Everything that looks like a second window today — Sheet, InteractionDialog, ToastBar, Dialog — is an overlay inside the current form's layered panes.

This adds real native windows, each rendering its own component tree, on all four desktop targets, without changing the single-form model mobile depends on.

API

TopLevelContainer is the shared contract Form and Window both implement. Its members were chosen by counting actual getComponentForm().<method>() chains in CodenameOne/src, and every one of them was already public on Form with an identical signature, so Form needed nothing beyond the implements clause and asContainer() — a Java interface cannot extend a class, so without that bridge a TopLevelContainer reference cannot go anywhere a Component is wanted.

Window extends Container implements TopLevelContainer. Inside a window getComponentForm() returns null, by design; Component.getTopLevelContainer() is the new resolution API, and core now uses it internally. Desktop and Monitor are the public parallel to Display for "what screens exist and what windows are open", including per-monitor DPI and backing scale; Display keeps meaning "the main app surface" exactly as before.

Modality is enforced in core rather than per port, so it behaves identically everywhere: Display keeps a modal stack and handleEvent drops input to blocked windows. showModal() parks the caller through invokeAndBlock the way Dialog already does, which re-enters the event loop — so every other window stays live and repainting while a modal is up.

Implementation

The impl SPI is a single WindowManager facade returned from CodenameOneImplementation.getWindowManager(). Returning null is the capability query, so there is no separate isMultiWindowSupported() that could drift from it. Only genuinely universal operations are abstract; anything a port might not offer has a no-op default, so adding a capability later never breaks a port.

Per-window paint state moves into a PaintSurface value object with the main window as instance zero; getCodenameOneGraphics(), repaint(Animation), cancelRepaint and hasPendingPaints() keep their signatures, so every existing port still compiles and behaves. paintDirty()'s body is parameterized rather than globally rebound — a global "active surface" was rejected because Display.getDisplayWidth() is public and callable off the EDT, so a live binding would change its answer re-entrantly across ~210 call sites.

Events pack the window id into the type word (type | (windowId << 8)). Window 0 is numerically identical to the previous wire format, so drag coalescing and the stack-swap logic are untouched. The port is handed the id at creation and echoes it back, so there is no peer-to-window map on the off-EDT input path.

Ports: JavaSE (per-canvas graphics de-singletonization — getNativeGraphics used to return one shared instance, and isScreenGraphics was an identity check against one buffer, so a second window would have drawn into the first window's pixels), native Windows (Direct2D per-window render targets, GWLP_USERDATA identity, WM_DPICHANGED), native Linux (per-window cairo back buffer, GTK closure data), and Mac Catalyst (UIWindowScene per window). Peer components and native text editing work in every window on every one of the four. iOS, Android and JavaScript need no port changes at all: they inherit the false capability and the throw lives in core.

Latent bug fixed on the way

handleEvent returned offset unchanged when the form was null, while the caller loops while (offset < actualTmpPointer) — an infinite EDT spin. It is unreachable today only because all nine entry points guard on getCurrentForm() != null; window disposal with events in flight makes it reachable. It is now a skipEvent that drains the packet so the rest of the batch still dispatches.

Testing

Core unit tests drive a scriptable fake WindowManager on TestCodenameOneImplementation — settable, defaulting to null, so the unsupported path is the default — covering lifecycle, paint isolation, event routing, modality including a modal window nested in a modal dialog, the TopLevelContainer contract, and a fake multi-monitor table at mixed DPI. JavaSE port tests cover the per-canvas graphics resolution, which is the riskiest edit here and had no coverage before.

The centrepiece is a windowed screenshot family in scripts/hellocodenameone: representative UI re-run inside a real window at several sizes and compared against its own goldens. A picture of a window proves nothing; layout, scrolling, graphics, layered overlays, native editing and modality rendering correctly on a non-primary surface is the actual claim. The three sizes, including a deliberately non-square one, are what prove content lays out to the window rather than to Display.getDisplayWidth(). This needed per-window capture on every port, since the existing pipeline can only see the main framebuffer.

Mac Catalyst was built and run on real hardware for this branch rather than left to CI, because it is the hardest of the four. That found four defects compiling never would have: capture() was unimplemented; the readiness probe was a false positive; captures were taken before the first paint; and the scene was never asked for the geometry the window was created with, so several captures came out at the main display size with the window's content in the corner.

Known scope limits, documented

HTMLComponent, accessibility on secondary windows, Dialog.show() from inside a window and form transitions into or out of one are out of scope for v1 and called out in the guide. Display.getDisplayWidth()/getDisplayHeight() keep reporting the main window; components inside a window use their top level's size.

🤖 Generated with Claude Code

@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

f.keyPressed(inputEventStackTmp[offset]);

P1 Badge Dispatch key events to the window's focused component

When f is a Window, this invokes the inherited Container.keyPressed(), because Window does not override the key handlers. That implementation only forwards to a container lead component, so ordinary focused controls receive no physical-key input, focus traversal never runs, and the listeners stored by Window.addKeyListener() are never fired. Window needs form-equivalent key pressed, released, repeated, and long-press dispatch.

ℹ️ 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/Window.java Outdated
Comment thread CodenameOne/src/com/codename1/ui/Window.java Outdated
Comment thread Ports/WindowsPort/src/com/codename1/impl/windows/WindowsWindowManager.java Outdated
Comment thread CodenameOne/src/com/codename1/ui/Window.java Outdated
Comment thread CodenameOne/src/com/codename1/ui/Window.java Outdated
Comment thread CodenameOne/src/com/codename1/ui/Window.java
Comment thread CodenameOne/src/com/codename1/ui/Window.java
@shai-almog

shai-almog commented Aug 17, 2026

Copy link
Copy Markdown
Collaborator Author

Compared 12 screenshots: 12 matched.
✅ JavaSE simulator integration screenshots matched stored baselines.

@github-actions

github-actions Bot commented Aug 17, 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.

@github-actions

Copy link
Copy Markdown
Contributor

Cloudflare Preview

@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: 338ee1a6f1

ℹ️ 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 Ports/LinuxPort/nativeSources/cn1_linux_desktopwindow.c Outdated
Comment thread Ports/JavaSE/src/com/codename1/impl/javase/JavaSEPort.java Outdated
Comment thread CodenameOne/src/com/codename1/ui/Window.java Outdated
Comment thread CodenameOne/src/com/codename1/ui/Window.java Outdated
Comment thread CodenameOne/src/com/codename1/ui/Window.java
@github-actions

github-actions Bot commented Aug 17, 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)

@shai-almog

shai-almog commented Aug 17, 2026

Copy link
Copy Markdown
Collaborator Author

Compared 151 screenshots: 151 matched.

Native Android coverage

  • 📊 Line coverage: 9.03% (8944/99009 lines covered) [HTML preview] (artifact android-coverage-report, jacocoAndroidReport/html/index.html)
    • Other counters: instruction 8.83% (46223/523333), branch 3.45% (1709/49575), complexity 3.45% (1826/52883), method 5.30% (1476/27827), class 10.67% (397/3720)
    • Lowest covered classes
      • kotlin.collections.kotlin.collections.ArraysKt___ArraysKt – 0.00% (0/6367 lines covered)
      • kotlin.collections.unsigned.kotlin.collections.unsigned.UArraysKt___UArraysKt – 0.00% (0/2384 lines covered)
      • org.jacoco.agent.rt.internal_0e20598.asm.org.jacoco.agent.rt.internal_0e20598.asm.ClassReader – 0.00% (0/1524 lines covered)
      • kotlin.collections.kotlin.collections.CollectionsKt___CollectionsKt – 0.00% (0/1187 lines covered)
      • org.jacoco.agent.rt.internal_0e20598.asm.org.jacoco.agent.rt.internal_0e20598.asm.MethodWriter – 0.00% (0/922 lines covered)
      • kotlin.sequences.kotlin.sequences.SequencesKt___SequencesKt – 0.00% (0/736 lines covered)
      • com.google.common.cache.com.google.common.cache.LocalCache$Segment – 0.00% (0/726 lines covered)
      • okio.okio.Buffer – 0.00% (0/687 lines covered)
      • kotlin.text.kotlin.text.StringsKt___StringsKt – 0.00% (0/625 lines covered)
      • org.jacoco.agent.rt.internal_0e20598.asm.org.jacoco.agent.rt.internal_0e20598.asm.Frame – 0.00% (0/570 lines covered)

✅ Native Android screenshot tests passed.

Native Android coverage

  • 📊 Line coverage: 9.03% (8944/99009 lines covered) [HTML preview] (artifact android-coverage-report, jacocoAndroidReport/html/index.html)
    • Other counters: instruction 8.83% (46223/523333), branch 3.45% (1709/49575), complexity 3.45% (1826/52883), method 5.30% (1476/27827), class 10.67% (397/3720)
    • Lowest covered classes
      • kotlin.collections.kotlin.collections.ArraysKt___ArraysKt – 0.00% (0/6367 lines covered)
      • kotlin.collections.unsigned.kotlin.collections.unsigned.UArraysKt___UArraysKt – 0.00% (0/2384 lines covered)
      • org.jacoco.agent.rt.internal_0e20598.asm.org.jacoco.agent.rt.internal_0e20598.asm.ClassReader – 0.00% (0/1524 lines covered)
      • kotlin.collections.kotlin.collections.CollectionsKt___CollectionsKt – 0.00% (0/1187 lines covered)
      • org.jacoco.agent.rt.internal_0e20598.asm.org.jacoco.agent.rt.internal_0e20598.asm.MethodWriter – 0.00% (0/922 lines covered)
      • kotlin.sequences.kotlin.sequences.SequencesKt___SequencesKt – 0.00% (0/736 lines covered)
      • com.google.common.cache.com.google.common.cache.LocalCache$Segment – 0.00% (0/726 lines covered)
      • okio.okio.Buffer – 0.00% (0/687 lines covered)
      • kotlin.text.kotlin.text.StringsKt___StringsKt – 0.00% (0/625 lines covered)
      • org.jacoco.agent.rt.internal_0e20598.asm.org.jacoco.agent.rt.internal_0e20598.asm.Frame – 0.00% (0/570 lines covered)

Benchmark Results

Detailed Performance Metrics

Metric Duration
SIMD kernel backend scalar fallback (no native SIMD)
SIMD int-add (64K x300) java 150ms / native 134ms = 1.1x speedup
SIMD float-mul (64K x300) java 107ms / native 65ms = 1.6x 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 94.000 ms
Base64 CN1 decode 84.000 ms
Base64 native encode 430.000 ms
Base64 encode ratio (CN1/native) 0.219x (78.1% faster)
Base64 native decode 270.000 ms
Base64 decode ratio (CN1/native) 0.311x (68.9% faster)
Image encode benchmark status skipped (SIMD unsupported)

@shai-almog

shai-almog commented Aug 17, 2026

Copy link
Copy Markdown
Collaborator Author

Compared 163 screenshots: 163 matched.
Native Windows port (x64 / Intel-AMD): full hellocodenameone screenshot suite rendered offscreen with Direct2D/DirectWrite, plus the real benchmarks (base64 native/CN1/SIMD, image createMask/applyMask/modifyAlpha/PNG/JPEG, SSE2 SIMD kernels). Compared against the in-repo baseline in scripts/windows/screenshots.

Benchmark Results

Detailed Performance Metrics

Metric Duration
SIMD kernel backend SSE2 (x64) / NEON (arm64) native kernels
SIMD int-add (64K x300) java 59ms / native 4ms = 14.7x speedup
SIMD float-mul (64K x300) java 61ms / native 4ms = 15.2x 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 gated to scalar (CPU autovectorizes scalar; explicit SIMD not beneficial here)
Base64 CN1 encode 175.000 ms
Base64 CN1 decode 120.000 ms
Base64 SIMD encode 93.000 ms
Base64 encode ratio (SIMD/CN1) 0.531x (46.9% faster)
Base64 SIMD decode 91.000 ms
Base64 decode ratio (SIMD/CN1) 0.758x (24.2% faster)
Image encode benchmark iterations 100
Image createMask (SIMD off) 37.000 ms
Image createMask (SIMD on) 24.000 ms
Image createMask ratio (SIMD on/off) 0.649x (35.1% faster)
Image applyMask (SIMD off) 69.000 ms
Image applyMask (SIMD on) 47.000 ms
Image applyMask ratio (SIMD on/off) 0.681x (31.9% faster)
Image modifyAlpha (SIMD off) 49.000 ms
Image modifyAlpha (SIMD on) 377.000 ms
Image modifyAlpha ratio (SIMD on/off) 7.694x (669.4% slower)
Image modifyAlpha removeColor (SIMD off) 88.000 ms
Image modifyAlpha removeColor (SIMD on) 60.000 ms
Image modifyAlpha removeColor ratio (SIMD on/off) 0.682x (31.8% faster)

@shai-almog

shai-almog commented Aug 17, 2026

Copy link
Copy Markdown
Collaborator Author

Compared 163 screenshots: 163 matched.
Native Windows port (arm64 / Apple Silicon - Arm): full hellocodenameone screenshot suite rendered offscreen with Direct2D/DirectWrite, plus the real benchmarks (base64 native/CN1/SIMD, image createMask/applyMask/modifyAlpha/PNG/JPEG, NEON SIMD kernels). Compared against the in-repo baseline in scripts/windows/screenshots.

Benchmark Results

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 56ms / native 4ms = 14.0x 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 gated to scalar (CPU autovectorizes scalar; explicit SIMD not beneficial here)
Base64 CN1 encode 266.000 ms
Base64 CN1 decode 155.000 ms
Base64 SIMD encode 64.000 ms
Base64 encode ratio (SIMD/CN1) 0.241x (75.9% faster)
Base64 SIMD decode 63.000 ms
Base64 decode ratio (SIMD/CN1) 0.406x (59.4% faster)
Image encode benchmark iterations 100
Image createMask (SIMD off) 12.000 ms
Image createMask (SIMD on) 7.000 ms
Image createMask ratio (SIMD on/off) 0.583x (41.7% faster)
Image applyMask (SIMD off) 23.000 ms
Image applyMask (SIMD on) 18.000 ms
Image applyMask ratio (SIMD on/off) 0.783x (21.7% faster)
Image modifyAlpha (SIMD off) 16.000 ms
Image modifyAlpha (SIMD on) 12.000 ms
Image modifyAlpha ratio (SIMD on/off) 0.750x (25.0% faster)
Image modifyAlpha removeColor (SIMD off) 169.000 ms
Image modifyAlpha removeColor (SIMD on) 11.000 ms
Image modifyAlpha removeColor ratio (SIMD on/off) 0.065x (93.5% faster)

@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: 13311a9bed

ℹ️ 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/Display.java
Comment thread CodenameOne/src/com/codename1/ui/Window.java
Comment thread Ports/WindowsPort/src/com/codename1/impl/windows/WindowsWindowManager.java Outdated
Comment thread Ports/WindowsPort/src/com/codename1/impl/windows/WindowsWindowManager.java Outdated
Comment thread CodenameOne/src/com/codename1/ui/Window.java
Comment thread Ports/JavaSE/src/com/codename1/impl/javase/JavaSEWindowManager.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: 1670ca0579

ℹ️ 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/Display.java Outdated
Comment thread CodenameOne/src/com/codename1/ui/Window.java
Comment thread docs/developer-guide/Desktop-Windows.asciidoc 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: bea352c1ee

ℹ️ 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 Ports/JavaSE/src/com/codename1/impl/javase/JavaSEPort.java
Comment thread Ports/JavaSE/src/com/codename1/impl/javase/JavaSEPort.java
Comment thread CodenameOne/src/com/codename1/ui/Window.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: c7832a8ae0

ℹ️ 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/Window.java Outdated
Comment thread CodenameOne/src/com/codename1/ui/Window.java
Comment thread Ports/JavaSE/src/com/codename1/impl/javase/JavaSEPort.java Outdated
Comment thread CodenameOne/src/com/codename1/ui/Window.java Outdated
@shai-almog

shai-almog commented Aug 17, 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: bc45217f4f

ℹ️ 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/Window.java Outdated
Comment thread Ports/JavaSE/src/com/codename1/impl/javase/JavaSEPort.java
Comment thread CodenameOne/src/com/codename1/ui/Window.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: fc3370ca1b

ℹ️ 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 Ports/iOSPort/nativeSources/CN1MacWindows.m Outdated
Comment thread Ports/iOSPort/nativeSources/CN1MacWindows.m
Comment thread Ports/JavaSE/src/com/codename1/impl/javase/JavaSEPort.java
Comment thread Ports/LinuxPort/nativeSources/cn1_linux_desktopwindow.c Outdated
@shai-almog

shai-almog commented Aug 17, 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: 259c862b4b

ℹ️ 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/Window.java
Comment thread Ports/JavaSE/src/com/codename1/impl/javase/JavaSEWindowManager.java
Comment thread Ports/JavaSE/src/com/codename1/impl/javase/JavaSEWindowManager.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: a128cc7ff2

ℹ️ 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 Ports/JavaSE/src/com/codename1/impl/javase/JavaSEWindowManager.java Outdated
Comment thread Ports/WindowsPort/nativeSources/cn1_windows_desktopwindow.cpp
Comment thread CodenameOne/src/com/codename1/ui/Window.java Outdated
Comment thread CodenameOne/src/com/codename1/ui/Window.java Outdated
@shai-almog

shai-almog commented Aug 17, 2026

Copy link
Copy Markdown
Collaborator Author

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

Form implements TopLevelContainer now, so 43 of its methods override an
interface method and the MissingOverride rule wants them annotated.

The volatile rule has no exception list, so the two volatile fields are
gone rather than suppressed: disposed is published and read under
Display.lock, which is the monitor showModal already parks on, and
paintedOnce is written and read only on the event dispatch thread.

Two index loops are deliberate -- a window can be disposed part way
through a nested event loop, and an animation can deregister itself while
it is being iterated -- and are marked, matching the existing NOPMD in
Form.loopAnimations. The owned-window loop had no such reason and is a
foreach now.

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: 47e0aa010f

ℹ️ 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/Window.java
Comment thread CodenameOne/src/com/codename1/ui/Window.java
Comment thread CodenameOne/src/com/codename1/ui/Window.java
Comment thread Ports/JavaSE/src/com/codename1/impl/javase/JavaSEWindowManager.java
Comment thread Ports/iOSPort/nativeSources/CN1MacWindows.m Outdated
Comment thread CodenameOne/src/com/codename1/ui/Display.java Outdated
Comment thread CodenameOne/src/com/codename1/ui/Window.java Outdated
@shai-almog

shai-almog commented Aug 17, 2026

Copy link
Copy Markdown
Collaborator Author

Compared 163 screenshots: 163 matched.
Native Windows port, REAL shipping pipeline: the hellocodenameone screenshot suite rendered by a binary CROSS-COMPILED on Linux (clang-cl + xwin, WebView2 linked) and RUN on a Windows x64 runner. Compared against the in-repo baseline in scripts/windows/screenshots.

Benchmark Results

Detailed Performance Metrics

Metric Duration
SIMD kernel backend SSE2 (x64) / NEON (arm64) native kernels
SIMD int-add (64K x300) java 59ms / native 4ms = 14.7x speedup
SIMD float-mul (64K x300) java 69ms / native 7ms = 9.8x 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 gated to scalar (CPU autovectorizes scalar; explicit SIMD not beneficial here)
Base64 CN1 encode 170.000 ms
Base64 CN1 decode 123.000 ms
Base64 SIMD encode 91.000 ms
Base64 encode ratio (SIMD/CN1) 0.535x (46.5% faster)
Base64 SIMD decode 95.000 ms
Base64 decode ratio (SIMD/CN1) 0.772x (22.8% faster)
Image encode benchmark iterations 100
Image createMask (SIMD off) 27.000 ms
Image createMask (SIMD on) 21.000 ms
Image createMask ratio (SIMD on/off) 0.778x (22.2% faster)
Image applyMask (SIMD off) 52.000 ms
Image applyMask (SIMD on) 49.000 ms
Image applyMask ratio (SIMD on/off) 0.942x (5.8% faster)
Image modifyAlpha (SIMD off) 281.000 ms
Image modifyAlpha (SIMD on) 49.000 ms
Image modifyAlpha ratio (SIMD on/off) 0.174x (82.6% faster)
Image modifyAlpha removeColor (SIMD off) 57.000 ms
Image modifyAlpha removeColor (SIMD on) 48.000 ms
Image modifyAlpha removeColor ratio (SIMD on/off) 0.842x (15.8% faster)

@shai-almog

shai-almog commented Aug 17, 2026

Copy link
Copy Markdown
Collaborator Author

Compared 163 screenshots: 163 matched.
Native Linux port (x64), GTK3/Cairo/Pango, ParparVM bytecode-to-C (no JVM): the hellocodenameone screenshot suite rendered by a native ELF built + run on the GitHub x64 runner. Baseline: scripts/linux/screenshots.

@shai-almog

shai-almog commented Aug 17, 2026

Copy link
Copy Markdown
Collaborator Author

Compared 163 screenshots: 163 matched.
Native Linux port (arm64), GTK3/Cairo/Pango, ParparVM bytecode-to-C (no JVM): the hellocodenameone screenshot suite rendered by a native ELF built + run on the GitHub arm64 runner. Baseline: scripts/linux/screenshots-arm.

Dragging a non-scrollable child of a window recursed until the stack ran
out. A drag bubbles up looking for something scrollable and stopped only
at a Form; a Window dispatches drags to the pressed child itself, so
bubbling past one came straight back and repeated. It stops at any top
level now.

The title area is a sibling of the content pane, and hit testing always
started at the content pane, so a Toolbar or button drawn as custom
chrome in an undecorated window could never be pressed.

Pull to refresh threw when the window was shown. Its setup still resolved
a Form and called listener methods on it straight away, and a component
in a window has no Form. It goes through the top level, which also meant
giving TopLevelContainer the awaiting-release contract Form already had.

JavaSE modality only elevated the modal window and left every blocked
frame enabled. The framework filters canvas input, but the native title
bar is outside that filter: closing a blocked window still fired a close
request, and closing the main frame still exited the application. The
frames a modal window blocks are disabled while it is up.

A Catalyst window hidden before its scene arrived came back visible,
because the queued hide found no window yet and adoption then showed it
unconditionally. The requested visibility is recorded and honoured.

An unowned window with MODALITY_WINDOW blocked the main form, which never
owned it. And showing a child before its owner marked it owned by the
main window, fixing the wrong native owner permanently, since every port
establishes that relation at creation -- the owner is created first now.

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

shai-almog commented Aug 17, 2026

Copy link
Copy Markdown
Collaborator Author

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

Benchmark Results

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

Build and Run Timing

Metric Duration
Simulator Boot 81000 ms
Simulator Boot (Run) 0 ms
App Install 23000 ms
App Launch 4000 ms
Test Execution 514000 ms

Detailed Performance Metrics

Metric Duration
SIMD kernel backend SSE2 (x64) / NEON (arm64) native kernels
SIMD int-add (64K x300) java 323ms / native 3ms = 107.6x speedup
SIMD float-mul (64K x300) java 287ms / native 3ms = 95.6x 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 220.000 ms
Base64 CN1 decode 277.000 ms
Base64 native encode 977.000 ms
Base64 encode ratio (CN1/native) 0.225x (77.5% faster)
Base64 native decode 516.000 ms
Base64 decode ratio (CN1/native) 0.537x (46.3% faster)
Base64 SIMD encode 78.000 ms
Base64 encode ratio (SIMD/CN1) 0.355x (64.5% faster)
Base64 SIMD decode 56.000 ms
Base64 decode ratio (SIMD/CN1) 0.202x (79.8% faster)
Base64 encode ratio (SIMD/native) 0.080x (92.0% faster)
Base64 decode ratio (SIMD/native) 0.109x (89.1% faster)
Image encode benchmark iterations 100
Image createMask (SIMD off) 8.000 ms
Image createMask (SIMD on) 2.000 ms
Image createMask ratio (SIMD on/off) 0.250x (75.0% faster)
Image applyMask (SIMD off) 61.000 ms
Image applyMask (SIMD on) 51.000 ms
Image applyMask ratio (SIMD on/off) 0.836x (16.4% faster)
Image modifyAlpha (SIMD off) 236.000 ms
Image modifyAlpha (SIMD on) 172.000 ms
Image modifyAlpha ratio (SIMD on/off) 0.729x (27.1% faster)
Image modifyAlpha removeColor (SIMD off) 441.000 ms
Image modifyAlpha removeColor (SIMD on) 90.000 ms
Image modifyAlpha removeColor ratio (SIMD on/off) 0.204x (79.6% faster)

shai-almog and others added 2 commits August 17, 2026 16:55
The Mac producer caught one of the fourteen windows rendering at the
previous window's size: a recycled scene reports its old geometry the
instant it is adopted, before the new request lands, and the framework
laid the window out against that.

A layout report is now filtered against any geometry still outstanding.
While a request is unanswered only the size that was asked for is
delivered; anything else is the old geometry on its way out. Once it
matches, the window is free again and a later user resize passes straight
through.

The harness let it past because the size rule only rejected a window
larger than requested -- chrome makes one legitimately smaller on
Windows. It now also rejects one implausibly smaller: chrome costs tens
of pixels, never a quarter of the window.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The modality change captured a method parameter in an anonymous Runnable
without declaring it final. Maven builds this port at a source level where
effectively-final is enough, so it compiled here; the Ant project that the
simulator integration tests use sets javac.source=1.7, where it is an
error.

Verified by compiling all 174 port sources with -source 1.7 rather than
by fixing the one line the compiler happened to name.

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: 75ef76f159

ℹ️ 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 Ports/iOSPort/nativeSources/CodenameOne_GLSceneDelegate.m Outdated
Comment thread Ports/iOSPort/nativeSources/CN1MacWindows.m
Comment thread Ports/iOSPort/nativeSources/CN1MacWindows.m
Comment thread Ports/iOSPort/nativeSources/CN1MacWindows.m Outdated
Comment thread Ports/JavaSE/src/com/codename1/impl/javase/JavaSEWindowManager.java Outdated
Comment thread Ports/JavaSE/src/com/codename1/impl/javase/JavaSEWindowManager.java Outdated
Comment thread Ports/WindowsPort/src/com/codename1/impl/windows/WindowsWindowManager.java Outdated
Comment thread Ports/iOSPort/nativeSources/CN1MacWindows.m
shai-almog and others added 9 commits August 24, 2026 06:40
…splay

Display had grown 27 arrays, all of them parallel tables keyed by window id,
with three separate slot allocators handing entries out and taking them back.
That was the wrong shape, and it was not a theoretical problem: two of the
defects found in review last round were caused by it rather than found by it.
The drag-activation filter silently switched off for the whole application
once eight windows had been disposed mid-press without returning their slots,
and the pressed-selection bug was "fixed" by adding three more arrays.

State that belongs to a window now lives on the window, as fields:

  drag path, drag-occurred, pressed-selection and its coordinates,
  key repeat and long key press timers, long pointer press timer

Display keeps exactly what it kept before this branch for the main surface --
its own dragPathX/Y/Time ring, keyRepeatCharged, longPressCharged,
longPointerCharged, keyRepeatValue, nextKeyRepeatEvent, longKeyPressTime --
so window zero is the special case it always was, on the code it always ran.
The event loop services those the way master does, then walks the windows that
are actually open rather than a fixed table mostly full of empty entries.

Removed: 17 arrays, dragHistorySlot(), keyRepeatSlot(), the inline allocator
in chargeLongPress(), and releaseDragHistory() with its three call sites --
including the one guarded against a nested invokeAndBlock, which only had to
exist because the ring was shared.

PointerDragHistory is the one piece worth sharing: the sample ring and its
wrap arithmetic. Display owns one, each Window owns one. Everything else is a
field, and a field on the right object needs no sharing.

Two tests were rewritten rather than repaired. dragHistorySlotsAreReclaimed-
AfterEachGesture reflected into the slot table to prove slots came back; there
are no slots, so it now asserts the behaviour that mattered -- a window opened
after ten others have gestured can still drag. The key-repeat and long-press
helpers likewise ask the surface that owns the timer instead of reading a
table out of Display.

5372 core tests pass.

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

Two of the things that made this branch spread. A Window had a title area and
a Toolbar, which is the mobile title bar standing in for chrome the platform
already draws -- so a window carried two titles and gave up content space to
the one nobody asked for. TopLevelContainer no longer declares getTitleArea(),
getToolbar() or setToolbar(); a Window's title goes straight to the platform,
and Toolbar and its search bar are Form-only again.

That alone reverts Toolbar.java to master exactly: 285 lines and 26 of the
getTopLevelContainer() call sites, gone because the question does not arise.

The other is the pattern the sweep kept repeating. Registering a component for
animation had turned into "resolve the top level, null-check it, remember it in
a private field so deregistering can use the same one" -- twelve lines and a
field, replacing a single getComponentForm().registerAnimated(this). It was
written out again in each class that needed it, and the field was there because
of a real defect: register against one top level, get moved, deregister
resolves another, and the first goes on animating a component that left it.

Component.registerForAnimation() and deregisterFromAnimation() hold that. The
component remembers what it registered with and deregisters from the same
thing, so the defect is fixed once instead of a dozen times, and the call sites
are shorter than they were before this branch. ImageViewer's three of them go
from twelve lines each to one.

Nine tests went with the toolbar: eight exercised toolbars and side menus
inside a Window, which is no longer a thing. The ninth is about animation
registration in a window, which still is, so it stays with the toolbar part
removed.

One test changed for a real behaviour change rather than a fix:
aPressDraggedOutOfAButtonInAWindowIsCancelled released at (2,2) as "outside the
button", which only worked while a title area occupied the top of the window.
With the content pane filling the window a centred button covers every
in-window point, so it now releases outside the window.

5364 core tests pass.

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

Ten more sites that had each written out "resolve the top level, null-check
it, register this" -- and in two cases the matching resolve-and-deregister --
now call registerForAnimation() and deregisterFromAnimation(). InfiniteProgress,
ScaleImageButton, ScaleImageLabel, Button, Label, TextField and Component.

Worth recording how the sweep went wrong, because the pattern-match nearly
repeated the mistake it was cleaning up: the substitution also consumed the
declaration it matched, and in TextField.deinitialize() that variable had a
second use further down. The compiler caught it; nothing else in the batch
had the same shape. Enumerating would have been safer than matching, again.

5364 core tests pass.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Modality is a question about windows, and the window registry is on Desktop.
Display was holding the modal list, the push/pop, the native blocking sync and
the four predicates that decide what a given modal blocks -- 153 lines about a
concept the main surface has no stake in.

They move to Desktop. Display now asks one question, isWindowInputBlocked(id),
and does not need to know what a modal is; Window pushes and pops its own
registration directly rather than through Display.

Display's diff against master is down from +2249 to +1968 lines with this and
the earlier state moves.

5364 core tests pass.

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

Display is already a god class; the window work was making it worse. Moving
out what is genuinely about windows rather than about the display:

  window lifecycle  shown, hidden, focus, moved, resized, monitor changed,
                    close requested, closed natively, activation failed,
                    monitorsChanged and the WindowCallback that carries them,
                    plus the pending-size table and the coalescing guard
  geometry          windowWidth, windowHeight, windowDragRegionStatus
  input entry       the pointer and key events a port reports for a window

All of it resolves an id to a window, and the registry that does that is on
Desktop. Display's diff against master drops from +2249 to +1504 lines, and
the ports now report window events to Desktop instead of to Display.

Some of it deliberately stayed. windowInputCancelled and windowDisposed tear
down input state Display owns. windowMouseWheelEvent, windowMagnifyGesture
and windowRotationGesture carry the shared implementation the main surface
uses as well, so moving them would have duplicated it. windowKeyReleased and
the two hover-press entry points only pack an event and put it on the queue,
which is Display's actual job -- moving those would have meant widening the
queue's internals to the package to buy nothing.

pointerReleasedImpl now cancels the long-press timer itself rather than
relying on its callers to, since a release ends the gesture either way.

5364 core tests pass; core, javase, windows, linux and ios all compile.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
createWindowGraphics, the paint pass over the open windows and the
"does any window still animate" check were all "walk the windows and do
something", which is Desktop's job. Display's event loop keeps the loop and
calls Desktop once per pass for the window half.

repaintTopLevels splits the same way: the current Form is Display's to
repaint, the windows are Desktop's.

wakeEdt stays -- it notifies Display's own lock.

Display's diff against master is now +1459 lines, down from +2249 when this
round started, and what remains is the input queue, the main surface's own
state and the shared implementations window zero uses too.

5364 core tests pass; core, javase, windows, linux and ios all compile
through the reactor.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
SpotBugs is a zero-findings gate and caught five leftovers from moving the
window API out of Display: a local that lost its last use when windowWidth
moved to Desktop, and four private methods -- isSelectionPressed, longPressKey,
repeatTarget, selectionX and selectionY -- whose only callers went with the
code that moved.

This is the case CLAUDE.md warns about: removing a caller can make a private
method dead, and the gate fails on it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Five iOS jobs -- build-ios, build-ios-watch, build-ios-metal, native-ios and
packaging -- were failing on the same six errors:

  call to undeclared function
  'com_codename1_impl_ios_IOSWearableCallbacks_nativeMessageReceived___...';
  ISO C99 and later do not support implicit function declarations

Nothing about them had changed. The WatchConnectivity block in IOSNative.m
calls six translated static methods on IOSWearableCallbacks, ParparVM emits
their definitions, and the file includes no header that declares them -- so
every call was an implicit declaration. C99 dropped those, and this only
compiled while the toolchain treated it as a warning. The same commit passed
this job at 17:42 and failed it at 00:07 with no relevant change in between,
which is what a runner image moving to a stricter clang looks like.

Declared explicitly, inside the same CN1_USE_WATCHCONNECTIVITY guard as the
calls. check-native-signatures.sh reports no MISSING or SIGNATURE findings for
the ios port.

Not caused by this branch -- the wearable code came from master in #5487 --
but it blocks it, so it is fixed here.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Moving the key-repeat and long-press timers onto the windows dropped a check
the old routing helper made. repeatTarget() returned null for a surface that
a modal was blocking, so a key held down before the modal appeared stopped
repeating into the window behind it. serviceInputTimers() checked only that
the window was visible, so those repeats started getting through again, and
the main surface had lost the same guard.

Both now consult Desktop.isWindowInputBlocked() before firing.

Found by refusing to delete a test. SpotBugs reported repeatTarget() as an
uncalled private method once its production callers moved to Desktop, and I
deleted it -- but a test reached it by reflection, which SpotBugs cannot see,
so the build went from a clean gate to a NoSuchMethodException that killed the
event dispatch thread and left every later test timing out at five seconds a
piece. Reflecting into privates is what made that possible, so the test is
rewritten against behaviour: it holds a key, drives the timers with an explicit
clock rather than waiting out the 800ms first-repeat delay, and asserts the
repeats stop when a modal goes up. It fails without the fix.

5364 core tests pass; SpotBugs, PMD and Checkstyle report nothing.

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: 4b1456cef9

ℹ️ 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/Component.java
shai-almog and others added 2 commits August 24, 2026 13:00
… goldens

build-test (8) failed on seventeen forbidden PMD violations, all of them
residue from moving the window API to Desktop:

  nine index loops over the Window[] getWindows() returns  ForLoopCanBeForeach
  MAIN_LONG_PRESS_ID, orphaned by the slot removal        UnusedPrivateField
  WindowManager import, orphaned by the modality move     UnnecessaryImport
  TopLevelContainer import in ScaleImageButton            UnnecessaryImport
  five fully qualified Desktop.getInstance() calls        UnnecessaryFullyQualifiedName

Worth recording why local verification missed them: PMD is enforced by
.github/scripts/generate-quality-report.py against its own forbidden list, not
by the Maven build, so `mvn verify` passes with violations present. Reading
maven/core-unittests/target/pmd.xml is the local equivalent, and it now reports
zero -- as do SpotBugs and Checkstyle.

The Linux windowed goldens are re-recorded, both arches. Taking the title area
out of Window changed exactly what it should: the in-content title label is
gone and the content starts at the top of the window instead of below it. The
window dimensions are unchanged, and the fourteen windowed captures were the
only ones that moved -- nothing else in the suite differs. Checked by looking
at the images side by side rather than by trusting the dimensions.

Windows and Mac Catalyst carry the same fourteen and will need the same
treatment once their runs report.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The fix itself went in with a14ff33, whose subject is about PMD and
goldens and says nothing about it -- my mistake, and worth stating rather
than quietly leaving the history misleading.

The defect: the material pull-to-refresh drag listener is built once and kept
for the life of the component, capturing the top level it was created in. A
component moved to another Form or Window is re-registered on the new one but
the listener still targets the old, so the overlay goes up on the top level
the component left and the release arriving on the new one finds nothing to
finish -- the refresh task never runs. The host is now resolved when the drag
happens instead of captured when the listener is built.

Pre-existing: master captures `final Form p` in exactly the same place. This
branch only widened where it bites, by letting a component move between a Form
and a Window.

The test moves a scrollable pull-to-refresh container from a Form into a
Window, pulls, and asserts the overlay lands on the window and nothing is
added to the form. It fails with the captured host. The container is given
content that overflows on purpose -- the gesture is gated on the container
actually being scrollable, and a first attempt with a single label produced no
overlay either way, which would have passed for the wrong reason.

147 WindowTest tests pass.

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: 0b0be1a5cb

ℹ️ 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".

The array and dict tags were made structural earlier in this branch; the key
tags were left matching "</key>" as a literal, so a fragment writing
"</key >" -- which is valid XML -- reported the key as absent.

Both callers then fail, in opposite directions. plistKeyIndex tells the
injection path there is no UIApplicationSceneManifest, so it appends a second
one beside the application's own and the bundle ships duplicate keys.
plistKeyEnd loses the key's value, so the validation path rejects a Mac
Catalyst build that is correctly configured.

Both now use plistCloseElementIndex, the closing-tag counterpart already
written for the container tags.

Two tests, and both fail against the literal matching: one on a single key
with a spaced closing tag, one on a whole scene manifest where every closing
tag is spaced.

912 plugin tests pass; SpotBugs zero.

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: 6c4d17bba7

ℹ️ 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 Ports/JavaSE/src/com/codename1/impl/javase/JavaSEWindowManager.java
…restore

AWT delivers componentShown / componentHidden for every visibility change,
whoever caused it, and the port turned those into windowShowNotify /
windowHideNotify. The existing comment claimed that was safe for the explicit
path because Window.hide() and show() set nativeVisible before calling the
manager, so the notification would find the state already correct.

That holds only if the notification is delivered inline, and it is not: the
AWT callback runs on the AWT thread and queues onto the Codename One event
dispatch thread. A show and a hide in the same turn therefore both queue, and
both run afterwards against the state the second one left. The pair reads as
a minimize followed by a restore, and in the show-then-hide order the window
finishes hidden while still marked iconified -- which is the state showModal()
waits on, so its caller waits for good.

The port now counts the visibility events it is about to cause, on the AWT
thread where they are also delivered, and the listeners consume that count
instead of reporting a lifecycle change. Counted only when the frame is really
changing state, since AWT delivers nothing when it is not and the count would
otherwise be spent on a later event the user caused.

The test drives show() then hide() in one turn and asserts no Minimized or
Restored is reported. Without the correlation the window reports:

  [Resized, Shown, Hidden, Moved, Resized, Restored, Minimized]

278 JavaSE port tests pass.

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: 5a53552596

ℹ️ 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/Display.java
… sweep

Two findings, both mine.

edtLoopImpl takes an early return while the main form is running a transition,
which is right for the main surface and wrong for the windows: a secondary
window is an independent native window with no part in that transition, and it
stopped painting and animating for the transition's duration. The new test
shows it painting zero times either side of one. The window pass now runs
before that return.

Input deliberately stays queued there rather than being dispatched. That is
how the main surface already behaves during a transition, and draining the
shared queue from that branch would change the main path's semantics to fix a
window's -- the wrong trade for a few hundred milliseconds of deferred input.

The other is the third report of one bug class: a closing tag matched as a
literal. plistStringValueAfter searched for "</string>", so a delegate written
"</string >" looked absent and aborted a correctly configured Catalyst build.
Rather than fix the reported line alone, every remaining literal closing tag in
the file was enumerated -- which turned up a second one, mergeUserActivityTypes
parsing "<array>" and "</array>" out of the injected fragment. Both now use the
structural lookups. The one literal left reads our own generated Info.plist
line by line, where we control the spelling.

Enumerating should have happened when the first of these was fixed; matching
the reported pattern is what let it come back twice.

5366 core tests and 92 IPhoneBuilder tests pass.

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: 8a785b1283

ℹ️ 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/Window.java
minimize(), toggleMaximize() and requestWindowFocus() called the window
manager straight from the calling thread, unlike show(), hide(), restore()
and dispose(). setResizable() and setAlwaysOnTop() did the same with their
native half.

I had looked at exactly these methods when centerOn() and restore() were
marshalled, and left them alone on the grounds that each was a single call
with no read to go stale and that the ports marshal internally. The second
half of that was wrong. WindowsWindowManager.minimize() resolves the peer to
a slot index on whatever thread calls it and hands that index to the native
layer -- so a background caller can read a slot an EDT disposal is tearing
down, and the same shape appears in setTitle, setResizable, setAlwaysOnTop,
setMinimumSize, requestFocus and toggleMaximize. The developer guide also
promises callers this is marshalled for them.

The three commands marshal whole. The two setters write their field on the
calling thread, so a getter stays consistent, and marshal only the SPI call.

TestWindowManager now records any window-manager call that arrives off the
event dispatch thread, which is the property worth asserting -- not whether
the call eventually happened. The test drives all three controls from a
background thread and expects an empty list; without the fix it names the
offender: expected <[]> but was <[minimize]>.

5367 core tests pass.

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: 1c1298262d

ℹ️ 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/Window.java
shai-almog and others added 9 commits August 24, 2026 14:53
Window.getMonitor() answers from a lazy cache, and moving the window is
exactly what invalidates it -- but moved() fired the Moved event without
touching it. The cache is otherwise refreshed only by monitorChanged(),
which both the JavaSE componentMoved path and the Windows WM_MOVE path
enqueue *after* Moved, so a Moved listener asking getMonitor(), getScale()
or getDensity() was told which monitor the window had been on before it
moved, and nothing later told the application to ask again.

Cleared rather than recomputed, so a move nobody asks about costs nothing.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Same 14 that shifted on Linux, for the same reason: a Window no longer
paints a title area into its own content, since the title belongs to the
native chrome the capture does not include. Content moves up by the height
of that strip and is otherwise unchanged.

The x64 and cross-compiled runs produce these byte-for-byte identically,
so one golden set covers both.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Container is the nearest common supertype of Form and Window and already
declared inert hooks for exactly this, so the chains of

    if (top instanceof Form) { ((Form) top).x(); }
    else if (top instanceof Window) { ((Window) top).x(); }

were ceremony around a virtual call. TopLevelSupport had eight of them and
now has none. The same treatment covers the command notification behind a
Button and a List, the title-area height, the clear command a TextField
displaces, the selection a press paints, the native peer of a top level and
the safe area a container snaps to.

Two hooks were missing and are added beside the existing ones:
addComponentToTopLevel/removeComponentFromTopLevel. Form forwards them to
its existing Form-named pair so Toolbar, MenuBar and SideMenuBar keep the
call sites they have on master; Window has no such callers and is renamed
onto the hook.

Display keeps one new package-private method, mainSurfacePressIsOver, so
Form can answer showsSelectionFor from the pointer state that lives there
rather than Display asking what kind of top level it is looking at.

SearchBar goes back to Form entirely. It reached for the top level because
a Window used to have a Toolbar; a Window has neither toolbar nor title
area now, so a SearchBar can only ever be in a Form. It keeps
getComponentForm() rather than master's cast of getParent(), which
ParparVM would not have checked.

Behaviour is preserved deliberately in two places the hooks would have
changed: a component with no top level still answers selection from the
main surface, and a window that has not been shown yet still reports the
primary monitor rather than borrowing the main window's.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
CodenameOneImplementation is the largest class in the framework and this
branch had been adding to it: a public static inner class plus six public
methods that took the surface as an Object and cast it back on every call.

PaintSurface is now a class of its own in com.codename1.impl, owning its
queue, its graphics and the paint routine that drains one into the other.
Callers hold a typed reference and say surface.clear(), surface.repaint(a),
surface.paintDirty(w, h) -- so the Object handles, the asPaintSurface cast
and the six wrappers are gone. What is left on the implementation is one
factory, createPaintSurface, and a package-private hook the surface calls to
unregister itself. Two fields it reads (displayLock, getPaintableBounds)
widen from private to package private, which keeps them invisible to ports:
they live in com.codename1.impl and every port is in a subpackage of it.

The scratch rectangle for paintable bounds becomes per surface rather than
one shared field, so one surface's paint pass cannot read another's.

Window input was split across two classes for no reason: ten entry points on
Desktop and six on Display, with ports calling whichever half happened to
hold theirs. All sixteen are on Desktop now, which is the class that owns the
windows; Display keeps the bodies as package-private *Impl helpers, matching
keyPressedImpl and the rest that were already shaped that way.

getWindowPeerForComponent moves to Desktop for the same reason -- which
window a component is in is not a question about the application's main
surface -- and Display loses it entirely.

Null surfaces stay tolerated. The handle API ignored them and a window that
was never shown has none, so Window guards rather than letting hide() or
dispose() start failing.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The pointer drag-activation filter for windows was five parallel arrays on
CodenameOneImplementation with a claim/release protocol and a fixed cap of
eight, plus windowDragSlot to hand entries out and releaseWindowDragSlot to
give them back. Its own comment described the failure: a window disposed
with a press still down never releases, window ids are never reused, and
after eight of those the allocator answers -1 and every window silently
loses the filter -- so a pixel of jitter becomes a real drag.

That state belongs to a window. PointerDragActivation is now a small class
in com.codename1.impl, each Window owns one, and Desktop routes the
implementation to the right one by id. Nothing to claim, nothing to release,
nothing to leak, and no cap. The main surface is untouched: it keeps the
same four fields on the implementation it has always had.

everyOpenWindowFiltersItsOwnDragNoMatterHowMany drags sixteen windows at
once and checks each filters its own jitter. Reintroducing the cap makes it
fail at window 8, which is where the old design stopped filtering.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The developer guide isn't dated the way a blog post is, so framing the
chapter as news ages badly: "Codename One applications have always had
exactly one window" reads as false the moment the reader is holding the API
that makes it false, and "the new TopLevelContainer" is still claiming to be
new years from now. The chapter now says what is true rather than what
changed -- a phone application has one window and a desktop one usually
doesn't -- and describes the components as asking getTopLevelContainer()
rather than as having been migrated to it.

One stale claim went with it: TopLevelContainer was described as carrying
the toolbar. It doesn't, and a Window has no Toolbar at all; its title and
menus belong to the platform chrome. The Toolbar is now listed with the
other Form-only members.

Vale and LanguageTool both report zero on the file.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The six windowed screenshot baselines reported SKIPPED on android, ios-gl,
ios-metal, javascript, tvos and watchos -- thirty-six rows on the public
port status table, each needing an erratum explaining that the reader should
not count it against the port. A port with one full-screen surface was never
asked to open a second window, so there is nothing there to skip.

port_status.json can now scope a test to the ports it applies to:

    {"test": "WindowLayoutTest", "ports": ["linux-x64", ...]}

and every per-port check -- what a report may carry, what it must carry, and
what build_report tracks in the first place -- reads that scope instead of
the whole test list. Those ports' reports no longer carry the six at all,
and the six errata are gone with them. The tests themselves report nothing
on a port without a windowing system rather than reporting a skip.

MultiWindowApiTest stays unscoped. It asserts the contract on every port,
including that the API throws where windows are unsupported, and it passes
everywhere.

Scoping must not become a way to stop reporting quietly, so it is fenced:
a scope naming a port that does not exist, or naming none at all, fails
validation, and a report missing a test scoped *to* it is still drift.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The port status conflicts are all one change: #5590 stopped treating the
checked-in snapshots as contract. Reports are CI output, a report that
predates a registered test is ordinary drift rather than a build failure,
and "every registered test runs on every port" moved to the coverage gate,
which reads the reports the ports actually published.

So this branch stops hand-editing the eleven snapshots. They are taken from
master verbatim; all eleven simply predate the window tests, which is what
drift means and what the page renders as "awaiting this port's next run".

The scoping this branch added is reapplied on top of the new shape, and it
matters more under it than it did before. The coverage gate reads a test
absent from a port's report as dropped once any earlier run elsewhere
carried it -- so the first desktop port to publish WindowLayoutTest would
have made every phone port look like it had dropped a test it can never run.
tests_for_port() now bounds the absence check, build_report, and the
publishable drift split.

MultiWindowApiTest stays unscoped: it asserts the contract on every port,
including that the API throws where windows are unsupported.

Verified on the merged tree: 5478 core tests pass, PMD, SpotBugs and
Checkstyle report zero, javase, ios, windows and linux compile, and all 81
port-status tests pass. Removing the scope from the coverage gate fails
test_coverage_does_not_call_a_scoped_test_dropped_where_it_cannot_run.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Both were seeded from CodenameOneImplementation, which carries the older
Oracle header. A newly added file has to use the Codename One GPLv2 +
Classpath Exception header, which is what check-copyright-headers enforces.

Co-Authored-By: Claude Opus 5 (1M context) <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.

1 participant