Replace the GUI Builder with a Maven-first Codename One app - #5523
Replace the GUI Builder with a Maven-first Codename One app#5523shai-almog wants to merge 91 commits into
Conversation
There was a problem hiding this comment.
Pull request overview
This PR replaces the legacy Swing “Settings-era” GUI Builder with a Maven-first, standalone Codename One desktop app under scripts/guibuilder, integrated with the Maven plugin via mvn cn1:guibuilder. It also adds the minimal core framework changes required to support the new editor (protected regions in the code editor, corrected baseline alignment in LayeredLayout, and null-safety fixes).
Changes:
- Introduces the new standalone GUI Builder app (common + JavaSE modules), demo project assets, and interaction/unit tests.
- Extends core editor/layout APIs to support protected generated regions and correct baseline alignment behavior.
- Updates Maven plugin + release/CI workflows to build, test, and publish the new GUI Builder artifact (
com.codenameone:codenameone-guibuilder).
Reviewed changes
Copilot reviewed 44 out of 45 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| scripts/guibuilder/tools/guibuilder-mcp-client.mjs | Adds a local Node MCP client for driving/inspecting the GUI Builder over MCP. |
| scripts/guibuilder/pom.xml | Adds standalone GUI Builder Maven reactor parent (Java 17) with publishing profile. |
| scripts/guibuilder/javase/src/test/java/com/codename1/guibuilder/GeneratedSourceTest.java | Verifies generated sources compile together (form + model strategies + guided constraints). |
| scripts/guibuilder/javase/src/test/java/com/codename1/guibuilder/CodeEditorInteractionTest.java | Tests protected region behavior and caret positioning in the pure editor. |
| scripts/guibuilder/javase/src/desktop/java/com/codename1/guibuilder/CodenameOneGUIBuilderStub.java | Desktop stub/launcher wiring + self-tests for editor/guided layout/interaction. |
| scripts/guibuilder/javase/src/desktop/java/com/codename1/guibuilder/CodenameOneGUIBuilderLauncher.java | Small main-class wrapper for the executable jar. |
| scripts/guibuilder/javase/pom.xml | Defines the published com.codenameone:codenameone-guibuilder JavaSE module and executable-jar profile. |
| scripts/guibuilder/demo-project/src/main/guibuilder/com/example/TableLayoutForm.gui | Demo GUI fixture for TableLayout behaviors. |
| scripts/guibuilder/demo-project/src/main/guibuilder/com/example/NestedLayoutsForm.gui | Demo GUI fixture for nested layout hierarchy behaviors. |
| scripts/guibuilder/demo-project/src/main/guibuilder/com/example/LoginForm.gui | Demo GUI fixture for a basic form. |
| scripts/guibuilder/demo-project/src/main/guibuilder/com/example/GuidedLayoutForm.gui | Demo GUI fixture for Guided Layout constraints and baseline snapping. |
| scripts/guibuilder/demo-project/src/main/guibuilder/com/example/GridLayoutForm.gui | Demo GUI fixture for GridLayout reorder/cell behaviors. |
| scripts/guibuilder/demo-project/src/main/guibuilder/com/example/BoxXLayoutForm.gui | Demo GUI fixture for horizontal BoxLayout scrolling/reorder. |
| scripts/guibuilder/demo-project/src/main/guibuilder/com/example/BorderDropForm.gui | Demo GUI fixture for BorderLayout drop/constraint behaviors. |
| scripts/guibuilder/demo-project/src/main/css/theme.css | Demo project theme for previewing styling + dark mode. |
| scripts/guibuilder/common/src/test/java/com/codename1/guibuilder/project/ProjectBindingTest.java | Unit test for parsing the modern binding format. |
| scripts/guibuilder/common/src/test/java/com/codename1/guibuilder/model/GuiDocumentTest.java | Unit tests for document editing, undo/redo, naming, relationships, drag/drop logic. |
| scripts/guibuilder/common/src/main/java/com/codename1/guibuilder/ui/GuidedLayoutSupport.java | Applies name-based Guided Layout constraints into LayeredLayout at preview/runtime. |
| scripts/guibuilder/common/src/main/java/com/codename1/guibuilder/ui/DragGuideOverlay.java | Overlay painting for drag/drop guides, selection, and simulated layout previews. |
| scripts/guibuilder/common/src/main/java/com/codename1/guibuilder/ui/ComponentPreviewFactory.java | Renders live preview components from .gui XML with designer interaction hooks. |
| scripts/guibuilder/common/src/main/java/com/codename1/guibuilder/project/ProjectIO.java | Loads binding + reads/writes GUI/CSS/source content via FileSystemStorage. |
| scripts/guibuilder/common/src/main/java/com/codename1/guibuilder/project/ProjectBinding.java | Binding model for guibuilder.input key/value format. |
| scripts/guibuilder/common/src/main/java/com/codename1/guibuilder/model/GuiDocument.java | Core .gui XML document model with transactions, undo/redo, and relationship management. |
| scripts/guibuilder/common/src/main/java/com/codename1/guibuilder/GuiBuilderMcpController.java | MCP tool registration and event/state streaming for automation/interaction tests. |
| scripts/guibuilder/common/src/main/css/theme.css | Editor UI theme (builder chrome styling + dark mode). |
| scripts/guibuilder/common/pom.xml | Common module config incl. cn1 plugin integration and test artifact attachment. |
| scripts/guibuilder/common/codenameone_settings.properties | GUI Builder CN1 settings (Java 17, desktop defaults, theme flags). |
| scripts/guibuilder/.gitignore | Ignores build output + generated binding input file for demo project. |
| maven/update-version.sh | Extends version bump script to include the new GUI Builder reactor. |
| maven/core-unittests/src/test/java/com/codename1/ui/layouts/LayeredLayoutTest.java | Adds regression test for true-baseline alignment with padding/margins. |
| maven/core-unittests/src/test/java/com/codename1/ui/CodeEditorTest.java | Adds regression tests for protected markers and caret movement. |
| maven/codenameone-maven-plugin/src/test/java/com/codename1/maven/OpenGuiBuilderMojoTest.java | Tests binding output, property forwarding, desktop identity args, and project dir detection. |
| maven/codenameone-maven-plugin/src/main/java/com/codename1/maven/OpenGuiBuilderMojo.java | Replaces legacy jar-based GUI Builder launch with Maven-resolved Java 17 editor launch. |
| CodenameOne/src/com/codename1/ui/Tabs.java | Adds null-safety around getComponentForm() during gesture handling. |
| CodenameOne/src/com/codename1/ui/layouts/LayeredLayout.java | Updates baseline unit behavior to use declared baselines only when resize behavior is declared. |
| CodenameOne/src/com/codename1/ui/editor/PureEditor.java | Adds a setCursor command for pure editor caret positioning. |
| CodenameOne/src/com/codename1/ui/editor/CodeView.java | Adds protected region markers that block edits to generated ranges. |
| CodenameOne/src/com/codename1/ui/editor/CodePureEditor.java | Wires setProtectedMarkers command into CodeView protected-region support. |
| CodenameOne/src/com/codename1/ui/CodeEditor.java | Public API for protected region markers and caret positioning. |
| CodenameOne/src/com/codename1/components/SplitPane.java | Adds null-safety around getComponentForm() during init. |
| .github/workflows/release-on-maven-central.yml | Extends release workflow to deploy/confirm/publish GUI Builder alongside other editors. |
| .github/workflows/guibuilder.yml | Adds dedicated CI workflow to build core + compile/test/package the standalone GUI Builder. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 6b3fb02eb7
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
|
Compared 151 screenshots: 151 matched. Native Android coverage
✅ Native Android screenshot tests passed. Native Android coverage
Benchmark ResultsDetailed Performance Metrics
|
✅ Continuous Quality ReportTest & Coverage
Static Analysis
Generated automatically by the PR CI workflow. |
Cloudflare Preview
|
|
Compared 181 screenshots: 181 matched. |
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 45 out of 46 changed files in this pull request and generated no new comments.
Suppressed comments (4)
CodenameOne/src/com/codename1/ui/editor/CodeView.java:163
- Edits at the exact end of a protected block (i.e., immediately after the end marker) are currently treated as "inside" the protected region because the caret check uses
<= protectedEnd. This prevents inserting text right after the generated block, which should be allowed.
if ((start == end && start >= protectedStart && start <= protectedEnd)
|| (start < protectedEnd && end > protectedStart)) {
return true;
scripts/guibuilder/tools/guibuilder-mcp-client.mjs:23
- Incoming messages are parsed with
JSON.parse(line)without error handling. If the server emits a malformed line (or the stream is corrupted), the client will crash rather than reporting a parse error and continuing.
scripts/guibuilder/common/src/main/java/com/codename1/guibuilder/project/ProjectIO.java:87 fsUrl()currently just prependsfile://without normalizing Windows paths. On Windows this will produce invalid file URLs (e.g.file://C:\Users\...), and consumers that stripfile://can end up with drive-relative paths (the same pitfall covered by scripts/settings ProjectIOTest). This can break project file reads/writes on Windows.
public static String fsUrl(String path) {
if (path == null || path.startsWith("file://") || path.indexOf("://") > 0) return path;
return "file://" + path;
}
scripts/guibuilder/tools/guibuilder-mcp-client.mjs:12
portis parsed withNumber(...)and used directly innet.createConnection(). If the env var/arg is non-numeric, this becomesNaNand the client fails with a low-signal runtime error. Validating the port early provides a clearer failure mode.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: f0099dabf7
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: a745163cb4
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 46 out of 47 changed files in this pull request and generated no new comments.
Suppressed comments (4)
CodenameOne/src/com/codename1/ui/editor/CodeView.java:163
- The protected-region check treats the end marker as inclusive for insertions (
start <= protectedEnd), which blocks edits immediately after the closing marker (i.e., at the first character following the protected block). This makes it hard to place user code right after a generated section.
if ((start == end && start >= protectedStart && start <= protectedEnd)
|| (start < protectedEnd && end > protectedStart)) {
return true;
scripts/guibuilder/tools/guibuilder-mcp-client.mjs:26
JSON.parse(line)in the socket data handler is unguarded. Any malformed/partial line from the server will throw and crash the client process, leaving pending requests unresolved.
scripts/guibuilder/tools/guibuilder-mcp-client.mjs:43request()resolves even when the JSON-RPC response contains anerrorobject, so callers proceed as if initialization/tool calls succeeded. This should reject the promise on JSON-RPC errors.
maven/codenameone-maven-plugin/src/main/java/com/codename1/maven/OpenGuiBuilderMojo.java:68- The binding file name includes a random UUID, so every
mvn cn1:guibuilderrun leaves another staleguibuilder-*.inputin~/.codenameoneGUIBuilder. Over time this can accumulate unnecessarily.
File runtimeDir = new File(System.getProperty("user.home"), ".codenameoneGUIBuilder");
runtimeDir.mkdirs();
File input = new File(runtimeDir, "guibuilder-" + UUID.randomUUID() + ".input");
writeBinding(input, projectDir, guiDir, sourceDir, cssFile);
|
Compared 148 screenshots: 148 matched. Benchmark Results
Detailed Performance Metrics
|
|
Compared 143 screenshots: 143 matched. Benchmark Results
Build and Run Timing
Detailed Performance Metrics
|
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 52 out of 53 changed files in this pull request and generated no new comments.
Suppressed comments (4)
CodenameOne/src/com/codename1/ui/editor/CodeView.java:163
- Protected-region edit detection treats an insertion exactly at the end marker boundary as protected ("<= protectedEnd"), which prevents typing immediately after a generated block. This makes it impossible to add code right after the protected end marker without first moving further away.
if ((start == end && start >= protectedStart && start <= protectedEnd)
|| (start < protectedEnd && end > protectedStart)) {
return true;
CodenameOne/src/com/codename1/ui/layouts/LayeredLayout.java:3688
- UNIT_BASELINE absolute pixel calculation mixes the reference component's current size with the target component's preferred size when calling getBaseline(). This can yield incorrect baseline offsets for components that are resized by constraints. Use the component's current size when available, with a preferred-size fallback.
int componentBaseline = declaredBaseline(cmp, cmp.getPreferredW(), cmp.getPreferredH());
scripts/guibuilder/tools/guibuilder-mcp-client.mjs:22
- The TCP client assumes every non-empty line is valid JSON and calls JSON.parse() without a try/catch. Any non-JSON line (e.g., server logging, partial/corrupt output) will throw and crash the client, leaving pending requests unresolved. Handle parse errors explicitly and continue reading.
CodenameOne/src/com/codename1/ui/layouts/LayeredLayout.java:3142 - UNIT_BASELINE alignment uses the reference component's current size (ref.getWidth()/getHeight()) but computes the moved component's baseline using its preferred size. If the component is laid out at a non-preferred size, this can misalign baselines. Use the component's current size when available (with a preferred-size fallback if size is still 0 at this point).
This issue also appears on line 3688 of the same file.
int componentBaseline = declaredBaseline(cmp, cmp.getPreferredW(), cmp.getPreferredH());
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 83742fbb96
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 52 out of 53 changed files in this pull request and generated no new comments.
Suppressed comments (1)
scripts/guibuilder/common/src/main/java/com/codename1/guibuilder/project/ProjectIO.java:88
fsUrl()doesn’t normalize Windows paths (backslashes) or handle drive-letter file URLs. This differs from the existing Settings/Game BuilderProjectIO.fsUrl()implementations and will break binding/file reads and directory creation on Windows (e.g.file://C:\...andensureParent()relying on/).
public static String fsUrl(String path) {
if (path == null || path.startsWith("file://") || path.indexOf("://") > 0) return path;
return "file://" + path;
}
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: a48a0129f7
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
|
Compared 217 screenshots: 217 matched. |
|
Compared 144 screenshots: 144 matched. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 6daf8abcb0
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
Type parameters sit before extends, so the superclass check passed while the generated class is not generic: carried members mentioning T were left in a class that has no T and the companion stopped compiling. Refused, like an unsupported superclass, rather than half-migrated. Copy on the root followed by Paste inserted a cloned Form beneath itself. The canvas substitutes a content pane container for that nested element while the generated source emits new Form(...), so the preview and the saved application stopped describing the same tree -- and the palette cannot add a screen as a child either. Paste rejects the root-only types; ordinary components are unaffected, which the test pins alongside. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 84de20afce
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
getPluginContext() is indexed by the current project, and isCN1ProjectDir() accepts both the aggregator and the app module, so running cn1:guibuilder from a generated multi-module root gave each execution its own empty map and started a second editor on the same files -- two processes writing the same .gui. The guard lives in the session's context for the top level project instead: one map per invocation, shared by every project in the reactor, and still not a JVM-wide flag, which was the previous bug where a long lived Maven never launched again. The session lookup is its own method so the composition can be tested without constructing a MavenSession, which this module has no mocking dependency for: two executions resolving to one map and writing through it, and the fallback to the per-project context when there is no session. The choice of the top level project as the key is a line of code rather than something those tests prove. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 25dec5cc01
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
Descendants are named in appendGeneratedChildren() and the root was named nowhere, so the saved screen returned null from getName() and editing the root's name in the inspector had no runtime effect -- every create-gui-form scaffold carries one. enabled, visible and rtl on the root belong to the Form, which is what the generated class calls them on: setVisible(false) hides the whole screen at runtime while the canvas, applying them to the content pane, left the surface and its toolbar on show. They go to the Form layer now, at build time and on a live inspector edit, and the content pane no longer takes them. Both follow from splitting the canvas into two layers, like the root UIID before them; this is the same mistake in the two places that were left. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: b22d76337a
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
Both legacy markers are located with markerLine(), the rule the new-format merger already used: either marker quoted in a string or mentioned in a comment above the scaffold was found first, and legacyUserMembers() then cut from that offset through to the real end marker, taking the developer's members with it. classDeclaration() matched "class " anywhere, so @register(LoginForm.class ) put one in front of the real declaration; the empty class name that came back meant the scaffolded constructors were never found and stayed beside the generated one. It requires a standalone keyword now, and the name is read after whitespace rather than after exactly one space -- legacyClassHeader() made the same assumption and is fixed with it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 2ffab07287
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
guidedReferences is pipe separated and a rename is written straight into it, so a name containing a pipe split into several sides on the next rebuild: the renamed anchor stopped resolving and its dependant jumped. The reserved characters are stripped before the name is committed -- whitespace too, since guidedReferencePositions is space separated and had the same exposure. An abstract legacy class was rebuilt as a concrete public class while its abstract members were carried across, so the companion did not compile. Refused with the type-parameter and superclass cases, and the test pins that a concrete class of the same shape still migrates. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 6a0fc87f54
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
lastIndexOf('}') found the closing brace of a helper class declared after the
form, so the carried region ran past the form's own brace and pulled most of
the helper into the regenerated class. It matches the brace that opens the
detected declaration instead, on the comment-masked copy.
The abstract check scanned backwards and stopped at an '@', so the modifiers in
"public abstract @registered class Foo" were never read and the refusal did not
fire. Annotations are blanked first, through the same helper carryClass
Annotations() now uses rather than its own copy of that loop, and "abstract" is
matched as a token rather than a substring.
A throws clause can carry a Java 8 type-use annotation, which skipThrowsClause
rejected outright -- the handler read as missing and Save appended a duplicate
signature.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 5697b1cd48
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
…uctors Bounding the carried region at the form's own brace stopped a helper class being pulled inside the regenerated class and then dropped it altogether -- a top level declaration deleted on the first save, with its callers left without a definition. It is appended after the regenerated class, where it was. The test that asserted its absence was asserting the wrong thing and now pins that it survives outside the class. "import com.example.Widget; // used by callbacks" did not end with a semicolon, so the import was dropped while the member needing it was carried. The header is read comment-masked and the statement cut at its semicolon. An annotated scaffold constructor is refused rather than silently replaced by an unannotated delegate, which leaves a class that compiles while DI no longer sees the constructor. The check had to go on the delegate path as well as the removal path: the scaffold's own constructors live inside the DON'T EDIT block and never reach removeConstructors(), so the first version of this fix did nothing for the case reported. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: f55b7ecaf9
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
Migration puts a top level declaration back after the regenerated class, but every later save takes the ordinary marker branch and rebuilds from the template, so the helper survived exactly one save and the next deleted it. That branch carries the suffix after the file's final generated marker now, the same way it already carries the imports for the same reason. The test saves three times and asserts the helper is there each time and present exactly once, since appending on every save is the obvious way to get this wrong in the other direction. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 0b7e7a9ae4
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
Every super(...) counted as scaffold work although the scaffold never wrote
one, so removing the constructor replaced a developer's super("Runtime title",
new BorderLayout()) with whatever the .gui says. Only the argument-less form,
which the compiler would insert anyway, is treated as scaffolding.
classDeclaration() took the first class in the file, so a legal package-private
helper declared before the form won: its body was read as the form's user
region and the real form ended up appended after a freshly generated class of
the same name. The declaration is located by the companion's expected class
name, with the old behaviour as the fallback.
Which makes a leading helper reachable, so it is preserved too -- dropping it
would be the trailing-helper bug on the other side, and waiting to be told
about it seemed a poor use of the review. Both are appended after the
regenerated class; order among top level declarations does not matter.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…fore The sweep caught it, but in the same command as the commit, so it went out with the previous push instead of before it. Running the two separately from now on. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: aee8682d10
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
Every this(...) was treated as scaffold work, so a constructor customised to chain through this(createTenantResources()) was removed and the generated one created the model directly -- the resource selection and any side effects gone, silently. Only this(), the scaffold's this(Resources.getGlobalResources()) and its qualified form are accepted; anything else refuses. This is the same shape as the super(...) case in the previous commit, on the statement I left behind there. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 4e46cb6c10
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
The data-change listener writes every keystroke, so by the time the editor closes the value is usually the one the document has. Writing it again snapshotted an edit containing nothing, and the next Undo restored identical text and read as broken -- worse when Undo is what closed the editor, since the asynchronous completion then landed after the revert and put the text back. The test needed two corrections before it was worth anything. It held an Element captured before the undo, which is detached once restore() reparses, so it read the old value whatever happened; and without a canvas the unguarded path died in the preview lookup, so removing the fix made the test error rather than fail its assertion. With both fixed it now fails exactly as reported -- one undo lands on "after" -- and passes with the guard. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 95f90b30d1
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
legacyResourcesConstructor() took the first ')' while removeConstructors() counts nesting, so an annotation with arguments in the parameter list split the two: the removal path recognised the constructor and took it away, the delegate path did not and added nothing, and every caller of new LoginForm(resources) stopped compiling. Both count nesting now. An annotation on that parameter is refused for the same reason a declaration annotation is: the delegate declares a plain parameter and cannot reproduce it. The two paths disagreeing is the recurring shape here -- one of them was fixed to count nesting and the other was left -- so they share the comment saying so. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 205986a973
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
Prefix matching accepted initGuiBuilderComponentsExtra() as the scaffold's own call, so removing the constructor took the developer's helper and its side effects with it. The name before the parenthesis has to be the initializer. Choosing an occupied BorderLayout region stored the request and let effectiveBorderConstraint() quietly relocate the child instead, so the picker read Center while the canvas and the generated source both kept it in North. The occupant is swapped into the region being vacated, which is what a drop into an occupied region already does, and the two now agree. Move earlier/later indexed raw XML children, so a <command> interleaved with the components was what the selection swapped across: the operation reported success and dirtied the history while the generated order did not change, and it took several clicks before anything moved. It counts component siblings. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: ab36cce029
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
The guibuilder job hit the one hour limit because of two tests I added. Both focused a real text component, and focusing one starts native editing on the simulator, which blocks the EDT that callSeriallyAndWait needs -- so they hung rather than failed, which is why I never got a result out of them locally either. They are replaced by the branch that can be driven here: with nothing editable focused the key belongs to the designer, and with no editor open the Save shortcut belongs to the form. The focused-surface half is not covered, and the tests say so rather than pretending otherwise. LiveCssTest is 8 tests in 132 seconds again. Class annotations are reapplied on every save, not only during migration -- same shape as the imports and the trailing declarations before them, and the third thing that ordinary branch has had to be taught to carry. The accessors Object already declares are reserved, so a bindable component named "Class" no longer generates a getClass() that cannot override the final one and will not compile. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: d12bcaff86
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| : "right".equals(value) ? Component.RIGHT : Component.TOP); | ||
| } else if ("layeredInsets".equals(attribute) && component.getParent() != null | ||
|
|
There was a problem hiding this comment.
Reject qualified custom superclasses with matching simple names
When a legacy companion explicitly extends a project class whose simple name matches the expected CN1 type, such as extends com.acme.Form, this comparison accepts it because simpleTypeName(base) is just Form. The regenerated declaration instead resolves Form from com.codename1.ui.*, silently discarding the custom superclass and potentially removing inherited behavior or APIs used by preserved members. Compare the qualified type when one is supplied, or refuse migration unless the superclass is the actual Codename One class.
Useful? React with 👍 / 👎.
| ((Tabs) component).setTabPlacement("bottom".equals(value) ? Component.BOTTOM : "left".equals(value) ? Component.LEFT | ||
| : "right".equals(value) ? Component.RIGHT : Component.TOP); | ||
| } else if ("layeredInsets".equals(attribute) && component.getParent() != null | ||
|
|
There was a problem hiding this comment.
Reject customized initializer arguments during migration
Fresh evidence beyond the resolved initializer-name prefix case is a scaffold constructor containing initGuiBuilderComponents(registerAndGetResources()): this check accepts every argument list as generated work, so migration removes the constructor without evaluating the developer's resource-selection helper or its side effects. Accept only known scaffold argument forms, or refuse migration when the initializer argument is a custom expression.
Useful? React with 👍 / 👎.
The Settings-era GUI Builder is a Swing tool tied to the pre-Maven project
layout. This replaces it with a standalone Codename One desktop app under
scripts/guibuilder, launched bymvn cn1:guibuilder— the same shape ascn1:settingsand the Game Builder: its own Maven build, its own executableJAR, and its own Maven Central coordinates
(
com.codenameone:codenameone-guibuilder).What it does
The editor edits
.guiXML undersrc/main/guibuilderand round-trips thegenerated Java next to it, so the design surface and the source stay in step.
Generated regions of that Java are protected in the embedded code editor rather
than merely regenerated over.
Guided Layout builds on
LayeredLayoutwith builder-owned relationships —match width/height, reference targets, anchors — stored by component name.
That is why the model enforces unique names and cascades renames, deletes, and
pastes across every relationship pointing at them; a stale name is a broken
layout and a duplicate name is a duplicate Java field.
Placement adapters cover Border, Layered, Box, Flow, Grid, and Table layouts.
The whole surface is also drivable over MCP (
-Dguibuilder.mcp.port=…), whichis how the interaction tests replay complete gestures.
Core changes
Kept to the minimum the editor needs:
CodeEditorgains protected-region markers and caret positioning.LayeredLayoutUNIT_BASELINEnow uses a component's reported baselineonly when the component also describes its baseline resize behavior. The
default
Component#getBaselinereturns the bottom content edge rather than atext baseline, so without this the documented font-ascent fallback was
unreachable and containers and text areas aligned on the wrong line.
SplitPaneandTabsno longer assumegetComponentForm()isnon-null. Both can be deinitialized by the same gesture that triggers the
callback, which the builder hits routinely when it rebuilds the inspector.
Tooling
cn1:guibuildernow forwards everyguibuilder.*property, passes the desktopidentity and
--add-exportsarguments the JavaSE runtime needs, and fails witha clear message when Maven runs on a JDK older than 17 (previously an
UnsupportedClassVersionErrorburied inguibuilder.log).scripts/**is excluded from PR CI, so.github/workflows/guibuilder.ymlisadded as the only job that compiles the editor against a freshly built core —
the exact way it can otherwise rot silently. The release workflow gains the
matching Central + R2 publish/confirm steps, wired into the completion gate.
Tests
scripts/guibuilder(JDK 21)core-unittestsCodeEditorTest,LayeredLayoutTest(JDK 8)OpenGuiBuilderMojoTestscripts/guibuilder/STATUS.mdcarries the full design notes, the knownlimitations, and the phased road map.
🤖 Generated with Claude Code