[DIAGNOSTIC] Investigate iOS cn1-derive hang#4810
[DIAGNOSTIC] Investigate iOS cn1-derive hang#4810liannacasper wants to merge 70 commits intomasterfrom
Conversation
First slice of the native-themes refactor: the CSS compiler now lives in
its own Maven module with clean dependencies (core + flute + sac) so the
shipped platform themes (iOS Modern liquid-glass + Android Material) can
be generated at framework build time without pulling in JavaSE / JavaFX /
CEF / the Designer GUI.
Phase 1 (no-cef enforcement):
- CSSTheme.strictNoCef static flag + enforceNoCef() pre-scan that lists
every UIID state requiring CEF-backed image rasterization and throws
before any WebView call.
- CN1CSSCLI gained a -no-cef CLI arg.
- New NoCefCSSCLI minimal entry point (no JavaSEPort/BrowserComponent
bootstrap) with a throwing WebViewProvider as a safety net.
Phase 2 (module split):
- New maven/css-compiler Maven module; registered in the reactor between
factory and sqlite-jdbc. Produces a jar and a fat
jar-with-dependencies whose main class is NoCefCSSCLI.
- maven/designer now depends on codenameone-css-compiler.
- EditableResources physically moved into maven/css-compiler, with its
com.codename1.designer.* and com.codename1.impl.javase.* imports
stripped. GUI functionality exposed as protected throwing hooks
(persistUIContainer, onOpenFileComplete, writeUIXml,
getRuntimeNativeTheme) plus a settable loadedBaseFile field and an
inline IS_MAC constant replacing ResourceEditorApp.IS_MAC.
- New EditableResourcesEditor subclass lives in the Designer and
overrides every hook, reinstating the GUI behavior (UserInterfaceEditor,
ThemeEditor, JavaSEPortWithSVGSupport, getResourceEditor, ...).
- New com.codename1.ui.util.SVGDocument interface in core; javase-svg's
SVG class implements it. EditableResources casts to SVGDocument so the
thin module avoids the compile-time dep on impl.javase.SVG.
- EditableResourcesForCSS, CSSTheme, ResourcesMutator, Color,
MissingNativeBrowserException, PollingFileWatcher, and the
com.codename1.ui.util.xml package moved alongside EditableResources.
- Designer callers bulk-updated: new EditableResources(...) ->
new EditableResourcesEditor(...) with imports added, in
ResourceEditorView, ResourceEditorApp, AddThemeResource, AddUIResource,
CodenameOneTask, CN1CSSCLI, CN1CSSCompiler, CN1CSSInstallerCLI.
- ResourceEditorView.loadedResources retyped to EditableResourcesEditor.
Build pipeline:
- scripts/build-native-themes.sh drives the thin jar (prefers a fresh
target/ build, falls back to ~/.m2). Writes iOSModernTheme.res and
AndroidMaterialTheme.res under Themes/ (gitignored).
- Smoke CSS sources in native-themes/{ios-modern,android-material}/theme.css
with light+dark tokens and includeNativeBool:false to avoid the
self-inheriting recursion trap.
- native-themes/README.md documents the CEF-free subset.
CI:
- pr.yml gains a step that installs css-compiler and runs the
native-themes build, failing on missing outputs.
- designer.yml switched from Ant to Maven for building the Designer jar
and running the CLI CSS smoke test (the Ant Designer build is broken
until its source roots are taught about maven/css-compiler; Maven is
the preferred path per CLAUDE.md anyway). Also runs the native-themes
smoke under xvfb.
Known follow-ups (not in this commit):
- Ant-based Designer build (CodenameOneDesigner/build.xml) still expects
all CSS classes under src/; local NetBeans/Ant developers will need
source-tree awareness of maven/css-compiler or a switch to Maven.
- ResourceEditorView line ~2382 still calls EditableResources.open(...)
returning a base instance; fine for the override-resource side path
but a future EditableResourcesEditor.open(...) factory would be tidier.
- Phase 3+ (real CSS themes, port integration, simulator bundling,
build hints, screenshot tests) pending.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
maven/designer/pom.xml declared a dependency on codenameone-css-compiler without a version, expecting the root pom's dependencyManagement to fill it in. The entry was missing, so every downstream module failed to resolve the POM (observed in PR CI). Add the managed version entry next to codenameone-core. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
CI (Java CI + Designer CI) surfaced two classes of errors that the refactor missed: 1. Accessor/helper classes declared in core-like packages but living in the Designer source tree (EditorFont, EditorTTFFont, CodenameOneAccessor, animations.AnimationAccessor, plaf.Accessor, plaf.ProtectedUIManager) were left behind when EditableResources moved to the css-compiler module. They use package-private access into core, so they must travel with EditableResources. Moved them into the css-compiler src tree. Designer still sees them via the codenameone-css-compiler dependency. 2. EditableResources.openFile() directly instantiated CodenameOneDesigner's UIBuilderOverride to materialize an XML-stored UI container before re-serializing. UIBuilderOverride imports com.codename1.designer.* (ActionCommand, UserInterfaceEditor) so it cannot live in the thin module. Introduced a new protected hook loadUIContainerFromXml(ComponentEntry) that returns null in the base (triggering the binary-blob fallback already in the loop) and is overridden by EditableResourcesEditor to drive UIBuilderOverride. 3. SimpleWebServer and WebviewSnapshotter (used by ResourcesMutator's CEF image rasterization) had clean imports and were still referenced by the compile path, so they moved to the css-compiler module too. In strict-no-cef builds they are still never invoked. 4. SVGDocument.java switched from /** classic Javadoc to /// markdown comments per the repo's java25-markdown-docs style validator. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Two leftover references prevented the css-compiler module from compiling:
- EditableResources.saveXMLFile() still instantiated UIBuilderOverride
directly in the MAGIC_UI branch to materialize a container from a
binary UI resource before writing it back as XML. Wrapped in a new
materializeUIContainer(resourceName) hook; base throws, the Designer
EditableResourcesEditor overrides with the UIBuilderOverride call.
- ResourcesMutator.createScreenshots() used Logger.getLogger(CN1CSSCompiler
.class.getName()) purely as a logger name. Rerouted to
Logger.getLogger(ResourcesMutator.class.getName()).
Also tightened NoCefCSSCLI's header comment (plain text instead of a
broken {@link CN1CSSCLI} reference that javadoc-plugin would flag).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
.claude/scheduled_tasks.lock slipped into the previous commit because it wasn't covered by .gitignore. It's a Claude Code session-local scheduled-wakeup lock, not repo content. Untrack and ignore. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ditor Retyping ResourceEditorView.loadedResources to EditableResourcesEditor broke generateStateMachineCodeEx (takes a base EditableResources and assigns it to the field). Narrower fix: field stays base-typed and the single .getResourceEditor(...) call site casts to the editor subclass. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Two CI fixes on top of the now-green Java CI: - build-native-themes.sh: ensure_jar() used log() (which went to stdout) AND echo "$jar" inside the same function whose output was captured via $(...) by the caller. Result: the log line "Using CSS compiler jar: <path>" got concatenated with the path and handed to java -jar, which responded with "Unable to access jarfile". Redirect log() to stderr so only the jar path lands on stdout. - designer.yml: the Maven-produced codenameone-designer-*-jar-with-dependencies.jar is actually a ZIP wrapper around the runnable designer_1.jar (see the antrun add-designer-jar-with-dependencies execution in maven/designer/pom.xml). Unzip to a temp dir and run the inner jar. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Both PR CI and Designer CI are hitting 'Cannot assign field "cssFile" because "theme" is null' at NoCefCSSCLI.java - meaning CSSTheme.load returned null without any diagnostic. The Designer's original NPE catch logged nothing for non-"encoding properties" NPEs (the Logger.log line was commented out). Re-enable logging for the general case, null-guard the message check, and have NoCefCSSCLI fail with a helpful message if the parser returns null. Next CI run should show the real stack trace. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Stack trace from PR CI shows: at com.codename1.io.Util.copy(Util.java:211) at com.codename1.io.Util.readInputStream(Util.java:402) at com.codename1.designer.css.CSSTheme.load(CSSTheme.java:7110) at com.codename1.designer.css.NoCefCSSCLI.main(NoCefCSSCLI.java:55) Util.copy(in, out, bufferSize) unconditionally dereferences Util.getImplementation() to route cleanup() through the platform impl. In the native-themes build the css-compiler runs headless - no Display has been initialized, no Util implementation is set, and the unwrapped null crashes before CSSTheme can even parse the CSS. Guard the cleanup path: if no implementation is set, close the streams directly (which is what every impl's cleanup(Object) ends up doing for InputStream/OutputStream anyway). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Designer CI logs (now with working stack traces) show:
CSSException: Unsupported CSS condition type 10 for ElementSelectorImpl
at com.codename1.designer.css.CSSTheme.getElementForSelector(CSSTheme.java:6561)
My smoke CSS used :pressed / :disabled pseudo-classes. The CN1 CSS
compiler actually handles state selectors as dot-class conditions
(.pressed, .disabled) - see docs/developer-guide/css.asciidoc line 38
("Button.pressed defines styles for the 'Button' UIID's 'pressed' state")
and the SAC_CLASS_CONDITION branch in CSSTheme.getElementForSelector.
The pseudo-class syntax (condition type 10) is not recognized. Switch
smoke themes to .state syntax and clarify the native-themes README.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Next NPE after the pseudo-class fix: at com.codename1.ui.Font.<init>(Font.java:176) at com.codename1.ui.Font.createSystemFont(Font.java:452) at CSSTheme$Element.getThemeFont(CSSTheme.java:4671) at CSSTheme.updateResources(CSSTheme.java:1887) Font(int, int, int) dereferences Display.getInstance().getImplementation() to create a native font - null in the headless css-compiler run. The smoke themes don't need a font to exercise the no-cef pipeline end to end, so drop font-family. Phase 3 will add a minimal headless impl (or make Font creation degrade gracefully when Display is uninitialized) so real themes can specify fonts. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Phase 3: replace the smoke placeholder CSS with real component coverage.
Both themes now style ~25 UIIDs each with light/dark palettes, including:
- base (Component, Form, ContentPane, Container)
- typography (Label, SecondaryLabel, TertiaryLabel, SpanLabel*)
- buttons (Button, RaisedButton, FlatButton + .pressed / .disabled)
- text input (TextField, TextArea, TextHint + focused/disabled)
- selection controls (CheckBox, RadioButton, OnOffSwitch + .selected)
- toolbar (Toolbar, TitleArea, Title, MainTitle, Back/Title commands)
- tabs (Tabs, TabsContainer, Tab, Selected/UnselectedTab)
- side menu (SideNavigationPanel, SideCommand)
- list + MultiButton (List, ListRenderer, MultiButton, MultiLine1..4)
- dialog/sheet (Dialog, DialogBody, DialogTitle, Dialog{Content,Command}Area)
- FAB (FloatingActionButton + .pressed)
- misc (Separator, PopupContent)
Palettes:
- iOS Modern — Apple system colors (accent=#007aff light / #0a84ff dark,
Apple grouped-background surfaces, separator colors); liquid-glass feel
is approximated via solid fills with subtle tonal surface variants.
- Android Material — Material 3 baseline tonal palette (primary=#6750a4
light / #d0bcff dark, Material surface-container tiers). Elevation is
approximated with surface-container-high tonal values since box-shadow
would force CEF rasterization.
Font.java (core) small fix: the package-private Font(int,int,int)
constructor used to NPE when Display.impl was null. The css-compiler
native-themes build is headless (no Display.init) and needs to serialize
font descriptors without actually allocating native font handles. Guard
the createFont call; headless serialization writes face/style/size only
and the native handle is recreated when the resource is loaded in a
running app.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Phase 3 Designer CI revealed: RuntimeException: Unsupported CSS property cn1-pill-border RuntimeException: Unsupported CSS property cn1-round-border Those are not top-level CSS properties in the CN1 compiler; they are values of the cn1-background-type property. Rewrite to cn1-background-type: cn1-pill-border; cn1-background-type: cn1-round-border; Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…iter) Phase 3 CI error was a cascade: the CSS compiler's transformDarkModeMediaQueries turns any selector inside @media (prefers-color-scheme: dark) into $DarkSelector. For component selectors this produces the wanted $DarkButton etc. But for :root the rewrite emits $DarkComponent:root which Flute rejects ("encountered ' '. Was expecting ':' <IDENT> <FUNCTION>"), and every declaration inside that dark :root block is skipped. The light :root block then survives just fine, but because Flute aborts the dark block early the parser never registers those variables. When update_resources later tries to serialize a fg color it finds a raw var() FUNCTION lexical-unit instead of a resolved color and throws "Unsupported color type 41". Simplest path that keeps the compiler as-is: drop CSS variables from the shipped themes and inline hex values per UIID. Light values go in the top-level rules, dark values go in the @media (prefers-color-scheme: dark) block which the compiler maps to $DarkUIID. Every UIID now has a matching dark entry. When the compiler grows real :root-in-@media support (separate change), we can re-introduce variables. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Two more headless NPEs surfaced by the real themes: 1. EditorTTFFont.refresh() loaded /com/codename1/impl/javase/Roboto-*.ttf via getClass().getResourceAsStream. That resource ships in the javase port jar, not in our thin css-compiler jar, so the stream is null and java.awt.Font.createFont(null) throws IOException. Guard the null stream and return early; the .res serialization only needs the nativeFontName descriptor, and the native AWT font is recreated at app runtime when the platform impl is available. 2. RoundBorder.<init> calls Display.getInstance().convertToPixels(2) to set its shadowSpread - which dereferences a null impl in the headless build. Make convertToPixels return a 1:1 fallback when impl is null. Conversions are recomputed at runtime. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Next NPE in Phase 3: at com.codename1.ui.Font.getFace(Font.java:742) at com.codename1.ui.util.EditableResources.saveTheme(EditableResources.java:2095) EditableResources serializes system fonts by calling Font.getFace(), getSize(), getStyle(), each of which dereferences Display.impl. In the headless css-compiler build impl is null. Capture face/style/size in the Font(int,int,int) constructor into headlessFace/Style/Size fields and return them from the three accessors when impl is null. Non-system fonts (TTF, bitmap) never enter this path and keep the fields at zero. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Feedback: "SVGDocument should be package private to avoid polluting the UI. Looking at the code I don't see why it needs to be in the core API to begin with." Deleted com.codename1.ui.util.SVGDocument from core. Reverted javase-svg's SVG class to a plain class (no implements). The few EditableResources code paths that need SVG fields now go through a package-private static SvgBridge inside EditableResources itself, which reflectively calls SVG's methods. This is cold code from the css-compiler point of view (SVG paths only fire when the resource being serialized contains SVG images, which the native-themes build never produces) so reflection overhead is a non-issue. Bridge lives where it is used, no cross-module interface or API surface is added. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Port integration for the CSS-generated iOSModernTheme.res and
AndroidMaterialTheme.res:
- IOSImplementation.installNativeTheme() resolves theme by the
existing ios.themeMode hint. "modern" / "liquid" / "auto" loads
/iOSModernTheme.res (with a graceful fall-through to iOS7Theme if
the generator hasn't produced it yet, so apps still boot in a
partial build); "ios7" / "flat" keeps the flat theme; everything
else falls back to the pre-flat iPhone theme. "auto" now defaults
to modern, per the decided release plan.
- AndroidImplementation.installNativeTheme() reads a new
cn1.androidTheme property ("material" | "hololight" | "legacy");
and.hololight=true still maps to hololight for back-compat. Default
is material. Drops the SDK_INT<14 gate (universal Android today)
and swaps the holo-unless-hint logic for the cleaner hint-first
path. Falls back to holo light if the apk doesn't contain the
modern .res (partial build).
- Ports/iOSPort/build.xml -pre-compile copies
../../Themes/iOSModernTheme.res into nativeSources/ so it ends up
in dist/nativeios.jar alongside the legacy .res files.
failonerror=false lets the port still build if
scripts/build-native-themes.sh hasn't produced the file yet
(runtime fallback kicks in).
- Ports/Android/build.xml -pre-compile copies
../../Themes/AndroidMaterialTheme.res into src/ so it lands on the
APK classpath via the existing <fileset dir="src" includes="*.res"/>
jar packaging. Same failonerror=false guard.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The java25-markdown-docs validator rejects /** ... */ Javadoc blocks in CodenameOne/ and Ports/CLDC11/. My Phase 3 edit to Font.java added one for the headlessFace/Style/Size fields. Convert to /// markdown. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
SpotBugs on core-unittests flagged DE_MIGHT_IGNORE for the inline catch (IOException ignored) blocks I added for the headless close fallback. Refactor into a closeQuietly(Closeable) helper that prints the exception to stderr instead of silently swallowing it. Semantics preserved (close failure doesn't propagate) but no more "might ignore" warning. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Three pieces of the JavaSE simulator integration:
- maven/javase/pom.xml antrun build-skins execution copies the six
shipped .res themes (iOSModernTheme, AndroidMaterialTheme, plus the
four legacy ones) from Themes/ into ${project.build.outputDirectory}
alongside iPhoneX.skin. These end up at the root of the simulator
jar-with-dependencies so the simulator can pick any theme at runtime
without requiring a skin-repo update. failonerror=false so the
simulator still builds if the native-themes generator hasn't run.
- JavaSEPort.loadSkinFile(): after the skin archive's platformName is
parsed, consult -Dcn1.forceSimulatorTheme and the
simulatorNativeTheme Preference. If neither is set (or is "auto"),
map ios -> iOSModernTheme and and -> AndroidMaterialTheme; other
platforms fall through to the skin's embedded theme. "embedded"
bypasses the override entirely. When a theme id is resolved the
bundled /<id>.res from the simulator jar replaces nativeThemeData,
and the existing downstream installNativeTheme path picks it up.
- JavaSEPort.createNativeThemeMenu(): new top-level "Native Theme"
JMenu next to "Skins" with a radio group for the six themes plus
"Auto" and "Use skin's embedded theme". Selection writes the
simulatorNativeTheme Preference, flips reload.simulator, and
disposes the current window so the skin reloader kicks in with the
new theme.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
CodenameOne/ compiles against Ports/CLDC11/dist/CLDC11.jar as the bootclasspath (see endorsed.classpath.cmd.line.arg in CodenameOne/nbproject/project.properties). CLDC11 is a stripped J2ME- style surface and does not ship java.io.Closeable. My generic closeQuietly(Closeable) helper therefore fails the CodenameOne Ant compile. Split into two overloads on InputStream and OutputStream; both types are in CLDC11 and are all Util.copy needs anyway. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- New Ports/JavaSE/src/com/codename1/impl/javase/BuildHintSchemaDefaults
registers codename1.arg.{{...}}.label/type/values/description system
properties for a new "Native Theme" group containing
cn1.nativeTheme (Shared override), ios.themeMode, and cn1.androidTheme.
Simulator.main() calls register() right after NSHighResolutionCapable
so the Build Hints GUI picks them up on every simulator launch.
- IPhoneBuilder now falls back to cn1.nativeTheme when ios.themeMode is
unset (modern -> modern, legacy -> ios7, otherwise auto).
- docs/developer-guide/Advanced-Topics-Under-The-Hood.asciidoc: updated
ios.themeMode row, added cn1.androidTheme and cn1.nativeTheme rows
describing the new values.
Android side: cn1.androidTheme is read at runtime from
Display.getProperty (see AndroidImplementation.installNativeTheme);
generic hint-to-Display-property propagation already exists for build
hints in the Android build path, so no further builder surgery is
needed here.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds a DualAppearanceBaseTest helper plus four initial <Component>ThemeScreenshotTest subclasses under scripts/hellocodenameone/common/.../tests/: - DualAppearanceBaseTest - sequences two captures (light, dark) by toggling Display.setDarkMode(false/true), refreshing the global theme props, building a fresh Form per appearance, waiting for onShowCompleted, emitting CN1SS chunks named <baseName>_light and <baseName>_dark. Resets setDarkMode(null) on completion. - ButtonThemeScreenshotTest covers Button / RaisedButton / FlatButton in default, pressed, and disabled states. - TextFieldThemeScreenshotTest covers TextField with value, hint, disabled + TextArea. - CheckBoxRadioThemeScreenshotTest covers CheckBox / RadioButton in unselected, selected, and disabled states. - ToolbarThemeScreenshotTest covers Toolbar with title, left/right material-icon commands, and an overflow command. All four registered in Cn1ssDeviceRunner's TEST_CLASSES list right before the terminal OrientationLockScreenshotTest so they execute as part of the existing iOS/Android/JavaScript screenshot runs. Goldens (scripts/hellocodenameone/goldens/<platform>/<theme>/...) are captured manually on a trusted baseline; the diff pipeline (scripts/common/java/RenderScreenshotReport.java + PostPrComment.java) picks them up automatically. More scenes (ComboBox/Picker, Dialog, Tabs, SideMenu, List+MultiButton, FloatingActionButton, SpanLabel, Layouts) land in a follow-up. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Build failure: incompatible types: HashMap cannot be converted to Hashtable getThemeProps() is not public in com.codename1.ui.plaf.UIManager I was trying to force a global theme refresh by round-tripping getThemeProps/setThemeProps. That API is package-private and the return type has changed. Dropping the refresh entirely: because DualAppearanceBaseTest creates a FRESH Form per appearance, each component is styled at attach time against the current CN.isDarkMode() flag. No global refresh needed. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Rebased onto master to pull in PR #4677 (Initial work on the new JavaScript port). Follow-up to make the JS port consume the new native-themes output: - HTML5Implementation.installNativeTheme(): new default-theme resolution parallel to the iOS/Android ports. Android defaults to /AndroidMaterialTheme.res (hololight / legacy reachable via cn1.androidTheme); iOS defaults to /iOSModernTheme.res (ios7 / legacy reachable via ios.themeMode). javascript.native.theme still wins if set. If the modern .res is missing (partial build) the loader falls back to the legacy theme so the app still boots. - scripts/build-native-themes.sh now mirrors the generated iOSModernTheme.res and AndroidMaterialTheme.res into Ports/JavaScriptPort/src/main/webapp/assets/ alongside the existing legacy .res files. .gitignore in that directory treats the mirrors as build artifacts (sources in native-themes/ stay authoritative). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
PR CI Java 8 quality gate flagged: ControlStatementBraces: CodenameOne/src/com/codename1/io/Util.java:226 ControlStatementBraces: CodenameOne/src/com/codename1/io/Util.java:235 Rewrite the two "if (c == null) return;" one-liners with explicit braces. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Three user-reported issues fixed in one round: 1. Dark screenshots were actually light in every test. Root cause: UIManager caches resolved Style objects per UIID in styles/selectedStyles. A Style created while CN.isDarkMode()==false is the light variant; the cache then returns it on later lookups even after Display.setDarkMode(true) flips the flag. Components built on the fresh dark Form were picking up the cached light Style. Fix in DualAppearanceBaseTest: reflectively clear UIManager.styles and selectedStyles between appearance flips (and after the test finishes) so the next Component.initLaf -> UIManager.getComponentStyle goes through the full resolution path. shouldUseDarkStyle consults CN.isDarkMode() on every call and picks up the $Dark<UIID> entries emitted by the native themes' @media (prefers-color-scheme: dark) block. 2. iOS CheckBox / RadioButton disabled state looked weak. Previously only "color: #c7c7cc" was set on .disabled so the only visible change was a slightly lighter check glyph. Add a background-color: #e5e5ea (tertiary surface) for light and #2c2c2e for dark, plus an explicit transparent background on the default state. RadioButton also now inherits CheckBox's padding + base background instead of just deriving the label. 3. Test coverage was too thin (only Button/TextField/CheckBoxRadio/Toolbar). Added nine more theme-fidelity tests under scripts/hellocodenameone: - SwitchThemeScreenshotTest (OnOffSwitch default/on/disabled) - PickerThemeScreenshotTest - TabsThemeScreenshotTest - MultiButtonThemeScreenshotTest (1..4 line variants) - ListThemeScreenshotTest - DialogThemeScreenshotTest (inline Dialog/DialogBody/DialogTitle/ DialogCommandArea to avoid modal animation flake) - FloatingActionButtonThemeScreenshotTest - SpanLabelThemeScreenshotTest - DarkLightShowcaseThemeScreenshotTest (mixed-components stress) All registered in Cn1ssDeviceRunner; each emits light+dark pair. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Android and JavaScript port builds flagged: SpanLabelThemeScreenshotTest.java:5 - cannot find symbol SpanLabel DarkLightShowcaseThemeScreenshotTest.java:9 - cannot find symbol SpanLabel DialogThemeScreenshotTest.java:9 - cannot find symbol SpanLabel PickerThemeScreenshotTest.java:5 - cannot find symbol Picker SpanLabel lives in com.codename1.components, Picker in com.codename1.ui.spinner - not com.codename1.ui. Update imports in all four tests. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The JavaScript port bytecode-compliance check flagged
DualAppearanceBaseTest's reflective UIManager cache-clear as forbidden
API usage (Class.getDeclaredField, Field.setAccessible, Field.get are
not on the CN1-allowed surface). Also the wider concern: apps should
not need reflection to re-resolve styles after a Display.setDarkMode()
flip.
Added a tiny public API on UIManager:
public void refreshTheme() {
// snapshot current themeProps and re-run setThemePropsImpl,
// which clears styles/selectedStyles/themeConstants/imageCache
// and re-runs buildTheme against the current CN.isDarkMode().
}
Using this instead of reflection in DualAppearanceBaseTest. Clean,
compliant, and generally useful for any CN1 app that wants to respond
to a dark-mode flip without reloading its theme from disk.
The previous commit (3bfcc05) found this via the JS port compliance
check, so the new tests already exercise the new API end-to-end.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Previous run pins the hang to new Form() itself - form.newCtor.begin
is the last log, nothing after. Form's field initializers resolve
styles for Title (via Label("", "Title")) and Container (via
titleArea = new Container(...)); the ctor body then resolves Form,
TitleArea, ContentPane, Toolbar (via initGlobalToolbar).
Probe each of those UIIDs' Style up front with a log before/after,
so the next iOS run names the UIID whose getComponentStyle call
goes into the infinite loop on the freshly-installed modern theme.
Likely suspect: Title (only UIID in the modern theme that uses
"native:MainBold", and the iOS port may round-trip to native code
to resolve that font which could deadlock).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Last iOS UI run pins the BottomTheme hang to getComponentStyle('TitleArea')
- the probe emits probeStyle.begin uiid=TitleArea but never reaches .done.
TitleArea is the only probe UIID that uses cn1-derive to reference a
sibling (Toolbar) in the freshly-installed modern theme; Title
(which uses native:MainBold but no derive) resolves fine and Android
renders the same derive-heavy structure without issue.
Inline Toolbar's fg/bg/margin into TitleArea as a workaround so
the iOS screenshots can make progress. The underlying cn1-derive
resolver bug on iOS (after setThemeProps swaps the prop table
mid-flight) still needs a real fix in the port / UIManager; that
is tracked for a follow-up once the fidelity captures confirm this
is the only affected UIID.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The per-step runAppearance trace and probeStyle helper served their one-off purpose of narrowing the iOS cn1-derive hang to TitleArea and landing the inline-Toolbar-props workaround. Keep the install path INFO/WARN/ERR lines (they are useful any time a platform fails to pick up a native theme) and collapse the Form construction back to the one-line new Form(title, layout) form. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
14 tests x (light + dark) x 2 platforms = 56 PNGs captured from run 24862307480 (iOS UI build scripts) and 24862307487 (Test Android build scripts) on 12d47fd. The new theme-fidelity tests were previously producing missing_expected status (non-fatal under CN1SS_FAIL_ON_MISMATCH=1) because no goldens existed - reviewers got no visual diff in PR comments and future theme regressions would not be caught. With these goldens in place the tests become regression-worthy and the PR-comment diff report lights them up. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…mpler derives User feedback on the first round of modern-theme captures: 1. Dialog blends into the Form in Android light mode (floating text + buttons without a visible dialog surface). Bumped Material 3 Dialog bg from surface-container (#f3edf7) to surface-container-highest (#e6e0e9) so it sits visibly above the surface (#fef7ff). iOS Dialog stays pure white against the grouped-form #f2f2f7 background. Both themes now inline Dialog/DialogBody/DialogTitle/PopupContent props instead of using cross-UIID cn1-derive. 2. Android Title label renders as a white rectangle on the off-white TitleArea (Title has no explicit bg). Gave Title an explicit background-color matching TitleArea and bumped font-size to 4.5mm (the default size is too small for a title on both platforms). 3. iOS Tabs appeared top-left-aligned instead of the iOS 26 bottom navigation-bar look. Added @tabPlacementInt=2 (Component.BOTTOM) and @tabsFillRowsBool=true to both modern themes; styled TabsContainer as a rounded-rect pill with a surface-container fill and distinct selected-Tab pill inside it. Mirrors the Material 3 bottom-nav-rail look on Android and approximates the iOS 26 pill tab group. A real backdrop-filter glass is still future work and will need port-side code. 4. cn1-derive cross-dependency limit: per user, inheritance should stay simple (child refines parent). Inlined TitleArea/Toolbar, DialogTitle/Title, DialogBody/Dialog, PopupContent/Dialog, RadioButton/CheckBox, TextArea/TextField so only refinement-style derives (Label variants, Button variants, Tab variants, MainTitle from Title) remain. 5. Doc: auto stays on legacy for now. The build-hint docs previously said the default had flipped; reverted to reflect the agreed behaviour - auto (unset) keeps iOS 7 / Holo Light and we will flip in a later release. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The 7338c18 theme edits (bottom tabs, distinct dialog bg, explicit title bg, font-size bumps, derives inlined) intentionally change the rendered pixels of every theme-fidelity test on both iOS and Android. Leaving the existing goldens in place would make CN1SS report 28 "different" captures on Android and tank the build under CN1SS_FAIL_ON_MISMATCH=1. Drop the goldens so the next CI run reports missing_expected (non-fatal) and produces a clean diff report; regenerate them after visually confirming the new captures match the design intent. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Codify the inheritance rule ("child refines parent" only) and the
set of currently-inlined cross-context pairs so future contributors
know why TitleArea/DialogTitle/etc. don't cn1-derive. Also note
that real backdrop-filter glass needs a separate PR (port-side
UIVisualEffectView / RenderEffect mapping for a new
cn1-backdrop-filter CSS primitive).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Tabs.initLaf reads the tabPlacementInt theme constant (Component.TOP / BOTTOM / LEFT / RIGHT) and assigns it to the tabPlacement field - but the constructor has already added tabsContainer to BorderLayout.NORTH from the default field value. Writing the field alone leaves the tabsContainer in NORTH regardless of what the theme asked for. Route through setTabPlacement() so the container is re-parented to the correct BorderLayout slot. The condition guards against a revalidate cascade when the constant matches the current placement. Found while verifying the modern theme puts tabs at the bottom (iOS 26 / Material 3 bottom-nav-rail look). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Component() super-ctor invokes initLaf polymorphically before the Tabs ctor body has allocated tabsContainer. The previous form of this fix called setTabPlacement() unconditionally, which NPE'd inside setTabsLayout() (tabsContainer.setLayout on null) and left the container stuck at NORTH from the original field-default assignment. Check tabsContainer==null: on the first call during super() just stash tabPlace into the field, and rely on the ctor's own setTabPlacement(tabPlacement) call at line 156 to reparent the then-allocated container. On subsequent refreshTheme-triggered initLaf calls, the container exists and we reparent if needed. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The existing assignment in Tabs.initLaf does not take effect because initLaf fires during Component()'s super-ctor - at which point Tabs's tabPlacement field hasn't been set, and even if initLaf updates it, the field assignment is inside the (not-yet-started) subclass initialisation phase and the constructor's own setTabPlacement call reads the value Java initialised to default (TOP=0), never the theme's value. Read the constant directly in the new Tabs() path (tabP == -1) before the setTabPlacement call so the container actually lands in the theme-specified slot. initLaf continues to handle subsequent refreshTheme cycles for the tabsContainer != null case. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Android captures on 3a2efe0 still show tabs at TOP despite the direct-read fix in the ctor. Either getThemeConstant returns -1 at this call site (theme constants not yet populated?), or the setTabPlacement call hits an unexpected early-return path. Log the values so the next Android run tells us which is the case. Log lines are throwaway and will be removed once the root cause is fixed. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
buildTheme() strips @-prefixed keys into a separate themeConstants
map and drops them from the main themeProps map. refreshTheme()
rebuilt the input to setThemePropsImpl from themeProps alone, so
every constant (tabPlacementInt, tabsFillRowsBool,
paintsTitleBarBool, switchTrackScaleY, etc.) was silently lost on
the second call. Diagnosed via a CI println in the Tabs ctor:
getThemeConstant("tabPlacementInt", -1) returned -1 even though
setThemeProps had been called with the constant moments earlier.
Re-add the @-prefixed keys from themeConstants when rebuilding the
input Hashtable so setThemePropsImpl -> buildTheme sees the shape
it expects. Also drop the diagnostic println from Tabs.java now
that the root cause is fixed.
This unblocks the Android/iOS modern-theme captures rendering
tabs at the bottom (tabPlacementInt=2) instead of stuck at TOP
because the refresh-then-construct flow in
DualAppearanceBaseTest.runAppearance was silently resetting the
placement constant back to the default.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…contrast fixes User review round 2 on 95290d2 captures: * Android fonts / padding were too tall for the small emulator screen. Bumped everything down a notch - Label 3.5mm, Title 4mm, Button padding 1.5/3mm, CheckBox/Radio 0.5/1.5mm, TextField 1.5/2.5mm. Keeps Material 3 proportions without overflowing. * Android bottom-tab change reverted. Material 3 Tabs stay at TOP (bottom-rail is a separate component, not Tabs). Removed @tabPlacementInt/tabsFillRowsBool from the Android theme and restored the flat underline-by-color top-tab styling. * iOS TabsContainer + selected/pressed Tab switched from border-radius to cn1-pill-border so the group is a true pill. * Button / RaisedButton / FlatButton now declare text-align: center explicitly (the legend overlay was complaining about left-aligned button text). * RaisedButton.pressed had no explicit text color so it was invisible against the darker pressed fill. Pinned to #ffffff on iOS and #21005d on Android. * Android Raised vs default Button now use different container tones (elevated #eaddff vs primary #6750a4) so the pair stays distinct in both light and dark mode. Showcase dark no longer collapses them. * iOS Dark Dialog moved from #1c1c1e (matched form bg) to #2c2c2e (elevated surface) so the dialog doesn't look like floating text in dark mode. * CheckBox / RadioButton gained icon-gap: 2mm for breathing room between the symbol and the label text. native-themes/README.md cn1-derive rule was not changed in this commit; the cn1-pill-border / border-radius + border CEF caveats still apply. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds docs/developer-guide/Native-Themes.asciidoc and registers it in developer-guide.asciidoc between Advanced-Theming and css. Covers: * How to enable the iOS liquid-glass / Android Material 3 themes via ios.themeMode / cn1.androidTheme / cn1.nativeTheme, plus the and.hololight back-compat hint. * Default stays on legacy; auto flip is planned for later. * Dark mode wiring (@media prefers-color-scheme: dark -> $DarkUIID, Display.setDarkMode, system appearance). * Overriding the palette statically (includeNativeBool: true in the user theme) and dynamically (UIManager.addThemeProps + refreshTheme), with a pointer at the runtime-override example test. * Complete table of @constants each theme exposes (commandBehavior, tabPlacementInt, tabsFillRowsBool, switchThumbScaleY, switchTrackScaleX, ios7StatusBarHack, etc.) with their iOS and Android defaults and what each one controls. * Catalogue of every styled UIID grouped by role: base, typography, buttons, inputs, navigation / containers, dialogs / overlays. * cn1-derive rule of thumb ("child refines parent") with safe and deliberately-avoided examples; names the cross-context chains that now inline their props instead of deriving. * CEF-free subset constraints (allowed vs forbidden CSS). * Future backdrop-filter glass primitive as out-of-scope work. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…radio icons User feedback on d412866 round: * Add an opt-in textured backdrop (diagonal-stripe palette, light and dark variants) to DualAppearanceBaseTest. Dialog and Tabs tests opt in so any translucent surface reveals its see-through tint instead of blending into a plain Form bg. Non-translucent tests keep the clean surface. * Beef up the iOS MultiButton styling: 3mm/4mm padding, 4mm primary bold line, 3.5mm secondary regular, 3mm tertiary/quaternary, left-aligned text, 3mm icon gap. Matches the iOS Settings row density the user expected. * Per-platform CheckBox/RadioButton icons: DefaultLookAndFeel now honours four new optional theme constants (@checkBoxCheckedIconInt / @checkBoxUncheckedIconInt / @radioCheckedIconInt / @radioUncheckedIconInt) containing the Material icon codepoint to use for each state. Existing themes that don't set these see no change. The iOS modern theme uses CHECK_CIRCLE / CHECK_CIRCLE_OUTLINE for checkboxes (Reminders-app circle aesthetic) and keeps the circular radio glyphs; Android Material keeps the default square check-box so Material 3 look is preserved. * Dev guide documents the four new icon constants alongside the existing @constants table. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Setting the texture via form.getUnselectedStyle().setBgPainter silently did not take effect on Android: the captures on aeff616 still showed the plain theme form bg. The Form's style pipeline installs a BGPainter at component-init time that gets re-initialised on show(), so any post-ctor setBgPainter replacement is discarded before the paint loop runs. Instead override paintBackground on the anonymous Form subclass used by runAppearance and paint the texture directly there when useTexturedBackdrop() is true. Same painter, same colours; just a location that actually survives the style pipeline. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The paintBackground override does paint the diagonal-stripe texture, but the ContentPane / TitleArea above it have their own opaque bgColor from the modern theme and immediately wash over it with a solid surface fill. Setting bgTransparency=0 on those sub-containers when useTexturedBackdrop() is true lets the texture read through, so any translucent widget on top of them shows its see-through tint against the stripes. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…alignment
User feedback round 4:
* Remove headless-Display fallbacks added to CodenameOne core for
the css-compiler. Replace them with a proper
HeadlessCssCompilerImplementation in maven/css-compiler that gets
installed into Display.impl + Util.implInstance via reflection at
the start of NoCefCSSCLI.main, mirroring the unit-test module's
TestCodenameOneImplementation pattern. CN1 core no longer carries
compiler-only stubs.
* Tabs.initLaf reads tabPlacementInt via the component's own
getUIManager() instead of UIManager.getInstance().
* BuildHintSchemaDefaults gets a class-level Javadoc that explains
why it exists (publishes hint schema metadata for the simulator's
Build Hints UI), what fails without it, and that it is unrelated
to live CSS recompilation.
* Native-Themes developer-guide chapter rewritten:
- Drops implementation-detail constants (commandBehavior,
ios7StatusBarHack, paintsTitleBarBool) and the CEF-free /
Platform-constraints sections - those are framework-internal,
not user-facing.
- Documents each platform's design palette (Material 3 baseline
+ Apple system) with a colour-role -> UIID mapping so brand
overrides are obvious.
- Notes that Display.setDarkMode(null) follows the device.
- Calls out platform-specific UIIDs (iOS Settings-style
MultiButton, top-tab vs bottom-pill behaviour, status-bar
treatment).
- Documents the four optional check-box / radio glyph constants
and the @switchTrackScale / @tabPlacementInt tuning constants.
* iOS Dialog now uses rgba(.., 0.78) so it reads as translucent
over the textured backdrop. DialogBody / Title / ContentPane /
CommandArea are transparent so the Dialog surface paints once
and the rounded corners no longer expose a different inner
shade. Same treatment for dark mode (rgba(44,44,46,0.78)).
* iOS Tabs container + selected tab use rgba so the pill group
reads as glass-frosted. Dark mode unselected tab text stays on
the translucent dark surface.
* Android Dialog sub-UIIDs (Body / Title / ContentPane /
CommandArea) are transparent so the Dialog surface is the only
fill - fixes the visible body-vs-dialog colour mismatch in
light mode.
* iOS MultiButton: rgba surface + 1mm/3mm margin + 3mm border-radius
so the rows read as separated cards instead of edge-to-edge
stripes.
* Switch (both themes) zero padding + margin tuned so the track's
left edge lands at the same x as the Label text-left of the row
above (fixes the visible misalignment).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Generated projects now ship with ios.themeMode=modern, cn1.androidTheme=material, and the cross-platform cn1.nativeTheme=modern hint already set in the project's codenameone_settings.properties. New apps get the iOS liquid-glass + Android Material 3 themes by default; existing projects are unaffected because the cn1 framework itself still defaults to legacy when these hints are unset. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sets ios.themeMode=modern, cn1.androidTheme=material, cn1.nativeTheme=modern in the playground's codenameone_settings.properties so users browsing the playground see the iOS liquid-glass and Material 3 looks while they explore components. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The pass-4 revert that removed headless fallbacks left an unused import. PMD flagged it as UnnecessaryImport. Drop it. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Diagnostic-only branch. Re-instates the iOS modern theme's
cn1-derive: Toolbar on TitleArea (the workaround was to inline
the Toolbar props) and adds Log.p tracing inside
UIManager.createStyle and getComponentStyleImpl so the next iOS UI
CI run prints exactly which method enters / which step is the last
log line before the watchdog SIGTERMs the app.
Expected output: createStyle.enter for TitleArea, then
deriveBase -> getComponentStyle("Toolbar"), then
getComponentStyleImpl.enter for Toolbar. Whichever .enter has no
matching .done / .props line points at the offending step.
Revert this commit before merging.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
Developer Guide build artifacts are available for download from this workflow run:
Developer Guide quality checks: |
|
Compared 7 screenshots: 7 matched. |
Cloudflare Preview
|
|
Compared 34 screenshots: 34 matched. |
Android screenshot updatesCompared 65 screenshots: 37 matched, 28 missing references.
Native Android coverage
Benchmark ResultsDetailed Performance Metrics
|
iOS screenshot updatesCompared 36 screenshots: 35 matched, 1 error.
Benchmark Results
Build and Run Timing
|
✅ ByteCodeTranslator Quality ReportTest & Coverage
Benchmark Results
Static Analysis
Generated automatically by the PR CI workflow. |
…emplates * Root-cause fix for the iOS cn1-derive hang: UIManager.resetThemeProps unconditionally injected a defensive Toolbar.derive=TitleArea default. When a user theme declared TitleArea.derive=Toolbar (the iOS modern theme does), themeProps ended up with both directions and createStyle's derive resolver looped forever between them. Diagnosed via instrumented Log.p tracing on the derive-investigation branch (PR #4810): Toolbar resolution derived TitleArea, TitleArea derived Toolbar, lather/rinse/SIGTERM. Skip the legacy default whenever the installed theme already wires TitleArea -> Toolbar. iOS modern theme reverts the workaround that inlined Toolbar's props into TitleArea; cn1-derive: Toolbar now works correctly. * iOS Tabs polish: TabbedPane (the inner content area) gets an explicit surface fill in both light and dark blocks so the tab pages stay opaque against the textured backdrop in both modes (was opaque-light / transparent-dark). The pill TabsContainer is the only translucent piece. Tab text + icons stay neutral (#000 light / #fff dark) regardless of selection state - the selected pill now uses surface-tertiary for visible contrast instead of an accent colour. Reduced TabsContainer / Tab padding so the selected pill centers vertically inside the group. * iOS Dark Dialog opacity bumped from 0.78 to 0.95 - lower opacity let bright textured-backdrop stripes bleed through and clash with the white text. The light Dialog stays at 0.78 because the light backdrop palette is gentle enough that text contrast is fine at that level. * Generative templates: scripts/initializr and scripts/cn1playground ship a common.zip that the host app expands into newly-generated CN1 projects. Both zips's bundled common/codenameone_settings.properties now include the three modern-theme hints by default so any project spawned by initializr or by the playground starts with the liquid-glass + Material 3 themes pre-selected. The host apps' own preview settings (which I touched earlier) keep the modern themes for their in-app preview. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>




























Diagnostic-only PR (will be closed once root cause is found).
Re-enables
cn1-derive: Toolbaron TitleArea in the iOS modern themeand instruments
UIManager.createStyle+getComponentStyleImplwith
Log.ptracing so the next iOS UI CI run prints exactlywhere in the resolver the hang sits.
Expected output once the iOS UI run completes:
The last printed line names the method that loops.
Closes after diagnosis lands as a separate framework fix.