From d3b0d4afefac33786cbcba9ce70b5c6585455975 Mon Sep 17 00:00:00 2001 From: Shai Almog <67850168+shai-almog@users.noreply.github.com> Date: Fri, 15 May 2026 07:04:57 +0300 Subject: [PATCH 1/6] Blog: Skills, Java 17, and Theme Accents Weekly post covering the Initializr's switch to Java 17 by default, the bundled Codename One authoring skill (.claude/skills/codename-one/), the new runtime accent vocabulary on native themes, this week's Metal follow-up (per-axis scale decomposition, clip-under-rotation diagnostic, ios.metal.colorSpace hint, new translateMatrix API), the closed String.replace/replaceAll/replaceFirst gap, and the iOS push permission prompt moving off app launch. Co-Authored-By: Claude Opus 4.7 (1M context) --- .../blog/skills-java17-and-theme-accents.md | 252 ++++++++++++++++++ .../blog/skills-java17-and-theme-accents.jpg | Bin 0 -> 90469 bytes 2 files changed, 252 insertions(+) create mode 100644 docs/website/content/blog/skills-java17-and-theme-accents.md create mode 100644 docs/website/static/blog/skills-java17-and-theme-accents.jpg diff --git a/docs/website/content/blog/skills-java17-and-theme-accents.md b/docs/website/content/blog/skills-java17-and-theme-accents.md new file mode 100644 index 0000000000..af63f0c275 --- /dev/null +++ b/docs/website/content/blog/skills-java17-and-theme-accents.md @@ -0,0 +1,252 @@ +--- +title: Skills, Java 17, And Theme Accents +slug: skills-java17-and-theme-accents +url: /blog/skills-java17-and-theme-accents/ +date: '2026-05-15' +author: Shai Almog +description: New projects now generate on Java 17 by default and ship with a Codename One authoring skill that drops into Claude Code (or any Anthropic-compatible agent). The new native themes expose a runtime accent vocabulary so a single addThemeProps call retunes the whole app. Plus a Metal pipeline that picks up per-axis transform decomposition, a colour-space build hint, a new matrix-correct translateMatrix API, push permission that no longer fires at app launch, and the JDK 11+ String API gap closes. +feed_html: 'Skills, Java 17, And Theme Accents New projects default to Java 17 and bundle a Codename One authoring skill at .claude/skills/codename-one/. Native themes gain a runtime accent vocabulary:
UIManager.getInstance().addThemeProps(override);
retunes every accent-bearing UIID at once. Metal picks up per-axis transform decomposition, an ios.metal.colorSpace hint, and a new matrix-correct translateMatrix API. iOS push permission no longer fires at launch.' +--- + +![Skills, Java 17, And Theme Accents](/blog/skills-java17-and-theme-accents.jpg) + +Last week was Metal and the Skin Designer — two big structural pieces landing the same week. This week the diff is smaller in line count and bigger in surface area: it's the week the *output of the Initializr* changes. New projects you generate today look meaningfully different from the projects you generated three weeks ago, and the difference is the part of Codename One a brand new developer actually touches first. + +The two headline changes are the default JDK and the bundled agent skill. Both are about the same thing: the generated project should be the most modern thing we know how to ship, on day one. + +## Java 17 is the default + +[PR #4946](https://github.com/codenameone/CodenameOne/pull/4946) flips the Initializr's default Java version to **Java 17** and drops the *(Experimental)* label that has sat on it since we first added Java 11+ support. Java 8 is still selectable from the radio panel — it is now labelled **Java 8 (Legacy)** — but the preselected choice for a brand new project is Java 17. + +A few practical notes on what that actually means: + +* **Generated projects compile and run on Java 17 source/target.** Records, pattern matching for `instanceof`, sealed types, `var`, text blocks — all available in your app code from day one. The framework itself still builds with Java 8 source/target for backwards compatibility, so libraries you bring in keep working; what changes is the language level *your* code can use. +* **The Android port handles JDK 17 automatically.** This was the last lingering reason to keep Java 8 as the default. The Maven plugin now reuses the JVM it is already running on when `JAVA17_HOME` is not set, so the canonical "set `JAVA17_HOME` to a separate JDK" step is no longer mandatory — if your Maven is already on Java 17 (which the Initializr-generated project's setup instructions arrange) the build picks it up directly. +* **iOS keeps translating bytecode the same way.** ParparVM does not care which Java language version the source was written in; it ingests bytecode. So Java 17 features work in the iOS build, including text blocks and records, with no port-side changes. +* **The "Experimental" label is gone everywhere.** If you have CI templates that branch on `JavaVersion.JAVA_17_EXPERIMENTAL`, they continue to compile against the legacy constant but the canonical name is now `JAVA_17`. The label your users saw on the radio button changes from "Java 17 (Experimental)" to "Java 17", which is the more important half of this change. + +For anyone running the [Initializr](https://start.codenameone.com) today, you will see Java 17 preselected. Pick Java 8 (Legacy) if you have a hard reason to — you still get a working project — but the new default is what we now recommend. + +## The Codename One skill + +The bigger surface-area change in the same PR is the **Codename One authoring skill** that now ships inside every generated project. If you have not used Claude Code or an agent SDK yet, the short version is: a "skill" is a directory of Markdown files at a well-known location (`.claude/skills//`) that an Anthropic-compatible agent loads as task-specific knowledge whenever the user starts working in that project. It's how you tell the agent "here is the stuff that is not obvious from reading the code". + +Every project the Initializr generates from today onwards contains: + +``` +.claude/skills/codename-one/ + SKILL.md + references/ + android-to-cn1.md + build-and-run.md + build-hints.md + cn1libs.md + css.md + debugging.md + html-css-cheatsheet.md + java-api-subset.md + mobile-adaptability.md + native-interfaces.md + snapshot-builds.md + swing-comparison.md + testing-and-screenshots.md + ui-components.md + tools/ + IsApiSupported.java + IsCssValid.java + README.md +``` + +The top-level `SKILL.md` is the entry point the agent reads first. The seven `references/` files are the deeper context the agent pulls in when the conversation actually touches the topic — there is no point loading 500 lines of CSS notes into the context window when the user is asking about a `native-interfaces` question, so each reference is gated by topic. The two small Java tools under `tools/` are runnable from the agent: one checks whether a Java API is part of the Codename One subset, the other validates whether a given CSS snippet is one Codename One's CSS compiler will accept. + +Why does this matter in practice? Two things: + +**Codename One is not stock JVM.** The framework targets a Java 5/8-shaped subset of the JDK so that the same bytecode translates cleanly to iOS via ParparVM, to Android via Gradle, to JavaSE for the simulator, and to JavaScript via the JS port. An agent that has only ever read regular Java idioms will routinely suggest APIs that compile against `rt.jar` but don't exist on the device — `java.nio.file`, `java.time`, half of `java.util.concurrent`. The `java-api-subset.md` reference is the small, dense map of what is *actually* on the device, which is what you want a code-writing agent to be holding in its head. + +**Codename One CSS is not browser CSS.** Same shape, different runtime. Our compiler accepts a subset that maps to the framework's `Style` model — UIID-keyed rules, theme constants prefixed `@`, the binding vocabulary you'll see in the theme accents section below, and a handful of platform-specific keys like `cn1-derive`. The `css.md` and `html-css-cheatsheet.md` references spell out what works, and `IsCssValid.java` lets the agent verify a proposed snippet without running the simulator. The number of times "the agent wrote me a perfectly normal-looking CSS rule that the compiler silently dropped" came up in our own testing is the entire reason this file exists. + +The skill is authored as plain Markdown under `scripts/initializr/common/src/main/resources/skill/` in the repo, then bundled into a `skill.zip` at build time and unpacked into the generated project by `GeneratorModel.addClaudeSkillEntries`. The same template-token replacement that rewrites `MyAppName` and `com.example.myapp` in your `pom.xml` runs over the skill files too — when the agent reads an example snippet that says `package com.acme.todo;`, it's because the project is actually called `todo`. + +You don't need to use Claude or Claude Code to benefit. The skill is plain Markdown — `.claude/skills/codename-one/SKILL.md` is also one of the better entry-point reads we have for a developer who wants the framework's mental model in one sitting. Open it in any editor and read top to bottom. + +For agent users specifically, two notes: the directory layout is the convention Anthropic established for [Claude Code](https://claude.com/claude-code), so dropping a generated project into a working tree and starting Claude Code "just works" — the skill loads automatically and the agent introduces itself as Codename One-aware on the first message. If your team uses a different agent runtime, the references are plain Markdown — you can repackage them into whichever loader format that runtime expects. + +There is one small piece of CN1 plumbing worth pointing out, because it is the kind of thing future-you will hit if you try to extend this: Codename One's classloader doesn't tolerate nested resource directories on the JAR classpath the same way a regular JVM does. So the skill on disk lives under `scripts/initializr/common/src/main/resources/skill/`, but the build excludes that directory from the produced JAR and ships only `skill.zip`. The Initializr unpacks the zip at generation time. If you ever wondered why a couple of resource directories in the repo are shipped as zips, that is why. + +## Theme accents at runtime + +[PR #4884](https://github.com/codenameone/CodenameOne/pull/4884) is the change I'm most pleased with this week, because it closes a gap that has been quietly annoying me ever since we shipped the new iOS Modern and Material 3 themes [two weeks ago](/blog/liquid-glass-material-3-modern-native-themes/). + +When we first introduced those themes, "rebrand the app to your own colours" was a forking exercise: copy the native theme's `theme.css`, change `#007aff` to your magenta, recompile. That works in principle but the native themes ship inside the framework build, so forking is not actually something app developers can do from inside their project — you'd have to fork the framework. We knew it at the time. We shipped the themes anyway because the alternative was holding everything back on a plumbing problem. + +The plumbing problem is now solved. The shipped native themes still source-author with `var(--accent-color, fallback)` so the .css file reads cleanly, but the CSS compiler now additionally emits a `@cn1-bind:.=accent-color` constant alongside every affected style key. The .res file ships with metadata that says "Button.fgColor follows accent-color, FloatingActionButton.bgColor follows accent-color, RaisedButton.bgColor follows accent-color, ..." and the framework's `UIManager.buildTheme()` gains an `applyThemeBindings()` pass that fans an override out to every bound key in one shot. + +Translated into something you actually run, the day-to-day usage is this: + +```java +Hashtable override = new Hashtable(); +override.put("@accent-color", "ff2d95"); +override.put("@accent-color-dark", "ff2d95"); +override.put("@accent-pressed-color", "c71a75"); +override.put("@accent-on-color", "ffffff"); +// Material 3 also has a separate "container" tonal pair; iOS ignores +// it (no bindings reference it) so setting it unconditionally is safe. +override.put("@accent-container-color", "ff2d95"); +override.put("@accent-on-container-color", "ffffff"); +UIManager.getInstance().addThemeProps(override); +Form.getCurrentForm().refreshTheme(); +``` + +One call. Every accent-bearing UIID in your app — `Button.fgColor`, `RaisedButton.bgColor`, `CheckBox.selected`, `RadioButton.selected`, `OnOffSwitch.fgColor`, `BackCommand`, `TitleCommand`, `FloatingActionButton.bgColor`, and all the `.press` / `.dis` state variants — retunes to magenta. Light and dark stay independent (`@accent-color` vs `@accent-color-dark`); leaving one side alone just leaves it on its baked-in default. Values can be passed with or without the leading `#` and in any case — `"ff2d95"`, `"#FF2D95"`, and the 3-digit shorthand `"#f0a"` are all accepted. + +There is also a static path that the same machinery supports. If you don't need to swap palettes at runtime — you just want your app to launch in your brand colours — redeclare the variable inside the `#Constants` block of your own `theme.css`: + +```css +#Constants { + includeNativeBool: true; + darkModeBool: true; + + /* The compiler exports every --name in #Constants as the + matching @name theme constant, which feeds the same + binding pass that the runtime override uses. */ + --accent-color: #ff2d95; + --accent-color-dark: #ff2d95; + --accent-pressed-color: #c71a75; +} +``` + +Partial overrides are fine — anything you don't redeclare stays at the framework default. + +### The point + +I want to single out one sentence from the Native Themes chapter of the developer guide, because it's the part of this change that I think actually matters: + +> When the resolved theme constants pick up a new `@accent-color` value (whether from your CSS or via runtime `UIManager.addThemeProps`), the framework fans the override out to every bound UIID at once — no per-UIID rule duplication, no theme recompile. + +That sentence is the whole reason we built this. Every previous "rebrand to your own colours" workflow in Codename One has involved either listing every UIID in your `theme.css` and duplicating the framework's accent logic per-state-per-mode, or forking the native theme and recompiling. Both work, both have shipped real apps, both are roughly 200 lines of CSS for what should be five colours. + +This change moves the *binding* (UIID X follows accent Y) into the .res metadata once, at the framework level, and lets your app supply the *colour* in five lines. That is the core value proposition: the parts of theming that don't change per app — which UIIDs participate in the "accent" palette, which states they expose, which dark-mode counterparts they have — live inside the framework and stay there. The parts that *do* change per app — actual colours, in your brand — live in your project as five `@accent-*` constants and nothing else. + +The new vocabulary is documented in detail in the developer guide's [Native Themes chapter](https://github.com/codenameone/CodenameOne/blob/master/docs/developer-guide/Native-Themes.asciidoc#accent-palette-override), including the iOS and Android constant tables, the per-state coverage, and the small set of cases where the binding system intentionally doesn't apply (the iOS `Switch` green stays the system green; the binding system is for *accent* colours, not every colour on screen). + +Concrete example: an in-app dark/light brand toggle. Until this week that was forty lines of `addThemeProps` calls, one per UIID-and-state. Today it's the snippet above, called from the toggle's action listener. The Magenta-over-iOS-Modern light/dark screenshot pair from the `PaletteOverrideThemeScreenshotTest` ([same harness we shipped two weeks ago](/blog/liquid-glass-material-3-modern-native-themes/#runtime-palette-overrides)) keeps it honest in CI — the test now uses the same `@accent-*` constants that this section documents, and both `iOSModernTheme.res` and `AndroidMaterialTheme.res` are regenerated through the new binding-aware compiler in the same PR. + +## Metal: nuance is now the work + +Last week was *shipping* the Metal renderer. This week is the second-week curve: every shape of bug we can find in the new pipeline, characterised, fixed, regression-tested. Three PRs landed against that goal, plus a new API on `Graphics` that I think is going to quietly pay for itself many times over. + +### Per-axis scale decomposition (#4939, fixes #3302) + +The headline Metal fix this week is the alpha-mask path under non-uniform scale. Long-standing issue [#3302](https://github.com/codenameone/CodenameOne/issues/3302) had a clear repro: `g.translate + g.scale(sx, sy) + fillShape` with `sx != sy` produced shapes that visibly drifted off the axis-aligned `drawRect` / `drawLine` siblings the framework would emit alongside them. Tilted triangles inscribed in rectangles escaped their bounding rect. + +The cause: the legacy alpha-mask path rasterised the shape at a uniform scale (the diagonal ratio `h2/h1`) and then stretched the resulting texture non-uniformly through the GPU matrix to recover the requested aspect. The bbox math is exact in real numbers, but the texture is *pixel-rounded* at the intermediate uniform scale, so the stretch drifts the rasterised shape off the pixel grid that `drawRect` / `drawLine` are already on. + +The fix is the kind of small change you only land after you've stared at the symptom for a while. Factor the user transform's 2x2 linear part by taking the column norms as `(sx, sy)`. Rasterise the path at `S(sx, sy)` — so the per-axis stretch happens at rasterisation time, on the CPU, against a vector path, not against a pixel grid. Then apply only the residual `transform * S(1/sx, 1/sy)` on the GPU. The residual is pure rotation (and shear in the worst case), so no per-axis stretch happens at sample time, and the alpha-mask texture lands on the same pixel grid as its `drawRect` siblings. + +Stroke widening and the radial-gradient bbox use `sqrt(sx*sy)` so the on-screen stroke matches the legacy uniform behaviour when `sx == sy` — the old behaviour stays byte-identical for the symmetric scale case the test suite already covers. + +The change is gated to Metal: the GL ES2 path keeps its legacy `h2/h1` branch so the existing GL goldens are byte-identical. A new `InscribedTriangleGrid` screenshot test was registered with `Cn1ssDeviceRunner` that exercises `(sx, sy)` in `{1, 2}` cells under `translate + scale + drawRect + fillShape + drawShape` — the inscribed-triangle property is now visually verifiable in CI. + +### Clip-under-rotation diagnostic (#4924, towards #3921) + +[PR #4924](https://github.com/codenameone/CodenameOne/pull/4924) is the kind of PR that doesn't fix a bug, it *localises* one. Issue [#3921](https://github.com/codenameone/CodenameOne/issues/3921) is "clip-under-rotation behaves wrong on some ports", and that report is already entangled with a `getClip / setClip(int[])` round-trip limitation that the reporter himself called out as a separate issue. + +To split the two, we shipped a screenshot test that uses *only* `pushClip` / `popClip` and `rotateRadians` — no `getClip`, no `setClip(int[])`. The clip becomes non-axis-aligned via `clipRect` inside a 30-degree rotation, which is what forces the framework through its polygon-clip branch. The expected visual outcome is a 30-degree-tilted red fill that overlaps the navy outline at two diagonal corners and falls short at the other two. Two distinguishable failure modes are pre-labelled in the PR: the clip widened to its axis-aligned bbox (red exactly matches the navy outline), or the polygon clip dropped entirely (red fills the whole cell). + +When the iOS Metal cell of this test renders, we know within a glance which of the three behaviours we are looking at. The expected-failure cell is also a hypothesis: `ClipRect.m`'s polygon initialiser stores `x = y = w = h = -1`, and the Metal execute path then calls `CN1MetalSetScissor(0, 0, -2, -2)`, whose `width <= 0 / height <= 0` branch sets the scissor to the full framebuffer instead of the intended polygon. If the screenshot confirms the hypothesis, the fix is a one-line replacement of the polygon-scissor fallback. Either way, future-us no longer have to chase this through a sweep of "is it the clip API or is it the renderer". + +### iOS Metal colour space hint (#4909, fixes #4908) + +[PR #4909](https://github.com/codenameone/CodenameOne/pull/4909) adds an `ios.metal.colorSpace` build hint. Until this week, the Metal layer's `CAMetalLayer.colorspace` was hard-coded to sRGB. For most apps that is the right call — sRGB is what your existing assets are authored in. But on iPhone XR and later, Apple's screens are wide-gamut (Display P3), and a marketing-led brand that ships P3 artwork was visibly losing saturation by being routed through the sRGB pipeline. + +Accepted values are `sRGB` (default), `displayP3`, `deviceRGB`, `linearSRGB`, `extendedSRGB`, `extendedLinearSRGB`, and `none` (leaves the layer's colorspace unset and lets iOS pick the system default). Set it in `codenameone_settings.properties`: + +``` +codename1.arg.ios.metal.colorSpace=displayP3 +``` + +The hint is dormant when `ios.metal=false`, so existing GL builds are unchanged. Unrecognised values produce a warning log and fall back to sRGB, so a typo can't accidentally produce a build that won't launch. Documented under [Working-With-iOS.asciidoc](https://github.com/codenameone/CodenameOne/blob/master/docs/developer-guide/Working-With-iOS.asciidoc). + +### The new `translateMatrix` API + +The Inscribed-Triangle-Grid test in #4939 also surfaced something I think is worth pulling out as its own feature, because it's been a quiet papercut in the `Graphics` API for years. + +`Graphics.translate(int, int)` does not compose into the affine transform the way `scale()` and `rotateRadians()` do. It accumulates into a per-Graphics integer offset that is added to draw coordinates *before* the impl matrix is applied. That's a holdover from the very first version of the framework, when `Graphics` did not have a matrix at all. Today the consequence is surprising: a subsequent `g.scale(sx, sy)` multiplies the integer translate too, which means the same code produces visibly different positions depending on whether you scale before or after you translate. + +The new `Graphics.translateMatrix(float, float)` composes the translation directly onto the impl matrix, in the same way `scale` and `rotateRadians` already do. The result is "post-multiply translate onto the current transform" semantics across iOS (both GL and Metal), JavaSE, Android, and the JavaScript port. Same code, same on-screen position, whether you're drawing into a `Form`'s `Graphics` or a mutable `Image`'s `Graphics`. + +```java +// Matrix-correct composition. Use this when you want translate to +// behave like scale and rotate -- composed into the affine transform. +g.translateMatrix(centerX, centerY); +g.rotateRadians(angle); +g.scale(sx, sy); +g.translateMatrix(-centerX, -centerY); +g.fillShape(path); + +// Legacy integer accumulator. Still here, still works, still +// supported -- just be aware it is not part of the matrix. +g.translate(deltaX, deltaY); +``` + +For app code that's writing affine-transform pipelines — the "translate to pivot, rotate, scale, translate back" idiom from Java2D and AWT — this is the API you want. `isTranslateMatrixSupported()` returns true on every modern port; on the legacy JavaScript port (where the impl genuinely cannot, the matrix lives elsewhere) it returns false and the call falls back to the integer `translate(int, int)` so apps don't silently render at the wrong position. + +The old `translate(int, int)` is not deprecated and is not going anywhere — half the framework's internal scrolling code is built on it and that code is fine. The new method is the right one to reach for in *new* drawing code, particularly anything that combines translate with scale or rotate. + +## String API: `replace(CharSequence, CharSequence)`, `replaceAll`, `replaceFirst` + +[PR #4893](https://github.com/codenameone/CodenameOne/pull/4893) closes a long-standing gap reported in issue [#4878](https://github.com/codenameone/CodenameOne/issues/4878). The JDK 1.5+ overload of `String.replace` that takes `CharSequence` arguments — the one nearly every modern Java tutorial reaches for — was missing from the Codename One subset. So was `String.replaceAll(String, String)` and `String.replaceFirst(String, String)`. Code that depended on them compiled fine against the JDK at build time and then silently produced wrong output (or threw `NoSuchMethodError`) on the device. + +All three are now wired in: + +* **`String.replace(CharSequence, CharSequence)`** has a real implementation in `vm/JavaAPI` (so iOS gets it through ParparVM) and a matching stub in `Ports/CLDC11` matching the file's stubs-only convention. +* **`String.replaceAll(String, String)`** and **`String.replaceFirst(String, String)`** are wired through the bytecode-compliance rewriter to a new `JdkApiRewriteHelper.replaceAll`/`replaceFirst` pair that delegates to the existing `RE` regex engine. Same pattern we've been using for years on `String.split` — the rewriter rewrites the call at translation time so the device gets a regex-backed implementation without dragging `java.util.regex` onto the device. +* **`BytecodeComplianceMojoTest`** gains two new cases covering the `replaceAll` and `replaceFirst` rewrite rules so the compliance pipeline can't regress. + +Tactically this is a small change. Strategically it is a noticeable improvement in how often "I copied a snippet from Stack Overflow and it didn't work on iOS" turns into a real bug for an app developer. Three of the most-reached-for `String` methods in modern Java are now part of the on-device API. + +## iOS push permission no longer fires at app launch + +[PR #4894](https://github.com/codenameone/CodenameOne/pull/4894) fixes issue [#4876](https://github.com/codenameone/CodenameOne/issues/4876), which is one of those bugs where the right fix is mostly about *when* something happens rather than *whether* it happens. + +The setup: with `ios.includePush=true`, the framework used to call `requestAuthorizationWithOptions` from `application:didFinishLaunchingWithOptions:`. That meant the iOS system permission dialog — "AppName Would Like To Send You Notifications" — fired as soon as the app finished launching, before the user had seen *any* of your screens. There is no good way to recover from a "Don't Allow" tap at that point. The user hasn't experienced the app yet, doesn't know why notifications would matter, and tapping Don't Allow is the path of least resistance. Once denied, re-prompting requires sending the user out to the Settings app. + +The fix moves the prompt to the natural points it should already have been at: + +* **`Push.register()`** triggers the system prompt (this code path already requested permission inside `IOSNative.m`; we just stopped firing it ahead of time). +* **`LocalNotification.schedule()`** also triggers the system prompt, via a new `requestAuthorizationWithOptions` call in `sendLocalNotification`. + +Same flow Android has been on for years — `POST_NOTIFICATIONS` is requested when `registerPush` / `scheduleLocalNotification` runs, not at launch. The practical consequence for your app is that you can now show your own rationale screen — a one-card "we'd like to ping you when your order ships" — *before* the system dialog fires. The accept rate difference is real, and well-documented across the iOS dev community. + +If you have an app that genuinely needs the legacy launch-time behaviour — there are a few — a backwards-compatibility build hint restores it: + +``` +codename1.arg.ios.notificationPermissionAtLaunch=true +``` + +That uncomments a `CN1_NOTIFICATION_PERMISSION_AT_LAUNCH` macro in `CodenameOne_GLAppDelegate.h` which guards the re-introduced `requestAuthorizationWithOptions` block in `CodenameOne_GLAppDelegate.m`. Default is `false`, so existing apps that did not opt in pick up the new (better) behaviour on next rebuild. Documented in [Push-Notifications.asciidoc](https://github.com/codenameone/CodenameOne/blob/master/docs/developer-guide/Push-Notifications.asciidoc). + +Two things to flag explicitly for anyone updating an existing iOS app: + +1. **If your app was relying on the launch-time prompt happening automatically**, your prompt now never fires unless `Push.register()` or `LocalNotification.schedule()` is invoked somewhere. That's almost certainly what you want, but check your onboarding flow to make sure the call lands. +2. **The cloud-side build server change shipped as [BuildDaemon #71](https://github.com/codenameone/BuildDaemon/pull/71)**. If you build locally you already have everything; if you build on our build server, the change is live there too. + +## Wrapping up + +The Metal port's first week was the big swing. This week was the follow-up: per-axis scale decomposition for non-uniform transforms, a screenshot test that localises the clip-under-rotation question, the colour-space hint that wide-gamut apps have been quietly asking about, and a new matrix-correct `translateMatrix` API that makes the rest of `Graphics` behave less surprisingly. None of those alone is a headline. Together they are what "Metal is mature" looks like a week into the rollout. And as a reminder: **flip `ios.metal=true` on your real app this week** — the default flip is days away and we'd rather find any remaining edge case against your screens than against the install base on launch day. + +The Initializr changes — Java 17 as the default, the bundled authoring skill — are the part of the diff that a new developer will see first, and the part that an existing developer will pick up the next time they generate a project from scratch. Open the `SKILL.md` in any project you generate today; even if you don't use an agent, it is a reasonably tight tour of the framework. + +The accent palette work is the small change I'm most pleased with — five constants, one `addThemeProps` call, every accent-bearing UIID retunes at once, light and dark independent. Try it on your own theme this week and let us know what you find. + +A specific thank-you this week to the reporter on [#3302](https://github.com/codenameone/CodenameOne/issues/3302) for sticking with the inscribed-triangle bug for as long as the GL pipeline was the only target we had, **Durank** for the iOS push permission report on [#4876](https://github.com/codenameone/CodenameOne/issues/4876), and the reporter on [#4878](https://github.com/codenameone/CodenameOne/issues/4878) who flagged the missing `String.replace(CharSequence, CharSequence)` — that one had been sitting in the gap for a long time. + +Issue tracker is [here](https://github.com/codenameone/CodenameOne/issues), the [Playground](/playground) and [Initializr](/initializr/) are the easiest places to poke at the new defaults, and the [Skin Designer](/skindesigner/) from last week is still there if you have a device shape you need a skin for. + +--- + +## Discussion + +_Join the conversation via GitHub Discussions._ + +{{< giscus >}} diff --git a/docs/website/static/blog/skills-java17-and-theme-accents.jpg b/docs/website/static/blog/skills-java17-and-theme-accents.jpg new file mode 100644 index 0000000000000000000000000000000000000000..f57cd9156d8ef993f0b4ef8e66d9edd5c20676d8 GIT binary patch literal 90469 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 literal 0 HcmV?d00001 From eb1056a44a4cd142ca387ea681ab469e26dbea2b Mon Sep 17 00:00:00 2001 From: Shai Almog <67850168+shai-almog@users.noreply.github.com> Date: Fri, 15 May 2026 07:09:33 +0300 Subject: [PATCH 2/6] Blog: clarify the skill is vendor-neutral, not Anthropic-specific The generated project ships AGENTS.md at the root (the emerging vendor-neutral convention) and the canonical skill content under .agent-skills/codename-one/. The .claude/skills/codename-one/SKILL.md is intentionally a thin stub that redirects to the vendor-neutral location, so Claude Code's /skills picker indexes the skill without us picking sides on which loader format wins. Co-Authored-By: Claude Opus 4.7 (1M context) --- .../blog/skills-java17-and-theme-accents.md | 28 +++++++++++-------- 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/docs/website/content/blog/skills-java17-and-theme-accents.md b/docs/website/content/blog/skills-java17-and-theme-accents.md index af63f0c275..ffeec04781 100644 --- a/docs/website/content/blog/skills-java17-and-theme-accents.md +++ b/docs/website/content/blog/skills-java17-and-theme-accents.md @@ -4,8 +4,8 @@ slug: skills-java17-and-theme-accents url: /blog/skills-java17-and-theme-accents/ date: '2026-05-15' author: Shai Almog -description: New projects now generate on Java 17 by default and ship with a Codename One authoring skill that drops into Claude Code (or any Anthropic-compatible agent). The new native themes expose a runtime accent vocabulary so a single addThemeProps call retunes the whole app. Plus a Metal pipeline that picks up per-axis transform decomposition, a colour-space build hint, a new matrix-correct translateMatrix API, push permission that no longer fires at app launch, and the JDK 11+ String API gap closes. -feed_html: 'Skills, Java 17, And Theme Accents New projects default to Java 17 and bundle a Codename One authoring skill at .claude/skills/codename-one/. Native themes gain a runtime accent vocabulary:

    UIManager.getInstance().addThemeProps(override);
    retunes every accent-bearing UIID at once. Metal picks up per-axis transform decomposition, an ios.metal.colorSpace hint, and a new matrix-correct translateMatrix API. iOS push permission no longer fires at launch.' +description: New projects now generate on Java 17 by default and ship with a vendor-neutral Codename One authoring skill that any AI agent can pick up via the emerging AGENTS.md convention. The new native themes expose a runtime accent vocabulary so a single addThemeProps call retunes the whole app. Plus a Metal pipeline that picks up per-axis transform decomposition, a colour-space build hint, a new matrix-correct translateMatrix API, push permission that no longer fires at app launch, and the JDK 11+ String API gap closes. +feed_html: 'Skills, Java 17, And Theme Accents New projects default to Java 17 and bundle a vendor-neutral authoring skill at .agent-skills/codename-one/ with a top-level AGENTS.md any agent can discover. Native themes gain a runtime accent vocabulary:
    UIManager.getInstance().addThemeProps(override);
    retunes every accent-bearing UIID at once. Metal picks up per-axis transform decomposition, an ios.metal.colorSpace hint, and a new matrix-correct translateMatrix API. iOS push permission no longer fires at launch.' --- ![Skills, Java 17, And Theme Accents](/blog/skills-java17-and-theme-accents.jpg) @@ -29,13 +29,14 @@ For anyone running the [Initializr](https://start.codenameone.com) today, you wi ## The Codename One skill -The bigger surface-area change in the same PR is the **Codename One authoring skill** that now ships inside every generated project. If you have not used Claude Code or an agent SDK yet, the short version is: a "skill" is a directory of Markdown files at a well-known location (`.claude/skills//`) that an Anthropic-compatible agent loads as task-specific knowledge whenever the user starts working in that project. It's how you tell the agent "here is the stuff that is not obvious from reading the code". +The bigger surface-area change in the same PR is the **Codename One authoring skill** that now ships inside every generated project — and it's vendor-neutral by design. There's an emerging convention called [`AGENTS.md`](https://agents.md) that proposes a single, universal entry-point file at the root of a repository for *any* AI agent — Claude Code, Cursor, Codex, Aider, future tools we haven't heard of — to discover project-specific guidance. Generated projects now ship one, and the actual skill content lives at a vendor-neutral path beside it. Every project the Initializr generates from today onwards contains: ``` -.claude/skills/codename-one/ - SKILL.md +AGENTS.md # universal entry point any agent can discover +.agent-skills/codename-one/ + SKILL.md # canonical top-level cheat sheet references/ android-to-cn1.md build-and-run.md @@ -55,21 +56,24 @@ Every project the Initializr generates from today onwards contains: IsApiSupported.java IsCssValid.java README.md +.claude/skills/codename-one/SKILL.md # thin stub that redirects to .agent-skills/ ``` -The top-level `SKILL.md` is the entry point the agent reads first. The seven `references/` files are the deeper context the agent pulls in when the conversation actually touches the topic — there is no point loading 500 lines of CSS notes into the context window when the user is asking about a `native-interfaces` question, so each reference is gated by topic. The two small Java tools under `tools/` are runnable from the agent: one checks whether a Java API is part of the Codename One subset, the other validates whether a given CSS snippet is one Codename One's CSS compiler will accept. +The top-level `AGENTS.md` is what any agent sees first. It explains in a dozen lines that this is a Codename One project, points at `.agent-skills/codename-one/SKILL.md` as the canonical content, and gives a four-line orientation: where the app source lives, where the CSS lives, the simulator command, the test command. That's enough for an agent that has never heard of Codename One to find its footing without us picking sides on which vendor's loader format to ship. -Why does this matter in practice? Two things: +The canonical content lives under `.agent-skills/codename-one/`. `SKILL.md` is the entry-point cheat sheet, the seven `references/` files are the deeper context the agent pulls in when the conversation actually touches the topic (no point loading 500 lines of CSS notes when the user is asking a `native-interfaces` question), and the two small Java tools under `tools/` are runnable single-file `java`-source-mode utilities: one checks whether a Java API is part of the Codename One subset, the other validates whether a given CSS snippet is one Codename One's CSS compiler will accept. + +The `.claude/skills/codename-one/SKILL.md` you also see in the tree is intentionally just a stub. Claude Code's skill picker indexes files at that path with a `name:` / `description:` frontmatter block, so we ship a tiny one purely so the skill shows up by name in `/skills`. The stub body redirects to the same `.agent-skills/codename-one/SKILL.md` that any other agent reads. If a future agent runtime invents its own well-known location, the fix is one more thin stub at that path — the *content* stays in one place. + +Why does this skill matter in practice? Two things: **Codename One is not stock JVM.** The framework targets a Java 5/8-shaped subset of the JDK so that the same bytecode translates cleanly to iOS via ParparVM, to Android via Gradle, to JavaSE for the simulator, and to JavaScript via the JS port. An agent that has only ever read regular Java idioms will routinely suggest APIs that compile against `rt.jar` but don't exist on the device — `java.nio.file`, `java.time`, half of `java.util.concurrent`. The `java-api-subset.md` reference is the small, dense map of what is *actually* on the device, which is what you want a code-writing agent to be holding in its head. **Codename One CSS is not browser CSS.** Same shape, different runtime. Our compiler accepts a subset that maps to the framework's `Style` model — UIID-keyed rules, theme constants prefixed `@`, the binding vocabulary you'll see in the theme accents section below, and a handful of platform-specific keys like `cn1-derive`. The `css.md` and `html-css-cheatsheet.md` references spell out what works, and `IsCssValid.java` lets the agent verify a proposed snippet without running the simulator. The number of times "the agent wrote me a perfectly normal-looking CSS rule that the compiler silently dropped" came up in our own testing is the entire reason this file exists. -The skill is authored as plain Markdown under `scripts/initializr/common/src/main/resources/skill/` in the repo, then bundled into a `skill.zip` at build time and unpacked into the generated project by `GeneratorModel.addClaudeSkillEntries`. The same template-token replacement that rewrites `MyAppName` and `com.example.myapp` in your `pom.xml` runs over the skill files too — when the agent reads an example snippet that says `package com.acme.todo;`, it's because the project is actually called `todo`. - -You don't need to use Claude or Claude Code to benefit. The skill is plain Markdown — `.claude/skills/codename-one/SKILL.md` is also one of the better entry-point reads we have for a developer who wants the framework's mental model in one sitting. Open it in any editor and read top to bottom. +The skill is authored as plain Markdown under `scripts/initializr/common/src/main/resources/skill/` in the repo, then bundled into a `skill.zip` at build time and unpacked into the generated project by `GeneratorModel.addAgentSkillEntries`. The same template-token replacement that rewrites `MyAppName` and `com.example.myapp` in your `pom.xml` runs over the skill files too — when the agent reads an example snippet that says `package com.acme.todo;`, it's because the project is actually called `todo`. -For agent users specifically, two notes: the directory layout is the convention Anthropic established for [Claude Code](https://claude.com/claude-code), so dropping a generated project into a working tree and starting Claude Code "just works" — the skill loads automatically and the agent introduces itself as Codename One-aware on the first message. If your team uses a different agent runtime, the references are plain Markdown — you can repackage them into whichever loader format that runtime expects. +You don't need to use any agent at all to benefit. The skill is plain Markdown, ASCII-only — `.agent-skills/codename-one/SKILL.md` is also one of the better entry-point reads we have for a developer who wants the framework's mental model in one sitting. Open it in any editor and read top to bottom. There is one small piece of CN1 plumbing worth pointing out, because it is the kind of thing future-you will hit if you try to extend this: Codename One's classloader doesn't tolerate nested resource directories on the JAR classpath the same way a regular JVM does. So the skill on disk lives under `scripts/initializr/common/src/main/resources/skill/`, but the build excludes that directory from the produced JAR and ships only `skill.zip`. The Initializr unpacks the zip at generation time. If you ever wondered why a couple of resource directories in the repo are shipped as zips, that is why. @@ -235,7 +239,7 @@ Two things to flag explicitly for anyone updating an existing iOS app: The Metal port's first week was the big swing. This week was the follow-up: per-axis scale decomposition for non-uniform transforms, a screenshot test that localises the clip-under-rotation question, the colour-space hint that wide-gamut apps have been quietly asking about, and a new matrix-correct `translateMatrix` API that makes the rest of `Graphics` behave less surprisingly. None of those alone is a headline. Together they are what "Metal is mature" looks like a week into the rollout. And as a reminder: **flip `ios.metal=true` on your real app this week** — the default flip is days away and we'd rather find any remaining edge case against your screens than against the install base on launch day. -The Initializr changes — Java 17 as the default, the bundled authoring skill — are the part of the diff that a new developer will see first, and the part that an existing developer will pick up the next time they generate a project from scratch. Open the `SKILL.md` in any project you generate today; even if you don't use an agent, it is a reasonably tight tour of the framework. +The Initializr changes — Java 17 as the default, the bundled vendor-neutral authoring skill — are the part of the diff that a new developer will see first, and the part that an existing developer will pick up the next time they generate a project from scratch. Open `.agent-skills/codename-one/SKILL.md` in any project you generate today; even if you don't use an agent, it is a reasonably tight tour of the framework. The top-level `AGENTS.md` is twelve lines — read that one even sooner. The accent palette work is the small change I'm most pleased with — five constants, one `addThemeProps` call, every accent-bearing UIID retunes at once, light and dark independent. Try it on your own theme this week and let us know what you find. From 2993275db1f61400b80d40dcc9b61e1733a1d869 Mon Sep 17 00:00:00 2001 From: Shai Almog <67850168+shai-almog@users.noreply.github.com> Date: Fri, 15 May 2026 07:12:29 +0300 Subject: [PATCH 3/6] Blog: trim description and rephrase the Initializr-output sentence Co-Authored-By: Claude Opus 4.7 (1M context) --- docs/website/content/blog/skills-java17-and-theme-accents.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/website/content/blog/skills-java17-and-theme-accents.md b/docs/website/content/blog/skills-java17-and-theme-accents.md index ffeec04781..b87670052e 100644 --- a/docs/website/content/blog/skills-java17-and-theme-accents.md +++ b/docs/website/content/blog/skills-java17-and-theme-accents.md @@ -4,13 +4,13 @@ slug: skills-java17-and-theme-accents url: /blog/skills-java17-and-theme-accents/ date: '2026-05-15' author: Shai Almog -description: New projects now generate on Java 17 by default and ship with a vendor-neutral Codename One authoring skill that any AI agent can pick up via the emerging AGENTS.md convention. The new native themes expose a runtime accent vocabulary so a single addThemeProps call retunes the whole app. Plus a Metal pipeline that picks up per-axis transform decomposition, a colour-space build hint, a new matrix-correct translateMatrix API, push permission that no longer fires at app launch, and the JDK 11+ String API gap closes. +description: Java 17 is the new Initializr default, generated projects ship a vendor-neutral AGENTS.md authoring skill, native themes get a runtime accent palette, plus Metal follow-ups and iOS push that no longer prompts at launch. feed_html: 'Skills, Java 17, And Theme Accents New projects default to Java 17 and bundle a vendor-neutral authoring skill at .agent-skills/codename-one/ with a top-level AGENTS.md any agent can discover. Native themes gain a runtime accent vocabulary:
    UIManager.getInstance().addThemeProps(override);
    retunes every accent-bearing UIID at once. Metal picks up per-axis transform decomposition, an ios.metal.colorSpace hint, and a new matrix-correct translateMatrix API. iOS push permission no longer fires at launch.' --- ![Skills, Java 17, And Theme Accents](/blog/skills-java17-and-theme-accents.jpg) -Last week was Metal and the Skin Designer — two big structural pieces landing the same week. This week the diff is smaller in line count and bigger in surface area: it's the week the *output of the Initializr* changes. New projects you generate today look meaningfully different from the projects you generated three weeks ago, and the difference is the part of Codename One a brand new developer actually touches first. +Last week was Metal and the Skin Designer — two big structural pieces landing the same week. This week the diff is smaller in line count and bigger in surface area: the projects the Initializr hands you have changed. Generate a new app today and it looks meaningfully different from one you generated three weeks ago, and the difference is the part of Codename One a brand new developer actually touches first. The two headline changes are the default JDK and the bundled agent skill. Both are about the same thing: the generated project should be the most modern thing we know how to ship, on day one. From af7ac97f83672684df93cd4b1e802e713b2ba7c5 Mon Sep 17 00:00:00 2001 From: Shai Almog <67850168+shai-almog@users.noreply.github.com> Date: Fri, 15 May 2026 09:01:29 +0300 Subject: [PATCH 4/6] Blog: revise per Shai's feedback (voice, structure, trims) - Sync description and feed_html - Drop em-dashes throughout - Rewrite Java 17 section in Shai's voice: focus on the future of CN1, builds on any JDK 17+, Java 8 still selectable (don't call it Legacy, the radio just says "Java 8") - Link to /blog/official-experimental-java-17-support/ instead of repeating the practical-notes bullets - AGENTS.md is the convention -- no "emerging convention" framing - Drop the file tree and link to the skill source in git instead - Highlight what the skill actually enables: agents driving jdb against the simulator, IsApiSupported / IsCssValid tools - Explain the Java 17 only scope (older build constraints made the picture confusing for agents) - Drop the Hashtable example on theme accents; keep the CSS path and point at the developer guide for the runtime override - Remove "surface area" wording Co-Authored-By: Claude Opus 4.7 (1M context) --- .../blog/skills-java17-and-theme-accents.md | 199 +++++------------- 1 file changed, 48 insertions(+), 151 deletions(-) diff --git a/docs/website/content/blog/skills-java17-and-theme-accents.md b/docs/website/content/blog/skills-java17-and-theme-accents.md index b87670052e..4879291a72 100644 --- a/docs/website/content/blog/skills-java17-and-theme-accents.md +++ b/docs/website/content/blog/skills-java17-and-theme-accents.md @@ -4,246 +4,143 @@ slug: skills-java17-and-theme-accents url: /blog/skills-java17-and-theme-accents/ date: '2026-05-15' author: Shai Almog -description: Java 17 is the new Initializr default, generated projects ship a vendor-neutral AGENTS.md authoring skill, native themes get a runtime accent palette, plus Metal follow-ups and iOS push that no longer prompts at launch. -feed_html: 'Skills, Java 17, And Theme Accents New projects default to Java 17 and bundle a vendor-neutral authoring skill at .agent-skills/codename-one/ with a top-level AGENTS.md any agent can discover. Native themes gain a runtime accent vocabulary:
    UIManager.getInstance().addThemeProps(override);
    retunes every accent-bearing UIID at once. Metal picks up per-axis transform decomposition, an ios.metal.colorSpace hint, and a new matrix-correct translateMatrix API. iOS push permission no longer fires at launch.' +description: Java 17 is the new Initializr default, generated projects ship an AGENTS.md authoring skill that any AI agent can pick up (including a workflow that lets agents drive jdb against the simulator), native themes get a runtime accent palette, plus Metal follow-ups and iOS push that no longer prompts at launch. +feed_html: 'Skills, Java 17, And Theme Accents Java 17 is the new Initializr default, generated projects ship an AGENTS.md authoring skill that any AI agent can pick up (including a workflow that lets agents drive
    jdb
    against the simulator), native themes get a runtime accent palette, plus Metal follow-ups and iOS push that no longer prompts at launch.' --- ![Skills, Java 17, And Theme Accents](/blog/skills-java17-and-theme-accents.jpg) -Last week was Metal and the Skin Designer — two big structural pieces landing the same week. This week the diff is smaller in line count and bigger in surface area: the projects the Initializr hands you have changed. Generate a new app today and it looks meaningfully different from one you generated three weeks ago, and the difference is the part of Codename One a brand new developer actually touches first. +Last week was about Metal and the Skin Designer. This week the headline items are about what a brand new project looks like when you generate it: the default JDK is Java 17, and every generated project ships with an `AGENTS.md` authoring skill that lets any modern AI agent work on the project intelligently. There are also some other things worth covering: a runtime accent palette on the new native themes, three Metal follow-ups (one of which introduces a new matrix-correct translate API), the JDK 11+ String API gap closed, and iOS push permission that no longer fires at app launch. -The two headline changes are the default JDK and the bundled agent skill. Both are about the same thing: the generated project should be the most modern thing we know how to ship, on day one. +## Java 17 by default -## Java 17 is the default +We changed the default projects generated by the Initializr to Java 17+ to focus on the future of Codename One. The existing Java 8 option in the Initializr is still selectable from the radio panel if you have a reason to use it. Pick whichever you want. -[PR #4946](https://github.com/codenameone/CodenameOne/pull/4946) flips the Initializr's default Java version to **Java 17** and drops the *(Experimental)* label that has sat on it since we first added Java 11+ support. Java 8 is still selectable from the radio panel — it is now labelled **Java 8 (Legacy)** — but the preselected choice for a brand new project is Java 17. +The Java 17 path is the one we now recommend for new work. Generated projects build with any JDK from 17 onwards (we routinely test on 21 and 25); you do not need to install Java 17 specifically. The bigger picture of how Java 17 support works in the toolchain, including which language features land in your app code and how the iOS / Android ports handle the newer bytecode, was covered in [Official Experimental Java 17 Support](/blog/official-experimental-java-17-support/) earlier this year. The change this week is the default and the wording: the *(Experimental)* tag is gone, and Java 17 is now what you get unless you opt out. -A few practical notes on what that actually means: +## AGENTS.md and the Codename One skill -* **Generated projects compile and run on Java 17 source/target.** Records, pattern matching for `instanceof`, sealed types, `var`, text blocks — all available in your app code from day one. The framework itself still builds with Java 8 source/target for backwards compatibility, so libraries you bring in keep working; what changes is the language level *your* code can use. -* **The Android port handles JDK 17 automatically.** This was the last lingering reason to keep Java 8 as the default. The Maven plugin now reuses the JVM it is already running on when `JAVA17_HOME` is not set, so the canonical "set `JAVA17_HOME` to a separate JDK" step is no longer mandatory — if your Maven is already on Java 17 (which the Initializr-generated project's setup instructions arrange) the build picks it up directly. -* **iOS keeps translating bytecode the same way.** ParparVM does not care which Java language version the source was written in; it ingests bytecode. So Java 17 features work in the iOS build, including text blocks and records, with no port-side changes. -* **The "Experimental" label is gone everywhere.** If you have CI templates that branch on `JavaVersion.JAVA_17_EXPERIMENTAL`, they continue to compile against the legacy constant but the canonical name is now `JAVA_17`. The label your users saw on the radio button changes from "Java 17 (Experimental)" to "Java 17", which is the more important half of this change. +The other change in [PR #4946](https://github.com/codenameone/CodenameOne/pull/4946) is that every Java 17 project the Initializr generates now ships an `AGENTS.md` file at the project root and a Codename One authoring skill alongside it. -For anyone running the [Initializr](https://start.codenameone.com) today, you will see Java 17 preselected. Pick Java 8 (Legacy) if you have a hard reason to — you still get a working project — but the new default is what we now recommend. +`AGENTS.md` is the convention for handing project-specific context to any AI agent. Claude Code, Cursor, Codex, Aider; they all look for it. Codename One projects now ship one. The actual skill content lives under `.agent-skills/codename-one/` (vendor-neutral) and the source for it is in the repo at [`scripts/initializr/common/src/main/resources/skill`](https://github.com/codenameone/CodenameOne/tree/master/scripts/initializr/common/src/main/resources/skill) if you want to read through it directly. There is also a thin stub at `.claude/skills/codename-one/SKILL.md` so Claude Code's `/skills` picker indexes it; the stub redirects to the same vendor-neutral content. -## The Codename One skill +We deliberately scoped this to Java 17 projects. The older Java 8 build had additional constraints (Java 5/8 source target, retrolambda, the historical bytecode rewrite rules) that made the "what can I actually use" answer noticeably more complicated. Restricting the skill to Java 17 lets us give agents a cleaner picture of the language level, the toolchain, and the build commands without spending half the SKILL.md on caveats. If you stay on Java 8, you keep the project layout you had; nothing changes for you. -The bigger surface-area change in the same PR is the **Codename One authoring skill** that now ships inside every generated project — and it's vendor-neutral by design. There's an emerging convention called [`AGENTS.md`](https://agents.md) that proposes a single, universal entry-point file at the root of a repository for *any* AI agent — Claude Code, Cursor, Codex, Aider, future tools we haven't heard of — to discover project-specific guidance. Generated projects now ship one, and the actual skill content lives at a vendor-neutral path beside it. +A few things the skill makes possible that I think are genuinely useful: -Every project the Initializr generates from today onwards contains: +**Agents can debug a Codename One app under `jdb`.** This is the one I am most pleased with. The simulator is a regular JVM, so the standard Java Debugger attaches cleanly, but agents previously had no idea this workflow was available. The skill's `debugging.md` reference walks through starting the simulator with the right `-Xrunjdwp` flags, attaching `jdb`, setting breakpoints, dumping locals, and stepping. The same workflow works in CI and any headless context where a graphical debugger is not an option. For an LLM that is otherwise reduced to "add a `println` and hope", this is a much sharper tool. -``` -AGENTS.md # universal entry point any agent can discover -.agent-skills/codename-one/ - SKILL.md # canonical top-level cheat sheet - references/ - android-to-cn1.md - build-and-run.md - build-hints.md - cn1libs.md - css.md - debugging.md - html-css-cheatsheet.md - java-api-subset.md - mobile-adaptability.md - native-interfaces.md - snapshot-builds.md - swing-comparison.md - testing-and-screenshots.md - ui-components.md - tools/ - IsApiSupported.java - IsCssValid.java - README.md -.claude/skills/codename-one/SKILL.md # thin stub that redirects to .agent-skills/ -``` - -The top-level `AGENTS.md` is what any agent sees first. It explains in a dozen lines that this is a Codename One project, points at `.agent-skills/codename-one/SKILL.md` as the canonical content, and gives a four-line orientation: where the app source lives, where the CSS lives, the simulator command, the test command. That's enough for an agent that has never heard of Codename One to find its footing without us picking sides on which vendor's loader format to ship. - -The canonical content lives under `.agent-skills/codename-one/`. `SKILL.md` is the entry-point cheat sheet, the seven `references/` files are the deeper context the agent pulls in when the conversation actually touches the topic (no point loading 500 lines of CSS notes when the user is asking a `native-interfaces` question), and the two small Java tools under `tools/` are runnable single-file `java`-source-mode utilities: one checks whether a Java API is part of the Codename One subset, the other validates whether a given CSS snippet is one Codename One's CSS compiler will accept. - -The `.claude/skills/codename-one/SKILL.md` you also see in the tree is intentionally just a stub. Claude Code's skill picker indexes files at that path with a `name:` / `description:` frontmatter block, so we ship a tiny one purely so the skill shows up by name in `/skills`. The stub body redirects to the same `.agent-skills/codename-one/SKILL.md` that any other agent reads. If a future agent runtime invents its own well-known location, the fix is one more thin stub at that path — the *content* stays in one place. - -Why does this skill matter in practice? Two things: - -**Codename One is not stock JVM.** The framework targets a Java 5/8-shaped subset of the JDK so that the same bytecode translates cleanly to iOS via ParparVM, to Android via Gradle, to JavaSE for the simulator, and to JavaScript via the JS port. An agent that has only ever read regular Java idioms will routinely suggest APIs that compile against `rt.jar` but don't exist on the device — `java.nio.file`, `java.time`, half of `java.util.concurrent`. The `java-api-subset.md` reference is the small, dense map of what is *actually* on the device, which is what you want a code-writing agent to be holding in its head. - -**Codename One CSS is not browser CSS.** Same shape, different runtime. Our compiler accepts a subset that maps to the framework's `Style` model — UIID-keyed rules, theme constants prefixed `@`, the binding vocabulary you'll see in the theme accents section below, and a handful of platform-specific keys like `cn1-derive`. The `css.md` and `html-css-cheatsheet.md` references spell out what works, and `IsCssValid.java` lets the agent verify a proposed snippet without running the simulator. The number of times "the agent wrote me a perfectly normal-looking CSS rule that the compiler silently dropped" came up in our own testing is the entire reason this file exists. +**Agents can check whether an API is part of the Codename One subset before they suggest it.** Codename One targets a Java 5/8 shaped JDK so the same bytecode translates to iOS, Android, and JavaScript. An agent that has only read regular Java idioms will routinely reach for `java.nio.file`, `java.time`, or pieces of `java.util.concurrent` that the framework does not include. The skill ships a single-file `IsApiSupported.java` tool that an agent can invoke to verify a class or method before writing code against it. -The skill is authored as plain Markdown under `scripts/initializr/common/src/main/resources/skill/` in the repo, then bundled into a `skill.zip` at build time and unpacked into the generated project by `GeneratorModel.addAgentSkillEntries`. The same template-token replacement that rewrites `MyAppName` and `com.example.myapp` in your `pom.xml` runs over the skill files too — when the agent reads an example snippet that says `package com.acme.todo;`, it's because the project is actually called `todo`. +**Agents can validate a CSS snippet before applying it.** Codename One CSS is its own subset; rules that look fine to a browser developer get silently dropped by the compiler. The `IsCssValid.java` tool lets the agent confirm the compiler will accept a snippet without booting the simulator. -You don't need to use any agent at all to benefit. The skill is plain Markdown, ASCII-only — `.agent-skills/codename-one/SKILL.md` is also one of the better entry-point reads we have for a developer who wants the framework's mental model in one sitting. Open it in any editor and read top to bottom. +These three things together are most of why an agent that was previously polite-but-not-useful on a Codename One project is now actually productive on one. If you do not use agents, the same Markdown is one of the better tours of the framework's mental model that we have written; open `.agent-skills/codename-one/SKILL.md` in any project you generate today and read top to bottom. -There is one small piece of CN1 plumbing worth pointing out, because it is the kind of thing future-you will hit if you try to extend this: Codename One's classloader doesn't tolerate nested resource directories on the JAR classpath the same way a regular JVM does. So the skill on disk lives under `scripts/initializr/common/src/main/resources/skill/`, but the build excludes that directory from the produced JAR and ships only `skill.zip`. The Initializr unpacks the zip at generation time. If you ever wondered why a couple of resource directories in the repo are shipped as zips, that is why. - -## Theme accents at runtime - -[PR #4884](https://github.com/codenameone/CodenameOne/pull/4884) is the change I'm most pleased with this week, because it closes a gap that has been quietly annoying me ever since we shipped the new iOS Modern and Material 3 themes [two weeks ago](/blog/liquid-glass-material-3-modern-native-themes/). - -When we first introduced those themes, "rebrand the app to your own colours" was a forking exercise: copy the native theme's `theme.css`, change `#007aff` to your magenta, recompile. That works in principle but the native themes ship inside the framework build, so forking is not actually something app developers can do from inside their project — you'd have to fork the framework. We knew it at the time. We shipped the themes anyway because the alternative was holding everything back on a plumbing problem. - -The plumbing problem is now solved. The shipped native themes still source-author with `var(--accent-color, fallback)` so the .css file reads cleanly, but the CSS compiler now additionally emits a `@cn1-bind:.=accent-color` constant alongside every affected style key. The .res file ships with metadata that says "Button.fgColor follows accent-color, FloatingActionButton.bgColor follows accent-color, RaisedButton.bgColor follows accent-color, ..." and the framework's `UIManager.buildTheme()` gains an `applyThemeBindings()` pass that fans an override out to every bound key in one shot. - -Translated into something you actually run, the day-to-day usage is this: - -```java -Hashtable override = new Hashtable(); -override.put("@accent-color", "ff2d95"); -override.put("@accent-color-dark", "ff2d95"); -override.put("@accent-pressed-color", "c71a75"); -override.put("@accent-on-color", "ffffff"); -// Material 3 also has a separate "container" tonal pair; iOS ignores -// it (no bindings reference it) so setting it unconditionally is safe. -override.put("@accent-container-color", "ff2d95"); -override.put("@accent-on-container-color", "ffffff"); -UIManager.getInstance().addThemeProps(override); -Form.getCurrentForm().refreshTheme(); -``` +## Native theme accents -One call. Every accent-bearing UIID in your app — `Button.fgColor`, `RaisedButton.bgColor`, `CheckBox.selected`, `RadioButton.selected`, `OnOffSwitch.fgColor`, `BackCommand`, `TitleCommand`, `FloatingActionButton.bgColor`, and all the `.press` / `.dis` state variants — retunes to magenta. Light and dark stay independent (`@accent-color` vs `@accent-color-dark`); leaving one side alone just leaves it on its baked-in default. Values can be passed with or without the leading `#` and in any case — `"ff2d95"`, `"#FF2D95"`, and the 3-digit shorthand `"#f0a"` are all accepted. +[PR #4884](https://github.com/codenameone/CodenameOne/pull/4884) closes the loop on the new iOS Modern and Material 3 native themes [we shipped two weeks ago](/blog/liquid-glass-material-3-modern-native-themes/). The native themes now expose their accent palette as named theme constants, so rebranding your app to your own colours is a five-line CSS change instead of a fork. -There is also a static path that the same machinery supports. If you don't need to swap palettes at runtime — you just want your app to launch in your brand colours — redeclare the variable inside the `#Constants` block of your own `theme.css`: +Override the constants inside the `#Constants` block of your own `theme.css`: ```css #Constants { includeNativeBool: true; darkModeBool: true; - /* The compiler exports every --name in #Constants as the - matching @name theme constant, which feeds the same - binding pass that the runtime override uses. */ --accent-color: #ff2d95; --accent-color-dark: #ff2d95; --accent-pressed-color: #c71a75; + --accent-on-color: #ffffff; } ``` -Partial overrides are fine — anything you don't redeclare stays at the framework default. +That is it. Every accent-bearing UIID picks up the new colour. Light and dark are independent (`--accent-color` vs `--accent-color-dark`), and partial overrides are fine; anything you do not redeclare stays at the framework default. Material 3 has a couple of additional container-tier constants for the elevated-surface tone; iOS ignores those. -### The point +There is also a runtime path for dynamic theming (in-app accent toggles, branded flavours, A/B tests). It uses the same constants. The Native Themes chapter of the developer guide covers it in detail, along with the full iOS and Android constant tables and the places where the binding system intentionally does not apply: [Accent palette override](https://github.com/codenameone/CodenameOne/blob/master/docs/developer-guide/Native-Themes.asciidoc#accent-palette-override). -I want to single out one sentence from the Native Themes chapter of the developer guide, because it's the part of this change that I think actually matters: +The point worth pulling out: the parts of theming that do not change per app (which UIIDs participate in the accent palette, which states they expose, which dark-mode counterparts they have) live inside the framework and stay there. The parts that do change per app (your colours) live in your project as five constants and nothing else. That is the whole reason this change exists. -> When the resolved theme constants pick up a new `@accent-color` value (whether from your CSS or via runtime `UIManager.addThemeProps`), the framework fans the override out to every bound UIID at once — no per-UIID rule duplication, no theme recompile. +## Metal follow-ups -That sentence is the whole reason we built this. Every previous "rebrand to your own colours" workflow in Codename One has involved either listing every UIID in your `theme.css` and duplicating the framework's accent logic per-state-per-mode, or forking the native theme and recompiling. Both work, both have shipped real apps, both are roughly 200 lines of CSS for what should be five colours. - -This change moves the *binding* (UIID X follows accent Y) into the .res metadata once, at the framework level, and lets your app supply the *colour* in five lines. That is the core value proposition: the parts of theming that don't change per app — which UIIDs participate in the "accent" palette, which states they expose, which dark-mode counterparts they have — live inside the framework and stay there. The parts that *do* change per app — actual colours, in your brand — live in your project as five `@accent-*` constants and nothing else. - -The new vocabulary is documented in detail in the developer guide's [Native Themes chapter](https://github.com/codenameone/CodenameOne/blob/master/docs/developer-guide/Native-Themes.asciidoc#accent-palette-override), including the iOS and Android constant tables, the per-state coverage, and the small set of cases where the binding system intentionally doesn't apply (the iOS `Switch` green stays the system green; the binding system is for *accent* colours, not every colour on screen). - -Concrete example: an in-app dark/light brand toggle. Until this week that was forty lines of `addThemeProps` calls, one per UIID-and-state. Today it's the snippet above, called from the toggle's action listener. The Magenta-over-iOS-Modern light/dark screenshot pair from the `PaletteOverrideThemeScreenshotTest` ([same harness we shipped two weeks ago](/blog/liquid-glass-material-3-modern-native-themes/#runtime-palette-overrides)) keeps it honest in CI — the test now uses the same `@accent-*` constants that this section documents, and both `iOSModernTheme.res` and `AndroidMaterialTheme.res` are regenerated through the new binding-aware compiler in the same PR. - -## Metal: nuance is now the work - -Last week was *shipping* the Metal renderer. This week is the second-week curve: every shape of bug we can find in the new pipeline, characterised, fixed, regression-tested. Three PRs landed against that goal, plus a new API on `Graphics` that I think is going to quietly pay for itself many times over. +Last week was about shipping the Metal renderer. This week is the follow-up week: three PRs, plus one new API on `Graphics` that I think will quietly pay for itself many times over. ### Per-axis scale decomposition (#4939, fixes #3302) -The headline Metal fix this week is the alpha-mask path under non-uniform scale. Long-standing issue [#3302](https://github.com/codenameone/CodenameOne/issues/3302) had a clear repro: `g.translate + g.scale(sx, sy) + fillShape` with `sx != sy` produced shapes that visibly drifted off the axis-aligned `drawRect` / `drawLine` siblings the framework would emit alongside them. Tilted triangles inscribed in rectangles escaped their bounding rect. - -The cause: the legacy alpha-mask path rasterised the shape at a uniform scale (the diagonal ratio `h2/h1`) and then stretched the resulting texture non-uniformly through the GPU matrix to recover the requested aspect. The bbox math is exact in real numbers, but the texture is *pixel-rounded* at the intermediate uniform scale, so the stretch drifts the rasterised shape off the pixel grid that `drawRect` / `drawLine` are already on. +Long-standing issue [#3302](https://github.com/codenameone/CodenameOne/issues/3302) had a clear repro: `g.translate + g.scale(sx, sy) + fillShape` with `sx != sy` produced shapes that visibly drifted off the axis-aligned `drawRect` and `drawLine` calls the framework emitted alongside them. Triangles inscribed in rectangles escaped their bounding rect. -The fix is the kind of small change you only land after you've stared at the symptom for a while. Factor the user transform's 2x2 linear part by taking the column norms as `(sx, sy)`. Rasterise the path at `S(sx, sy)` — so the per-axis stretch happens at rasterisation time, on the CPU, against a vector path, not against a pixel grid. Then apply only the residual `transform * S(1/sx, 1/sy)` on the GPU. The residual is pure rotation (and shear in the worst case), so no per-axis stretch happens at sample time, and the alpha-mask texture lands on the same pixel grid as its `drawRect` siblings. +The cause was that the legacy alpha-mask path rasterised the shape at a uniform scale (the diagonal ratio `h2/h1`), then stretched the resulting texture non-uniformly through the GPU matrix to recover the requested aspect. The bbox math is exact in real numbers, but the texture is pixel-rounded at the intermediate uniform scale, so the stretch drifted the rasterised shape off the pixel grid that `drawRect` and `drawLine` were already on. -Stroke widening and the radial-gradient bbox use `sqrt(sx*sy)` so the on-screen stroke matches the legacy uniform behaviour when `sx == sy` — the old behaviour stays byte-identical for the symmetric scale case the test suite already covers. +The fix factors the user transform's 2x2 linear part by taking the column norms as `(sx, sy)`, rasterises the path at `S(sx, sy)` so the per-axis stretch happens at rasterisation time against a vector path rather than a pixel grid, and applies only the residual `transform * S(1/sx, 1/sy)` on the GPU. The residual is pure rotation (and shear in the worst case), so no per-axis stretch happens at sample time and the alpha-mask texture lands on the same pixel grid as its `drawRect` siblings. -The change is gated to Metal: the GL ES2 path keeps its legacy `h2/h1` branch so the existing GL goldens are byte-identical. A new `InscribedTriangleGrid` screenshot test was registered with `Cn1ssDeviceRunner` that exercises `(sx, sy)` in `{1, 2}` cells under `translate + scale + drawRect + fillShape + drawShape` — the inscribed-triangle property is now visually verifiable in CI. +The change is gated to Metal; the GL ES2 path keeps its legacy branch so the existing GL goldens are byte-identical. A new `InscribedTriangleGrid` screenshot test was registered with `Cn1ssDeviceRunner` so the inscribed-triangle property is now visually verifiable in CI. ### Clip-under-rotation diagnostic (#4924, towards #3921) -[PR #4924](https://github.com/codenameone/CodenameOne/pull/4924) is the kind of PR that doesn't fix a bug, it *localises* one. Issue [#3921](https://github.com/codenameone/CodenameOne/issues/3921) is "clip-under-rotation behaves wrong on some ports", and that report is already entangled with a `getClip / setClip(int[])` round-trip limitation that the reporter himself called out as a separate issue. +[PR #4924](https://github.com/codenameone/CodenameOne/pull/4924) does not fix a bug, it localises one. Issue [#3921](https://github.com/codenameone/CodenameOne/issues/3921) is "clip-under-rotation behaves wrong on some ports", entangled with a `getClip` / `setClip(int[])` round-trip limitation the reporter himself called out as a separate issue. To split the two, we shipped a screenshot test that uses only `pushClip` / `popClip` and `rotateRadians`. The clip becomes non-axis-aligned via `clipRect` inside a 30-degree rotation, which forces the framework through its polygon-clip branch. -To split the two, we shipped a screenshot test that uses *only* `pushClip` / `popClip` and `rotateRadians` — no `getClip`, no `setClip(int[])`. The clip becomes non-axis-aligned via `clipRect` inside a 30-degree rotation, which is what forces the framework through its polygon-clip branch. The expected visual outcome is a 30-degree-tilted red fill that overlaps the navy outline at two diagonal corners and falls short at the other two. Two distinguishable failure modes are pre-labelled in the PR: the clip widened to its axis-aligned bbox (red exactly matches the navy outline), or the polygon clip dropped entirely (red fills the whole cell). - -When the iOS Metal cell of this test renders, we know within a glance which of the three behaviours we are looking at. The expected-failure cell is also a hypothesis: `ClipRect.m`'s polygon initialiser stores `x = y = w = h = -1`, and the Metal execute path then calls `CN1MetalSetScissor(0, 0, -2, -2)`, whose `width <= 0 / height <= 0` branch sets the scissor to the full framebuffer instead of the intended polygon. If the screenshot confirms the hypothesis, the fix is a one-line replacement of the polygon-scissor fallback. Either way, future-us no longer have to chase this through a sweep of "is it the clip API or is it the renderer". +The expected outcome is a 30-degree-tilted red fill that overlaps the navy outline at two diagonal corners and falls short at the other two. Two distinguishable failure modes are pre-labelled in the PR: the clip widened to its axis-aligned bbox (red exactly matches the navy outline), or the polygon clip dropped entirely (red fills the whole cell). When the iOS Metal cell of this test renders, we know within a glance which of the three behaviours we are looking at. The expected-failure cell is also a hypothesis: `ClipRect.m`'s polygon initialiser stores `x = y = w = h = -1`, and the Metal execute path then calls `CN1MetalSetScissor(0, 0, -2, -2)`, whose `width <= 0 / height <= 0` branch sets the scissor to the full framebuffer instead of the intended polygon. If the screenshot confirms the hypothesis, the fix is a one-line replacement of the polygon-scissor fallback. ### iOS Metal colour space hint (#4909, fixes #4908) -[PR #4909](https://github.com/codenameone/CodenameOne/pull/4909) adds an `ios.metal.colorSpace` build hint. Until this week, the Metal layer's `CAMetalLayer.colorspace` was hard-coded to sRGB. For most apps that is the right call — sRGB is what your existing assets are authored in. But on iPhone XR and later, Apple's screens are wide-gamut (Display P3), and a marketing-led brand that ships P3 artwork was visibly losing saturation by being routed through the sRGB pipeline. +[PR #4909](https://github.com/codenameone/CodenameOne/pull/4909) adds an `ios.metal.colorSpace` build hint. Until this week, the Metal layer's `CAMetalLayer.colorspace` was hard-coded to sRGB. For most apps that is right; sRGB is what your existing assets are authored in. But on iPhone XR and later, Apple's screens are wide-gamut (Display P3), and a marketing-led brand that ships P3 artwork was visibly losing saturation by being routed through the sRGB pipeline. -Accepted values are `sRGB` (default), `displayP3`, `deviceRGB`, `linearSRGB`, `extendedSRGB`, `extendedLinearSRGB`, and `none` (leaves the layer's colorspace unset and lets iOS pick the system default). Set it in `codenameone_settings.properties`: +Accepted values are `sRGB` (default), `displayP3`, `deviceRGB`, `linearSRGB`, `extendedSRGB`, `extendedLinearSRGB`, and `none`. Set it in `codenameone_settings.properties`: ``` codename1.arg.ios.metal.colorSpace=displayP3 ``` -The hint is dormant when `ios.metal=false`, so existing GL builds are unchanged. Unrecognised values produce a warning log and fall back to sRGB, so a typo can't accidentally produce a build that won't launch. Documented under [Working-With-iOS.asciidoc](https://github.com/codenameone/CodenameOne/blob/master/docs/developer-guide/Working-With-iOS.asciidoc). +The hint is dormant when `ios.metal=false`, so existing GL builds are unchanged. Unrecognised values produce a warning log and fall back to sRGB. Documented under [Working-With-iOS.asciidoc](https://github.com/codenameone/CodenameOne/blob/master/docs/developer-guide/Working-With-iOS.asciidoc). ### The new `translateMatrix` API -The Inscribed-Triangle-Grid test in #4939 also surfaced something I think is worth pulling out as its own feature, because it's been a quiet papercut in the `Graphics` API for years. +The Inscribed-Triangle-Grid test in #4939 also surfaced a quiet papercut in `Graphics` that is worth pulling out as its own feature. -`Graphics.translate(int, int)` does not compose into the affine transform the way `scale()` and `rotateRadians()` do. It accumulates into a per-Graphics integer offset that is added to draw coordinates *before* the impl matrix is applied. That's a holdover from the very first version of the framework, when `Graphics` did not have a matrix at all. Today the consequence is surprising: a subsequent `g.scale(sx, sy)` multiplies the integer translate too, which means the same code produces visibly different positions depending on whether you scale before or after you translate. +`Graphics.translate(int, int)` does not compose into the affine transform the way `scale()` and `rotateRadians()` do. It accumulates into a per-Graphics integer offset that is added to draw coordinates before the impl matrix is applied. That is a holdover from the very first version of the framework, when `Graphics` did not have a matrix at all. Today the consequence is surprising: a subsequent `g.scale(sx, sy)` multiplies the integer translate too, which means the same code produces visibly different positions depending on whether you scale before or after you translate. -The new `Graphics.translateMatrix(float, float)` composes the translation directly onto the impl matrix, in the same way `scale` and `rotateRadians` already do. The result is "post-multiply translate onto the current transform" semantics across iOS (both GL and Metal), JavaSE, Android, and the JavaScript port. Same code, same on-screen position, whether you're drawing into a `Form`'s `Graphics` or a mutable `Image`'s `Graphics`. +The new `Graphics.translateMatrix(float, float)` composes the translation directly onto the impl matrix, in the same way `scale` and `rotateRadians` already do. The result is uniform "post-multiply translate onto the current transform" semantics across iOS (both GL and Metal), JavaSE, Android, and the JavaScript port. Same code, same on-screen position, whether you are drawing into a `Form`'s `Graphics` or a mutable `Image`'s `Graphics`. ```java // Matrix-correct composition. Use this when you want translate to -// behave like scale and rotate -- composed into the affine transform. +// behave like scale and rotate (composed into the affine transform). g.translateMatrix(centerX, centerY); g.rotateRadians(angle); g.scale(sx, sy); g.translateMatrix(-centerX, -centerY); g.fillShape(path); - -// Legacy integer accumulator. Still here, still works, still -// supported -- just be aware it is not part of the matrix. -g.translate(deltaX, deltaY); ``` -For app code that's writing affine-transform pipelines — the "translate to pivot, rotate, scale, translate back" idiom from Java2D and AWT — this is the API you want. `isTranslateMatrixSupported()` returns true on every modern port; on the legacy JavaScript port (where the impl genuinely cannot, the matrix lives elsewhere) it returns false and the call falls back to the integer `translate(int, int)` so apps don't silently render at the wrong position. - -The old `translate(int, int)` is not deprecated and is not going anywhere — half the framework's internal scrolling code is built on it and that code is fine. The new method is the right one to reach for in *new* drawing code, particularly anything that combines translate with scale or rotate. +For app code writing affine-transform pipelines (the "translate to pivot, rotate, scale, translate back" idiom from Java2D and AWT), this is the API you want. `isTranslateMatrixSupported()` returns true on every modern port. The old `translate(int, int)` is not deprecated and is not going anywhere; half the framework's internal scrolling code is built on it. The new method is the one to reach for in new drawing code, particularly anything that combines translate with scale or rotate. ## String API: `replace(CharSequence, CharSequence)`, `replaceAll`, `replaceFirst` -[PR #4893](https://github.com/codenameone/CodenameOne/pull/4893) closes a long-standing gap reported in issue [#4878](https://github.com/codenameone/CodenameOne/issues/4878). The JDK 1.5+ overload of `String.replace` that takes `CharSequence` arguments — the one nearly every modern Java tutorial reaches for — was missing from the Codename One subset. So was `String.replaceAll(String, String)` and `String.replaceFirst(String, String)`. Code that depended on them compiled fine against the JDK at build time and then silently produced wrong output (or threw `NoSuchMethodError`) on the device. - -All three are now wired in: +[PR #4893](https://github.com/codenameone/CodenameOne/pull/4893) closes a long-standing gap reported in issue [#4878](https://github.com/codenameone/CodenameOne/issues/4878). The JDK 1.5+ overload of `String.replace` that takes `CharSequence` arguments (the one nearly every modern Java tutorial reaches for) was missing from the Codename One subset. So were `String.replaceAll(String, String)` and `String.replaceFirst(String, String)`. Code that depended on them compiled fine against the JDK and then silently produced wrong output (or threw `NoSuchMethodError`) on the device. -* **`String.replace(CharSequence, CharSequence)`** has a real implementation in `vm/JavaAPI` (so iOS gets it through ParparVM) and a matching stub in `Ports/CLDC11` matching the file's stubs-only convention. -* **`String.replaceAll(String, String)`** and **`String.replaceFirst(String, String)`** are wired through the bytecode-compliance rewriter to a new `JdkApiRewriteHelper.replaceAll`/`replaceFirst` pair that delegates to the existing `RE` regex engine. Same pattern we've been using for years on `String.split` — the rewriter rewrites the call at translation time so the device gets a regex-backed implementation without dragging `java.util.regex` onto the device. -* **`BytecodeComplianceMojoTest`** gains two new cases covering the `replaceAll` and `replaceFirst` rewrite rules so the compliance pipeline can't regress. +All three are now wired in. `String.replace(CharSequence, CharSequence)` has a real implementation in `vm/JavaAPI`. `replaceAll` and `replaceFirst` are wired through the bytecode-compliance rewriter to a new `JdkApiRewriteHelper` pair that delegates to the existing `RE` regex engine (the same pattern we have been using for years on `String.split`). New compliance tests cover both rewrite rules. -Tactically this is a small change. Strategically it is a noticeable improvement in how often "I copied a snippet from Stack Overflow and it didn't work on iOS" turns into a real bug for an app developer. Three of the most-reached-for `String` methods in modern Java are now part of the on-device API. +It is a small change in line count. In practice it is a noticeable reduction in how often "I copied a snippet from Stack Overflow and it didn't work on iOS" turns into a real bug. Three of the most-reached-for `String` methods in modern Java are now part of the on-device API. ## iOS push permission no longer fires at app launch -[PR #4894](https://github.com/codenameone/CodenameOne/pull/4894) fixes issue [#4876](https://github.com/codenameone/CodenameOne/issues/4876), which is one of those bugs where the right fix is mostly about *when* something happens rather than *whether* it happens. - -The setup: with `ios.includePush=true`, the framework used to call `requestAuthorizationWithOptions` from `application:didFinishLaunchingWithOptions:`. That meant the iOS system permission dialog — "AppName Would Like To Send You Notifications" — fired as soon as the app finished launching, before the user had seen *any* of your screens. There is no good way to recover from a "Don't Allow" tap at that point. The user hasn't experienced the app yet, doesn't know why notifications would matter, and tapping Don't Allow is the path of least resistance. Once denied, re-prompting requires sending the user out to the Settings app. - -The fix moves the prompt to the natural points it should already have been at: +[PR #4894](https://github.com/codenameone/CodenameOne/pull/4894) fixes issue [#4876](https://github.com/codenameone/CodenameOne/issues/4876). With `ios.includePush=true` the framework used to call `requestAuthorizationWithOptions` from `application:didFinishLaunchingWithOptions:`, which meant the iOS system permission dialog fired as soon as the app finished launching, before the user had seen any of your screens. There is no good way to recover from a "Don't Allow" tap at that point. The user has not experienced the app yet, does not know why notifications matter, and tapping Don't Allow is the path of least resistance. Once denied, re-prompting requires sending the user out to Settings. -* **`Push.register()`** triggers the system prompt (this code path already requested permission inside `IOSNative.m`; we just stopped firing it ahead of time). -* **`LocalNotification.schedule()`** also triggers the system prompt, via a new `requestAuthorizationWithOptions` call in `sendLocalNotification`. +The fix moves the prompt to the natural points. `Push.register()` triggers the system prompt (this code path already requested permission inside `IOSNative.m`; we just stopped firing it ahead of time). `LocalNotification.schedule()` also triggers it, via a new `requestAuthorizationWithOptions` call in `sendLocalNotification`. Same flow Android has been on for years. The practical consequence is that you can now show your own rationale screen ("we'd like to ping you when your order ships") before the system dialog fires. -Same flow Android has been on for years — `POST_NOTIFICATIONS` is requested when `registerPush` / `scheduleLocalNotification` runs, not at launch. The practical consequence for your app is that you can now show your own rationale screen — a one-card "we'd like to ping you when your order ships" — *before* the system dialog fires. The accept rate difference is real, and well-documented across the iOS dev community. - -If you have an app that genuinely needs the legacy launch-time behaviour — there are a few — a backwards-compatibility build hint restores it: +If you have an app that needs the legacy launch-time behaviour, a backwards-compatibility build hint restores it: ``` codename1.arg.ios.notificationPermissionAtLaunch=true ``` -That uncomments a `CN1_NOTIFICATION_PERMISSION_AT_LAUNCH` macro in `CodenameOne_GLAppDelegate.h` which guards the re-introduced `requestAuthorizationWithOptions` block in `CodenameOne_GLAppDelegate.m`. Default is `false`, so existing apps that did not opt in pick up the new (better) behaviour on next rebuild. Documented in [Push-Notifications.asciidoc](https://github.com/codenameone/CodenameOne/blob/master/docs/developer-guide/Push-Notifications.asciidoc). - -Two things to flag explicitly for anyone updating an existing iOS app: +The default is `false`, so existing apps that did not opt in pick up the new behaviour on next rebuild. Documented in [Push-Notifications.asciidoc](https://github.com/codenameone/CodenameOne/blob/master/docs/developer-guide/Push-Notifications.asciidoc). The cloud-side build server change shipped as [BuildDaemon #71](https://github.com/codenameone/BuildDaemon/pull/71), so local and cloud builds match. -1. **If your app was relying on the launch-time prompt happening automatically**, your prompt now never fires unless `Push.register()` or `LocalNotification.schedule()` is invoked somewhere. That's almost certainly what you want, but check your onboarding flow to make sure the call lands. -2. **The cloud-side build server change shipped as [BuildDaemon #71](https://github.com/codenameone/BuildDaemon/pull/71)**. If you build locally you already have everything; if you build on our build server, the change is live there too. +One thing to flag if you are updating an existing iOS app: if your onboarding flow was relying on the launch-time prompt happening automatically, your prompt now never fires unless `Push.register()` or `LocalNotification.schedule()` is invoked somewhere. That is almost certainly what you want, but check that the call lands. ## Wrapping up -The Metal port's first week was the big swing. This week was the follow-up: per-axis scale decomposition for non-uniform transforms, a screenshot test that localises the clip-under-rotation question, the colour-space hint that wide-gamut apps have been quietly asking about, and a new matrix-correct `translateMatrix` API that makes the rest of `Graphics` behave less surprisingly. None of those alone is a headline. Together they are what "Metal is mature" looks like a week into the rollout. And as a reminder: **flip `ios.metal=true` on your real app this week** — the default flip is days away and we'd rather find any remaining edge case against your screens than against the install base on launch day. - -The Initializr changes — Java 17 as the default, the bundled vendor-neutral authoring skill — are the part of the diff that a new developer will see first, and the part that an existing developer will pick up the next time they generate a project from scratch. Open `.agent-skills/codename-one/SKILL.md` in any project you generate today; even if you don't use an agent, it is a reasonably tight tour of the framework. The top-level `AGENTS.md` is twelve lines — read that one even sooner. - -The accent palette work is the small change I'm most pleased with — five constants, one `addThemeProps` call, every accent-bearing UIID retunes at once, light and dark independent. Try it on your own theme this week and let us know what you find. +Two reminders. First, **flip `ios.metal=true` on your real app this week** if you have not. The default flip is days away and we would rather find any remaining edge case against your screens than against the install base on launch day. Second, if you have not generated a project from the [Initializr](/initializr/) recently, do it; the Java 17 default and the `AGENTS.md` skill are both worth seeing for yourself. -A specific thank-you this week to the reporter on [#3302](https://github.com/codenameone/CodenameOne/issues/3302) for sticking with the inscribed-triangle bug for as long as the GL pipeline was the only target we had, **Durank** for the iOS push permission report on [#4876](https://github.com/codenameone/CodenameOne/issues/4876), and the reporter on [#4878](https://github.com/codenameone/CodenameOne/issues/4878) who flagged the missing `String.replace(CharSequence, CharSequence)` — that one had been sitting in the gap for a long time. +A specific thank-you this week to the reporter on [#3302](https://github.com/codenameone/CodenameOne/issues/3302) for sticking with the inscribed-triangle bug for as long as GL was the only target, **Durank** for the iOS push permission report on [#4876](https://github.com/codenameone/CodenameOne/issues/4876), and the reporter on [#4878](https://github.com/codenameone/CodenameOne/issues/4878) who flagged the missing `String.replace(CharSequence, CharSequence)`; that one had been sitting in the gap for a long time. Issue tracker is [here](https://github.com/codenameone/CodenameOne/issues), the [Playground](/playground) and [Initializr](/initializr/) are the easiest places to poke at the new defaults, and the [Skin Designer](/skindesigner/) from last week is still there if you have a device shape you need a skin for. From 48c2e075cc6068109b91e927d21d943017fbc340 Mon Sep 17 00:00:00 2001 From: Shai Almog <67850168+shai-almog@users.noreply.github.com> Date: Fri, 15 May 2026 09:15:13 +0300 Subject: [PATCH 5/6] Blog: correct the String API description -- methods weren't on the bootclasspath The previous wording implied the calls compiled and then failed at runtime. They didn't compile at all because the methods were absent from the bootclasspath; you had to know to use replace(char,char) and roll your own regex. Co-Authored-By: Claude Opus 4.7 (1M context) --- docs/website/content/blog/skills-java17-and-theme-accents.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/website/content/blog/skills-java17-and-theme-accents.md b/docs/website/content/blog/skills-java17-and-theme-accents.md index 4879291a72..e6e9549546 100644 --- a/docs/website/content/blog/skills-java17-and-theme-accents.md +++ b/docs/website/content/blog/skills-java17-and-theme-accents.md @@ -114,7 +114,7 @@ For app code writing affine-transform pipelines (the "translate to pivot, rotate ## String API: `replace(CharSequence, CharSequence)`, `replaceAll`, `replaceFirst` -[PR #4893](https://github.com/codenameone/CodenameOne/pull/4893) closes a long-standing gap reported in issue [#4878](https://github.com/codenameone/CodenameOne/issues/4878). The JDK 1.5+ overload of `String.replace` that takes `CharSequence` arguments (the one nearly every modern Java tutorial reaches for) was missing from the Codename One subset. So were `String.replaceAll(String, String)` and `String.replaceFirst(String, String)`. Code that depended on them compiled fine against the JDK and then silently produced wrong output (or threw `NoSuchMethodError`) on the device. +[PR #4893](https://github.com/codenameone/CodenameOne/pull/4893) closes a long-standing gap reported in issue [#4878](https://github.com/codenameone/CodenameOne/issues/4878). The JDK 1.5+ overload of `String.replace` that takes `CharSequence` arguments (the one nearly every modern Java tutorial reaches for) was missing from the Codename One subset. So were `String.replaceAll(String, String)` and `String.replaceFirst(String, String)`. Because none of the three were on the bootclasspath, code that reached for them did not compile against a Codename One project at all; you had to know to fall back to the older `replace(char, char)` overload and to roll your own regex. All three are now wired in. `String.replace(CharSequence, CharSequence)` has a real implementation in `vm/JavaAPI`. `replaceAll` and `replaceFirst` are wired through the bytecode-compliance rewriter to a new `JdkApiRewriteHelper` pair that delegates to the existing `RE` regex engine (the same pattern we have been using for years on `String.split`). New compliance tests cover both rewrite rules. From 50fb3d9083c599c8f85bacd05e4edbe6e579e9a7 Mon Sep 17 00:00:00 2001 From: Shai Almog <67850168+shai-almog@users.noreply.github.com> Date: Fri, 15 May 2026 09:18:50 +0300 Subject: [PATCH 6/6] Blog: add Skin Designer FAQ follow-up linking discussion #4928 Three points from the discussion: skins do not affect CSS, the defaults usually work for a known device (customization is for refinement when our database is incomplete), and themes are migrating out of skins into Maven-distributed framework themes. Co-Authored-By: Claude Opus 4.7 (1M context) --- .../content/blog/skills-java17-and-theme-accents.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/docs/website/content/blog/skills-java17-and-theme-accents.md b/docs/website/content/blog/skills-java17-and-theme-accents.md index e6e9549546..49560c8259 100644 --- a/docs/website/content/blog/skills-java17-and-theme-accents.md +++ b/docs/website/content/blog/skills-java17-and-theme-accents.md @@ -136,6 +136,16 @@ The default is `false`, so existing apps that did not opt in pick up the new beh One thing to flag if you are updating an existing iOS app: if your onboarding flow was relying on the launch-time prompt happening automatically, your prompt now never fires unless `Push.register()` or `LocalNotification.schedule()` is invoked somewhere. That is almost certainly what you want, but check that the call lands. +## Skin Designer FAQ follow-up + +A few questions came up on [discussion #4928](https://github.com/codenameone/CodenameOne/discussions/4928) after last week's Skin Designer post, worth pulling forward here because they keep coming up in the same shape: + +* **Skins do not affect CSS.** The skin is simulator scaffolding (device frame, screen rect, cutouts, safe-area insets); your `theme.css` and your native theme are unrelated. +* **For a known device, the defaults are usually right.** Pick the device, hit *Pick a shape*, click *Finish*. The customization UI is there for when our device database is incomplete (the iPhone 17e entry might say "no notch" when it actually has one, or the notch position might be off by a few pixels); when you have a physical device to measure against, that is where you refine. +* **Themes are leaving skins.** Historically the native theme was bundled inside each skin because that is what made sense at the time. Going forward the right home for themes is the framework itself, distributed via Maven, so you pick up updates automatically. The new native themes already work this way. The per-skin embedded theme stays for legacy compatibility and the Skin Designer still writes one for you, but the *Native Theme* menu we shipped two weeks ago is the path forward. + +The device database the Skin Designer reads from is open at [`scripts/skindesigner/common/src/main/resources/devices.json`](https://github.com/codenameone/CodenameOne/blob/master/scripts/skindesigner/common/src/main/resources/devices.json) if you want to file a PR with a device we are missing or a row whose details are off. + ## Wrapping up Two reminders. First, **flip `ios.metal=true` on your real app this week** if you have not. The default flip is days away and we would rather find any remaining edge case against your screens than against the install base on launch day. Second, if you have not generated a project from the [Initializr](/initializr/) recently, do it; the Java 17 default and the `AGENTS.md` skill are both worth seeing for yourself.