From 6b3fb02eb754f20f51373291cd862ef02e3765bd Mon Sep 17 00:00:00 2001 From: Shai Almog <67850168+shai-almog@users.noreply.github.com> Date: Wed, 5 Aug 2026 14:20:29 +0700 Subject: [PATCH 01/33] Add the Maven-first GUI Builder rewrite The Settings-era GUI Builder is a Swing tool tied to the old project layout. This replaces it with a standalone Codename One desktop app under scripts/guibuilder, launched by `mvn cn1:guibuilder`, in the same shape as `cn1:settings` and the Game Builder: its own Maven build, its own executable JAR, and its own Maven Central coordinates. The editor edits `.gui` XML in `src/main/guibuilder` and round-trips the generated Java next to it, so the design surface and the source stay in step. Guided Layout builds on LayeredLayout with builder-owned, name-based relationships (match width/height, reference targets, anchors), which is why the model enforces unique component names and cascades renames, deletes, and pastes across every relationship that points at them. Core changes are the minimum the editor needs: - CodeEditor gains protected-region markers and caret positioning, so the generated regions of a form's Java cannot be edited by hand. - LayeredLayout UNIT_BASELINE now only uses a component's reported baseline when the component also describes its baseline resize behavior. The default `Component#getBaseline` returns the bottom content edge rather than a text baseline, so without this the documented font-ascent fallback was unreachable and containers and text areas aligned on the wrong line. - SplitPane and Tabs no longer assume `getComponentForm()` is non-null; both can be deinitialized by the same gesture that triggers the callback, which the builder hits routinely when it rebuilds the inspector. `cn1:guibuilder` now forwards every `guibuilder.*` property, passes the desktop identity and `--add-exports` arguments the JavaSE runtime needs, and fails with a clear message when Maven is running on a JDK older than 17. scripts/** is excluded from PR CI, so .github/workflows/guibuilder.yml is added as the only job that compiles the editor against a freshly built core. Co-Authored-By: Claude Opus 5 (1M context) --- .github/workflows/guibuilder.yml | 104 + .../workflows/release-on-maven-central.yml | 100 +- .../com/codename1/components/SplitPane.java | 4 +- .../src/com/codename1/ui/CodeEditor.java | 12 + CodenameOne/src/com/codename1/ui/Tabs.java | 5 +- .../codename1/ui/editor/CodePureEditor.java | 7 + .../src/com/codename1/ui/editor/CodeView.java | 53 + .../com/codename1/ui/editor/PureEditor.java | 15 + .../codename1/ui/layouts/LayeredLayout.java | 55 +- .../codename1/maven/OpenGuiBuilderMojo.java | 459 +- .../maven/OpenGuiBuilderMojoTest.java | 80 + .../java/com/codename1/ui/CodeEditorTest.java | 46 + .../ui/layouts/LayeredLayoutTest.java | 23 + maven/update-version.sh | 14 + scripts/guibuilder/.gitignore | 7 + scripts/guibuilder/STATUS.md | 1153 +++++ .../common/codenameone_settings.properties | 12 + scripts/guibuilder/common/pom.xml | 20 + .../guibuilder/common/src/main/css/theme.css | 430 ++ .../guibuilder/CodenameOneGUIBuilder.java | 4558 +++++++++++++++++ .../guibuilder/GuiBuilderMcpController.java | 208 + .../guibuilder/model/GuiDocument.java | 636 +++ .../guibuilder/project/ProjectBinding.java | 38 + .../guibuilder/project/ProjectIO.java | 88 + .../ui/ComponentPreviewFactory.java | 329 ++ .../guibuilder/ui/DragGuideOverlay.java | 471 ++ .../guibuilder/ui/GuidedLayoutSupport.java | 141 + .../guibuilder/model/GuiDocumentTest.java | 354 ++ .../project/ProjectBindingTest.java | 19 + .../demo-project/src/main/css/theme.css | 49 + .../guibuilder/com/example/BorderDropForm.gui | 8 + .../guibuilder/com/example/BoxXLayoutForm.gui | 13 + .../guibuilder/com/example/GridLayoutForm.gui | 7 + .../com/example/GuidedLayoutForm.gui | 10 + .../main/guibuilder/com/example/LoginForm.gui | 9 + .../com/example/NestedLayoutsForm.gui | 16 + .../com/example/TableLayoutForm.gui | 8 + scripts/guibuilder/javase/pom.xml | 38 + .../CodenameOneGUIBuilderLauncher.java | 8 + .../guibuilder/CodenameOneGUIBuilderStub.java | 589 +++ .../guibuilder/CodeEditorInteractionTest.java | 104 + .../guibuilder/DesignerInteractionTest.java | 1334 +++++ .../guibuilder/GeneratedSourceTest.java | 116 + scripts/guibuilder/pom.xml | 51 + .../tools/guibuilder-mcp-client.mjs | 95 + 45 files changed, 11653 insertions(+), 243 deletions(-) create mode 100644 .github/workflows/guibuilder.yml create mode 100644 maven/codenameone-maven-plugin/src/test/java/com/codename1/maven/OpenGuiBuilderMojoTest.java create mode 100644 scripts/guibuilder/.gitignore create mode 100644 scripts/guibuilder/STATUS.md create mode 100644 scripts/guibuilder/common/codenameone_settings.properties create mode 100644 scripts/guibuilder/common/pom.xml create mode 100644 scripts/guibuilder/common/src/main/css/theme.css create mode 100644 scripts/guibuilder/common/src/main/java/com/codename1/guibuilder/CodenameOneGUIBuilder.java create mode 100644 scripts/guibuilder/common/src/main/java/com/codename1/guibuilder/GuiBuilderMcpController.java create mode 100644 scripts/guibuilder/common/src/main/java/com/codename1/guibuilder/model/GuiDocument.java create mode 100644 scripts/guibuilder/common/src/main/java/com/codename1/guibuilder/project/ProjectBinding.java create mode 100644 scripts/guibuilder/common/src/main/java/com/codename1/guibuilder/project/ProjectIO.java create mode 100644 scripts/guibuilder/common/src/main/java/com/codename1/guibuilder/ui/ComponentPreviewFactory.java create mode 100644 scripts/guibuilder/common/src/main/java/com/codename1/guibuilder/ui/DragGuideOverlay.java create mode 100644 scripts/guibuilder/common/src/main/java/com/codename1/guibuilder/ui/GuidedLayoutSupport.java create mode 100644 scripts/guibuilder/common/src/test/java/com/codename1/guibuilder/model/GuiDocumentTest.java create mode 100644 scripts/guibuilder/common/src/test/java/com/codename1/guibuilder/project/ProjectBindingTest.java create mode 100644 scripts/guibuilder/demo-project/src/main/css/theme.css create mode 100644 scripts/guibuilder/demo-project/src/main/guibuilder/com/example/BorderDropForm.gui create mode 100644 scripts/guibuilder/demo-project/src/main/guibuilder/com/example/BoxXLayoutForm.gui create mode 100644 scripts/guibuilder/demo-project/src/main/guibuilder/com/example/GridLayoutForm.gui create mode 100644 scripts/guibuilder/demo-project/src/main/guibuilder/com/example/GuidedLayoutForm.gui create mode 100644 scripts/guibuilder/demo-project/src/main/guibuilder/com/example/LoginForm.gui create mode 100644 scripts/guibuilder/demo-project/src/main/guibuilder/com/example/NestedLayoutsForm.gui create mode 100644 scripts/guibuilder/demo-project/src/main/guibuilder/com/example/TableLayoutForm.gui create mode 100644 scripts/guibuilder/javase/pom.xml create mode 100644 scripts/guibuilder/javase/src/desktop/java/com/codename1/guibuilder/CodenameOneGUIBuilderLauncher.java create mode 100644 scripts/guibuilder/javase/src/desktop/java/com/codename1/guibuilder/CodenameOneGUIBuilderStub.java create mode 100644 scripts/guibuilder/javase/src/test/java/com/codename1/guibuilder/CodeEditorInteractionTest.java create mode 100644 scripts/guibuilder/javase/src/test/java/com/codename1/guibuilder/DesignerInteractionTest.java create mode 100644 scripts/guibuilder/javase/src/test/java/com/codename1/guibuilder/GeneratedSourceTest.java create mode 100644 scripts/guibuilder/pom.xml create mode 100644 scripts/guibuilder/tools/guibuilder-mcp-client.mjs diff --git a/.github/workflows/guibuilder.yml b/.github/workflows/guibuilder.yml new file mode 100644 index 00000000000..1fd9a6855c3 --- /dev/null +++ b/.github/workflows/guibuilder.yml @@ -0,0 +1,104 @@ +name: GUI Builder + +# The standalone GUI Builder lives outside the Maven reactor (scripts/guibuilder) +# and PR CI ignores scripts/**, so nothing else compiles it. It depends on core +# APIs (CodeEditor protected regions, LayeredLayout baseline units) that can be +# changed in the same PR, which is exactly how the editor silently stops +# compiling against a freshly built core. + +on: + workflow_dispatch: + pull_request: + branches: + - master + paths: + - '.github/workflows/guibuilder.yml' + - 'scripts/guibuilder/**' + - '!scripts/guibuilder/**/*.md' + - 'CodenameOne/src/com/codename1/ui/CodeEditor.java' + - 'CodenameOne/src/com/codename1/ui/editor/**' + - 'CodenameOne/src/com/codename1/ui/layouts/LayeredLayout.java' + - 'maven/codenameone-maven-plugin/src/main/java/com/codename1/maven/OpenGuiBuilderMojo.java' + push: + branches: + - master + paths: + - '.github/workflows/guibuilder.yml' + - 'scripts/guibuilder/**' + - '!scripts/guibuilder/**/*.md' + - 'CodenameOne/src/com/codename1/ui/CodeEditor.java' + - 'CodenameOne/src/com/codename1/ui/editor/**' + - 'CodenameOne/src/com/codename1/ui/layouts/LayeredLayout.java' + - 'maven/codenameone-maven-plugin/src/main/java/com/codename1/maven/OpenGuiBuilderMojo.java' + +concurrency: + group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} + cancel-in-progress: true + +jobs: + guibuilder: + runs-on: ubuntu-latest + timeout-minutes: 60 + container: ghcr.io/codenameone/codenameone/pr-ci-container:latest + defaults: + run: + shell: bash + + steps: + - uses: actions/checkout@v6 + + - name: Cache Maven dependencies + uses: actions/cache@v5 + with: + path: ~/.m2 + key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }} + restore-keys: | + ${{ runner.os }}-m2 + + - name: Prepare Codename One binaries + # The JavaSE port needs jfxrt.jar from cn1-binaries; without it the + # build fails with a large, misleading set of missing javafx.* errors. + run: | + set -euo pipefail + rm -rf maven/target/cn1-binaries + mkdir -p maven/target + cp -r /opt/cn1-binaries maven/target/cn1-binaries + + - name: Install Codename One artifacts (JDK 8) + # JAVA_HOME_8 / JAVA_HOME_17 are container image environment variables, + # so they are expanded by the shell rather than the env context. + run: | + set -euo pipefail + export JAVA_HOME="${JAVA_HOME_8}" + export PATH="${JAVA_HOME}/bin:${PATH}" + cd maven + mvn -B install -Plocal-dev-javase -DskipTests \ + -Darchetype.test.skip=true -Dmaven.javadoc.skip=true \ + -Dcn1.binaries="${GITHUB_WORKSPACE}/maven/target/cn1-binaries" + + - name: Run standalone GUI Builder tests (JDK 17) + run: | + set -euo pipefail + export JAVA_HOME="${JAVA_HOME_17}" + export PATH="${JAVA_HOME}/bin:${PATH}" + cd scripts/guibuilder + xvfb-run -a mvn -B -pl javase -am clean test -Dcodename1.platform=javase + + - name: Package the executable GUI Builder (JDK 17) + run: | + set -euo pipefail + export JAVA_HOME="${JAVA_HOME_17}" + export PATH="${JAVA_HOME}/bin:${PATH}" + cd scripts/guibuilder + xvfb-run -a mvn -B -pl javase -am -Pexecutable-jar package \ + -Dcodename1.platform=javase -Dmaven.test.skip=true + ls javase/target/codenameone-guibuilder-*.jar + + - name: Upload GUI Builder test reports + if: always() + uses: actions/upload-artifact@v7 + with: + name: guibuilder-test-reports + path: scripts/guibuilder/javase/target/surefire-reports + if-no-files-found: warn + retention-days: 14 diff --git a/.github/workflows/release-on-maven-central.yml b/.github/workflows/release-on-maven-central.yml index 5d23a530e06..aaa3360b592 100644 --- a/.github/workflows/release-on-maven-central.yml +++ b/.github/workflows/release-on-maven-central.yml @@ -15,9 +15,9 @@ concurrency: jobs: build: runs-on: ubuntu-latest - # The four Central confirmation polls each cover the documented 30-minute + # The five Central confirmation polls each cover the documented 30-minute # propagation window, and each fires only when its own deploy reported failure, - # so the worst case is ~2h of polling on top of the build. Bounded explicitly + # so the worst case is ~2.5h of polling on top of the build. Bounded explicitly # rather than inheriting the 6h default, so a wedged release fails in a shift # rather than overnight. timeout-minutes: 240 @@ -62,7 +62,7 @@ jobs: export GPG_TTY=$(tty) # Once dual publish is off, R2 is the repository and Central must not receive # new immutable releases. skipPublishing still stages, so the R2 upload that - # follows is unaffected -- same treatment as the three editor deploys. + # follows is unaffected -- same treatment as the four editor deploys. skip_publishing=true if [ "${{ vars.CN1_DUAL_PUBLISH }}" = "true" ]; then skip_publishing=false @@ -246,7 +246,7 @@ jobs: continue-on-error: true # always(): central-publishing stages before it uploads, so the tree exists # even when the Central deploy above failed -- which is precisely the case - # R2 is here to survive. Metadata is regenerated once, after all three. + # R2 is here to survive. Metadata is regenerated once, after all four. if: always() && steps.deploy_gamebuilder.conclusion != 'skipped' run: | # Each editor is its own reactor, so it stages into its own target dir. @@ -335,7 +335,7 @@ jobs: continue-on-error: true # always(): central-publishing stages before it uploads, so the tree exists # even when the Central deploy above failed -- which is precisely the case - # R2 is here to survive. Metadata is regenerated once, after all three. + # R2 is here to survive. Metadata is regenerated once, after all four. if: always() && steps.deploy_certificatewizard.conclusion != 'skipped' run: | # Each editor is its own reactor, so it stages into its own target dir. @@ -413,7 +413,7 @@ jobs: continue-on-error: true # always(): central-publishing stages before it uploads, so the tree exists # even when the Central deploy above failed -- which is precisely the case - # R2 is here to survive. Metadata is regenerated once, after all three. + # R2 is here to survive. Metadata is regenerated once, after all four. if: always() && steps.deploy_settings.conclusion != 'skipped' run: | # Each editor is its own reactor, so it stages into its own target dir. @@ -447,10 +447,88 @@ jobs: echo "codenameone-settings ${GITHUB_REF_NAME} never appeared on Maven Central within 30 minutes." >&2 exit 1 + # --- GUI Builder editor ------------------------------------------------- + # The cn1:guibuilder goal resolves the standalone Java-17 GUI Builder and + # its runtime dependencies from Maven Central, exactly like cn1:settings. + - name: Deploy GUI Builder editor to Maven Central + id: deploy_guibuilder + continue-on-error: true + if: >- + always() && (steps.deploy.outcome == 'success' || steps.confirm.outcome == 'success' || + steps.r2_core.outcome == 'success') + run: | + export GPG_TTY=$(tty) + # Do not publish this editor to Central unless the core release actually + # reached Central. The editor declares core/plugin at this same version, so + # publishing it against a core that is not there leaves an immutable Central + # release whose dependencies cannot resolve. skipPublishing still stages the + # artifacts, so the R2 upload that follows is unaffected. + skip_publishing=true + if [ "${{ vars.CN1_DUAL_PUBLISH }}" = "true" ] \ + && { [ "${{ steps.deploy.outcome }}" = "success" ] \ + || [ "${{ steps.confirm.outcome }}" = "success" ]; }; then + skip_publishing=false + else + echo "Core release did not reach Central (or dual publish is off):" + echo "staging this editor for R2 only." + fi + cd scripts/guibuilder + xvfb-run -a mvn -Pexecutable-jar -Pguibuilder-central deploy \ + -DskipPublishing=$skip_publishing \ + -Dcodename1.platform=javase \ + -Dgpg.passphrase=$MAVEN_GPG_PASSPHRASE \ + -Dcn1.version=$GITHUB_REF_NAME -Dcn1.plugin.version=$GITHUB_REF_NAME \ + -Dmaven.test.skip=true + env: + MAVEN_USERNAME: ${{ secrets.MAVEN_CENTRAL_USERNAME }} + MAVEN_PASSWORD: ${{ secrets.MAVEN_CENTRAL_PASSWORD }} + MAVEN_GPG_PASSPHRASE: ${{ secrets.MAVEN_GPG_PASSPHRASE }} + + - name: Publish GUI Builder editor to R2 + id: r2_guibuilder + # Non-fatal for the same reason as the core upload: one editor's R2 failure + # must not skip the remaining editors' Central deploys. + continue-on-error: true + # always(): central-publishing stages before it uploads, so the tree exists + # even when the Central deploy above failed -- which is precisely the case + # R2 is here to survive. Metadata is regenerated once, after all four. + if: always() && steps.deploy_guibuilder.conclusion != 'skipped' + run: | + # Each editor is its own reactor, so it stages into its own target dir. + bash maven/scripts/r2/publish-staging-to-r2.sh scripts/guibuilder/target/central-staging + url="${R2_BASE_URL}/com/codenameone/codenameone-guibuilder/${GITHUB_REF_NAME}/codenameone-guibuilder-${GITHUB_REF_NAME}.pom?cb=${GITHUB_RUN_ID}-${GITHUB_RUN_ATTEMPT}" + code=$(curl -s -o /dev/null -w "%{http_code}" "$url") + [ "$code" = "200" ] || { echo "MISSING on R2: codenameone-guibuilder (HTTP $code)" >&2; exit 1; } + echo "ok: codenameone-guibuilder ${GITHUB_REF_NAME} on R2" + + - name: Confirm GUI Builder editor on Maven Central + id: confirm_guibuilder + # Runs only to second-guess a failed deploy: central-publishing + # reports "Deployment failed while publishing" for bundles it actually accepted, + # and Central releases are immutable so a rerun cannot repair that. + if: >- + always() && vars.CN1_DUAL_PUBLISH == 'true' && + steps.deploy_guibuilder.outcome == 'failure' + continue-on-error: true + run: | + set +e + url="https://repo1.maven.org/maven2/com/codenameone/codenameone-guibuilder/${GITHUB_REF_NAME}/codenameone-guibuilder-${GITHUB_REF_NAME}.jar" + for i in $(seq 1 90); do + code=$(curl -s -o /dev/null -w "%{http_code}" "$url") + if [ "$code" = "200" ]; then + echo "Confirmed codenameone-guibuilder ${GITHUB_REF_NAME} on Maven Central" + exit 0 + fi + echo "[$i/90] Waiting on Maven Central for codenameone-guibuilder (code=$code)" + sleep 20 + done + echo "codenameone-guibuilder ${GITHUB_REF_NAME} never appeared on Maven Central within 30 minutes." >&2 + exit 1 + - name: Mark the R2 release complete id: r2_mark_complete continue-on-error: true - # Only now, once the core reactor and all three editors are up AND the core + # Only now, once the core reactor and all four editors are up AND the core # artifacts have been observed on R2. This is what makes the tag discoverable: # regen refuses to advertise a version without it, on this run and every future # one. r2_core_confirm is what verifies the expected artifacts are actually @@ -465,7 +543,8 @@ jobs: steps.r2_core_confirm.outcome == 'success' && steps.r2_gamebuilder.outcome == 'success' && steps.r2_certificatewizard.outcome == 'success' && - steps.r2_settings.outcome == 'success' + steps.r2_settings.outcome == 'success' && + steps.r2_guibuilder.outcome == 'success' run: bash maven/scripts/r2/mark-release-complete.sh "${GITHUB_REF_NAME}" - name: Regenerate R2 metadata and archetype catalog @@ -517,6 +596,7 @@ jobs: bad "${{ steps.r2_gamebuilder.outcome }}" && fail "Game Builder -> R2" bad "${{ steps.r2_certificatewizard.outcome }}" && fail "Signing Wizard -> R2" bad "${{ steps.r2_settings.outcome }}" && fail "Settings -> R2" + bad "${{ steps.r2_guibuilder.outcome }}" && fail "GUI Builder -> R2" bad "${{ steps.r2_mark_complete.outcome }}" && fail "marking the R2 release complete" bad "${{ steps.r2_metadata.outcome }}" && fail "R2 metadata regeneration" if [ "${{ vars.CN1_DUAL_PUBLISH }}" = "true" ]; then @@ -538,6 +618,10 @@ jobs: && [ "${{ steps.confirm_settings.outcome }}" != "success" ]; then fail "Settings -> Maven Central" fi + if bad "${{ steps.deploy_guibuilder.outcome }}" \ + && [ "${{ steps.confirm_guibuilder.outcome }}" != "success" ]; then + fail "GUI Builder -> Maven Central" + fi fi [ "$status" = "0" ] && echo " all publications succeeded" exit $status diff --git a/CodenameOne/src/com/codename1/components/SplitPane.java b/CodenameOne/src/com/codename1/components/SplitPane.java index ad78355aa0d..efcfa218c5e 100644 --- a/CodenameOne/src/com/codename1/components/SplitPane.java +++ b/CodenameOne/src/com/codename1/components/SplitPane.java @@ -29,6 +29,7 @@ import com.codename1.ui.Container; import com.codename1.ui.Display; import com.codename1.ui.FontImage; +import com.codename1.ui.Form; import com.codename1.ui.Graphics; import com.codename1.ui.Image; import com.codename1.ui.Label; @@ -1094,7 +1095,8 @@ protected boolean isStickyDrag() { @Override protected void initComponent() { super.initComponent(); - getComponentForm().setEnableCursors(true); + Form form = getComponentForm(); + if (form != null) form.setEnableCursors(true); } diff --git a/CodenameOne/src/com/codename1/ui/CodeEditor.java b/CodenameOne/src/com/codename1/ui/CodeEditor.java index b1b1d9cef8e..92739a5b646 100644 --- a/CodenameOne/src/com/codename1/ui/CodeEditor.java +++ b/CodenameOne/src/com/codename1/ui/CodeEditor.java @@ -219,6 +219,18 @@ public void insertAtCursor(String text) { command("insertText", text); } + /// Protects all text between matching marker lines from editing. This is intended for generated + /// source previews that contain explicit user-editable regions. Passing null clears protection. + public void setProtectedRegionMarkers(String startMarker, String endMarker) { + command("setProtectedMarkers", startMarker == null || endMarker == null + ? "" : startMarker + "\n" + endMarker); + } + + /// Moves the caret to a character offset, clamped by the editor backend to the document bounds. + public void setCursorPosition(int offset) { + command("setCursor", String.valueOf(Math.max(0, offset))); + } + /// Retrieves the current caret character offset. The callback is invoked on the EDT. /// /// #### Parameters diff --git a/CodenameOne/src/com/codename1/ui/Tabs.java b/CodenameOne/src/com/codename1/ui/Tabs.java index 21fc3bd9c67..667033dd206 100644 --- a/CodenameOne/src/com/codename1/ui/Tabs.java +++ b/CodenameOne/src/com/codename1/ui/Tabs.java @@ -2469,7 +2469,10 @@ public void actionPerformed(ActionEvent evt) { } } Form parent = getComponentForm(); - parent.clearComponentsAwaitingRelease(); + // A tab can be removed in response to the same pointer gesture + // (e.g. an inspector rebuild). Its global swipe listener may still + // receive the queued drag after deinitialization. + if (parent != null) parent.clearComponentsAwaitingRelease(); } } } diff --git a/CodenameOne/src/com/codename1/ui/editor/CodePureEditor.java b/CodenameOne/src/com/codename1/ui/editor/CodePureEditor.java index b390e95f58d..d0cf776089d 100644 --- a/CodenameOne/src/com/codename1/ui/editor/CodePureEditor.java +++ b/CodenameOne/src/com/codename1/ui/editor/CodePureEditor.java @@ -74,6 +74,13 @@ public void cmd(String name, String arg) { codeView.setDiagnostics(parseDiagnostics(arg)); return; } + if ("setProtectedMarkers".equals(name)) { + int separator = arg == null ? -1 : arg.indexOf('\n'); + codeView.setProtectedRegionMarkers( + separator < 0 ? null : arg.substring(0, separator), + separator < 0 || separator + 1 >= arg.length() ? null : arg.substring(separator + 1)); + return; + } if ("showCompletions".equals(name)) { handleShowCompletions(arg); return; diff --git a/CodenameOne/src/com/codename1/ui/editor/CodeView.java b/CodenameOne/src/com/codename1/ui/editor/CodeView.java index ea2ec0b5723..f842688f0f9 100644 --- a/CodenameOne/src/com/codename1/ui/editor/CodeView.java +++ b/CodenameOne/src/com/codename1/ui/editor/CodeView.java @@ -45,6 +45,8 @@ public class CodeView extends EditorView { private ThemePalette palette = ThemePalette.LIGHT; private boolean showLineNumbers = true; private int tabSize = 4; + private String protectedStartMarker; + private String protectedEndMarker; private int[] endStates; private int validUpTo; @@ -114,6 +116,57 @@ public void setDiagnostics(List diagnostics) { repaint(); } + /// Protects every range delimited by matching marker strings from user edits. Passing null or an + /// empty marker clears the protection. Whole-document replacement remains available so generated + /// source can still be refreshed by the host. + public void setProtectedRegionMarkers(String startMarker, String endMarker) { + protectedStartMarker = emptyToNull(startMarker); + protectedEndMarker = emptyToNull(endMarker); + } + + private static String emptyToNull(String value) { + return value == null || value.length() == 0 ? null : value; + } + + @Override + protected void replaceRange(int start, int end, String text, boolean record) { + int clampedStart = getDocument().clamp(start); + int clampedEnd = getDocument().clamp(end); + if (clampedStart > clampedEnd) { + int swap = clampedStart; + clampedStart = clampedEnd; + clampedEnd = swap; + } + if (isProtectedEdit(clampedStart, clampedEnd)) { + return; + } + super.replaceRange(clampedStart, clampedEnd, text, record); + } + + private boolean isProtectedEdit(int start, int end) { + if (protectedStartMarker == null || protectedEndMarker == null) { + return false; + } + String source = getDocument().getText(); + int searchFrom = 0; + while (searchFrom < source.length()) { + int protectedStart = source.indexOf(protectedStartMarker, searchFrom); + if (protectedStart < 0) { + return false; + } + int endMarker = source.indexOf(protectedEndMarker, + protectedStart + protectedStartMarker.length()); + int protectedEnd = endMarker < 0 + ? source.length() : endMarker + protectedEndMarker.length(); + if ((start == end && start >= protectedStart && start <= protectedEnd) + || (start < protectedEnd && end > protectedStart)) { + return true; + } + searchFrom = protectedEnd; + } + return false; + } + /// Enables or disables the completion popup. public void setCompletionEnabled(boolean enabled) { this.completionEnabled = enabled; diff --git a/CodenameOne/src/com/codename1/ui/editor/PureEditor.java b/CodenameOne/src/com/codename1/ui/editor/PureEditor.java index 2ff4c1d6408..3e006b0ca49 100644 --- a/CodenameOne/src/com/codename1/ui/editor/PureEditor.java +++ b/CodenameOne/src/com/codename1/ui/editor/PureEditor.java @@ -89,6 +89,10 @@ public void cmd(String name, String arg) { view.setEditableState("1".equals(arg)); return; } + if ("setCursor".equals(name)) { + view.moveCaret(parseInt(arg, 0), false); + return; + } if ("focus".equals(name)) { view.requestFocus(); return; @@ -108,6 +112,17 @@ public void cmd(String name, String arg) { // remaining rich / code commands are handled by subclasses; ignore here } + private static int parseInt(String value, int defaultValue) { + if (value == null) { + return defaultValue; + } + try { + return Integer.parseInt(value.trim()); + } catch (NumberFormatException err) { + return defaultValue; + } + } + /// Executes a query returning a string result. Unknown queries return an empty string. /// /// #### Parameters diff --git a/CodenameOne/src/com/codename1/ui/layouts/LayeredLayout.java b/CodenameOne/src/com/codename1/ui/layouts/LayeredLayout.java index b0654c66d5e..d8a962a9e9e 100644 --- a/CodenameOne/src/com/codename1/ui/layouts/LayeredLayout.java +++ b/CodenameOne/src/com/codename1/ui/layouts/LayeredLayout.java @@ -340,6 +340,18 @@ private static int getOuterY(Component cmp) { return cmp.getY() - cmp.getStyle().getMarginTop(); } + /// Returns the text baseline a component reports for the given size, or -1 when the component + /// does not describe one. `Component#getBaseline(int, int)` has a non-abstract default that + /// returns the bottom content edge rather than a text baseline, so only components that also + /// describe their baseline resize behavior take part in `#UNIT_BASELINE` alignment. Everything + /// else keeps the historical font-ascent approximation. + private static int declaredBaseline(Component cmp, int width, int height) { + if (cmp == null || cmp.getBaselineResizeBehavior() == Component.BRB_OTHER) { + return -1; + } + return cmp.getBaseline(width, height); + } + /* @@ -2989,9 +3001,17 @@ public int calcPreferredValue(Container parent, Component cmp) { case UNIT_BASELINE: { Style rs = referenceComponent.getStyle(); Style s = cmp.getStyle(); - preferredValue = baseValue + (referenceComponent.getPreferredH() - cmp.getPreferredH()) / 2 - + (rs.getFont().getAscent() - s.getFont().getAscent()) - + (rs.getPaddingTop() - s.getPaddingTop()); + int referenceBaseline = declaredBaseline(referenceComponent, + referenceComponent.getPreferredW(), referenceComponent.getPreferredH()); + int componentBaseline = declaredBaseline(cmp, cmp.getPreferredW(), cmp.getPreferredH()); + if (referenceBaseline >= 0 && componentBaseline >= 0) { + preferredValue = baseValue + rs.getMarginTop() + referenceBaseline + - s.getMarginTop() - componentBaseline; + } else { + preferredValue = baseValue + (referenceComponent.getPreferredH() - cmp.getPreferredH()) / 2 + + (rs.getFont().getAscent() - s.getFont().getAscent()) + + (rs.getPaddingTop() - s.getPaddingTop()); + } break; } default: @@ -3118,14 +3138,21 @@ private int calculate(Component cmp, int top, int left, int bottom, int right) { Component ref = getReferenceComponent(); Style rs = ref.getStyle(); Style s = cmp.getStyle(); - Font rf = rs.getFont(); - Font sf = s.getFont(); - int ra = rf == null || sf == null ? 0 : rf.getAscent(); - int sa = rf == null || sf == null ? 0 : sf.getAscent(); - calculatedValue = baseValue + (ref.getHeight() - cmp.getPreferredH()) / 2 - + (rs.getPaddingTop() - s.getPaddingTop()) - + (rs.getMarginTop() - s.getMarginTop()) - + (ra - sa); + int referenceBaseline = declaredBaseline(ref, ref.getWidth(), ref.getHeight()); + int componentBaseline = declaredBaseline(cmp, cmp.getPreferredW(), cmp.getPreferredH()); + if (referenceBaseline >= 0 && componentBaseline >= 0) { + calculatedValue = baseValue + rs.getMarginTop() + referenceBaseline + - s.getMarginTop() - componentBaseline; + } else { + Font rf = rs.getFont(); + Font sf = s.getFont(); + int ra = rf == null || sf == null ? 0 : rf.getAscent(); + int sa = rf == null || sf == null ? 0 : sf.getAscent(); + calculatedValue = baseValue + (ref.getHeight() - cmp.getPreferredH()) / 2 + + (rs.getPaddingTop() - s.getPaddingTop()) + + (rs.getMarginTop() - s.getMarginTop()) + + (ra - sa); + } } break; @@ -3657,6 +3684,12 @@ public int getAbsolutePixels(Component cmp) { } else { Style rs = ref.getStyle(); Style cs = cmp.getStyle(); + int referenceBaseline = declaredBaseline(ref, ref.getWidth(), ref.getHeight()); + int componentBaseline = declaredBaseline(cmp, cmp.getPreferredW(), cmp.getPreferredH()); + if (referenceBaseline >= 0 && componentBaseline >= 0) { + return baseValue + rs.getMarginTop() + referenceBaseline + - cs.getMarginTop() - componentBaseline; + } Font rf = rs.getFont(); Font cf = cs.getFont(); int ra = rf == null || cf == null ? 0 : rf.getAscent(); diff --git a/maven/codenameone-maven-plugin/src/main/java/com/codename1/maven/OpenGuiBuilderMojo.java b/maven/codenameone-maven-plugin/src/main/java/com/codename1/maven/OpenGuiBuilderMojo.java index 5694b242fd4..f4b25a61c1d 100644 --- a/maven/codenameone-maven-plugin/src/main/java/com/codename1/maven/OpenGuiBuilderMojo.java +++ b/maven/codenameone-maven-plugin/src/main/java/com/codename1/maven/OpenGuiBuilderMojo.java @@ -1,268 +1,283 @@ /* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. + * Copyright (c) 2012, Codename One and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. */ package com.codename1.maven; -import java.io.DataInputStream; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.FileOutputStream; -import java.io.IOException; -import java.nio.charset.StandardCharsets; -import java.util.Collections; -import java.util.UUID; +import org.apache.commons.io.FileUtils; +import org.apache.maven.artifact.Artifact; +import org.apache.maven.artifact.repository.ArtifactRepository; +import org.apache.maven.artifact.resolver.ArtifactResolutionRequest; +import org.apache.maven.artifact.resolver.ArtifactResolutionResult; import org.apache.maven.plugin.MojoExecutionException; import org.apache.maven.plugin.MojoFailureException; import org.apache.maven.plugins.annotations.Mojo; import org.apache.maven.plugins.annotations.Parameter; -import org.apache.maven.shared.invoker.DefaultInvocationRequest; -import org.apache.maven.shared.invoker.DefaultInvoker; -import org.apache.maven.shared.invoker.InvocationRequest; -import org.apache.maven.shared.invoker.Invoker; -import org.apache.maven.shared.invoker.MavenInvocationException; import org.apache.tools.ant.taskdefs.Java; +import java.io.File; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; + /** - * Goal to open the gui builder. - * @author shannah + * Opens the modern standalone Codename One GUI Builder for every GUI form in the project. + * The editor is resolved from Maven using the same distribution model as Codename One + * Settings; no downloaded {@code ~/.codenameone/guibuilder.jar} is used. + * + *
mvn cn1:guibuilder [-DclassName=com.example.MyForm]
*/ @Mojo(name = "guibuilder") public class OpenGuiBuilderMojo extends AbstractCN1Mojo { - private File guibuilderInput = new File(System.getProperty("user.home") + File.separator + ".guiBuilder" + File.separator + "guibuilder.input"); + private static final String LAUNCHED_PROPERTY = "com.codename1.maven.OpenGuiBuilderMojo.launched"; + + /** The editor is compiled for this Java release, so an older forked JVM cannot load it. */ + private static final int REQUIRED_JAVA_VERSION = 17; - @Parameter(property="className", required=true) + /** Optional fully-qualified form to select initially. */ + @Parameter(property = "className", required = false) private String className; + + @Parameter(property = "guibuilder.spawn", required = false, defaultValue = "true") + private boolean spawn; + @Override protected void executeImpl() throws MojoExecutionException, MojoFailureException { + if (Boolean.getBoolean(LAUNCHED_PROPERTY)) { + getLog().debug("Skipping guibuilder: already launched in this Maven invocation"); + return; + } if (!isCN1ProjectDir()) { + getLog().debug("Skipping guibuilder: not a Codename One project directory"); return; } - try { - + requireModernJdk(); + System.setProperty(LAUNCHED_PROPERTY, "true"); - File sourceFile = findSourceFile(className); - if (!sourceFile.exists()) { - throw new MojoExecutionException("Cannot find source file "+sourceFile); - } - File guiFile = findGuiFile(className); - if (!guiFile.exists()) { - throw new MojoExecutionException("Cannot find gui fuild "+guiFile); - } - - File resourceFile = getProjectResourceFile(); - if (!resourceFile.exists()) { - if (isCSSProject()) { - // If it is a CSS project, we may simply need to compile the CSS since the theme.res - // will be located in the compiled classes directory - InvocationRequest request = new DefaultInvocationRequest(); - //request.setPomFile( new File( "/path/to/pom.xml" ) ); - request.setGoals( Collections.singletonList( "cn1:css" ) ); - + File projectDir = getCN1ProjectDir(); + File guiDir = new File(projectDir, "src" + File.separator + "main" + File.separator + "guibuilder"); + File sourceDir = new File(projectDir, "src" + File.separator + "main" + File.separator + "java"); + File cssFile = new File(projectDir, "src" + File.separator + "main" + File.separator + "css" + File.separator + "theme.css"); + guiDir.mkdirs(); - Invoker invoker = new DefaultInvoker(); - try { - getLog().info("theme.res file not found. Trying to compile CSS to generate it now"); - invoker.execute( request ); - } catch (MavenInvocationException ex) { - getLog().error("Failed to compile CSS"); - throw new MojoExecutionException(ex.getMessage(), ex); + 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); - } - if (!resourceFile.exists()) { - throw new MojoExecutionException("Still cannot find resource file at "+resourceFile+" even after compiling project CSS"); - - } - } else { - throw new MojoExecutionException("Cannot find project resource file "+resourceFile); - } - } - openInGuiBuilder(project.getName(), sourceFile, resourceFile, guiFile); - } catch (IOException ex) { - getLog().error("Failed to open form"); - throw new MojoExecutionException("Failed to open form", ex); + ToolClasspath classpath = getGuiBuilderClasspath(); + getLog().info("Launching Codename One GUI Builder bound to " + projectDir); + if (shouldSpawn()) { + launchDetached(classpath, runtimeDir, input, projectDir); + return; } + Java java = createJava(); + java.setFork(true); + java.setClassname("com.codename1.guibuilder.CodenameOneGUIBuilderLauncher"); + java.createClasspath().setPath(joinClasspath(classpath.files)); + for (String arg : desktopIdentityArgs()) { + java.createJvmarg().setValue(arg); + } + java.createJvmarg().setValue("-Dguibuilder.input=" + input.getAbsolutePath()); + for (String arg : forwardedGuiBuilderProperties()) { + java.createJvmarg().setValue(arg); + } + java.executeJava(); } - - private boolean isCSSProject() { - return ("true".equals(properties.getProperty("codename1.cssTheme", null))); - } - - private File getProjectResourceFile() { - if (isCSSProject()) { - return new File(project.getBuild().getOutputDirectory() + File.separator + "theme.res"); + + /** + * The GUI Builder is a Java 17 artifact. The spawned process writes only to its log file, so + * without this check an older Maven JVM fails with an UnsupportedClassVersionError that never + * reaches the console. + */ + private void requireModernJdk() throws MojoFailureException { + int version = javaFeatureVersion(); + if (version >= REQUIRED_JAVA_VERSION) { + return; } - return new File(project.getBasedir() + File.separator + "src" + File.separator + "main" + File.separator + "resources" + File.separator + "theme.res"); + throw new MojoFailureException("The Codename One GUI Builder needs JDK " + + REQUIRED_JAVA_VERSION + " or newer, but Maven is running on " + + System.getProperty("java.version", String.valueOf(version)) + " (" + + System.getProperty("java.home") + ").\n" + + "Point JAVA_HOME at a JDK " + REQUIRED_JAVA_VERSION + + "+ installation (for example Eclipse Temurin from https://adoptium.net) and run " + + "mvn cn1:guibuilder again."); } - - private File getGuiBuilderSourcesDir() { - return new File(project.getBasedir() + File.separator + "src" + File.separator + "main" + File.separator + "guibuilder"); + + static int javaFeatureVersion() { + String version = System.getProperty("java.specification.version", ""); + if (version.startsWith("1.")) { + version = version.substring(2); + } + int dot = version.indexOf('.'); + if (dot > 0) { + version = version.substring(0, dot); + } + try { + return Integer.parseInt(version.trim()); + } catch (NumberFormatException ex) { + return 0; + } } - - private File findGuiFile(String fullyQualifiedClassName) { - return new File(getGuiBuilderSourcesDir(), fullyQualifiedClassName.replace(".", File.separator) + ".gui"); + + /** + * Forwards {@code guibuilder.*} system properties (MCP port, canvas mode, dark mode, initial + * selection, editor to open) from the Maven invocation to the editor JVM, so + * {@code mvn cn1:guibuilder -Dguibuilder.mcp.port=18349} works. The {@code guibuilder.input} + * binding and the {@code guibuilder.spawn} launch flag are owned by this mojo and never + * forwarded. + */ + List forwardedGuiBuilderProperties() { + List args = new ArrayList(); + for (String key : System.getProperties().stringPropertyNames()) { + if (key.startsWith("guibuilder.") + && !key.equals("guibuilder.input") + && !key.equals("guibuilder.spawn")) { + args.add("-D" + key + "=" + System.getProperty(key)); + } + } + return args; } - - private File findSourceFile(String fullyQualifiedClassName) { - return new File( - project.getBasedir() + File.separator + "src" + File.separator + - "main" + File.separator + "java" + File.separator + - fullyQualifiedClassName.replace(".", File.separator) + ".java" - ); + + /** + * Names the process for the dock, taskbar and window manager, and opens the JDK packages the + * JavaSE port needs on Java 9 and newer, matching cn1:settings and cn1:certificate-wizard. + */ + List desktopIdentityArgs() { + List args = new ArrayList(); + args.add("-Dapple.awt.application.name=Codename One GUI Builder"); + args.add("-Dcom.apple.mrj.application.apple.menu.about.name=Codename One GUI Builder"); + args.add("-Dsun.awt.application.name=Codename One GUI Builder"); + args.add("-Dsun.awt.X11.XWMClass=CodenameOneGUIBuilder"); + args.add("--add-exports=java.desktop/com.apple.eawt.event=ALL-UNNAMED"); + args.add("--add-exports=java.desktop/com.apple.eawt=ALL-UNNAMED"); + if (isMacOs()) { + args.add("-Xdock:name=Codename One GUI Builder"); + } + return args; } - - private File getGuiBuilderJar() { - File home = new File(System.getProperty("user.home")); - File codenameone = new File(home, ".codenameone"); - File settingsJar = new File(codenameone, "guibuilder.jar"); - - return settingsJar; + + private static boolean isMacOs() { + return System.getProperty("os.name", "").toLowerCase().contains("mac"); } - - - - static String xmlize(String s) { - s = s.replace("&", "&"); - s = s.replace("<", "<"); - s = s.replace(">", ">"); - s = s.replace("\"", """); - int charCount = s.length(); - for(int iter = 0 ; iter < charCount ; iter++) { - char c = s.charAt(iter); - if(c > 127) { - // we need to localize the string... - StringBuilder b = new StringBuilder(); - for(int counter = 0 ; counter < charCount ; counter++) { - c = s.charAt(counter); - if(c > 127) { - b.append("&#x"); - b.append(Integer.toHexString(c)); - b.append(";"); - } else { - b.append(c); - } - } - return b.toString(); - } + + @Override + protected boolean isCN1ProjectDir() { + File cn1 = getCN1ProjectDir(); + if (cn1 == null || project == null || project.getBasedir() == null) return false; + try { + File current = project.getBasedir().getCanonicalFile(); + File projectDir = cn1.getCanonicalFile(); + return projectDir.equals(current) || projectDir.equals(new File(current, "common").getCanonicalFile()); + } catch (IOException ex) { + return false; } - return s; } - - public void openInGuiBuilder(String projectName, final File javaSourceFile, File projectResourceFile, File guiFile) throws IOException, MojoExecutionException { + + private boolean shouldSpawn() { + String legacy = System.getProperty("spawn"); + return legacy == null ? spawn : Boolean.parseBoolean(legacy); + } + + void writeBinding(File input, File projectDir, File guiDir, File sourceDir, File cssFile) + throws MojoExecutionException { + StringBuilder content = new StringBuilder(); + content.append("# Codename One GUI Builder project binding\n"); + content.append("projectDir=").append(projectDir.getAbsolutePath()).append('\n'); + content.append("guiDir=").append(guiDir.getAbsolutePath()).append('\n'); + content.append("sourceDir=").append(sourceDir.getAbsolutePath()).append('\n'); + content.append("cssFile=").append(cssFile.getAbsolutePath()).append('\n'); + if (className != null && className.trim().length() > 0) { + content.append("initialForm=").append(className.trim()).append('\n'); + } try { - File guiBuilderDirectory = new File(System.getProperty("user.home") + File.separator + ".guiBuilder"); - File cn1Directory = new File(System.getProperty("user.home") + File.separator + ".cn1"); - File codenameOneDirectory = new File(System.getProperty("user.home") + File.separator + ".codenameone"); - guiBuilderDirectory.mkdirs(); - cn1Directory.mkdirs(); - codenameOneDirectory.mkdirs(); - if(!guiBuilderDirectory.exists()) { - throw new FileNotFoundException("Couldn't find or create the GUI builder directory within your home directory specifically: " + guiBuilderDirectory.getAbsolutePath()); - - } - if(!cn1Directory.exists()) { - throw new FileNotFoundException("Couldn't find or create the cn1 directory within your home directory specifically: " + cn1Directory.getAbsolutePath()); - - } - if(!codenameOneDirectory.exists()) { - throw new FileNotFoundException("Couldn't find or create the codename1 directory within your home directory specifically: " + codenameOneDirectory.getAbsolutePath()); - } - String connectionId = UUID.randomUUID().toString(); - final File runningFile = new File(System.getProperty("user.home") + File.separator + ".guiBuilder" + File.separator + connectionId); - final File outputFile = new File(System.getProperty("user.home") + File.separator + ".guiBuilder" + File.separator + connectionId + ".ouput"); - runningFile.getParentFile().mkdirs(); - FileOutputStream r = new FileOutputStream(runningFile); - r.write(0); - r.close(); - FileOutputStream fos = new FileOutputStream(guibuilderInput); - String formName = javaSourceFile.getName(); - formName = formName.substring(0, formName.length() - 5); - fos.write(("\n" + - "\n").getBytes(StandardCharsets.UTF_8)); - fos.close(); - launchGuiBuilderApp(); + FileUtils.write(input, content.toString(), StandardCharsets.UTF_8); + } catch (IOException ex) { + throw new MojoExecutionException("Failed to write GUI Builder project binding", ex); + } + } - new Thread() { - private long lastModified = 0; - public void run() { - while(runningFile.exists()) { - if(outputFile.exists() && lastModified != outputFile.lastModified()) { - try { - Thread.sleep(100); - } catch(InterruptedException e) {} - try { - FileInputStream fis = new FileInputStream(outputFile); - byte[] data = new byte[(int)outputFile.length()]; - new DataInputStream(fis).readFully(data); - fis.close(); - lastModified = outputFile.lastModified(); - String d = new String(data, StandardCharsets.UTF_8); - if(d.endsWith("DataChangeEvent")) { - gotoSourceFileLine(javaSourceFile, "void " + d, "\n public void " + d + "(com.codename1.ui.Component cmp, int type, int index) {\n }\n"); - } else { - if(d.endsWith("Command")) { - gotoSourceFileLine(javaSourceFile, "void " + d, "\n public void " + d + "(com.codename1.ui.events.ActionEvent ev, Command cmd) {\n }\n"); - } else { - if(d.endsWith("ListModel")) { - gotoSourceFileLine(javaSourceFile, "ListModel " + d, "\n public com.codename1.ui.list.ListModel " + d + "() {\n }\n"); - } else { - gotoSourceFileLine(javaSourceFile, "void " + d, "\n public void " + d + "(com.codename1.ui.events.ActionEvent ev) {\n }\n"); - } - } - } - outputFile.delete(); - } catch(IOException err) { - err.printStackTrace(); - } - } - try { - Thread.sleep(1000); - } catch(InterruptedException e) {} - } - } - }.start(); + private void launchDetached(ToolClasspath classpath, File runtimeDir, File input, File projectDir) + throws MojoExecutionException { + List command = new ArrayList(); + command.add(javaExecutable()); + command.addAll(desktopIdentityArgs()); + command.add("-Dguibuilder.input=" + input.getAbsolutePath()); + command.addAll(forwardedGuiBuilderProperties()); + command.add("-cp"); + command.add(joinClasspath(classpath.files)); + command.add("com.codename1.guibuilder.CodenameOneGUIBuilderLauncher"); + ProcessBuilder builder = new ProcessBuilder(command); + builder.directory(projectDir); + builder.redirectErrorStream(true); + builder.redirectOutput(ProcessBuilder.Redirect.appendTo(new File(runtimeDir, "guibuilder.log"))); + try { + builder.start(); + getLog().info("GUI Builder launched in the background. Log: " + new File(runtimeDir, "guibuilder.log")); + } catch (IOException ex) { + throw new MojoExecutionException("Failed to launch Codename One GUI Builder", ex); + } + } - } catch(IOException err) { - handleGUIBuilderError(err, "Error launching GUI builder: " + err); + private ToolClasspath getGuiBuilderClasspath() throws MojoExecutionException, MojoFailureException { + Artifact artifact = getArtifact("com.codenameone", "codenameone-guibuilder"); + if (artifact == null) { + artifact = repositorySystem.createArtifact("com.codenameone", "codenameone-guibuilder", pluginVersion(), "jar"); + } + List files = new ArrayList(); + ArtifactResolutionResult result = repositorySystem.resolve(new ArtifactResolutionRequest() + .setLocalRepository(localRepository) + .setRemoteRepositories(new ArrayList(remoteRepositories)) + .setResolveTransitively(true) + .setArtifact(artifact)); + addArtifact(files, artifact); + if (result != null && result.getArtifacts() != null) { + for (Artifact resolved : result.getArtifacts()) addArtifact(files, resolved); } - + if (files.isEmpty()) { + throw new MojoFailureException("Could not resolve the GUI Builder (com.codenameone:codenameone-guibuilder:" + + pluginVersion() + "). It is distributed through Maven Central alongside the Codename One plugin.\n" + + "To work on the editor, run:\n" + + " cd scripts/guibuilder && mvn -Pexecutable-jar -pl javase -am package -Dcodename1.platform=javase"); + } + return new ToolClasspath(files); } - - /** - * Opens the source file, brings the IDE to the foreground. If methodSig doesn't exist it inserts the method - * prototype at the end of the source file before the last curly bracket - */ - public void gotoSourceFileLine(File javaSource, String methodSig, String methodPrototype) { - + + private static void addArtifact(List files, Artifact artifact) { + if (artifact == null || artifact.getFile() == null || !"jar".equals(artifact.getType())) return; + File file = artifact.getFile().getAbsoluteFile(); + if (file.exists() && !files.contains(file)) files.add(file); } - public void handleGUIBuilderError(Exception err, String message) { - + private String pluginVersion() { + if (pluginArtifacts != null) { + for (Artifact artifact : pluginArtifacts) { + if ("com.codenameone".equals(artifact.getGroupId()) + && "codenameone-maven-plugin".equals(artifact.getArtifactId())) return artifact.getVersion(); + } + } + return project.getProperties().getProperty("cn1.plugin.version", + project.getProperties().getProperty("cn1.version", "8.0-SNAPSHOT")); } - /** - * Launches the actual GUI builder jar executable - * @throws org.apache.maven.plugin.MojoExecutionException - */ - public void launchGuiBuilderApp() throws MojoExecutionException{ - - updateCodenameOne(false, getGuiBuilderJar()); - Java java = createJava(); - java.setFork(true); - if ("true".equals(System.getProperty("spawn", "true"))) { - java.setSpawn(true); + private String javaExecutable() { + boolean windows = System.getProperty("os.name", "").toLowerCase().contains("win"); + return new File(new File(System.getProperty("java.home"), "bin"), windows ? "javaw.exe" : "java").getAbsolutePath(); + } + + private static String joinClasspath(List files) { + StringBuilder value = new StringBuilder(); + for (File file : files) { + if (value.length() > 0) value.append(File.pathSeparator); + value.append(file.getAbsolutePath()); } - java.setJar(getGuiBuilderJar()); - java.executeJava(); + return value.toString(); + } + + private static final class ToolClasspath { + final List files; + ToolClasspath(List files) { this.files = files; } } } diff --git a/maven/codenameone-maven-plugin/src/test/java/com/codename1/maven/OpenGuiBuilderMojoTest.java b/maven/codenameone-maven-plugin/src/test/java/com/codename1/maven/OpenGuiBuilderMojoTest.java new file mode 100644 index 00000000000..5446600025b --- /dev/null +++ b/maven/codenameone-maven-plugin/src/test/java/com/codename1/maven/OpenGuiBuilderMojoTest.java @@ -0,0 +1,80 @@ +package com.codename1.maven; + +import org.apache.maven.project.MavenProject; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; +import java.io.File; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.util.List; +import static org.junit.Assert.*; + +public class OpenGuiBuilderMojoTest { + @Rule public TemporaryFolder tmp = new TemporaryFolder(); + + @Test + public void bindingIncludesProjectWideGuiAndCssLocations() throws Exception { + File common = tmp.newFolder("common"); + File input = tmp.newFile("guibuilder.input"); + File gui = new File(common, "src/main/guibuilder"); + File source = new File(common, "src/main/java"); + File css = new File(common, "src/main/css/theme.css"); + new OpenGuiBuilderMojo().writeBinding(input, common, gui, source, css); + String binding = new String(Files.readAllBytes(input.toPath()), StandardCharsets.UTF_8); + assertTrue(binding.contains("projectDir=" + common.getAbsolutePath())); + assertTrue(binding.contains("guiDir=" + gui.getAbsolutePath())); + assertTrue(binding.contains("cssFile=" + css.getAbsolutePath())); + } + + @Test + public void forwardsGuiBuilderPropertiesButNotTheBindingOrLaunchFlag() { + System.setProperty("guibuilder.mcp.port", "18349"); + System.setProperty("guibuilder.canvasMode", "desktop"); + System.setProperty("guibuilder.input", "/tmp/should-not-be-forwarded.input"); + System.setProperty("guibuilder.spawn", "false"); + try { + List args = new OpenGuiBuilderMojo().forwardedGuiBuilderProperties(); + assertTrue(args.contains("-Dguibuilder.mcp.port=18349")); + assertTrue(args.contains("-Dguibuilder.canvasMode=desktop")); + for (String arg : args) { + assertFalse(arg.startsWith("-Dguibuilder.input=")); + assertFalse(arg.startsWith("-Dguibuilder.spawn=")); + } + } finally { + System.clearProperty("guibuilder.mcp.port"); + System.clearProperty("guibuilder.canvasMode"); + System.clearProperty("guibuilder.input"); + System.clearProperty("guibuilder.spawn"); + } + } + + @Test + public void desktopIdentityOpensThePackagesTheJavaseRuntimeNeeds() { + List args = new OpenGuiBuilderMojo().desktopIdentityArgs(); + assertTrue(args.contains("--add-exports=java.desktop/com.apple.eawt=ALL-UNNAMED")); + assertTrue(args.contains("-Dsun.awt.application.name=Codename One GUI Builder")); + } + + @Test + public void detectsTheRunningJavaFeatureVersion() { + assertTrue("the plugin itself runs on JDK 8 or newer", + OpenGuiBuilderMojo.javaFeatureVersion() >= 8); + } + + @Test + public void launchesFromAggregatorOrCommonModule() throws Exception { + File root = tmp.newFolder("app"); + File common = new File(root, "common"); + assertTrue(common.mkdirs()); + Files.write(new File(root, "pom.xml").toPath(), "".getBytes(StandardCharsets.UTF_8)); + Files.write(new File(common, "pom.xml").toPath(), "".getBytes(StandardCharsets.UTF_8)); + Files.write(new File(common, "codenameone_settings.properties").toPath(), "codename1.packageName=com.example\n".getBytes(StandardCharsets.UTF_8)); + OpenGuiBuilderMojo mojo = new OpenGuiBuilderMojo(); + MavenProject project = new MavenProject(); + project.setFile(new File(root, "pom.xml")); + project.addCompileSourceRoot(new File(root, "src/main/java").getAbsolutePath()); + mojo.project = project; + assertTrue(mojo.isCN1ProjectDir()); + } +} diff --git a/maven/core-unittests/src/test/java/com/codename1/ui/CodeEditorTest.java b/maven/core-unittests/src/test/java/com/codename1/ui/CodeEditorTest.java index 95c24afbabf..6b01910646d 100644 --- a/maven/core-unittests/src/test/java/com/codename1/ui/CodeEditorTest.java +++ b/maven/core-unittests/src/test/java/com/codename1/ui/CodeEditorTest.java @@ -302,4 +302,50 @@ void testPureEditorUsedWithoutLowLevelTextInput() { assertTrue(editor.isEditorReady()); assertTrue(editor.getComponentAt(0) instanceof com.codename1.ui.editor.EditorView); } + + @FormTest + void testPureEditorProtectsGeneratedRegionsButAllowsUserRegions() { + implementation.setEditorNativePeerSupported(false); + String source = "class Form {\n" + + "// \n" + + "void generated() {}\n" + + "// \n" + + "void userCode() {}\n" + + "}"; + CodeEditor editor = new CodeEditor("java", source); + Form form = new Form("code", new BorderLayout()); + form.add(BorderLayout.CENTER, editor); + form.show(); + pump(); + + editor.setProtectedRegionMarkers( + "// ", "// "); + com.codename1.ui.editor.EditorView view = + (com.codename1.ui.editor.EditorView) editor.getComponentAt(0); + int generatedOffset = source.indexOf("generated()"); + view.moveCaret(generatedOffset, false); + view.insertText("blocked"); + assertEquals(source, view.getText(), "generated source must remain locked"); + + int userOffset = source.indexOf("userCode"); + view.moveCaret(userOffset, false); + view.insertText("custom"); + assertTrue(view.getText().contains("void customuserCode() {}"), + "the companion user-code region must remain editable"); + } + + @FormTest + void testSetCursorPositionMovesPureEditorCaret() { + implementation.setEditorNativePeerSupported(false); + CodeEditor editor = new CodeEditor("java", "0123456789"); + Form form = new Form("code", new BorderLayout()); + form.add(BorderLayout.CENTER, editor); + form.show(); + pump(); + + editor.setCursorPosition(7); + AtomicReference cursor = new AtomicReference(); + editor.getCursorPosition(cursor::set); + assertEquals(Integer.valueOf(7), cursor.get()); + } } diff --git a/maven/core-unittests/src/test/java/com/codename1/ui/layouts/LayeredLayoutTest.java b/maven/core-unittests/src/test/java/com/codename1/ui/layouts/LayeredLayoutTest.java index d4a01890af5..64b6b422768 100644 --- a/maven/core-unittests/src/test/java/com/codename1/ui/layouts/LayeredLayoutTest.java +++ b/maven/core-unittests/src/test/java/com/codename1/ui/layouts/LayeredLayoutTest.java @@ -282,6 +282,29 @@ void testUnitConstants() { assertEquals(LayeredLayout.UNIT_BASELINE, LayeredLayout.UNIT_BASELINE); } + @FormTest + void baselineReferenceUsesActualComponentBaselinesWithDifferentMarginsAndPadding() { + LayeredLayout layout = new LayeredLayout(); + Container container = new Container(layout); + container.setSize(new Dimension(500, 240)); + Label label = new Label("Baseline label"); + Button button = new Button("Baseline button"); + label.getAllStyles().setPadding(4, 6, 3, 3); + label.getAllStyles().setMargin(0, 0, 0, 0); + button.getAllStyles().setPadding(12, 10, 8, 8); + button.getAllStyles().setMargin(10, 5, 4, 4); + container.add(label).add(button); + layout.setInsets(label, "30px auto auto 20px"); + layout.setInsets(button, "baseline auto auto 180px"); + layout.setReferenceComponentTop(button, label, 0); + + container.layoutContainer(); + + assertEquals(label.getY() + label.getBaseline(label.getWidth(), label.getHeight()), + button.getY() + button.getBaseline(button.getWidth(), button.getHeight()), + "baseline unit must align the actual rendered text, including themed margins and padding"); + } + @FormTest void testLayeredLayoutWithVariedSizes() { LayeredLayout layout = new LayeredLayout(); diff --git a/maven/update-version.sh b/maven/update-version.sh index 22f1e84f2b3..8383f3a79bf 100755 --- a/maven/update-version.sh +++ b/maven/update-version.sh @@ -114,6 +114,19 @@ if [ -f "$settingsParent" ]; then perl -pi -e "s{\Q$oldVersion\E}{$version}g" "$settingsParent" fi +# GUI Builder editor is a standalone Maven-first desktop app distributed alongside +# the plugin, matching Settings and Game Builder. +for f in ../scripts/guibuilder/pom.xml \ + ../scripts/guibuilder/common/pom.xml \ + ../scripts/guibuilder/javase/pom.xml; do + [ -f "$f" ] && perl -pi -e "s{\Q$oldVersion\E}{$version}g" "$f" +done +guiBuilderParent=../scripts/guibuilder/pom.xml +if [ -f "$guiBuilderParent" ]; then + perl -pi -e "s{\Q$oldVersion\E}{$version}g" "$guiBuilderParent" + perl -pi -e "s{\Q$oldVersion\E}{$version}g" "$guiBuilderParent" +fi + echo "Committing version change in git" git add -u . # Note: the -u is to prevent adding files that aren't added to git yet. Only changed @@ -121,6 +134,7 @@ git add -u . git add -u ../scripts/gamebuilder git add -u ../scripts/certificatewizard git add -u ../scripts/settings +git add -u ../scripts/guibuilder git commit -m "Updated version to $version" if [[ "$version" == *-SNAPSHOT ]]; then echo "This is a snapshot version so not adding a tag" diff --git a/scripts/guibuilder/.gitignore b/scripts/guibuilder/.gitignore new file mode 100644 index 00000000000..16b737a5787 --- /dev/null +++ b/scripts/guibuilder/.gitignore @@ -0,0 +1,7 @@ +target/ +common/target/ +javase/target/ + +# Written by cn1:guibuilder (and by hand for the demo project); it holds +# absolute paths for one machine, so it is generated rather than tracked. +demo-project/guibuilder.input diff --git a/scripts/guibuilder/STATUS.md b/scripts/guibuilder/STATUS.md new file mode 100644 index 00000000000..77e70f3c949 --- /dev/null +++ b/scripts/guibuilder/STATUS.md @@ -0,0 +1,1153 @@ +# Modern GUI Builder status + +- Last updated: 2026-08-05 +- Branch: `feat-guibuilder-rewrite` +- Working state: committed and rebased on `origin/master` + +## Executive summary + +The old settings-era GUI Builder has been replaced locally with a Maven-first, +standalone Codename One desktop application under `scripts/guibuilder`. The new +builder opens an entire Maven project rather than one form at a time, discovers +all `.gui` files recursively, switches between them, renders a live preview, +edits the XML model, generates Java and binding code, edits `theme.css`, exposes +accessibility semantics, and can be inspected or driven over MCP. + +Guided Layout is the default for newly created containers. It is implemented on +top of `LayeredLayout` with persistent name-based relationships, responsive +insets, reference positions, size policies, baseline alignment, live simulated +drag/resize results, dependency visualization, detach behavior, multi-selection, +group movement, and group resizing. + +The latest `origin/master` was fetched and fast-forwarded into this working tree +on 2026-07-28. The previous base was 33 commits behind. There were no overlapping +paths between the upstream changes and this GUI Builder work, so the update was a +clean fast-forward. + +Current validation after that update and the 2026-07-28 review pass: + +- 18 `CodeEditorTest` tests pass. +- 23 `LayeredLayoutTest` tests pass. +- 41 focused core tests pass in total. +- All 66 standalone GUI Builder tests pass (63 previous plus 3 new model tests). +- All 5 `OpenGuiBuilderMojoTest` tests pass (2 previous plus 3 new launcher tests). +- Both `git diff --check` and `git diff --cached --check` pass. + +Important: the standalone suite only compiles after the current core is +reinstalled into the local repository it builds against. Running it against a +stale `8.0-SNAPSHOT` fails with `cannot find symbol +setProtectedRegionMarkers/setCursorPosition`, because the editor depends on +core APIs that are part of this same change. See +[Install current snapshots for the standalone build](#install-current-snapshots-for-the-standalone-build). + +The implementation is substantially functional and test-backed, but it is still +a large uncommitted change. It should not be treated as shipped until it is +reviewed, staged intentionally, committed on a feature branch, and validated in +CI and through another deliberate hands-on GUI session. + +## Original requirements and product direction + +The work is based on these requirements: + +- Replace the GUI Builder bundled with the old Codename One Settings. +- Package and distribute it like the standalone Maven-based Settings tool. +- Preserve the useful architecture of the old builder without retaining Ant-era + assumptions or the old downloaded `guibuilder.jar` mechanism. +- Use modern Codename One UI constructs, menus, CSS, accessibility, and MCP. +- Detect all GUI Builder files in a project and switch between forms quickly. +- Prefer CSS and UIIDs over resource-file authoring. +- Provide source and event editing inside the application. +- Make Guided Layout the default and strong enough to construct arbitrary, + responsive user interfaces. +- Support predictable drag/drop, live placement previews, resizing, alignment, + baseline relationships, size policies, detach behavior, multi-selection, and + atomic undo/redo. + +The historical design reference is +`codenameone/CodenameOne#3175`. The legacy implementation used for behavioral +reference is the `GUIBuilder` module of the pre-Maven CodenameOne checkout, which +is not part of this repository. + +## Repository and cleanup state + +### Latest master + +The branch is now exactly at: + +```text +c1e839071d83ed84b79dae527ad738a8061e0b83 +Let the grace passes mark objects the resolve guard was discarding (issue 5425) (#5477) +``` + +The update from `4c4fc3e327` to `c1e839071d` was a 33-commit fast-forward. +Upstream did not add or modify `scripts/guibuilder` or any other path that +collided with the local GUI Builder changes. + +### Cleanup performed + +The following unrelated or temporary material was discarded: + +- `docs/website/static/social/` — untracked social artwork unrelated to the + GUI Builder. +- `scripts/hellocodenameone/common/iosCerts/` — untracked local signing + certificates and provisioning profiles. +- `scripts/hellocodenameone/common/androidCerts/` — ignored local Android + keystore material. +- `scripts/hellocodenameone/common/target/` — unrelated ignored build output. +- Local signing paths and passwords in + `scripts/hellocodenameone/common/codenameone_settings.properties` were + restored to `HEAD`. +- The accidental stash was dropped after confirming that intended changes were + already recovered. Its only non-recovered changes were the obsolete + `CodeEditorHtml` and JavaSE Swing-editor path that were deliberately replaced + by master’s pure Codename One editor. + +No stash is required to recover the current GUI Builder work. The working tree +itself is now the authoritative local copy. + +### Important staging warning + +The worktree contains a mixture of staged, unstaged, and untracked changes +because it was recovered from an accidental stash and then adapted to master. +Most of `scripts/guibuilder` is still untracked. Before committing, inspect +`git status`, stage only the paths listed in this document, and do not use a +broad commit that could pick up unrelated files. + +## Module and file map + +### Standalone application + +- `scripts/guibuilder/pom.xml` + - Maven reactor root. + - Java 17 source/target. + - Modules for common and JavaSE code. + - `guibuilder-central` release profile with sources, Javadocs, GPG signing, + and Central publishing. +- `scripts/guibuilder/common/pom.xml` + - Codename One common application module. + - Compiles CSS and annotations through the Codename One Maven plugin. + - Attaches common tests for the JavaSE test module. +- `scripts/guibuilder/javase/pom.xml` + - Desktop executable artifact: + `com.codenameone:codenameone-guibuilder`. + - `executable-jar` profile builds the launcher JAR and copies runtime + dependencies into `target/libs`. +- `scripts/guibuilder/common/src/main/java/com/codename1/guibuilder/CodenameOneGUIBuilder.java` + - Main application, workspace, menus, inspector, drag/drop, resize, + source/CSS editing, generation, undo/redo, accessibility state, and MCP + domain operations. +- `scripts/guibuilder/common/src/main/java/com/codename1/guibuilder/GuiBuilderMcpController.java` + - GUI Builder-specific MCP tools and live action journal. +- `scripts/guibuilder/common/src/main/java/com/codename1/guibuilder/model/GuiDocument.java` + - XML model, normalized attributes, selection, clipboard operations, + hierarchy changes, toolbar commands, transactions, undo/redo, and + serialization. +- `scripts/guibuilder/common/src/main/java/com/codename1/guibuilder/project/ProjectBinding.java` + - Parses the project-binding input file. +- `scripts/guibuilder/common/src/main/java/com/codename1/guibuilder/project/ProjectIO.java` + - Portable file access, recursive `.gui` discovery, and UTF-8 reads/writes. +- `scripts/guibuilder/common/src/main/java/com/codename1/guibuilder/ui/ComponentPreviewFactory.java` + - Converts XML elements to live Codename One components. + - Applies component properties, layout constraints, stable accessibility + identifiers, design-only event behavior, and safe preferred-size overrides. +- `scripts/guibuilder/common/src/main/java/com/codename1/guibuilder/ui/DragGuideOverlay.java` + - Selection outlines, visible resize handles, drag ghosts, snap guides, + dependency arrows, affected-component rectangles, and descriptive tags. +- `scripts/guibuilder/common/src/main/java/com/codename1/guibuilder/ui/GuidedLayoutSupport.java` + - Persistent name-based Guided Layout constraints and size policies. +- `scripts/guibuilder/common/src/main/css/theme.css` + - Builder UI theme, including light/dark styles, toolbars, inspector, + sidebars, editors, selection controls, and canvas skins. +- `scripts/guibuilder/javase/src/desktop/java/com/codename1/guibuilder/CodenameOneGUIBuilderLauncher.java` + - Desktop entry point. +- `scripts/guibuilder/javase/src/desktop/java/com/codename1/guibuilder/CodenameOneGUIBuilderStub.java` + - Generated-style JavaSE lifecycle wrapper. +- `scripts/guibuilder/tools/guibuilder-mcp-client.mjs` + - Small JSON-line MCP client used to inspect and drive the live builder. + +### Demo project + +`scripts/guibuilder/demo-project` is the reproducible manual-test project. It +contains: + +- `GuidedLayoutForm.gui` +- `BorderDropForm.gui` +- `BoxXLayoutForm.gui` +- `GridLayoutForm.gui` +- `TableLayoutForm.gui` +- `NestedLayoutsForm.gui` +- `LoginForm.gui` +- `theme.css` +- `guibuilder.input` + +The input file binds: + +```text +projectDir= +guiDir=/src/main/guibuilder +sourceDir=/src/main/java +cssFile=/src/main/css/theme.css +initialForm=com.example.GuidedLayoutForm +``` + +## Maven distribution and launcher integration + +`maven/codenameone-maven-plugin/src/main/java/com/codename1/maven/OpenGuiBuilderMojo.java` +was rewritten to launch the standalone Maven artifact instead of downloading or +opening the old `~/.codenameone/guibuilder.jar`. + +Current behavior: + +- `mvn cn1:guibuilder` can run from the application aggregator or `common` + module. +- `-DclassName=com.example.FormName` optionally selects the initial form. +- The mojo creates a unique binding file under + `~/.codenameoneGUIBuilder/`. +- The binding covers the whole project: project directory, GUI directory, + source directory, CSS file, and optional initial form. +- The launcher resolves `com.codenameone:codenameone-guibuilder` and its + transitive runtime dependencies through Maven. +- It normally spawns a detached process and writes output to + `~/.codenameoneGUIBuilder/guibuilder.log`. +- `-Dguibuilder.spawn=false` or the legacy `-Dspawn=false` path can run it in + the foreground. +- A JVM property prevents duplicate launches within one Maven invocation. +- Every other `guibuilder.*` system property is forwarded to the editor JVM, so + `mvn cn1:guibuilder -Dguibuilder.mcp.port=18349` (and `darkMode`, + `canvasMode`, `initialSelection`, `openEditor`) reach the application. The + `guibuilder.input` binding and the `guibuilder.spawn` flag are owned by the + mojo and are never forwarded. +- The process is named for the dock, taskbar, and window manager, and the + `java.desktop/com.apple.eawt*` packages the JavaSE port needs are exported, + matching `cn1:settings` and `cn1:certificate-wizard`. +- The goal fails with a friendly message when Maven runs on a JDK older than + 17. The editor is a Java 17 artifact and the spawned process writes only to + `guibuilder.log`, so without the check an old JDK produces a silent + `UnsupportedClassVersionError`. +- No Ant GUI Builder protocol, running marker file, resource-file handoff, or + IDE source-jump callback remains. + +Release integration is present in: + +- `.github/workflows/release-on-maven-central.yml` +- `maven/update-version.sh` + +The release workflow deploys the executable JavaSE artifact after the main +Codename One deployment and polls Maven Central for it. The version update +script updates all three GUI Builder POMs and their Codename One/plugin +properties. + +## Workspace and general UX + +The builder uses a resizable three-area workspace: + +- Left: project forms/hierarchy tabs plus the searchable component palette. +- Center: canvas-mode toolbar and live design surface. +- Right: Properties, Layout, and Events inspector tabs. + +Nested `SplitPane` components make both sidebar boundaries resizable. Defensive +null guards were added to `SplitPane` and `Tabs` because the builder rebuilds +parts of the inspector and tab hierarchy during active pointer gestures. + +The main toolbar exposes: + +- Save +- Undo +- Redo +- Refresh +- CSS editor +- Companion Java editor + +The canvas toolbar exposes: + +- Phone portrait +- Phone landscape +- Tablet portrait +- Desktop full-canvas mode + +Desktop mode has no device skin and fills the available center area. Phone and +tablet modes use predictable fixed design surfaces. Dark mode is persisted in +preferences and can be toggled through the application command path and MCP. + +All project forms are discovered recursively beneath `guiDir`, sorted, shown +with simple names and fully qualified tooltips, and opened as independent +`GuiDocument` instances. Unsaved work is never silently discarded when +switching forms. + +The component palette currently includes: + +- Button +- Label +- SpanLabel +- TextField +- TextArea +- CheckBox +- RadioButton +- Slider +- Container +- Tabs + +Palette items can be activated or dragged. Palette search filters immediately. + +## Component model and property editing + +`GuiDocument` preserves unknown XML while normalizing edited attributes +case-insensitively. It supports: + +- Unique component names. +- Add, delete, copy, cut, paste, reorder, and cross-container moves. +- Cycle prevention when moving containers in the hierarchy. +- Compound transactions so one placement/group action is one undo entry. +- Toolbar commands stored separately from visual components. +- BorderLayout constraint normalization so duplicate or missing constraints do + not hide components. +- Default UIIDs derived from component type when an explicit UIID is absent. +- Guided Layout as the default for new child-bearing components. + +The Properties inspector supports type-aware fields, including: + +- Name +- Form title +- Text +- Hint +- UIID/CSS selector +- Enabled, visible, RTL +- Alignment, gap, ticker +- Button toggle state +- CheckBox/RadioButton selected state +- Text-area columns, rows, maximum length, editable/grow settings, constraints +- Slider range/progress/editability +- Tabs selected index and placement +- Container horizontal/vertical scrolling + +Numeric layout fields use bounded parsing and reject invalid text and overflow +instead of writing malformed values into the document. + +Editing a text-bearing component or form title can be started through the +inspector, long press, or double click. The inline `TextField` is an overlay, +not a child inserted into the designed layout. Clicking outside commits and +tears it down; the XML property and visible preview update together. + +Form toolbar commands can be added, removed, renamed, assigned to left/right/ +overflow/side placement, and connected to generated event handlers. + +## Layout adapter architecture + +Every supported layout is routed through a placement adapter: + +- `LayeredPlacementAdapter` +- `BorderPlacementAdapter` +- `BoxPlacementAdapter` +- `FlowPlacementAdapter` +- `GridPlacementAdapter` +- `TablePlacementAdapter` + +This separation is important. A generic component-reorder algorithm cannot +correctly represent BorderLayout slots, TableLayout cells, Guided Layout +relationships, or BoxLayout insertion gaps. + +### BorderLayout + +- Edge bands are resolved before component hit testing so Center cannot consume + all reachable East/West/North/South positions. +- The band size scales with the dragged component while remaining usable with a + mouse. +- Dropping onto an occupied slot swaps components instead of stacking one + behind the other. +- Missing and duplicate legacy constraints are normalized deterministically. +- The designer caps edge-component preferred dimensions to keep a displaced + component from consuming the whole preview. +- Tests verify that a West-to-Center move remains visible, serializes, and + renders after reopening. + +### BoxLayout and FlowLayout + +- A real temporary spacer opens at the exact insertion point. +- X and Y axes calculate before/after using the correct dimension. +- The spacer is removed on cancellation and completion. +- Reordering updates both the XML and rendered order. +- Scrollable previews disable tensile/bounce behavior. +- Edge dragging can advance a scrollable horizontal container without the + native tensile animation fighting the drag. + +### GridLayout + +- Drops reorder children into deterministic grid order. +- Cells remain unique and components are not overlaid. +- Row/column values are validated. + +### TableLayout + +- Drops assign unique cells. +- Rows expand when necessary rather than hiding overflow. +- Reorder commands reassign cells and visibly change order. +- Row, column, spans, and optional percentage sizes are editable. + +### Nested containers and form isolation + +- Hit testing finds the deepest valid container. +- Moving across nested containers updates only the selected branch. +- A container cannot be moved into its own descendant. +- Stale elements from a previously opened form are rejected because every drag + retains and validates its originating `GuiDocument`. + +## Guided Layout + +Guided Layout is represented by `LayeredLayout` plus builder-owned, +name-based metadata. Names are used because serialized component indexes become +unstable when siblings are reordered. + +Per-component data includes: + +- `layeredInsets` +- `guidedReferences` +- `guidedReferencePositions` +- horizontal and vertical anchors +- horizontal and vertical size policies +- optional matched-width/matched-height targets +- fixed preferred width/height values when explicitly resized + +Supported size policies on each axis: + +- Preferred +- Fixed +- Fill parent +- Match reference + +Supported snap/alignment relationships: + +- Left edge +- Horizontal center +- Right edge +- Top edge +- Text baseline +- Bottom edge +- Same width +- Same height +- Fill width +- Fill height + +The `LayeredLayout` baseline implementation was corrected to use actual +component baselines, margins, and padding for components that declare a +baseline. `Component.getBaseline()` has a non-abstract default that returns the +bottom content edge rather than a text baseline, so the layout only trusts it +for components that also describe their baseline resize behavior (`Label` and +its subclasses: `Button`, `CheckBox`, `RadioButton`). Everything else +(`Container`, `TextArea`, `TextField`) keeps the historical font-ascent +approximation, so no existing layout changes behavior. See +[Known limitations](#known-limitations-and-remaining-work) for the follow-up: +`TextArea` should report a real text baseline. + +### Drag behavior + +- The initial pointer grab offset is preserved, so a component does not jump + when dragging starts. +- Selection does not modify component styles or layout metrics. +- A live simulation clones the document and lays out the proposed result. +- The overlay shows the dragged ghost, every affected rectangle, snap lines, + dependency arrows, and concise movement/size deltas. +- The simulated result and committed result are tested to match. +- Dependencies are visualized before commit so cascading effects are visible. +- Moving into free space tears away incoming relationships and persists fixed + insets. +- Snapping back to the same reference preserves the explicit relationship. +- Cycle-breaking rebases or freezes only the relationship that would create the + cycle, preventing the rest of the layout from bouncing or disappearing. +- Transitive dependents remain visible when the primary/secondary sample + components are moved around each other. + +### Resizing + +- Edge and corner hit zones are larger than the painted handles. +- Cursor shapes change to the relevant horizontal, vertical, or diagonal resize + cursor. +- The filled handles identify the stable reference component. +- Live resize simulation shows dependent cascade without mutating the model. +- Resizing the reference component can resize all selected components in + unison. +- Same-width/same-height uses the primary selected component as the reference; + other components stretch to it instead of all components shrinking to an + arbitrary minimum. +- Fixed guided dimensions are implemented through `calcPreferredSize()` + overrides in generated/preview components. Deprecated + `setPreferredW()`, `setPreferredH()`, or similar APIs are not used. + +### Multi-selection + +- Shift, Control, or Command click toggles additive selection. +- A normal click selects a new component without requiring an initial drag. +- Every selected component receives its own outline and handles. +- The first/primary selected component remains the reference and uses filled, + larger handles. +- Group drag preserves relative positions and internal relationships. +- Group drag and group resize commit atomically and undo in one step. +- The floating icon toolbar is shown only for meaningful multi-selection in one + Guided Layout parent. +- Every icon has a tooltip and accessibility label explaining that the + filled-handle component is the reference. +- Actions cover left/center/right, top/baseline/bottom, same width, same + height, and disconnect. + +## Undo/redo + +Structural and layout changes are stored in `GuiDocument` snapshots. Compound +operations use explicit transactions, making a group move, group resize, slot +swap, or relationship action one undo step. + +The embedded pure `CodeEditor` has its own undo/redo stack. CSS and Java editing +remain inside the editor rather than routing Command-Z to close the editor. + +Form-model undo/redo and source-editor undo/redo are separate domains. A future +polish pass may expose which domain currently owns the global shortcut more +explicitly. + +## CSS and theme editing + +The CSS button opens the project `theme.css` in the built-in Codename One +`CodeEditor`, not a native external application or CEF-based editor. + +Current behavior: + +- Editable pure Codename One source surface. +- CSS syntax highlighting and editor gutter come from the modern `CodeEditor`. +- A short debounced live-edit timer reads the editor text. +- `CSSThemeCompiler` compiles into an in-memory `MutableResource`. +- The compiled project theme is applied to the preview immediately. +- The builder theme is restored after preview styling, preventing the builder + chrome from inheriting arbitrary project styles. +- Compile failures produce editor diagnostics and leave the last valid preview + intact. +- Saving writes UTF-8 back to the project CSS file. +- Font and box-unit values from the runtime compiler are normalized for safe + preview use. + +The project preview is CSS-driven; the standalone builder still has a compiled +`theme.res` generated from its own `theme.css` as a normal Codename One build +artifact. This is not a return to resource-file authoring. + +## Java source, events, and binding + +The Code toolbar opens an editable, live-generated companion source preview +inside the builder. + +Generated source contains explicit markers: + +```java +// +// +// +// +``` + +The generated regions are protected in the editor while the user-code region +remains editable. Regeneration merges the existing user-code region instead of +deleting it. The editor can move the caret directly to a generated event +handler. + +Generated code includes: + +- Form title and layout. +- Component fields and construction. +- Names and UIIDs. +- BorderLayout and TableLayout constraints. +- Guided Layout insets, name-resolved references, reference positions, and + anchors. +- Toolbar commands. +- Component and command action handlers. + +Binding strategy is selectable per form: + +- None +- `PropertyBusinessObject` +- `@Bindable` POJO using build-time binding annotations + +Generated-source tests compile the form and model together for all three +strategies. No-binding output has no model dependency. + +## Accessibility + +The builder uses the accessibility APIs added on master: + +- Stable identifiers for workspace, forms list, hierarchy tree, palette, + inspector, status, canvas modes, selection actions, and preview components. +- Roles for lists, tree, search field, and generic container groups. +- Labels, descriptions, hints, selected state, pane titles, and grouping. +- A polite live region for status updates. + +Preview component identifiers follow: + +```text +guibuilder.preview. +``` + +These identifiers are also published in MCP state, allowing tests and agents to +correlate XML elements, physical bounds, and accessibility nodes. + +## MCP support and live inspection + +The builder registers the portable UI accessibility MCP tools provided by +Codename One and adds domain-specific tools: + +- `guibuilder_state` + - Active form and path + - All form names + - Canvas mode and bounds + - Dark mode + - Modified/undo/redo state + - Selected component(s) + - Selection paint bounds + - Component attributes, layouts, bounds, visibility, and accessibility IDs + - Current drop/resize guide +- `guibuilder_actions` + - Sequence-numbered journal + - Optional long poll up to ten seconds + - Bounded history of 500 actions +- `guibuilder_select` + - Normal or additive selection by component name +- `guibuilder_open_form` + - Safe form switching that refuses to discard unsaved work +- `guibuilder_drag` + - Drives the real pointer drag path by absolute coordinates or semantic + target/placement +- `guibuilder_command` + - Save, undo, redo, refresh, dark mode, and canvas-mode commands + +The socket is opt-in through: + +```text +-Dguibuilder.mcp.port=18349 +``` + +The verified endpoint is loopback: + +```text +127.0.0.1:18349 +``` + +The MCP controller crosses an EDT barrier after commands before returning +state, preventing new component bounds from being paired with stale selection +bounds. + +## Tests + +### Standalone GUI Builder: 63 tests + +`DesignerInteractionTest` has 41 tests covering: + +- Fixed width without freezing theme-derived height +- Exact BoxLayout spacer targets +- Selection without drag +- modifier-based multi-selection +- reference-based same width +- atomic group movement and undo +- group ghost/commit equivalence +- internal group relationship preservation +- guided rectangle persistence +- pointer grab offsets +- responsive surface-edge docking +- free-space detach +- explicit relationship preservation +- cycle prevention and downstream visibility +- actual baseline alignment +- center anchors +- selection/layout metric stability +- resize snapping +- group resize +- resize simulation +- drag simulation +- BorderLayout swap/serialization +- inline editor teardown +- explicit placement adapter routing +- reachable BorderLayout edges +- numeric validation +- BoxLayout X +- GridLayout +- TableLayout placement/reorder +- nested placement +- cross-form stale-element rejection +- horizontal scrolling and drag autoscroll +- accessibility identifiers +- MCP state, action journal, and additive selection + +Additional standalone tests: + +- `GuiDocumentTest`: 18, including the three added by the review pass: + - a pasted container renames its children and keeps its internal + relationships pointed at the copy, + - renaming keeps names unique and repoints every relationship in one undo + step, + - deleting a referenced component leaves no dangling relationship. +- `GeneratedSourceTest`: 4 +- `CodeEditorInteractionTest`: 2 +- `ProjectBindingTest`: 1 + +Latest result: + +```text +Tests run: 66, Failures: 0, Errors: 0, Skipped: 0 +``` + +### Core tests: 41 + +Latest focused result: + +```text +CodeEditorTest: 18 passed +LayeredLayoutTest: 23 passed +Total: 41 passed +``` + +The two new editor tests verify: + +- Protected generated regions reject user edits while the user-code region + remains editable. +- `setCursorPosition()` moves the pure editor caret. + +The LayeredLayout regression verifies actual baseline alignment across +different component margins and padding. + +### Maven launcher tests: 5 + +`OpenGuiBuilderMojoTest` verifies: + +- Binding files contain project-wide GUI, source, CSS, and initial-form data. +- The goal launches correctly from either the application aggregator or common + module without duplicate launches. +- `guibuilder.*` properties are forwarded while the binding and spawn flag are + not. +- The desktop identity arguments export the JDK packages the JavaSE runtime + needs. +- The running Java feature version is detected (the input to the JDK 17 gate). + +Latest result: + +```text +Tests run: 5, Failures: 0, Errors: 0, Skipped: 0 +``` + +### Non-failing test noise + +- The JavaSE interaction suite prints many `EDT violation detected!` diagnostic + lines. The assertions pass, but this noise should eventually be investigated + or suppressed by making the tests consistently cross the EDT. +- The plugin test prints an HTML tidy warning: + `line 23 column 45 - Error: is not recognized!` + It is non-fatal and unrelated to the GUI Builder assertions. +- The supplied `-Plocal-dev-javase` core-test profile is not defined at the + top-level Maven reactor in this checkout; Maven warns and continues. The + focused tests still compile and pass with Java 8. + +## Build and test commands + +### Core regression tests + +Run with Java 8: + +```bash +cd maven +export JAVA_HOME="$(/usr/libexec/java_home -v 1.8)" # any JDK 8 +export PATH="$JAVA_HOME/bin:$PATH" +mvn -pl core-unittests -am \ + -DunitTests=true \ + -Dmaven.javadoc.skip=true \ + -Plocal-dev-javase \ + -Dcn1.binaries="$PWD/target/cn1-binaries" \ + -Dtest=CodeEditorTest,LayeredLayoutTest \ + -Dsurefire.failIfNoSpecifiedTests=false \ + test +``` + +### Install current snapshots for the standalone build + +The isolated repository used during development is +`/tmp/cn1-local-repo`. Reinstall current core, CSS compiler, JavaSE, and Maven +plugin artifacts after changing branches or updating master. Otherwise Maven +can silently test against an older `8.0-SNAPSHOT`. + +This is a prerequisite, not an optimization. The editor calls +`CodeEditor.setProtectedRegionMarkers()` and `setCursorPosition()`, which are +added by this change, so against an older snapshot the standalone build fails +with `cannot find symbol` before any test runs. Reinstalling `core` alone is +enough after editing only `CodenameOne/src`. + +JavaSE must be built with `cn1.binaries` so `jfxrt.jar` is available: + +```bash +cd maven +mvn -Dmaven.repo.local=/tmp/cn1-local-repo \ + -pl factory,core,css-compiler \ + -DskipTests -Dmaven.javadoc.skip=true install + +mvn -Dcn1.binaries="$PWD/target/cn1-binaries" \ + -Dmaven.repo.local=/tmp/cn1-local-repo \ + -pl javase \ + -DskipTests -Dmaven.javadoc.skip=true \ + clean install + +mvn -Dmaven.repo.local=/tmp/cn1-local-repo \ + -pl codenameone-maven-plugin \ + -DskipTests -Dmaven.javadoc.skip=true install +``` + +### Full GUI Builder suite + +Run with Java 21 because the standalone project targets Java 17 and the +executable build was validated with the installed Azul 21 JDK: + +```bash +cd scripts/guibuilder +export JAVA_HOME="$(/usr/libexec/java_home -v 21)" # JDK 17 or newer +export PATH="$JAVA_HOME/bin:$PATH" +mvn -nsu \ + -Dmaven.repo.local=/tmp/cn1-local-repo \ + -pl javase -am clean test +``` + +On macOS the CSS compiler may exit with code 134 inside the filesystem/process +sandbox. Run the same Maven command with normal desktop/native process access; +this is a process-environment issue, not a CSS syntax failure. + +### Maven launcher test + +```bash +cd maven +mvn -nsu \ + -Dmaven.repo.local=/tmp/cn1-local-repo \ + -pl codenameone-maven-plugin \ + -Dtest=OpenGuiBuilderMojoTest \ + test +``` + +### Package executable + +```bash +cd scripts/guibuilder +mvn -nsu \ + -Dmaven.repo.local=/tmp/cn1-local-repo \ + -pl javase -am \ + -Pexecutable-jar \ + -DskipTests package +``` + +Expected main artifact: + +```text +scripts/guibuilder/javase/target/codenameone-guibuilder-8.0-SNAPSHOT.jar +``` + +### Run the demo with MCP + +The binding file holds absolute paths, so it is generated per machine rather +than tracked (see `scripts/guibuilder/.gitignore`). `mvn cn1:guibuilder` writes +one for a real project; for the demo project, write it once: + +```bash +cd scripts/guibuilder/demo-project +cat > guibuilder.input < + + 4.0.0 + com.codenameone.guibuildercn1-guibuilder8.0-SNAPSHOT + cn1-guibuilder-common + common + + org.codehaus.mojoproperties-maven-plugin1.0.0initializeread-project-properties${basedir}/codenameone_settings.properties + com.codenameonecodenameone-maven-plugin${cn1.plugin.version}true + cn1-process-classesprocess-classesbytecode-compliancecssprocess-annotations + attach-test-artifacttestattach-test-artifact + + org.apache.maven.pluginsmaven-surefire-plugintruefalse + + + com.codenameonecodenameone-core + org.junit.jupiterjunit-jupiter-api5.10.2test + org.junit.jupiterjunit-jupiter-engine5.10.2test + + diff --git a/scripts/guibuilder/common/src/main/css/theme.css b/scripts/guibuilder/common/src/main/css/theme.css new file mode 100644 index 00000000000..82d35628b12 --- /dev/null +++ b/scripts/guibuilder/common/src/main/css/theme.css @@ -0,0 +1,430 @@ +#Constants { + includeNativeBool: true; + globalToolbarBool: true; + defaultSourceDPIInt: 0; + centeredPopupBool: true; +} + +Form, BuilderCanvasArea, BuilderStage { + background-color: #f4f6fa; + color: #182033; + font-family: "native:MainRegular"; +} + +Toolbar { + background-color: #17233b; + color: #ffffff; + padding: 1mm 2mm; + border: none; +} + +Title, TitleCommand { + background-color: transparent; + color: #ffffff; + font-family: "native:MainBold"; + font-size: 3mm; +} + +BuilderSidebar, BuilderInspector { + background-color: #ffffff; + border-right: 0.25mm solid #d8deea; +} + +BuilderInspector { + border-left: 0.25mm solid #d8deea; + border-right: none; +} + +BuilderSectionTitle { + background-color: #f8f9fc; + color: #65708a; + font-family: "native:MainBold"; + font-size: 2.1mm; + padding: 2mm 2.5mm 1.4mm 2.5mm; + border-bottom: 0.25mm solid #e1e5ee; +} + +BuilderFormItem, BuilderFormItemSelected { + background-color: transparent; + color: #313b52; + text-align: left; + font-size: 2.55mm; + padding: 1.7mm 2mm; + margin: 0.4mm 1mm; + border: none; +} + +BuilderFormItemSelected { + background-color: #eaf1ff; + color: #2459b8; + border-left: 0.7mm solid #4f8cff; +} + +BuilderHierarchyItem, BuilderHierarchySelected { + background-color: transparent; + color: #34415a; + border: none; + text-align: left; + font-size: 2.2mm; + padding: 1.2mm 1mm; + margin: 0.2mm 0.5mm; +} + +BuilderHierarchySelected { + background-color: #eaf1ff; + color: #2459b8; + border-left: 0.6mm solid #4f8cff; +} + +SplitPaneDivider { + background-color: #d8deea; +} + +SplitPaneDragHandle { + color: #71809e; +} + +BuilderFormIcon, BuilderInlineIcon, BuilderPaletteIcon { + color: #71809e; + padding: 0 1mm 0 0; +} + +BuilderPalette { + background-color: #f8f9fc; + border-top: 0.25mm solid #d8deea; + padding: 0 1mm 1mm 1mm; +} + +BuilderSearch, BuilderField, BuilderPicker { + background-color: #ffffff; + color: #182033; + border: 0.25mm solid #c7cfdd; + border-radius: 1.4mm; + padding: 1.4mm 1.6mm; + margin: 1mm; + font-size: 2.55mm; +} + +BuilderFieldError { + background-color: #fff0f0; + color: #bd3232; + border: 1px solid #d64545; + border-radius: 1.4mm; + padding: 1.4mm 1.6mm; + margin: 1mm; + font-size: 2.55mm; +} + +BuilderInlineEditor { + background-color: #ffffff; + color: #182033; + border: 2px solid #20b486; + border-radius: 1mm; + padding: 1mm; + margin: 0; + font-size: 2.7mm; +} + +BuilderPaletteItem { + background-color: #ffffff; + color: #34415a; + border: 0.25mm solid #dce1eb; + border-radius: 1.5mm; + text-align: left; + font-size: 2.2mm; + padding: 1.8mm 1.2mm; + margin: 0.6mm; +} + +BuilderCanvasToolbar { + background-color: #ffffff; + padding: 0.7mm; + border-bottom: 0.25mm solid #d8deea; +} + +BuilderCanvasButton { + background-color: transparent; + color: #5f6c86; + border: none; + border-radius: 1.2mm; + padding: 1.2mm; + margin: 0.3mm; +} + +BuilderCanvasButton.pressed { + background-color: #eaf1ff; + color: #2459b8; +} + +BuilderCanvasIcon { color: #5f6c86; } + +BuilderSelectionActions { + background-color: #192437; + border: 0.25mm solid #61708a; + border-radius: 1.8mm; + padding: 0.45mm; +} + +BuilderSelectionAction { + background-color: transparent; + color: #ffffff; + border: none; + border-radius: 1mm; + padding: 0.8mm; + margin: 0.15mm; + font-size: 2.2mm; +} + +BuilderSelectionAction.pressed { background-color: #356dcc; } + +TooltipDialog { + background-color: #192437; + border: 0.3mm solid #4f8cff; + border-radius: 1.6mm; + padding: 0; + margin: 0; +} + +Tooltip { + background-color: #192437; + color: #ffffff; + border: none; + padding: 1.2mm 2.2mm; + font-size: 2.2mm; +} + +BuilderStage { + padding: 6mm; +} + +BuilderDevice { + background-color: #ffffff; + border: 0.35mm solid #bdc6d6; + border-radius: 3mm; + padding: 4mm 2mm; + margin: 2mm; +} + +BuilderDesktopSurface { + background-color: #ffffff; + border: none; + padding: 0; + margin: 0; +} + +BuilderFormToolbar { + background-color: #17233b; + color: #ffffff; + padding: 1mm 2mm; + border-bottom: 0.25mm solid #0f182b; +} + +BuilderSelection { + background-color: #f7faff; + padding: 1mm; + border: 0.5mm solid #4f8cff; +} + +BuilderDropSpacer { + background-color: #20b486; + color: #20b486; + border: 1px solid #148766; + padding: 0; + margin: 1px; +} + +BuilderEmptyHint, BuilderEmptyProject { + background-color: #f7f9fc; + color: #7c879b; + border: 0.25mm solid #bdc6d6; + padding: 4mm; + margin: 1mm; + text-align: center; +} + +BuilderInspectorTabs { + background-color: #ffffff; +} + +TabsContainer, TabContainer { + background-color: #ffffff; + border: none; +} + +Tab, TabSelected { + background-color: #f8f9fc; + color: #68738a; + border: none; + border-bottom: 0.3mm solid #d8deea; + padding: 1.7mm 1mm; + font-size: 2.2mm; +} + +TabSelected { + background-color: #ffffff; + color: #2459b8; + border-bottom: 0.7mm solid #4f8cff; +} + +BuilderInspectorFields { + background-color: #ffffff; + padding: 1.5mm; +} + +BuilderInspectorComponent { + background-color: #eef4ff; + color: #2459b8; + font-family: "native:MainBold"; + padding: 1.7mm 2mm; + margin: 0.7mm; + border-radius: 1.4mm; +} + +BuilderCommandCard { + background-color: #f7f9fc; + border: 0.25mm solid #d8deea; + border-radius: 1.5mm; + padding: 1mm; + margin: 1mm; +} + +BuilderEditorPane { + background-color: #ffffff; + border-right: 0.25mm solid #d8deea; +} + +BuilderEditorToolbar { + background-color: #f8f9fc; + border-bottom: 0.25mm solid #d8deea; + padding: 0.5mm; +} + +BuilderEditorTitle { + color: #34415a; + font-family: "native:MainBold"; + padding: 2mm; +} + +BuilderFieldLabel { + background-color: transparent; + color: #606c83; + font-size: 2.25mm; + padding: 1.2mm 1mm 0 1mm; +} + +BuilderCheck, BuilderCheck.selected { + background-color: transparent; + color: #34415a; + border: none; + padding: 1.2mm 1mm; + font-size: 2.45mm; +} + +BuilderHelp { + background-color: #f7f9fc; + color: #68738a; + font-size: 2.25mm; + padding: 2mm; + margin: 1mm; + border-radius: 1.2mm; +} + +BuilderPrimaryAction, BuilderSecondaryAction, BuilderDangerAction { + background-color: #3677e8; + color: #ffffff; + border: none; + border-radius: 1.5mm; + padding: 1.7mm 2mm; + margin: 1mm; + font-size: 2.45mm; +} + +BuilderSecondaryAction { + background-color: #eef2f8; + color: #34415a; +} + +BuilderDangerAction { + background-color: #fff0f0; + color: #bd3232; +} + +BuilderDangerIcon { color: #bd3232; } + +BuilderStatus { + background-color: #17233b; + color: #d9e2f4; + padding: 1mm 2mm; + font-size: 2.1mm; +} + +BuilderWelcome { + background-color: #ffffff; + border: 0.25mm solid #d8deea; + border-radius: 2.5mm; + padding: 7mm; +} + +BuilderWelcomeTitle { + color: #182033; + font-family: "native:MainBold"; + font-size: 5mm; + text-align: center; +} + +BuilderWelcomeCopy { + color: #68738a; + font-size: 2.8mm; + text-align: center; + padding: 2mm; +} + +@media (prefers-color-scheme: dark) { + Form, BuilderCanvasArea, BuilderStage { background-color: #071B4D; color: #F5F8FF; } + Toolbar { background-color: #071B4D; color: #F5F8FF; } + Title, TitleCommand { background-color: transparent; color: #F5F8FF; } + BuilderSidebar { background-color: #102B66; border-right: 1px solid #36558F; } + BuilderInspector { background-color: #102B66; border-left: 1px solid #36558F; border-right: none; } + BuilderSectionTitle { background-color: #163575; color: #D8E2F7; border-bottom: 1px solid #36558F; } + BuilderFormItem { background-color: transparent; color: #F5F8FF; } + BuilderFormItemSelected { background-color: #21478F; color: #F5F8FF; border-left-color: #4D86FF; } + BuilderHierarchyItem { background-color: transparent; color: #F5F8FF; } + BuilderHierarchySelected { background-color: #21478F; color: #F5F8FF; border-left-color: #4D86FF; } + SplitPaneDivider { background-color: #36558F; } + SplitPaneDragHandle, BuilderFormIcon, BuilderInlineIcon, BuilderPaletteIcon, BuilderCanvasIcon { color: #A8B8DA; } + BuilderPalette { background-color: #163575; border-top: 1px solid #36558F; } + BuilderSearch, BuilderField, BuilderPicker { background-color: #0E2A61; color: #F5F8FF; border: 1px solid #5876A9; } + BuilderFieldError { background-color: #4B2940; color: #FFC2CC; border: 1px solid #D96A7A; } + BuilderInlineEditor { background-color: #0E2A61; color: #F5F8FF; border: 2px solid #41D6A4; } + BuilderPaletteItem { background-color: #102B66; color: #F5F8FF; border: 1px solid #36558F; } + BuilderCanvasToolbar { background-color: #102B66; border-bottom: 1px solid #36558F; } + BuilderCanvasButton { background-color: transparent; color: #A8B8DA; } + BuilderCanvasButton.pressed { background-color: #21478F; color: #F5F8FF; } + BuilderSelectionActions { background-color: #07132F; border: 1px solid #5876A9; } + BuilderSelectionAction { background-color: transparent; color: #F5F8FF; } + BuilderSelectionAction.pressed { background-color: #21478F; } + BuilderDevice { background-color: #102B66; border: 1px solid #5876A9; } + BuilderDesktopSurface { background-color: #071B4D; border: none; } + BuilderFormToolbar { background-color: #071B4D; color: #F5F8FF; border-bottom: 1px solid #36558F; } + BuilderSelection { background-color: #163575; border: 2px solid #4D86FF; } + BuilderDropSpacer { background-color: #41D6A4; color: #41D6A4; border: 1px solid #20B486; } + BuilderEmptyHint, BuilderEmptyProject { background-color: #163575; color: #D8E2F7; border: 1px solid #5876A9; } + BuilderInspectorTabs, TabsContainer, TabContainer, BuilderInspectorFields { background-color: #102B66; } + Tab { background-color: #163575; color: #D8E2F7; border: none; border-bottom: 1px solid #36558F; } + TabSelected { background-color: #102B66; color: #F5F8FF; border-bottom-color: #4D86FF; } + BuilderInspectorComponent { background-color: #21478F; color: #F5F8FF; } + BuilderCommandCard { background-color: #163575; border: 1px solid #36558F; } + BuilderEditorPane { background-color: #102B66; border-right: 1px solid #36558F; } + BuilderEditorToolbar { background-color: #102B66; border-bottom: 1px solid #36558F; } + BuilderEditorTitle { color: #F5F8FF; } + BuilderFieldLabel { background-color: transparent; color: #A8B8DA; } + BuilderCheck, BuilderCheck.selected { background-color: transparent; color: #F5F8FF; border: none; } + BuilderHelp { background-color: #163575; color: #D8E2F7; } + BuilderPrimaryAction { background-color: #4D86FF; color: #F5F8FF; } + BuilderSecondaryAction { background-color: #163575; color: #F5F8FF; border: 1px solid #36558F; } + BuilderDangerAction { background-color: #4B2940; color: #FFC2CC; } + BuilderStatus { background-color: #071B4D; color: #F5F8FF; } + BuilderWelcome { background-color: #102B66; border: 1px solid #36558F; } + BuilderWelcomeTitle { color: #F5F8FF; } + BuilderWelcomeCopy { color: #A8B8DA; } +} diff --git a/scripts/guibuilder/common/src/main/java/com/codename1/guibuilder/CodenameOneGUIBuilder.java b/scripts/guibuilder/common/src/main/java/com/codename1/guibuilder/CodenameOneGUIBuilder.java new file mode 100644 index 00000000000..b3f8b710487 --- /dev/null +++ b/scripts/guibuilder/common/src/main/java/com/codename1/guibuilder/CodenameOneGUIBuilder.java @@ -0,0 +1,4558 @@ +package com.codename1.guibuilder; + +import com.codename1.components.SpanLabel; +import com.codename1.components.SplitPane; +import com.codename1.components.ToastBar; +import com.codename1.guibuilder.model.GuiDocument; +import com.codename1.guibuilder.project.ProjectBinding; +import com.codename1.guibuilder.project.ProjectIO; +import com.codename1.guibuilder.ui.ComponentPreviewFactory; +import com.codename1.guibuilder.ui.DragGuideOverlay; +import com.codename1.guibuilder.ui.GuidedLayoutSupport; +import com.codename1.io.Log; +import com.codename1.io.Preferences; +import com.codename1.mcp.MCP; +import com.codename1.system.Lifecycle; +import com.codename1.ui.Button; +import com.codename1.ui.CheckBox; +import com.codename1.ui.CodeDiagnostic; +import com.codename1.ui.CodeEditor; +import com.codename1.ui.Component; +import com.codename1.ui.Container; +import com.codename1.ui.Display; +import com.codename1.ui.FontImage; +import com.codename1.ui.Form; +import com.codename1.ui.Font; +import com.codename1.ui.Label; +import com.codename1.ui.Tabs; +import com.codename1.ui.TextField; +import com.codename1.ui.TextArea; +import com.codename1.ui.Toolbar; +import com.codename1.ui.TooltipManager; +import com.codename1.ui.layouts.BorderLayout; +import com.codename1.ui.layouts.BoxLayout; +import com.codename1.ui.layouts.FlowLayout; +import com.codename1.ui.layouts.GridLayout; +import com.codename1.ui.layouts.LayeredLayout; +import com.codename1.ui.events.ActionEvent; +import com.codename1.ui.events.FocusListener; +import com.codename1.ui.events.PointerEvent; +import com.codename1.ui.geom.Dimension; +import com.codename1.ui.accessibility.AccessibilityGrouping; +import com.codename1.ui.accessibility.AccessibilityLiveRegion; +import com.codename1.ui.accessibility.AccessibilityRole; +import com.codename1.ui.plaf.Style; +import com.codename1.ui.plaf.UIManager; +import com.codename1.ui.css.CSSThemeCompiler; +import com.codename1.ui.spinner.Picker; +import com.codename1.ui.util.MutableResource; +import com.codename1.ui.util.Resources; +import com.codename1.xml.Element; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.Hashtable; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +public class CodenameOneGUIBuilder extends Lifecycle { + private static CodenameOneGUIBuilder active; + private ProjectBinding binding; + private List guiFiles = new ArrayList<>(); + private GuiDocument document; + private Form workspace; + private Container formsPanel; + private Container hierarchyPanel; + private Container canvasHost; + private Container canvasOverlayHost; + private Container inspectorHost; + private Label status; + private String paletteFilter = ""; + private String clipboardXml; + private boolean darkMode; + private String canvasMode = "phonePortrait"; + private Element dropGuideTarget; + private Hashtable projectTheme; + private Hashtable builderTheme; + private Component previewRoot; + private Component formToolbarPreview; + private Label formTitlePreview; + private DragGuideOverlay dragGuideOverlay; + private Container selectionActions; + private final LinkedHashSet selectedElements = new LinkedHashSet<>(); + private Component boxDropSpacer; + private Element boxDropTargetElement; + private Container boxDropParent; + private int boxDropIndex = -1; + private int inspectorTabIndex; + private boolean refreshPending; + private TextField inlineEditor; + private Component inlineEditorSource; + private Container inlineEditorParent; + private Element inlineEditorTarget; + private String inlineEditorAttribute; + private boolean finishingInlineEditor; + private DropPlan activeDropPlan; + private CodeEditor activeCodeEditor; + private String lastObservedCss; + private com.codename1.ui.util.UITimer cssLiveTimer; + private int cssEditRevision; + private Element designerDraggedElement; + private GuiDocument designerDragDocument; + private String designerPaletteType; + private Component designerDragSource; + private Runnable designerSuppressAction; + private int designerPressX; + private int designerPressY; + private int designerGrabOffsetX; + private int designerGrabOffsetY; + private boolean designerDragArmed; + private boolean designerDragActive; + private Element guidedResizeElement; + private Component guidedResizeSource; + private int guidedResizeEdges; + private int guidedResizePressX; + private int guidedResizePressY; + private int guidedResizeStartX; + private int guidedResizeStartY; + private int guidedResizeStartW; + private int guidedResizeStartH; + private boolean guidedResizeArmed; + private boolean guidedResizeActive; + private Component designerCursorComponent; + private ResizePlan activeResizePlan; + private GuiBuilderMcpController mcpController; + private String lastDragJournalSignature; + + @Override + public void init(Object context) { + super.init(context); + active = this; + Resources global = Resources.getGlobalResources(); + String[] themeNames = global == null ? new String[0] : global.getThemeResourceNames(); + builderTheme = themeNames.length == 0 ? new Hashtable() : global.getTheme(themeNames[0]); + Display.getInstance().setProperty("GUIBuilderDesignMode", "true"); + Display.getInstance().setDragStartPercentage(1); + binding = ProjectIO.loadBinding(); + String requestedDarkMode = System.getProperty("guibuilder.darkMode"); + darkMode = requestedDarkMode == null + ? Preferences.get("guibuilder.darkMode", Boolean.TRUE.equals(Display.getInstance().isDarkMode())) + : Boolean.parseBoolean(requestedDarkMode); + Display.getInstance().setDarkMode(Boolean.valueOf(darkMode)); + Log.bindCrashProtection(false); + } + + @Override + public void runApp() { + Toolbar.setGlobalToolbar(true); + TooltipManager.enableTooltips(); + if (binding == null) { + showUnboundState(); + return; + } + guiFiles = ProjectIO.findGuiFiles(binding.guiDir()); + String requestedCanvasMode = System.getProperty("guibuilder.canvasMode"); + if (requestedCanvasMode != null && requestedCanvasMode.length() > 0) canvasMode = requestedCanvasMode; + workspace = new Form("Codename One GUI Builder", new BorderLayout()) { + @Override + public void pointerHover(int[] x, int[] y) { + if (x != null && y != null && x.length > 0 && y.length > 0) { + updateDesignerHoverCursor(x[0], y[0]); + } + super.pointerHover(x, y); + } + }; + workspace.setName("GUIBuilderWorkspace"); + workspace.setEnableCursors(true); + workspace.getSemantics().setIdentifier("guibuilder.workspace") + .setPaneTitle("Codename One GUI Builder").setGrouping(AccessibilityGrouping.GROUP); + workspace.getToolbar().setTitleCentered(false); + installToolbar(workspace.getToolbar()); + workspace.add(BorderLayout.CENTER, buildWorkspace()); + status = new Label("Ready", "BuilderStatus"); + status.getSemantics().setIdentifier("guibuilder.status").setLabel("GUI Builder status") + .setLiveRegion(AccessibilityLiveRegion.POLITE); + workspace.add(BorderLayout.SOUTH, status); + workspace.show(); + installDragGuideOverlay(); + if (!guiFiles.isEmpty()) { + String initial = resolveInitialForm(); + openForm(initial == null ? guiFiles.get(0) : initial); + String initialSelection = System.getProperty("guibuilder.initialSelection"); + if (initialSelection != null && initialSelection.length() > 0) { + String[] names = initialSelection.split(","); + for (int i = 0; i < names.length; i++) { + Element element = findElementNamed(document, names[i].trim()); + if (element != null) selectElement(element, i > 0, "startup"); + } + } + if ("css".equals(System.getProperty("guibuilder.openEditor"))) { + Display.getInstance().callSerially(this::openCss); + } else if ("java".equals(System.getProperty("guibuilder.openEditor"))) { + Display.getInstance().callSerially(this::openCompanionSource); + } + } else { + showEmptyProject(); + } + installWorkspacePointerRouting(); + configureMcp(); + } + + private void configureMcp() { + mcpController = new GuiBuilderMcpController(this); + mcpController.register(); + recordAction("mcp_ready", "socketSupported", Boolean.valueOf(MCP.isSocketSupported())); + String requestedPort = System.getProperty("guibuilder.mcp.port"); + if (requestedPort == null || requestedPort.trim().length() == 0 || MCP.isRunning()) return; + try { + int port = Integer.parseInt(requestedPort.trim()); + if (port < 1 || port > 65535) throw new NumberFormatException("port out of range"); + MCP.startSocketServer(port); + recordAction("mcp_starting", "port", Integer.valueOf(port)); + setStatus("MCP starting on 127.0.0.1:" + port); + final int checkedPort = port; + new com.codename1.ui.util.UITimer(() -> { + if (MCP.isRunning()) { + recordAction("mcp_started", "port", Integer.valueOf(checkedPort)); + setStatus("Ready • MCP listening on 127.0.0.1:" + checkedPort); + } else { + recordAction("mcp_error", "message", "Unable to bind 127.0.0.1:" + checkedPort); + setStatus("MCP unavailable • port " + checkedPort + " is already in use"); + } + }).schedule(700, false, workspace); + } catch (Throwable ex) { + Log.e(ex); + recordAction("mcp_error", "message", ex.getMessage()); + setStatus("MCP unavailable • " + (ex.getMessage() == null ? ex.getClass().getSimpleName() : ex.getMessage())); + } + } + + /** Form initialization may transfer pointer listeners to an owning form. Install the + * designer's capture routing only after show(), when this is definitively the active form. */ + private void installWorkspacePointerRouting() { + workspace.addPointerPressedListener(e -> handleDesignerPointerPressed(e.getX(), e.getY(), additiveSelection(e))); + workspace.addPointerDraggedListener(e -> { + if (guidedResizeArmed) updateGuidedResize(e.getX(), e.getY()); + else updateDesignerDrag(e.getX(), e.getY()); + }); + workspace.addPointerReleasedListener(e -> { + if (guidedResizeArmed) finishGuidedResize(e.getX(), e.getY()); + else finishDesignerDrag(e.getX(), e.getY()); + }); + } + + void handleDesignerPointerPressed(int x, int y) { + handleDesignerPointerPressed(x, y, false); + } + + void handleDesignerPointerPressed(int x, int y, boolean additive) { + if (selectionActions != null && selectionActions.isVisible() + && x >= selectionActions.getAbsoluteX() && x <= selectionActions.getAbsoluteX() + selectionActions.getWidth() + && y >= selectionActions.getAbsoluteY() && y <= selectionActions.getAbsoluteY() + selectionActions.getHeight()) return; + TextField editor = inlineEditor; + if (editor != null && (x < editor.getAbsoluteX() || x > editor.getAbsoluteX() + editor.getWidth() + || y < editor.getAbsoluteY() || y > editor.getAbsoluteY() + editor.getHeight())) { + finishInlineEditor(); + } + Element hit = null; + if (inlineEditor == null && canvasHost != null + && x >= canvasHost.getAbsoluteX() && x <= canvasHost.getAbsoluteX() + canvasHost.getWidth() + && y >= canvasHost.getAbsoluteY() && y <= canvasHost.getAbsoluteY() + canvasHost.getHeight()) { + hit = elementAt(canvasHost, x, y); + } + boolean duplicateComponentPress = designerDragArmed && hit == designerDraggedElement + && Math.abs(x - designerPressX) + Math.abs(y - designerPressY) <= 2; + boolean duplicateResizePress = guidedResizeArmed && hit == guidedResizeElement + && Math.abs(x - guidedResizePressX) + Math.abs(y - guidedResizePressY) <= 2; + if (duplicateComponentPress || duplicateResizePress) return; + if (designerDragArmed || guidedResizeArmed) cancelDesignerDrag(); + if (hit != null && document != null && hit != document.root() + && (additive || !selectedElements.contains(hit) + || (selectedElements.size() > 1 && hit != document.selected()))) { + selectElement(hit, additive, "canvas"); + } + // Resize hit-testing must use the component under this press, never the previously + // selected component. Otherwise an ordinary click on an overlapping neighbor is stolen. + if (!additive && hit != null && document != null && selectedElements.contains(hit) + && beginGuidedResize(x, y)) return; + if (hit != null && document != null && hit != document.root() && selectedElements.contains(hit)) { + armDesignerDrag(hit, null, componentForElement(canvasHost, hit), x, y, null); + } + } + + private static boolean additiveSelection(ActionEvent event) { + PointerEvent pointer = event == null ? null : event.getPointerEvent(); + return pointer != null && (pointer.isShiftDown() || pointer.isControlDown() || pointer.isMetaDown()); + } + + private void showUnboundState() { + Form form = new Form("Codename One GUI Builder", new BorderLayout()); + Container empty = new Container(BoxLayout.y()); + empty.setUIID("BuilderWelcome"); + empty.add(new Label("GUI Builder", "BuilderWelcomeTitle")); + empty.add(new SpanLabel("Open the builder from a Codename One Maven project with mvn cn1:guibuilder.", "BuilderWelcomeCopy")); + form.add(BorderLayout.CENTER, BorderLayout.centerAbsolute(empty)); + form.show(); + } + + private Component buildWorkspace() { + formsPanel = new Container(BoxLayout.y()); + formsPanel.setUIID("BuilderSidebar"); + formsPanel.setScrollableY(true); + formsPanel.getSemantics().setIdentifier("guibuilder.forms").setLabel("Project forms") + .setRole(AccessibilityRole.LIST).setGrouping(AccessibilityGrouping.GROUP); + hierarchyPanel = new Container(BoxLayout.y()); + hierarchyPanel.setUIID("BuilderSidebar"); + hierarchyPanel.setScrollableY(true); + hierarchyPanel.getSemantics().setIdentifier("guibuilder.hierarchy").setLabel("Component hierarchy") + .setRole(AccessibilityRole.TREE).setGrouping(AccessibilityGrouping.GROUP); + canvasHost = new Container(new BorderLayout()); + canvasHost.setUIID("BuilderCanvasArea"); + canvasHost.getSemantics().setIdentifier("guibuilder.canvas").setLabel("Design canvas") + .setGrouping(AccessibilityGrouping.GROUP); + canvasOverlayHost = new Container(new LayeredLayout()); + canvasOverlayHost.setUIID("BuilderCanvasArea"); + canvasOverlayHost.add(canvasHost); + ((LayeredLayout) canvasOverlayHost.getLayout()).setInsets(canvasHost, "0 0 0 0"); + inspectorHost = new Container(new BorderLayout()); + inspectorHost.setUIID("BuilderInspector"); + inspectorHost.getSemantics().setIdentifier("guibuilder.inspector").setLabel("Inspector") + .setGrouping(AccessibilityGrouping.GROUP); + + Container left = new Container(new BorderLayout()); + left.setUIID("BuilderSidebar"); + left.add(BorderLayout.NORTH, sectionTitle("PROJECT FORMS")); + Container leftBody = new Container(new GridLayout(2, 1)); + leftBody.setUIID("BuilderSidebar"); + Tabs projectTabs = new Tabs(); + projectTabs.getSemantics().setIdentifier("guibuilder.projectTabs").setLabel("Project navigation"); + projectTabs.setSwipeActivated(false); + projectTabs.addTab("Forms", formsPanel); + projectTabs.addTab("Hierarchy", hierarchyPanel); + leftBody.add(projectTabs); + leftBody.add(buildPalette()); + left.add(BorderLayout.CENTER, leftBody); + refreshForms(); + + Container center = new Container(new BorderLayout()); + center.setUIID("BuilderCanvasArea"); + center.setScrollableX(true); + center.add(BorderLayout.NORTH, buildCanvasToolbar()); + center.add(BorderLayout.CENTER, canvasOverlayHost); + + Container right = new Container(new BorderLayout()); + right.setUIID("BuilderInspector"); + right.add(BorderLayout.NORTH, sectionTitle("INSPECTOR")); + right.add(BorderLayout.CENTER, inspectorHost); + + SplitPane centerAndInspector = new SplitPane(SplitPane.HORIZONTAL_SPLIT, center, right, + "45%", "72%", "90%"); + SplitPane workspaceSplit = new SplitPane(SplitPane.HORIZONTAL_SPLIT, left, centerAndInspector, + "10%", "20%", "40%"); + return workspaceSplit; + } + + private void installDragGuideOverlay() { + dragGuideOverlay = new DragGuideOverlay(); + canvasOverlayHost.add(dragGuideOverlay); + ((LayeredLayout) canvasOverlayHost.getLayout()).setInsets(dragGuideOverlay, "0 0 0 0"); + selectionActions = buildSelectionActions(); + selectionActions.setVisible(false); + canvasOverlayHost.add(selectionActions); + ((LayeredLayout) canvasOverlayHost.getLayout()).setInsets(selectionActions, "0 auto auto 0"); + canvasOverlayHost.revalidate(); + } + + private Container buildSelectionActions() { + Container palette = new Container(new GridLayout(1, 9)); + palette.setUIID("BuilderSelectionActions"); + palette.getSemantics().setIdentifier("guibuilder.selectionActions") + .setLabel("Selected component layout actions").setGrouping(AccessibilityGrouping.GROUP); + palette.add(selectionAction(FontImage.MATERIAL_ALIGN_HORIZONTAL_LEFT, + "Align left edges to the filled-handle reference component", "alignLeft")); + palette.add(selectionAction(FontImage.MATERIAL_ALIGN_HORIZONTAL_CENTER, + "Align horizontal centers to the filled-handle reference component", "alignHCenter")); + palette.add(selectionAction(FontImage.MATERIAL_ALIGN_HORIZONTAL_RIGHT, + "Align right edges to the filled-handle reference component", "alignRight")); + palette.add(selectionAction(FontImage.MATERIAL_ALIGN_VERTICAL_TOP, + "Align top edges to the filled-handle reference component", "alignTop")); + palette.add(selectionAction(FontImage.MATERIAL_TEXT_FIELDS, + "Align text baselines to the filled-handle reference component", "alignBaseline")); + palette.add(selectionAction(FontImage.MATERIAL_ALIGN_VERTICAL_BOTTOM, + "Align bottom edges to the filled-handle reference component", "alignBottom")); + palette.add(selectionAction(FontImage.MATERIAL_WIDTH_FULL, + "Make every selected component as wide as the filled-handle reference component", "matchWidth")); + palette.add(selectionAction(FontImage.MATERIAL_HEIGHT, + "Make every selected component as tall as the filled-handle reference component", "matchHeight")); + palette.add(selectionAction(FontImage.MATERIAL_LINK_OFF, + "Disconnect selected components from alignment and size relationships", "disconnect")); + return palette; + } + + private Button selectionAction(char icon, String tooltip, String action) { + Button button = new Button(""); + button.setUIID("BuilderSelectionAction"); + button.setTooltip(tooltip); + button.setCursor(Component.HAND_CURSOR); + button.setAlignment(Component.CENTER); + button.getAllStyles().setAlignment(Component.CENTER); + FontImage.setMaterialIcon(button, icon, 3.2f); + button.getSemantics().setIdentifier("guibuilder.selectionAction." + action).setLabel(tooltip); + button.addActionListener(e -> applySelectionAction(action)); + return button; + } + + private void installToolbar(Toolbar toolbar) { + toolbar.addMaterialCommandToLeftBar("Save", FontImage.MATERIAL_SAVE, e -> save()); + toolbar.addMaterialCommandToLeftBar("Undo", FontImage.MATERIAL_UNDO, e -> undo()); + toolbar.addMaterialCommandToLeftBar("Redo", FontImage.MATERIAL_REDO, e -> redo()); + toolbar.addMaterialCommandToLeftBar("Refresh", FontImage.MATERIAL_REFRESH, e -> refreshProject()); + toolbar.addMaterialCommandToRightBar("CSS", FontImage.MATERIAL_COLOR_LENS, e -> openCss()); + toolbar.addMaterialCommandToRightBar("Code", FontImage.MATERIAL_CODE, e -> openCompanionSource()); + } + + private Component buildCanvasToolbar() { + Container bar = new Container(new FlowLayout(Component.CENTER)); + bar.setUIID("BuilderCanvasToolbar"); + bar.add(iconButton(FontImage.MATERIAL_STAY_CURRENT_PORTRAIT, "Phone portrait", () -> setCanvasMode("phonePortrait"))); + bar.add(iconButton(FontImage.MATERIAL_STAY_CURRENT_LANDSCAPE, "Phone landscape", () -> setCanvasMode("phoneLandscape"))); + bar.add(iconButton(FontImage.MATERIAL_TABLET, "Tablet portrait", () -> setCanvasMode("tabletPortrait"))); + bar.add(iconButton(FontImage.MATERIAL_DESKTOP_MAC, "Desktop — full canvas", () -> setCanvasMode("desktop"))); + return bar; + } + + private Component buildPalette() { + Container palette = new Container(new BorderLayout()); + palette.setUIID("BuilderPalette"); + palette.getSemantics().setIdentifier("guibuilder.palette").setLabel("Component palette") + .setRole(AccessibilityRole.LIST).setGrouping(AccessibilityGrouping.GROUP); + TextField search = new TextField("", "Search components"); + search.setUIID("BuilderSearch"); + search.getSemantics().setIdentifier("guibuilder.palette.search").setLabel("Search components") + .setRole(AccessibilityRole.SEARCH_FIELD); + search.addDataChangedListener((type, index) -> { + paletteFilter = search.getText().toLowerCase(); + rebuildPalette(palette); + }); + palette.putClientProperty("search", search); + rebuildPalette(palette); + return palette; + } + + private void rebuildPalette(Container palette) { + TextField search = (TextField) palette.getClientProperty("search"); + palette.removeAll(); + Container heading = new Container(BoxLayout.y()); + heading.add(sectionTitle("COMPONENTS")); + heading.add(search); + palette.add(BorderLayout.NORTH, heading); + Container items = new Container(new GridLayout(5, 2)); + items.setScrollableY(true); + String[] types = {"Button", "Label", "SpanLabel", "TextField", "TextArea", "CheckBox", "RadioButton", "Slider", "Container", "Tabs"}; + char[] icons = {FontImage.MATERIAL_SMART_BUTTON, FontImage.MATERIAL_TEXT_FIELDS, FontImage.MATERIAL_SUBJECT, + FontImage.MATERIAL_EDIT, FontImage.MATERIAL_NOTES, FontImage.MATERIAL_CHECK_BOX, + FontImage.MATERIAL_RADIO_BUTTON_CHECKED, FontImage.MATERIAL_TUNE, FontImage.MATERIAL_VIEW_AGENDA, + FontImage.MATERIAL_TAB}; + for (int i = 0; i < types.length; i++) { + String type = types[i]; + if (paletteFilter.length() > 0 && !type.toLowerCase().contains(paletteFilter)) continue; + Button item = new Button(type, material(icons[i], "BuilderPaletteIcon")); + item.setUIID("BuilderPaletteItem"); + item.getSemantics().setIdentifier("guibuilder.palette." + type.toLowerCase()) + .setLabel("Add " + type).setHint("Activate to add, or drag onto the design canvas"); + final boolean[] suppressAction = new boolean[1]; + item.addActionListener(e -> { + if (suppressAction[0]) { + suppressAction[0] = false; + return; + } + addComponent(type); + }); + item.addPointerPressedListener(e -> armDesignerDrag(null, type, item, e.getX(), e.getY(), + () -> suppressAction[0] = true)); + items.add(item); + } + palette.add(BorderLayout.CENTER, items); + palette.revalidate(); + } + + private void refreshForms() { + formsPanel.removeAll(); + for (String path : guiFiles) { + String relative = relativeFormName(path); + String simple = relative.substring(relative.lastIndexOf('.') + 1); + Button form = new Button(simple, material(FontImage.MATERIAL_INSERT_DRIVE_FILE, "BuilderFormIcon")); + form.setTooltip(relative); + form.getSemantics().setIdentifier("guibuilder.form." + relative).setLabel(simple) + .setDescription(relative).setRole(AccessibilityRole.LIST_ITEM) + .setSelected(Boolean.valueOf(document != null && path.equals(document.path()))); + form.setUIID(document != null && path.equals(document.path()) ? "BuilderFormItemSelected" : "BuilderFormItem"); + form.addActionListener(e -> switchForm(path)); + formsPanel.add(form); + } + formsPanel.revalidate(); + } + + private void switchForm(String path) { + cancelDesignerDrag(); + if (document != null && document.isModified() && !com.codename1.ui.Dialog.show("Unsaved changes", "Save changes before switching forms?", "Save", "Discard")) { + openForm(path); + return; + } + if (document != null && document.isModified()) save(); + openForm(path); + } + + private void openForm(String path) { + try { + cancelDesignerDrag(); + document = GuiDocument.parse(path, ProjectIO.read(path)); + selectedElements.clear(); + recordAction("form_opened", "form", relativeFormName(path)); + loadProjectTheme(); + refreshForms(); + refreshEditor(); + setStatus("Editing " + relativeFormName(path)); + } catch (Exception ex) { + ToastBar.showErrorMessage("Unable to open GUI file: " + ex.getMessage()); + } + } + + private void refreshEditor() { + if (document == null) return; + TooltipManager.hideTooltip(); + normalizeSelection(); + cancelDesignerDrag(); + activeCodeEditor = null; + finishInlineEditor(); + canvasHost.removeAll(); + boolean desktop = "desktop".equals(canvasMode); + Container stage = new Container(desktop ? new BorderLayout() : new FlowLayout(Component.CENTER)); + stage.setUIID("BuilderStage"); + final int surfaceWidth = desktop ? -1 : "phoneLandscape".equals(canvasMode) ? 1200 + : "tabletPortrait".equals(canvasMode) ? 960 : 720; + final int surfaceHeight = desktop ? -1 : "phoneLandscape".equals(canvasMode) ? 720 + : "tabletPortrait".equals(canvasMode) ? 1280 : 1200; + Container device = new Container(new BorderLayout()) { + @Override protected Dimension calcPreferredSize() { + Dimension natural = super.calcPreferredSize(); + if (surfaceWidth > 0) natural.setWidth(surfaceWidth); + if (surfaceHeight > 0) natural.setHeight(surfaceHeight); + return natural; + } + }; + device.setUIID(desktop ? "BuilderDesktopSurface" : "BuilderDevice"); + formToolbarPreview = buildFormToolbarPreview(); + device.add(BorderLayout.NORTH, formToolbarPreview); + Component preview = ComponentPreviewFactory.create(document.root(), document.selected(), new ComponentPreviewFactory.SelectionHandler() { + @Override public void selected(Element element) { + selectElement(element, false, "preview"); + } + @Override public void selected(Element element, boolean additive) { + selectElement(element, additive, "preview"); + } + @Override public void dragPressed(Element element, Component source, int x, int y) { + if (!selectedElements.contains(element)) return; + if (selectedElements.size() <= 1 && !beginGuidedResize(x, y)) { + armDesignerDrag(element, null, source, x, y, null); + } else if (selectedElements.size() > 1) { + armDesignerDrag(element, null, source, x, y, null); + } + } + @Override public boolean isDragActive() { return designerDragActive; } + @Override public void dragMoved(int x, int y) { + if (guidedResizeArmed) updateGuidedResize(x, y); else updateDesignerDrag(x, y); + } + @Override public void dragReleased(int x, int y) { + if (guidedResizeArmed) finishGuidedResize(x, y); else finishDesignerDrag(x, y); + } + @Override public void editContent(Element element) { + selectElement(element, false, "inline-edit"); + editSelectedContent(); + } + }); + previewRoot = preview; + refreshProjectThemeOnPreview(); + device.add(BorderLayout.CENTER, preview); + if (desktop) stage.add(BorderLayout.CENTER, device); else stage.add(device); + canvasHost.add(BorderLayout.CENTER, stage); + refreshInspector(); + refreshHierarchy(); + canvasHost.revalidate(); + Display.getInstance().callSerially(this::refreshGuidedSelectionOverlay); + } + + private Component buildFormToolbarPreview() { + Container bar = new Container(new BorderLayout()); + bar.setUIID("BuilderFormToolbar"); + formTitlePreview = new Label(document.root().getAttribute("title") == null ? "Untitled Form" : document.root().getAttribute("title"), "Title"); + formTitlePreview.getSemantics().setIdentifier("guibuilder.preview.formTitle") + .setLabel("Form title").setHint("Double click to edit the form title"); + formTitlePreview.addLongPressListener(e -> { document.select(document.root()); editSelectedContent(); }); + formTitlePreview.addPointerReleasedListener(e -> { + long now = System.currentTimeMillis(); + Object previous = formTitlePreview.getClientProperty("gui.lastClick"); + formTitlePreview.putClientProperty("gui.lastClick", Long.valueOf(now)); + if (previous instanceof Long time && now - time.longValue() < 450) { + document.select(document.root()); + editSelectedContent(); + } + }); + bar.add(BorderLayout.CENTER, formTitlePreview); + Container left = new Container(BoxLayout.x()); + Container right = new Container(BoxLayout.x()); + for (Element command : document.commands()) { + Button button = new Button(value(command, "name", "Command")); + button.setUIID("TitleCommand"); + if ("left".equals(value(command, "placement", "right"))) left.add(button); else right.add(button); + } + if (left.getComponentCount() > 0) bar.add(BorderLayout.WEST, left); + if (right.getComponentCount() > 0) bar.add(BorderLayout.EAST, right); + return bar; + } + + private void refreshHierarchy() { + hierarchyPanel.removeAll(); + if (document != null) addHierarchyRow(document.root(), 0); + hierarchyPanel.revalidate(); + } + + private void addHierarchyRow(Element element, int depth) { + String type = element.getAttribute("type"); + String name = element.getAttribute("name"); + Button row = new Button((name == null ? type : name) + " · " + type, + material(GuiDocument.acceptsChildren(element) ? FontImage.MATERIAL_FOLDER_OPEN : FontImage.MATERIAL_DRAG_HANDLE, + "BuilderFormIcon")); + row.setUIID(selectedElements.contains(element) ? "BuilderHierarchySelected" : "BuilderHierarchyItem"); + row.getSemantics().setIdentifier("guibuilder.hierarchy." + value(element, "name", type)) + .setLabel((name == null ? type : name) + ", " + type) + .setRole(AccessibilityRole.TREE_ITEM) + .setSelected(Boolean.valueOf(selectedElements.contains(element))); + row.getAllStyles().setPaddingLeft(depth * 3 + 1); + final boolean[] suppressAction = new boolean[1]; + row.addActionListener(e -> { + if (suppressAction[0]) { + suppressAction[0] = false; + return; + } + selectElement(element, false, "hierarchy"); + refreshEditor(); + }); + if (element != document.root()) { + row.addPointerPressedListener(e -> armHierarchyDrag(element, + componentForElement(canvasHost, element), e.getX(), e.getY(), () -> suppressAction[0] = true)); + } + row.putClientProperty("gui.element", element); + hierarchyPanel.add(row); + for (int i = 0; i < element.getNumChildren(); i++) { + Object child = element.getChildAt(i); + if (child instanceof Element childElement && "component".equals(childElement.getTagName())) { + addHierarchyRow(childElement, depth + 1); + } + } + } + + private boolean dragThresholdReached(int startX, int startY, int x, int y) { + return Math.abs(x - startX) + Math.abs(y - startY) >= 6; + } + + private void armDesignerDrag(Element element, String paletteType, Component source, int x, int y, + Runnable suppressAction) { + if (document == null || (element != null && !document.containsElement(element))) { + cancelDesignerDrag(); + return; + } + designerDraggedElement = element; + designerDragDocument = document; + designerPaletteType = paletteType; + designerDragSource = source; + designerSuppressAction = suppressAction; + designerPressX = x; + designerPressY = y; + designerGrabOffsetX = element == null || source == null ? 0 : Math.max(0, x - source.getAbsoluteX()); + designerGrabOffsetY = element == null || source == null ? 0 : Math.max(0, y - source.getAbsoluteY()); + designerDragArmed = true; + designerDragActive = false; + lastDragJournalSignature = null; + recordAction("drag_armed", "component", element == null ? null : value(element, "name", "component"), + "paletteType", paletteType, "x", Integer.valueOf(x), "y", Integer.valueOf(y)); + } + + private void armHierarchyDrag(Element element, Component preview, int x, int y, Runnable suppressAction) { + armDesignerDrag(element, null, preview, x, y, suppressAction); + // The press occurs in the tree row, not inside the preview component. Using the tree's + // coordinates as a grab offset can place the guide hundreds of pixels away. + designerGrabOffsetX = 0; + designerGrabOffsetY = 0; + } + + private boolean beginGuidedResize(int x, int y) { + ResizeHit hit = resizeHitAt(x, y); + if (hit == null) return false; + Element element = hit.element; + Component source = hit.component; + int edges = hit.edges; + if (element != document.selected()) { + document.select(element); + refreshGuidedSelectionOverlay(); + } + cancelDesignerDrag(); + guidedResizeElement = element; + guidedResizeSource = source; + guidedResizeEdges = edges; + guidedResizePressX = x; + guidedResizePressY = y; + guidedResizeStartX = source.getAbsoluteX(); + guidedResizeStartY = source.getAbsoluteY(); + guidedResizeStartW = source.getWidth(); + guidedResizeStartH = source.getHeight(); + guidedResizeArmed = true; + guidedResizeActive = false; + return true; + } + + private ResizeHit resizeHitAt(int x, int y) { + if (document == null || canvasHost == null || selectedElements.isEmpty()) return null; + int slop = Math.max(10, Display.getInstance().convertToPixels(1.6f)); + Element primary = document.selected(); + ResizeHit primaryHit = resizeHit(primary, x, y, slop); + if (primaryHit != null) return primaryHit; + for (Element element : selectedElements) { + if (element == primary) continue; + ResizeHit hit = resizeHit(element, x, y, slop); + if (hit != null) return hit; + } + return null; + } + + private ResizeHit resizeHit(Element element, int x, int y, int slop) { + if (element == null || element == document.root() || !selectedElements.contains(element) + || !"LayeredLayout".equals(document.parentLayout(element))) return null; + Component source = componentForElement(canvasHost, element); + if (source == null || source.getWidth() < 1 || source.getHeight() < 1) return null; + int leftDistance = Math.abs(x - source.getAbsoluteX()); + int rightDistance = Math.abs(x - source.getAbsoluteX() - source.getWidth()); + int topDistance = Math.abs(y - source.getAbsoluteY()); + int bottomDistance = Math.abs(y - source.getAbsoluteY() - source.getHeight()); + boolean withinX = x >= source.getAbsoluteX() - slop && x <= source.getAbsoluteX() + source.getWidth() + slop; + boolean withinY = y >= source.getAbsoluteY() - slop && y <= source.getAbsoluteY() + source.getHeight() + slop; + int edges = 0; + if (withinY && leftDistance <= slop && leftDistance + 1 < rightDistance) edges |= 1; + else if (withinY && rightDistance <= slop && rightDistance + 1 < leftDistance) edges |= 2; + if (withinX && topDistance <= slop && topDistance + 1 < bottomDistance) edges |= 4; + else if (withinX && bottomDistance <= slop && bottomDistance + 1 < topDistance) edges |= 8; + return edges == 0 ? null : new ResizeHit(element, source, edges); + } + + private void updateDesignerHoverCursor(int x, int y) { + if (workspace == null) return; + Component hovered = workspace.getComponentAt(x, y); + int cursor = designerResizeCursorAt(x, y); + if (designerCursorComponent != null && designerCursorComponent != hovered) { + designerCursorComponent.setCursor(Component.DEFAULT_CURSOR); + } + if (hovered != null) hovered.setCursor(cursor); + designerCursorComponent = cursor == Component.DEFAULT_CURSOR ? null : hovered; + } + + int designerResizeCursorAt(int x, int y) { + ResizeHit hit = resizeHitAt(x, y); + return hit == null ? Component.DEFAULT_CURSOR : resizeCursor(hit.edges); + } + + private int resizeCursor(int edges) { + if ((edges & 4) != 0 && (edges & 1) != 0) return Component.NW_RESIZE_CURSOR; + if ((edges & 4) != 0 && (edges & 2) != 0) return Component.NE_RESIZE_CURSOR; + if ((edges & 8) != 0 && (edges & 1) != 0) return Component.SW_RESIZE_CURSOR; + if ((edges & 8) != 0 && (edges & 2) != 0) return Component.SE_RESIZE_CURSOR; + if ((edges & 1) != 0) return Component.W_RESIZE_CURSOR; + if ((edges & 2) != 0) return Component.E_RESIZE_CURSOR; + if ((edges & 4) != 0) return Component.N_RESIZE_CURSOR; + if ((edges & 8) != 0) return Component.S_RESIZE_CURSOR; + return Component.DEFAULT_CURSOR; + } + + private void updateGuidedResize(int x, int y) { + if (!guidedResizeArmed) return; + if (!guidedResizeActive && !dragThresholdReached(guidedResizePressX, guidedResizePressY, x, y)) return; + guidedResizeActive = true; + activeResizePlan = planGuidedResize(x, y); + if (activeResizePlan != null && dragGuideOverlay != null) { + dragGuideOverlay.showResize((Container) guidedResizeSource.getParent(), guidedResizeSource, + activeResizePlan.x, activeResizePlan.y, activeResizePlan.width, activeResizePlan.height, + activeResizePlan.description); + showGuidedResizeSimulation(guidedResizeElement, (Container) guidedResizeSource.getParent(), + guidedResizeSource, activeResizePlan, guidedResizeEdges); + setStatus(activeResizePlan.description); + } + } + + private ResizePlan planGuidedResize(int pointerX, int pointerY) { + if (guidedResizeSource == null || guidedResizeSource.getParent() == null) return null; + Container parent = guidedResizeSource.getParent(); + int dx = pointerX - guidedResizePressX; + int dy = pointerY - guidedResizePressY; + int left = guidedResizeStartX; + int right = guidedResizeStartX + guidedResizeStartW; + int top = guidedResizeStartY; + int bottom = guidedResizeStartY + guidedResizeStartH; + if ((guidedResizeEdges & 1) != 0) left += dx; + if ((guidedResizeEdges & 2) != 0) right += dx; + if ((guidedResizeEdges & 4) != 0) top += dy; + if ((guidedResizeEdges & 8) != 0) bottom += dy; + int minWidth = Math.max(16, Display.getInstance().convertToPixels(2)); + int minHeight = Math.max(16, Display.getInstance().convertToPixels(2)); + if (right - left < minWidth) { + if ((guidedResizeEdges & 1) != 0) left = right - minWidth; else right = left + minWidth; + } + if (bottom - top < minHeight) { + if ((guidedResizeEdges & 4) != 0) top = bottom - minHeight; else bottom = top + minHeight; + } + ResizePlan plan = new ResizePlan(left, top, right - left, bottom - top); + int threshold = Math.max(8, Display.getInstance().convertToPixels(1.5f)); + int bestHorizontal = threshold + 1; + int bestVertical = threshold + 1; + for (int i = 0; i < parent.getComponentCount(); i++) { + Component sibling = parent.getComponentAt(i); + if (sibling == guidedResizeSource || sibling.getClientProperty("gui.element") == null) continue; + Element siblingElement = (Element) sibling.getClientProperty("gui.element"); + if ((guidedResizeEdges & 2) != 0) { + int distance = Math.abs((plan.x + plan.width) - (plan.x + sibling.getWidth())); + if (distance < bestHorizontal) { + plan.width = sibling.getWidth(); plan.matchWidth = siblingElement; bestHorizontal = distance; + plan.horizontalDescription = "same width as " + value(siblingElement, "name", "component"); + } + } else if ((guidedResizeEdges & 1) != 0) { + int candidate = plan.x + plan.width - sibling.getWidth(); + int distance = Math.abs(plan.x - candidate); + if (distance < bestHorizontal) { + plan.x = candidate; plan.width = sibling.getWidth(); plan.matchWidth = siblingElement; bestHorizontal = distance; + plan.horizontalDescription = "same width as " + value(siblingElement, "name", "component"); + } + } + if ((guidedResizeEdges & 8) != 0) { + int distance = Math.abs((plan.y + plan.height) - (plan.y + sibling.getHeight())); + if (distance < bestVertical) { + plan.height = sibling.getHeight(); plan.matchHeight = siblingElement; bestVertical = distance; + plan.verticalDescription = "same height as " + value(siblingElement, "name", "component"); + } + } else if ((guidedResizeEdges & 4) != 0) { + int candidate = plan.y + plan.height - sibling.getHeight(); + int distance = Math.abs(plan.y - candidate); + if (distance < bestVertical) { + plan.y = candidate; plan.height = sibling.getHeight(); plan.matchHeight = siblingElement; bestVertical = distance; + plan.verticalDescription = "same height as " + value(siblingElement, "name", "component"); + } + } + } + int parentLeft = contentX(parent); + int parentTop = contentY(parent); + int parentRight = parentLeft + contentWidth(parent); + int parentBottom = parentTop + contentHeight(parent); + if ((guidedResizeEdges & 1) != 0 && Math.abs(plan.x - parentLeft) <= threshold) { + plan.width += plan.x - parentLeft; plan.x = parentLeft; plan.matchWidth = null; plan.horizontalDescription = "dock left"; + } + if ((guidedResizeEdges & 2) != 0 && Math.abs(plan.x + plan.width - parentRight) <= threshold) { + plan.width = parentRight - plan.x; plan.matchWidth = null; plan.horizontalDescription = "dock right"; + } + if ((guidedResizeEdges & 4) != 0 && Math.abs(plan.y - parentTop) <= threshold) { + plan.height += plan.y - parentTop; plan.y = parentTop; plan.matchHeight = null; plan.verticalDescription = "dock top"; + } + if ((guidedResizeEdges & 8) != 0 && Math.abs(plan.y + plan.height - parentBottom) <= threshold) { + plan.height = parentBottom - plan.y; plan.matchHeight = null; plan.verticalDescription = "dock bottom"; + } + plan.description = plan.horizontalDescription == null ? plan.verticalDescription + : plan.verticalDescription == null ? plan.horizontalDescription + : plan.horizontalDescription + " • " + plan.verticalDescription; + if (plan.description == null) plan.description = plan.width + " × " + plan.height; + return plan; + } + + private void finishGuidedResize(int x, int y) { + if (!guidedResizeArmed) return; + boolean active = guidedResizeActive; + ResizePlan plan = active ? planGuidedResize(x, y) : null; + Element element = guidedResizeElement; + Component source = guidedResizeSource; + int edges = guidedResizeEdges; + guidedResizeArmed = guidedResizeActive = false; + guidedResizeElement = null; + guidedResizeSource = null; + activeResizePlan = null; + if (!active || plan == null || element == null || !document.containsElement(element) + || source == null || source.getParent() == null) { + refreshGuidedSelectionOverlay(); + return; + } + Container parent = source.getParent(); + commitGuidedSelectionResize(element, parent, plan, edges); + refreshEditor(); + setStatus(selectedElements.size() > 1 + ? "Resized " + selectedElements.size() + " components using reference " + + value(element, "name", "component") + " at " + plan.width + " × " + plan.height + : "Resized " + value(element, "name", "component") + " to " + plan.width + " × " + plan.height); + } + + void commitGuidedResize(Element element, Container parent, ResizePlan plan, int edges) { + Component preview = componentForElement(canvasHost, element); + commitGuidedResize(document, element, parent, preview, plan, edges); + } + + void commitGuidedSelectionResize(Element element, Container parent, ResizePlan plan, int edges) { + boolean group = selectedElements.size() > 1 && selectedElements.contains(element) + && selectedElementsShareGuidedParent(); + ResizePlan referencePlan = group ? fixedReferenceResizePlan(plan, edges) : plan; + document.beginTransaction(); + try { + commitGuidedResize(document, element, parent, componentForElement(canvasHost, element), referencePlan, edges); + if (group) { + for (Element selected : new ArrayList(selectedElements)) { + if (selected == element) continue; + Component preview = componentForElement(canvasHost, selected); + if ((edges & 3) != 0) { + applyLayeredRelationship(document, selected, element, "matchWidth", preview); + } + if ((edges & 12) != 0) { + applyLayeredRelationship(document, selected, element, "matchHeight", preview); + } + } + } + document.select(element); + } finally { + document.endTransaction(); + } + } + + private ResizePlan fixedReferenceResizePlan(ResizePlan plan, int edges) { + ResizePlan fixed = new ResizePlan(plan.x, plan.y, plan.width, plan.height); + fixed.horizontalDescription = plan.horizontalDescription; + fixed.verticalDescription = plan.verticalDescription; + fixed.description = plan.description; + fixed.matchWidth = (edges & 3) == 0 ? plan.matchWidth : null; + fixed.matchHeight = (edges & 12) == 0 ? plan.matchHeight : null; + return fixed; + } + + private void commitGuidedResize(GuiDocument targetDocument, Element element, Container parent, + Component preview, ResizePlan plan, int edges) { + targetDocument.select(element); + int marginLeft = preview == null ? 0 : preview.getStyle().getMarginLeftNoRTL(); + int marginTop = preview == null ? 0 : preview.getStyle().getMarginTop(); + String[] insets = GuidedLayoutSupport.insetValues(element); + String[] refs = GuidedLayoutSupport.referenceNames(element); + String[] positions = GuidedLayoutSupport.referencePositions(element); + targetDocument.beginTransaction(); + try { + if ((edges & 3) != 0) { + String explicitMatch = value(element, "guidedMatchWidth", ""); + boolean keepExplicitMatch = GuidedLayoutSupport.MATCH.equals(GuidedLayoutSupport.horizontalPolicy(element)) + && plan.matchWidth != null && explicitMatch.equals(value(plan.matchWidth, "name", "")); + if (keepExplicitMatch) { + applyMatchedWidth(plan.matchWidth, insets, refs, positions, plan.x, plan.width); + targetDocument.setAttribute("guidedMatchWidth", explicitMatch); + targetDocument.setAttribute("guidedHorizontalSize", GuidedLayoutSupport.MATCH); + } else { + insets[3] = Math.max(0, plan.x - contentX(parent) - marginLeft) + "px"; + insets[1] = "auto"; refs[3] = refs[1] = "-"; positions[3] = positions[1] = "0"; + targetDocument.setAttribute("guidedPreferredWidth", String.valueOf(plan.width)); + targetDocument.setAttribute("guidedHorizontalSize", GuidedLayoutSupport.FIXED); + targetDocument.setAttribute("guidedMatchWidth", null); + } + targetDocument.setAttribute("guidedHorizontalAnchor", null); + } + if ((edges & 12) != 0) { + String explicitMatch = value(element, "guidedMatchHeight", ""); + boolean keepExplicitMatch = GuidedLayoutSupport.MATCH.equals(GuidedLayoutSupport.verticalPolicy(element)) + && plan.matchHeight != null && explicitMatch.equals(value(plan.matchHeight, "name", "")); + if (keepExplicitMatch) { + applyMatchedHeight(plan.matchHeight, insets, refs, positions, plan.y, plan.height); + targetDocument.setAttribute("guidedMatchHeight", explicitMatch); + targetDocument.setAttribute("guidedVerticalSize", GuidedLayoutSupport.MATCH); + } else { + insets[0] = Math.max(0, plan.y - contentY(parent) - marginTop) + "px"; + insets[2] = "auto"; refs[0] = refs[2] = "-"; positions[0] = positions[2] = "0"; + targetDocument.setAttribute("guidedPreferredHeight", String.valueOf(plan.height)); + targetDocument.setAttribute("guidedVerticalSize", GuidedLayoutSupport.FIXED); + targetDocument.setAttribute("guidedMatchHeight", null); + } + targetDocument.setAttribute("guidedVerticalAnchor", null); + } + targetDocument.setAttribute("layeredInsets", GuidedLayoutSupport.joinInsets(insets[0], insets[1], insets[2], insets[3])); + targetDocument.setAttribute("guidedReferences", GuidedLayoutSupport.joinReferences(refs[0], refs[1], refs[2], refs[3])); + targetDocument.setAttribute("guidedReferencePositions", GuidedLayoutSupport.joinPositions(positions[0], positions[1], positions[2], positions[3])); + } finally { + targetDocument.endTransaction(); + } + } + + private void updateDesignerDrag(int x, int y) { + if (!designerDragArmed) return; + if (!designerDragActive && dragThresholdReached(designerPressX, designerPressY, x, y)) { + designerDragActive = true; + recordAction("drag_started", "component", designerDraggedElement == null ? null + : value(designerDraggedElement, "name", "component"), "paletteType", designerPaletteType); + if (designerSuppressAction != null) designerSuppressAction.run(); + finishInlineEditor(); + hideDropGuide(); + } + if (designerDragActive) { + autoScrollDuringDrag(x, y); + showDropGuide(designerDraggedElement, activePreviewElementAt(x, y), designerDragSource, x, y); + } + } + + private void finishDesignerDrag(int x, int y) { + if (!designerDragArmed) { + if (activeDropPlan != null || boxDropSpacer != null) hideDropGuide(); + return; + } + Element element = designerDraggedElement; + GuiDocument dragDocument = designerDragDocument; + String paletteType = designerPaletteType; + boolean activeDrag = designerDragActive; + Component dragSource = designerDragSource; + DropPlan releasePlan = activeDrag && element != null && dragDocument == document + ? planDrop(element, designerDropTargetAt(element, x, y), dragSource, x, y) : null; + designerDraggedElement = null; + designerDragDocument = null; + designerPaletteType = null; + designerDragSource = null; + designerSuppressAction = null; + designerDragArmed = false; + designerDragActive = false; + if (!activeDrag) { + recordAction("drag_cancelled", "reason", "released_before_threshold"); + hideDropGuide(); + return; + } + recordAction("drag_released", "component", element == null ? null : value(element, "name", "component"), + "x", Integer.valueOf(x), "y", Integer.valueOf(y)); + if (dragDocument != document || (element != null && !document.containsElement(element))) { + hideDropGuide(); + setStatus("Drop cancelled because the active form changed"); + return; + } + if (paletteType != null) addComponentAt(paletteType, x, y); + else if (element != null) handleComponentDrop(element, releasePlan, x, y); + } + + private void handleComponentDrop(Element dragged, DropPlan releasePlan, int x, int y) { + try { + if (!document.containsElement(dragged)) { + setStatus("Drop cancelled — component is not part of the active form"); + return; + } + DropPlan plan = releasePlan; + if (plan == null || !plan.valid) { + recordAction("drop_rejected", "component", value(dragged, "name", "component"), + "reason", plan == null ? "outside_form" : plan.message); + setStatus(plan == null ? "Drop outside the form — no changes made" : plan.message); + return; + } + boolean grouped = selectedElements.size() > 1 && selectedElements.contains(dragged) + && applyGroupedGuidedDrop(dragged, plan); + if (grouped || applyDropPlan(dragged, plan, x, y)) { + recordAction("drop_committed", "component", value(dragged, "name", "component"), + "groupCount", Integer.valueOf(grouped ? selectedElements.size() : 1), + "layout", plan.layout, "constraint", plan.constraint, "target", + value(plan.target, "name", value(plan.target, "type", "component")), + "snap", plan.snapDescription, + "horizontalKind", plan.horizontalSnap == null ? null : plan.horizontalSnap.kind, + "horizontalReference", snapReferenceName(plan.horizontalSnap), + "verticalKind", plan.verticalSnap == null ? null : plan.verticalSnap.kind, + "verticalReference", snapReferenceName(plan.verticalSnap)); + setStatus(grouped ? "Moved " + selectedElements.size() + " components as a group" + : "Moved " + value(dragged, "name", value(dragged, "type", "component")) + + (plan.constraint == null ? "" : " to " + plan.constraint)); + scheduleDesignerRefresh(); + } + } catch (Throwable ex) { + Log.e(ex); + recordAction("drop_error", "component", value(dragged, "name", "component"), "message", ex.getMessage()); + setStatus("Drop failed safely — " + (ex.getMessage() == null ? ex.getClass().getSimpleName() : ex.getMessage())); + ToastBar.showErrorMessage("Unable to complete drop; the form was left editable"); + } finally { + hideDropGuide(); + Component preview = componentForElement(canvasHost, dragged); + if (preview != null) preview.setVisible(true); + if (workspace != null) workspace.revalidate(); + } + } + + boolean applyGroupedGuidedDrop(Element dragged, DropPlan plan) { + if (document == null || plan == null || !plan.valid || !"LayeredLayout".equals(plan.layout) + || !selectedElementsShareGuidedParent()) return false; + GroupMoveGeometry geometry = groupedMoveGeometry(dragged, plan); + if (geometry == null) return false; + Element primary = document.selected(); + Set selectedNames = new LinkedHashSet<>(); + for (Element element : selectedElements) selectedNames.add(value(element, "name", "")); + document.beginTransaction(); + try { + // Components outside the group must not be pulled along by a selected anchor. Rebase + // them at their current rectangle before moving the group, while retaining every + // relationship whose two endpoints are both selected. + for (Element selected : selectedElements) { + String selectedName = value(selected, "name", ""); + for (Element candidate : document.components()) { + String candidateName = value(candidate, "name", ""); + if (selectedNames.contains(candidateName)) continue; + if (incomingReferenceNames(document, candidateName).contains(selectedName)) { + freezeDependencyAtCurrentBounds(document, candidateName, selectedName); + } + } + } + for (Map.Entry entry : geometry.previews.entrySet()) { + Component preview = entry.getValue(); + translateGuidedGroupElement(entry.getKey(), geometry.parent, preview, + geometry.dx, geometry.dy, selectedNames); + } + document.select(primary); + } finally { + document.endTransaction(); + } + return true; + } + + private GroupMoveGeometry groupedMoveGeometry(Element dragged, DropPlan plan) { + if (document == null || canvasHost == null || dragged == null || plan == null + || !plan.valid || !"LayeredLayout".equals(plan.layout) + || !selectedElements.contains(dragged) || !selectedElementsShareGuidedParent()) return null; + Element parentElement = document.parentOf(dragged); + if (parentElement != plan.parent) return null; + Component parentPreview = componentForElement(canvasHost, parentElement); + Component draggedPreview = componentForElement(canvasHost, dragged); + if (!(parentPreview instanceof Container parent) || draggedPreview == null) return null; + Map previews = new LinkedHashMap<>(); + int minX = Integer.MAX_VALUE, minY = Integer.MAX_VALUE; + int maxX = Integer.MIN_VALUE, maxY = Integer.MIN_VALUE; + for (Element element : selectedElements) { + if (document.parentOf(element) != parentElement) return null; + Component preview = componentForElement(canvasHost, element); + if (preview == null) return null; + previews.put(element, preview); + minX = Math.min(minX, preview.getAbsoluteX()); + minY = Math.min(minY, preview.getAbsoluteY()); + maxX = Math.max(maxX, preview.getAbsoluteX() + preview.getWidth()); + maxY = Math.max(maxY, preview.getAbsoluteY() + preview.getHeight()); + } + int dx = plan.snapX - draggedPreview.getAbsoluteX(); + int dy = plan.snapY - draggedPreview.getAbsoluteY(); + int contentLeft = contentX(parent); + int contentTop = contentY(parent); + int contentRight = contentLeft + contentWidth(parent); + int contentBottom = contentTop + contentHeight(parent); + dx = Math.max(contentLeft - minX, Math.min(dx, contentRight - maxX)); + dy = Math.max(contentTop - minY, Math.min(dy, contentBottom - maxY)); + return new GroupMoveGeometry(parent, previews, dx, dy); + } + + GuidedSimulation simulateGroupedGuidedDrop(Element dragged, DropPlan plan) { + GroupMoveGeometry geometry = groupedMoveGeometry(dragged, plan); + if (geometry == null || geometry.previews.size() < 2) return null; + List items = new ArrayList<>(); + Set changed = new LinkedHashSet<>(); + String activeName = value(dragged, "name", "component"); + for (Map.Entry entry : geometry.previews.entrySet()) { + Component preview = entry.getValue(); + String name = value(entry.getKey(), "name", value(entry.getKey(), "type", "component")); + changed.add(name); + items.add(new DragGuideOverlay.GlassItem(name, + preview.getAbsoluteX(), preview.getAbsoluteY(), preview.getWidth(), preview.getHeight(), + preview.getAbsoluteX() + geometry.dx, preview.getAbsoluteY() + geometry.dy, + preview.getWidth(), preview.getHeight(), name.equals(activeName))); + } + return new GuidedSimulation(document, items, new ArrayList<>(), + "Preview: move " + items.size() + " components as a group", changed); + } + + private void translateGuidedGroupElement(Element element, Container parent, Component component, + int dx, int dy, Set selectedNames) { + String[] insets = GuidedLayoutSupport.insetValues(element); + String[] refs = GuidedLayoutSupport.referenceNames(element); + String[] positions = GuidedLayoutSupport.referencePositions(element); + boolean leftInternal = selectedNames.contains(refs[3]); + boolean rightInternal = selectedNames.contains(refs[1]); + boolean topInternal = selectedNames.contains(refs[0]); + boolean bottomInternal = selectedNames.contains(refs[2]); + document.select(element); + + if (!leftInternal && !rightInternal) { + insets[3] = Math.max(0, component.getAbsoluteX() + dx - contentX(parent) + - component.getStyle().getMarginLeftNoRTL()) + "px"; + insets[1] = "auto"; + refs[3] = refs[1] = "-"; + positions[3] = positions[1] = "0"; + document.setAttribute("guidedHorizontalAnchor", null); + } else { + if (!leftInternal) { insets[3] = "auto"; refs[3] = "-"; positions[3] = "0"; } + if (!rightInternal) { insets[1] = "auto"; refs[1] = "-"; positions[1] = "0"; } + } + if (!topInternal && !bottomInternal) { + insets[0] = Math.max(0, component.getAbsoluteY() + dy - contentY(parent) + - component.getStyle().getMarginTop()) + "px"; + insets[2] = "auto"; + refs[0] = refs[2] = "-"; + positions[0] = positions[2] = "0"; + document.setAttribute("guidedVerticalAnchor", null); + } else { + if (!topInternal) { insets[0] = "auto"; refs[0] = "-"; positions[0] = "0"; } + if (!bottomInternal) { insets[2] = "auto"; refs[2] = "-"; positions[2] = "0"; } + } + + // Fill needs opposing constraints. If a group move removes one external edge, preserve + // the rendered dimension instead of allowing the component to resize unexpectedly. + if (GuidedLayoutSupport.FILL.equals(GuidedLayoutSupport.horizontalPolicy(element)) + && !(leftInternal && rightInternal)) { + document.setAttribute("guidedHorizontalSize", GuidedLayoutSupport.FIXED); + document.setAttribute("guidedPreferredWidth", String.valueOf(Math.max(1, component.getWidth()))); + } + if (GuidedLayoutSupport.FILL.equals(GuidedLayoutSupport.verticalPolicy(element)) + && !(topInternal && bottomInternal)) { + document.setAttribute("guidedVerticalSize", GuidedLayoutSupport.FIXED); + document.setAttribute("guidedPreferredHeight", String.valueOf(Math.max(1, component.getHeight()))); + } + document.setAttribute("layeredInsets", GuidedLayoutSupport.joinInsets(insets[0], insets[1], insets[2], insets[3])); + document.setAttribute("guidedReferences", GuidedLayoutSupport.joinReferences(refs[0], refs[1], refs[2], refs[3])); + document.setAttribute("guidedReferencePositions", GuidedLayoutSupport.joinPositions( + positions[0], positions[1], positions[2], positions[3])); + } + + private void addComponentAt(String type, int x, int y) { + Element previous = document.selected(); + Element added = null; + try { + added = document.addComponent(type); + Element target = activePreviewElementAt(x, y); + Component palettePreview = ComponentPreviewFactory.create(added, null, new ComponentPreviewFactory.SelectionHandler() { + @Override public void selected(Element element) { } + @Override public void dragPressed(Element element, Component source, int px, int py) { } + @Override public boolean isDragActive() { return false; } + @Override public void editContent(Element element) { } + }); + DropPlan plan = planDrop(added, target, palettePreview, x, y); + if (plan == null || !plan.valid) { + document.select(added); + document.deleteSelected(); + document.select(previous); + setStatus(plan == null ? "Drop inside the form to add " + type : plan.message); + return; + } + if (applyDropPlan(added, plan, x, y)) { + setStatus("Added " + type + (plan.constraint == null ? "" : " to " + plan.constraint)); + scheduleDesignerRefresh(); + } + } catch (Throwable ex) { + Log.e(ex); + if (added != null && document.parentOf(added) != null) { + document.select(added); + document.deleteSelected(); + document.select(previous); + } + setStatus("Drop failed safely — " + (ex.getMessage() == null ? ex.getClass().getSimpleName() : ex.getMessage())); + } finally { + hideDropGuide(); + if (workspace != null) workspace.revalidate(); + } + } + + private boolean dropAfter(Element target, String layout, int x, int y) { + if (target == null || GuiDocument.acceptsChildren(target)) return false; + Component preview = componentForElement(canvasHost, target); + if (preview == null) return false; + Element parent = document.parentOf(target); + return "FlowLayout".equals(layout) || "GridLayout".equals(layout) + ? x >= preview.getAbsoluteX() + preview.getWidth() / 2 + : "X".equals(value(parent, "boxLayoutAxis", "Y")) + ? x >= preview.getAbsoluteX() + preview.getWidth() / 2 + : y >= preview.getAbsoluteY() + preview.getHeight() / 2; + } + + private void showDropGuide(Element dragged, Element target, Component source, int x, int y) { + target = normalizeDesignerDropTarget(dragged, target); + dropGuideTarget = target; + DropPlan plan = planDrop(dragged, target, source, x, y); + if (plan == null) { + hideDropGuide(); + return; + } + activeDropPlan = plan; + String journalSignature = value(dragged, "name", designerPaletteType == null ? "component" : designerPaletteType) + + "|" + value(plan.target, "name", value(plan.target, "type", "component")) + + "|" + plan.layout + "|" + plan.constraint + "|" + plan.valid + "|" + plan.snapDescription; + if (!journalSignature.equals(lastDragJournalSignature)) { + lastDragJournalSignature = journalSignature; + recordAction("drag_preview", "component", value(dragged, "name", designerPaletteType), + "target", value(plan.target, "name", value(plan.target, "type", "component")), + "layout", plan.layout, "constraint", plan.constraint, "valid", Boolean.valueOf(plan.valid), + "description", plan.snapDescription, + "horizontalKind", plan.horizontalSnap == null ? null : plan.horizontalSnap.kind, + "horizontalReference", snapReferenceName(plan.horizontalSnap), + "verticalKind", plan.verticalSnap == null ? null : plan.verticalSnap.kind, + "verticalReference", snapReferenceName(plan.verticalSnap)); + } + Component parentPreview = componentForElement(canvasHost, plan.parent); + Element guideElement = plan.occupied == null ? target : plan.occupied; + Component targetPreview = componentForElement(canvasHost, guideElement); + if (dragGuideOverlay != null && parentPreview != null) { + dragGuideOverlay.showGuide(plan.layout, value(plan.parent, "boxLayoutAxis", "Y"), plan.constraint, + plan.valid, parentPreview, targetPreview, source, x, y, + plan.snapX, plan.snapY, plan.snapW, plan.snapH, plan.snapDescription); + if (dragged != null && plan.valid && "LayeredLayout".equals(plan.layout)) { + GuidedSimulation grouped = simulateGroupedGuidedDrop(dragged, plan); + if (grouped != null) showSimulation(grouped); + else showGuidedDropSimulation(dragged, plan, (Container) parentPreview, source); + } else { + dragGuideOverlay.clearSimulation(); + } + } + if ("BoxLayout".equals(plan.layout) && plan.valid && parentPreview instanceof Container parentContainer) { + showBoxDropSpacer(parentContainer, targetPreview, plan.after, + "X".equals(value(plan.parent, "boxLayoutAxis", "Y"))); + } else { + clearBoxDropSpacer(); + } + setStatus(plan.message); + } + + private void hideDropGuide() { + dropGuideTarget = null; + activeDropPlan = null; + if (dragGuideOverlay != null) dragGuideOverlay.hideGuide(); + clearBoxDropSpacer(); + } + + private void scheduleDesignerRefresh() { + if (refreshPending) return; + refreshPending = true; + Display.getInstance().callSerially(() -> { + refreshPending = false; + refreshEditor(); + }); + } + + private void showBoxDropSpacer(Container parent, Component target, boolean after, boolean horizontal) { + int index = target != null && target.getParent() == parent ? parent.getComponentIndex(target) : parent.getComponentCount(); + if (after && index < parent.getComponentCount()) index++; + if (boxDropParent == parent && boxDropIndex == index && boxDropSpacer != null) return; + if (boxDropParent == parent && boxDropSpacer != null && boxDropSpacer.getParent() == parent) { + boxDropIndex = Math.max(0, Math.min(index, parent.getComponentCount() - 1)); + parent.removeComponent(boxDropSpacer); + parent.addComponent(Math.min(boxDropIndex, parent.getComponentCount()), boxDropSpacer); + parent.revalidate(); + return; + } + clearBoxDropSpacer(); + final int spacerWidth = horizontal ? Math.max(12, Display.getInstance().convertToPixels(2)) : 1; + final int spacerHeight = horizontal ? 1 : Math.max(12, Display.getInstance().convertToPixels(2)); + Label spacer = new Label(" ", "BuilderDropSpacer") { + @Override protected Dimension calcPreferredSize() { + return new Dimension(spacerWidth, spacerHeight); + } + }; + spacer.setShowEvenIfBlank(true); + boxDropSpacer = spacer; + boxDropTargetElement = target == null ? null : (Element) target.getClientProperty("gui.element"); + spacer.putClientProperty("gui.dropTargetElement", boxDropTargetElement); + boxDropParent = parent; + boxDropIndex = Math.max(0, Math.min(index, parent.getComponentCount())); + parent.addComponent(boxDropIndex, spacer); + parent.animateLayout(90); + } + + private void clearBoxDropSpacer() { + if (boxDropSpacer != null && boxDropSpacer.getParent() != null) { + Container parent = boxDropSpacer.getParent(); + parent.removeComponent(boxDropSpacer); + parent.revalidate(); + } + boxDropSpacer = null; + boxDropTargetElement = null; + boxDropParent = null; + boxDropIndex = -1; + } + + DropPlan planDrop(Element dragged, Element target, Component source, int x, int y) { + if (document == null || target == null || target == dragged + || !document.containsElement(target) + || (dragged != null && !document.containsElement(dragged))) return null; + Element parent = GuiDocument.acceptsChildren(target) ? target : document.parentOf(target); + if (parent == null || parent == dragged) return null; + String layout = value(parent, "layout", "BoxLayout"); + DropPlan plan = new DropPlan(); + plan.document = document; + plan.target = target; + plan.parent = parent; + plan.layout = layout; + plan.snapX = x - (dragged == null ? 0 : designerGrabOffsetX); + plan.snapY = y - (dragged == null ? 0 : designerGrabOffsetY); + plan.after = dropAfter(target, layout, x, y); + plan.valid = true; + plan.message = "Drop into " + value(parent, "name", value(parent, "type", "container")); + placementAdapter(layout).plan(plan, dragged, target, source, x, y); + return plan; + } + + private PlacementAdapter placementAdapter(String layout) { + if ("BorderLayout".equals(layout)) return new BorderPlacementAdapter(); + if ("LayeredLayout".equals(layout)) return new LayeredPlacementAdapter(); + if ("TableLayout".equals(layout)) return new TablePlacementAdapter(); + if ("GridLayout".equals(layout)) return new GridPlacementAdapter(); + if ("FlowLayout".equals(layout)) return new FlowPlacementAdapter(); + return new BoxPlacementAdapter(); + } + + String placementAdapterName(String layout) { return placementAdapter(layout).getClass().getSimpleName(); } + + private interface PlacementAdapter { + void plan(DropPlan plan, Element dragged, Element target, Component source, int x, int y); + } + + private final class BorderPlacementAdapter implements PlacementAdapter { + public void plan(DropPlan plan, Element dragged, Element target, Component source, int x, int y) { + Component parentPreview = componentForElement(canvasHost, plan.parent); + plan.constraint = borderRegionAt(plan.parent, dragged, parentPreview, source, x, y); + plan.occupied = GuiDocument.childAtBorderConstraint(plan.parent, plan.constraint, dragged); + if (plan.occupied != null && (dragged == null || source == null)) { + plan.valid = false; + plan.message = plan.constraint + " is occupied by " + + value(plan.occupied, "name", value(plan.occupied, "type", "component")); + } else if (plan.occupied != null) { + plan.message = "Swap " + value(dragged, "name", "component") + " with " + + value(plan.occupied, "name", "component") + " in " + plan.constraint; + } else plan.message = "Drop into the " + plan.constraint + " region"; + } + } + + private final class LayeredPlacementAdapter implements PlacementAdapter { + public void plan(DropPlan plan, Element dragged, Element target, Component source, int x, int y) { + resolveLayeredSnap(plan, source, plan.snapX, plan.snapY); + plan.message = plan.snapDescription == null ? "Place freely in LayeredLayout" : "Snap: " + plan.snapDescription; + } + } + + private class BoxPlacementAdapter implements PlacementAdapter { + public void plan(DropPlan plan, Element dragged, Element target, Component source, int x, int y) { + plan.message = "Insert " + (plan.after ? "after " : "before ") + value(target, "name", "component"); + } + } + + private final class FlowPlacementAdapter extends BoxPlacementAdapter { } + private final class GridPlacementAdapter extends BoxPlacementAdapter { } + private final class TablePlacementAdapter extends BoxPlacementAdapter { } + + private String borderRegionAt(Element parent, Element dragged, Component parentPreview, + Component source, int x, int y) { + if (parentPreview == null || parentPreview.getWidth() < 1 || parentPreview.getHeight() < 1 + || x < parentPreview.getAbsoluteX() || x > parentPreview.getAbsoluteX() + parentPreview.getWidth() + || y < parentPreview.getAbsoluteY() || y > parentPreview.getAbsoluteY() + parentPreview.getHeight()) { + return firstAvailableBorderConstraint(parent, dragged); + } + int px = parentPreview.getAbsoluteX(); + int py = parentPreview.getAbsoluteY(); + int pw = parentPreview.getWidth(); + int ph = parentPreview.getHeight(); + int sourceW = source == null || source.getWidth() < 1 ? Math.max(48, pw / 5) : source.getWidth(); + int sourceH = source == null || source.getHeight() < 1 ? Math.max(32, ph / 8) : source.getHeight(); + + // CENTER normally covers every pixel left over by BorderLayout. If component hit-testing + // runs first, that makes empty EAST/WEST (and often NORTH/SOUTH) impossible to reach. + // Resolve intentional edge bands first, then use the component under the pointer for the + // central area. The bands scale with the dragged component but remain generous enough to + // use with a mouse. + String edgeRegion = borderEdgeRegion(px, py, pw, ph, sourceW, sourceH, x, y); + if (edgeRegion != null) return edgeRegion; + + for (int i = 0; i < parent.getNumChildren(); i++) { + Object childValue = parent.getChildAt(i); + if (!(childValue instanceof Element child) || child == dragged || !"component".equals(child.getTagName())) continue; + Component childPreview = componentForElement(canvasHost, child); + if (childPreview != null && x >= childPreview.getAbsoluteX() && x <= childPreview.getAbsoluteX() + childPreview.getWidth() + && y >= childPreview.getAbsoluteY() && y <= childPreview.getAbsoluteY() + childPreview.getHeight()) { + return GuiDocument.effectiveBorderConstraint(parent, child); + } + } + return "Center"; + } + + static String borderEdgeRegion(int px, int py, int pw, int ph, int sourceW, int sourceH, int x, int y) { + int horizontalBand = Math.min(Math.max(Math.max(64, sourceW), pw * 30 / 100), Math.max(1, pw * 40 / 100)); + int verticalBand = Math.min(Math.max(Math.max(44, sourceH), ph * 22 / 100), Math.max(1, ph * 35 / 100)); + int north = y - py; + int south = py + ph - y; + int west = x - px; + int east = px + pw - x; + String region = null; + double score = Double.MAX_VALUE; + if (north >= 0 && north <= verticalBand) { region = "North"; score = (double) north / verticalBand; } + if (south >= 0 && south <= verticalBand && (double) south / verticalBand < score) { + region = "South"; score = (double) south / verticalBand; + } + if (west >= 0 && west <= horizontalBand && (double) west / horizontalBand < score) { + region = "West"; score = (double) west / horizontalBand; + } + if (east >= 0 && east <= horizontalBand && (double) east / horizontalBand < score) region = "East"; + return region; + } + + private void resolveLayeredSnap(DropPlan plan, Component source, int x, int y) { + Component parentComponent = componentForElement(canvasHost, plan.parent); + if (!(parentComponent instanceof Container parent)) return; + int contentW = contentWidth(parent); + int contentH = contentHeight(parent); + int width = source == null ? Math.max(80, contentW / 6) + : source.getWidth() > 0 ? source.getWidth() : Math.max(1, source.getPreferredW()); + int height = source == null ? Math.max(36, contentH / 12) + : source.getHeight() > 0 ? source.getHeight() : Math.max(1, source.getPreferredH()); + int desiredX = Math.max(contentX(parent), Math.min(x, contentX(parent) + contentW - width)); + int desiredY = Math.max(contentY(parent), Math.min(y, contentY(parent) + contentH - height)); + SnapResult horizontal = snapAxis(parent, source, desiredX, width, true); + SnapResult vertical = snapAxis(parent, source, desiredY, height, false); + plan.snapX = horizontal.position; + plan.snapY = vertical.position; + plan.snapW = width; + plan.snapH = height; + plan.horizontalSnap = horizontal; + plan.verticalSnap = vertical; + if (horizontal.description != null && vertical.description != null) { + plan.snapDescription = horizontal.description + " • " + vertical.description; + } else { + plan.snapDescription = horizontal.description == null ? vertical.description : horizontal.description; + } + } + + private SnapResult snapAxis(Container parent, Component source, int desired, int size, boolean horizontal) { + List candidates = new ArrayList<>(); + int start = horizontal ? contentX(parent) : contentY(parent); + int length = horizontal ? contentWidth(parent) : contentHeight(parent); + candidates.add(new SnapCandidate(start, horizontal ? "dock left" : "dock top", null, "parentStart")); + candidates.add(new SnapCandidate(start + length - size, horizontal ? "dock right" : "dock bottom", null, "parentEnd")); + candidates.add(new SnapCandidate(start + (length - size) / 2, + horizontal ? "center horizontally" : "center vertically", null, "parentCenter")); + int gap = Math.max(6, Display.getInstance().convertToPixels(1)); + for (int i = 0; i < parent.getComponentCount(); i++) { + Component sibling = parent.getComponentAt(i); + if (sibling == source || sibling == boxDropSpacer || sibling.getClientProperty("gui.element") == null) continue; + Element siblingElement = (Element) sibling.getClientProperty("gui.element"); + int siblingStart = horizontal ? sibling.getAbsoluteX() : sibling.getAbsoluteY(); + int siblingSize = horizontal ? sibling.getWidth() : sibling.getHeight(); + String name = sibling.getName() == null ? "component" : sibling.getName().replace("preview.", ""); + candidates.add(new SnapCandidate(siblingStart, "align start with " + name, siblingElement, "alignStart")); + candidates.add(new SnapCandidate(siblingStart + siblingSize - size, "align end with " + name, siblingElement, "alignEnd")); + candidates.add(new SnapCandidate(siblingStart + (siblingSize - size) / 2, + "align center with " + name, siblingElement, "alignCenter")); + candidates.add(new SnapCandidate(siblingStart + siblingSize + gap, "space after " + name, siblingElement, "after")); + candidates.add(new SnapCandidate(siblingStart - size - gap, "space before " + name, siblingElement, "before")); + if (!horizontal) { + int sourceBaseline = source == null ? -1 : source.getBaseline( + source.getWidth() > 0 ? source.getWidth() : source.getPreferredW(), + source.getHeight() > 0 ? source.getHeight() : source.getPreferredH()); + int siblingBaseline = sibling.getBaseline(sibling.getWidth(), sibling.getHeight()); + if (sourceBaseline >= 0 && siblingBaseline >= 0) { + candidates.add(new SnapCandidate(sibling.getAbsoluteY() + siblingBaseline - sourceBaseline, + "align baseline with " + name, siblingElement, "baseline")); + } + } + } + int threshold = Math.max(8, Display.getInstance().convertToPixels(1.5f)); + int best = desired; + int bestDistance = threshold + 1; + SnapCandidate selected = null; + for (int i = 0; i < candidates.size(); i++) { + SnapCandidate candidate = candidates.get(i); + int distance = Math.abs(candidate.position - desired); + if (distance < bestDistance) { + best = candidate.position; + bestDistance = distance; + selected = candidate; + } + } + best = Math.max(start, Math.min(best, start + length - size)); + if (bestDistance > threshold) selected = null; + return new SnapResult(best, selected == null ? null : selected.description, + selected == null ? null : selected.reference, selected == null ? null : selected.kind); + } + + private String firstAvailableBorderConstraint(Element parent, Element excluding) { + String[] constraints = {"Center", "North", "South", "West", "East"}; + for (String candidate : constraints) { + if (GuiDocument.childAtBorderConstraint(parent, candidate, excluding) == null) return candidate; + } + return excluding != null && document.parentOf(excluding) == parent + ? GuiDocument.effectiveBorderConstraint(parent, excluding) : "Center"; + } + + boolean applyDropPlan(Element dragged, DropPlan plan, int x, int y) { + if (document == null || plan == null || plan.document != document + || !document.containsElement(dragged) + || !document.containsElement(plan.parent) + || !document.containsElement(plan.target) + || (plan.occupied != null && !document.containsElement(plan.occupied))) return false; + document.beginTransaction(); + try { + return applyDropPlanImpl(dragged, plan, x, y); + } finally { + document.endTransaction(); + } + } + + private boolean applyDropPlanImpl(Element dragged, DropPlan plan, int x, int y) { + Element oldParent = document.parentOf(dragged); + int oldIndex = document.componentIndex(oldParent, dragged); + String oldLayout = oldParent == null ? "" : value(oldParent, "layout", "BoxLayout"); + String oldLayeredInsets = dragged.getAttribute("layeredInsets"); + String oldConstraint = oldParent == null || !"BorderLayout".equals(value(oldParent, "layout", "BoxLayout")) + ? null : GuiDocument.effectiveBorderConstraint(oldParent, dragged); + if (plan.occupied != null && oldParent == plan.parent) { + document.select(plan.occupied); + document.setAttribute("layoutConstraint", oldConstraint); + document.select(dragged); + document.setAttribute("layoutConstraint", plan.constraint); + return true; + } + if (plan.occupied != null) { + int destinationIndex = document.componentIndex(plan.parent, plan.occupied); + document.select(plan.occupied); + if (!document.moveSelectedToParent(oldParent, oldIndex)) return false; + if ("BorderLayout".equals(oldLayout)) { + document.setAttribute("layoutConstraint", oldConstraint); + document.setAttribute("layeredInsets", null); + } else if ("LayeredLayout".equals(oldLayout)) { + document.setAttribute("layoutConstraint", null); + document.setAttribute("layeredInsets", oldLayeredInsets); + } else { + document.setAttribute("layoutConstraint", null); + document.setAttribute("layeredInsets", null); + } + document.select(dragged); + if (!document.moveSelectedToParent(plan.parent, destinationIndex)) return false; + } + document.select(dragged); + // A positional change inside a LayeredLayout must not also change stacking/XML order. + // Apart from being surprising, that made commit differ from the glass simulation and + // could cause a referenced sibling chain to be laid out in a different pass. Moving to a + // different parent still needs a structural move; reordering within the same guided + // parent belongs to the explicit tree/z-order commands. + boolean sameGuidedParent = "LayeredLayout".equals(plan.layout) && oldParent == plan.parent; + boolean moved = plan.occupied != null || sameGuidedParent || document.moveSelectedTo(plan.target, plan.after); + if (!moved && document.parentOf(dragged) != plan.parent) return false; + document.select(dragged); + if ("BorderLayout".equals(plan.layout)) { + document.setAttribute("layoutConstraint", plan.constraint); + document.setAttribute("layeredInsets", null); + } else if ("LayeredLayout".equals(plan.layout)) { + document.setAttribute("layoutConstraint", null); + Component parentPreview = componentForElement(canvasHost, plan.parent); + if (parentPreview != null && parentPreview.getWidth() > 0 && parentPreview.getHeight() > 0) { + Component source = componentForElement(canvasHost, dragged); + int width = plan.snapW > 0 ? plan.snapW + : source == null || source.getWidth() < 1 ? Math.max(1, parentPreview.getWidth() / 5) : source.getWidth(); + int height = plan.snapH > 0 ? plan.snapH + : source == null || source.getHeight() < 1 ? Math.max(1, parentPreview.getHeight() / 10) : source.getHeight(); + if (plan.horizontalSnap != null || plan.verticalSnap != null) { + prepareGuidedCycleBreak(document, dragged, plan); + persistGuidedConstraints(document, dragged, plan, (Container) parentPreview, source, width, height); + return true; + } + int contentW = contentWidth(parentPreview); + int contentH = contentHeight(parentPreview); + int marginLeft = source == null ? 0 : source.getStyle().getMarginLeftNoRTL(); + int marginRight = source == null ? 0 : source.getStyle().getMarginRightNoRTL(); + int marginTop = source == null ? 0 : source.getStyle().getMarginTop(); + int marginBottom = source == null ? 0 : source.getStyle().getMarginBottom(); + int leftPx = Math.max(0, plan.snapX - contentX(parentPreview) - marginLeft); + int topPx = Math.max(0, plan.snapY - contentY(parentPreview) - marginTop); + int rightPx = Math.max(0, contentW - leftPx - width - marginLeft - marginRight); + int bottomPx = Math.max(0, contentH - topPx - height - marginTop - marginBottom); + document.setAttribute("layeredInsets", topPx + "px " + rightPx + "px " + + bottomPx + "px " + leftPx + "px"); + } + } else if ("TableLayout".equals(plan.layout)) { + document.setAttribute("layoutConstraint", null); + document.setAttribute("layeredInsets", null); + normalizeTableCells(plan.parent); + document.select(dragged); + } else { + document.setAttribute("layoutConstraint", null); + document.setAttribute("layeredInsets", null); + } + return true; + } + + private void persistGuidedConstraints(GuiDocument targetDocument, Element element, DropPlan plan, Container parent, + Component source, int width, int height) { + String[] insets = GuidedLayoutSupport.insetValues(element); + String[] references = GuidedLayoutSupport.referenceNames(element); + String[] positions = GuidedLayoutSupport.referencePositions(element); + String horizontalPolicy = GuidedLayoutSupport.horizontalPolicy(element); + String verticalPolicy = GuidedLayoutSupport.verticalPolicy(element); + String matchWidthName = value(element, "guidedMatchWidth", ""); + String matchHeightName = value(element, "guidedMatchHeight", ""); + Set retainedReferences = new LinkedHashSet<>(); + if (plan.horizontalSnap != null && plan.horizontalSnap.reference != null) { + retainedReferences.add(value(plan.horizontalSnap.reference, "name", "")); + } + if (plan.verticalSnap != null && plan.verticalSnap.reference != null) { + retainedReferences.add(value(plan.verticalSnap.reference, "name", "")); + } + int tearDistance = Math.max(6, Display.getInstance().convertToPixels(1)); + boolean pulledAway = source == null || Math.abs(plan.snapX - source.getAbsoluteX()) + + Math.abs(plan.snapY - source.getAbsoluteY()) > tearDistance; + int marginLeft = source == null ? 0 : source.getStyle().getMarginLeftNoRTL(); + int marginRight = source == null ? 0 : source.getStyle().getMarginRightNoRTL(); + int marginTop = source == null ? 0 : source.getStyle().getMarginTop(); + int marginBottom = source == null ? 0 : source.getStyle().getMarginBottom(); + int left = plan.snapX - contentX(parent) - marginLeft; + int top = plan.snapY - contentY(parent) - marginTop; + int right = contentWidth(parent) - left - width - marginLeft - marginRight; + int bottom = contentHeight(parent) - top - height - marginTop - marginBottom; + + clearHorizontalReferences(references, positions); + clearVerticalReferences(references, positions); + targetDocument.select(element); + targetDocument.setAttribute("guidedHorizontalAnchor", null); + targetDocument.setAttribute("guidedVerticalAnchor", null); + + applyHorizontalPosition(targetDocument, plan.horizontalSnap, insets, references, positions, left, right, width, source); + applyVerticalPosition(targetDocument, plan.verticalSnap, insets, references, positions, top, bottom, height, source); + + if (GuidedLayoutSupport.FILL.equals(horizontalPolicy) && pulledAway && retainedReferences.isEmpty()) { + horizontalPolicy = GuidedLayoutSupport.FIXED; + targetDocument.setAttribute("guidedMatchWidth", null); + } else if (GuidedLayoutSupport.FILL.equals(horizontalPolicy)) { + insets[3] = Math.max(0, left) + "px"; + insets[1] = Math.max(0, right) + "px"; + references[3] = references[1] = "-"; + } else if (GuidedLayoutSupport.MATCH.equals(horizontalPolicy)) { + Element match = namedSibling(plan.parent, matchWidthName); + if (pulledAway && !retainedReferences.contains(matchWidthName)) { + horizontalPolicy = GuidedLayoutSupport.FIXED; + targetDocument.setAttribute("guidedMatchWidth", null); + } else if (match != null) { + applyMatchedWidth(match, insets, references, positions, plan.snapX, width); + } else { + horizontalPolicy = GuidedLayoutSupport.FIXED; + targetDocument.setAttribute("guidedMatchWidth", null); + } + } + if (GuidedLayoutSupport.FIXED.equals(horizontalPolicy)) { + stabilizeFixedHorizontalSpan(plan.horizontalSnap, insets, references, positions, width, source); + targetDocument.setAttribute("guidedPreferredWidth", String.valueOf(Math.max(1, width))); + } else if (GuidedLayoutSupport.PREFERRED.equals(horizontalPolicy)) { + targetDocument.setAttribute("guidedPreferredWidth", null); + } + + if ("baseline".equals(plan.verticalSnap == null ? null : plan.verticalSnap.kind)) { + verticalPolicy = GuidedLayoutSupport.PREFERRED; + targetDocument.setAttribute("guidedPreferredHeight", null); + } else if (GuidedLayoutSupport.FILL.equals(verticalPolicy) && pulledAway && retainedReferences.isEmpty()) { + verticalPolicy = GuidedLayoutSupport.FIXED; + targetDocument.setAttribute("guidedMatchHeight", null); + } else if (GuidedLayoutSupport.FILL.equals(verticalPolicy)) { + insets[0] = Math.max(0, top) + "px"; + insets[2] = Math.max(0, bottom) + "px"; + references[0] = references[2] = "-"; + } else if (GuidedLayoutSupport.MATCH.equals(verticalPolicy)) { + Element match = namedSibling(plan.parent, matchHeightName); + if (pulledAway && !retainedReferences.contains(matchHeightName)) { + verticalPolicy = GuidedLayoutSupport.FIXED; + targetDocument.setAttribute("guidedMatchHeight", null); + } else if (match != null) { + applyMatchedHeight(match, insets, references, positions, plan.snapY, height); + } else { + verticalPolicy = GuidedLayoutSupport.FIXED; + targetDocument.setAttribute("guidedMatchHeight", null); + } + } + if (GuidedLayoutSupport.FIXED.equals(verticalPolicy)) { + stabilizeFixedVerticalSpan(plan.verticalSnap, insets, references, positions, height, source); + targetDocument.setAttribute("guidedPreferredHeight", String.valueOf(Math.max(1, height))); + } else if (GuidedLayoutSupport.PREFERRED.equals(verticalPolicy)) { + targetDocument.setAttribute("guidedPreferredHeight", null); + } + + targetDocument.setAttribute("guidedHorizontalSize", horizontalPolicy); + targetDocument.setAttribute("guidedVerticalSize", verticalPolicy); + targetDocument.setAttribute("layeredInsets", GuidedLayoutSupport.joinInsets(insets[0], insets[1], insets[2], insets[3])); + targetDocument.setAttribute("guidedReferences", GuidedLayoutSupport.joinReferences( + references[0], references[1], references[2], references[3])); + targetDocument.setAttribute("guidedReferencePositions", GuidedLayoutSupport.joinPositions( + positions[0], positions[1], positions[2], positions[3])); + } + + /** + * An auto opposite inset normally supplies preferred size. Near a parent edge LayeredLayout + * clamps that auto inset to zero and silently shrinks the component. Pair both sides with the + * same reference anchor instead: the relationship remains responsive, while the distance + * between the two constraints is the component's complete outer width. + */ + private void stabilizeFixedHorizontalSpan(SnapResult snap, String[] insets, String[] refs, + String[] positions, int width, Component source) { + if (snap == null || snap.reference == null) return; + String ref = value(snap.reference, "name", "-"); + int outer = Math.max(1, width + (source == null ? 0 + : source.getStyle().getMarginLeftNoRTL() + source.getStyle().getMarginRightNoRTL())); + int gap = standardGap(); + if ("alignStart".equals(snap.kind)) { + refs[1] = ref; positions[1] = "1"; insets[1] = (-outer) + "px"; + } else if ("alignEnd".equals(snap.kind)) { + refs[1] = ref; positions[1] = "0"; insets[1] = "0px"; + } else if ("after".equals(snap.kind)) { + refs[1] = ref; positions[1] = "0"; insets[1] = (-(outer + gap)) + "px"; + } else if ("before".equals(snap.kind)) { + refs[3] = ref; positions[3] = "0"; insets[3] = (-(outer + gap)) + "px"; + } + } + + private void stabilizeFixedVerticalSpan(SnapResult snap, String[] insets, String[] refs, + String[] positions, int height, Component source) { + if (snap == null || snap.reference == null || "baseline".equals(snap.kind)) return; + String ref = value(snap.reference, "name", "-"); + int outer = Math.max(1, height + (source == null ? 0 + : source.getStyle().getMarginTop() + source.getStyle().getMarginBottom())); + int gap = standardGap(); + if ("alignStart".equals(snap.kind)) { + refs[2] = ref; positions[2] = "1"; insets[2] = (-outer) + "px"; + } else if ("alignEnd".equals(snap.kind)) { + refs[2] = ref; positions[2] = "0"; insets[2] = "0px"; + } else if ("after".equals(snap.kind)) { + refs[2] = ref; positions[2] = "0"; insets[2] = (-(outer + gap)) + "px"; + } else if ("before".equals(snap.kind)) { + refs[0] = ref; positions[0] = "0"; insets[0] = (-(outer + gap)) + "px"; + } + } + + private void applyHorizontalPosition(GuiDocument targetDocument, SnapResult snap, String[] insets, String[] refs, + String[] positions, int left, int right, int width, Component source) { + String kind = snap == null ? null : snap.kind; + String ref = snap == null || snap.reference == null ? "-" : value(snap.reference, "name", "-"); + insets[1] = "auto"; + if ("parentEnd".equals(kind)) { + insets[3] = "auto"; insets[1] = "0px"; + } else if ("parentCenter".equals(kind)) { + insets[3] = "50%"; targetDocument.setAttribute("guidedHorizontalAnchor", "0.5"); + } else if ("alignStart".equals(kind)) { + insets[3] = "0px"; refs[3] = ref; positions[3] = "0"; + } else if ("alignEnd".equals(kind)) { + // A right inset referenced to a component can clip preferred width to the reference + // box. Express equal right edges as a left edge measured backwards from the + // reference's right edge so a position-only drag cannot resize the component. + int outerWidth = Math.max(1, width + (source == null ? 0 + : source.getStyle().getMarginLeftNoRTL() + source.getStyle().getMarginRightNoRTL())); + insets[3] = (-outerWidth) + "px"; insets[1] = "auto"; + refs[3] = ref; positions[3] = "1"; + } else if ("alignCenter".equals(kind)) { + insets[3] = "0%"; refs[3] = ref; positions[3] = "0.5"; + targetDocument.setAttribute("guidedHorizontalAnchor", "0.5"); + } else if ("after".equals(kind)) { + insets[3] = standardGap() + "px"; refs[3] = ref; positions[3] = "1"; + } else if ("before".equals(kind)) { + insets[3] = "auto"; insets[1] = standardGap() + "px"; refs[1] = ref; positions[1] = "1"; + } else { + insets[3] = Math.max(0, left) + "px"; + } + } + + private void applyVerticalPosition(GuiDocument targetDocument, SnapResult snap, String[] insets, String[] refs, + String[] positions, int top, int bottom, int height, Component source) { + String kind = snap == null ? null : snap.kind; + String ref = snap == null || snap.reference == null ? "-" : value(snap.reference, "name", "-"); + insets[2] = "auto"; + if ("parentEnd".equals(kind)) { + insets[0] = "auto"; insets[2] = "0px"; + } else if ("parentCenter".equals(kind)) { + insets[0] = "50%"; targetDocument.setAttribute("guidedVerticalAnchor", "0.5"); + } else if ("alignStart".equals(kind)) { + insets[0] = "0px"; refs[0] = ref; positions[0] = "0"; + } else if ("alignEnd".equals(kind)) { + int outerHeight = Math.max(1, height + (source == null ? 0 + : source.getStyle().getMarginTop() + source.getStyle().getMarginBottom())); + insets[0] = (-outerHeight) + "px"; insets[2] = "auto"; + refs[0] = ref; positions[0] = "1"; + } else if ("alignCenter".equals(kind)) { + insets[0] = "0%"; refs[0] = ref; positions[0] = "0.5"; + targetDocument.setAttribute("guidedVerticalAnchor", "0.5"); + } else if ("after".equals(kind)) { + insets[0] = standardGap() + "px"; refs[0] = ref; positions[0] = "1"; + } else if ("before".equals(kind)) { + insets[0] = "auto"; insets[2] = standardGap() + "px"; refs[2] = ref; positions[2] = "1"; + } else if ("baseline".equals(kind)) { + insets[0] = "baseline"; insets[2] = "auto"; refs[0] = ref; positions[0] = "0"; + } else { + insets[0] = Math.max(0, top) + "px"; + } + } + + private void applyMatchedWidth(Element match, String[] insets, String[] refs, String[] positions, + int absoluteX, int width) { + applyMatchedWidth(match, componentForElement(canvasHost, match), insets, refs, positions, absoluteX, width); + } + + private void applyMatchedWidth(Element match, Component target, String[] insets, String[] refs, + String[] positions, int absoluteX, int width) { + if (target == null) return; + int delta = absoluteX - target.getAbsoluteX(); + String name = value(match, "name", "-"); + insets[3] = delta + "px"; + insets[1] = (-delta) + "px"; + refs[3] = refs[1] = name; + positions[3] = positions[1] = "0"; + } + + private void applyMatchedHeight(Element match, String[] insets, String[] refs, String[] positions, + int absoluteY, int height) { + applyMatchedHeight(match, componentForElement(canvasHost, match), insets, refs, positions, absoluteY, height); + } + + private void applyMatchedHeight(Element match, Component target, String[] insets, String[] refs, + String[] positions, int absoluteY, int height) { + if (target == null) return; + int delta = absoluteY - target.getAbsoluteY(); + String name = value(match, "name", "-"); + insets[0] = delta + "px"; + insets[2] = (-delta) + "px"; + refs[0] = refs[2] = name; + positions[0] = positions[2] = "0"; + } + + private Element namedSibling(Element parent, String name) { + if (parent == null || name == null || name.length() == 0) return null; + for (Element child : componentChildren(parent)) if (name.equals(child.getAttribute("name"))) return child; + return null; + } + + /** A component cannot be anchored to one of its own transitive dependents. Before installing + * such a relationship, rebase the first dependent in each path onto the parent at its current + * pixels. This preserves the downstream UI exactly and makes the requested target a stable + * anchor instead of relying on LayeredLayout's circular-reference fallback. */ + private void prepareGuidedCycleBreak(GuiDocument targetDocument, Element targetDragged, DropPlan plan) { + String draggedName = value(targetDragged, "name", ""); + if (draggedName.length() == 0) return; + Set requestedReferences = new LinkedHashSet<>(); + if (plan.horizontalSnap != null && plan.horizontalSnap.reference != null) { + requestedReferences.add(value(plan.horizontalSnap.reference, "name", "")); + } + if (plan.verticalSnap != null && plan.verticalSnap.reference != null) { + requestedReferences.add(value(plan.verticalSnap.reference, "name", "")); + } + Set rebase = new LinkedHashSet<>(); + for (String requested : requestedReferences) { + if (requested.length() == 0 || !dependencyReachable(document, draggedName, requested)) continue; + for (Element candidate : document.components()) { + String candidateName = value(candidate, "name", ""); + if (!incomingReferenceNames(document, candidateName).contains(draggedName)) continue; + if (requested.equals(candidateName) || dependencyReachable(document, candidateName, requested)) { + rebase.add(candidateName); + } + } + } + for (String dependentName : rebase) { + freezeDependencyAtCurrentBounds(targetDocument, dependentName, draggedName); + } + } + + private boolean dependencyReachable(GuiDocument sourceDocument, String from, String to) { + if (from.equals(to)) return true; + List pending = new ArrayList<>(); + Set visited = new LinkedHashSet<>(); + pending.add(from); + while (!pending.isEmpty()) { + String current = pending.remove(0); + if (!visited.add(current)) continue; + for (Element candidate : sourceDocument.components()) { + String name = value(candidate, "name", ""); + if (name.length() == 0 || !incomingReferenceNames(sourceDocument, name).contains(current)) continue; + if (to.equals(name)) return true; + pending.add(name); + } + } + return false; + } + + private void freezeDependencyAtCurrentBounds(GuiDocument targetDocument, String dependentName, + String removedReference) { + Element original = findElementNamed(document, dependentName); + Element target = findElementNamed(targetDocument, dependentName); + Component preview = original == null ? null : componentForElement(canvasHost, original); + if (target == null || preview == null || preview.getParent() == null) return; + Container parent = preview.getParent(); + String[] insets = GuidedLayoutSupport.insetValues(target); + String[] refs = GuidedLayoutSupport.referenceNames(target); + String[] positions = GuidedLayoutSupport.referencePositions(target); + boolean horizontal = removedReference.equals(refs[1]) || removedReference.equals(refs[3]) + || removedReference.equals(value(target, "guidedMatchWidth", "")); + boolean vertical = removedReference.equals(refs[0]) || removedReference.equals(refs[2]) + || removedReference.equals(value(target, "guidedMatchHeight", "")); + targetDocument.select(target); + if (horizontal) { + insets[3] = Math.max(0, preview.getAbsoluteX() - contentX(parent) + - preview.getStyle().getMarginLeftNoRTL()) + "px"; + insets[1] = "auto"; + clearHorizontalReferences(refs, positions); + targetDocument.setAttribute("guidedHorizontalAnchor", null); + targetDocument.setAttribute("guidedMatchWidth", null); + // Detaching an axis must freeze the complete rendered rectangle. A component + // can currently render larger than its nominal preferred size because of theme + // metrics, margins, or its old relationship. Falling back to preferred size here + // resized it and pulled every downstream dependent along with it. + targetDocument.setAttribute("guidedHorizontalSize", GuidedLayoutSupport.FIXED); + targetDocument.setAttribute("guidedPreferredWidth", String.valueOf(Math.max(1, preview.getWidth()))); + } + if (vertical) { + insets[0] = Math.max(0, preview.getAbsoluteY() - contentY(parent) + - preview.getStyle().getMarginTop()) + "px"; + insets[2] = "auto"; + clearVerticalReferences(refs, positions); + targetDocument.setAttribute("guidedVerticalAnchor", null); + targetDocument.setAttribute("guidedMatchHeight", null); + targetDocument.setAttribute("guidedVerticalSize", GuidedLayoutSupport.FIXED); + targetDocument.setAttribute("guidedPreferredHeight", String.valueOf(Math.max(1, preview.getHeight()))); + } + targetDocument.setAttribute("layeredInsets", GuidedLayoutSupport.joinInsets( + insets[0], insets[1], insets[2], insets[3])); + targetDocument.setAttribute("guidedReferences", GuidedLayoutSupport.joinReferences( + refs[0], refs[1], refs[2], refs[3])); + targetDocument.setAttribute("guidedReferencePositions", GuidedLayoutSupport.joinPositions( + positions[0], positions[1], positions[2], positions[3])); + } + + private void showGuidedDropSimulation(Element dragged, DropPlan plan, Container parent, Component source) { + GuidedSimulation simulation = simulateGuidedDrop(dragged, plan, parent, source); + showSimulation(simulation); + } + + private void showGuidedResizeSimulation(Element element, Container parent, Component source, + ResizePlan plan, int edges) { + GuidedSimulation simulation = simulateGuidedResize(element, parent, source, plan, edges); + showSimulation(simulation); + } + + private void showSimulation(GuidedSimulation simulation) { + if (dragGuideOverlay == null) return; + if (simulation == null) dragGuideOverlay.clearSimulation(); + else dragGuideOverlay.showSimulation(simulation.items, simulation.links, simulation.summary); + } + + /** Runs a proposed guided drop against an isolated XML clone. The active document and undo + * history remain untouched until pointer release. */ + GuidedSimulation simulateGuidedDrop(Element dragged, DropPlan plan, Container parent, Component source) { + if (document == null || previewRoot == null || dragged == null || plan == null || !plan.valid + || !"LayeredLayout".equals(plan.layout)) return null; + GuiDocument simulated = GuiDocument.parse(document.path(), document.toXml()); + Element simulatedDragged = equivalentElement(simulated, dragged); + Element simulatedParent = equivalentElement(simulated, plan.parent); + if (simulatedDragged == null || simulatedParent == null) return null; + if (simulated.parentOf(simulatedDragged) != simulatedParent) { + Element simulatedTarget = equivalentElement(simulated, plan.target); + simulated.select(simulatedDragged); + if (simulatedTarget == null || !simulated.moveSelectedTo(simulatedTarget, plan.after)) return null; + } + int width = plan.snapW > 0 ? plan.snapW : source == null || source.getWidth() < 1 + ? Math.max(1, parent.getWidth() / 5) : source.getWidth(); + int height = plan.snapH > 0 ? plan.snapH : source == null || source.getHeight() < 1 + ? Math.max(1, parent.getHeight() / 10) : source.getHeight(); + prepareGuidedCycleBreak(simulated, simulatedDragged, plan); + persistGuidedConstraints(simulated, simulatedDragged, plan, parent, source, width, height); + return buildGuidedSimulation(simulated, value(dragged, "name", "component")); + } + + /** Runs a proposed resize against an isolated XML clone, including all existing match and + * positional references so its full cascade can be painted before commit. */ + GuidedSimulation simulateGuidedResize(Element element, Container parent, Component source, + ResizePlan plan, int edges) { + if (document == null || previewRoot == null || element == null || plan == null) return null; + GuiDocument simulated = GuiDocument.parse(document.path(), document.toXml()); + Element simulatedElement = equivalentElement(simulated, element); + if (simulatedElement == null) return null; + boolean group = selectedElements.size() > 1 && selectedElements.contains(element) + && selectedElementsShareGuidedParent(); + commitGuidedResize(simulated, simulatedElement, parent, source, + group ? fixedReferenceResizePlan(plan, edges) : plan, edges); + if (group) { + for (Element selected : selectedElements) { + if (selected == element) continue; + Element simulatedSelected = equivalentElement(simulated, selected); + Component preview = componentForElement(canvasHost, selected); + if (simulatedSelected == null || preview == null) continue; + if ((edges & 3) != 0) { + applyLayeredRelationship(simulated, simulatedSelected, simulatedElement, "matchWidth", preview); + } + if ((edges & 12) != 0) { + applyLayeredRelationship(simulated, simulatedSelected, simulatedElement, "matchHeight", preview); + } + } + simulated.select(simulatedElement); + } + return buildGuidedSimulation(simulated, value(element, "name", "component")); + } + + private GuidedSimulation buildGuidedSimulation(GuiDocument simulated, String activeName) { + Component originalRoot = previewRoot; + if (!(originalRoot instanceof Container) || originalRoot.getWidth() < 1 || originalRoot.getHeight() < 1) return null; + Map before = previewBounds(document, (Container) originalRoot); + Component rendered; + try { + if (projectTheme != null) UIManager.getInstance().setThemeProps(projectTheme); + rendered = ComponentPreviewFactory.create(simulated.root(), null, simulationSelectionHandler()); + if (projectTheme != null) rendered.refreshTheme(); + } finally { + if (builderTheme != null) UIManager.getInstance().setThemeProps(builderTheme); + } + ComponentPreviewFactory.stabilizeDesignStyles(rendered); + rendered.setX(originalRoot.getAbsoluteX()); + rendered.setY(originalRoot.getAbsoluteY()); + rendered.setWidth(originalRoot.getWidth()); + rendered.setHeight(originalRoot.getHeight()); + layoutSimulation((Container) rendered); + Map after = previewBounds(simulated, (Container) rendered); + Set changed = new LinkedHashSet<>(); + List items = new ArrayList<>(); + for (Map.Entry entry : after.entrySet()) { + String name = entry.getKey(); + PreviewRect oldRect = before.get(name); + PreviewRect newRect = entry.getValue(); + if (oldRect == null) continue; + boolean activeItem = name.equals(activeName); + if (!activeItem && !oldRect.differs(newRect)) continue; + if (oldRect.differs(newRect)) changed.add(name); + items.add(new DragGuideOverlay.GlassItem(name, + oldRect.x, oldRect.y, oldRect.width, oldRect.height, + newRect.x, newRect.y, newRect.width, newRect.height, activeItem)); + } + List links = dependencyLinks(simulated, after, changed, activeName); + Map originalPairs = dependencyPairs(document); + Map simulatedPairs = dependencyPairs(simulated); + List rebased = new ArrayList<>(); + Set detachedFromActive = new LinkedHashSet<>(); + for (Map.Entry entry : originalPairs.entrySet()) { + if (simulatedPairs.containsKey(entry.getKey())) continue; + String reference = entry.getValue()[0]; + String dependent = entry.getValue()[1]; + PreviewRect referenceBefore = before.get(reference); + PreviewRect dependentAfter = after.get(dependent); + if (referenceBefore != null && dependentAfter != null) { + links.add(new DragGuideOverlay.DependencyLink(reference, dependent, + referenceBefore.x + referenceBefore.width / 2, + referenceBefore.y + referenceBefore.height / 2, + dependentAfter.x + dependentAfter.width / 2, + dependentAfter.y + dependentAfter.height / 2, true)); + } + if (activeName.equals(dependent)) detachedFromActive.add(reference); + else rebased.add(dependent + " from " + reference); + } + List affected = new ArrayList<>(); + for (String name : changed) if (!name.equals(activeName)) affected.add(name); + String summary = "Preview: " + activeName; + if (changed.contains(activeName)) summary += " changes"; + if (!affected.isEmpty()) summary += " • also affects " + joinNames(affected); + else summary += " • no dependent components change"; + if (!detachedFromActive.isEmpty()) summary += " • detaches from " + joinNames(new ArrayList<>(detachedFromActive)); + if (!rebased.isEmpty()) summary += " • keeps in place " + joinNames(rebased); + return new GuidedSimulation(simulated, items, links, summary, changed); + } + + private Map dependencyPairs(GuiDocument targetDocument) { + Map result = new LinkedHashMap<>(); + for (Element dependent : targetDocument.components()) { + String dependentName = value(dependent, "name", ""); + if (dependentName.length() == 0) continue; + for (String reference : incomingReferenceNames(targetDocument, dependentName)) { + String key = reference + "\n" + dependentName; + result.put(key, new String[]{reference, dependentName}); + } + } + return result; + } + + private Set incomingReferenceNames(GuiDocument targetDocument, String componentName) { + Set result = new LinkedHashSet<>(); + Element element = findElementNamed(targetDocument, componentName); + if (element == null) return result; + for (String reference : GuidedLayoutSupport.referenceNames(element)) { + if (reference != null && reference.length() > 0 && !"-".equals(reference)) result.add(reference); + } + String matchWidth = value(element, "guidedMatchWidth", ""); + String matchHeight = value(element, "guidedMatchHeight", ""); + if (matchWidth.length() > 0) result.add(matchWidth); + if (matchHeight.length() > 0) result.add(matchHeight); + return result; + } + + private String joinNames(List names) { + StringBuilder out = new StringBuilder(); + for (String name : names) { + if (out.length() > 0) out.append(", "); + out.append(name); + } + return out.toString(); + } + + private List dependencyLinks(GuiDocument simulated, + Map bounds, Set changed, String activeName) { + List result = new ArrayList<>(); + Set unique = new LinkedHashSet<>(); + for (Element dependent : simulated.components()) { + String dependentName = value(dependent, "name", ""); + if (dependentName.length() == 0) continue; + Set references = new LinkedHashSet<>(); + for (String reference : GuidedLayoutSupport.referenceNames(dependent)) { + if (reference != null && reference.length() > 0 && !"-".equals(reference)) references.add(reference); + } + String matchWidth = value(dependent, "guidedMatchWidth", ""); + String matchHeight = value(dependent, "guidedMatchHeight", ""); + if (matchWidth.length() > 0) references.add(matchWidth); + if (matchHeight.length() > 0) references.add(matchHeight); + for (String reference : references) { + if (!changed.contains(dependentName) && !changed.contains(reference) + && !activeName.equals(dependentName) && !activeName.equals(reference)) continue; + PreviewRect from = bounds.get(reference); + PreviewRect to = bounds.get(dependentName); + String key = reference + "\n" + dependentName; + if (from == null || to == null || !unique.add(key)) continue; + result.add(new DragGuideOverlay.DependencyLink(reference, dependentName, + from.x + from.width / 2, from.y + from.height / 2, + to.x + to.width / 2, to.y + to.height / 2)); + } + } + return result; + } + + private Map previewBounds(GuiDocument targetDocument, Container root) { + Map result = new LinkedHashMap<>(); + for (Element element : targetDocument.components()) { + if (element == targetDocument.root()) continue; + String name = value(element, "name", ""); + Component component = componentForElement(root, element); + if (name.length() == 0 || component == null) continue; + result.put(name, new PreviewRect(component.getAbsoluteX(), component.getAbsoluteY(), + component.getWidth(), component.getHeight())); + } + return result; + } + + private void layoutSimulation(Container container) { + container.layoutContainer(); + for (int i = 0; i < container.getComponentCount(); i++) { + Component child = container.getComponentAt(i); + if (child instanceof Container nested) layoutSimulation(nested); + } + } + + private ComponentPreviewFactory.SelectionHandler simulationSelectionHandler() { + return new ComponentPreviewFactory.SelectionHandler() { + @Override public void selected(Element element) { } + @Override public void dragPressed(Element element, Component source, int x, int y) { } + @Override public boolean isDragActive() { return false; } + @Override public void editContent(Element element) { } + }; + } + + private Element equivalentElement(GuiDocument targetDocument, Element original) { + if (original == null) return null; + if (document != null && original == document.root()) return targetDocument.root(); + String name = value(original, "name", ""); + if (name.length() == 0) return null; + return findElementNamed(targetDocument, name); + } + + private Element findElementNamed(GuiDocument targetDocument, String name) { + if (targetDocument == null || name == null || name.length() == 0) return null; + for (Element candidate : targetDocument.components()) { + if (name.equals(candidate.getAttribute("name"))) return candidate; + } + return null; + } + + private void clearHorizontalReferences(String[] refs, String[] positions) { + refs[1] = refs[3] = "-"; positions[1] = positions[3] = "0"; + } + + private void clearVerticalReferences(String[] refs, String[] positions) { + refs[0] = refs[2] = "-"; positions[0] = positions[2] = "0"; + } + + private int standardGap() { return Math.max(6, Display.getInstance().convertToPixels(1)); } + + private void normalizeTableCells(Element parent) { + int columns = Math.max(1, integer(value(parent, "tableLayoutColumns", "2"), 2)); + List children = componentChildren(parent); + int requiredRows = Math.max(1, (children.size() + columns - 1) / columns); + if (integer(value(parent, "tableLayoutRows", "2"), 2) < requiredRows) { + document.select(parent); + document.setAttribute("tableLayoutRows", String.valueOf(requiredRows)); + } + for (int i = 0; i < children.size(); i++) { + document.select(children.get(i)); + document.setAttribute("tableRow", String.valueOf(i / columns)); + document.setAttribute("tableColumn", String.valueOf(i % columns)); + } + } + + private List componentChildren(Element parent) { + List children = new ArrayList<>(); + if (parent == null) return children; + for (int i = 0; i < parent.getNumChildren(); i++) { + Object child = parent.getChildAt(i); + if (child instanceof Element element && "component".equals(element.getTagName())) children.add(element); + } + return children; + } + + static final class DropPlan { + GuiDocument document; + Element target; + Element parent; + Element occupied; + String layout; + String constraint; + String message; + boolean after; + boolean valid; + int snapX; + int snapY; + int snapW; + int snapH; + String snapDescription; + SnapResult horizontalSnap; + SnapResult verticalSnap; + } + + static final class GuidedSimulation { + final GuiDocument document; + final List items; + final List links; + final String summary; + final Set changedNames; + + GuidedSimulation(GuiDocument document, List items, + List links, String summary, Set changedNames) { + this.document = document; + this.items = items; + this.links = links; + this.summary = summary; + this.changedNames = changedNames; + } + } + + private static final class GroupMoveGeometry { + final Container parent; + final Map previews; + final int dx; + final int dy; + + GroupMoveGeometry(Container parent, Map previews, int dx, int dy) { + this.parent = parent; + this.previews = previews; + this.dx = dx; + this.dy = dy; + } + } + + private static final class PreviewRect { + final int x, y, width, height; + PreviewRect(int x, int y, int width, int height) { + this.x = x; this.y = y; this.width = width; this.height = height; + } + boolean differs(PreviewRect other) { + return other == null || Math.abs(x - other.x) > 1 || Math.abs(y - other.y) > 1 + || Math.abs(width - other.width) > 1 || Math.abs(height - other.height) > 1; + } + } + + private static final class ResizeHit { + final Element element; + final Component component; + final int edges; + + ResizeHit(Element element, Component component, int edges) { + this.element = element; + this.component = component; + this.edges = edges; + } + } + + static final class ResizePlan { + int x; + int y; + int width; + int height; + Element matchWidth; + Element matchHeight; + String horizontalDescription; + String verticalDescription; + String description; + ResizePlan(int x, int y, int width, int height) { + this.x = x; + this.y = y; + this.width = width; + this.height = height; + } + } + + static final class SnapResult { + final int position; + final String description; + final Element reference; + final String kind; + SnapResult(int position, String description, Element reference, String kind) { + this.position = position; + this.description = description; + this.reference = reference; + this.kind = kind; + } + } + + private static final class SnapCandidate { + final int position; + final String description; + final Element reference; + final String kind; + SnapCandidate(int position, String description, Element reference, String kind) { + this.position = position; + this.description = description; + this.reference = reference; + this.kind = kind; + } + } + + private static int contentX(Component component) { + return component.getAbsoluteX() + component.getStyle().getPaddingLeftNoRTL(); + } + + private static int contentY(Component component) { + return component.getAbsoluteY() + component.getStyle().getPaddingTop(); + } + + private static int contentWidth(Component component) { + return Math.max(1, component.getWidth() - component.getStyle().getPaddingLeftNoRTL() + - component.getStyle().getPaddingRightNoRTL()); + } + + private static int contentHeight(Component component) { + return Math.max(1, component.getHeight() - component.getStyle().getPaddingTop() + - component.getStyle().getPaddingBottom()); + } + + private Component componentForElement(Container root, Element element) { + if (element == null) return null; + for (int i = 0; i < root.getComponentCount(); i++) { + Component component = root.getComponentAt(i); + if (component.getClientProperty("gui.element") == element) return component; + if (component instanceof Container container) { + Component nested = componentForElement(container, element); + if (nested != null) return nested; + } + } + return null; + } + + Element elementAt(Container root, int x, int y) { + for (int i = root.getComponentCount() - 1; i >= 0; i--) { + Component component = root.getComponentAt(i); + int ax = component.getAbsoluteX(); + int ay = component.getAbsoluteY(); + if (x < ax || x > ax + component.getWidth() || y < ay || y > ay + component.getHeight()) continue; + if (component instanceof Container container) { + Element nested = elementAt(container, x, y); + if (nested != null) return nested; + } + Object element = component.getClientProperty("gui.element"); + if (element instanceof Element e) return e; + Object dropTarget = component.getClientProperty("gui.dropTargetElement"); + if (dropTarget instanceof Element e) return e; + } + return null; + } + + private Element activePreviewElementAt(int x, int y) { + if (document == null || canvasHost == null) return null; + Element hit = elementAt(canvasHost, x, y); + return document.containsElement(hit) ? hit : null; + } + + private Element designerDropTargetAt(Element dragged, int x, int y) { + return normalizeDesignerDropTarget(dragged, activePreviewElementAt(x, y)); + } + + Element normalizeDesignerDropTarget(Element dragged, Element target) { + if (dragged == null || target == null) return target; + if (target == dragged || containsElementIdentity(dragged, target)) return document.parentOf(dragged); + return target; + } + + private boolean containsElementIdentity(Element ancestor, Element candidate) { + if (ancestor == null || candidate == null) return false; + for (int i = 0; i < ancestor.getNumChildren(); i++) { + Object child = ancestor.getChildAt(i); + if (child == candidate) return true; + if (child instanceof Element element && containsElementIdentity(element, candidate)) return true; + } + return false; + } + + boolean isActiveDocumentElement(Element element) { + return document != null && document.containsElement(element); + } + + private void cancelDesignerDrag() { + designerDraggedElement = null; + designerDragDocument = null; + designerPaletteType = null; + designerDragSource = null; + designerSuppressAction = null; + designerDragArmed = false; + designerDragActive = false; + guidedResizeElement = null; + guidedResizeSource = null; + guidedResizeArmed = false; + guidedResizeActive = false; + activeResizePlan = null; + hideDropGuide(); + } + + private void refreshGuidedSelectionOverlay() { + if (dragGuideOverlay == null || document == null || canvasHost == null) return; + normalizeSelection(); + List components = new ArrayList<>(); + for (Element element : selectedElements) { + Component component = componentForElement(canvasHost, element); + if (component != null && "LayeredLayout".equals(document.parentLayout(element))) components.add(component); + } + Component primary = document.selected() == null ? null : componentForElement(canvasHost, document.selected()); + if (!components.isEmpty()) { + dragGuideOverlay.showSelections(components, primary); + } else { + dragGuideOverlay.clearSelection(); + } + refreshSelectionActions(components); + } + + private void normalizeSelection() { + if (document == null) { + selectedElements.clear(); + return; + } + selectedElements.removeIf(element -> !document.containsElement(element)); + Element primary = document.selected(); + if (selectedElements.isEmpty() && primary != null && primary != document.root()) selectedElements.add(primary); + if (!selectedElements.isEmpty() && !selectedElements.contains(primary)) { + document.select(selectedElements.iterator().next()); + } + } + + private void selectElement(Element element, boolean additive, String source) { + if (document == null || element == null || !document.containsElement(element)) return; + if (element == document.root()) { + selectedElements.clear(); + document.select(element); + } else if (additive) { + if (selectedElements.contains(element)) { + boolean removedReference = element == document.selected(); + selectedElements.remove(element); + if (selectedElements.isEmpty()) document.select(document.root()); + else if (removedReference) document.select(selectedElements.iterator().next()); + } else { + Element reference = selectedElements.contains(document.selected()) ? document.selected() : null; + selectedElements.add(element); + // Modifier-click grows the group without silently changing the component that + // alignment and size actions use as their reference. + document.select(reference == null ? element : reference); + } + } else if (selectedElements.size() > 1 && selectedElements.contains(element)) { + // Keep the group intact when a selected member becomes the drag handle. Modifier + // clicks still toggle membership; clicking an unselected component starts a new group. + document.select(element); + } else { + selectedElements.clear(); + selectedElements.add(element); + document.select(element); + } + recordAction("selection_changed", "component", value(element, "name", value(element, "type", "component")), + "source", source, "additive", Boolean.valueOf(additive), "count", Integer.valueOf(selectedElements.size())); + if (inspectorHost != null) refreshInspector(); + if (hierarchyPanel != null) refreshHierarchy(); + refreshGuidedSelectionOverlay(); + setStatus(selectedElements.size() > 1 ? selectedElements.size() + " components selected • reference: " + + value(document.selected(), "name", "component") + + " • click a selected component to change the reference" + : selectedElements.isEmpty() ? "Selection cleared" + : "Selected " + value(document.selected(), "name", value(document.selected(), "type", "component")) + " • drag to reposition"); + } + + private void refreshSelectionActions(List selectedComponents) { + if (selectionActions == null || canvasOverlayHost == null) return; + boolean show = selectedComponents != null && selectedComponents.size() > 1 + && selectedElementsShareGuidedParent(); + selectionActions.setVisible(show); + if (!show) return; + int minX = Integer.MAX_VALUE; + int minY = Integer.MAX_VALUE; + int maxX = Integer.MIN_VALUE; + for (Component component : selectedComponents) { + minX = Math.min(minX, component.getAbsoluteX()); + minY = Math.min(minY, component.getAbsoluteY()); + maxX = Math.max(maxX, component.getAbsoluteX() + component.getWidth()); + } + int paletteWidth = Math.max(1, selectionActions.getPreferredW()); + int paletteHeight = Math.max(1, selectionActions.getPreferredH()); + int hostX = canvasOverlayHost.getAbsoluteX(); + int hostY = canvasOverlayHost.getAbsoluteY(); + int beside = maxX - hostX + 8; + boolean fitsBeside = beside + paletteWidth <= canvasOverlayHost.getWidth() - 4; + int left = fitsBeside ? beside : Math.max(4, Math.min(canvasOverlayHost.getWidth() - paletteWidth - 4, + (minX + maxX - paletteWidth) / 2 - hostX)); + int top = fitsBeside ? Math.max(4, minY - hostY) + : Math.max(4, minY - hostY - paletteHeight - 8); + ((LayeredLayout) canvasOverlayHost.getLayout()).setInsets(selectionActions, + top + "px auto auto " + left + "px"); + selectionActions.revalidate(); + } + + private boolean selectedElementsShareGuidedParent() { + Element parent = null; + for (Element element : selectedElements) { + Element candidate = document.parentOf(element); + if (candidate == null || !"LayeredLayout".equals(value(candidate, "layout", "BoxLayout"))) return false; + if (parent == null) parent = candidate; + else if (parent != candidate) return false; + } + return parent != null; + } + + void autoScrollDuringDrag(int x, int y) { + if (canvasHost == null) return; + Component hit = deepestComponentAt(canvasHost, x, y); + Container scrollable = hit instanceof Container ? (Container) hit : hit == null ? null : hit.getParent(); + while (scrollable != null && scrollable != canvasHost + && !scrollable.isScrollableX() && !scrollable.isScrollableY()) scrollable = scrollable.getParent(); + if (scrollable == null || scrollable == canvasHost) return; + int edge = Math.max(24, Display.getInstance().convertToPixels(3)); + int stepX = Math.max(18, scrollable.getWidth() / 12); + int stepY = Math.max(18, scrollable.getHeight() / 12); + if (scrollable.isScrollableX()) { + if (x < scrollable.getAbsoluteX() + edge) { + scrollable.scrollRectToVisible(Math.max(0, scrollable.getScrollX() - stepX), + scrollable.getScrollY(), 1, 1, null); + } else if (x > scrollable.getAbsoluteX() + scrollable.getWidth() - edge) { + scrollable.scrollRectToVisible(scrollable.getScrollX() + scrollable.getWidth() + stepX, + scrollable.getScrollY(), 1, 1, null); + } + } + if (scrollable.isScrollableY()) { + if (y < scrollable.getAbsoluteY() + edge) { + scrollable.scrollRectToVisible(scrollable.getScrollX(), + Math.max(0, scrollable.getScrollY() - stepY), 1, 1, null); + } else if (y > scrollable.getAbsoluteY() + scrollable.getHeight() - edge) { + scrollable.scrollRectToVisible(scrollable.getScrollX(), + scrollable.getScrollY() + scrollable.getHeight() + stepY, 1, 1, null); + } + } + } + + private Component deepestComponentAt(Container root, int x, int y) { + for (int i = root.getComponentCount() - 1; i >= 0; i--) { + Component component = root.getComponentAt(i); + if (x < component.getAbsoluteX() || x > component.getAbsoluteX() + component.getWidth() + || y < component.getAbsoluteY() || y > component.getAbsoluteY() + component.getHeight()) continue; + if (component instanceof Container container) { + Component nested = deepestComponentAt(container, x, y); + if (nested != null) return nested; + } + return component; + } + return null; + } + + private void refreshInspector() { + inspectorHost.removeAll(); + Element selected = document.selected(); + Tabs tabs = new Tabs(); + tabs.setSwipeActivated(false); + tabs.setUIID("BuilderInspectorTabs"); + tabs.addTab("Properties", propertiesTab(selected)); + tabs.addTab("Layout", layoutTab(selected)); + tabs.addTab("Events", eventsTab(selected)); + tabs.setSelectedIndex(Math.max(0, Math.min(2, inspectorTabIndex)), false); + tabs.addSelectionListener((oldIndex, newIndex) -> inspectorTabIndex = newIndex); + inspectorHost.add(BorderLayout.CENTER, tabs); + inspectorHost.revalidate(); + } + + private Component propertiesTab(Element element) { + Container fields = inspectorFields(); + String type = document.attribute("type", "Component"); + fields.add(new Label(type, "BuilderInspectorComponent")); + fields.add(nameField()); + if ("Form".equals(type)) { + fields.add(propertyField("Title", "title")); + fields.add(bindingStrategyPicker()); + } + else if (hasText(type)) fields.add(propertyField("Text", "text")); + if ("TextField".equals(type) || "TextArea".equals(type)) fields.add(propertyField("Hint", "hint")); + fields.add(propertyField("UIID (CSS selector)", "uiid", document.effectiveUiid(element))); + fields.add(booleanProperty("Enabled", "enabled", true)); + fields.add(booleanProperty("Visible", "visible", true)); + fields.add(booleanProperty("Right-to-left", "rtl", false)); + if (hasText(type)) { + fields.add(propertyField("Icon/text gap", "gap", "2")); + fields.add(pickerProperty("Alignment", "alignment", new String[]{"left", "center", "right"}, "left")); + fields.add(booleanProperty("Ticker when clipped", "tickerEnabled", false)); + } + if ("Button".equals(type)) fields.add(booleanProperty("Toggle button", "toggle", false)); + if ("CheckBox".equals(type) || "RadioButton".equals(type)) fields.add(booleanProperty("Selected", "selected", false)); + if ("TextField".equals(type) || "TextArea".equals(type)) { + fields.add(propertyField("Columns", "columns", "12")); + fields.add(propertyField("Maximum length", "maxSize", "0")); + fields.add(booleanProperty("Editable", "editable", true)); + fields.add(booleanProperty("Grow by content", "growByContent", true)); + fields.add(pickerProperty("Input constraint", "constraint", + new String[]{"ANY", "EMAILADDR", "PASSWORD", "NUMERIC", "URL"}, "ANY")); + } + if ("TextArea".equals(type)) fields.add(propertyField("Rows", "rows", "3")); + if ("Slider".equals(type)) { + fields.add(propertyField("Minimum", "minValue", "0")); + fields.add(propertyField("Maximum", "maxValue", "100")); + fields.add(propertyField("Progress", "progress", "50")); + fields.add(booleanProperty("Editable", "editable", false)); + fields.add(booleanProperty("Infinite progress", "infinite", false)); + } + if ("Tabs".equals(type)) { + fields.add(propertyField("Selected tab index", "selectedIndex", "0")); + fields.add(pickerProperty("Tab placement", "tabPlacement", + new String[]{"top", "bottom", "left", "right"}, "top")); + } + if (GuiDocument.acceptsChildren(element)) { + fields.add(booleanProperty("Scroll horizontally", "scrollableX", false)); + fields.add(booleanProperty("Scroll vertically", "scrollableY", false)); + } + if (hasText(type) || "Form".equals(type)) { + Button inline = new Button("Edit content in place", material(FontImage.MATERIAL_EDIT, "BuilderInlineIcon")); + inline.setUIID("BuilderSecondaryAction"); + inline.addActionListener(e -> editSelectedContent()); + fields.add(inline); + } + if ("Form".equals(type)) { + fields.add(fieldLabel("Toolbar commands")); + fields.add(toolbarCommandsEditor()); + } + Button css = new Button("Edit theme.css", material(FontImage.MATERIAL_COLOR_LENS, "BuilderInlineIcon")); + css.setUIID("BuilderSecondaryAction"); + css.addActionListener(e -> openCss()); + fields.add(css); + Button delete = new Button("Delete component", material(FontImage.MATERIAL_DELETE, "BuilderDangerIcon")); + delete.setUIID("BuilderDangerAction"); + delete.setEnabled(element != document.root()); + delete.addActionListener(e -> { if (document.deleteSelected()) refreshEditor(); }); + fields.add(delete); + return fields; + } + + private Component bindingStrategyPicker() { + Container field = new Container(BoxLayout.y()); + field.add(fieldLabel("Data binding")); + String strategy = value(document.root(), "bindingStrategy", "properties"); + String selected = "bindable".equals(strategy) ? "@Bindable POJO" + : "none".equals(strategy) ? "None" : "PropertyBusinessObject"; + Picker picker = stringPicker(new String[]{"None", "PropertyBusinessObject", "@Bindable POJO"}, selected); + picker.addActionListener(e -> { + String choice = picker.getSelectedString(); + document.select(document.root()); + document.setAttribute("bindingStrategy", "None".equals(choice) ? "none" + : "@Bindable POJO".equals(choice) ? "bindable" : "properties"); + setStatus("Binding strategy: " + choice + " • Java preview regenerated on open"); + }); + field.add(picker); + return field; + } + + private Component toolbarCommandsEditor() { + Container commands = new Container(BoxLayout.y()); + for (Element command : document.commands()) { + Container row = new Container(BoxLayout.y()); + row.setUIID("BuilderCommandCard"); + TextField name = new TextField(value(command, "name", "Command")); + name.setHint("Command label"); + name.addDataChangedListener((t, i) -> document.setCommandAttribute(command, "name", name.getText())); + Picker placement = stringPicker(new String[]{"left", "right", "overflow", "side"}, value(command, "placement", "right")); + placement.addActionListener(e -> { + document.setCommandAttribute(command, "placement", placement.getSelectedString()); + refreshEditor(); + }); + TextField event = new TextField(value(command, "actionEvent", "onCommand")); + event.setHint("Event handler"); + event.addDataChangedListener((t, i) -> document.setCommandAttribute(command, "actionEvent", event.getText())); + Button remove = new Button("Remove command"); + remove.setUIID("BuilderDangerAction"); + remove.addActionListener(e -> { document.removeCommand(command); refreshEditor(); }); + row.add(name).add(placement).add(event).add(remove); + commands.add(row); + } + Button add = new Button("Add toolbar command", material(FontImage.MATERIAL_ADD, "BuilderInlineIcon")); + add.setUIID("BuilderSecondaryAction"); + add.addActionListener(e -> { document.addCommand(); refreshEditor(); }); + commands.add(add); + return commands; + } + + private Component layoutTab(Element element) { + Container fields = inspectorFields(); + if (element != document.root()) { + Container order = new Container(new GridLayout(1, 2)); + Button earlier = new Button("Move earlier", material(FontImage.MATERIAL_ARROW_UPWARD, "BuilderInlineIcon")); + Button later = new Button("Move later", material(FontImage.MATERIAL_ARROW_DOWNWARD, "BuilderInlineIcon")); + earlier.setUIID("BuilderSecondaryAction"); + later.setUIID("BuilderSecondaryAction"); + earlier.addActionListener(e -> moveSelectedInParent(-1)); + later.addActionListener(e -> moveSelectedInParent(1)); + order.add(earlier).add(later); + fields.add(fieldLabel("Order in parent")); + fields.add(order); + } + if (GuiDocument.acceptsChildren(element)) { + Picker layout = stringPicker(new String[]{"BoxLayout", "BorderLayout", "FlowLayout", "GridLayout", "TableLayout", "LayeredLayout"}, document.attribute("layout", "BoxLayout")); + layout.addActionListener(e -> update("layout", String.valueOf(layout.getSelectedString()))); + fields.add(fieldLabel("Container layout")); + fields.add(layout); + if ("BoxLayout".equals(document.attribute("layout", "BoxLayout"))) { + Picker axis = stringPicker(new String[]{"Y", "X"}, document.attribute("boxLayoutAxis", "Y")); + axis.addActionListener(e -> update("boxLayoutAxis", axis.getSelectedString())); + fields.add(fieldLabel("Axis")); + fields.add(axis); + } else if ("GridLayout".equals(document.attribute("layout", "BoxLayout"))) { + fields.add(numericPropertyField("Grid rows", "gridLayoutRows", "1", 1, 100)); + fields.add(numericPropertyField("Grid columns", "gridLayoutColumns", "2", 1, 100)); + } else if ("TableLayout".equals(document.attribute("layout", "BoxLayout"))) { + fields.add(numericPropertyField("Table rows", "tableLayoutRows", "2", 1, 100)); + fields.add(numericPropertyField("Table columns", "tableLayoutColumns", "2", 1, 100)); + } + } + Picker constraint = stringPicker(new String[]{"", "North", "South", "East", "West", "Center"}, document.attribute("layoutConstraint", "")); + constraint.addActionListener(e -> update("layoutConstraint", constraint.getSelectedString())); + fields.add(fieldLabel("Parent constraint")); + fields.add(constraint); + if ("LayeredLayout".equals(document.parentLayout(element))) { + Element referenceElement = guidedReferenceElement(element); + fields.add(fieldLabel("Alignment reference")); + Picker reference = stringPicker(guidedReferenceNames(element), referenceElement == null + ? "Nearest component" : value(referenceElement, "name", "Nearest component")); + reference.addActionListener(e -> { + String selected = reference.getSelectedString(); + document.setAttribute("guidedReferenceTarget", "Nearest component".equals(selected) ? null : selected); + }); + fields.add(reference); + + fields.add(fieldLabel("Horizontal size policy")); + Picker horizontalPolicy = stringPicker(new String[]{"Preferred", "Fixed", "Fill parent", "Match reference"}, + policyLabel(GuidedLayoutSupport.horizontalPolicy(element))); + horizontalPolicy.addActionListener(e -> applyGuidedSizePolicy(true, policyValue(horizontalPolicy.getSelectedString()))); + fields.add(horizontalPolicy); + fields.add(fieldLabel("Vertical size policy")); + Picker verticalPolicy = stringPicker(new String[]{"Preferred", "Fixed", "Fill parent", "Match reference"}, + policyLabel(GuidedLayoutSupport.verticalPolicy(element))); + verticalPolicy.addActionListener(e -> applyGuidedSizePolicy(false, policyValue(verticalPolicy.getSelectedString()))); + fields.add(verticalPolicy); + + fields.add(fieldLabel("Align to reference")); + Container align = new Container(new GridLayout(2, 3)); + align.add(layeredAction("Left", "alignLeft")); + align.add(layeredAction("H center", "alignHCenter")); + align.add(layeredAction("Right", "alignRight")); + align.add(layeredAction("Top", "alignTop")); + align.add(layeredAction("Baseline", "alignBaseline")); + align.add(layeredAction("Bottom", "alignBottom")); + fields.add(align); + Container actions = new Container(new GridLayout(2, 2)); + actions.add(layeredAction("Same width", "matchWidth")); + actions.add(layeredAction("Same height", "matchHeight")); + actions.add(layeredAction("Fill width", "fillWidth")); + actions.add(layeredAction("Fill height", "fillHeight")); + fields.add(actions); + fields.add(propertyField("Advanced insets (top right bottom left)", "layeredInsets", "auto auto auto auto")); + } else if ("TableLayout".equals(document.parentLayout(element))) { + fields.add(numericPropertyField("Table row", "tableRow", "0", 0, 99)); + fields.add(numericPropertyField("Table column", "tableColumn", "0", 0, 99)); + fields.add(numericPropertyField("Horizontal span", "tableHorizontalSpan", "1", 1, 100)); + fields.add(numericPropertyField("Vertical span", "tableVerticalSpan", "1", 1, 100)); + fields.add(numericPropertyField("Column width %", "tableWidth", "-1", -1, 100)); + fields.add(numericPropertyField("Row height %", "tableHeight", "-1", -1, 100)); + } + fields.add(new SpanLabel("Guided Layout is the default free-form designer. Drag edges or corners to resize. Blue guides align edges and centers; the baseline guide keeps text aligned. Size policies remain responsive when the form changes size.", "BuilderHelp")); + return fields; + } + + private Button layeredAction(String label, String action) { + Button button = new Button(label); + button.setUIID("BuilderSecondaryAction"); + button.addActionListener(e -> applyLayeredAction(action)); + return button; + } + + void applySelectionAction(String action) { + normalizeSelection(); + if (selectedElements.isEmpty() || !selectedElementsShareGuidedParent()) { + setStatus("Layout actions require components in the same Guided Layout container"); + return; + } + Element anchor = document.selected(); + if (anchor == null || !selectedElements.contains(anchor)) return; + if (!"disconnect".equals(action) && selectedElements.size() < 2) return; + document.beginTransaction(); + try { + if ("disconnect".equals(action)) { + for (Element element : new ArrayList(selectedElements)) disconnectGuidedElement(element); + } else { + for (Element element : new ArrayList(selectedElements)) { + if (element != anchor) applyLayeredRelationship(element, anchor, action); + } + } + document.select(anchor); + } finally { + document.endTransaction(); + } + recordAction("multi_selection_action", "action", action, "count", Integer.valueOf(selectedElements.size()), + "anchor", value(anchor, "name", "component")); + setStatus("disconnect".equals(action) ? "Disconnected " + selectedElements.size() + " components from layout relationships" + : labelForLayeredAction(action) + " using reference " + value(anchor, "name", "component") + + " across " + selectedElements.size() + " components"); + if (workspace != null) refreshEditor(); + } + + private void applyLayeredRelationship(Element element, Element referenceElement, String action) { + Component component = componentForElement(canvasHost, element); + applyLayeredRelationship(document, element, referenceElement, action, component); + } + + private void applyLayeredRelationship(GuiDocument targetDocument, Element element, + Element referenceElement, String action, Component component) { + if (component == null || referenceElement == null) return; + String[] insets = GuidedLayoutSupport.insetValues(element); + String[] refs = GuidedLayoutSupport.referenceNames(element); + String[] positions = GuidedLayoutSupport.referencePositions(element); + String referenceName = value(referenceElement, "name", "-"); + targetDocument.select(element); + if ("alignLeft".equals(action)) { + insets[3] = "0px"; insets[1] = "auto"; refs[3] = referenceName; refs[1] = "-"; positions[3] = "0"; + targetDocument.setAttribute("guidedHorizontalAnchor", null); + } else if ("alignHCenter".equals(action)) { + insets[3] = "0%"; insets[1] = "auto"; refs[3] = referenceName; refs[1] = "-"; positions[3] = "0.5"; + targetDocument.setAttribute("guidedHorizontalAnchor", "0.5"); + } else if ("alignRight".equals(action)) { + insets[3] = "auto"; insets[1] = "0px"; refs[3] = "-"; refs[1] = referenceName; positions[1] = "0"; + targetDocument.setAttribute("guidedHorizontalAnchor", null); + } else if ("alignTop".equals(action)) { + insets[0] = "0px"; insets[2] = "auto"; refs[0] = referenceName; refs[2] = "-"; positions[0] = "0"; + targetDocument.setAttribute("guidedVerticalAnchor", null); + } else if ("alignBaseline".equals(action)) { + insets[0] = "baseline"; insets[2] = "auto"; refs[0] = referenceName; refs[2] = "-"; positions[0] = "0"; + targetDocument.setAttribute("guidedVerticalSize", GuidedLayoutSupport.PREFERRED); + targetDocument.setAttribute("guidedPreferredHeight", null); + } else if ("alignBottom".equals(action)) { + insets[0] = "auto"; insets[2] = "0px"; refs[0] = "-"; refs[2] = referenceName; positions[2] = "0"; + targetDocument.setAttribute("guidedVerticalAnchor", null); + } else if ("matchWidth".equals(action)) { + applyMatchedWidth(referenceElement, relationshipReferencePreview(targetDocument, referenceElement), + insets, refs, positions, component.getAbsoluteX(), component.getWidth()); + targetDocument.setAttribute("guidedMatchWidth", referenceName); + targetDocument.setAttribute("guidedHorizontalSize", GuidedLayoutSupport.MATCH); + } else if ("matchHeight".equals(action)) { + applyMatchedHeight(referenceElement, relationshipReferencePreview(targetDocument, referenceElement), + insets, refs, positions, component.getAbsoluteY(), component.getHeight()); + targetDocument.setAttribute("guidedMatchHeight", referenceName); + targetDocument.setAttribute("guidedVerticalSize", GuidedLayoutSupport.MATCH); + } + targetDocument.setAttribute("layeredInsets", GuidedLayoutSupport.joinInsets(insets[0], insets[1], insets[2], insets[3])); + targetDocument.setAttribute("guidedReferences", GuidedLayoutSupport.joinReferences(refs[0], refs[1], refs[2], refs[3])); + targetDocument.setAttribute("guidedReferencePositions", GuidedLayoutSupport.joinPositions( + positions[0], positions[1], positions[2], positions[3])); + } + + private Component relationshipReferencePreview(GuiDocument targetDocument, Element referenceElement) { + Element liveReference = targetDocument == document ? referenceElement + : findElementNamed(document, value(referenceElement, "name", "")); + return componentForElement(canvasHost, liveReference); + } + + private void disconnectGuidedElement(Element element) { + Component component = componentForElement(canvasHost, element); + Element parentElement = document.parentOf(element); + Component parentComponent = componentForElement(canvasHost, parentElement); + if (component == null || !(parentComponent instanceof Container parent)) return; + freezeGuidedElement(element, parent, component, component.getAbsoluteX(), component.getAbsoluteY()); + } + + private void freezeGuidedElement(Element element, Container parent, Component component, int absoluteX, int absoluteY) { + int left = Math.max(0, absoluteX - contentX(parent) - component.getStyle().getMarginLeftNoRTL()); + int top = Math.max(0, absoluteY - contentY(parent) - component.getStyle().getMarginTop()); + document.select(element); + document.setAttribute("layeredInsets", top + "px auto auto " + left + "px"); + document.setAttribute("guidedReferences", "- - - -"); + document.setAttribute("guidedReferencePositions", "0 0 0 0"); + document.setAttribute("guidedHorizontalAnchor", null); + document.setAttribute("guidedVerticalAnchor", null); + document.setAttribute("guidedMatchWidth", null); + document.setAttribute("guidedMatchHeight", null); + document.setAttribute("guidedHorizontalSize", GuidedLayoutSupport.FIXED); + document.setAttribute("guidedVerticalSize", GuidedLayoutSupport.FIXED); + document.setAttribute("guidedPreferredWidth", String.valueOf(Math.max(1, component.getWidth()))); + document.setAttribute("guidedPreferredHeight", String.valueOf(Math.max(1, component.getHeight()))); + } + + private void moveSelectedInParent(int delta) { + if (reorderSelectedInParent(delta)) refreshEditor(); + } + + boolean reorderSelectedInParent(int delta) { + Element selected = document.selected(); + Element parent = document.parentOf(selected); + boolean moved = false; + document.beginTransaction(); + try { + if (!document.moveSelectedBy(delta)) return false; + moved = true; + if (parent != null && "TableLayout".equals(value(parent, "layout", "BoxLayout"))) { + normalizeTableCells(parent); + document.select(selected); + } + } finally { + document.endTransaction(); + } + return moved; + } + + private void applyLayeredAction(String action) { + Element element = document.selected(); + Element parentElement = document.parentOf(element); + Component component = componentForElement(canvasHost, element); + Component parentComponent = componentForElement(canvasHost, parentElement); + if (component == null || !(parentComponent instanceof Container parent)) return; + int leftPx = component.getAbsoluteX() - parent.getAbsoluteX(); + int topPx = component.getAbsoluteY() - parent.getAbsoluteY(); + int width = component.getWidth(); + int height = component.getHeight(); + Component reference = guidedReferenceComponent(element, parent, component); + Element referenceElement = reference == null ? null : (Element) reference.getClientProperty("gui.element"); + if ((action.startsWith("align") || action.startsWith("match")) && referenceElement == null) { + setStatus("Add another component before creating an alignment relationship"); + return; + } + String[] insets = GuidedLayoutSupport.insetValues(element); + String[] refs = GuidedLayoutSupport.referenceNames(element); + String[] positions = GuidedLayoutSupport.referencePositions(element); + String referenceName = referenceElement == null ? "-" : value(referenceElement, "name", "-"); + document.beginTransaction(); + try { + if ("alignLeft".equals(action)) { + insets[3] = "0px"; insets[1] = "auto"; refs[3] = referenceName; refs[1] = "-"; positions[3] = "0"; + document.setAttribute("guidedHorizontalAnchor", null); + } else if ("alignHCenter".equals(action)) { + insets[3] = "0%"; insets[1] = "auto"; refs[3] = referenceName; refs[1] = "-"; positions[3] = "0.5"; + document.setAttribute("guidedHorizontalAnchor", "0.5"); + } else if ("alignRight".equals(action)) { + insets[3] = "auto"; insets[1] = "0px"; refs[3] = "-"; refs[1] = referenceName; positions[1] = "0"; + document.setAttribute("guidedHorizontalAnchor", null); + } else if ("alignTop".equals(action)) { + insets[0] = "0px"; insets[2] = "auto"; refs[0] = referenceName; refs[2] = "-"; positions[0] = "0"; + document.setAttribute("guidedVerticalAnchor", null); + } else if ("alignBaseline".equals(action)) { + insets[0] = "baseline"; insets[2] = "auto"; refs[0] = referenceName; refs[2] = "-"; positions[0] = "0"; + document.setAttribute("guidedVerticalSize", GuidedLayoutSupport.PREFERRED); + document.setAttribute("guidedPreferredHeight", null); + } else if ("alignBottom".equals(action)) { + insets[0] = "auto"; insets[2] = "0px"; refs[0] = "-"; refs[2] = referenceName; positions[2] = "0"; + document.setAttribute("guidedVerticalAnchor", null); + } else if ("matchWidth".equals(action)) { + applyMatchedWidth(referenceElement, insets, refs, positions, component.getAbsoluteX(), component.getWidth()); + document.setAttribute("guidedMatchWidth", referenceName); + document.setAttribute("guidedHorizontalSize", GuidedLayoutSupport.MATCH); + } else if ("matchHeight".equals(action)) { + applyMatchedHeight(referenceElement, insets, refs, positions, component.getAbsoluteY(), component.getHeight()); + document.setAttribute("guidedMatchHeight", referenceName); + document.setAttribute("guidedVerticalSize", GuidedLayoutSupport.MATCH); + } else if ("fillWidth".equals(action)) { + insets[3] = Math.max(0, component.getAbsoluteX() - contentX(parent)) + "px"; + insets[1] = Math.max(0, contentX(parent) + contentWidth(parent) - component.getAbsoluteX() - component.getWidth()) + "px"; + refs[3] = refs[1] = "-"; + document.setAttribute("guidedHorizontalSize", GuidedLayoutSupport.FILL); + } else if ("fillHeight".equals(action)) { + insets[0] = Math.max(0, component.getAbsoluteY() - contentY(parent)) + "px"; + insets[2] = Math.max(0, contentY(parent) + contentHeight(parent) - component.getAbsoluteY() - component.getHeight()) + "px"; + refs[0] = refs[2] = "-"; + document.setAttribute("guidedVerticalSize", GuidedLayoutSupport.FILL); + } + document.setAttribute("layeredInsets", GuidedLayoutSupport.joinInsets(insets[0], insets[1], insets[2], insets[3])); + document.setAttribute("guidedReferences", GuidedLayoutSupport.joinReferences(refs[0], refs[1], refs[2], refs[3])); + document.setAttribute("guidedReferencePositions", GuidedLayoutSupport.joinPositions(positions[0], positions[1], positions[2], positions[3])); + } finally { + document.endTransaction(); + } + setStatus(labelForLayeredAction(action) + (reference == null ? "" : " using " + reference.getName().replace("preview.", ""))); + refreshEditor(); + } + + private void applyGuidedSizePolicy(boolean horizontal, String policy) { + Element element = document.selected(); + Element parentElement = document.parentOf(element); + Component component = componentForElement(canvasHost, element); + Component parentComponent = componentForElement(canvasHost, parentElement); + if (component == null || !(parentComponent instanceof Container parent)) return; + Component reference = guidedReferenceComponent(element, parent, component); + Element referenceElement = reference == null ? null : (Element) reference.getClientProperty("gui.element"); + if (GuidedLayoutSupport.MATCH.equals(policy) && referenceElement == null) { + setStatus("Match size needs another component as its reference"); + return; + } + String[] insets = GuidedLayoutSupport.insetValues(element); + String[] refs = GuidedLayoutSupport.referenceNames(element); + String[] positions = GuidedLayoutSupport.referencePositions(element); + document.beginTransaction(); + try { + if (horizontal) { + if (GuidedLayoutSupport.PREFERRED.equals(policy) || GuidedLayoutSupport.FIXED.equals(policy)) { + insets[3] = Math.max(0, component.getAbsoluteX() - contentX(parent)) + "px"; + insets[1] = "auto"; refs[3] = refs[1] = "-"; + document.setAttribute("guidedPreferredWidth", GuidedLayoutSupport.FIXED.equals(policy) + ? String.valueOf(component.getWidth()) : null); + } else if (GuidedLayoutSupport.FILL.equals(policy)) { + insets[3] = Math.max(0, component.getAbsoluteX() - contentX(parent)) + "px"; + insets[1] = Math.max(0, contentX(parent) + contentWidth(parent) - component.getAbsoluteX() - component.getWidth()) + "px"; + refs[3] = refs[1] = "-"; + } else { + applyMatchedWidth(referenceElement, insets, refs, positions, component.getAbsoluteX(), component.getWidth()); + document.setAttribute("guidedMatchWidth", value(referenceElement, "name", "")); + } + document.setAttribute("guidedHorizontalSize", policy); + } else { + if (GuidedLayoutSupport.PREFERRED.equals(policy) || GuidedLayoutSupport.FIXED.equals(policy)) { + insets[0] = Math.max(0, component.getAbsoluteY() - contentY(parent)) + "px"; + insets[2] = "auto"; refs[0] = refs[2] = "-"; + document.setAttribute("guidedPreferredHeight", GuidedLayoutSupport.FIXED.equals(policy) + ? String.valueOf(component.getHeight()) : null); + } else if (GuidedLayoutSupport.FILL.equals(policy)) { + insets[0] = Math.max(0, component.getAbsoluteY() - contentY(parent)) + "px"; + insets[2] = Math.max(0, contentY(parent) + contentHeight(parent) - component.getAbsoluteY() - component.getHeight()) + "px"; + refs[0] = refs[2] = "-"; + } else { + applyMatchedHeight(referenceElement, insets, refs, positions, component.getAbsoluteY(), component.getHeight()); + document.setAttribute("guidedMatchHeight", value(referenceElement, "name", "")); + } + document.setAttribute("guidedVerticalSize", policy); + } + document.setAttribute("layeredInsets", GuidedLayoutSupport.joinInsets(insets[0], insets[1], insets[2], insets[3])); + document.setAttribute("guidedReferences", GuidedLayoutSupport.joinReferences(refs[0], refs[1], refs[2], refs[3])); + document.setAttribute("guidedReferencePositions", GuidedLayoutSupport.joinPositions(positions[0], positions[1], positions[2], positions[3])); + } finally { + document.endTransaction(); + } + refreshEditor(); + setStatus((horizontal ? "Horizontal" : "Vertical") + " size policy: " + policyLabel(policy)); + } + + private String[] guidedReferenceNames(Element element) { + Element parent = document.parentOf(element); + List names = new ArrayList<>(); + names.add("Nearest component"); + for (Element child : componentChildren(parent)) if (child != element) names.add(value(child, "name", value(child, "type", "component"))); + return names.toArray(new String[names.size()]); + } + + private Element guidedReferenceElement(Element element) { + Element parent = document.parentOf(element); + return namedSibling(parent, value(element, "guidedReferenceTarget", "")); + } + + private Component guidedReferenceComponent(Element element, Container parent, Component component) { + Element explicit = guidedReferenceElement(element); + Component result = explicit == null ? null : componentForElement(canvasHost, explicit); + return result == null ? nearestSibling(parent, component) : result; + } + + private String policyLabel(String policy) { + if (GuidedLayoutSupport.FIXED.equals(policy)) return "Fixed"; + if (GuidedLayoutSupport.FILL.equals(policy)) return "Fill parent"; + if (GuidedLayoutSupport.MATCH.equals(policy)) return "Match reference"; + return "Preferred"; + } + + private String policyValue(String label) { + if ("Fixed".equals(label)) return GuidedLayoutSupport.FIXED; + if ("Fill parent".equals(label)) return GuidedLayoutSupport.FILL; + if ("Match reference".equals(label)) return GuidedLayoutSupport.MATCH; + return GuidedLayoutSupport.PREFERRED; + } + + private Component nearestSibling(Container parent, Component source) { + Component nearest = null; + long best = Long.MAX_VALUE; + int cx = source.getAbsoluteX() + source.getWidth() / 2; + int cy = source.getAbsoluteY() + source.getHeight() / 2; + for (int i = 0; i < parent.getComponentCount(); i++) { + Component candidate = parent.getComponentAt(i); + if (candidate == source || candidate.getClientProperty("gui.element") == null) continue; + long dx = candidate.getAbsoluteX() + candidate.getWidth() / 2 - cx; + long dy = candidate.getAbsoluteY() + candidate.getHeight() / 2 - cy; + long distance = dx * dx + dy * dy; + if (distance < best) { best = distance; nearest = candidate; } + } + return nearest; + } + + private String percent(int pixels, int total) { + return Math.max(0, Math.min(100, Math.round(pixels * 100f / Math.max(1, total)))) + "%"; + } + + private String labelForLayeredAction(String action) { + if ("alignLeft".equals(action)) return "Aligned left edges"; + if ("alignHCenter".equals(action)) return "Aligned horizontal centers"; + if ("alignRight".equals(action)) return "Aligned right edges"; + if ("alignTop".equals(action)) return "Aligned top edges"; + if ("alignBaseline".equals(action)) return "Aligned text baselines"; + if ("alignBottom".equals(action)) return "Aligned bottom edges"; + if ("fillWidth".equals(action)) return "Filled available width"; + if ("fillHeight".equals(action)) return "Filled available height"; + if ("matchWidth".equals(action)) return "Matched nearest width"; + return "Matched nearest height"; + } + + private Component eventsTab(Element element) { + Container fields = inspectorFields(); + fields.add(propertyField("Action handler", "actionEvent")); + fields.add(new SpanLabel("Create or edit the selected handler here. The embedded Java editor keeps generated UI code separate and adds a handler stub only when it is missing.", "BuilderHelp")); + Button code = new Button("Edit event handler", material(FontImage.MATERIAL_CODE, "BuilderInlineIcon")); + code.setUIID("BuilderPrimaryAction"); + code.addActionListener(e -> openEventHandler()); + fields.add(code); + return fields; + } + + private Container inspectorFields() { + Container fields = new Container(BoxLayout.y()); + fields.setUIID("BuilderInspectorFields"); + fields.setScrollableY(true); + return fields; + } + + private Component propertyField(String label, String attribute) { + return propertyField(label, attribute, document.attribute(attribute, "")); + } + + /** + * The component name is the identity that Guided Layout relationships and generated Java + * fields are built from. It is committed on Enter or focus loss rather than on every + * keystroke, is forced to stay unique, and repoints every relationship that referenced the + * previous name in one undo step. + */ + private Component nameField() { + Element target = document.selected(); + TextField field = new TextField(value(target, "name", "")); + field.setUIID("BuilderField"); + field.getSemantics().setIdentifier("guibuilder.property.name").setLabel("Name"); + Runnable commit = () -> { + if (document == null || !document.containsElement(target)) return; + String previous = value(target, "name", ""); + String requested = field.getText() == null ? "" : field.getText().trim(); + if (requested.equals(previous)) return; + if (requested.length() == 0) { + field.setText(previous); + setStatus("A component name cannot be empty"); + return; + } + document.select(target); + String applied = document.renameSelected(requested); + recordAction("property_changed", "component", previous, "property", "name", "value", applied); + if (applied != null && !applied.equals(requested)) { + field.setText(applied); + setStatus("Renamed to " + applied + " because " + requested + " is already used"); + } else { + setStatus("Renamed " + previous + " to " + applied + " • relationships updated"); + } + scheduleDesignerRefresh(); + }; + field.addActionListener(e -> commit.run()); + field.addFocusListener(new FocusListener() { + @Override public void focusGained(Component component) { } + @Override public void focusLost(Component component) { commit.run(); } + }); + return BoxLayout.encloseY(fieldLabel("Name"), field); + } + + private Component propertyField(String label, String attribute, String fallback) { + Element target = document.selected(); + TextField field = new TextField(document.attribute(attribute, fallback)); + field.setUIID("BuilderField"); + field.getSemantics().setIdentifier("guibuilder.property." + attribute).setLabel(label); + field.addDataChangedListener((type, index) -> update(target, attribute, field.getText())); + return BoxLayout.encloseY(fieldLabel(label), field); + } + + private Component numericPropertyField(String label, String attribute, String fallback, int minimum, int maximum) { + Element target = document.selected(); + TextField field = new TextField(document.attribute(attribute, fallback)); + field.setUIID("BuilderField"); + field.getSemantics().setIdentifier("guibuilder.property." + attribute).setLabel(label); + Runnable validate = () -> { + Integer parsed = parseInteger(field.getText()); + boolean valid = parsed != null && parsed >= minimum && parsed <= maximum; + field.setUIID(valid ? "BuilderField" : "BuilderFieldError"); + field.repaint(); + }; + Runnable commit = () -> { + Integer parsed = parseInteger(field.getText()); + if (parsed == null || parsed < minimum || parsed > maximum) { + field.setText(document.attribute(attribute, fallback)); + field.setUIID("BuilderField"); + setStatus(label + " must be a number from " + minimum + " to " + maximum); + return; + } + String normalized = String.valueOf(parsed); + if (!normalized.equals(document.attribute(attribute, fallback))) update(target, attribute, normalized); + }; + field.addDataChangedListener((type, index) -> validate.run()); + field.addActionListener(e -> commit.run()); + field.addFocusListener(new com.codename1.ui.events.FocusListener() { + public void focusGained(Component component) { } + public void focusLost(Component component) { commit.run(); } + }); + return BoxLayout.encloseY(fieldLabel(label), field); + } + + static Integer parseInteger(String value) { + try { + if (value == null || value.length() == 0) return null; + int start = value.charAt(0) == '-' ? 1 : 0; + if (start == value.length()) return null; + for (int i = start; i < value.length(); i++) { + char ch = value.charAt(i); + if (ch < '0' || ch > '9') return null; + } + return Integer.valueOf(value); + } catch (NumberFormatException ex) { + return null; + } + } + + private Component booleanProperty(String label, String attribute, boolean fallback) { + Element target = document.selected(); + CheckBox field = new CheckBox(label); + field.setUIID("BuilderCheck"); + field.getSemantics().setIdentifier("guibuilder.property." + attribute).setLabel(label); + field.setSelected("true".equals(document.attribute(attribute, String.valueOf(fallback)))); + field.addActionListener(e -> update(target, attribute, String.valueOf(field.isSelected()))); + return field; + } + + private Component pickerProperty(String label, String attribute, String[] values, String fallback) { + Element target = document.selected(); + Picker picker = stringPicker(values, document.attribute(attribute, fallback)); + picker.getSemantics().setIdentifier("guibuilder.property." + attribute).setLabel(label); + picker.addActionListener(e -> update(target, attribute, picker.getSelectedString())); + return BoxLayout.encloseY(fieldLabel(label), picker); + } + + private Label fieldLabel(String text) { return new Label(text, "BuilderFieldLabel"); } + + private Picker stringPicker(String[] values, String selected) { + Picker picker = new Picker(); + picker.setType(Display.PICKER_TYPE_STRINGS); + picker.setStrings(values); + picker.setSelectedString(selected); + picker.setUIID("BuilderPicker"); + return picker; + } + + private void update(String attribute, String value) { + update(document.selected(), attribute, value); + } + + private void update(Element element, String attribute, String value) { + document.select(element); + document.setAttribute(attribute, value); + recordAction("property_changed", "component", value(element, "name", value(element, "type", "component")), + "property", attribute, "value", value); + if ("layout".equals(attribute) || "boxLayoutAxis".equals(attribute) + || "gridLayoutRows".equals(attribute) || "gridLayoutColumns".equals(attribute) + || "tableLayoutRows".equals(attribute) || "tableLayoutColumns".equals(attribute) + || "tableRow".equals(attribute) || "tableColumn".equals(attribute) + || "tableHorizontalSpan".equals(attribute) || "tableVerticalSpan".equals(attribute) + || "tableWidth".equals(attribute) || "tableHeight".equals(attribute) + || "layoutConstraint".equals(attribute)) { + refreshEditor(); + } else { + updatePreviewAttribute(element, attribute, value); + } + setStatus("Modified • " + relativeFormName(document.path())); + } + + private void updatePreviewAttribute(Element element, String attribute, String value) { + if (element == document.root() && "title".equals(attribute) && formTitlePreview != null) { + formTitlePreview.setText(value == null || value.length() == 0 ? "Untitled Form" : value); + formTitlePreview.getParent().revalidate(); + return; + } + Component component = componentForElement(canvasHost, element); + if (component == null) return; + if ("text".equals(attribute)) { + if (component instanceof SpanLabel span) span.setText(value); + else if (component instanceof Label label) label.setText(value); + else if (component instanceof TextArea area) area.setText(value); + } else if ("hint".equals(attribute) && component instanceof TextArea area) { + area.setHint(value); + } else if ("enabled".equals(attribute)) { + component.setEnabled(!"false".equals(value)); + } else if ("visible".equals(attribute)) { + component.setVisible(!"false".equals(value)); + } else if ("rtl".equals(attribute)) { + component.setRTL("true".equals(value)); + } else if ("name".equals(attribute)) { + component.setName("preview." + value); + } else if ("gap".equals(attribute) && component instanceof Label label) { + label.setGap(integer(value, label.getGap())); + } else if ("alignment".equals(attribute)) { + int alignment = "center".equals(value) ? Component.CENTER : "right".equals(value) ? Component.RIGHT : Component.LEFT; + if (component instanceof Label label) label.setAlignment(alignment); + else if (component instanceof TextArea area) area.setAlignment(alignment); + } else if ("tickerEnabled".equals(attribute) && component instanceof Label label) { + label.setTickerEnabled("true".equals(value)); + } else if ("toggle".equals(attribute) && component instanceof Button button) { + button.setToggle("true".equals(value)); + } else if ("selected".equals(attribute) && component instanceof CheckBox check) { + check.setSelected("true".equals(value)); + } else if ("columns".equals(attribute) && component instanceof TextArea area) { + area.setColumns(integer(value, area.getColumns())); + } else if ("rows".equals(attribute) && component instanceof TextArea area) { + area.setRows(integer(value, area.getRows())); + } else if ("maxSize".equals(attribute) && component instanceof TextArea area) { + area.setMaxSize(integer(value, area.getMaxSize())); + } else if ("editable".equals(attribute) && component instanceof TextArea area) { + area.setEditable(!"false".equals(value)); + } else if ("growByContent".equals(attribute) && component instanceof TextArea area) { + area.setGrowByContent(!"false".equals(value)); + } else if ("scrollableX".equals(attribute) && component instanceof Container container) { + container.setScrollableX("true".equals(value)); + } else if ("scrollableY".equals(attribute) && component instanceof Container container) { + container.setScrollableY("true".equals(value)); + } else if ("minValue".equals(attribute) && component instanceof com.codename1.ui.Slider slider) { + slider.setMinValue(integer(value, slider.getMinValue())); + } else if ("maxValue".equals(attribute) && component instanceof com.codename1.ui.Slider slider) { + slider.setMaxValue(integer(value, slider.getMaxValue())); + } else if ("progress".equals(attribute) && component instanceof com.codename1.ui.Slider slider) { + slider.setProgress(integer(value, slider.getProgress())); + } else if ("editable".equals(attribute) && component instanceof com.codename1.ui.Slider slider) { + slider.setEditable("true".equals(value)); + } else if ("infinite".equals(attribute) && component instanceof com.codename1.ui.Slider slider) { + slider.setInfinite("true".equals(value)); + } else if ("selectedIndex".equals(attribute) && component instanceof Tabs tabs && tabs.getTabCount() > 0) { + tabs.setSelectedIndex(Math.max(0, Math.min(tabs.getTabCount() - 1, integer(value, 0))), false); + } else if ("tabPlacement".equals(attribute) && component instanceof Tabs tabs) { + tabs.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 + && component.getParent().getLayout() instanceof LayeredLayout layered) { + try { layered.setInsets(component, value); } catch (RuntimeException ignored) { } + } else if ("uiid".equals(attribute)) { + component.setUIID(value == null || value.length() == 0 ? document.effectiveUiid(element) : value); + refreshSinglePreviewTheme(component); + } + if (component.getParent() != null) component.getParent().revalidate(); + component.repaint(); + } + + private void refreshSinglePreviewTheme(Component component) { + try { + if (projectTheme != null) { + UIManager.getInstance().setThemeProps(projectTheme); + component.refreshTheme(); + } + } finally { + if (builderTheme != null) UIManager.getInstance().setThemeProps(builderTheme); + } + ComponentPreviewFactory.stabilizeDesignStyles(component); + } + + private int integer(String value, int fallback) { + try { return Integer.parseInt(value == null || value.length() == 0 ? String.valueOf(fallback) : value); } + catch (NumberFormatException ex) { return fallback; } + } + + private void addComponent(String type) { + if (document == null) return; + document.addComponent(type); + refreshEditor(); + setStatus("Added " + type); + } + + private void editSelectedContent() { + if (document == null) return; + finishInlineEditor(); + Element target = document.selected(); + String type = value(target, "type", "Component"); + String attribute = "Form".equals(type) ? "title" : "text"; + Component source = target == document.root() ? formTitlePreview : componentForElement(canvasHost, target); + if (source == null || workspace == null) return; + Container parent = source.getParent(); + if (parent == null) return; + final int editorWidth = Math.max(1, source.getWidth()); + final int editorHeight = Math.max(1, source.getHeight()); + TextField editor = new TextField(value(target, attribute, "")) { + @Override protected Dimension calcPreferredSize() { + Dimension natural = super.calcPreferredSize(); + natural.setWidth(Math.max(editorWidth, natural.getWidth())); + natural.setHeight(Math.max(editorHeight, natural.getHeight())); + return natural; + } + }; + editor.setUIID("BuilderInlineEditor"); + editor.setSingleLineTextArea(true); + editor.addDataChangedListener((changeType, index) -> { + document.select(target); + document.setAttribute(attribute, editor.getText()); + updatePreviewAttribute(target, attribute, editor.getText()); + setStatus("Editing " + attribute + " in place • Enter or click away to finish"); + }); + editor.setDoneListener(e -> finishInlineEditor()); + editor.addFocusListener(new FocusListener() { + @Override public void focusGained(Component cmp) { } + @Override public void focusLost(Component cmp) { + Display.getInstance().callSerially(() -> finishInlineEditor()); + } + }); + inlineEditor = editor; + inlineEditorSource = source; + inlineEditorParent = parent; + inlineEditorTarget = target; + inlineEditorAttribute = attribute; + parent.replace(source, editor, null); + parent.revalidate(); + Display.getInstance().callSerially(() -> { + if (inlineEditor == editor) { + editor.requestFocus(); + editor.startEditingAsync(); + } + }); + } + + private void finishInlineEditor() { + if (inlineEditor == null || finishingInlineEditor) return; + finishingInlineEditor = true; + TextField editor = inlineEditor; + inlineEditor = null; + Component source = inlineEditorSource; + Container parent = inlineEditorParent; + Element target = inlineEditorTarget; + String attribute = inlineEditorAttribute; + inlineEditorSource = null; + inlineEditorParent = null; + inlineEditorTarget = null; + inlineEditorAttribute = null; + Runnable complete = () -> completeInlineEditor(editor, source, parent, target, attribute); + if (editor.isEditing()) editor.stopEditing(() -> Display.getInstance().callSerially(complete)); + else complete.run(); + } + + private void completeInlineEditor(TextField editor, Component source, Container parent, + Element target, String attribute) { + if (parent != null && source != null && editor.getParent() == parent) { + parent.replace(editor, source, null); + } else if (editor.getParent() != null) { + Container actualParent = editor.getParent(); + actualParent.removeComponent(editor); + } + commitInlineValue(target, attribute, editor.getText()); + if (parent != null) parent.revalidate(); + finishingInlineEditor = false; + if (document != null && inspectorHost != null) refreshInspector(); + } + + void commitInlineValue(Element target, String attribute, String value) { + if (document == null || target == null || attribute == null) return; + document.select(target); + document.setAttribute(attribute, value); + updatePreviewAttribute(target, attribute, value); + } + + private void copy() { + if (document != null) clipboardXml = document.copySelectedXml(); + } + + private void undo() { + finishInlineEditor(); + if (document != null && document.undo()) { + refreshEditor(); + recordAction("undo", "form", relativeFormName(document.path())); + setStatus("Undo"); + } + } + + private void redo() { + finishInlineEditor(); + if (document != null && document.redo()) { + refreshEditor(); + recordAction("redo", "form", relativeFormName(document.path())); + setStatus("Redo"); + } + } + + private void cut() { + if (document == null || document.selected() == document.root()) return; + copy(); + document.deleteSelected(); + refreshEditor(); + } + + private void paste() { + if (document != null && document.pasteXml(clipboardXml) != null) refreshEditor(); + } + + private void deleteSelection() { + if (document != null && document.deleteSelected()) refreshEditor(); + } + + private void toggleDarkMode() { + darkMode = !darkMode; + Preferences.set("guibuilder.darkMode", darkMode); + Display.getInstance().setDarkMode(Boolean.valueOf(darkMode)); + UIManager.getInstance().setThemeProps(builderTheme); + workspace.refreshTheme(); + refreshEditor(); + } + + private void save() { + if (document == null) return; + try { + ProjectIO.write(document.path(), document.toXml()); + document.markSaved(); + recordAction("saved", "form", relativeFormName(document.path())); + setStatus("Saved " + relativeFormName(document.path())); + ToastBar.showMessage("GUI form saved", FontImage.MATERIAL_CHECK); + } catch (IOException ex) { + ToastBar.showErrorMessage("Save failed: " + ex.getMessage()); + } + } + + private void refreshProject() { + guiFiles = ProjectIO.findGuiFiles(binding.guiDir()); + refreshForms(); + if (document != null) openForm(document.path()); + } + + private void showEmptyProject() { + canvasHost.removeAll(); + canvasHost.add(BorderLayout.CENTER, new SpanLabel("No GUI Builder files were found under src/main/guibuilder. Create one with mvn cn1:create-gui-form -DclassName=com.example.MyForm.", "BuilderEmptyProject")); + canvasHost.revalidate(); + } + + private void setCanvasMode(String mode) { + canvasMode = mode; + refreshEditor(); + setStatus("Preview: " + mode.replace("phone", "phone ").replace("tablet", "tablet ")); + } + + private void openCss() { + if (binding == null || binding.cssFile() == null) return; + try { + refreshEditor(); + String css = ProjectIO.read(binding.cssFile()); + CodeEditor editor = new CodeEditor("css", css); + activeCodeEditor = editor; + lastObservedCss = css; + editor.setTheme(darkMode ? "dark" : "light"); + editor.setEditable(true); + editor.setShowLineNumbers(true); + editor.onReady(editor::focusEditor); + Component stage = canvasHost.getComponentAt(0); + Container editorPane = editorPane("theme.css", editor, + () -> editor.getText(value -> saveCss(value, editor)), this::refreshEditor); + editor.addChangeListener(event -> scheduleLiveCss(editor)); + if (cssLiveTimer != null) cssLiveTimer.cancel(); + cssLiveTimer = com.codename1.ui.util.UITimer.timer(250, true, workspace, () -> { + if (activeCodeEditor != editor || editor.getComponentForm() != workspace) { + if (cssLiveTimer != null) cssLiveTimer.cancel(); + return; + } + editor.getText(value -> { + if (value != null && !value.equals(lastObservedCss)) { + scheduleLiveCss(editor); + } + }); + }); + canvasHost.removeComponent(stage); + SplitPane split = new SplitPane(SplitPane.HORIZONTAL_SPLIT, editorPane, stage, "20%", "50%", "80%"); + canvasHost.removeAll(); + canvasHost.add(BorderLayout.CENTER, split); + canvasHost.revalidate(); + setStatus("Editing CSS • changes compile into the live preview"); + } catch (IOException ex) { + ToastBar.showErrorMessage("Unable to read theme.css: " + ex.getMessage()); + } + } + + private void scheduleLiveCss(CodeEditor editor) { + final int revision = ++cssEditRevision; + com.codename1.ui.util.UITimer.timer(120, false, workspace, () -> { + if (revision != cssEditRevision || activeCodeEditor != editor) return; + editor.getText(value -> { + if (value == null || value.equals(lastObservedCss)) return; + lastObservedCss = value; + applyProjectCss(value, editor); + }); + }); + } + + private void openCompanionSource() { + openSourceEditor(null); + } + + private void openBindingModel() { + if (document == null || binding == null || binding.sourceDir() == null) return; + String formPath = companionSourcePath(); + String modelPath = formPath.substring(0, formPath.length() - 5) + "Model.java"; + try { + // Rebuild the canvas first: opening an editor over an already open one would nest + // split panes and push the design surface out of reach. + refreshEditor(); + String source = ProjectIO.exists(modelPath) ? ProjectIO.read(modelPath) : generatedModelSource(); + CodeEditor editor = new CodeEditor("java", source); + activeCodeEditor = editor; + editor.setTheme(darkMode ? "dark" : "light"); + editor.setEditable(true); + editor.onReady(editor::focusEditor); + Component stage = canvasHost.getComponentCount() == 0 ? new Label() : canvasHost.getComponentAt(0); + Container editorPane = editorPane("Binding model: " + relativeFormName(document.path()) + "Model", + editor, () -> editor.getText(value -> saveSource(modelPath, value)), this::refreshEditor); + canvasHost.removeComponent(stage); + canvasHost.removeAll(); + canvasHost.add(BorderLayout.CENTER, + new SplitPane(SplitPane.HORIZONTAL_SPLIT, editorPane, stage, "20%", "58%", "85%")); + canvasHost.revalidate(); + setStatus("Editing generated PropertyBusinessObject binding model"); + } catch (IOException ex) { + ToastBar.showErrorMessage("Unable to open binding model: " + ex.getMessage()); + } + } + + private void openEventHandler() { + String handler = document == null ? "" : document.attribute("actionEvent", "").trim(); + if (handler.length() == 0) { + ToastBar.showErrorMessage("Enter an action handler name first"); + return; + } + openSourceEditor(handler); + } + + private void openSourceEditor(String handler) { + if (document == null || binding == null || binding.sourceDir() == null) return; + String sourcePath = companionSourcePath(); + try { + // Rebuild the canvas first: opening an editor over an already open one would nest + // split panes and push the design surface out of reach. + refreshEditor(); + String generated = defaultCompanionSource(); + String source = ProjectIO.exists(sourcePath) ? mergeGeneratedSource(ProjectIO.read(sourcePath), generated) : generated; + for (String generatedHandler : generatedHandlers()) source = ensureHandler(source, generatedHandler); + if (handler != null) source = ensureHandler(source, handler); + CodeEditor editor = new CodeEditor("java", source); + activeCodeEditor = editor; + editor.setTheme(darkMode ? "dark" : "light"); + editor.setEditable(true); + editor.setProtectedRegionMarkers("// ", "// "); + int userMarker = source.indexOf("// "); + final int editableOffset = userMarker < 0 ? 0 + : userMarker + "// ".length() + 1; + editor.onReady(() -> { + editor.setCursorPosition(editableOffset); + editor.focusEditor(); + }); + Component stage = canvasHost.getComponentCount() == 0 ? new Label() : canvasHost.getComponentAt(0); + Container editorPane = editorPane(handler == null ? "Companion Java source • edit inside USER CODE markers" : "Handler: " + handler, editor, + () -> editor.getText(value -> saveSourceAndModel(sourcePath, value)), this::refreshEditor); + canvasHost.removeComponent(stage); + SplitPane split = new SplitPane(SplitPane.HORIZONTAL_SPLIT, editorPane, stage, "20%", "58%", "85%"); + canvasHost.removeAll(); + canvasHost.add(BorderLayout.CENTER, split); + canvasHost.revalidate(); + setStatus("Editing " + relativeFormName(document.path()) + " Java behavior"); + } catch (IOException ex) { + ToastBar.showErrorMessage("Unable to open source: " + ex.getMessage()); + } + } + + private Container editorPane(String title, CodeEditor editor, Runnable saveAction, Runnable closeAction) { + Container pane = new Container(new BorderLayout()); + pane.setUIID("BuilderEditorPane"); + Container actions = new Container(new BorderLayout()); + actions.setUIID("BuilderEditorToolbar"); + actions.add(BorderLayout.CENTER, new Label(title, "BuilderEditorTitle")); + Button close = new Button("Close"); + close.setUIID("BuilderSecondaryAction"); + close.addActionListener(e -> closeAction.run()); + Button save = new Button("Save"); + save.setUIID("BuilderPrimaryAction"); + save.addActionListener(e -> saveAction.run()); + actions.add(BorderLayout.WEST, close); + actions.add(BorderLayout.EAST, save); + pane.add(BorderLayout.NORTH, actions); + pane.add(BorderLayout.CENTER, editor); + return pane; + } + + private void saveCss(String css, CodeEditor editor) { + if (!applyProjectCss(css, editor)) return; + try { + ProjectIO.write(binding.cssFile(), css); + setStatus("Saved theme.css • preview updated"); + } catch (IOException ex) { + ToastBar.showErrorMessage("CSS save failed: " + ex.getMessage()); + } + } + + private boolean applyProjectCss(String css, CodeEditor editor) { + try { + MutableResource resources = new MutableResource(); + new CSSThemeCompiler().compile(css, resources, "ProjectTheme"); + projectTheme = resources.getTheme("ProjectTheme"); + normalizeCompiledTheme(projectTheme); + editor.setDiagnostics(new ArrayList()); + refreshProjectThemeOnPreview(); + setStatus("CSS compiled • live preview updated"); + return true; + } catch (RuntimeException ex) { + List diagnostics = new ArrayList<>(); + diagnostics.add(new CodeDiagnostic(1, 1, ex.getMessage() == null ? "CSS compile error" : ex.getMessage())); + editor.setDiagnostics(diagnostics); + setStatus("CSS error • fix the highlighted problem before saving"); + return false; + } + } + + private void refreshProjectThemeOnPreview() { + try { + if (projectTheme != null) { + UIManager.getInstance().setThemeProps(projectTheme); + if (previewRoot != null) previewRoot.refreshTheme(); + if (formToolbarPreview != null) formToolbarPreview.refreshTheme(); + } + } finally { + if (builderTheme != null) UIManager.getInstance().setThemeProps(builderTheme); + } + if (previewRoot != null) ComponentPreviewFactory.stabilizeDesignStyles(previewRoot); + if (formToolbarPreview != null) ComponentPreviewFactory.stabilizeDesignStyles(formToolbarPreview); + } + + private void loadProjectTheme() { + if (binding == null || binding.cssFile() == null) return; + try { + MutableResource resources = new MutableResource(); + new CSSThemeCompiler().compile(ProjectIO.read(binding.cssFile()), resources, "ProjectTheme"); + projectTheme = resources.getTheme("ProjectTheme"); + normalizeCompiledTheme(projectTheme); + } catch (Exception ex) { + projectTheme = null; + setStatus("Project CSS has errors: " + ex.getMessage()); + } + } + + private void saveSource(String path, String source) { + try { + ProjectIO.write(path, source); + setStatus("Saved companion Java source"); + } catch (IOException ex) { + ToastBar.showErrorMessage("Source save failed: " + ex.getMessage()); + } + } + + private void saveSourceAndModel(String path, String source) { + saveSource(path, source); + try { + String modelPath = path.substring(0, path.length() - 5) + "Model.java"; + if (!ProjectIO.exists(modelPath) && !"none".equals(value(document.root(), "bindingStrategy", "properties"))) { + ProjectIO.write(modelPath, generatedModelSource()); + setStatus("Saved form source and created its binding model"); + } else { + setStatus("Saved form source • existing model left unchanged"); + } + } catch (IOException ex) { + ToastBar.showErrorMessage("Model save failed: " + ex.getMessage()); + } + } + + private void normalizeCompiledTheme(Hashtable theme) { + if (theme == null) return; + List keys = new ArrayList<>(); + for (Object key : theme.keySet()) keys.add(key); + for (Object keyObject : keys) { + String key = String.valueOf(keyObject); + Object raw = theme.get(keyObject); + if (key.endsWith("." + Style.FONT) && raw instanceof String font) { + if (font.length() > 1 && font.charAt(0) == '"' && font.charAt(font.length() - 1) == '"') { + font = font.substring(1, font.length() - 1); + } + try { + int style = font.indexOf("Bold") >= 0 ? Font.STYLE_BOLD : Font.STYLE_PLAIN; + if (font.indexOf("Italic") >= 0) style |= Font.STYLE_ITALIC; + theme.put(keyObject, Font.createTrueTypeFont(font, font).derive(Font.getDefaultFont().getHeight(), style)); + } catch (RuntimeException ex) { + theme.put(keyObject, Font.getDefaultFont()); + } + continue; + } + boolean padding = key.endsWith("." + Style.PADDING); + boolean margin = key.endsWith("." + Style.MARGIN); + if ((!padding && !margin) || !(raw instanceof String box)) continue; + String[] values = box.split(","); + if (values.length != 4) continue; + byte[] units = new byte[4]; + StringBuilder normalized = new StringBuilder(); + for (int i = 0; i < values.length; i++) { + String item = values[i].trim(); + byte unit = Style.UNIT_TYPE_PIXELS; + if (item.endsWith("mm")) { unit = Style.UNIT_TYPE_DIPS; item = item.substring(0, item.length() - 2); } + else if (item.endsWith("px")) item = item.substring(0, item.length() - 2); + else if (item.endsWith("%")) { unit = Style.UNIT_TYPE_SCREEN_PERCENTAGE; item = item.substring(0, item.length() - 1); } + units[i] = unit; + if (i > 0) normalized.append(','); + normalized.append(item.length() == 0 ? "0" : item); + } + theme.put(keyObject, normalized.toString()); + String unitKey = key.substring(0, key.lastIndexOf('.') + 1) + (padding ? Style.PADDING_UNIT : Style.MARGIN_UNIT); + theme.put(unitKey, units); + } + } + + private String companionSourcePath() { + String relative = document.path().substring(ProjectIO.fsUrl(binding.guiDir()).length()); + if (relative.startsWith("/")) relative = relative.substring(1); + return binding.sourceDir() + "/" + relative.substring(0, relative.length() - 4) + ".java"; + } + + private String defaultCompanionSource() { + String form = relativeFormName(document.path()); + int dot = form.lastIndexOf('.'); + String packageName = dot < 0 ? "" : form.substring(0, dot); + String className = dot < 0 ? form : form.substring(dot + 1); + String modelName = className + "Model"; + String strategy = value(document.root(), "bindingStrategy", "properties"); + boolean bindingEnabled = !"none".equals(strategy); + boolean annotationBinding = "bindable".equals(strategy); + StringBuilder out = new StringBuilder(); + out.append("// \n"); + if (packageName.length() > 0) out.append("package ").append(packageName).append(";\n\n"); + out.append("import com.codename1.components.SpanLabel;\n") + .append("import com.codename1.ui.*;\n") + .append("import com.codename1.ui.events.ActionEvent;\n") + .append("import com.codename1.ui.geom.Dimension;\n") + .append("import com.codename1.ui.layouts.*;\n") + .append("import com.codename1.ui.table.TableLayout;\n"); + if (bindingEnabled) out.append(annotationBinding + ? "import com.codename1.binding.Binding;\nimport com.codename1.binding.Binders;\n" + : "import com.codename1.properties.UiBinding;\n"); + out.append("\n// Generated live from ").append(relativeFormName(document.path())).append(".gui.\n") + .append("public class ").append(className).append(" extends Form {\n"); + if (bindingEnabled) { + out.append(" private final ").append(modelName).append(" model;\n") + .append(annotationBinding ? " private Binding binding;\n" : " private UiBinding.Binding binding;\n"); + } + for (Element element : document.components()) { + if (element != document.root()) out.append(" private ").append(javaType(element)).append(" ") + .append(javaName(element)).append(";\n"); + } + if (bindingEnabled) { + out.append("\n public ").append(className).append("() { this(new ").append(modelName).append("()); }\n\n") + .append(" public ").append(className).append("(").append(modelName).append(" model) {\n") + .append(" super(\"").append(javaEscape(value(document.root(), "title", className))).append("\", ") + .append(layoutSource(document.root())).append(");\n") + .append(" this.model = model;\n buildUI();\n") + .append(annotationBinding ? " binding = Binders.bind(model, this);\n }\n\n" + : " binding = new UiBinding().bind(model, this);\n }\n\n"); + } else { + out.append("\n public ").append(className).append("() {\n") + .append(" super(\"").append(javaEscape(value(document.root(), "title", className))).append("\", ") + .append(layoutSource(document.root())).append(");\n buildUI();\n }\n\n"); + } + out.append(" private void buildUI() {\n"); + appendGeneratedChildren(out, document.root(), "this", " "); + for (Element command : document.commands()) { + String placement = value(command, "placement", "right"); + String method = "left".equals(placement) ? "addCommandToLeftBar" : "overflow".equals(placement) + ? "addCommandToOverflowMenu" : "side".equals(placement) ? "addCommandToSideMenu" : "addCommandToRightBar"; + out.append(" getToolbar().").append(method).append("(\"") + .append(javaEscape(value(command, "name", "Command"))).append("\", null, this::") + .append(javaIdentifier(value(command, "actionEvent", "onCommand"))).append(");\n"); + } + out.append(" }\n\n"); + if (bindingEnabled) out.append(" public ").append(modelName).append(" getModel() { return model; }\n\n"); + out.append("// \n") + .append("// \n"); + for (String handler : generatedHandlers()) { + out.append(" protected void ").append(handler).append("(ActionEvent event) {\n") + .append(" // Add behavior here.\n }\n\n"); + } + return out.append("// \n") + .append("// \n}\n// \n").toString(); + } + + private void appendGeneratedChildren(StringBuilder out, Element parent, String parentName, String indent) { + for (int i = 0; i < parent.getNumChildren(); i++) { + Object value = parent.getChildAt(i); + if (!(value instanceof Element child) || !"component".equals(child.getTagName())) continue; + String name = javaName(child); + String type = value(child, "type", "Container"); + out.append(indent).append(name).append(" = ").append(componentSource(child)).append(";\n") + .append(indent).append(name).append(".setName(\"").append(javaEscape(value(child, "name", name))).append("\");\n"); + if (child.getAttribute("uiid") != null) out.append(indent).append(name).append(".setUIID(\"") + .append(javaEscape(child.getAttribute("uiid"))).append("\");\n"); + String handler = child.getAttribute("actionEvent"); + if (handler != null && handler.length() > 0 && ("Button".equals(type) || "CheckBox".equals(type) || "RadioButton".equals(type))) { + out.append(indent).append(name).append(".addActionListener(this::").append(javaIdentifier(handler)).append(");\n"); + } + if ("Tabs".equals(value(parent, "type", ""))) { + out.append(indent).append(parentName).append(".addTab(\"").append(javaEscape(value(child, "name", "Tab"))) + .append("\", ").append(name).append(");\n"); + } else if ("BorderLayout".equals(value(parent, "layout", "BoxLayout"))) { + out.append(indent).append(parentName).append(".add(BorderLayout.") + .append(GuiDocument.effectiveBorderConstraint(parent, child).toUpperCase()).append(", ").append(name).append(");\n"); + } else if ("TableLayout".equals(value(parent, "layout", "BoxLayout"))) { + out.append(indent).append(parentName).append(".add(((TableLayout) ").append(parentName) + .append(".getLayout()).createConstraint(").append(value(child, "tableRow", "0")) + .append(", ").append(value(child, "tableColumn", "0")).append(")") + .append(".horizontalSpan(").append(value(child, "tableHorizontalSpan", "1")).append(")") + .append(".verticalSpan(").append(value(child, "tableVerticalSpan", "1")).append("), ") + .append(name).append(");\n"); + } else out.append(indent).append(parentName).append(".add(").append(name).append(");\n"); + if (GuiDocument.acceptsChildren(child)) appendGeneratedChildren(out, child, name, indent); + } + if ("LayeredLayout".equals(value(parent, "layout", "BoxLayout"))) { + for (Element child : componentChildren(parent)) { + String name = javaName(child); + String[] refs = GuidedLayoutSupport.referenceNames(child); + out.append(indent).append("((LayeredLayout) ").append(parentName).append(".getLayout())") + .append(".setInsets(").append(name).append(", \"") + .append(javaEscape(value(child, "layeredInsets", "auto auto auto auto"))).append("\")") + .append(".setReferenceComponents(").append(name); + for (int side = 0; side < 4; side++) { + Element reference = namedSibling(parent, refs[side]); + out.append(", ").append(reference == null ? "null" : javaName(reference)); + } + out.append(")") + .append(".setReferencePositions(").append(name).append(", \"") + .append(javaEscape(value(child, "guidedReferencePositions", "0 0 0 0"))).append("\")") + .append(".setPercentInsetAnchorHorizontal(").append(name).append(", ") + .append(value(child, "guidedHorizontalAnchor", "0")).append("f)") + .append(".setPercentInsetAnchorVertical(").append(name).append(", ") + .append(value(child, "guidedVerticalAnchor", "0")).append("f);\n"); + } + } + } + + private String generatedModelSource() { + String form = relativeFormName(document.path()); + int dot = form.lastIndexOf('.'); + String packageName = dot < 0 ? "" : form.substring(0, dot); + String className = (dot < 0 ? form : form.substring(dot + 1)) + "Model"; + String strategy = value(document.root(), "bindingStrategy", "properties"); + List bindable = new ArrayList<>(); + for (Element element : document.components()) { + String type = value(element, "type", ""); + if ("TextField".equals(type) || "TextArea".equals(type) || "CheckBox".equals(type) || "RadioButton".equals(type)) bindable.add(element); + } + StringBuilder out = new StringBuilder(); + if (packageName.length() > 0) out.append("package ").append(packageName).append(";\n\n"); + if ("none".equals(strategy)) { + return out.append("// Data binding is disabled for this form.\n") + .append("public class ").append(className).append(" {\n}\n").toString(); + } + if ("bindable".equals(strategy)) { + out.append("import com.codename1.annotations.Bind;\n") + .append("import com.codename1.annotations.Bindable;\n") + .append("import com.codename1.binding.BindAttr;\n\n") + .append("@Bindable\npublic class ").append(className).append(" {\n"); + for (Element element : bindable) { + String type = value(element, "type", ""); + boolean selected = "CheckBox".equals(type) || "RadioButton".equals(type); + String name = javaName(element); + String cap = Character.toUpperCase(name.charAt(0)) + name.substring(1); + out.append(" @Bind(name = \"").append(javaEscape(value(element, "name", name))) + .append("\", attr = BindAttr.").append(selected ? "SELECTED" : "TEXT").append(")\n") + .append(" private ").append(selected ? "boolean" : "String").append(" ").append(name).append(" = ") + .append(selected ? String.valueOf("true".equals(value(element, "selected", "false"))) + : "\"" + javaEscape(value(element, "text", "")) + "\"").append(";\n\n") + .append(" public ").append(selected ? "boolean is" : "String get").append(cap).append("() { return ") + .append(name).append("; }\n") + .append(" public void set").append(cap).append("(").append(selected ? "boolean" : "String") + .append(" value) { this.").append(name).append(" = value; }\n\n"); + } + return out.append("}\n").toString(); + } + out.append("import com.codename1.properties.*;\n\npublic class ").append(className) + .append(" implements PropertyBusinessObject {\n"); + for (Element element : bindable) { + String type = value(element, "type", ""); + boolean selected = "CheckBox".equals(type) || "RadioButton".equals(type); + out.append(" public final Property<").append(selected ? "Boolean" : "String").append(", ").append(className).append("> ") + .append(javaName(element)).append(" = new Property<>(\"").append(javaEscape(value(element, "name", javaName(element)))) + .append("\", ").append(selected ? String.valueOf("true".equals(value(element, "selected", "false"))) + : "\"" + javaEscape(value(element, "text", "")) + "\"").append(");\n"); + } + out.append(" private final PropertyIndex index = new PropertyIndex(this, \"").append(className).append("\""); + for (Element element : bindable) out.append(", ").append(javaName(element)); + return out.append(");\n @Override public PropertyIndex getPropertyIndex() { return index; }\n}\n").toString(); + } + + private String javaType(Element element) { + String type = value(element, "type", "Container"); + return "Form".equals(type) ? "Container" : type; + } + + private String componentSource(Element element) { + String type = value(element, "type", "Container"); + String text = "\"" + javaEscape(value(element, "text", "")) + "\""; + String source; + if ("Container".equals(type)) source = "new Container(" + layoutSource(element) + ")"; + else if ("Tabs".equals(type)) source = "new Tabs()"; + else if ("Slider".equals(type)) source = "new Slider()"; + else if ("TextField".equals(type)) source = "new TextField(" + text + ", \"" + javaEscape(value(element, "hint", "")) + "\")"; + else if ("TextArea".equals(type)) source = "new TextArea(" + text + ")"; + else source = "new " + type + "(" + text + ")"; + String fixedWidth = element.getAttribute("guidedPreferredWidth"); + String fixedHeight = element.getAttribute("guidedPreferredHeight"); + if (fixedWidth == null && fixedHeight == null) return source; + return source + " { @Override protected Dimension calcPreferredSize() { " + + "Dimension size = super.calcPreferredSize(); " + + (fixedWidth == null ? "" : "size.setWidth(" + fixedWidth + "); ") + + (fixedHeight == null ? "" : "size.setHeight(" + fixedHeight + "); ") + + "return size; } }"; + } + + private String layoutSource(Element element) { + String layout = value(element, "layout", "BoxLayout"); + if ("BorderLayout".equals(layout)) return "new BorderLayout()"; + if ("FlowLayout".equals(layout)) return "new FlowLayout()"; + if ("GridLayout".equals(layout)) return "new GridLayout(" + value(element, "gridLayoutRows", "1") + ", " + value(element, "gridLayoutColumns", "2") + ")"; + if ("TableLayout".equals(layout)) return "new TableLayout(" + value(element, "tableLayoutRows", "2") + ", " + value(element, "tableLayoutColumns", "2") + ")"; + if ("LayeredLayout".equals(layout)) return "new LayeredLayout()"; + return "X".equals(value(element, "boxLayoutAxis", "Y")) ? "BoxLayout.x()" : "BoxLayout.y()"; + } + + private List generatedHandlers() { + List handlers = new ArrayList<>(); + for (Element element : document.components()) { + String handler = element.getAttribute("actionEvent"); + if (handler != null && handler.length() > 0 && !handlers.contains(javaIdentifier(handler))) handlers.add(javaIdentifier(handler)); + } + for (Element command : document.commands()) { + String handler = javaIdentifier(value(command, "actionEvent", "onCommand")); + if (!handlers.contains(handler)) handlers.add(handler); + } + return handlers; + } + + private String javaName(Element element) { return javaIdentifier(value(element, "name", value(element, "type", "component"))); } + private String javaIdentifier(String value) { + String cleaned = value == null ? "component" : value.replaceAll("[^A-Za-z0-9_$]", "_"); + if (cleaned.length() == 0) return "component"; + char first = cleaned.charAt(0); + boolean validStart = first == '_' || first == '$' || first >= 'A' && first <= 'Z' || first >= 'a' && first <= 'z'; + return validStart ? cleaned : "_" + cleaned; + } + private String javaEscape(String value) { + return value == null ? "" : value.replace("\\", "\\\\").replace("\"", "\\\"").replace("\n", "\\n").replace("\r", ""); + } + + private String ensureHandler(String source, String handler) { + if (source.indexOf(handler + "(") >= 0) return source; + int close = source.indexOf("// "); + if (close < 0) close = source.lastIndexOf('}'); + String method = "\n private void " + handler + "(ActionEvent event) {\n" + + " // Add event behavior here.\n" + + " }\n"; + return close < 0 ? source + method : source.substring(0, close) + method + source.substring(close); + } + + private String mergeGeneratedSource(String existing, String generated) { + String startMarker = "// "; + String endMarker = "// "; + int oldStart = existing.indexOf(startMarker); + int oldEnd = existing.indexOf(endMarker); + if (oldStart < 0 || oldEnd < oldStart) { + return existing.indexOf("// Generated live from ") >= 0 ? generated : existing; + } + int newStart = generated.indexOf(startMarker); + int newEnd = generated.indexOf(endMarker); + if (newStart < 0 || newEnd < newStart) return generated; + String userCode = existing.substring(oldStart + startMarker.length(), oldEnd); + return generated.substring(0, newStart + startMarker.length()) + userCode + generated.substring(newEnd); + } + + private String resolveInitialForm() { + String requested = binding.initialForm(); + if (requested == null || requested.length() == 0) return null; + for (String file : guiFiles) { + if (file.endsWith(requested.replace('.', '/') + ".gui") || file.endsWith(requested + ".gui")) return file; + } + return null; + } + + private String relativeFormName(String path) { + String base = binding == null || binding.guiDir() == null ? "" : ProjectIO.fsUrl(binding.guiDir()); + String relative = path.startsWith(base) ? path.substring(base.length()) : path; + if (relative.startsWith("/")) relative = relative.substring(1); + return relative.endsWith(".gui") ? relative.substring(0, relative.length() - 4).replace('/', '.') : relative; + } + + private Button iconButton(char icon, String tooltip, Runnable action) { + Button button = new Button(material(icon, "BuilderCanvasIcon")); + button.setUIID("BuilderCanvasButton"); + button.setName(tooltip); + button.setAccessibilityText(tooltip); + button.getSemantics().setIdentifier("guibuilder.canvasMode." + semanticKey(tooltip)).setLabel(tooltip); + button.addActionListener(e -> action.run()); + return button; + } + + private Label sectionTitle(String title) { + Label label = new Label(title, "BuilderSectionTitle"); + label.getSemantics().setRole(AccessibilityRole.HEADING).setHeadingLevel(2).setLabel(title); + return label; + } + private boolean hasText(String type) { + return "Label".equals(type) || "SpanLabel".equals(type) || "Button".equals(type) + || "CheckBox".equals(type) || "RadioButton".equals(type) + || "TextField".equals(type) || "TextArea".equals(type); + } + private static String value(Element element, String attribute, String fallback) { + String result = element == null ? null : element.getAttribute(attribute); + return result == null || result.length() == 0 ? fallback : result; + } + private FontImage material(char icon, String uiid) { return FontImage.createMaterial(icon, UIManager.getInstance().getComponentStyle(uiid)); } + private void setStatus(String message) { + String previous = status == null ? null : status.getText(); + if (status != null) status.setText(message); + if (previous == null || !previous.equals(message)) recordAction("status", "message", message); + } + + private static String snapReferenceName(SnapResult snap) { + return snap == null || snap.reference == null ? null + : value(snap.reference, "name", value(snap.reference, "type", "component")); + } + + private void recordAction(String kind, Object... values) { + if (mcpController == null) return; + Map details = new LinkedHashMap(); + for (int i = 0; values != null && i + 1 < values.length; i += 2) { + if (values[i] != null && values[i + 1] != null) details.put(String.valueOf(values[i]), values[i + 1]); + } + if (document != null) { + details.put("form", relativeFormName(document.path())); + Element selected = document.selected(); + if (selected != null) details.put("selected", value(selected, "name", value(selected, "type", "component"))); + } + mcpController.record(kind, details); + } + + Map mcpState(long latestSequence) { + Map out = new LinkedHashMap(); + out.put("latestSequence", Long.valueOf(latestSequence)); + out.put("canvasMode", canvasMode); + out.put("darkMode", Boolean.valueOf(darkMode)); + out.put("status", status == null ? null : status.getText()); + List forms = new ArrayList(); + for (String path : guiFiles) forms.add(relativeFormName(path)); + out.put("forms", forms); + if (canvasHost != null) out.put("canvasBounds", componentBounds(canvasHost)); + if (document == null) return out; + normalizeSelection(); + out.put("activeForm", relativeFormName(document.path())); + out.put("path", document.path()); + out.put("modified", Boolean.valueOf(document.isModified())); + out.put("canUndo", Boolean.valueOf(document.canUndo())); + out.put("canRedo", Boolean.valueOf(document.canRedo())); + Element selected = document.selected(); + out.put("selected", selected == null ? null : value(selected, "name", value(selected, "type", "component"))); + List selection = new ArrayList(); + for (Element element : selectedElements) selection.add(value(element, "name", value(element, "type", "component"))); + out.put("selectedComponents", selection); + int[] selectionPaintBounds = dragGuideOverlay == null ? null : dragGuideOverlay.selectionPaintBounds(); + if (selectionPaintBounds != null) out.put("selectionPaintBounds", rect(selectionPaintBounds[0], + selectionPaintBounds[1], selectionPaintBounds[2], selectionPaintBounds[3])); + if (dragGuideOverlay != null) { + List selectionBounds = new ArrayList(); + for (int[] bounds : dragGuideOverlay.selectionPaintBoundsList()) { + selectionBounds.add(rect(bounds[0], bounds[1], bounds[2], bounds[3])); + } + out.put("selectionPaintBoundsList", selectionBounds); + } + List components = new ArrayList(); + for (Element element : document.components()) { + Map item = new LinkedHashMap(); + String name = value(element, "name", value(element, "type", "component")); + item.put("name", name); + item.put("type", value(element, "type", "Component")); + Element parent = document.parentOf(element); + item.put("parent", parent == null ? null : value(parent, "name", value(parent, "type", "component"))); + if (GuiDocument.acceptsChildren(element)) item.put("layout", value(element, "layout", "BoxLayout")); + Map attributes = new LinkedHashMap(); + Hashtable raw = element.getAttributes(); + if (raw != null) { + Enumeration keys = raw.keys(); + while (keys.hasMoreElements()) { + Object key = keys.nextElement(); + attributes.put(String.valueOf(key), raw.get(key)); + } + } + item.put("attributes", attributes); + Component preview = componentForElement(canvasHost, element); + if (preview != null) { + item.put("bounds", componentBounds(preview)); + item.put("visible", Boolean.valueOf(preview.isVisible() && preview.getWidth() > 0 && preview.getHeight() > 0)); + item.put("accessibilityIdentifier", preview.getSemantics().getIdentifier()); + } + components.add(item); + } + out.put("components", components); + if (activeDropPlan != null) { + Map guide = new LinkedHashMap(); + guide.put("layout", activeDropPlan.layout); + guide.put("valid", Boolean.valueOf(activeDropPlan.valid)); + guide.put("constraint", activeDropPlan.constraint); + guide.put("target", value(activeDropPlan.target, "name", value(activeDropPlan.target, "type", "component"))); + guide.put("message", activeDropPlan.message); + guide.put("snapDescription", activeDropPlan.snapDescription); + guide.put("snapBounds", rect(activeDropPlan.snapX, activeDropPlan.snapY, + activeDropPlan.snapW, activeDropPlan.snapH)); + out.put("dropGuide", guide); + } + return out; + } + + boolean mcpSelectComponent(String componentName) { + return mcpSelectComponent(componentName, false); + } + + boolean mcpSelectComponent(String componentName, boolean additive) { + if (document == null || componentName == null) return false; + Element element = findElementNamed(document, componentName); + if (element == null) return false; + selectElement(element, additive, "mcp"); + setStatus("Selected " + componentName + " through MCP"); + return true; + } + + String mcpOpenForm(String formName) { + if (formName == null || formName.length() == 0) return "form is required"; + if (document != null && document.isModified()) return "Active form has unsaved changes"; + for (String path : guiFiles) { + String relative = relativeFormName(path); + String simple = relative.substring(relative.lastIndexOf('.') + 1); + if (formName.equals(relative) || formName.equals(simple) || formName.equals(path)) { + openForm(path); + recordAction("form_opened", "form", relative, "source", "mcp"); + return null; + } + } + return "No form named " + formName; + } + + String mcpDragComponent(String componentName, String targetName, String placement, + Integer requestedX, Integer requestedY) { + if (document == null || canvasHost == null) return "No active GUI document"; + Element element = findElementNamed(document, componentName); + if (element == null || element == document.root()) return "No draggable component named " + componentName; + Component source = componentForElement(canvasHost, element); + if (source == null || source.getWidth() < 1 || source.getHeight() < 1) return componentName + " is not visible"; + if ((requestedX == null) != (requestedY == null)) return "x and y must be supplied together"; + int startX = source.getAbsoluteX() + source.getWidth() / 2; + int startY = source.getAbsoluteY() + source.getHeight() / 2; + int releaseX; + int releaseY; + if (requestedX != null) { + releaseX = requestedX.intValue(); + releaseY = requestedY.intValue(); + } else { + Element target = targetName == null || targetName.length() == 0 + ? document.root() : findElementNamed(document, targetName); + if (target == null) return "No target component named " + targetName; + Component targetPreview = componentForElement(canvasHost, target); + if (targetPreview == null) return "Target " + targetName + " is not visible"; + String where = placement == null || placement.length() == 0 ? "center" : placement; + int grabX = source.getWidth() / 2; + int grabY = source.getHeight() / 2; + int gap = standardGap(); + int desiredX = targetPreview.getAbsoluteX() + Math.max(0, (targetPreview.getWidth() - source.getWidth()) / 2); + int desiredY = targetPreview.getAbsoluteY() + Math.max(0, (targetPreview.getHeight() - source.getHeight()) / 2); + if ("above".equals(where)) desiredY = targetPreview.getAbsoluteY() - source.getHeight() - gap; + else if ("below".equals(where)) desiredY = targetPreview.getAbsoluteY() + targetPreview.getHeight() + gap; + else if ("leftOf".equals(where)) desiredX = targetPreview.getAbsoluteX() - source.getWidth() - gap; + else if ("rightOf".equals(where)) desiredX = targetPreview.getAbsoluteX() + targetPreview.getWidth() + gap; + else if ("before".equals(where) || "after".equals(where)) { + Element parent = document.parentOf(target); + boolean horizontal = parent != null && ("X".equals(value(parent, "boxLayoutAxis", "Y")) + || "FlowLayout".equals(value(parent, "layout", "BoxLayout")) + || "GridLayout".equals(value(parent, "layout", "BoxLayout"))); + if (horizontal) { + desiredX = "after".equals(where) ? targetPreview.getAbsoluteX() + targetPreview.getWidth() - 1 + : targetPreview.getAbsoluteX(); + desiredY = targetPreview.getAbsoluteY(); + } else { + desiredX = targetPreview.getAbsoluteX(); + desiredY = "after".equals(where) ? targetPreview.getAbsoluteY() + targetPreview.getHeight() - 1 + : targetPreview.getAbsoluteY(); + } + } else if (!"center".equals(where)) { + return "Unknown placement " + where; + } + releaseX = desiredX + grabX; + releaseY = desiredY + grabY; + } + String before = document.toXml(); + handleDesignerPointerPressed(startX, startY); + updateDesignerDrag(releaseX, releaseY); + finishDesignerDrag(releaseX, releaseY); + if (before.equals(document.toXml())) return "Drag completed without changing the document: " + + (status == null ? "no valid drop" : status.getText()); + recordAction("mcp_drag_completed", "component", componentName, + "x", Integer.valueOf(releaseX), "y", Integer.valueOf(releaseY)); + return null; + } + + String mcpCommand(String command) { + if (command == null) return "command is required"; + if ("save".equals(command)) save(); + else if ("undo".equals(command)) undo(); + else if ("redo".equals(command)) redo(); + else if ("refresh".equals(command)) refreshProject(); + else if ("toggleDarkMode".equals(command)) toggleDarkMode(); + else if ("phonePortrait".equals(command) || "phoneLandscape".equals(command) + || "tabletPortrait".equals(command) || "desktop".equals(command)) setCanvasMode(command); + else return "Unknown command " + command; + recordAction("mcp_command", "command", command); + return null; + } + + private static List componentBounds(Component component) { + return rect(component.getAbsoluteX(), component.getAbsoluteY(), component.getWidth(), component.getHeight()); + } + + private static List rect(int x, int y, int width, int height) { + List bounds = new ArrayList(); + bounds.add(Integer.valueOf(x)); + bounds.add(Integer.valueOf(y)); + bounds.add(Integer.valueOf(width)); + bounds.add(Integer.valueOf(height)); + return bounds; + } + + private static String semanticKey(String value) { + StringBuilder out = new StringBuilder(); + for (int i = 0; value != null && i < value.length(); i++) { + char ch = Character.toLowerCase(value.charAt(i)); + if (ch >= 'a' && ch <= 'z' || ch >= '0' && ch <= '9') out.append(ch); + else if (out.length() > 0 && out.charAt(out.length() - 1) != '.') out.append('.'); + } + while (out.length() > 0 && out.charAt(out.length() - 1) == '.') out.deleteCharAt(out.length() - 1); + return out.toString(); + } + + public static void saveActiveDocument() { if (active != null) active.save(); } + public static void refreshActiveProject() { if (active != null) active.refreshProject(); } + public static void openActiveCss() { if (active != null) active.openCss(); } + public static void openActiveSource() { if (active != null) active.openCompanionSource(); } + public static void openActiveModel() { if (active != null) active.openBindingModel(); } + static void openActiveCssForTest() { if (active != null) active.openCss(); } + static CodeEditor activeCodeEditorForTest() { return active == null ? null : active.activeCodeEditor; } + static int activePreviewForegroundForTest(String componentName) { + if (active == null || active.document == null || !(active.previewRoot instanceof Container)) return -1; + for (Element element : active.document.components()) { + if ((componentName == null && "Button".equals(element.getAttribute("type"))) + || (componentName != null && componentName.equals(element.getAttribute("name")))) { + Component component = active.componentForElement((Container) active.previewRoot, element); + return component == null ? -1 : component.getStyle().getFgColor(); + } + } + return -1; + } + static int[] activePreviewBoundsForTest(String componentName) { + if (active == null || active.document == null || !(active.previewRoot instanceof Container)) return null; + for (Element element : active.document.components()) { + if (componentName.equals(element.getAttribute("name"))) { + Component component = active.componentForElement((Container) active.previewRoot, element); + return component == null ? null : new int[]{component.getAbsoluteX(), component.getAbsoluteY(), + component.getWidth(), component.getHeight()}; + } + } + return null; + } + static String activeDocumentAttributeForTest(String componentName, String attribute) { + if (active == null || active.document == null) return null; + for (Element element : active.document.components()) { + if (componentName.equals(element.getAttribute("name"))) return element.getAttribute(attribute); + } + return null; + } + static int activePreviewBaselineForTest(String componentName) { + int[] bounds = activePreviewBoundsForTest(componentName); + if (bounds == null || active == null) return -1; + for (Element element : active.document.components()) { + if (componentName.equals(element.getAttribute("name"))) { + Component component = active.componentForElement((Container) active.previewRoot, element); + int baseline = component == null ? -1 : component.getBaseline(component.getWidth(), component.getHeight()); + return baseline < 0 ? -1 : component.getAbsoluteY() + baseline; + } + } + return -1; + } + static String activeDesignerStateForTest() { + return active == null ? "inactive" : "armed=" + active.designerDragArmed + + ", active=" + active.designerDragActive + + ", resizeArmed=" + active.guidedResizeArmed + + ", resizeActive=" + active.guidedResizeActive + + ", press=" + active.designerPressX + "," + active.designerPressY + + ", status=" + (active.status == null ? "" : active.status.getText()); + } + static String activeSelectedNameForTest() { + return active == null || active.document == null || active.document.selected() == null ? null + : active.document.selected().getAttribute("name"); + } + static String activeCanvasModeForTest() { return active == null ? null : active.canvasMode; } + static int[] activeNamedUiBoundsForTest(String componentName) { + if (active == null || active.workspace == null) return null; + Component component = active.findNamedUiComponent(active.workspace, componentName); + return component == null ? null : new int[]{component.getAbsoluteX(), component.getAbsoluteY(), + component.getWidth(), component.getHeight()}; + } + private Component findNamedUiComponent(Component component, String componentName) { + if (componentName.equals(component.getName())) return component; + if (component instanceof Container container) { + for (int i = 0; i < container.getComponentCount(); i++) { + Component found = findNamedUiComponent(container.getComponentAt(i), componentName); + if (found != null) return found; + } + } + return null; + } + static void activeDesktopPointerDragged(int x, int y) { + if (active == null || !active.designerDragArmed && !active.guidedResizeArmed) return; + if (active.guidedResizeArmed) active.updateGuidedResize(x, y); else active.updateDesignerDrag(x, y); + } + static void activeDesktopPointerReleased(int x, int y) { + if (active == null || !active.designerDragArmed && !active.guidedResizeArmed) return; + if (active.guidedResizeArmed) active.finishGuidedResize(x, y); else active.finishDesignerDrag(x, y); + } + public static void cutActiveSelection() { if (active != null) active.cut(); } + public static void copyActiveSelection() { if (active != null) active.copy(); } + public static void pasteActiveSelection() { if (active != null) active.paste(); } + public static void deleteActiveSelection() { if (active != null) active.deleteSelection(); } + public static void undoActiveEdit() { if (active != null) active.undo(); } + public static void redoActiveEdit() { if (active != null) active.redo(); } + public static void toggleActiveDarkMode() { if (active != null) active.toggleDarkMode(); } + public static boolean isActiveDarkMode() { return active != null && active.darkMode; } + public static String[] activeFormNames() { + if (active == null) return new String[0]; + String[] names = new String[active.guiFiles.size()]; + for (int i = 0; i < names.length; i++) names[i] = active.relativeFormName(active.guiFiles.get(i)); + return names; + } + public static void openActiveForm(int index) { + if (active != null && index >= 0 && index < active.guiFiles.size()) active.switchForm(active.guiFiles.get(index)); + } +} diff --git a/scripts/guibuilder/common/src/main/java/com/codename1/guibuilder/GuiBuilderMcpController.java b/scripts/guibuilder/common/src/main/java/com/codename1/guibuilder/GuiBuilderMcpController.java new file mode 100644 index 00000000000..b0c36e520ad --- /dev/null +++ b/scripts/guibuilder/common/src/main/java/com/codename1/guibuilder/GuiBuilderMcpController.java @@ -0,0 +1,208 @@ +package com.codename1.guibuilder; + +import com.codename1.ai.Tool; +import com.codename1.ai.ToolHandler; +import com.codename1.io.JSONParser; +import com.codename1.mcp.MCP; +import com.codename1.mcp.MCPServer; +import com.codename1.ui.Display; +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +/** GUI Builder domain tools layered over the portable MCP accessibility tools. */ +final class GuiBuilderMcpController { + private static final int MAX_EVENTS = 500; + private final CodenameOneGUIBuilder builder; + private final List> events = new ArrayList>(); + private long sequence; + + GuiBuilderMcpController(CodenameOneGUIBuilder builder) { + this.builder = builder; + } + + void register() { + MCPServer server = MCP.getServer(); + server.setServerInfo("codename-one-gui-builder", "8.0-SNAPSHOT"); + MCP.addTool(new Tool("guibuilder_state", + "Returns the active GUI document, selected component, preview bounds, layout constraints, " + + "undo/redo state and current drag guide.", + "{\"type\":\"object\",\"properties\":{}}", new ToolHandler() { + @Override public String invoke(String argumentsJson) { + return JSONParser.toJson(onEdtState()); + } + })); + MCP.addTool(new Tool("guibuilder_actions", + "Returns GUI Builder actions after a sequence number. timeoutMs optionally waits for live " + + "activity (maximum 10000ms). Use latestSequence as the next afterSequence value.", + "{\"type\":\"object\",\"properties\":{" + + "\"afterSequence\":{\"type\":\"integer\"}," + + "\"timeoutMs\":{\"type\":\"integer\",\"minimum\":0,\"maximum\":10000}," + + "\"limit\":{\"type\":\"integer\",\"minimum\":1,\"maximum\":500}}}", + new ToolHandler() { + @Override public String invoke(String argumentsJson) throws Exception { + Map args = parse(argumentsJson); + long after = longValue(args.get("afterSequence"), 0); + int timeout = (int) Math.max(0, Math.min(10000, + longValue(args.get("timeoutMs"), 0))); + int limit = (int) Math.max(1, Math.min(MAX_EVENTS, + longValue(args.get("limit"), 100))); + return JSONParser.toJson(actionsAfter(after, timeout, limit)); + } + })); + MCP.addTool(new Tool("guibuilder_select", + "Selects a component in the active form by its GUI Builder name and returns fresh state. " + + "Set additive to true to add or toggle it in the current multi-selection.", + "{\"type\":\"object\",\"properties\":{" + + "\"component\":{\"type\":\"string\"}," + + "\"additive\":{\"type\":\"boolean\"}},\"required\":[\"component\"]}", + new ToolHandler() { + @Override public String invoke(String argumentsJson) throws Exception { + Map args = parse(argumentsJson); + final String component = JSONParser.getString(args, "component"); + final boolean additive = booleanValue(args.get("additive")); + final boolean[] success = new boolean[1]; + runOnEdt(new Runnable() { + @Override public void run() { success[0] = builder.mcpSelectComponent(component, additive); } + }); + return resultWithState(success[0], success[0] ? null : "No component named " + component); + } + })); + MCP.addTool(new Tool("guibuilder_open_form", + "Opens a project GUI form by its relative or simple name. Refuses to discard unsaved changes.", + "{\"type\":\"object\",\"properties\":{" + + "\"form\":{\"type\":\"string\"}},\"required\":[\"form\"]}", + new ToolHandler() { + @Override public String invoke(String argumentsJson) throws Exception { + Map args = parse(argumentsJson); + final String form = JSONParser.getString(args, "form"); + final String[] error = new String[1]; + runOnEdt(new Runnable() { + @Override public void run() { error[0] = builder.mcpOpenForm(form); } + }); + return resultWithState(error[0] == null, error[0]); + } + })); + MCP.addTool(new Tool("guibuilder_drag", + "Performs the GUI Builder's real pointer drag path for a named preview component. Supply " + + "absolute x/y coordinates from guibuilder_state, or a target and placement: " + + "center, before, after, above, below, leftOf, rightOf.", + "{\"type\":\"object\",\"properties\":{" + + "\"component\":{\"type\":\"string\"}," + + "\"target\":{\"type\":\"string\"}," + + "\"placement\":{\"type\":\"string\",\"enum\":[\"center\",\"before\",\"after\",\"above\",\"below\",\"leftOf\",\"rightOf\"]}," + + "\"x\":{\"type\":\"integer\"},\"y\":{\"type\":\"integer\"}}," + + "\"required\":[\"component\"]}", new ToolHandler() { + @Override public String invoke(String argumentsJson) throws Exception { + Map args = parse(argumentsJson); + final String component = JSONParser.getString(args, "component"); + final String target = JSONParser.getString(args, "target"); + final String placement = JSONParser.getString(args, "placement"); + final Integer x = integerOrNull(args.get("x")); + final Integer y = integerOrNull(args.get("y")); + final String[] error = new String[1]; + runOnEdt(new Runnable() { + @Override public void run() { + error[0] = builder.mcpDragComponent(component, target, placement, x, y); + } + }); + // A successful drop schedules a fresh preview. A second EDT turn runs after it. + return resultWithState(error[0] == null, error[0]); + } + })); + MCP.addTool(new Tool("guibuilder_command", + "Runs a GUI Builder command and returns fresh state.", + "{\"type\":\"object\",\"properties\":{\"command\":{\"type\":\"string\"," + + "\"enum\":[\"save\",\"undo\",\"redo\",\"refresh\",\"toggleDarkMode\"," + + "\"phonePortrait\",\"phoneLandscape\",\"tabletPortrait\",\"desktop\"]}}," + + "\"required\":[\"command\"]}", new ToolHandler() { + @Override public String invoke(String argumentsJson) throws Exception { + Map args = parse(argumentsJson); + final String command = JSONParser.getString(args, "command"); + final String[] error = new String[1]; + runOnEdt(new Runnable() { + @Override public void run() { error[0] = builder.mcpCommand(command); } + }); + return resultWithState(error[0] == null, error[0]); + } + })); + } + + void record(String kind, Map details) { + Map event = new LinkedHashMap(); + synchronized (this) { + event.put("sequence", Long.valueOf(++sequence)); + event.put("timestamp", Long.valueOf(System.currentTimeMillis())); + event.put("kind", kind); + if (details != null) event.putAll(details); + events.add(event); + while (events.size() > MAX_EVENTS) events.remove(0); + notifyAll(); + } + } + + long latestSequence() { + synchronized (this) { return sequence; } + } + + private Map actionsAfter(long after, int timeout, int limit) throws InterruptedException { + synchronized (this) { + if (sequence <= after && timeout > 0) wait(timeout); + List outEvents = new ArrayList(); + for (int i = 0; i < events.size() && outEvents.size() < limit; i++) { + Map event = events.get(i); + if (longValue(event.get("sequence"), 0) > after) { + outEvents.add(new LinkedHashMap(event)); + } + } + Map out = new LinkedHashMap(); + out.put("latestSequence", Long.valueOf(sequence)); + out.put("events", outEvents); + return out; + } + } + + private String resultWithState(boolean success, String error) { + final Map result = new LinkedHashMap(); + result.put("success", Boolean.valueOf(success)); + if (error != null) result.put("error", error); + // A command queues refreshEditor(), which in turn queues the overlay refresh. Cross one + // EDT barrier before requesting state so both generations have completed and MCP never + // reports a new component rectangle with a stale selection rectangle. + runOnEdt(new Runnable() { @Override public void run() { } }); + result.put("state", onEdtState()); + return JSONParser.toJson(result); + } + + private Map onEdtState() { + final Map[] holder = new Map[1]; + runOnEdt(new Runnable() { + @Override public void run() { holder[0] = builder.mcpState(latestSequence()); } + }); + return holder[0]; + } + + private static void runOnEdt(Runnable runnable) { + Display display = Display.getInstance(); + if (display.isEdt()) runnable.run(); else display.callSeriallyAndWait(runnable); + } + + private static Map parse(String json) throws Exception { + if (json == null || json.length() == 0) return new LinkedHashMap(); + Map parsed = JSONParser.parseJSON(json); + return parsed == null ? new LinkedHashMap() : parsed; + } + + private static long longValue(Object value, long fallback) { + return value instanceof Number ? ((Number) value).longValue() : fallback; + } + + private static boolean booleanValue(Object value) { + return Boolean.TRUE.equals(value) || "true".equalsIgnoreCase(String.valueOf(value)); + } + + private static Integer integerOrNull(Object value) { + return value instanceof Number ? Integer.valueOf(((Number) value).intValue()) : null; + } +} diff --git a/scripts/guibuilder/common/src/main/java/com/codename1/guibuilder/model/GuiDocument.java b/scripts/guibuilder/common/src/main/java/com/codename1/guibuilder/model/GuiDocument.java new file mode 100644 index 00000000000..b8a1ec25795 --- /dev/null +++ b/scripts/guibuilder/common/src/main/java/com/codename1/guibuilder/model/GuiDocument.java @@ -0,0 +1,636 @@ +package com.codename1.guibuilder.model; + +import com.codename1.util.regex.StringReader; +import com.codename1.xml.Element; +import com.codename1.xml.XMLParser; +import com.codename1.xml.XMLWriter; +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +public final class GuiDocument { + /** Single-name Guided Layout relationships. Mirrors the attributes GuidedLayoutSupport reads. */ + private static final String[] REFERENCE_NAME_ATTRIBUTES = { + "guidedMatchWidth", "guidedMatchHeight", "guidedReferenceTarget"}; + private static final String SIZE_MATCH = "match"; + private static final String SIZE_PREFERRED = "preferred"; + + private final String path; + private Element root; + private Element selected; + private boolean modified; + private final List undo = new ArrayList<>(); + private final List redo = new ArrayList<>(); + private int transactionDepth; + private State transactionStart; + + private GuiDocument(String path, Element root) { + this.path = path; + this.root = root; + this.selected = root; + } + + public static GuiDocument parse(String path, String xml) { + Element root = new XMLParser().parse(new StringReader(xml)); + if (root == null || !"component".equals(root.getTagName())) { + throw new IllegalArgumentException("GUI file does not contain a component root"); + } + return new GuiDocument(path, root); + } + + public String path() { return path; } + public Element root() { return root; } + public Element selected() { return selected; } + public boolean isModified() { return modified; } + public void markSaved() { modified = false; } + + public void select(Element element) { + if (element != null && containsElement(element)) selected = element; + } + + /** Identity-based ownership check. Elements parsed for another form must never be accepted. */ + public boolean containsElement(Element element) { + return element != null && containsIdentity(root, element); + } + + public String attribute(String name, String fallback) { + String value = selected.getAttribute(name); + return value == null ? fallback : value; + } + + public void setAttribute(String name, String value) { + beforeMutation(); + setNormalizedAttribute(selected, name, value); + modified = true; + } + + public String effectiveUiid(Element element) { + String explicit = element == null ? null : element.getAttribute("uiid"); + if (explicit != null && explicit.length() > 0) return explicit; + String type = element == null ? null : element.getAttribute("type"); + return type == null || type.length() == 0 ? "Component" : type; + } + + public Element parentOf(Element element) { + return element == root ? null : findParent(root, element); + } + + public String parentLayout(Element element) { + Element parent = parentOf(element); + if (parent == null) return ""; + String layout = parent.getAttribute("layout"); + return layout == null || layout.length() == 0 ? "BoxLayout" : layout; + } + + /** Returns a valid, deterministic constraint even for malformed legacy BorderLayouts. */ + public static String effectiveBorderConstraint(Element parent, Element child) { + if (parent == null || child == null) return "Center"; + List used = new ArrayList<>(); + for (int i = 0; i < parent.getNumChildren(); i++) { + Object value = parent.getChildAt(i); + if (!(value instanceof Element candidate) || !"component".equals(candidate.getTagName())) continue; + String constraint = normalizeBorderConstraint(candidate.getAttribute("layoutConstraint")); + if (constraint == null || used.contains(constraint)) constraint = firstFreeBorderConstraint(used); + if (candidate == child) return constraint; + used.add(constraint); + } + return "Center"; + } + + public static Element childAtBorderConstraint(Element parent, String constraint, Element excluding) { + String wanted = normalizeBorderConstraint(constraint); + if (parent == null || wanted == null) return null; + for (int i = 0; i < parent.getNumChildren(); i++) { + Object value = parent.getChildAt(i); + if (value instanceof Element child && "component".equals(child.getTagName()) && child != excluding + && wanted.equals(effectiveBorderConstraint(parent, child))) return child; + } + return null; + } + + public static String normalizeBorderConstraint(String constraint) { + if (constraint == null) return null; + if ("north".equalsIgnoreCase(constraint)) return "North"; + if ("south".equalsIgnoreCase(constraint)) return "South"; + if ("east".equalsIgnoreCase(constraint)) return "East"; + if ("west".equalsIgnoreCase(constraint)) return "West"; + if ("center".equalsIgnoreCase(constraint)) return "Center"; + return null; + } + + private static String firstFreeBorderConstraint(List used) { + String[] order = {"Center", "North", "South", "West", "East"}; + for (String candidate : order) if (!used.contains(candidate)) return candidate; + return "Center"; + } + + public boolean moveSelectedBy(int delta) { + if (selected == root || delta == 0) return false; + Element parent = findParent(root, selected); + if (parent == null) return false; + int index = childIndex(parent, selected); + int target = index + delta; + if (index < 0 || target < 0 || target >= parent.getNumChildren()) return false; + beforeMutation(); + parent.removeChildAt(index); + parent.insertChildAt(selected, target); + modified = true; + return true; + } + + public List commands() { + List result = new ArrayList<>(); + for (int i = 0; i < root.getNumChildren(); i++) { + Object child = root.getChildAt(i); + if (child instanceof Element e && "command".equals(e.getTagName())) result.add(e); + } + return result; + } + + public Element addCommand() { + beforeMutation(); + Element command = new Element("command"); + command.setAttribute("name", "Command " + (commands().size() + 1)); + command.setAttribute("placement", "right"); + command.setAttribute("actionEvent", "onCommand" + (commands().size() + 1)); + root.addChild(command); + modified = true; + return command; + } + + public boolean removeCommand(Element command) { + if (command == null || !"command".equals(command.getTagName())) return false; + int index = childIndex(root, command); + if (index < 0) return false; + beforeMutation(); + root.removeChildAt(index); + modified = true; + return true; + } + + public void setCommandAttribute(Element command, String name, String value) { + if (command == null || !"command".equals(command.getTagName())) return; + beforeMutation(); + setNormalizedAttribute(command, name, value); + modified = true; + } + + public Element addComponent(String type) { + beforeMutation(); + Element parent = selected; + if (!acceptsChildren(parent)) parent = findParent(root, selected); + if (parent == null) parent = root; + Element child = new Element("component"); + child.setAttribute("type", type); + child.setAttribute("name", uniqueName(type)); + if (acceptsChildren(child)) child.setAttribute("layout", "LayeredLayout"); + parent.addChild(child); + selected = child; + modified = true; + return child; + } + + public boolean deleteSelected() { + if (selected == root) return false; + Element parent = findParent(root, selected); + if (parent == null) return false; + int index = childIndex(parent, selected); + if (index < 0) return false; + Element removed = selected; + beginTransaction(); + try { + parent.removeChildAt(index); + selected = parent; + // Guided Layout relationships are stored by name. Leaving a reference to a deleted + // component behind makes its dependents jump to an inset measured from nothing. + clearReferencesTo(root, namesIn(removed, new LinkedHashSet())); + modified = true; + } finally { + endTransaction(); + } + return true; + } + + /** + * Renames the selected component, guaranteeing a unique name and repointing every Guided + * Layout relationship that referenced the old name, as one undo step. Returns the name that + * was actually applied, which differs from the request when the request was already taken. + */ + public String renameSelected(String requestedName) { + Element target = selected; + if (target == null || !"component".equals(target.getTagName())) return null; + String previous = target.getAttribute("name"); + String requested = requestedName == null ? "" : requestedName.trim(); + if (requested.length() == 0 || requested.equals(previous)) return previous; + Set exclude = new LinkedHashSet<>(); + exclude.add(target); + String unique = availableName(requested, exclude, null); + beginTransaction(); + try { + setNormalizedAttribute(target, "name", unique); + if (previous != null && previous.length() > 0) { + Map renames = new LinkedHashMap<>(); + renames.put(previous, unique); + remapReferences(root, renames); + } + modified = true; + } finally { + endTransaction(); + } + return unique; + } + + public String copySelectedXml() { + return new XMLWriter(true).toXML(selected); + } + + public Element pasteXml(String xml) { + if (xml == null || xml.length() == 0) return null; + Element pasted = new XMLParser().parse(new StringReader(xml)); + if (pasted == null || !"component".equals(pasted.getTagName())) return null; + beginTransaction(); + try { + if (pasted.getParent() != null) removeChild(pasted.getParent(), pasted); + Element parent = acceptsChildren(selected) ? selected : findParent(root, selected); + if (parent == null) parent = root; + uniquifyPastedNames(pasted); + parent.addChild(pasted); + selected = pasted; + modified = true; + } finally { + endTransaction(); + } + return pasted; + } + + /** + * A pasted subtree keeps its internal relationships but must not collide with names that are + * already used: every component in it is renamed where necessary and the references inside the + * subtree follow. Names outside the subtree keep pointing at the original components. + */ + private void uniquifyPastedNames(Element pasted) { + List elements = new ArrayList<>(); + collect(pasted, elements); + Set exclude = new LinkedHashSet<>(elements); + Set assigned = new LinkedHashSet<>(); + Map renames = new LinkedHashMap<>(); + for (Element element : elements) { + String current = element.getAttribute("name"); + String type = element.getAttribute("type"); + String base = current == null || current.length() == 0 + ? lowerFirst(type == null || type.length() == 0 ? "component" : type) : current; + String unique = availableName(base, exclude, assigned); + assigned.add(unique); + if (unique.equals(current)) continue; + setNormalizedAttribute(element, "name", unique); + if (current != null && current.length() > 0) renames.put(current, unique); + } + if (!renames.isEmpty()) remapReferences(pasted, renames); + } + + /** Returns `requested` when it is free, otherwise the same base with the lowest free suffix. */ + private String availableName(String requested, Set exclude, Set reserved) { + if (isNameAvailable(requested, exclude, reserved)) return requested; + String base = requested; + while (base.length() > 1 && base.charAt(base.length() - 1) >= '0' + && base.charAt(base.length() - 1) <= '9') { + base = base.substring(0, base.length() - 1); + } + for (int index = 1; index < 10000; index++) { + String candidate = base + index; + if (isNameAvailable(candidate, exclude, reserved)) return candidate; + } + return requested; + } + + private boolean isNameAvailable(String name, Set exclude, Set reserved) { + if (reserved != null && reserved.contains(name)) return false; + return !containsName(root, name, exclude); + } + + private static void remapReferences(Element element, Map renames) { + String[] references = splitReferences(element); + if (references != null) { + boolean changed = false; + for (int i = 0; i < references.length; i++) { + String replacement = renames.get(references[i].trim()); + if (replacement == null) continue; + references[i] = replacement; + changed = true; + } + if (changed) setNormalizedAttribute(element, "guidedReferences", joinReferences(references)); + } + for (String attribute : REFERENCE_NAME_ATTRIBUTES) { + String value = element.getAttribute(attribute); + String replacement = value == null ? null : renames.get(value.trim()); + if (replacement != null) setNormalizedAttribute(element, attribute, replacement); + } + for (int i = 0; i < element.getNumChildren(); i++) { + Object child = element.getChildAt(i); + if (child instanceof Element childElement) remapReferences(childElement, renames); + } + } + + private static void clearReferencesTo(Element element, Set removedNames) { + String[] references = splitReferences(element); + if (references != null) { + boolean changed = false; + for (int i = 0; i < references.length; i++) { + if (!removedNames.contains(references[i].trim())) continue; + references[i] = "-"; + changed = true; + } + if (changed) setNormalizedAttribute(element, "guidedReferences", joinReferences(references)); + } + clearMatchReference(element, "guidedMatchWidth", "guidedHorizontalSize", removedNames); + clearMatchReference(element, "guidedMatchHeight", "guidedVerticalSize", removedNames); + String target = element.getAttribute("guidedReferenceTarget"); + if (target != null && removedNames.contains(target.trim())) { + setNormalizedAttribute(element, "guidedReferenceTarget", null); + } + for (int i = 0; i < element.getNumChildren(); i++) { + Object child = element.getChildAt(i); + if (child instanceof Element childElement) clearReferencesTo(childElement, removedNames); + } + } + + /** A match policy whose target is gone falls back to the component's own preferred size. */ + private static void clearMatchReference(Element element, String matchAttribute, + String policyAttribute, Set removedNames) { + String value = element.getAttribute(matchAttribute); + if (value == null || !removedNames.contains(value.trim())) return; + setNormalizedAttribute(element, matchAttribute, null); + if (SIZE_MATCH.equals(element.getAttribute(policyAttribute))) { + setNormalizedAttribute(element, policyAttribute, SIZE_PREFERRED); + } + } + + private static String[] splitReferences(Element element) { + String raw = element.getAttribute("guidedReferences"); + return raw == null || raw.length() == 0 ? null : raw.split("\\|", -1); + } + + private static String joinReferences(String[] references) { + StringBuilder out = new StringBuilder(); + for (int i = 0; i < references.length; i++) { + if (i > 0) out.append('|'); + out.append(references[i] == null || references[i].trim().length() == 0 + ? "-" : references[i].trim()); + } + return out.toString(); + } + + private static Set namesIn(Element element, Set out) { + String name = element.getAttribute("name"); + if (name != null && name.length() > 0) out.add(name); + for (int i = 0; i < element.getNumChildren(); i++) { + Object child = element.getChildAt(i); + if (child instanceof Element childElement && "component".equals(childElement.getTagName())) { + namesIn(childElement, out); + } + } + return out; + } + + public boolean moveSelectedTo(Element target) { + return moveSelectedTo(target, false); + } + + public boolean moveSelectedTo(Element target, boolean afterTarget) { + if (selected == root || target == null || target == selected) return false; + Element oldParent = findParent(root, selected); + Element targetParent = findParent(root, target); + if (oldParent == null) return false; + Element destination = acceptsChildren(target) ? target : targetParent; + if (destination == null || isDescendant(selected, destination)) return false; + beforeMutation(); + int targetIndex = destination.getNumChildren(); + if (destination == targetParent) { + for (int i = 0; i < destination.getNumChildren(); i++) { + if (destination.getChildAt(i) == target) { + targetIndex = i + (afterTarget ? 1 : 0); + break; + } + } + } + int oldIndex = destination == oldParent ? childIndex(oldParent, selected) : -1; + removeChild(oldParent, selected); + if (oldIndex >= 0 && oldIndex < targetIndex) targetIndex--; + if (destination == oldParent && targetIndex > destination.getNumChildren()) targetIndex = destination.getNumChildren(); + destination.insertChildAt(selected, targetIndex); + modified = true; + return true; + } + + public int componentIndex(Element parent, Element child) { + if (parent == null || child == null) return -1; + int componentIndex = 0; + for (int i = 0; i < parent.getNumChildren(); i++) { + Object value = parent.getChildAt(i); + if (!(value instanceof Element element) || !"component".equals(element.getTagName())) continue; + if (element == child) return componentIndex; + componentIndex++; + } + return -1; + } + + public boolean moveSelectedToParent(Element destination, int componentIndex) { + if (selected == root || destination == null || !acceptsChildren(destination) + || selected == destination || isDescendant(selected, destination)) return false; + Element oldParent = findParent(root, selected); + if (oldParent == null) return false; + beforeMutation(); + removeChild(oldParent, selected); + int xmlIndex = destination.getNumChildren(); + int seen = 0; + for (int i = 0; i < destination.getNumChildren(); i++) { + Object child = destination.getChildAt(i); + if (child instanceof Element element && "component".equals(element.getTagName())) { + if (seen >= componentIndex) { xmlIndex = i; break; } + seen++; + } + } + destination.insertChildAt(selected, Math.max(0, Math.min(xmlIndex, destination.getNumChildren()))); + modified = true; + return true; + } + + public List components() { + List result = new ArrayList<>(); + collect(root, result); + return result; + } + + public String toXml() { + return "\n\n" + new XMLWriter(true).toXML(root); + } + + public boolean canUndo() { return !undo.isEmpty(); } + public boolean canRedo() { return !redo.isEmpty(); } + + public void beginTransaction() { + if (transactionDepth++ == 0) transactionStart = capture(); + } + + public void endTransaction() { + if (transactionDepth == 0) return; + if (--transactionDepth == 0) { + if (transactionStart != null && !transactionStart.xml.equals(xmlOnly())) { + undo.add(transactionStart); + redo.clear(); + } + transactionStart = null; + } + } + + public boolean undo() { + if (!canUndo()) return false; + redo.add(capture()); + restore(undo.remove(undo.size() - 1)); + return true; + } + + public boolean redo() { + if (!canRedo()) return false; + undo.add(capture()); + restore(redo.remove(redo.size() - 1)); + return true; + } + + public static boolean acceptsChildren(Element element) { + String type = element == null ? null : element.getAttribute("type"); + return "Form".equals(type) || "Container".equals(type) || "Dialog".equals(type) + || "Tabs".equals(type) || "Accordion".equals(type); + } + + private String uniqueName(String type) { + int index = 1; + while (containsName(root, lowerFirst(type) + index, null)) index++; + return lowerFirst(type) + index; + } + + private static String lowerFirst(String value) { + return value.substring(0, 1).toLowerCase() + value.substring(1); + } + + private static boolean containsName(Element element, String name, Set exclude) { + if ((exclude == null || !exclude.contains(element)) && name.equals(element.getAttribute("name"))) { + return true; + } + for (int i = 0; i < element.getNumChildren(); i++) { + Object child = element.getChildAt(i); + if (child instanceof Element e && containsName(e, name, exclude)) return true; + } + return false; + } + + private static Element findParent(Element parent, Element target) { + for (int i = 0; i < parent.getNumChildren(); i++) { + Object child = parent.getChildAt(i); + if (child == target) return parent; + if (child instanceof Element e) { + Element found = findParent(e, target); + if (found != null) return found; + } + } + return null; + } + + private static void removeChild(Element parent, Element child) { + for (int i = 0; i < parent.getNumChildren(); i++) { + if (parent.getChildAt(i) == child) { + parent.removeChildAt(i); + return; + } + } + } + + private static int childIndex(Element parent, Element child) { + for (int i = 0; i < parent.getNumChildren(); i++) { + if (parent.getChildAt(i) == child) return i; + } + return -1; + } + + private static boolean isDescendant(Element ancestor, Element candidate) { + if (ancestor == candidate) return true; + for (int i = 0; i < ancestor.getNumChildren(); i++) { + Object child = ancestor.getChildAt(i); + if (child instanceof Element e && isDescendant(e, candidate)) return true; + } + return false; + } + + private static boolean containsIdentity(Element parent, Element target) { + if (parent == target) return true; + for (int i = 0; i < parent.getNumChildren(); i++) { + Object child = parent.getChildAt(i); + if (child instanceof Element element && containsIdentity(element, target)) return true; + } + return false; + } + + private static void collect(Element element, List result) { + result.add(element); + for (int i = 0; i < element.getNumChildren(); i++) { + Object child = element.getChildAt(i); + if (child instanceof Element e && "component".equals(e.getTagName())) collect(e, result); + } + } + + /** XMLParser stores case-insensitive attribute keys in lowercase. Always mutate that canonical + * key and remove a possible camel-case duplicate created by older GUI Builder versions. */ + private static void setNormalizedAttribute(Element element, String name, String value) { + String canonical = name.toLowerCase(); + element.removeAttribute(canonical); + if (!canonical.equals(name)) element.removeAttribute(name); + if (value != null && value.trim().length() > 0) element.setAttribute(canonical, value.trim()); + } + + private void beforeMutation() { + if (transactionDepth > 0) return; + undo.add(capture()); + redo.clear(); + } + + private State capture() { + return new State(xmlOnly(), selected == null ? null : selected.getAttribute("name"), modified); + } + + private String xmlOnly() { + return new XMLWriter(true).toXML(root); + } + + private void restore(State state) { + root = new XMLParser().parse(new StringReader(state.xml)); + selected = state.selectedName == null ? root : findByName(root, state.selectedName); + if (selected == null) selected = root; + modified = state.modified; + } + + private static Element findByName(Element element, String name) { + if (name.equals(element.getAttribute("name"))) return element; + for (int i = 0; i < element.getNumChildren(); i++) { + Object child = element.getChildAt(i); + if (child instanceof Element) { + Element found = findByName((Element) child, name); + if (found != null) return found; + } + } + return null; + } + + private static final class State { + final String xml; + final String selectedName; + final boolean modified; + State(String xml, String selectedName, boolean modified) { + this.xml = xml; + this.selectedName = selectedName; + this.modified = modified; + } + } +} diff --git a/scripts/guibuilder/common/src/main/java/com/codename1/guibuilder/project/ProjectBinding.java b/scripts/guibuilder/common/src/main/java/com/codename1/guibuilder/project/ProjectBinding.java new file mode 100644 index 00000000000..e9c96f8ec33 --- /dev/null +++ b/scripts/guibuilder/common/src/main/java/com/codename1/guibuilder/project/ProjectBinding.java @@ -0,0 +1,38 @@ +package com.codename1.guibuilder.project; + +public final class ProjectBinding { + private String projectDir; + private String guiDir; + private String sourceDir; + private String cssFile; + private String initialForm; + + public String projectDir() { return projectDir; } + public String guiDir() { return guiDir; } + public String sourceDir() { return sourceDir; } + public String cssFile() { return cssFile; } + public String initialForm() { return initialForm; } + public boolean isValid() { return projectDir != null && guiDir != null; } + + public static ProjectBinding parse(String content) { + ProjectBinding binding = new ProjectBinding(); + if (content == null) return binding; + for (String line : content.replace("\r\n", "\n").split("\n")) { + String value = line.trim(); + if (value.length() == 0 || value.startsWith("#")) continue; + int split = value.indexOf('='); + if (split < 1) continue; + String key = value.substring(0, split).trim(); + String field = value.substring(split + 1).trim(); + switch (key) { + case "projectDir" -> binding.projectDir = field; + case "guiDir" -> binding.guiDir = field; + case "sourceDir" -> binding.sourceDir = field; + case "cssFile" -> binding.cssFile = field; + case "initialForm" -> binding.initialForm = field; + default -> { } + } + } + return binding; + } +} diff --git a/scripts/guibuilder/common/src/main/java/com/codename1/guibuilder/project/ProjectIO.java b/scripts/guibuilder/common/src/main/java/com/codename1/guibuilder/project/ProjectIO.java new file mode 100644 index 00000000000..0daed7f242b --- /dev/null +++ b/scripts/guibuilder/common/src/main/java/com/codename1/guibuilder/project/ProjectIO.java @@ -0,0 +1,88 @@ +package com.codename1.guibuilder.project; + +import com.codename1.io.FileSystemStorage; +import com.codename1.io.Util; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +public final class ProjectIO { + public static final String INPUT_PROPERTY = "guibuilder.input"; + + private ProjectIO() { } + + public static ProjectBinding loadBinding() { + String path = System.getProperty(INPUT_PROPERTY); + if (path == null || path.trim().length() == 0) return null; + try { + String content = read(path); + ProjectBinding binding = ProjectBinding.parse(content); + return binding.isValid() ? binding : null; + } catch (IOException ex) { + return null; + } + } + + public static List findGuiFiles(String guiDir) { + List files = new ArrayList<>(); + collect(fsUrl(guiDir), files); + Collections.sort(files); + return files; + } + + private static void collect(String dir, List files) { + FileSystemStorage fs = FileSystemStorage.getInstance(); + if (dir == null || !fs.exists(dir) || !fs.isDirectory(dir)) return; + try { + for (String child : fs.listFiles(dir)) { + String path = dir + (dir.endsWith("/") ? "" : "/") + child; + if (fs.isDirectory(path)) collect(path, files); + else if (path.endsWith(".gui")) files.add(path); + } + } catch (IOException ignored) { } + } + + public static String read(String path) throws IOException { + InputStream in = null; + try { + in = FileSystemStorage.getInstance().openInputStream(fsUrl(path)); + return Util.readToString(in, "UTF-8"); + } finally { + Util.cleanup(in); + } + } + + public static boolean exists(String path) { + return path != null && FileSystemStorage.getInstance().exists(fsUrl(path)); + } + + public static void write(String path, String content) throws IOException { + OutputStream out = null; + try { + String url = fsUrl(path); + ensureParent(url); + out = FileSystemStorage.getInstance().openOutputStream(url); + out.write(content.getBytes("UTF-8")); + } finally { + Util.cleanup(out); + } + } + + private static void ensureParent(String path) { + int slash = path.lastIndexOf('/'); + if (slash <= "file://".length()) return; + String parent = path.substring(0, slash); + FileSystemStorage fs = FileSystemStorage.getInstance(); + if (fs.exists(parent)) return; + ensureParent(parent); + fs.mkdir(parent); + } + + public static String fsUrl(String path) { + if (path == null || path.startsWith("file://") || path.indexOf("://") > 0) return path; + return "file://" + path; + } +} diff --git a/scripts/guibuilder/common/src/main/java/com/codename1/guibuilder/ui/ComponentPreviewFactory.java b/scripts/guibuilder/common/src/main/java/com/codename1/guibuilder/ui/ComponentPreviewFactory.java new file mode 100644 index 00000000000..6a8ee8813e9 --- /dev/null +++ b/scripts/guibuilder/common/src/main/java/com/codename1/guibuilder/ui/ComponentPreviewFactory.java @@ -0,0 +1,329 @@ +package com.codename1.guibuilder.ui; + +import com.codename1.components.SpanLabel; +import com.codename1.guibuilder.model.GuiDocument; +import com.codename1.ui.Button; +import com.codename1.ui.CheckBox; +import com.codename1.ui.Component; +import com.codename1.ui.Container; +import com.codename1.ui.Label; +import com.codename1.ui.FontImage; +import com.codename1.ui.RadioButton; +import com.codename1.ui.Slider; +import com.codename1.ui.Tabs; +import com.codename1.ui.TextArea; +import com.codename1.ui.TextField; +import com.codename1.ui.accessibility.AccessibilityGrouping; +import com.codename1.ui.accessibility.AccessibilityRole; +import com.codename1.ui.events.ActionEvent; +import com.codename1.ui.events.PointerEvent; +import com.codename1.ui.layouts.BorderLayout; +import com.codename1.ui.layouts.BoxLayout; +import com.codename1.ui.layouts.FlowLayout; +import com.codename1.ui.layouts.GridLayout; +import com.codename1.ui.layouts.Layout; +import com.codename1.ui.layouts.LayeredLayout; +import com.codename1.ui.plaf.Style; +import com.codename1.ui.geom.Dimension; +import com.codename1.ui.table.TableLayout; +import com.codename1.xml.Element; + +public final class ComponentPreviewFactory { + private static final String MAX_DESIGN_WIDTH = "gui.maxDesignWidth"; + private static final String MAX_DESIGN_HEIGHT = "gui.maxDesignHeight"; + public interface SelectionHandler { + void selected(Element element); + default void selected(Element element, boolean additive) { selected(element); } + void dragPressed(Element element, Component source, int x, int y); + boolean isDragActive(); + void editContent(Element element); + default void dragMoved(int x, int y) { } + default void dragReleased(int x, int y) { } + } + + private ComponentPreviewFactory() { } + + public static Component create(Element element, Element selected, SelectionHandler handler) { + String type = value(element, "type", "Container"); + int guidedWidth = integer(element, "guidedPreferredWidth", -1); + int guidedHeight = integer(element, "guidedPreferredHeight", -1); + Component component; + switch (type) { + case "Button" -> component = new Button(value(element, "text", "Button")) { + @Override protected Dimension calcPreferredSize() { + return guidedSize(this, super.calcPreferredSize(), guidedWidth, guidedHeight); + } + }; + case "Label" -> component = new Label(value(element, "text", "Label")) { + @Override protected Dimension calcPreferredSize() { + return guidedSize(this, super.calcPreferredSize(), guidedWidth, guidedHeight); + } + }; + case "SpanLabel" -> component = new SpanLabel(value(element, "text", "Wrapped label text")) { + @Override protected Dimension calcPreferredSize() { + return guidedSize(this, super.calcPreferredSize(), guidedWidth, guidedHeight); + } + }; + case "TextField" -> component = new TextField(value(element, "text", ""), value(element, "hint", "Text field")) { + @Override protected Dimension calcPreferredSize() { + return guidedSize(this, super.calcPreferredSize(), guidedWidth, guidedHeight); + } + }; + case "TextArea" -> component = new TextArea(value(element, "text", "Text area")) { + @Override protected Dimension calcPreferredSize() { + return guidedSize(this, super.calcPreferredSize(), guidedWidth, guidedHeight); + } + }; + case "CheckBox" -> component = new CheckBox(value(element, "text", "Check box")) { + @Override protected Dimension calcPreferredSize() { + return guidedSize(this, super.calcPreferredSize(), guidedWidth, guidedHeight); + } + }; + case "RadioButton" -> component = new RadioButton(value(element, "text", "Radio button")) { + @Override protected Dimension calcPreferredSize() { + return guidedSize(this, super.calcPreferredSize(), guidedWidth, guidedHeight); + } + }; + case "Slider" -> component = new Slider() { + @Override protected Dimension calcPreferredSize() { + return guidedSize(this, super.calcPreferredSize(), guidedWidth, guidedHeight); + } + }; + case "Tabs" -> component = sizedTabs(element, selected, handler, guidedWidth, guidedHeight); + default -> component = sizedContainer(element, selected, handler, guidedWidth, guidedHeight); + } + component.setUIID(value(element, "uiid", type)); + applyAttributes(component, element); + if (component instanceof CheckBox check) { + check.setIcon(FontImage.createMaterial(check.isSelected() + ? FontImage.MATERIAL_CHECK_BOX : FontImage.MATERIAL_CHECK_BOX_OUTLINE_BLANK, + check.getUnselectedStyle())); + } else if (component instanceof RadioButton radio) { + radio.setIcon(FontImage.createMaterial(radio.isSelected() + ? FontImage.MATERIAL_RADIO_BUTTON_CHECKED : FontImage.MATERIAL_RADIO_BUTTON_UNCHECKED, + radio.getUnselectedStyle())); + } + component.putClientProperty("gui.originalBorder", component.getUnselectedStyle().getBorder()); + component.putClientProperty("gui.element", element); + String componentName = value(element, "name", type); + component.setName("preview." + componentName); + component.getSemantics().setIdentifier("guibuilder.preview." + componentName) + .setLabel(componentName + ", " + type) + .setDescription("Component on the GUI Builder design canvas") + .setGrouping(component instanceof Container ? AccessibilityGrouping.GROUP : AccessibilityGrouping.AUTO); + if (component instanceof Container) component.getSemantics().setRole(AccessibilityRole.GENERIC); + component.setDropTarget(false); + component.addPointerPressedListener(e -> { + handler.selected(element, additiveSelection(e)); + if (!"Form".equals(type)) handler.dragPressed(element, component, e.getX(), e.getY()); + }); + if (supportsInlineContent(type)) component.addLongPressListener(e -> handler.editContent(element)); + component.addPointerDraggedListener(e -> handler.dragMoved(e.getX(), e.getY())); + component.addPointerReleasedListener(e -> { + boolean completedDrag = handler.isDragActive(); + handler.dragReleased(e.getX(), e.getY()); + if (completedDrag) return; + if (!supportsInlineContent(type)) return; + long now = System.currentTimeMillis(); + Object previous = component.getClientProperty("gui.lastClick"); + component.putClientProperty("gui.lastClick", Long.valueOf(now)); + if (previous instanceof Long time && now - time.longValue() < 450) handler.editContent(element); + }); + return component; + } + + public static void stabilizeDesignStyles(Component component) { + component.setEnabled(true); + component.setFocusable(false); + component.setRippleEffect(false); + component.setPressedStyle(new Style(component.getUnselectedStyle())); + component.setSelectedStyle(new Style(component.getUnselectedStyle())); + if (component instanceof Container container) { + for (int i = 0; i < container.getComponentCount(); i++) stabilizeDesignStyles(container.getComponentAt(i)); + } + } + + private static Component sizedContainer(Element element, Element selected, SelectionHandler handler, + int guidedWidth, int guidedHeight) { + Container out = new Container(layout(element)) { + @Override protected Dimension calcPreferredSize() { + return guidedSize(this, super.calcPreferredSize(), guidedWidth, guidedHeight); + } + }; + // The UIID is applied by create() from the element's own uiid/type so the preview is + // styled by the project CSS exactly as the generated form will be at runtime. + for (int i = 0; i < element.getNumChildren(); i++) { + Object child = element.getChildAt(i); + if (child instanceof Element childElement && "component".equals(childElement.getTagName())) { + Component rendered = create(childElement, selected, handler); + if (out.getLayout() instanceof BorderLayout) { + out.add(GuiDocument.effectiveBorderConstraint(element, childElement), rendered); + } else if (out.getLayout() instanceof TableLayout table) { + int columns = integer(element, "tableLayoutColumns", 2); + int row = integer(childElement, "tableRow", i / Math.max(1, columns)); + int column = integer(childElement, "tableColumn", i % Math.max(1, columns)); + TableLayout.Constraint constraint = table.createConstraint(row, column) + .horizontalSpan(integer(childElement, "tableHorizontalSpan", 1)) + .verticalSpan(integer(childElement, "tableVerticalSpan", 1)); + int width = integer(childElement, "tableWidth", -1); + int height = integer(childElement, "tableHeight", -1); + if (width != -1) constraint.widthPercentage(width); + if (height != -1) constraint.heightPercentage(height); + out.add(constraint, rendered); + } else { + out.add(rendered); + } + } + } + if (out.getLayout() instanceof LayeredLayout) GuidedLayoutSupport.apply(element, out); + if (out.getComponentCount() == 0) out.add(new Label("Drop components here", "BuilderEmptyHint")); + return out; + } + + private static boolean additiveSelection(ActionEvent event) { + PointerEvent pointer = event == null ? null : event.getPointerEvent(); + return pointer != null && (pointer.isShiftDown() || pointer.isControlDown() || pointer.isMetaDown()); + } + + private static Component sizedTabs(Element element, Element selected, SelectionHandler handler, + int guidedWidth, int guidedHeight) { + Tabs tabs = new Tabs() { + @Override protected Dimension calcPreferredSize() { + return guidedSize(this, super.calcPreferredSize(), guidedWidth, guidedHeight); + } + }; + for (int i = 0; i < element.getNumChildren(); i++) { + Object child = element.getChildAt(i); + if (child instanceof Element e && "component".equals(e.getTagName())) { + tabs.addTab(value(e, "name", "Tab " + (i + 1)), create(e, selected, handler)); + } + } + if (tabs.getTabCount() == 0) tabs.addTab("Tab", new Label("Drop content here")); + return tabs; + } + + private static Layout layout(Element element) { + String layout = value(element, "layout", "BoxLayout"); + return switch (layout) { + case "BorderLayout" -> new DesignerBorderLayout(); + case "FlowLayout" -> new FlowLayout(); + case "GridLayout" -> new GridLayout(integer(element, "gridLayoutRows", 1), integer(element, "gridLayoutColumns", 2)); + case "TableLayout" -> new TableLayout(integer(element, "tableLayoutRows", 2), integer(element, "tableLayoutColumns", 2)); + case "LayeredLayout" -> new LayeredLayout(); + default -> "X".equals(element.getAttribute("boxLayoutAxis")) ? BoxLayout.x() : BoxLayout.y(); + }; + } + + /** Keeps a newly displaced edge component from consuming the entire designer surface. */ + private static final class DesignerBorderLayout extends BorderLayout { + @Override public void layoutContainer(Container parent) { + int contentWidth = Math.max(1, parent.getWidth() - parent.getStyle().getHorizontalPadding()); + int contentHeight = Math.max(1, parent.getHeight() - parent.getStyle().getVerticalPadding()); + capWidth(getWest(), Math.max(48, contentWidth * 30 / 100)); + capWidth(getEast(), Math.max(48, contentWidth * 30 / 100)); + capHeight(getNorth(), Math.max(36, contentHeight * 30 / 100)); + capHeight(getSouth(), Math.max(36, contentHeight * 30 / 100)); + super.layoutContainer(parent); + } + + private static void capWidth(Component component, int maximum) { + if (component == null) return; + component.putClientProperty(MAX_DESIGN_WIDTH, Integer.valueOf(maximum)); + component.setShouldCalcPreferredSize(true); + } + + private static void capHeight(Component component, int maximum) { + if (component == null) return; + component.putClientProperty(MAX_DESIGN_HEIGHT, Integer.valueOf(maximum)); + component.setShouldCalcPreferredSize(true); + } + } + + private static int integer(Element element, String name, int fallback) { + try { return Integer.parseInt(value(element, name, String.valueOf(fallback))); } + catch (NumberFormatException ex) { return fallback; } + } + + private static Dimension guidedSize(Component component, Dimension natural, int guidedWidth, int guidedHeight) { + if (guidedWidth > 0) natural.setWidth(guidedWidth); + if (guidedHeight > 0) natural.setHeight(guidedHeight); + Object maximumWidth = component.getClientProperty(MAX_DESIGN_WIDTH); + Object maximumHeight = component.getClientProperty(MAX_DESIGN_HEIGHT); + if (maximumWidth instanceof Integer value) natural.setWidth(Math.min(natural.getWidth(), value.intValue())); + if (maximumHeight instanceof Integer value) natural.setHeight(Math.min(natural.getHeight(), value.intValue())); + return natural; + } + + private static String value(Element element, String name, String fallback) { + String value = element.getAttribute(name); + return value == null || value.length() == 0 ? fallback : value; + } + + private static boolean supportsInlineContent(String type) { + return "Form".equals(type) || "Label".equals(type) || "SpanLabel".equals(type) + || "Button".equals(type) || "CheckBox".equals(type) || "RadioButton".equals(type) + || "TextField".equals(type) || "TextArea".equals(type); + } + + private static void applyAttributes(Component component, Element element) { + component.setEnabled(!"false".equals(value(element, "enabled", "true"))); + component.setVisible(!"false".equals(value(element, "visible", "true"))); + component.setRTL("true".equals(value(element, "rtl", "false"))); + if (component instanceof Label label) { + label.setGap(integer(element, "gap", label.getGap())); + label.setAlignment(alignment(value(element, "alignment", "left"))); + label.setTickerEnabled("true".equals(value(element, "tickerEnabled", "false"))); + } + if (component instanceof Button button) button.setToggle("true".equals(value(element, "toggle", "false"))); + if (component instanceof CheckBox check) check.setSelected("true".equals(value(element, "selected", "false"))); + if (component instanceof TextArea area) { + area.setColumns(integer(element, "columns", area.getColumns())); + area.setRows(integer(element, "rows", area.getRows())); + area.setMaxSize(integer(element, "maxSize", area.getMaxSize())); + area.setEditable(!"false".equals(value(element, "editable", "true"))); + area.setGrowByContent(!"false".equals(value(element, "growByContent", "true"))); + area.setConstraint(constraint(value(element, "constraint", "ANY"))); + } + if (component instanceof Container container) { + container.setScrollableX("true".equals(value(element, "scrollableX", "false"))); + container.setScrollableY("true".equals(value(element, "scrollableY", "false"))); + container.setTensileDragEnabled(false); + container.setAlwaysTensile(false); + container.setSmoothScrolling(false); + container.setScrollVisible(true); + } + if (component instanceof Slider slider) { + slider.setMinValue(integer(element, "minValue", slider.getMinValue())); + slider.setMaxValue(integer(element, "maxValue", slider.getMaxValue())); + slider.setProgress(integer(element, "progress", slider.getProgress())); + slider.setEditable("true".equals(value(element, "editable", "false"))); + slider.setInfinite("true".equals(value(element, "infinite", "false"))); + } + if (component instanceof Tabs tabs && tabs.getTabCount() > 0) { + int selected = Math.max(0, Math.min(tabs.getTabCount() - 1, integer(element, "selectedIndex", 0))); + tabs.setSelectedIndex(selected, false); + tabs.setTabPlacement(tabPlacement(value(element, "tabPlacement", "top"))); + } + } + + private static int alignment(String value) { + if ("center".equalsIgnoreCase(value)) return Component.CENTER; + if ("right".equalsIgnoreCase(value)) return Component.RIGHT; + return Component.LEFT; + } + + private static int constraint(String value) { + if ("EMAILADDR".equals(value)) return TextArea.EMAILADDR; + if ("PASSWORD".equals(value)) return TextArea.PASSWORD; + if ("NUMERIC".equals(value)) return TextArea.NUMERIC; + if ("URL".equals(value)) return TextArea.URL; + return TextArea.ANY; + } + + private static int tabPlacement(String value) { + if ("bottom".equalsIgnoreCase(value)) return Component.BOTTOM; + if ("left".equalsIgnoreCase(value)) return Component.LEFT; + if ("right".equalsIgnoreCase(value)) return Component.RIGHT; + return Component.TOP; + } +} diff --git a/scripts/guibuilder/common/src/main/java/com/codename1/guibuilder/ui/DragGuideOverlay.java b/scripts/guibuilder/common/src/main/java/com/codename1/guibuilder/ui/DragGuideOverlay.java new file mode 100644 index 00000000000..da8a51c32de --- /dev/null +++ b/scripts/guibuilder/common/src/main/java/com/codename1/guibuilder/ui/DragGuideOverlay.java @@ -0,0 +1,471 @@ +package com.codename1.guibuilder.ui; + +import com.codename1.ui.Component; +import com.codename1.ui.Container; +import com.codename1.ui.Graphics; +import com.codename1.ui.layouts.BorderLayout; +import java.util.ArrayList; +import java.util.List; + +/** Paints insertion and constraint geometry above the visual designer while dragging. */ +public final class DragGuideOverlay extends Component { + private String layout = "BoxLayout"; + private String axis = "Y"; + private String constraint; + private boolean valid = true; + private int pointerX; + private int pointerY; + private int parentX; + private int parentY; + private int parentW; + private int parentH; + private int targetX; + private int targetY; + private int targetW; + private int targetH; + private int sourceW; + private int sourceH; + private Component guideParent; + private Component guideSource; + private int snappedX; + private int snappedY; + private String snapDescription; + private boolean guideVisible; + private boolean resizeGuide; + private final List selections = new ArrayList<>(); + private Component primarySelection; + private int resizeX; + private int resizeY; + private int resizeW; + private int resizeH; + private List simulationItems = new ArrayList<>(); + private List simulationLinks = new ArrayList<>(); + private String simulationSummary; + + /** One component in the non-mutating layout simulation painted over the real designer. */ + public static final class GlassItem { + public final String name; + public final int oldX, oldY, oldW, oldH; + public final int newX, newY, newW, newH; + public final boolean active; + + public GlassItem(String name, int oldX, int oldY, int oldW, int oldH, + int newX, int newY, int newW, int newH, boolean active) { + this.name = name; + this.oldX = oldX; this.oldY = oldY; this.oldW = oldW; this.oldH = oldH; + this.newX = newX; this.newY = newY; this.newW = newW; this.newH = newH; + this.active = active; + } + + public boolean changed() { + return oldX != newX || oldY != newY || oldW != newW || oldH != newH; + } + } + + /** A durable constraint/reference edge in the simulated document. */ + public static final class DependencyLink { + public final String from; + public final String to; + public final int fromX, fromY, toX, toY; + public final boolean detached; + + public DependencyLink(String from, String to, int fromX, int fromY, int toX, int toY) { + this(from, to, fromX, fromY, toX, toY, false); + } + + public DependencyLink(String from, String to, int fromX, int fromY, int toX, int toY, + boolean detached) { + this.from = from; this.to = to; + this.fromX = fromX; this.fromY = fromY; this.toX = toX; this.toY = toY; + this.detached = detached; + } + } + + public DragGuideOverlay() { + setIgnorePointerEvents(true); + setVisible(false); + } + + public void showGuide(String layout, String axis, String constraint, boolean valid, + Component parent, Component target, Component source, int absoluteX, int absoluteY, + int absoluteSnapX, int absoluteSnapY, int plannedWidth, int plannedHeight, String snapDescription) { + this.layout = layout == null ? "BoxLayout" : layout; + this.axis = axis == null ? "Y" : axis; + this.constraint = constraint; + this.valid = valid; + this.guideParent = parent; + this.guideSource = source; + int ox = paintOriginX(); + int oy = paintOriginY(); + pointerX = absoluteX - ox; + pointerY = absoluteY - oy; + snappedX = absoluteSnapX - ox; + snappedY = absoluteSnapY - oy; + this.snapDescription = snapDescription; + int paddingLeft = parent.getStyle().getPaddingLeftNoRTL(); + int paddingRight = parent.getStyle().getPaddingRightNoRTL(); + int paddingTop = parent.getStyle().getPaddingTop(); + int paddingBottom = parent.getStyle().getPaddingBottom(); + parentX = parent.getAbsoluteX() + paddingLeft - ox; + parentY = parent.getAbsoluteY() + paddingTop - oy; + parentW = Math.max(1, parent.getWidth() - paddingLeft - paddingRight); + parentH = Math.max(1, parent.getHeight() - paddingTop - paddingBottom); + Component guideTarget = target == null ? parent : target; + targetX = guideTarget.getAbsoluteX() - ox; + targetY = guideTarget.getAbsoluteY() - oy; + targetW = guideTarget.getWidth(); + targetH = guideTarget.getHeight(); + sourceW = plannedWidth > 0 ? plannedWidth : source == null || source.getWidth() < 1 ? Math.max(48, targetW) : source.getWidth(); + sourceH = plannedHeight > 0 ? plannedHeight : source == null || source.getHeight() < 1 ? Math.max(32, targetH) : source.getHeight(); + guideVisible = true; + resizeGuide = false; + setVisible(true); + repaint(); + } + + public void showResize(Container parent, Component source, int absoluteX, int absoluteY, + int width, int height, String description) { + guideParent = parent; + guideSource = source; + int ox = paintOriginX(); + int oy = paintOriginY(); + resizeX = absoluteX - ox; + resizeY = absoluteY - oy; + resizeW = width; + resizeH = height; + snapDescription = description; + guideVisible = true; + resizeGuide = true; + setVisible(true); + repaint(); + } + + public void showSelection(Component component) { + selections.clear(); + if (component != null) selections.add(component); + primarySelection = component; + setVisible(component != null || guideVisible); + repaint(); + } + + public void showSelections(List components, Component primary) { + selections.clear(); + if (components != null) selections.addAll(components); + primarySelection = primary != null && selections.contains(primary) + ? primary : selections.isEmpty() ? null : selections.get(selections.size() - 1); + setVisible(!selections.isEmpty() || guideVisible || !simulationItems.isEmpty()); + repaint(); + } + + public void showSimulation(List items, List links, String summary) { + simulationItems = items == null ? new ArrayList<>() : new ArrayList<>(items); + simulationLinks = links == null ? new ArrayList<>() : new ArrayList<>(links); + simulationSummary = summary; + setVisible(!selections.isEmpty() || guideVisible || !simulationItems.isEmpty()); + repaint(); + } + + public void clearSimulation() { + simulationItems.clear(); + simulationLinks.clear(); + simulationSummary = null; + setVisible(!selections.isEmpty() || guideVisible); + repaint(); + } + + public void clearSelection() { + selections.clear(); + primarySelection = null; + setVisible(guideVisible); + repaint(); + } + + /** Absolute pixels occupied by the selection rectangle as it will be painted. */ + public int[] selectionPaintBounds() { + int[] local = selectionPaintLocalBounds(); + if (local == null) return null; + return new int[]{paintOriginX() + local[0], paintOriginY() + local[1], local[2], local[3]}; + } + + /** Coordinates passed to Graphics, whose origin is the overlay's parent—not the overlay itself. */ + public int[] selectionPaintLocalBounds() { + if (primarySelection == null || primarySelection.getParent() == null) return null; + return new int[]{primarySelection.getAbsoluteX() - paintOriginX(), primarySelection.getAbsoluteY() - paintOriginY(), + primarySelection.getWidth(), primarySelection.getHeight()}; + } + + public List selectionPaintBoundsList() { + List result = new ArrayList<>(); + for (Component selection : selections) { + if (selection == null || selection.getParent() == null) continue; + result.add(new int[]{selection.getAbsoluteX(), selection.getAbsoluteY(), + selection.getWidth(), selection.getHeight()}); + } + return result; + } + + private int paintOriginX() { return getParent() == null ? 0 : getParent().getAbsoluteX(); } + private int paintOriginY() { return getParent() == null ? 0 : getParent().getAbsoluteY(); } + + public void hideGuide() { + guideVisible = false; + resizeGuide = false; + simulationItems.clear(); + simulationLinks.clear(); + simulationSummary = null; + setVisible(!selections.isEmpty()); + repaint(); + } + + @Override + public void paint(Graphics g) { + if (!isVisible()) return; + super.paint(g); + int oldAlpha = g.getAlpha(); + paintSelection(g); + paintSimulation(g); + if (!guideVisible) { + g.setAlpha(oldAlpha); + return; + } + if (resizeGuide) { + g.setColor(0x20b486); + g.setAlpha(28); + g.fillRect(resizeX, resizeY, resizeW, resizeH); + g.setAlpha(255); + g.drawRect(resizeX, resizeY, resizeW, resizeH); + paintHandles(g, resizeX, resizeY, resizeW, resizeH, 0x20b486); + if (snapDescription != null) g.drawString(snapDescription, resizeX + 8, resizeY + 16); + g.setAlpha(oldAlpha); + return; + } + g.setColor(valid ? 0x20b486 : 0xd64545); + if ("LayeredLayout".equals(layout)) { + int x = clamp(snappedX, parentX, parentX + Math.max(0, parentW - sourceW)); + int y = clamp(snappedY, parentY, parentY + Math.max(0, parentH - sourceH)); + int w = Math.min(sourceW, parentW); + int h = Math.min(sourceH, parentH); + g.setAlpha(24); + g.fillRect(x, y, w, h); + g.setAlpha(190); + g.drawRect(x, y, w, h); + paintLayeredRelationships(g, x, y, w, h); + if (snapDescription != null && snapDescription.indexOf("baseline") >= 0 && guideSource != null) { + int baseline = guideSource.getBaseline(w, h); + if (baseline >= 0) { + g.setColor(0xbd45d6); + g.drawLine(parentX, y + baseline, parentX + parentW, y + baseline); + g.drawString("baseline", x + w + 5, y + baseline - 3); + } + } + g.setColor(0x20b486); + g.drawString(snapDescription == null ? "Free position" : snapDescription, x + 8, y + 14); + } else if ("BorderLayout".equals(layout)) { + int[] rect = borderRectangle(); + int x = rect[0], y = rect[1], w = rect[2], h = rect[3]; + g.setAlpha(100); + g.fillRect(x, y, w, h); + g.setAlpha(255); + g.drawRect(x, y, w, h); + g.drawString((constraint == null ? "CENTER" : constraint.toUpperCase()) + + (valid ? "" : " — OCCUPIED"), x + 8, y + 14); + } else if ("GridLayout".equals(layout) || "TableLayout".equals(layout) || "FlowLayout".equals(layout)) { + g.setAlpha(70); + g.fillRect(targetX, targetY, targetW, targetH); + g.setAlpha(255); + g.drawRect(targetX, targetY, targetW, targetH); + g.drawString(pointerX < targetX + targetW / 2 ? "Insert before" : "Insert after", + targetX + 8, targetY + 14); + } else { + g.setAlpha(255); + if ("X".equals(axis)) { + int line = pointerX < targetX + targetW / 2 ? targetX : targetX + targetW; + g.fillRect(line - 2, parentY, 4, parentH); + } else { + int line = pointerY < targetY + targetH / 2 ? targetY : targetY + targetH; + g.fillRect(parentX, line - 2, parentW, 4); + } + } + g.setAlpha(oldAlpha); + } + + private void paintSimulation(Graphics g) { + if (simulationItems.isEmpty()) return; + int ox = paintOriginX(); + int oy = paintOriginY(); + for (DependencyLink link : simulationLinks) { + g.setColor(link.detached ? 0xd64545 : 0x9b6cff); + g.setAlpha(105); + int x1 = link.fromX - ox, y1 = link.fromY - oy; + int x2 = link.toX - ox, y2 = link.toY - oy; + drawArrow(g, x1, y1, x2, y2); + } + for (GlassItem item : simulationItems) { + int oldX = item.oldX - ox, oldY = item.oldY - oy; + int newX = item.newX - ox, newY = item.newY - oy; + int color = item.active ? 0x4f8cff : 0xf29f3d; + if (item.changed()) { + g.setColor(0x7f8b99); + g.setAlpha(60); + g.drawRect(oldX, oldY, item.oldW, item.oldH); + drawArrow(g, oldX + item.oldW / 2, oldY + item.oldH / 2, + newX + item.newW / 2, newY + item.newH / 2); + } + g.setColor(color); + g.setAlpha(item.active ? 28 : 14); + g.fillRect(newX, newY, item.newW, item.newH); + g.setAlpha(item.active ? 210 : 135); + g.drawRect(newX, newY, item.newW, item.newH); + String delta = delta(item); + if (item.active) paintTag(g, item.name + (delta.length() == 0 ? "" : " " + delta), + newX + 4, newY + 3, color); + } + if (simulationSummary != null && simulationSummary.length() > 0) { + paintTag(g, simulationSummary, 12, 12, 0x263445); + } + g.setAlpha(255); + } + + private String delta(GlassItem item) { + String out = ""; + int dx = item.newX - item.oldX, dy = item.newY - item.oldY; + int dw = item.newW - item.oldW, dh = item.newH - item.oldH; + if (dx != 0 || dy != 0) out = "move " + signed(dx) + "," + signed(dy); + if (dw != 0 || dh != 0) out += (out.length() == 0 ? "" : " • ") + "size " + signed(dw) + "," + signed(dh); + return out; + } + + private String signed(int value) { return value > 0 ? "+" + value : String.valueOf(value); } + + private void paintTag(Graphics g, String text, int x, int y, int background) { + int width = Math.min(Math.max(20, g.getFont().stringWidth(text) + 10), Math.max(20, getWidth() - x - 4)); + int height = g.getFont().getHeight() + 5; + g.setColor(background); + g.setAlpha(235); + g.fillRect(x, y, width, height); + g.setColor(0xffffff); + g.setAlpha(255); + g.drawString(text, x + 5, y + 2); + } + + private void drawArrow(Graphics g, int x1, int y1, int x2, int y2) { + g.drawLine(x1, y1, x2, y2); + int dx = x2 - x1, dy = y2 - y1; + int scale = Math.max(1, Math.max(Math.abs(dx), Math.abs(dy))); + int baseX = x2 - dx * 9 / scale; + int baseY = y2 - dy * 9 / scale; + int wingX = -dy * 4 / scale; + int wingY = dx * 4 / scale; + g.drawLine(x2, y2, baseX + wingX, baseY + wingY); + g.drawLine(x2, y2, baseX - wingX, baseY - wingY); + } + + private void paintSelection(Graphics g) { + for (Component selection : selections) { + if (selection == null || selection.getParent() == null) continue; + int x = selection.getAbsoluteX() - paintOriginX(); + int y = selection.getAbsoluteY() - paintOriginY(); + int w = selection.getWidth(); + int h = selection.getHeight(); + boolean primary = selection == primarySelection; + g.setAlpha(primary ? 220 : 145); + g.setColor(0x4f8cff); + g.drawRect(x, y, w, h); + paintSelectionHandles(g, x, y, w, h, 0x4f8cff, primary); + } + } + + private void paintSelectionHandles(Graphics g, int x, int y, int w, int h, int color, + boolean primary) { + int handle = primary ? 11 : 9; + int half = handle / 2; + int[] xs = {x, x + w / 2, x + w}; + int[] ys = {y, y + h / 2, y + h}; + for (int xi = 0; xi < xs.length; xi++) { + for (int yi = 0; yi < ys.length; yi++) { + if (xi == 1 && yi == 1) continue; + int hx = xs[xi] - half; + int hy = ys[yi] - half; + if (primary) { + g.setColor(color); + g.setAlpha(245); + g.fillRect(hx, hy, handle, handle); + } else { + g.setColor(0xffffff); + g.setAlpha(235); + g.fillRect(hx, hy, handle, handle); + g.setColor(color); + g.setAlpha(220); + g.drawRect(hx, hy, handle, handle); + g.drawRect(hx + 1, hy + 1, handle - 2, handle - 2); + } + } + } + } + + private void paintHandles(Graphics g, int x, int y, int w, int h, int color) { + int handle = 8; + int half = handle / 2; + int[] xs = {x, x + w / 2, x + w}; + int[] ys = {y, y + h / 2, y + h}; + g.setColor(color); + for (int xi = 0; xi < xs.length; xi++) { + for (int yi = 0; yi < ys.length; yi++) { + if (xi == 1 && yi == 1) continue; + g.fillRect(xs[xi] - half, ys[yi] - half, handle, handle); + } + } + } + + private int[] borderRectangle() { + if (!valid && targetW > 0 && targetH > 0) return new int[]{targetX, targetY, targetW, targetH}; + int top = parentY; + int bottom = parentY + parentH; + int left = parentX; + int right = parentX + parentW; + if (guideParent instanceof Container container && container.getLayout() instanceof BorderLayout border) { + Component north = border.getNorth(); + Component south = border.getSouth(); + Component west = border.getWest(); + Component east = border.getEast(); + if (north != null && north != guideSource) top = north.getAbsoluteY() - paintOriginY() + north.getHeight(); + if (south != null && south != guideSource) bottom = south.getAbsoluteY() - paintOriginY(); + if (west != null && west != guideSource) left = west.getAbsoluteX() - paintOriginX() + west.getWidth(); + if (east != null && east != guideSource) right = east.getAbsoluteX() - paintOriginX(); + } + if ("North".equals(constraint)) return new int[]{parentX, parentY, parentW, Math.min(parentH, sourceH)}; + if ("South".equals(constraint)) return new int[]{parentX, Math.max(parentY, parentY + parentH - sourceH), parentW, Math.min(parentH, sourceH)}; + if ("West".equals(constraint)) return new int[]{parentX, top, Math.min(parentW, sourceW), Math.max(1, bottom - top)}; + if ("East".equals(constraint)) return new int[]{Math.max(parentX, parentX + parentW - sourceW), top, Math.min(parentW, sourceW), Math.max(1, bottom - top)}; + return new int[]{left, top, Math.max(1, right - left), Math.max(1, bottom - top)}; + } + + private void paintLayeredRelationships(Graphics g, int x, int y, int w, int h) { + if (!(guideParent instanceof Container parent)) return; + int threshold = 2; + g.setColor(0x4f8cff); + g.setAlpha(125); + for (int i = 0; i < parent.getComponentCount(); i++) { + Component sibling = parent.getComponentAt(i); + if (sibling == guideSource || sibling.getClientProperty("gui.element") == null) continue; + int sx = sibling.getAbsoluteX() - paintOriginX(); + int sy = sibling.getAbsoluteY() - paintOriginY(); + int sw = sibling.getWidth(); + int sh = sibling.getHeight(); + if (Math.abs(x - sx) <= threshold || Math.abs(x + w - sx - sw) <= threshold + || Math.abs(x + w / 2 - sx - sw / 2) <= threshold) { + int line = Math.abs(x + w / 2 - sx - sw / 2) <= threshold ? x + w / 2 + : Math.abs(x - sx) <= threshold ? x : x + w; + g.drawLine(line, parentY, line, parentY + parentH); + } + if (Math.abs(y - sy) <= threshold || Math.abs(y + h - sy - sh) <= threshold + || Math.abs(y + h / 2 - sy - sh / 2) <= threshold) { + int line = Math.abs(y + h / 2 - sy - sh / 2) <= threshold ? y + h / 2 + : Math.abs(y - sy) <= threshold ? y : y + h; + g.drawLine(parentX, line, parentX + parentW, line); + } + } + } + + private int clamp(int value, int min, int max) { return Math.max(min, Math.min(max, value)); } +} diff --git a/scripts/guibuilder/common/src/main/java/com/codename1/guibuilder/ui/GuidedLayoutSupport.java b/scripts/guibuilder/common/src/main/java/com/codename1/guibuilder/ui/GuidedLayoutSupport.java new file mode 100644 index 00000000000..71519a4a816 --- /dev/null +++ b/scripts/guibuilder/common/src/main/java/com/codename1/guibuilder/ui/GuidedLayoutSupport.java @@ -0,0 +1,141 @@ +package com.codename1.guibuilder.ui; + +import com.codename1.ui.Component; +import com.codename1.ui.Container; +import com.codename1.ui.layouts.LayeredLayout; +import com.codename1.xml.Element; +import java.util.ArrayList; +import java.util.List; + +/** + * Applies the persistent, name-based constraints used by the Guided Layout designer. + * LayeredLayout's native reference serialization is index based; names remain stable when + * components are reordered, so the builder stores names and resolves them at preview/runtime + * generation time. + */ +public final class GuidedLayoutSupport { + public static final String PREFERRED = "preferred"; + public static final String FIXED = "fixed"; + public static final String FILL = "fill"; + public static final String MATCH = "match"; + + private GuidedLayoutSupport() { } + + public static void apply(Element parentElement, Container parent) { + if (!(parent.getLayout() instanceof LayeredLayout layered)) return; + List elements = componentChildren(parentElement); + for (int i = 0; i < elements.size() && i < parent.getComponentCount(); i++) { + Element element = elements.get(i); + Component component = parent.getComponentAt(i); + layered.setInsets(component, value(element, "layeredInsets", "auto auto auto auto")); + String references = value(element, "guidedReferences", "-|-|-|-"); + String[] names = four(references, "-"); + Component[] refs = new Component[4]; + for (int side = 0; side < refs.length; side++) refs[side] = namedChild(parent, names[side]); + try { + layered.setReferenceComponents(component, refs); + layered.setReferencePositions(component, value(element, "guidedReferencePositions", "0 0 0 0")); + layered.setPercentInsetAnchorHorizontal(component, decimal(element, "guidedHorizontalAnchor", 0f)); + layered.setPercentInsetAnchorVertical(component, decimal(element, "guidedVerticalAnchor", 0f)); + } catch (IllegalArgumentException circularReference) { + // A malformed hand-edited file must remain designable. Ignore only its references; + // the inspector can then be used to repair the relationship. + layered.setReferenceComponents(component, new Component[]{null, null, null, null}); + } + } + } + + public static String horizontalPolicy(Element element) { + String explicit = element.getAttribute("guidedHorizontalSize"); + if (validPolicy(explicit)) return explicit; + String[] insets = insetValues(element); + return !"auto".equals(insets[1]) && !"auto".equals(insets[3]) ? FIXED : PREFERRED; + } + + public static String verticalPolicy(Element element) { + String explicit = element.getAttribute("guidedVerticalSize"); + if (validPolicy(explicit)) return explicit; + String[] insets = insetValues(element); + return !"auto".equals(insets[0]) && !"auto".equals(insets[2]) ? FIXED : PREFERRED; + } + + public static String[] insetValues(Element element) { + return cssFour(value(element, "layeredInsets", "auto auto auto auto"), "auto"); + } + + public static String[] referenceNames(Element element) { + return four(value(element, "guidedReferences", "-|-|-|-"), "-"); + } + + public static String[] referencePositions(Element element) { + return cssFour(value(element, "guidedReferencePositions", "0 0 0 0"), "0"); + } + + public static String joinInsets(String top, String right, String bottom, String left) { + return top + " " + right + " " + bottom + " " + left; + } + + public static String joinReferences(String top, String right, String bottom, String left) { + return cleanReference(top) + "|" + cleanReference(right) + "|" + + cleanReference(bottom) + "|" + cleanReference(left); + } + + public static String joinPositions(String top, String right, String bottom, String left) { + return top + " " + right + " " + bottom + " " + left; + } + + private static Component namedChild(Container parent, String name) { + if (name == null || name.length() == 0 || "-".equals(name)) return null; + for (int i = 0; i < parent.getComponentCount(); i++) { + Component child = parent.getComponentAt(i); + Object value = child.getClientProperty("gui.element"); + if (value instanceof Element element && name.equals(element.getAttribute("name"))) return child; + } + return null; + } + + private static List componentChildren(Element parent) { + List out = new ArrayList<>(); + for (int i = 0; i < parent.getNumChildren(); i++) { + Object child = parent.getChildAt(i); + if (child instanceof Element element && "component".equals(element.getTagName())) out.add(element); + } + return out; + } + + private static String[] four(String value, String fallback) { + String[] split = value == null ? new String[0] : value.split("\\|", -1); + String[] out = {fallback, fallback, fallback, fallback}; + for (int i = 0; i < out.length && i < split.length; i++) { + if (split[i] != null && split[i].trim().length() > 0) out[i] = split[i].trim(); + } + return out; + } + + private static String[] cssFour(String value, String fallback) { + String[] split = value == null ? new String[0] : value.trim().split("\\s+"); + if (split.length == 1) return new String[]{split[0], split[0], split[0], split[0]}; + if (split.length == 2) return new String[]{split[0], split[1], split[0], split[1]}; + if (split.length == 3) return new String[]{split[0], split[1], split[2], split[1]}; + if (split.length >= 4) return new String[]{split[0], split[1], split[2], split[3]}; + return new String[]{fallback, fallback, fallback, fallback}; + } + + private static boolean validPolicy(String value) { + return PREFERRED.equals(value) || FIXED.equals(value) || FILL.equals(value) || MATCH.equals(value); + } + + private static String cleanReference(String value) { + return value == null || value.trim().length() == 0 ? "-" : value.trim(); + } + + private static String value(Element element, String name, String fallback) { + String value = element.getAttribute(name); + return value == null || value.length() == 0 ? fallback : value; + } + + private static float decimal(Element element, String name, float fallback) { + try { return Float.parseFloat(value(element, name, String.valueOf(fallback))); } + catch (NumberFormatException ex) { return fallback; } + } +} diff --git a/scripts/guibuilder/common/src/test/java/com/codename1/guibuilder/model/GuiDocumentTest.java b/scripts/guibuilder/common/src/test/java/com/codename1/guibuilder/model/GuiDocumentTest.java new file mode 100644 index 00000000000..4af37d8f1ab --- /dev/null +++ b/scripts/guibuilder/common/src/test/java/com/codename1/guibuilder/model/GuiDocumentTest.java @@ -0,0 +1,354 @@ +package com.codename1.guibuilder.model; + +import com.codename1.xml.Element; +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.*; + +class GuiDocumentTest { + @Test + void undoRestoresTheSavedStateAndRedoRestoresTheModifiedState() { + GuiDocument document = GuiDocument.parse("Form.gui", + ""); + document.select(document.components().get(1)); + document.setAttribute("text", "After"); + assertTrue(document.isModified()); + + assertTrue(document.undo()); + assertFalse(document.isModified(), "undoing the first edit must return to the saved state"); + assertEquals("Before", document.components().get(1).getAttribute("text")); + + assertTrue(document.redo()); + assertTrue(document.isModified()); + assertEquals("After", document.components().get(1).getAttribute("text")); + } + + @Test + void preservesExistingXmlAndSupportsEditing() { + GuiDocument document = GuiDocument.parse("Login.gui", + "" + + "" + + ""); + + assertEquals(2, document.components().size()); + document.select(document.components().get(1)); + document.setAttribute("uiid", "LoginTitle"); + document.addComponent("Button"); + + String saved = document.toXml(); + assertTrue(saved.contains("custom=\"keep-me\"")); + assertTrue(saved.contains("uiid=\"LoginTitle\"")); + assertTrue(saved.contains("type=\"Button\"")); + assertTrue(document.isModified()); + } + + @Test + void createsUniqueComponentNames() { + GuiDocument document = GuiDocument.parse("Form.gui", ""); + assertEquals("button1", document.addComponent("Button").getAttribute("name")); + document.select(document.root()); + assertEquals("button2", document.addComponent("Button").getAttribute("name")); + } + + @Test + void newContainersUseGuidedLayoutByDefault() { + GuiDocument document = GuiDocument.parse("Form.gui", ""); + Element container = document.addComponent("Container"); + assertEquals("LayeredLayout", container.getAttribute("layout")); + } + + @Test + void doesNotDeleteRoot() { + GuiDocument document = GuiDocument.parse("Form.gui", ""); + assertFalse(document.deleteSelected()); + } + + @Test + void copiesAndPastesWithAUniqueName() { + GuiDocument document = GuiDocument.parse("Form.gui", + "" + + "" + + ""); + + document.select(document.components().get(1)); + String clipboard = document.copySelectedXml(); + document.select(document.root()); + Element pasted = document.pasteXml(clipboard); + + assertNotNull(pasted); + assertEquals("button2", pasted.getAttribute("name")); + assertEquals("Save", pasted.getAttribute("text")); + assertEquals(3, document.components().size()); + } + + @Test + void pastedContainerChildrenAlsoGetUniqueNamesAndKeepInternalRelationships() { + GuiDocument document = GuiDocument.parse("Form.gui", + "" + + "" + + "" + + "" + + ""); + + document.select(document.components().get(1)); + String clipboard = document.copySelectedXml(); + document.select(document.root()); + Element pasted = document.pasteXml(clipboard); + + assertNotNull(pasted); + assertEquals("card1", pasted.getAttribute("name")); + Element pastedAction = named(document, "action1"); + assertNotNull(named(document, "caption1")); + assertNotNull(pastedAction); + assertEquals("caption1|-|-|caption1", pastedAction.getAttribute("guidedReferences"), + "a pasted relationship must follow the pasted copy, not the original"); + assertEquals("caption1", pastedAction.getAttribute("guidedMatchWidth")); + assertEquals("caption|-|-|caption", named(document, "action").getAttribute("guidedReferences"), + "the original relationship must be untouched"); + } + + @Test + void renamingAComponentKeepsNamesUniqueAndRepointsEveryRelationship() { + GuiDocument document = GuiDocument.parse("Form.gui", + "" + + "" + + "" + + ""); + Element caption = document.components().get(1); + Element action = document.components().get(2); + + document.select(caption); + assertEquals("title", document.renameSelected("title")); + assertEquals("title|-|-|title", action.getAttribute("guidedReferences")); + assertEquals("title", action.getAttribute("guidedMatchWidth")); + assertEquals("title", action.getAttribute("guidedReferenceTarget")); + + document.select(action); + assertEquals("title1", document.renameSelected("title"), + "a duplicate name would break name-based references and generated fields"); + + assertTrue(document.undo()); + assertEquals("action", document.components().get(2).getAttribute("name")); + assertTrue(document.undo()); + assertEquals("caption", document.components().get(1).getAttribute("name")); + assertEquals("caption|-|-|caption", document.components().get(2).getAttribute("guidedReferences"), + "a rename and its reference updates must undo together"); + } + + @Test + void deletingAReferencedComponentLeavesNoDanglingRelationship() { + GuiDocument document = GuiDocument.parse("Form.gui", + "" + + "" + + "" + + ""); + + document.select(document.components().get(1)); + assertTrue(document.deleteSelected()); + Element action = document.components().get(1); + assertEquals("-|-|-|-", action.getAttribute("guidedReferences")); + assertNull(action.getAttribute("guidedMatchWidth")); + assertNull(action.getAttribute("guidedReferenceTarget")); + assertEquals("preferred", action.getAttribute("guidedHorizontalSize"), + "a match policy without a target must fall back to the component's own size"); + + assertTrue(document.undo()); + assertEquals("caption|-|-|caption", document.components().get(2).getAttribute("guidedReferences")); + } + + @Test + void reordersSiblingsAndMovesComponentsIntoContainers() { + GuiDocument document = GuiDocument.parse("Form.gui", + "" + + "" + + "" + + "" + + ""); + Element first = document.components().get(1); + Element second = document.components().get(2); + Element group = document.components().get(3); + + document.select(second); + assertTrue(document.moveSelectedTo(first)); + assertSame(second, document.root().getChildAt(0)); + + document.select(first); + assertTrue(document.moveSelectedTo(group)); + assertSame(first, group.getChildAt(0)); + assertEquals(2, document.root().getNumChildren()); + } + + @Test + void dropsSiblingsBeforeOrAfterTheIndicatedComponent() { + GuiDocument document = GuiDocument.parse("Form.gui", + "" + + "" + + "" + + "" + + ""); + Element first = document.components().get(1); + Element second = document.components().get(2); + Element third = document.components().get(3); + + document.select(first); + assertTrue(document.moveSelectedTo(second, true)); + assertSame(second, document.root().getChildAt(0)); + assertSame(first, document.root().getChildAt(1)); + assertSame(third, document.root().getChildAt(2)); + + document.select(third); + assertTrue(document.moveSelectedTo(second, false)); + assertSame(third, document.root().getChildAt(0)); + assertSame(second, document.root().getChildAt(1)); + assertSame(first, document.root().getChildAt(2)); + } + + @Test + void movesComponentsToAnExactParentSlotForCrossContainerSwaps() { + GuiDocument document = GuiDocument.parse("Form.gui", + "" + + "" + + "" + + ""); + Element left = document.components().get(1); + Element a = document.components().get(2); + Element b = document.components().get(3); + Element right = document.components().get(4); + Element c = document.components().get(5); + + document.select(c); + assertTrue(document.moveSelectedToParent(left, 1)); + assertSame(a, left.getChildAt(0)); + assertSame(c, left.getChildAt(1)); + assertSame(b, left.getChildAt(2)); + assertEquals(1, document.componentIndex(left, c)); + assertEquals(0, right.getNumChildren()); + } + + @Test + void preventsMovingAContainerIntoItsOwnDescendant() { + GuiDocument document = GuiDocument.parse("Form.gui", + "" + + "" + + "" + + ""); + Element outer = document.components().get(1); + Element inner = document.components().get(2); + + document.select(outer); + assertFalse(document.moveSelectedTo(inner)); + } + + @Test + void exposesDefaultUiidsAndTypeAwareParentLayout() { + GuiDocument document = GuiDocument.parse("Form.gui", + "" + + "" + + ""); + Element button = document.components().get(1); + + assertEquals("Button", document.effectiveUiid(button)); + assertEquals("LayeredLayout", document.parentLayout(button)); + button.setAttribute("uiid", "PrimaryAction"); + assertEquals("PrimaryAction", document.effectiveUiid(button)); + } + + @Test + void normalizesMissingAndDuplicateBorderLayoutConstraints() { + GuiDocument document = GuiDocument.parse("Form.gui", + "" + + "" + + "" + + "" + + ""); + Element first = document.components().get(1); + Element second = document.components().get(2); + Element third = document.components().get(3); + + assertEquals("Center", GuiDocument.effectiveBorderConstraint(document.root(), first)); + assertEquals("North", GuiDocument.effectiveBorderConstraint(document.root(), second)); + assertEquals("West", GuiDocument.effectiveBorderConstraint(document.root(), third)); + assertSame(second, GuiDocument.childAtBorderConstraint(document.root(), "North", null)); + assertNull(GuiDocument.childAtBorderConstraint(document.root(), "South", null)); + } + + @Test + void editsToolbarCommandsWithoutMixingThemIntoComponents() { + GuiDocument document = GuiDocument.parse("Form.gui", ""); + Element command = document.addCommand(); + document.setCommandAttribute(command, "name", "Save"); + document.setCommandAttribute(command, "placement", "left"); + + assertEquals(1, document.commands().size()); + assertEquals("Save", document.commands().get(0).getAttribute("name")); + assertEquals(1, document.components().size()); + assertTrue(document.toXml().contains("" + + "" + + ""); + Element card = document.components().get(1); + document.select(card); + document.setAttribute("layeredInsets", "20% 60% 60% 20%"); + document.setAttribute("layoutConstraint", "Center"); + + assertEquals("20% 60% 60% 20%", card.getAttribute("layeredInsets")); + assertEquals("Center", card.getAttribute("layoutConstraint")); + String xml = document.toXml(); + assertEquals(1, occurrences(xml.toLowerCase(), "layeredinsets=")); + assertEquals(1, occurrences(xml.toLowerCase(), "layoutconstraint=")); + GuiDocument reparsed = GuiDocument.parse("Form.gui", xml); + assertEquals("20% 60% 60% 20%", reparsed.components().get(1).getAttribute("layeredInsets")); + assertEquals("Center", reparsed.components().get(1).getAttribute("layoutConstraint")); + } + + @Test + void undoRedoTreatsACompoundPlacementAsOneEditAndRestoresNestedHierarchy() { + GuiDocument document = GuiDocument.parse("Form.gui", + "" + + "" + + ""); + Element button = document.components().get(3); + Element destination = document.components().get(4); + document.select(button); + document.beginTransaction(); + assertTrue(document.moveSelectedToParent(destination, 0)); + document.setAttribute("layoutConstraint", "Center"); + document.endTransaction(); + assertSame(destination, document.parentOf(document.selected())); + assertTrue(document.canUndo()); + + assertTrue(document.undo()); + Element restoredButton = document.selected(); + assertEquals("moveMe", restoredButton.getAttribute("name")); + assertEquals("inner", document.parentOf(restoredButton).getAttribute("name")); + assertFalse(document.canUndo(), "compound placement should create one undo entry"); + assertTrue(document.canRedo()); + + assertTrue(document.redo()); + assertEquals("destination", document.parentOf(document.selected()).getAttribute("name")); + assertEquals("Center", document.selected().getAttribute("layoutConstraint")); + } + + private static Element named(GuiDocument document, String name) { + for (Element element : document.components()) { + if (name.equals(element.getAttribute("name"))) return element; + } + return null; + } + + private static int occurrences(String text, String needle) { + int count = 0; + for (int at = 0; (at = text.indexOf(needle, at)) >= 0; at += needle.length()) count++; + return count; + } +} diff --git a/scripts/guibuilder/common/src/test/java/com/codename1/guibuilder/project/ProjectBindingTest.java b/scripts/guibuilder/common/src/test/java/com/codename1/guibuilder/project/ProjectBindingTest.java new file mode 100644 index 00000000000..e129ad0461e --- /dev/null +++ b/scripts/guibuilder/common/src/test/java/com/codename1/guibuilder/project/ProjectBindingTest.java @@ -0,0 +1,19 @@ +package com.codename1.guibuilder.project; + +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.*; + +class ProjectBindingTest { + @Test + void parsesModernProjectBinding() { + ProjectBinding binding = ProjectBinding.parse("# binding\n" + + "projectDir=/tmp/app/common\n" + + "guiDir=/tmp/app/common/src/main/guibuilder\n" + + "sourceDir=/tmp/app/common/src/main/java\n" + + "cssFile=/tmp/app/common/src/main/css/theme.css\n" + + "initialForm=com.example.Login\n"); + assertTrue(binding.isValid()); + assertEquals("com.example.Login", binding.initialForm()); + assertEquals("/tmp/app/common/src/main/css/theme.css", binding.cssFile()); + } +} diff --git a/scripts/guibuilder/demo-project/src/main/css/theme.css b/scripts/guibuilder/demo-project/src/main/css/theme.css new file mode 100644 index 00000000000..38ca4e7f013 --- /dev/null +++ b/scripts/guibuilder/demo-project/src/main/css/theme.css @@ -0,0 +1,49 @@ +Form { + background-color: #ffffff; + color: #172033; + padding: 3mm; +} + +Title { + background-color: #17233b; + color: #ffffff; + font-family: "native:MainBold"; + padding: 2mm; +} + +Label, SpanLabel { + background-color: transparent; + color: #26324a; + padding: 2mm; +} + +CheckBox, RadioButton { + background-color: transparent; + color: #26324a; +} + +Button { + background-color: white; + color: #2459b8; + border: 1px solid #315fce; + border-radius: 2mm; + padding: 2mm 4mm; + margin: 1mm; +} + +@media (prefers-color-scheme: dark) { + Form { background-color: black; color: #f5f8ff; } + Title { background-color: #071b4d; color: #f5f8ff; } + Label, SpanLabel { background-color: transparent; color: #f5f8ff; } + CheckBox, RadioButton { background-color: transparent; color: #f5f8ff; } + Button { background-color: #163575; color: #f5f8ff; border-color: #5876a9; } + TextField { background-color: #0e2a61; color: #f5f8ff; border-color: #5876a9; } +} + +TextField { + background-color: #f7f9fc; + color: #172033; + border: 1px solid #c8d1e2; + padding: 2mm; + margin: 1mm; +} diff --git a/scripts/guibuilder/demo-project/src/main/guibuilder/com/example/BorderDropForm.gui b/scripts/guibuilder/demo-project/src/main/guibuilder/com/example/BorderDropForm.gui new file mode 100644 index 00000000000..f98e600efb1 --- /dev/null +++ b/scripts/guibuilder/demo-project/src/main/guibuilder/com/example/BorderDropForm.gui @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/scripts/guibuilder/demo-project/src/main/guibuilder/com/example/BoxXLayoutForm.gui b/scripts/guibuilder/demo-project/src/main/guibuilder/com/example/BoxXLayoutForm.gui new file mode 100644 index 00000000000..256e5ab21be --- /dev/null +++ b/scripts/guibuilder/demo-project/src/main/guibuilder/com/example/BoxXLayoutForm.gui @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/scripts/guibuilder/demo-project/src/main/guibuilder/com/example/GridLayoutForm.gui b/scripts/guibuilder/demo-project/src/main/guibuilder/com/example/GridLayoutForm.gui new file mode 100644 index 00000000000..e8f4ee1d412 --- /dev/null +++ b/scripts/guibuilder/demo-project/src/main/guibuilder/com/example/GridLayoutForm.gui @@ -0,0 +1,7 @@ + + + + + + + diff --git a/scripts/guibuilder/demo-project/src/main/guibuilder/com/example/GuidedLayoutForm.gui b/scripts/guibuilder/demo-project/src/main/guibuilder/com/example/GuidedLayoutForm.gui new file mode 100644 index 00000000000..f390e46881f --- /dev/null +++ b/scripts/guibuilder/demo-project/src/main/guibuilder/com/example/GuidedLayoutForm.gui @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/scripts/guibuilder/demo-project/src/main/guibuilder/com/example/LoginForm.gui b/scripts/guibuilder/demo-project/src/main/guibuilder/com/example/LoginForm.gui new file mode 100644 index 00000000000..cc94588a539 --- /dev/null +++ b/scripts/guibuilder/demo-project/src/main/guibuilder/com/example/LoginForm.gui @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/scripts/guibuilder/demo-project/src/main/guibuilder/com/example/NestedLayoutsForm.gui b/scripts/guibuilder/demo-project/src/main/guibuilder/com/example/NestedLayoutsForm.gui new file mode 100644 index 00000000000..e128a24a8b2 --- /dev/null +++ b/scripts/guibuilder/demo-project/src/main/guibuilder/com/example/NestedLayoutsForm.gui @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/scripts/guibuilder/demo-project/src/main/guibuilder/com/example/TableLayoutForm.gui b/scripts/guibuilder/demo-project/src/main/guibuilder/com/example/TableLayoutForm.gui new file mode 100644 index 00000000000..618313ef4a6 --- /dev/null +++ b/scripts/guibuilder/demo-project/src/main/guibuilder/com/example/TableLayoutForm.gui @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/scripts/guibuilder/javase/pom.xml b/scripts/guibuilder/javase/pom.xml new file mode 100644 index 00000000000..22d9cb1230c --- /dev/null +++ b/scripts/guibuilder/javase/pom.xml @@ -0,0 +1,38 @@ + + + 4.0.0 + com.codenameone.guibuildercn1-guibuilder8.0-SNAPSHOT + com.codenameone + codenameone-guibuilder + codenameone-guibuilder + Standalone modern Codename One GUI Builder. + javasejavase + + ${project.basedir}/../common/src/test/java + ${project.basedir}/../../settings/javase/src/desktop/resourcesNativeTheme.res + + com.codenameonecodenameone-maven-plugin${cn1.plugin.version}add-se-sourcesgenerate-sourcesgenerate-javase-sources + org.codehaus.mojobuild-helper-maven-plugin3.4.0add-javase-test-sourcesgenerate-test-sourcesadd-test-source${project.basedir}/src/test/java + + + + com.codenameone.guibuilder${cn1app.name}-common${project.version} + com.codenameone.guibuilder${cn1app.name}-common${project.version}teststest + com.codenameonecodenameone-coretest + com.codenameonecodenameone-javasetest + org.junit.jupiterjunit-jupiter-api5.10.2test + org.junit.jupiterjunit-jupiter-engine5.10.2test + + executable-jarjavase + + com.codenameonecodenameone-corecompile + com.codenameonecodenameone-javasecompile + + + org.codehaus.mojoproperties-maven-plugin1.0.0initializeread-project-properties${basedir}/../common/codenameone_settings.properties + com.codenameonecodenameone-maven-plugin${cn1.plugin.version}generate-desktop-app-wrappergenerate-sourcesgenerate-desktop-app-wrapper + org.apache.maven.pluginsmaven-dependency-plugincopy-dependenciesprepare-packagecopy-dependencies${project.build.directory}/libsruntime + org.apache.maven.pluginsmaven-jar-plugintruelibs/com.codename1.guibuilder.CodenameOneGUIBuilderLauncherCodename One GUI BuilderCodename One GUI Builder${project.version} + + + diff --git a/scripts/guibuilder/javase/src/desktop/java/com/codename1/guibuilder/CodenameOneGUIBuilderLauncher.java b/scripts/guibuilder/javase/src/desktop/java/com/codename1/guibuilder/CodenameOneGUIBuilderLauncher.java new file mode 100644 index 00000000000..c6b19355ae2 --- /dev/null +++ b/scripts/guibuilder/javase/src/desktop/java/com/codename1/guibuilder/CodenameOneGUIBuilderLauncher.java @@ -0,0 +1,8 @@ +package com.codename1.guibuilder; + +public final class CodenameOneGUIBuilderLauncher { + private CodenameOneGUIBuilderLauncher() { } + public static void main(String[] args) throws Exception { + CodenameOneGUIBuilderStub.main(args); + } +} diff --git a/scripts/guibuilder/javase/src/desktop/java/com/codename1/guibuilder/CodenameOneGUIBuilderStub.java b/scripts/guibuilder/javase/src/desktop/java/com/codename1/guibuilder/CodenameOneGUIBuilderStub.java new file mode 100644 index 00000000000..4174f41a152 --- /dev/null +++ b/scripts/guibuilder/javase/src/desktop/java/com/codename1/guibuilder/CodenameOneGUIBuilderStub.java @@ -0,0 +1,589 @@ +package com.codename1.guibuilder; + +import com.codename1.impl.javase.JavaSEPort; +import com.codename1.ui.Display; +import java.awt.Desktop; +import java.awt.GraphicsDevice; +import java.awt.GraphicsEnvironment; +import java.awt.Toolkit; +import java.awt.Robot; +import java.awt.event.KeyEvent; +import java.awt.event.WindowEvent; +import java.awt.event.WindowListener; +import java.awt.image.BufferedImage; +import java.io.File; +import javax.imageio.ImageIO; +import javax.swing.JFrame; +import javax.swing.JCheckBoxMenuItem; +import javax.swing.JMenu; +import javax.swing.JMenuBar; +import javax.swing.JMenuItem; +import javax.swing.JTextPane; +import javax.swing.JComponent; +import javax.swing.KeyStroke; +import javax.swing.SwingUtilities; +import javax.swing.Timer; +import javax.swing.text.JTextComponent; +import javax.swing.undo.UndoManager; + +public final class CodenameOneGUIBuilderStub implements Runnable, WindowListener { + static final String APP_DISPLAY_NAME = "Codename One GUI Builder"; + private static final String APP_STORAGE_NAME = "CodenameOneGUIBuilder"; + private static final int APP_WIDTH = 1440; + private static final int APP_HEIGHT = 900; + private static JFrame frame; + private CodenameOneGUIBuilder app; + + public static void main(String[] args) { + System.setProperty("apple.awt.application.name", APP_DISPLAY_NAME); + System.setProperty("com.apple.mrj.application.apple.menu.about.name", APP_DISPLAY_NAME); + System.setProperty("sun.awt.application.name", APP_DISPLAY_NAME); + System.setProperty("sun.awt.X11.XWMClass", APP_STORAGE_NAME); + JavaSEPort.setNativeTheme("/NativeTheme.res"); + JavaSEPort.blockMonitors(); + JavaSEPort.setAppHomeDir("." + APP_STORAGE_NAME); + JavaSEPort.setExposeFilesystem(true); + JavaSEPort.setTablet(true); + JavaSEPort.setUseNativeInput(true); + JavaSEPort.setShowEDTViolationStacks(false); + JavaSEPort.setShowEDTWarnings(false); + JavaSEPort.setDesktopTitleBarMode("native"); + JavaSEPort.setDesktopInteractiveScrollbars(true); + JavaSEPort.setFontFaces(File.separatorChar == '\\' ? "ArialUnicodeMS" : "Arial", "SansSerif", "Monospaced"); + frame = new JFrame(APP_DISPLAY_NAME); + JavaSEPort.setDefaultPixelMilliRatio(Toolkit.getDefaultToolkit().getScreenResolution() / 25.4 * JavaSEPort.getRetinaScale()); + Display.init(frame.getContentPane()); + Display.getInstance().setProperty("AppName", APP_DISPLAY_NAME); + SwingUtilities.invokeLater(new CodenameOneGUIBuilderStub()); + } + + @Override + public void run() { + frame.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE); + frame.addWindowListener(this); + installMenus(); + GraphicsDevice device = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice(); + frame.setLocationByPlatform(true); + frame.setResizable(true); + frame.getContentPane().setPreferredSize(new java.awt.Dimension(APP_WIDTH, APP_HEIGHT)); + frame.getContentPane().setMinimumSize(new java.awt.Dimension(1000, 650)); + frame.pack(); + frame.setVisible(true); + Display.getInstance().callSerially(new Runnable() { + @Override public void run() { + app = new CodenameOneGUIBuilder(); + app.init(this); + app.start(); + SwingUtilities.invokeLater(() -> { + installDesignerPointerBridge(); + installMenus(); + scheduleOpenCss(); + scheduleScreenshot(); + scheduleEditorSelfTest(); + scheduleGuidedLayoutSelfTest(); + scheduleInteractionSelfTest(); + }); + } + }); + } + + private static void scheduleOpenCss() { + if (!Boolean.getBoolean("guibuilder.openCss")) return; + Timer open = new Timer(700, e -> Display.getInstance().callSerially(CodenameOneGUIBuilder::openActiveCssForTest)); + open.setRepeats(false); + open.start(); + } + + private static void installDesignerPointerBridge() { + java.awt.Container canvas = JavaSEPort.instance.getCanvas(); + canvas.addMouseMotionListener(new java.awt.event.MouseMotionAdapter() { + @Override public void mouseDragged(java.awt.event.MouseEvent event) { + int x = (int) Math.round(event.getX() * JavaSEPort.getRetinaScale()); + int y = (int) Math.round(event.getY() * JavaSEPort.getRetinaScale()); + Display.getInstance().callSerially(() -> CodenameOneGUIBuilder.activeDesktopPointerDragged(x, y)); + } + }); + canvas.addMouseListener(new java.awt.event.MouseAdapter() { + @Override public void mouseReleased(java.awt.event.MouseEvent event) { + int x = (int) Math.round(event.getX() * JavaSEPort.getRetinaScale()); + int y = (int) Math.round(event.getY() * JavaSEPort.getRetinaScale()); + Display.getInstance().callSerially(() -> CodenameOneGUIBuilder.activeDesktopPointerReleased(x, y)); + } + }); + } + + private static void scheduleEditorSelfTest() { + if (!Boolean.getBoolean("guibuilder.selfTest")) return; + Timer open = new Timer(900, e -> Display.getInstance().callSerially(CodenameOneGUIBuilder::openActiveCssForTest)); + open.setRepeats(false); + open.start(); + Timer exercise = new Timer(2200, e -> new Thread(() -> { + try { + final JTextPane[] editor = new JTextPane[1]; + SwingUtilities.invokeAndWait(() -> { + frame.setAlwaysOnTop(true); + frame.toFront(); + frame.requestFocus(); + editor[0] = findTextPane(frame.getLayeredPane()); + }); + if (editor[0] == null) throw new AssertionError("No live JTextPane was mounted by theme.css"); + final javax.swing.JScrollPane[] scroll = new javax.swing.JScrollPane[1]; + SwingUtilities.invokeAndWait(() -> { + java.awt.Container parent = editor[0].getParent(); + while (parent != null && !(parent instanceof javax.swing.JScrollPane)) parent = parent.getParent(); + scroll[0] = (javax.swing.JScrollPane) parent; + if (scroll[0] == null || editor[0].getMargin().left < 40 + || !scroll[0].getVerticalScrollBar().isVisible() + || scroll[0].getVerticalScrollBar().getWidth() < 10) { + throw new AssertionError("CSS editor chrome is missing: editorMargin=" + + editor[0].getMargin() + ", verticalBar=" + + (scroll[0] == null ? null : scroll[0].getVerticalScrollBar())); + } + }); + final java.awt.Point[] point = new java.awt.Point[1]; + final int[] initialLength = new int[1]; + SwingUtilities.invokeAndWait(() -> { + initialLength[0] = editor[0].getDocument().getLength(); + editor[0].setCaretPosition(Math.min(8, initialLength[0])); + point[0] = editor[0].getLocationOnScreen(); + }); + Robot robot = new Robot(); + robot.setAutoDelay(45); + robot.mouseMove(frame.getX() + frame.getWidth() / 2, frame.getY() + 10); + robot.mousePress(java.awt.event.InputEvent.BUTTON1_DOWN_MASK); + robot.mouseRelease(java.awt.event.InputEvent.BUTTON1_DOWN_MASK); + robot.mouseMove(point[0].x + 40, point[0].y + 24); + robot.mousePress(java.awt.event.InputEvent.BUTTON1_DOWN_MASK); + robot.mouseRelease(java.awt.event.InputEvent.BUTTON1_DOWN_MASK); + SwingUtilities.invokeAndWait(() -> { + frame.toFront(); + frame.requestFocus(); + editor[0].requestFocus(); + }); + long focusDeadline = System.currentTimeMillis() + 1800; + while (!editor[0].isFocusOwner() && System.currentTimeMillis() < focusDeadline) { + robot.mouseMove(point[0].x + 40, point[0].y + 24); + robot.mousePress(java.awt.event.InputEvent.BUTTON1_DOWN_MASK); + robot.mouseRelease(java.awt.event.InputEvent.BUTTON1_DOWN_MASK); + SwingUtilities.invokeAndWait(() -> { + frame.toFront(); + editor[0].requestFocus(); + }); + Thread.sleep(120); + } + if (!editor[0].isFocusOwner()) throw new AssertionError("macOS did not focus the mounted CSS editor"); + int[] keys = {KeyEvent.VK_Z, KeyEvent.VK_Z, KeyEvent.VK_T, KeyEvent.VK_E, KeyEvent.VK_S, KeyEvent.VK_T}; + for (int key : keys) { robot.keyPress(key); robot.keyRelease(key); } + robot.waitForIdle(); + final String[] text = new String[1]; + final String[] editorState = new String[1]; + SwingUtilities.invokeAndWait(() -> { + text[0] = editor[0].getText(); + java.awt.Component focus = java.awt.KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner(); + editorState[0] = "editable=" + editor[0].isEditable() + ", focusOwner=" + focus + + ", editorBounds=" + editor[0].getBounds() + ", showing=" + editor[0].isShowing() + + ", glassVisible=" + frame.getGlassPane().isVisible(); + }); + if (text[0].length() <= initialLength[0] || text[0].toLowerCase().indexOf("zztest") < 0) { + throw new AssertionError("Physical typing did not mutate the actual theme.css editor: " + editorState[0]); + } + int typedLength = text[0].length(); + int shortcutKey = (Toolkit.getDefaultToolkit().getMenuShortcutKeyMaskEx() + & java.awt.event.InputEvent.META_DOWN_MASK) != 0 ? KeyEvent.VK_META : KeyEvent.VK_CONTROL; + robot.keyPress(shortcutKey); + robot.keyPress(KeyEvent.VK_Z); + robot.keyRelease(KeyEvent.VK_Z); + robot.keyRelease(shortcutKey); + robot.waitForIdle(); + final int[] undoLength = new int[1]; + final JTextPane[] stillMounted = new JTextPane[1]; + SwingUtilities.invokeAndWait(() -> { + undoLength[0] = editor[0].getDocument().getLength(); + stillMounted[0] = findTextPane(frame.getLayeredPane()); + }); + if (stillMounted[0] != editor[0] || undoLength[0] >= typedLength) { + throw new AssertionError("Command-Z escaped the focused CSS editor instead of using its undo buffer"); + } + SwingUtilities.invokeAndWait(() -> editor[0].setText("Button { color: #e11919; }")); + Thread.sleep(450); + java.util.concurrent.atomic.AtomicInteger previewColor = new java.util.concurrent.atomic.AtomicInteger(-1); + Display.getInstance().callSerially(() -> previewColor.set( + CodenameOneGUIBuilder.activePreviewForegroundForTest(null))); + long deadline = System.currentTimeMillis() + 2500; + while (previewColor.get() == -1 && System.currentTimeMillis() < deadline) Thread.sleep(25); + if (previewColor.get() != 0xe11919) { + throw new AssertionError("CSS document changed but live preview color was 0x" + + Integer.toHexString(previewColor.get())); + } + System.out.println("GUIBUILDER_EDITOR_SELF_TEST_PASS"); + if (!"false".equals(System.getProperty("guibuilder.selfTest.exit"))) System.exit(0); + } catch (Throwable failure) { + failure.printStackTrace(); + System.err.println("GUIBUILDER_EDITOR_SELF_TEST_FAIL"); + System.exit(2); + } + }, "GUIBuilder editor self-test").start()); + exercise.setRepeats(false); + exercise.start(); + } + + private static JTextPane findTextPane(java.awt.Container parent) { + for (java.awt.Component child : parent.getComponents()) { + if (child instanceof JTextPane) return (JTextPane) child; + if (child instanceof java.awt.Container) { + JTextPane found = findTextPane((java.awt.Container) child); + if (found != null) return found; + } + } + return null; + } + + private static void scheduleGuidedLayoutSelfTest() { + if (!Boolean.getBoolean("guibuilder.guidedSelfTest")) return; + Timer exercise = new Timer(1800, e -> new Thread(() -> { + try { + SwingUtilities.invokeAndWait(() -> { + frame.setAlwaysOnTop(true); + frame.toFront(); + frame.requestFocus(); + }); + final int[][] initial = new int[1][]; + final int[][] sameWidth = new int[1][]; + final int[] baselines = new int[2]; + Display.getInstance().callSerially(() -> { + initial[0] = CodenameOneGUIBuilder.activePreviewBoundsForTest("primary"); + sameWidth[0] = CodenameOneGUIBuilder.activePreviewBoundsForTest("secondary"); + baselines[0] = CodenameOneGUIBuilder.activePreviewBaselineForTest("baselineLabel"); + baselines[1] = CodenameOneGUIBuilder.activePreviewBaselineForTest("baselineAction"); + }); + waitFor(() -> initial[0] != null && sameWidth[0] != null && baselines[0] >= 0 && baselines[1] >= 0, 2500); + if (initial[0][2] != sameWidth[0][2]) throw new AssertionError("Initial same-width relationship is not rendered"); + if (Math.abs(baselines[0] - baselines[1]) > 1) throw new AssertionError("Baseline relationship differs by " + + Math.abs(baselines[0] - baselines[1]) + "px"); + + Robot robot = new Robot(); + robot.setAutoDelay(55); + java.awt.Container canvas = JavaSEPort.instance.getCanvas(); + java.awt.Point canvasPoint = canvas.getLocationOnScreen(); + double scale = JavaSEPort.getRetinaScale(); + int startX = canvasPoint.x + (int) Math.round((initial[0][0] + initial[0][2] / 2.0) / scale); + int startY = canvasPoint.y + (int) Math.round((initial[0][1] + initial[0][3] / 2.0) / scale); + System.out.println("GUIDED_SELF_TEST_COORDS frame=" + frame.getBounds() + " canvas=" + canvas.getBounds() + + " canvasScreen=" + canvasPoint + " scale=" + scale + " initial=" + + java.util.Arrays.toString(initial[0]) + " robotStart=" + startX + "," + startY); + drag(robot, startX, startY, startX + (int) Math.round(70 / scale), startY + (int) Math.round(60 / scale)); + Thread.sleep(650); + + final int[][] moved = new int[1][]; + long moveDeadline = System.currentTimeMillis() + 2500; + while (System.currentTimeMillis() < moveDeadline) { + moved[0] = activeBounds("primary"); + if (moved[0] != null && (moved[0][0] != initial[0][0] || moved[0][1] != initial[0][1])) break; + Thread.sleep(60); + } + if (moved[0] == null || moved[0][0] == initial[0][0] && moved[0][1] == initial[0][1]) { + throw new AssertionError("Physical body drag did not change bounds: initial=" + + java.util.Arrays.toString(initial[0]) + " last=" + java.util.Arrays.toString(moved[0]) + + " designer=" + activeDesignerState()); + } + if (Math.abs(moved[0][0] - initial[0][0]) < 30 || Math.abs(moved[0][1] - initial[0][1]) < 25) { + throw new AssertionError("Physical body drag did not commit near the guide: initial=" + + java.util.Arrays.toString(initial[0]) + " moved=" + java.util.Arrays.toString(moved[0])); + } + + int resizeX = canvasPoint.x + (int) Math.round((moved[0][0] + moved[0][2]) / scale); + int resizeY = canvasPoint.y + (int) Math.round((moved[0][1] + moved[0][3] / 2.0) / scale); + drag(robot, resizeX, resizeY, resizeX + (int) Math.round(100 / scale), resizeY); + Thread.sleep(650); + + final int[][] resized = new int[1][]; + final int[][] linked = new int[1][]; + final String[] policy = new String[1]; + long resizeDeadline = System.currentTimeMillis() + 2500; + while (System.currentTimeMillis() < resizeDeadline) { + resized[0] = activeBounds("primary"); + linked[0] = activeBounds("secondary"); + policy[0] = activeAttribute("primary", "guidedHorizontalSize"); + if (resized[0] != null && linked[0] != null && resized[0][2] > moved[0][2] + 50) break; + Thread.sleep(60); + } + if (resized[0] == null || resized[0][2] <= moved[0][2] + 50) { + throw new AssertionError("Physical edge resize did not grow the width: moved=" + + java.util.Arrays.toString(moved[0]) + " last=" + java.util.Arrays.toString(resized[0])); + } + if (Math.abs(resized[0][0] - moved[0][0]) > 2 || Math.abs(resized[0][1] - moved[0][1]) > 2) { + throw new AssertionError("Right-edge resize moved an untouched edge: moved=" + + java.util.Arrays.toString(moved[0]) + " resized=" + java.util.Arrays.toString(resized[0])); + } + if (!"fixed".equals(policy[0])) throw new AssertionError("Resize did not persist the fixed horizontal policy: " + policy[0]); + if (resized[0][2] != linked[0][2]) throw new AssertionError("Linked width did not update after physical resize: " + + resized[0][2] + " vs " + linked[0][2]); + System.out.println("GUIBUILDER_GUIDED_LAYOUT_SELF_TEST_PASS moved=" + + java.util.Arrays.toString(moved[0]) + " resized=" + java.util.Arrays.toString(resized[0])); + if (!"false".equals(System.getProperty("guibuilder.guidedSelfTest.exit"))) System.exit(0); + } catch (Throwable failure) { + failure.printStackTrace(); + System.err.println("GUIBUILDER_GUIDED_LAYOUT_SELF_TEST_FAIL"); + System.exit(3); + } + }, "GUIBuilder guided-layout self-test").start()); + exercise.setRepeats(false); + exercise.start(); + } + + private static void scheduleInteractionSelfTest() { + if (!Boolean.getBoolean("guibuilder.interactionSelfTest")) return; + Timer exercise = new Timer(1800, e -> new Thread(() -> { + try { + SwingUtilities.invokeAndWait(() -> { + frame.setAlwaysOnTop(true); + frame.toFront(); + frame.requestFocus(); + }); + int[] primary = activeBounds("primary"); + int[] secondary = activeBounds("secondary"); + if (primary == null || secondary == null) throw new AssertionError("Guided fixture components are missing"); + Robot robot = new Robot(); + robot.setAutoDelay(55); + clickCn1(robot, secondary[0] + secondary[2] / 2, secondary[1] + secondary[3] / 2); + waitFor(() -> "secondary".equals(CodenameOneGUIBuilder.activeSelectedNameForTest()), 2000); + clickCn1(robot, primary[0] + primary[2] / 2, primary[1] + primary[3] / 2); + waitFor(() -> "primary".equals(CodenameOneGUIBuilder.activeSelectedNameForTest()), 2000); + + int[] desktop = activeNamedUiBounds("Desktop — full canvas"); + if (desktop == null) throw new AssertionError("Desktop canvas-mode button is missing"); + System.out.println("INTERACTION_SELF_TEST_DESKTOP bounds=" + java.util.Arrays.toString(desktop) + + " designer=" + activeDesignerState() + " mode=" + CodenameOneGUIBuilder.activeCanvasModeForTest()); + clickCn1(robot, desktop[0] + desktop[2] / 2, desktop[1] + desktop[3] / 2); + try { + waitFor(() -> "desktop".equals(CodenameOneGUIBuilder.activeCanvasModeForTest()), 2500); + } catch (AssertionError timeout) { + throw new AssertionError("Desktop mode click failed: bounds=" + java.util.Arrays.toString(desktop) + + " designer=" + activeDesignerState() + " mode=" + + CodenameOneGUIBuilder.activeCanvasModeForTest(), timeout); + } + int[] landscape = activeNamedUiBounds("Phone landscape"); + if (landscape == null) throw new AssertionError("Phone landscape button is missing after refresh"); + clickCn1(robot, landscape[0] + landscape[2] / 2, landscape[1] + landscape[3] / 2); + waitFor(() -> "phoneLandscape".equals(CodenameOneGUIBuilder.activeCanvasModeForTest()), 2500); + System.out.println("GUIBUILDER_INTERACTION_SELF_TEST_PASS selection=primary mode=phoneLandscape"); + System.exit(0); + } catch (Throwable failure) { + failure.printStackTrace(); + System.err.println("GUIBUILDER_INTERACTION_SELF_TEST_FAIL"); + System.exit(4); + } + }, "GUIBuilder interaction self-test").start()); + exercise.setRepeats(false); + exercise.start(); + } + + private static int[] activeNamedUiBounds(String name) throws Exception { + final int[][] value = new int[1][]; + java.util.concurrent.CountDownLatch latch = new java.util.concurrent.CountDownLatch(1); + Display.getInstance().callSerially(() -> { + value[0] = CodenameOneGUIBuilder.activeNamedUiBoundsForTest(name); + latch.countDown(); + }); + if (!latch.await(1, java.util.concurrent.TimeUnit.SECONDS)) throw new AssertionError("EDT did not return UI bounds"); + return value[0]; + } + + private static void clickCn1(Robot robot, int cn1X, int cn1Y) throws Exception { + java.awt.Container canvas = JavaSEPort.instance.getCanvas(); + java.awt.Point canvasPoint = canvas.getLocationOnScreen(); + double scale = JavaSEPort.getRetinaScale(); + robot.mouseMove(canvasPoint.x + (int) Math.round(cn1X / scale), + canvasPoint.y + (int) Math.round(cn1Y / scale)); + robot.mousePress(java.awt.event.InputEvent.BUTTON1_DOWN_MASK); + robot.delay(120); + robot.mouseRelease(java.awt.event.InputEvent.BUTTON1_DOWN_MASK); + robot.waitForIdle(); + } + + private static void drag(Robot robot, int fromX, int fromY, int toX, int toY) { + robot.mouseMove(fromX, fromY); + robot.delay(180); + robot.mousePress(java.awt.event.InputEvent.BUTTON1_DOWN_MASK); + robot.delay(220); + int steps = 12; + for (int i = 1; i <= steps; i++) { + robot.mouseMove(fromX + (toX - fromX) * i / steps, fromY + (toY - fromY) * i / steps); + robot.delay(35); + } + robot.delay(180); + robot.mouseRelease(java.awt.event.InputEvent.BUTTON1_DOWN_MASK); + robot.waitForIdle(); + } + + private static void waitFor(java.util.function.BooleanSupplier condition, long timeout) throws Exception { + long deadline = System.currentTimeMillis() + timeout; + while (!condition.getAsBoolean() && System.currentTimeMillis() < deadline) Thread.sleep(30); + if (!condition.getAsBoolean()) throw new AssertionError("Timed out waiting for Guided Layout UI state"); + } + + private static int[] activeBounds(String name) throws Exception { + final int[][] value = new int[1][]; + java.util.concurrent.CountDownLatch latch = new java.util.concurrent.CountDownLatch(1); + Display.getInstance().callSerially(() -> { + value[0] = CodenameOneGUIBuilder.activePreviewBoundsForTest(name); + latch.countDown(); + }); + if (!latch.await(1, java.util.concurrent.TimeUnit.SECONDS)) throw new AssertionError("EDT did not return preview bounds"); + return value[0]; + } + + private static String activeAttribute(String name, String attribute) throws Exception { + final String[] value = new String[1]; + java.util.concurrent.CountDownLatch latch = new java.util.concurrent.CountDownLatch(1); + Display.getInstance().callSerially(() -> { + value[0] = CodenameOneGUIBuilder.activeDocumentAttributeForTest(name, attribute); + latch.countDown(); + }); + if (!latch.await(1, java.util.concurrent.TimeUnit.SECONDS)) throw new AssertionError("EDT did not return document attribute"); + return value[0]; + } + + private static String activeDesignerState() throws Exception { + final String[] value = new String[1]; + java.util.concurrent.CountDownLatch latch = new java.util.concurrent.CountDownLatch(1); + Display.getInstance().callSerially(() -> { + value[0] = CodenameOneGUIBuilder.activeDesignerStateForTest(); + latch.countDown(); + }); + latch.await(1, java.util.concurrent.TimeUnit.SECONDS); + return value[0]; + } + + private static void installMenus() { + JMenuBar bar = new JMenuBar(); + JMenu file = new JMenu("File"); + file.add(item("Save Form", KeyEvent.VK_S, true, CodenameOneGUIBuilder::saveActiveDocument)); + file.add(item("Reload Project Forms", KeyEvent.VK_R, true, CodenameOneGUIBuilder::refreshActiveProject)); + file.addSeparator(); + file.add(item("Close GUI Builder", KeyEvent.VK_W, true, () -> Display.getInstance().exitApplication())); + + JMenu edit = new JMenu("Edit"); + edit.add(item("Undo", KeyEvent.VK_Z, true, CodenameOneGUIBuilderStub::undoFocusedEditorOrForm)); + JMenuItem redo = item("Redo", 0, false, CodenameOneGUIBuilderStub::redoFocusedEditorOrForm); + redo.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_Z, + Toolkit.getDefaultToolkit().getMenuShortcutKeyMaskEx() | KeyEvent.SHIFT_DOWN_MASK)); + edit.add(redo); + edit.addSeparator(); + edit.add(item("Cut", KeyEvent.VK_X, true, () -> editFocusedTextOrForm("cut"))); + edit.add(item("Copy", KeyEvent.VK_C, true, () -> editFocusedTextOrForm("copy"))); + edit.add(item("Paste", KeyEvent.VK_V, true, () -> editFocusedTextOrForm("paste"))); + edit.addSeparator(); + edit.add(item("Delete Component", KeyEvent.VK_BACK_SPACE, false, CodenameOneGUIBuilder::deleteActiveSelection)); + + JMenu view = new JMenu("View"); + JCheckBoxMenuItem dark = new JCheckBoxMenuItem("Dark Mode", CodenameOneGUIBuilder.isActiveDarkMode()); + dark.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_D, Toolkit.getDefaultToolkit().getMenuShortcutKeyMaskEx())); + dark.addActionListener(e -> CodenameOneGUIBuilder.toggleActiveDarkMode()); + view.add(dark); + view.addSeparator(); + view.add(item("Refresh Canvas", KeyEvent.VK_0, true, CodenameOneGUIBuilder::refreshActiveProject)); + + JMenu forms = new JMenu("Forms"); + String[] names = CodenameOneGUIBuilder.activeFormNames(); + for (int i = 0; i < names.length; i++) { + final int formIndex = i; + String name = names[i]; + String simple = name.substring(name.lastIndexOf('.') + 1); + JMenuItem form = item(simple, i < 9 ? KeyEvent.VK_1 + i : 0, i < 9, + () -> CodenameOneGUIBuilder.openActiveForm(formIndex)); + form.setToolTipText(name); + forms.add(form); + } + + JMenu code = new JMenu("Editors"); + code.add(item("Edit Companion Java Source", KeyEvent.VK_J, true, CodenameOneGUIBuilder::openActiveSource)); + code.add(item("Edit Binding Model", KeyEvent.VK_K, true, CodenameOneGUIBuilder::openActiveModel)); + code.add(item("Edit theme.css with Live Preview", KeyEvent.VK_T, true, CodenameOneGUIBuilder::openActiveCss)); + bar.add(file); + bar.add(edit); + bar.add(view); + bar.add(forms); + bar.add(code); + frame.setJMenuBar(bar); + try { + if (Desktop.isDesktopSupported() && Desktop.getDesktop().isSupported(Desktop.Action.APP_ABOUT)) { + Desktop.getDesktop().setAboutHandler(e -> javax.swing.JOptionPane.showMessageDialog(frame, + "Modern Maven-first visual editor for Codename One GUI forms.", APP_DISPLAY_NAME, + javax.swing.JOptionPane.INFORMATION_MESSAGE)); + } + } catch (Throwable ignored) { } + } + + private static UndoManager focusedEditorUndoManager() { + java.awt.Component focus = java.awt.KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner(); + if (!(focus instanceof JComponent)) return null; + Object value = ((JComponent) focus).getClientProperty("cn1.codeEditorUndoManager"); + return value instanceof UndoManager ? (UndoManager) value : null; + } + + private static void undoFocusedEditorOrForm() { + UndoManager manager = focusedEditorUndoManager(); + if (manager != null) { + if (manager.canUndo()) manager.undo(); + } else CodenameOneGUIBuilder.undoActiveEdit(); + } + + private static void redoFocusedEditorOrForm() { + UndoManager manager = focusedEditorUndoManager(); + if (manager != null) { + if (manager.canRedo()) manager.redo(); + } else CodenameOneGUIBuilder.redoActiveEdit(); + } + + private static void editFocusedTextOrForm(String operation) { + java.awt.Component focus = java.awt.KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner(); + if (focus instanceof JTextComponent) { + JTextComponent text = (JTextComponent) focus; + if ("cut".equals(operation)) text.cut(); + else if ("copy".equals(operation)) text.copy(); + else text.paste(); + return; + } + if ("cut".equals(operation)) CodenameOneGUIBuilder.cutActiveSelection(); + else if ("copy".equals(operation)) CodenameOneGUIBuilder.copyActiveSelection(); + else CodenameOneGUIBuilder.pasteActiveSelection(); + } + + private static JMenuItem item(String label, int key, boolean menuShortcut, Runnable action) { + JMenuItem item = new JMenuItem(label); + if (key > 0) { + int mask = menuShortcut ? Toolkit.getDefaultToolkit().getMenuShortcutKeyMaskEx() : 0; + item.setAccelerator(KeyStroke.getKeyStroke(key, mask)); + } + item.addActionListener(e -> action.run()); + return item; + } + + private static void scheduleScreenshot() { + String output = System.getProperty("guibuilder.screenshot"); + if (output == null || output.length() == 0) return; + Timer timer = new Timer(1600, e -> { + try { + frame.setAlwaysOnTop(true); + frame.toFront(); + BufferedImage image = new Robot().createScreenCapture(frame.getBounds()); + ImageIO.write(image, "png", new File(output)); + } catch (Exception ex) { + ex.printStackTrace(); + } finally { + if (!"false".equals(System.getProperty("guibuilder.screenshot.exit"))) Display.getInstance().exitApplication(); + } + }); + timer.setRepeats(false); + timer.start(); + } + + @Override public void windowClosing(WindowEvent e) { Display.getInstance().exitApplication(); } + @Override public void windowOpened(WindowEvent e) { } + @Override public void windowClosed(WindowEvent e) { } + @Override public void windowIconified(WindowEvent e) { } + @Override public void windowDeiconified(WindowEvent e) { } + @Override public void windowActivated(WindowEvent e) { } + @Override public void windowDeactivated(WindowEvent e) { } +} diff --git a/scripts/guibuilder/javase/src/test/java/com/codename1/guibuilder/CodeEditorInteractionTest.java b/scripts/guibuilder/javase/src/test/java/com/codename1/guibuilder/CodeEditorInteractionTest.java new file mode 100644 index 00000000000..e05b9c956e6 --- /dev/null +++ b/scripts/guibuilder/javase/src/test/java/com/codename1/guibuilder/CodeEditorInteractionTest.java @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Codename One designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Codename One through http://www.codenameone.com/ if you + * need additional information or have any questions. + */ + +package com.codename1.guibuilder; + +import com.codename1.ui.TextInputClient; +import com.codename1.ui.TextInputConfig; +import com.codename1.ui.TextInputState; +import com.codename1.ui.editor.CodePureEditor; +import com.codename1.ui.editor.CodeView; +import com.codename1.ui.editor.EditorHost; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +class CodeEditorInteractionTest { + @Test + void pureEditorAcceptsUserEditsUndoRedoAndProtectsOnlyGeneratedBlocks() { + CodePureEditor editor = new CodePureEditor(new TestEditorHost(), "code"); + CodeView view = (CodeView) editor.getView(); + String source = "// \nfinal int locked = 1;\n// \n" + + "// \nvoid handler() { }\n// \n"; + editor.cmd("setText", source); + editor.cmd("setProtectedMarkers", "// \n// "); + + int userOffset = source.indexOf("void handler"); + view.replaceRange(userOffset, userOffset, "public "); + assertTrue(editor.query("getText", null).contains("public void handler"), + "typing in the user region must mutate the document"); + + editor.cmd("undo", null); + assertFalse(editor.query("getText", null).contains("public void handler"), + "source and CSS edits must enter the editor-local undo buffer"); + editor.cmd("redo", null); + assertTrue(editor.query("getText", null).contains("public void handler")); + + String beforeProtectedAttempt = editor.query("getText", null); + int generatedOffset = beforeProtectedAttempt.indexOf("final int"); + view.replaceRange(generatedOffset, generatedOffset, "BROKEN "); + assertEquals(beforeProtectedAttempt, editor.query("getText", null), + "generated regions alone must reject edits"); + } + + @Test + void guiBuilderCanMoveThePureEditorCaretToAnExactSourceOffset() { + CodePureEditor editor = new CodePureEditor(new TestEditorHost(), "code"); + editor.cmd("setText", "alpha beta gamma"); + editor.cmd("setCursor", "11"); + assertEquals("11", editor.query("getCursor", null)); + + editor.cmd("setCursor", "999"); + assertEquals("16", editor.query("getCursor", null), "caret must clamp to the document"); + } + + private static final class TestEditorHost implements EditorHost { + @Override + public boolean isTextInputSupported() { + return false; + } + + @Override + public Object startTextInput(TextInputClient client, TextInputConfig config) { + return null; + } + + @Override + public void updateTextInputState(Object handle, TextInputState state) { + } + + @Override + public void stopTextInput(Object handle) { + } + + @Override + public void editorChanged() { + } + + @Override + public void fireEditorEvent(String type, String value) { + } + } +} diff --git a/scripts/guibuilder/javase/src/test/java/com/codename1/guibuilder/DesignerInteractionTest.java b/scripts/guibuilder/javase/src/test/java/com/codename1/guibuilder/DesignerInteractionTest.java new file mode 100644 index 00000000000..43c10b36f00 --- /dev/null +++ b/scripts/guibuilder/javase/src/test/java/com/codename1/guibuilder/DesignerInteractionTest.java @@ -0,0 +1,1334 @@ +package com.codename1.guibuilder; + +import com.codename1.guibuilder.model.GuiDocument; +import com.codename1.guibuilder.project.ProjectBinding; +import com.codename1.guibuilder.ui.ComponentPreviewFactory; +import com.codename1.mcp.MCP; +import com.codename1.ui.Component; +import com.codename1.ui.Button; +import com.codename1.ui.Container; +import com.codename1.ui.Label; +import com.codename1.ui.layouts.LayeredLayout; +import com.codename1.ui.layouts.BorderLayout; +import com.codename1.xml.Element; +import java.lang.reflect.Field; +import java.util.List; +import java.util.Map; +import javax.swing.JPanel; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +class DesignerInteractionTest { + @BeforeAll + static void initializeCodenameOneRuntime() { + if (!com.codename1.ui.Display.isInitialized()) com.codename1.ui.Display.init(new JPanel()); + } + + @Test + void aFixedGuidedWidthNeverFreezesTheThemeDerivedHeight() { + GuiDocument document = document("" + + "" + + "" + + ""); + Container rendered = (Container) ComponentPreviewFactory.create(document.root(), null, handler()); + Component fixed = rendered.getComponentAt(0); + Component natural = rendered.getComponentAt(1); + + fixed.getAllStyles().setPadding(24, 24, 10, 10); + natural.getAllStyles().setPadding(24, 24, 10, 10); + + assertEquals(300, fixed.getPreferredW()); + assertEquals(natural.getPreferredH(), fixed.getPreferredH(), + "fixing one axis must leave the other axis responsive to UIID/CSS metrics"); + assertFalse(fixed.hasFixedPreferredSize(), + "the designer must not use deprecated setPreferredW/H or preferredSizeStr state"); + } + + @Test + void boxSpacerKeepsTheExactSiblingAsItsHitTarget() throws Exception { + GuiDocument document = document(""); + Element b = document.components().get(2); + CodenameOneGUIBuilder builder = builder(document); + Container host = new Container(new LayeredLayout()); + host.setWidth(400); + host.setHeight(400); + Label spacer = new Label(); + spacer.putClientProperty("gui.dropTargetElement", b); + spacer.setX(20); + spacer.setY(40); + spacer.setWidth(200); + spacer.setHeight(24); + host.add(spacer); + + assertSame(b, builder.elementAt(host, 30, 45), + "the animated gap must not turn a precise insertion into a parent/end drop"); + } + + @Test + void aNewPressSelectsAnotherComponentWithoutRequiringAnyDrag() throws Exception { + GuiDocument document = document("" + + "" + + "" + + ""); + Element first = document.components().get(1); + Element second = document.components().get(2); + Container preview = (Container) render(document, 700, 400); + Container canvas = new Container(new LayeredLayout()); + canvas.setWidth(700); canvas.setHeight(400); canvas.add(preview); + CodenameOneGUIBuilder builder = builder(document); + set(builder, "canvasHost", canvas); + set(builder, "previewRoot", preview); + Component firstPreview = componentForElement(preview, first); + Component secondPreview = componentForElement(preview, second); + document.select(first); + + builder.handleDesignerPointerPressed(secondPreview.getAbsoluteX() + secondPreview.getWidth() / 2, + secondPreview.getAbsoluteY() + secondPreview.getHeight() / 2); + assertSame(second, document.selected(), "selection must change on press, before drag threshold"); + + // A missed release must not lock the designer onto the old component. The next press is a + // new gesture and immediately selects its own hit target. + builder.handleDesignerPointerPressed(firstPreview.getAbsoluteX() + firstPreview.getWidth() / 2, + firstPreview.getAbsoluteY() + firstPreview.getHeight() / 2); + assertSame(first, document.selected()); + } + + @Test + void modifierPressBuildsAMultiSelectionWithoutMovingAnything() throws Exception { + GuiDocument document = document("" + + "" + + "" + + ""); + Element first = document.components().get(1); + Element second = document.components().get(2); + Container preview = (Container) render(document, 700, 400); + Container canvas = new Container(new LayeredLayout()); + canvas.setWidth(700); canvas.setHeight(400); canvas.add(preview); + CodenameOneGUIBuilder builder = builder(document); + set(builder, "canvasHost", canvas); + set(builder, "previewRoot", preview); + Component firstPreview = componentForElement(preview, first); + Component secondPreview = componentForElement(preview, second); + String before = document.toXml(); + + builder.handleDesignerPointerPressed(firstPreview.getAbsoluteX() + 10, firstPreview.getAbsoluteY() + 10, false); + builder.handleDesignerPointerPressed(secondPreview.getAbsoluteX() + 10, secondPreview.getAbsoluteY() + 10, true); + + Map state = builder.mcpState(0); + assertEquals(java.util.Arrays.asList("first", "second"), state.get("selectedComponents")); + assertSame(first, document.selected(), "modifier-click must preserve the original sizing reference"); + builder.handleDesignerPointerPressed(firstPreview.getAbsoluteX() + 10, firstPreview.getAbsoluteY() + 10, false); + assertEquals(java.util.Arrays.asList("first", "second"), builder.mcpState(0).get("selectedComponents"), + "pressing a selected member must preserve the group so it can become the drag handle"); + assertSame(first, document.selected()); + assertEquals(before, document.toXml(), "selection must never mutate layout data"); + } + + @Test + void sameWidthStretchesEverySelectionMemberToTheStableReferenceWidth() throws Exception { + GuiDocument document = document("" + + "" + + "" + + "" + + ""); + Element reference = document.components().get(1); + Element small = document.components().get(2); + Element medium = document.components().get(3); + Container preview = (Container) render(document, 760, 520); + Container canvas = new Container(new LayeredLayout()); + canvas.setWidth(760); canvas.setHeight(520); canvas.add(preview); + CodenameOneGUIBuilder builder = builder(document); + set(builder, "canvasHost", canvas); + set(builder, "previewRoot", preview); + Component referencePreview = componentForElement(preview, reference); + Component smallPreview = componentForElement(preview, small); + Component mediumPreview = componentForElement(preview, medium); + String originalXml = document.toXml(); + + builder.handleDesignerPointerPressed(referencePreview.getAbsoluteX() + referencePreview.getWidth() / 2, + referencePreview.getAbsoluteY() + referencePreview.getHeight() / 2, false); + builder.handleDesignerPointerPressed(smallPreview.getAbsoluteX() + smallPreview.getWidth() / 2, + smallPreview.getAbsoluteY() + smallPreview.getHeight() / 2, true); + builder.handleDesignerPointerPressed(mediumPreview.getAbsoluteX() + mediumPreview.getWidth() / 2, + mediumPreview.getAbsoluteY() + mediumPreview.getHeight() / 2, true); + assertSame(reference, document.selected()); + + builder.applySelectionAction("matchWidth"); + + Container matched = (Container) render(document, 760, 520); + assertEquals(320, componentForElement(matched, reference).getWidth(), 1); + assertEquals(320, componentForElement(matched, small).getWidth(), 1); + assertEquals(320, componentForElement(matched, medium).getWidth(), 1); + assertEquals("reference", small.getAttribute("guidedMatchWidth")); + assertEquals("reference", medium.getAttribute("guidedMatchWidth")); + assertTrue(document.undo()); + assertEquals(originalXml, document.toXml(), "same-size must be one atomic undo step"); + assertFalse(document.canUndo()); + } + + @Test + void groupedGuidedDropTranslatesEverySelectedRectangleAndIsOneUndoStep() throws Exception { + GuiDocument document = document("" + + "" + + "" + + ""); + Element first = document.components().get(1); + Element second = document.components().get(2); + Container preview = (Container) render(document, 800, 500); + Container canvas = new Container(new LayeredLayout()); + canvas.setWidth(800); canvas.setHeight(500); canvas.add(preview); + CodenameOneGUIBuilder builder = builder(document); + set(builder, "canvasHost", canvas); + set(builder, "previewRoot", preview); + Component firstPreview = componentForElement(preview, first); + Component secondPreview = componentForElement(preview, second); + int[] firstBefore = bounds(firstPreview); + int[] secondBefore = bounds(secondPreview); + String xmlBefore = document.toXml(); + builder.handleDesignerPointerPressed(firstPreview.getAbsoluteX() + 10, firstPreview.getAbsoluteY() + 10, false); + builder.handleDesignerPointerPressed(secondPreview.getAbsoluteX() + 10, secondPreview.getAbsoluteY() + 10, true); + + CodenameOneGUIBuilder.DropPlan plan = new CodenameOneGUIBuilder.DropPlan(); + plan.document = document; plan.target = document.root(); plan.parent = document.root(); + plan.layout = "LayeredLayout"; plan.valid = true; + plan.snapX = firstPreview.getAbsoluteX() + 80; + plan.snapY = firstPreview.getAbsoluteY() + 55; + plan.snapW = firstPreview.getWidth(); plan.snapH = firstPreview.getHeight(); + assertTrue(builder.applyGroupedGuidedDrop(first, plan)); + + Container moved = (Container) render(document, 800, 500); + int[] firstAfter = bounds(componentForElement(moved, first)); + int[] secondAfter = bounds(componentForElement(moved, second)); + assertEquals(firstAfter[0] - firstBefore[0], secondAfter[0] - secondBefore[0]); + assertEquals(firstAfter[1] - firstBefore[1], secondAfter[1] - secondBefore[1]); + assertEquals(firstBefore[2], firstAfter[2]); + assertEquals(firstBefore[3], firstAfter[3]); + assertEquals(secondBefore[2], secondAfter[2]); + assertEquals(secondBefore[3], secondAfter[3]); + assertTrue(document.canUndo()); + assertTrue(document.undo()); + assertEquals(xmlBefore, document.toXml(), "the whole group move must undo atomically"); + assertFalse(document.canUndo(), "group movement must contribute exactly one undo entry"); + } + + @Test + void groupedDragPreviewShowsEveryMemberAndMatchesTheAtomicCommit() throws Exception { + GuiDocument document = document("" + + "" + + "" + + ""); + Element first = document.components().get(1); + Element second = document.components().get(2); + Container preview = (Container) render(document, 800, 500); + Container canvas = new Container(new LayeredLayout()); + canvas.setWidth(800); canvas.setHeight(500); canvas.add(preview); + CodenameOneGUIBuilder builder = builder(document); + set(builder, "canvasHost", canvas); + set(builder, "previewRoot", preview); + Component firstPreview = componentForElement(preview, first); + Component secondPreview = componentForElement(preview, second); + builder.handleDesignerPointerPressed(firstPreview.getAbsoluteX() + 10, firstPreview.getAbsoluteY() + 10, false); + builder.handleDesignerPointerPressed(secondPreview.getAbsoluteX() + 10, secondPreview.getAbsoluteY() + 10, true); + + CodenameOneGUIBuilder.DropPlan plan = new CodenameOneGUIBuilder.DropPlan(); + plan.document = document; plan.target = document.root(); plan.parent = document.root(); + plan.layout = "LayeredLayout"; plan.valid = true; + plan.snapX = firstPreview.getAbsoluteX() + 95; + plan.snapY = firstPreview.getAbsoluteY() + 60; + plan.snapW = firstPreview.getWidth(); plan.snapH = firstPreview.getHeight(); + String originalXml = document.toXml(); + + CodenameOneGUIBuilder.GuidedSimulation simulation = builder.simulateGroupedGuidedDrop(first, plan); + + assertNotNull(simulation); + assertEquals(originalXml, document.toXml(), "a group glass preview must not mutate the document"); + assertEquals(2, simulation.items.size(), "every selected component needs a moving glass rectangle"); + com.codename1.guibuilder.ui.DragGuideOverlay.GlassItem predictedFirst = simulation.items.stream() + .filter(item -> "first".equals(item.name)).findFirst().orElseThrow(); + com.codename1.guibuilder.ui.DragGuideOverlay.GlassItem predictedSecond = simulation.items.stream() + .filter(item -> "second".equals(item.name)).findFirst().orElseThrow(); + assertEquals(predictedFirst.newX - predictedFirst.oldX, predictedSecond.newX - predictedSecond.oldX); + assertEquals(predictedFirst.newY - predictedFirst.oldY, predictedSecond.newY - predictedSecond.oldY); + assertTrue(predictedFirst.active); + assertFalse(predictedSecond.active); + + assertTrue(builder.applyGroupedGuidedDrop(first, plan)); + Container committed = (Container) render(document, 800, 500); + Component committedFirst = componentForElement(committed, first); + Component committedSecond = componentForElement(committed, second); + assertEquals(predictedFirst.newX, committedFirst.getAbsoluteX(), 2); + assertEquals(predictedFirst.newY, committedFirst.getAbsoluteY(), 2); + assertEquals(predictedSecond.newX, committedSecond.getAbsoluteX(), 2); + assertEquals(predictedSecond.newY, committedSecond.getAbsoluteY(), 2); + } + + @Test + void groupedGuidedDropPreservesInternalBindingsAndLeavesOutsideDependentsInPlace() throws Exception { + GuiDocument document = document("" + + "" + + "" + + "" + + "" + + ""); + Element primary = document.components().get(2); + Element secondary = document.components().get(3); + Element outsideDependent = document.components().get(4); + Container preview = (Container) render(document, 900, 650); + Container canvas = new Container(new LayeredLayout()); + canvas.setWidth(900); canvas.setHeight(650); canvas.add(preview); + CodenameOneGUIBuilder builder = builder(document); + set(builder, "canvasHost", canvas); + set(builder, "previewRoot", preview); + Component primaryPreview = componentForElement(preview, primary); + Component secondaryPreview = componentForElement(preview, secondary); + int[] dependentBefore = bounds(componentForElement(preview, outsideDependent)); + builder.handleDesignerPointerPressed(primaryPreview.getAbsoluteX() + 10, primaryPreview.getAbsoluteY() + 10, false); + builder.handleDesignerPointerPressed(secondaryPreview.getAbsoluteX() + 10, secondaryPreview.getAbsoluteY() + 10, true); + + CodenameOneGUIBuilder.DropPlan plan = new CodenameOneGUIBuilder.DropPlan(); + plan.document = document; plan.target = document.root(); plan.parent = document.root(); + plan.layout = "LayeredLayout"; plan.valid = true; + plan.snapX = primaryPreview.getAbsoluteX() + 120; + plan.snapY = primaryPreview.getAbsoluteY() + 80; + plan.snapW = primaryPreview.getWidth(); plan.snapH = primaryPreview.getHeight(); + assertTrue(builder.applyGroupedGuidedDrop(primary, plan)); + + assertEquals("primary", secondary.getAttribute("guidedMatchWidth")); + assertTrue(secondary.getAttribute("guidedReferences").contains("primary"), + "relationships entirely inside the selection must survive a group move"); + assertFalse(primary.getAttribute("guidedReferences").contains("outsideAnchor"), + "the group root must detach from positioning references outside the selection"); + Container moved = (Container) render(document, 900, 650); + assertArrayEquals(dependentBefore, bounds(componentForElement(moved, outsideDependent)), + "an unselected dependent must be rebased rather than pulled along with the group"); + assertEquals(componentForElement(moved, primary).getWidth(), componentForElement(moved, secondary).getWidth(), 1); + } + + @Test + void layeredDropPersistsTheGuidedRectangleAndRerendersThere() throws Exception { + GuiDocument document = document(""); + Element card = document.components().get(1); + CodenameOneGUIBuilder builder = builder(document); + Container canvas = new Container(new LayeredLayout()); + Container parentPreview = new Container(new LayeredLayout()); + parentPreview.putClientProperty("gui.element", document.root()); + parentPreview.setX(50); + parentPreview.setY(70); + parentPreview.setWidth(500); + parentPreview.setHeight(400); + Label source = new Label("Card"); + source.putClientProperty("gui.element", card); + source.setWidth(100); + source.setHeight(80); + parentPreview.add(source); + canvas.add(parentPreview); + set(builder, "canvasHost", canvas); + + CodenameOneGUIBuilder.DropPlan plan = new CodenameOneGUIBuilder.DropPlan(); + plan.document = document; + plan.target = document.root(); + plan.parent = document.root(); + plan.layout = "LayeredLayout"; + plan.valid = true; + plan.snapX = 150; + plan.snapY = 150; + assertTrue(builder.applyDropPlan(card, plan, plan.snapX, plan.snapY)); + assertEquals("78px 298px 238px 98px", card.getAttribute("layeredInsets"), document.toXml()); + + Component rendered = ComponentPreviewFactory.create(document.root(), card, handler()); + rendered.setWidth(500); + rendered.setHeight(400); + ((Container) rendered).layoutContainer(); + Component renderedCard = ((Container) rendered).getComponentAt(0); + assertEquals(100, renderedCard.getX(), 2, "rerendered left edge must match the guide"); + assertEquals(80, renderedCard.getY(), 2, "rerendered top edge must match the guide"); + assertEquals(100, renderedCard.getWidth(), 5, "drop must preserve the guided width within percentage rounding"); + assertEquals(80, renderedCard.getHeight(), 5, "drop must preserve the guided height within percentage rounding"); + } + + @Test + void layeredGuideUsesPointerGrabOffsetAndCommittedBoundsMatchIt() throws Exception { + GuiDocument document = document(""); + Element card = document.components().get(1); + CodenameOneGUIBuilder builder = builder(document); + Container canvas = new Container(new LayeredLayout()); + Container parentPreview = new Container(new LayeredLayout()); + parentPreview.putClientProperty("gui.element", document.root()); + parentPreview.setX(50); + parentPreview.setY(70); + parentPreview.setWidth(500); + parentPreview.setHeight(400); + Button source = new Button("Card"); + source.putClientProperty("gui.element", card); + source.setWidth(100); + source.setHeight(80); + parentPreview.add(source); + canvas.add(parentPreview); + set(builder, "canvasHost", canvas); + set(builder, "designerGrabOffsetX", 30); + set(builder, "designerGrabOffsetY", 20); + + CodenameOneGUIBuilder.DropPlan guide = builder.planDrop(card, document.root(), source, 293, 267); + assertNotNull(guide); + assertTrue(builder.applyDropPlan(card, guide, 293, 267)); + Component rendered = render(document, 500, 400); + Component renderedCard = componentForElement((Container) rendered, card); + + assertEquals(guide.snapX - parentPreview.getAbsoluteX(), renderedCard.getX(), 2, + "the committed left edge must be the guide left edge, not the raw pointer"); + assertEquals(guide.snapY - parentPreview.getAbsoluteY(), renderedCard.getY(), 2); + assertEquals(source.getWidth(), renderedCard.getWidth(), 2); + assertEquals(source.getHeight(), renderedCard.getHeight(), 2); + } + + @Test + void dockingToTheSurfaceEdgeReflowsWhenTheSurfaceWidthChanges() throws Exception { + GuiDocument document = document("" + + "" + + ""); + Element card = document.components().get(1); + Container preview = (Container) render(document, 600, 320); + Container canvas = new Container(new LayeredLayout()); + canvas.setWidth(600); canvas.setHeight(320); canvas.add(preview); + CodenameOneGUIBuilder builder = builder(document); + set(builder, "canvasHost", canvas); + set(builder, "previewRoot", preview); + Component source = componentForElement(preview, card); + CodenameOneGUIBuilder.DropPlan plan = new CodenameOneGUIBuilder.DropPlan(); + plan.document = document; plan.target = document.root(); plan.parent = document.root(); + plan.layout = "LayeredLayout"; plan.valid = true; + plan.snapX = 600 - source.getWidth(); plan.snapY = source.getAbsoluteY(); + plan.snapW = source.getWidth(); plan.snapH = source.getHeight(); + plan.horizontalSnap = new CodenameOneGUIBuilder.SnapResult(plan.snapX, "dock right", null, "parentEnd"); + plan.verticalSnap = new CodenameOneGUIBuilder.SnapResult(plan.snapY, null, null, null); + assertTrue(builder.applyDropPlan(card, plan, plan.snapX, plan.snapY)); + + Container narrow = (Container) render(document, 600, 320); + Container wide = (Container) render(document, 900, 320); + Component narrowCard = componentForElement(narrow, card); + Component wideCard = componentForElement(wide, card); + assertEquals(narrowCard.getWidth(), wideCard.getWidth()); + assertEquals(300, wideCard.getX() - narrowCard.getX(), 1, + "right docking must reflow by the surface width delta, not preserve an absolute x"); + assertEquals(600, narrowCard.getX() + narrowCard.getWidth(), 4); + assertEquals(900, wideCard.getX() + wideCard.getWidth(), 4); + } + + @Test + void shortGuidedDragOverItsOwnPixelsTargetsTheParentInsteadOfCancelling() throws Exception { + GuiDocument document = document("" + + "" + + ""); + Element card = document.components().get(1); + CodenameOneGUIBuilder builder = builder(document); + + assertSame(document.root(), builder.normalizeDesignerDropTarget(card, card)); + } + + @Test + void guidedReferencesRemainResponsiveAndMatchWidthAcrossCanvasSizes() { + GuiDocument document = document("" + + "" + + "" + + ""); + Element anchor = document.components().get(1); + Element linked = document.components().get(2); + + Container narrow = (Container) render(document, 420, 260); + Component narrowAnchor = componentForElement(narrow, anchor); + Component narrowLinked = componentForElement(narrow, linked); + assertEquals(narrowAnchor.getWidth(), narrowLinked.getWidth(), 1); + assertEquals(40, narrowLinked.getX() - narrowAnchor.getX(), 1, + "same-size links may retain an independent position offset"); + + Container wide = (Container) render(document, 760, 260); + Component wideAnchor = componentForElement(wide, anchor); + Component wideLinked = componentForElement(wide, linked); + assertEquals(wideAnchor.getWidth(), wideLinked.getWidth(), 1, + "same width must be a relationship, not a one-time pixel copy"); + assertEquals(40, wideLinked.getX() - wideAnchor.getX(), 1); + } + + @Test + void baselineConstraintAlignsDifferentTextComponentsExactly() { + GuiDocument document = document("" + + "" + + "" + + ""); + Element caption = document.components().get(1); + Element action = document.components().get(2); + Container rendered = (Container) render(document, 500, 220); + Component renderedCaption = componentForElement(rendered, caption); + Component renderedAction = componentForElement(rendered, action); + + int captionBaseline = renderedCaption.getY() + renderedCaption.getBaseline(renderedCaption.getWidth(), renderedCaption.getHeight()); + int actionBaseline = renderedAction.getY() + renderedAction.getBaseline(renderedAction.getWidth(), renderedAction.getHeight()); + assertEquals(captionBaseline, actionBaseline, 1); + } + + @Test + void centerAnchorsCenterComponentsRatherThanTheirLeftEdges() { + GuiDocument document = document("" + + "" + + ""); + Element centered = document.components().get(1); + Container rendered = (Container) render(document, 600, 400); + Component component = componentForElement(rendered, centered); + assertEquals(300, component.getX() + component.getWidth() / 2, 2); + assertEquals(200, component.getY() + component.getHeight() / 2, 2); + } + + @Test + void selectingAComponentNeverChangesLayoutMetrics() { + GuiDocument document = document("" + + "" + + "" + + ""); + Element first = document.components().get(1); + Element second = document.components().get(2); + Container unselected = (Container) render(document, 600, 300); + Component selectedRender = ComponentPreviewFactory.create(document.root(), first, handler()); + selectedRender.setWidth(600); selectedRender.setHeight(300); + layoutNested((Container) selectedRender); + + Component beforeFirst = componentForElement(unselected, first); + Component afterFirst = componentForElement((Container) selectedRender, first); + Component beforeSecond = componentForElement(unselected, second); + Component afterSecond = componentForElement((Container) selectedRender, second); + assertArrayEquals(new int[]{beforeFirst.getX(), beforeFirst.getY(), beforeFirst.getWidth(), beforeFirst.getHeight()}, + new int[]{afterFirst.getX(), afterFirst.getY(), afterFirst.getWidth(), afterFirst.getHeight()}); + assertArrayEquals(new int[]{beforeSecond.getX(), beforeSecond.getY(), beforeSecond.getWidth(), beforeSecond.getHeight()}, + new int[]{afterSecond.getX(), afterSecond.getY(), afterSecond.getWidth(), afterSecond.getHeight()}); + } + + @Test + void resizeSnapCopiesTheSizeWithoutInventingADurableBinding() throws Exception { + GuiDocument document = document("" + + "" + + "" + + ""); + Element anchor = document.components().get(1); + Element resized = document.components().get(2); + Container preview = (Container) render(document, 600, 300); + CodenameOneGUIBuilder builder = builder(document); + Container canvas = new Container(new LayeredLayout()); + canvas.add(preview); + set(builder, "canvasHost", canvas); + Component resizedPreview = componentForElement(preview, resized); + CodenameOneGUIBuilder.ResizePlan plan = new CodenameOneGUIBuilder.ResizePlan( + resizedPreview.getAbsoluteX(), resizedPreview.getAbsoluteY(), + componentForElement(preview, anchor).getWidth(), resizedPreview.getHeight()); + plan.matchWidth = anchor; + + builder.commitGuidedResize(resized, preview, plan, 2); + + assertEquals("fixed", resized.getAttribute("guidedHorizontalSize")); + assertNull(resized.getAttribute("guidedMatchWidth")); + assertEquals(String.valueOf(componentForElement(preview, anchor).getWidth()), + resized.getAttribute("guidedPreferredWidth")); + Container rerendered = (Container) render(document, 800, 300); + assertEquals(componentForElement(rerendered, anchor).getWidth(), + componentForElement(rerendered, resized).getWidth(), 1); + } + + @Test + void resizingASelectedReferencePreviewsAndResizesTheWholeGroupInUnison() throws Exception { + GuiDocument document = document("" + + "" + + "" + + "" + + ""); + Element reference = document.components().get(1); + Element second = document.components().get(2); + Element third = document.components().get(3); + Container preview = (Container) render(document, 760, 520); + Container canvas = new Container(new LayeredLayout()); + canvas.setWidth(760); canvas.setHeight(520); canvas.add(preview); + CodenameOneGUIBuilder builder = builder(document); + set(builder, "canvasHost", canvas); + set(builder, "previewRoot", preview); + Component referencePreview = componentForElement(preview, reference); + Component secondPreview = componentForElement(preview, second); + Component thirdPreview = componentForElement(preview, third); + builder.handleDesignerPointerPressed(referencePreview.getAbsoluteX() + referencePreview.getWidth() / 2, + referencePreview.getAbsoluteY() + referencePreview.getHeight() / 2, false); + builder.handleDesignerPointerPressed(secondPreview.getAbsoluteX() + secondPreview.getWidth() / 2, + secondPreview.getAbsoluteY() + secondPreview.getHeight() / 2, true); + builder.handleDesignerPointerPressed(thirdPreview.getAbsoluteX() + thirdPreview.getWidth() / 2, + thirdPreview.getAbsoluteY() + thirdPreview.getHeight() / 2, true); + String originalXml = document.toXml(); + CodenameOneGUIBuilder.ResizePlan plan = new CodenameOneGUIBuilder.ResizePlan( + referencePreview.getAbsoluteX(), referencePreview.getAbsoluteY(), 360, referencePreview.getHeight()); + + CodenameOneGUIBuilder.GuidedSimulation simulation = builder.simulateGuidedResize( + reference, preview, referencePreview, plan, 2); + + assertNotNull(simulation); + assertEquals(originalXml, document.toXml()); + assertEquals(3, simulation.items.stream().filter(item -> item.newW == 360).count(), + "the glass preview must show every selected component at the reference width"); + assertEquals(Component.E_RESIZE_CURSOR, builder.designerResizeCursorAt( + referencePreview.getAbsoluteX() + referencePreview.getWidth(), + referencePreview.getAbsoluteY() + referencePreview.getHeight() / 2)); + assertEquals(Component.SE_RESIZE_CURSOR, builder.designerResizeCursorAt( + referencePreview.getAbsoluteX() + referencePreview.getWidth(), + referencePreview.getAbsoluteY() + referencePreview.getHeight())); + assertEquals(Component.DEFAULT_CURSOR, builder.designerResizeCursorAt( + referencePreview.getAbsoluteX() + referencePreview.getWidth() / 2, + referencePreview.getAbsoluteY() + referencePreview.getHeight() / 2)); + + builder.commitGuidedSelectionResize(reference, preview, plan, 2); + Container resized = (Container) render(document, 760, 520); + assertEquals(360, componentForElement(resized, reference).getWidth(), 1); + assertEquals(360, componentForElement(resized, second).getWidth(), 1); + assertEquals(360, componentForElement(resized, third).getWidth(), 1); + assertEquals("reference", second.getAttribute("guidedMatchWidth")); + assertEquals("reference", third.getAttribute("guidedMatchWidth")); + assertTrue(document.undo()); + assertEquals(originalXml, document.toXml(), "group resize must undo atomically"); + assertFalse(document.canUndo()); + } + + @Test + void resizeSimulationShowsDependentCascadeWithoutMutatingTheDocument() throws Exception { + GuiDocument document = document("" + + "" + + "" + + ""); + Element primary = document.components().get(1); + Container preview = (Container) render(document, 600, 320); + Container canvas = new Container(new LayeredLayout()); + canvas.setWidth(600); canvas.setHeight(320); canvas.add(preview); + CodenameOneGUIBuilder builder = builder(document); + set(builder, "canvasHost", canvas); + set(builder, "previewRoot", preview); + Component primaryPreview = componentForElement(preview, primary); + String originalXml = document.toXml(); + CodenameOneGUIBuilder.ResizePlan plan = new CodenameOneGUIBuilder.ResizePlan( + primaryPreview.getAbsoluteX(), primaryPreview.getAbsoluteY(), 240, primaryPreview.getHeight()); + + CodenameOneGUIBuilder.GuidedSimulation simulation = builder.simulateGuidedResize( + primary, preview, primaryPreview, plan, 2); + + assertNotNull(simulation); + assertEquals(originalXml, document.toXml(), "a live preview must not touch the real document or undo history"); + assertTrue(simulation.changedNames.contains("primary")); + assertTrue(simulation.changedNames.contains("linked"), "the explicit width dependent must be shown changing"); + assertTrue(simulation.links.stream().anyMatch(link -> "primary".equals(link.from) && "linked".equals(link.to))); + com.codename1.guibuilder.ui.DragGuideOverlay.GlassItem linked = simulation.items.stream() + .filter(item -> "linked".equals(item.name)).findFirst().orElseThrow(); + assertEquals(240, linked.newW, 2); + } + + @Test + void dragSimulationAndCommitProduceTheSameGuidedRectangle() throws Exception { + GuiDocument document = document("" + + "" + + ""); + Element card = document.components().get(1); + Container preview = (Container) render(document, 600, 320); + Container canvas = new Container(new LayeredLayout()); + canvas.setWidth(600); canvas.setHeight(320); canvas.add(preview); + CodenameOneGUIBuilder builder = builder(document); + set(builder, "canvasHost", canvas); + set(builder, "previewRoot", preview); + Component source = componentForElement(preview, card); + CodenameOneGUIBuilder.DropPlan plan = new CodenameOneGUIBuilder.DropPlan(); + plan.document = document; plan.target = document.root(); plan.parent = document.root(); + plan.layout = "LayeredLayout"; plan.valid = true; + plan.snapX = 280; plan.snapY = 150; plan.snapW = source.getWidth(); plan.snapH = source.getHeight(); + plan.horizontalSnap = new CodenameOneGUIBuilder.SnapResult(plan.snapX, null, null, null); + plan.verticalSnap = new CodenameOneGUIBuilder.SnapResult(plan.snapY, null, null, null); + String originalXml = document.toXml(); + + CodenameOneGUIBuilder.GuidedSimulation simulation = builder.simulateGuidedDrop(card, plan, preview, source); + assertNotNull(simulation); + assertEquals(originalXml, document.toXml()); + com.codename1.guibuilder.ui.DragGuideOverlay.GlassItem predicted = simulation.items.stream() + .filter(item -> "card".equals(item.name)).findFirst().orElseThrow(); + + assertTrue(builder.applyDropPlan(card, plan, plan.snapX, plan.snapY)); + Container committed = (Container) render(document, 600, 320); + Component committedCard = componentForElement(committed, card); + assertEquals(predicted.newX, committedCard.getAbsoluteX(), 2); + assertEquals(predicted.newY, committedCard.getAbsoluteY(), 2); + assertEquals(predicted.newW, committedCard.getWidth(), 2); + assertEquals(predicted.newH, committedCard.getHeight(), 2); + } + + @Test + void draggingAComponentIntoFreeSpaceTearsAwayItsIncomingRelationships() throws Exception { + GuiDocument document = document("" + + "" + + "" + + ""); + Element linked = document.components().get(2); + Container preview = (Container) render(document, 700, 400); + Container canvas = new Container(new LayeredLayout()); + canvas.setWidth(700); canvas.setHeight(400); canvas.add(preview); + CodenameOneGUIBuilder builder = builder(document); + set(builder, "canvasHost", canvas); + set(builder, "previewRoot", preview); + Component source = componentForElement(preview, linked); + CodenameOneGUIBuilder.DropPlan plan = new CodenameOneGUIBuilder.DropPlan(); + plan.document = document; plan.target = document.root(); plan.parent = document.root(); + plan.layout = "LayeredLayout"; plan.valid = true; + plan.snapX = 420; plan.snapY = 260; plan.snapW = source.getWidth(); plan.snapH = source.getHeight(); + plan.horizontalSnap = new CodenameOneGUIBuilder.SnapResult(plan.snapX, null, null, null); + plan.verticalSnap = new CodenameOneGUIBuilder.SnapResult(plan.snapY, null, null, null); + String before = document.toXml(); + + CodenameOneGUIBuilder.GuidedSimulation simulation = builder.simulateGuidedDrop(linked, plan, preview, source); + + assertEquals(before, document.toXml()); + Element simulatedLinked = simulation.document.components().get(2); + assertEquals("fixed", simulatedLinked.getAttribute("guidedHorizontalSize")); + assertNull(simulatedLinked.getAttribute("guidedMatchWidth")); + assertEquals("-|-|-|-", simulatedLinked.getAttribute("guidedReferences")); + assertTrue(simulation.summary.contains("detaches from anchor")); + assertTrue(simulation.links.stream().anyMatch(link -> link.detached + && "anchor".equals(link.from) && "linked".equals(link.to))); + + assertTrue(builder.applyDropPlan(linked, plan, plan.snapX, plan.snapY)); + assertEquals("fixed", linked.getAttribute("guidedHorizontalSize")); + assertNull(linked.getAttribute("guidedMatchWidth")); + assertEquals("-|-|-|-", linked.getAttribute("guidedReferences")); + } + + @Test + void snappingBackToTheSameReferenceKeepsTheExplicitRelationship() throws Exception { + GuiDocument document = document("" + + "" + + "" + + ""); + Element anchor = document.components().get(1); + Element linked = document.components().get(2); + Container preview = (Container) render(document, 700, 400); + Container canvas = new Container(new LayeredLayout()); + canvas.setWidth(700); canvas.setHeight(400); canvas.add(preview); + CodenameOneGUIBuilder builder = builder(document); + set(builder, "canvasHost", canvas); + set(builder, "previewRoot", preview); + Component source = componentForElement(preview, linked); + CodenameOneGUIBuilder.DropPlan plan = new CodenameOneGUIBuilder.DropPlan(); + plan.document = document; plan.target = document.root(); plan.parent = document.root(); + plan.layout = "LayeredLayout"; plan.valid = true; + plan.snapX = componentForElement(preview, anchor).getAbsoluteX(); + plan.snapY = 220; plan.snapW = source.getWidth(); plan.snapH = source.getHeight(); + plan.horizontalSnap = new CodenameOneGUIBuilder.SnapResult(plan.snapX, "align start", anchor, "alignStart"); + plan.verticalSnap = new CodenameOneGUIBuilder.SnapResult(plan.snapY, null, null, null); + + CodenameOneGUIBuilder.GuidedSimulation simulation = builder.simulateGuidedDrop(linked, plan, preview, source); + Element simulatedLinked = simulation.document.components().get(2); + assertEquals("match", simulatedLinked.getAttribute("guidedHorizontalSize")); + assertEquals("anchor", simulatedLinked.getAttribute("guidedMatchWidth")); + assertFalse(simulation.links.stream().anyMatch(link -> link.detached)); + } + + @Test + void movingSameWidthBelowItsDependentActionRebasesTheCycleWithoutBouncingAnything() throws Exception { + GuiDocument document = document("" + + "" + + "" + + "" + + "" + + ""); + Element secondary = document.components().get(2); + Element baselineLabel = document.components().get(3); + Element action = document.components().get(4); + Container preview = (Container) render(document, 900, 600); + Container canvas = new Container(new LayeredLayout()); + canvas.setWidth(900); canvas.setHeight(600); canvas.add(preview); + CodenameOneGUIBuilder builder = builder(document); + set(builder, "canvasHost", canvas); + set(builder, "previewRoot", preview); + Component source = componentForElement(preview, secondary); + Component labelPreview = componentForElement(preview, baselineLabel); + Component actionPreview = componentForElement(preview, action); + int[] oldSource = bounds(source); + int[] oldLabel = bounds(labelPreview); + int[] oldAction = bounds(actionPreview); + CodenameOneGUIBuilder.DropPlan plan = new CodenameOneGUIBuilder.DropPlan(); + plan.document = document; plan.target = action; plan.parent = document.root(); + plan.layout = "LayeredLayout"; plan.valid = true; + plan.snapX = actionPreview.getAbsoluteX() + actionPreview.getWidth() - source.getWidth(); + plan.snapY = actionPreview.getAbsoluteY() + actionPreview.getHeight() + 12; + plan.snapW = source.getWidth(); plan.snapH = source.getHeight(); + plan.horizontalSnap = new CodenameOneGUIBuilder.SnapResult(plan.snapX, "align end with Action", action, "alignEnd"); + plan.verticalSnap = new CodenameOneGUIBuilder.SnapResult(plan.snapY, "space after Action", action, "after"); + + CodenameOneGUIBuilder.GuidedSimulation simulation = builder.simulateGuidedDrop(secondary, plan, preview, source); + Container proposed = (Container) render(simulation.document, 900, 600); + Component proposedSecondary = componentForElement(proposed, simulation.document.components().get(2)); + Component proposedLabel = componentForElement(proposed, simulation.document.components().get(3)); + Component proposedAction = componentForElement(proposed, simulation.document.components().get(4)); + + assertEquals(oldSource[2], proposedSecondary.getWidth(), 1, + "a position drag must preserve width: " + simulation.document.toXml()); + assertEquals(oldSource[3], proposedSecondary.getHeight(), 1, "a position drag must preserve height"); + assertEquals(oldAction[0] + oldAction[2], proposedSecondary.getX() + proposedSecondary.getWidth(), 1, + "the selected right-edge rule must be the rendered result; source margins=" + + source.getStyle().getMarginLeftNoRTL() + "," + source.getStyle().getMarginRightNoRTL() + + " target margins=" + actionPreview.getStyle().getMarginLeftNoRTL() + "," + + actionPreview.getStyle().getMarginRightNoRTL()); + assertTrue(proposedSecondary.getY() >= oldAction[1] + oldAction[3], + "the selected below-Action rule must be the rendered result"); + assertArrayEquals(oldLabel, bounds(proposedLabel), "the baseline label must be rebased in place"); + assertArrayEquals(oldAction, bounds(proposedAction), "Action must not bounce when it becomes the new anchor"); + assertFalse(simulation.changedNames.contains("baselineLabel")); + assertFalse(simulation.changedNames.contains("baselineAction")); + assertTrue(simulation.summary.contains("keeps in place baselineLabel from secondary")); + assertTrue(simulation.links.stream().anyMatch(link -> link.detached + && "secondary".equals(link.from) && "baselineLabel".equals(link.to))); + + assertTrue(builder.applyDropPlan(secondary, plan, plan.snapX, plan.snapY)); + Container committed = (Container) render(document, 900, 600); + assertArrayEquals(bounds(proposedSecondary), bounds(componentForElement(committed, secondary))); + assertArrayEquals(oldLabel, bounds(componentForElement(committed, baselineLabel))); + assertArrayEquals(oldAction, bounds(componentForElement(committed, action))); + + Container wider = (Container) render(document, 1200, 700); + Component widerSecondary = componentForElement(wider, secondary); + Component widerAction = componentForElement(wider, action); + assertEquals(widerAction.getX() + widerAction.getWidth(), + widerSecondary.getX() + widerSecondary.getWidth(), 1); + assertTrue(widerSecondary.getY() >= widerAction.getY() + widerAction.getHeight()); + } + + @Test + void aligningPrimaryWithItsTransitiveDependentPreservesEveryUnaffectedRenderedRectangle() throws Exception { + GuiDocument document = guidedChainDocument(); + Element primary = document.components().get(1); + Element secondary = document.components().get(2); + Element baselineLabel = document.components().get(3); + Element baselineAction = document.components().get(4); + Container preview = (Container) render(document, 900, 700); + Container canvas = new Container(new LayeredLayout()); + canvas.setWidth(900); canvas.setHeight(700); canvas.add(preview); + CodenameOneGUIBuilder builder = builder(document); + set(builder, "canvasHost", canvas); + set(builder, "previewRoot", preview); + + Component primaryPreview = componentForElement(preview, primary); + Component secondaryPreview = componentForElement(preview, secondary); + Component labelPreview = componentForElement(preview, baselineLabel); + Component actionPreview = componentForElement(preview, baselineAction); + // Reproduce the live theme geometry: the relationship currently renders Secondary + // taller than its nominal preferred height, and the downstream chain follows it. + secondaryPreview.setHeight(secondaryPreview.getHeight() + 40); + labelPreview.setY(labelPreview.getY() + 40); + actionPreview.setY(actionPreview.getY() + 40); + int[] oldPrimary = bounds(primaryPreview); + int[] oldSecondary = bounds(secondaryPreview); + int[] oldLabel = bounds(labelPreview); + int[] oldAction = bounds(actionPreview); + + CodenameOneGUIBuilder.DropPlan plan = new CodenameOneGUIBuilder.DropPlan(); + plan.document = document; plan.target = document.root(); plan.parent = document.root(); + plan.layout = "LayeredLayout"; plan.valid = true; + plan.snapX = actionPreview.getAbsoluteX(); + plan.snapY = actionPreview.getAbsoluteY() + actionPreview.getHeight() + 80; + plan.snapW = primaryPreview.getWidth(); plan.snapH = primaryPreview.getHeight(); + plan.horizontalSnap = new CodenameOneGUIBuilder.SnapResult( + plan.snapX, "align start with baselineAction", baselineAction, "alignStart"); + plan.verticalSnap = new CodenameOneGUIBuilder.SnapResult(plan.snapY, null, null, null); + + CodenameOneGUIBuilder.GuidedSimulation simulation = + builder.simulateGuidedDrop(primary, plan, preview, primaryPreview); + assertNotNull(simulation); + Container proposed = (Container) render(simulation.document, 900, 700); + Component proposedPrimary = componentForElement(proposed, simulation.document.components().get(1)); + Component proposedSecondary = componentForElement(proposed, simulation.document.components().get(2)); + Component proposedLabel = componentForElement(proposed, simulation.document.components().get(3)); + Component proposedAction = componentForElement(proposed, simulation.document.components().get(4)); + + assertEquals(oldPrimary[2], proposedPrimary.getWidth(), 1, "position-only drag changed Primary width"); + assertEquals(oldPrimary[3], proposedPrimary.getHeight(), 1, "position-only drag changed Primary height"); + assertArrayEquals(oldSecondary, bounds(proposedSecondary), "cycle rebase resized Secondary"); + assertArrayEquals(oldLabel, bounds(proposedLabel), "Secondary resize moved Baseline Label"); + assertArrayEquals(oldAction, bounds(proposedAction), "Secondary resize moved Action"); + assertFalse(simulation.changedNames.contains("secondary")); + assertFalse(simulation.changedNames.contains("baselineLabel")); + assertFalse(simulation.changedNames.contains("baselineAction")); + + assertTrue(builder.applyDropPlan(primary, plan, plan.snapX, plan.snapY)); + Container committed = (Container) render(document, 900, 700); + assertArrayEquals(bounds(proposedPrimary), bounds(componentForElement(committed, primary))); + assertArrayEquals(oldSecondary, bounds(componentForElement(committed, secondary))); + assertArrayEquals(oldLabel, bounds(componentForElement(committed, baselineLabel))); + assertArrayEquals(oldAction, bounds(componentForElement(committed, baselineAction))); + } + + @Test + void movingPrimaryBelowSameWidthKeepsTheEntireDownstreamChainVisible() throws Exception { + assertPrimarySecondaryPlacementKeepsEverythingVisible(true); + } + + @Test + void movingSameWidthBelowPrimaryKeepsTheEntireDownstreamChainVisible() throws Exception { + assertPrimarySecondaryPlacementKeepsEverythingVisible(false); + } + + private void assertPrimarySecondaryPlacementKeepsEverythingVisible(boolean movePrimary) throws Exception { + GuiDocument document = guidedChainDocument(); + Element primary = document.components().get(1); + Element secondary = document.components().get(2); + Element dragged = movePrimary ? primary : secondary; + Element anchor = movePrimary ? secondary : primary; + Container preview = (Container) render(document, 900, 600); + Container canvas = new Container(new LayeredLayout()); + canvas.setWidth(900); canvas.setHeight(600); canvas.add(preview); + CodenameOneGUIBuilder builder = builder(document); + set(builder, "canvasHost", canvas); + set(builder, "previewRoot", preview); + Component source = componentForElement(preview, dragged); + Component anchorPreview = componentForElement(preview, anchor); + CodenameOneGUIBuilder.DropPlan plan = new CodenameOneGUIBuilder.DropPlan(); + plan.document = document; plan.target = anchor; plan.parent = document.root(); + plan.layout = "LayeredLayout"; plan.valid = true; + plan.snapX = anchorPreview.getAbsoluteX(); + plan.snapY = anchorPreview.getAbsoluteY() + anchorPreview.getHeight() + 8; + plan.snapW = source.getWidth(); plan.snapH = source.getHeight(); + plan.horizontalSnap = new CodenameOneGUIBuilder.SnapResult(plan.snapX, "align start", anchor, "alignStart"); + plan.verticalSnap = new CodenameOneGUIBuilder.SnapResult(plan.snapY, "space after", anchor, "after"); + + CodenameOneGUIBuilder.GuidedSimulation simulation = builder.simulateGuidedDrop(dragged, plan, preview, source); + assertNotNull(simulation); + Container proposed = (Container) render(simulation.document, 900, 600); + assertAllComponentsVisible(simulation.document, proposed); + + assertTrue(builder.applyDropPlan(dragged, plan, plan.snapX, plan.snapY)); + for (int i = 0; i < document.components().size(); i++) { + assertEquals(simulation.document.components().get(i).getAttribute("name"), + document.components().get(i).getAttribute("name"), + "a positional guided drag must not silently change stacking order"); + } + Container committed = (Container) render(document, 900, 600); + assertAllComponentsVisible(document, committed); + for (int i = 1; i < document.components().size(); i++) { + Component expected = componentForElement(proposed, simulation.document.components().get(i)); + Component actual = componentForElement(committed, document.components().get(i)); + assertArrayEquals(bounds(expected), bounds(actual), document.components().get(i).getAttribute("name")); + } + } + + private static GuiDocument guidedChainDocument() { + return document("" + + "" + + "" + + "" + + "" + + ""); + } + + private static void assertAllComponentsVisible(GuiDocument document, Container rendered) { + for (int i = 1; i < document.components().size(); i++) { + Element element = document.components().get(i); + Component component = componentForElement(rendered, element); + assertNotNull(component, element.getAttribute("name") + " must still render"); + assertTrue(component.isVisible(), element.getAttribute("name") + " must remain visible"); + assertTrue(component.getWidth() > 1 && component.getHeight() > 1, + element.getAttribute("name") + " collapsed to " + java.util.Arrays.toString(bounds(component))); + assertTrue(component.getAbsoluteX() + component.getWidth() > rendered.getAbsoluteX() + && component.getAbsoluteY() + component.getHeight() > rendered.getAbsoluteY() + && component.getAbsoluteX() < rendered.getAbsoluteX() + rendered.getWidth() + && component.getAbsoluteY() < rendered.getAbsoluteY() + rendered.getHeight(), + element.getAttribute("name") + " moved outside the surface: " + java.util.Arrays.toString(bounds(component))); + } + } + + @Test + void borderReplacementSwapsConstraintsAndSurvivesSerialization() throws Exception { + GuiDocument document = document(""); + Element west = document.components().get(1); + Element center = document.components().get(2); + CodenameOneGUIBuilder builder = builder(document); + CodenameOneGUIBuilder.DropPlan plan = new CodenameOneGUIBuilder.DropPlan(); + plan.document = document; + plan.target = center; + plan.parent = document.root(); + plan.occupied = center; + plan.layout = "BorderLayout"; + plan.constraint = "Center"; + plan.valid = true; + + assertTrue(builder.applyDropPlan(west, plan, 0, 0)); + assertEquals("Center", west.getAttribute("layoutConstraint"), document.toXml()); + assertEquals("West", center.getAttribute("layoutConstraint")); + GuiDocument reparsed = GuiDocument.parse("Form.gui", document.toXml()); + assertEquals("Center", reparsed.components().get(1).getAttribute("layoutConstraint")); + assertEquals("West", reparsed.components().get(2).getAttribute("layoutConstraint")); + + Component rendered = ComponentPreviewFactory.create(reparsed.root(), null, handler()); + rendered.setWidth(600); + rendered.setHeight(400); + ((Container) rendered).layoutContainer(); + Component renderedWestButton = componentForElement((Container) rendered, reparsed.components().get(1)); + Component renderedFormerCenter = componentForElement((Container) rendered, reparsed.components().get(2)); + assertNotNull(renderedWestButton); + assertNotNull(renderedFormerCenter); + assertTrue(renderedWestButton.isVisible() && renderedWestButton.getWidth() > 0 && renderedWestButton.getHeight() > 0, + "the component moved into Center must not vanish"); + assertTrue(renderedFormerCenter.isVisible() && renderedFormerCenter.getWidth() > 0 && renderedFormerCenter.getHeight() > 0, + "the displaced Center component must remain visible in West"); + BorderLayout renderedLayout = (BorderLayout) ((Container) rendered).getLayout(); + assertSame(renderedWestButton, renderedLayout.getCenter(), "the dragged WEST component must own CENTER"); + assertSame(renderedFormerCenter, renderedLayout.getWest(), "the displaced CENTER component must own WEST"); + assertTrue(renderedFormerCenter.getX() + renderedFormerCenter.getWidth() <= renderedWestButton.getX(), + "Border regions must be adjacent, never stacked behind each other"); + assertTrue(renderedWestButton.getWidth() >= 100, + "a long component displaced into WEST must be capped so CENTER cannot vanish"); + } + + @Test + void inlineEditorTeardownCommitsModelAndVisiblePreview() throws Exception { + GuiDocument document = document(""); + Element title = document.components().get(1); + CodenameOneGUIBuilder builder = builder(document); + Container canvas = new Container(); + Label preview = new Label("Old"); + preview.putClientProperty("gui.element", title); + canvas.add(preview); + set(builder, "canvasHost", canvas); + + builder.commitInlineValue(title, "text", "Edited in place"); + + assertEquals("Edited in place", title.getAttribute("text")); + assertEquals("Edited in place", preview.getText()); + assertTrue(document.isModified()); + } + + @Test + void everySupportedLayoutUsesAnExplicitPlacementAdapter() throws Exception { + CodenameOneGUIBuilder builder = builder(document("")); + assertEquals("BorderPlacementAdapter", builder.placementAdapterName("BorderLayout")); + assertEquals("LayeredPlacementAdapter", builder.placementAdapterName("LayeredLayout")); + assertEquals("BoxPlacementAdapter", builder.placementAdapterName("BoxLayout")); + assertEquals("GridPlacementAdapter", builder.placementAdapterName("GridLayout")); + assertEquals("TablePlacementAdapter", builder.placementAdapterName("TableLayout")); + assertEquals("FlowPlacementAdapter", builder.placementAdapterName("FlowLayout")); + } + + @Test + void borderCenterCannotConsumeReachableEastAndWestDropBands() { + assertEquals("West", CodenameOneGUIBuilder.borderEdgeRegion(100, 100, 600, 500, 90, 40, 175, 350)); + assertEquals("East", CodenameOneGUIBuilder.borderEdgeRegion(100, 100, 600, 500, 90, 40, 625, 350)); + assertNull(CodenameOneGUIBuilder.borderEdgeRegion(100, 100, 600, 500, 90, 40, 400, 350)); + assertEquals("North", CodenameOneGUIBuilder.borderEdgeRegion(100, 100, 600, 500, 90, 40, 400, 120)); + assertEquals("South", CodenameOneGUIBuilder.borderEdgeRegion(100, 100, 600, 500, 90, 40, 400, 580)); + } + + @Test + void numericLayoutValuesRejectNonNumbersAndOverflow() { + assertNull(CodenameOneGUIBuilder.parseInteger("three")); + assertNull(CodenameOneGUIBuilder.parseInteger("2.5")); + assertNull(CodenameOneGUIBuilder.parseInteger("")); + assertEquals(-1, CodenameOneGUIBuilder.parseInteger("-1")); + assertEquals(12, CodenameOneGUIBuilder.parseInteger("12")); + } + + @Test + void boxLayoutXDropReordersAndRendersInHorizontalOrder() throws Exception { + GuiDocument document = document("" + + "" + + ""); + Element a = document.components().get(1); + Element c = document.components().get(3); + CodenameOneGUIBuilder builder = builder(document); + assertTrue(builder.applyDropPlan(c, sequentialPlan(document, document.root(), a, "BoxLayout", false), 0, 0)); + assertSame(c, document.root().getChildAt(0)); + Component rendered = render(document, 600, 180); + Component renderedC = componentForElement((Container) rendered, c); + Component renderedA = componentForElement((Container) rendered, a); + assertTrue(renderedC.getX() < renderedA.getX()); + } + + @Test + void gridLayoutDropReordersWithoutOverlappingOrLosingCells() throws Exception { + GuiDocument document = document("" + + "" + + ""); + Element a = document.components().get(1); + Element d = document.components().get(4); + CodenameOneGUIBuilder builder = builder(document); + assertTrue(builder.applyDropPlan(d, sequentialPlan(document, document.root(), a, "GridLayout", false), 0, 0)); + Component rendered = render(document, 600, 400); + java.util.Set cells = new java.util.HashSet<>(); + for (int i = 1; i < document.components().size(); i++) { + Component component = componentForElement((Container) rendered, document.components().get(i)); + assertTrue(component.getWidth() > 0 && component.getHeight() > 0); + cells.add(component.getX() + ":" + component.getY()); + } + assertEquals(4, cells.size()); + } + + @Test + void tableLayoutDropAssignsUniqueCellsAndExpandsRowsInsteadOfHidingOverflow() throws Exception { + GuiDocument document = document("" + + "" + + ""); + Element a = document.components().get(1); + Element c = document.components().get(3); + CodenameOneGUIBuilder builder = builder(document); + assertTrue(builder.applyDropPlan(c, sequentialPlan(document, document.root(), a, "TableLayout", false), 0, 0)); + assertEquals("2", document.root().getAttribute("tableLayoutRows")); + java.util.Set modelCells = new java.util.HashSet<>(); + for (int i = 1; i < document.components().size(); i++) { + Element child = document.components().get(i); + modelCells.add(child.getAttribute("tableRow") + ":" + child.getAttribute("tableColumn")); + } + assertEquals(3, modelCells.size()); + Component rendered = render(document, 600, 400); + for (int i = 1; i < document.components().size(); i++) { + Component component = componentForElement((Container) rendered, document.components().get(i)); + assertTrue(component.isVisible() && component.getWidth() > 0 && component.getHeight() > 0); + } + } + + @Test + void tableMoveEarlierReassignsCellsAndChangesRenderedOrder() throws Exception { + GuiDocument document = document("" + + "" + + "" + + ""); + Element b = document.components().get(2); + Element c = document.components().get(3); + document.select(c); + CodenameOneGUIBuilder builder = builder(document); + + assertTrue(builder.reorderSelectedInParent(-1)); + assertEquals("1", c.getAttribute("tableColumn")); + assertEquals("2", b.getAttribute("tableColumn")); + Component rendered = render(document, 600, 200); + assertTrue(componentForElement((Container) rendered, c).getX() + < componentForElement((Container) rendered, b).getX()); + } + + @Test + void nestedGridPlacementMovesOnlyTheSelectedBranch() throws Exception { + GuiDocument document = document("" + + ""); + Element grid = document.components().get(1); + Element inside = document.components().get(2); + Element outside = document.components().get(3); + CodenameOneGUIBuilder builder = builder(document); + assertTrue(builder.applyDropPlan(outside, sequentialPlan(document, grid, inside, "GridLayout", true), 0, 0)); + assertSame(grid, document.parentOf(outside)); + assertSame(document.root(), document.parentOf(grid)); + Component rendered = render(document, 600, 400); + Component outsidePreview = componentForElement((Container) rendered, outside); + assertNotNull(outsidePreview); + assertTrue(outsidePreview.getWidth() > 0 && outsidePreview.getHeight() > 0); + } + + private static CodenameOneGUIBuilder builder(GuiDocument document) throws Exception { + CodenameOneGUIBuilder builder = new CodenameOneGUIBuilder(); + set(builder, "document", document); + return builder; + } + + @Test + void staleElementFromAnotherFormCannotEnterTheActiveDocument() throws Exception { + GuiDocument oldForm = document(""); + GuiDocument activeForm = document(""); + Element stale = oldForm.components().get(1); + Element target = activeForm.components().get(1); + CodenameOneGUIBuilder builder = builder(activeForm); + String before = activeForm.toXml(); + CodenameOneGUIBuilder.DropPlan forged = sequentialPlan(activeForm, activeForm.root(), target, "BoxLayout", true); + + assertFalse(builder.applyDropPlan(stale, forged, 0, 0)); + assertEquals(before, activeForm.toXml()); + assertFalse(builder.isActiveDocumentElement(stale)); + assertTrue(builder.isActiveDocumentElement(target)); + } + + @Test + void boxXPreviewIsActuallyScrollableAndDragAtRightEdgeAdvancesIt() throws Exception { + GuiDocument document = document("" + + "" + + "" + + "" + + ""); + Container preview = (Container) render(document, 280, 160); + assertTrue(preview.isScrollableX(), "the sample must overflow horizontally"); + Container canvas = new Container(new BorderLayout()); + canvas.setWidth(280); + canvas.setHeight(160); + canvas.add(BorderLayout.CENTER, preview); + canvas.layoutContainer(); + CodenameOneGUIBuilder builder = builder(document); + set(builder, "canvasHost", canvas); + + builder.autoScrollDuringDrag(preview.getAbsoluteX() + preview.getWidth() - 2, + preview.getAbsoluteY() + preview.getHeight() / 2); + + assertTrue(preview.getScrollX() > 0, "holding a drag at the right edge must reveal later siblings"); + } + + @Test + void previewComponentsExposeStableAccessibilityIdentifiers() { + GuiDocument document = document("" + + "" + + ""); + Container preview = (Container) render(document, 600, 400); + Component button = componentForElement(preview, document.components().get(1)); + + assertNotNull(button); + assertEquals("guibuilder.preview.LoginForm", preview.getSemantics().getIdentifier()); + assertEquals("guibuilder.preview.primaryAction", button.getSemantics().getIdentifier()); + assertTrue(button.getSemantics().getLabel().contains("primaryAction")); + } + + @Test + void mcpPublishesStructuredDesignerStateAndLiveActions() throws Exception { + GuiDocument document = document("" + + "" + + ""); + Container preview = (Container) render(document, 640, 420); + Container canvas = new Container(new LayeredLayout()); + canvas.setWidth(640); canvas.setHeight(420); + canvas.getAllStyles().setPadding(11, 13, 17, 19); + canvas.add(preview); + ((LayeredLayout) canvas.getLayout()).setInsets(preview, "0 0 0 0"); + CodenameOneGUIBuilder builder = builder(document); + set(builder, "binding", ProjectBinding.parse("projectDir=/tmp/project\nguiDir=/tmp/project/src/main/guibuilder")); + set(builder, "canvasHost", canvas); + set(builder, "previewRoot", preview); + set(builder, "guiFiles", new java.util.ArrayList(java.util.Arrays.asList("Form.gui"))); + com.codename1.guibuilder.ui.DragGuideOverlay overlay = + new com.codename1.guibuilder.ui.DragGuideOverlay(); + canvas.add(overlay); + ((LayeredLayout) canvas.getLayout()).setInsets(overlay, "0 0 0 0"); + canvas.layoutContainer(); + document.select(document.components().get(1)); + Component selectedPreview = componentForElement(preview, document.components().get(1)); + overlay.showSelection(selectedPreview); + int[] paintCoordinates = overlay.selectionPaintLocalBounds(); + assertEquals(selectedPreview.getAbsoluteX() - canvas.getAbsoluteX(), paintCoordinates[0], + "selection X must be expressed in the parent Graphics coordinate system"); + assertEquals(selectedPreview.getAbsoluteY() - canvas.getAbsoluteY(), paintCoordinates[1], + "selection Y must be expressed in the parent Graphics coordinate system"); + assertTrue(overlay.getX() > 0 || overlay.getY() > 0, + "the regression needs a padded parent so overlay-local and paint coordinates differ"); + set(builder, "dragGuideOverlay", overlay); + GuiBuilderMcpController controller = new GuiBuilderMcpController(builder); + set(builder, "mcpController", controller); + controller.register(); + controller.record("test_action", java.util.Collections.singletonMap("detail", "live")); + + Map state = builder.mcpState(controller.latestSequence()); + assertEquals("Form", state.get("activeForm")); + assertEquals("primary", state.get("selected")); + List components = (List) state.get("components"); + assertEquals(2, components.size()); + assertEquals("primary", ((Map) components.get(1)).get("name")); + assertEquals("guibuilder.preview.primary", + ((Map) components.get(1)).get("accessibilityIdentifier")); + assertEquals(((Map) components.get(1)).get("bounds"), state.get("selectionPaintBounds"), + "MCP must expose the exact absolute pixels painted by the selection overlay"); + + String tools = MCP.getServer().handleMessage( + "{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"tools/list\"}"); + assertTrue(tools.contains("guibuilder_state")); + assertTrue(tools.contains("guibuilder_drag")); + assertTrue(tools.contains("guibuilder_actions")); + String actions = MCP.getServer().handleMessage( + "{\"jsonrpc\":\"2.0\",\"id\":2,\"method\":\"tools/call\",\"params\":{" + + "\"name\":\"guibuilder_actions\",\"arguments\":{\"afterSequence\":0}}}"); + assertTrue(actions.contains("test_action")); + assertTrue(actions.contains("live")); + } + + @Test + void mcpAdditiveSelectionUsesJsonBooleansAndPublishesTheWholeGroup() throws Exception { + GuiDocument document = document("" + + "" + + ""); + Container preview = (Container) render(document, 640, 420); + Container canvas = new Container(new LayeredLayout()); + canvas.setWidth(640); canvas.setHeight(420); canvas.add(preview); + CodenameOneGUIBuilder builder = builder(document); + set(builder, "canvasHost", canvas); + set(builder, "previewRoot", preview); + GuiBuilderMcpController controller = new GuiBuilderMcpController(builder); + set(builder, "mcpController", controller); + controller.register(); + + MCP.getServer().handleMessage("{\"jsonrpc\":\"2.0\",\"id\":10,\"method\":\"tools/call\",\"params\":{" + + "\"name\":\"guibuilder_select\",\"arguments\":{\"component\":\"first\"}}}"); + String additive = MCP.getServer().handleMessage( + "{\"jsonrpc\":\"2.0\",\"id\":11,\"method\":\"tools/call\",\"params\":{" + + "\"name\":\"guibuilder_select\",\"arguments\":{\"component\":\"second\",\"additive\":true}}}"); + + assertTrue(additive.contains("\\\"selectedComponents\\\":[\\\"first\\\",\\\"second\\\"]"), additive); + } + + private static CodenameOneGUIBuilder.DropPlan sequentialPlan(GuiDocument document, Element parent, Element target, String layout, boolean after) { + CodenameOneGUIBuilder.DropPlan plan = new CodenameOneGUIBuilder.DropPlan(); + plan.document = document; + plan.parent = parent; + plan.target = target; + plan.layout = layout; + plan.after = after; + plan.valid = true; + return plan; + } + + private static Component render(GuiDocument document, int width, int height) { + Component rendered = ComponentPreviewFactory.create(document.root(), null, handler()); + rendered.setWidth(width); + rendered.setHeight(height); + ((Container) rendered).layoutContainer(); + layoutNested((Container) rendered); + return rendered; + } + + private static void layoutNested(Container container) { + container.layoutContainer(); + for (int i = 0; i < container.getComponentCount(); i++) { + if (container.getComponentAt(i) instanceof Container) layoutNested((Container) container.getComponentAt(i)); + } + } + + private static GuiDocument document(String xml) { + return GuiDocument.parse("Form.gui", xml); + } + + private static void set(Object target, String name, Object value) throws Exception { + Field field = CodenameOneGUIBuilder.class.getDeclaredField(name); + field.setAccessible(true); + field.set(target, value); + } + + private static ComponentPreviewFactory.SelectionHandler handler() { + return new ComponentPreviewFactory.SelectionHandler() { + public void selected(Element element) { } + public void dragPressed(Element element, Component source, int x, int y) { } + public boolean isDragActive() { return false; } + public void editContent(Element element) { } + }; + } + + private static Component componentForElement(Container root, Element element) { + for (int i = 0; i < root.getComponentCount(); i++) { + Component component = root.getComponentAt(i); + if (component.getClientProperty("gui.element") == element) return component; + if (component instanceof Container) { + Component nested = componentForElement((Container) component, element); + if (nested != null) return nested; + } + } + return null; + } + + private static int[] bounds(Component component) { + return new int[]{component.getAbsoluteX(), component.getAbsoluteY(), component.getWidth(), component.getHeight()}; + } +} diff --git a/scripts/guibuilder/javase/src/test/java/com/codename1/guibuilder/GeneratedSourceTest.java b/scripts/guibuilder/javase/src/test/java/com/codename1/guibuilder/GeneratedSourceTest.java new file mode 100644 index 00000000000..12c4085bdc0 --- /dev/null +++ b/scripts/guibuilder/javase/src/test/java/com/codename1/guibuilder/GeneratedSourceTest.java @@ -0,0 +1,116 @@ +package com.codename1.guibuilder; + +import com.codename1.guibuilder.model.GuiDocument; +import com.codename1.guibuilder.project.ProjectBinding; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import javax.tools.JavaCompiler; +import javax.tools.ToolProvider; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +class GeneratedSourceTest { + @Test + void generatedFormAndBindingModelCompileTogether() throws Exception { + CodenameOneGUIBuilder builder = builder("properties"); + + String form = invoke(builder, "defaultCompanionSource"); + String model = invoke(builder, "generatedModelSource"); + assertTrue(form.contains("extends Form")); + assertTrue(form.contains("new UiBinding().bind(model, this)")); + assertTrue(model.contains("implements PropertyBusinessObject")); + assertTrue(form.contains("// ")); + assertTrue(form.contains("// ")); + + compile(form, model); + } + + @Test + void bindablePojoStrategyGeneratesAnnotationBinderSource() throws Exception { + CodenameOneGUIBuilder builder = builder("bindable"); + String form = invoke(builder, "defaultCompanionSource"); + String model = invoke(builder, "generatedModelSource"); + assertTrue(form.contains("Binders.bind(model, this)")); + assertTrue(model.contains("@Bindable")); + assertTrue(model.contains("@Bind(name = \"email\", attr = BindAttr.TEXT)")); + assertTrue(model.contains("@Bind(name = \"remember\", attr = BindAttr.SELECTED)")); + compile(form, model); + } + + @Test + void noBindingStrategyHasNoModelDependency() throws Exception { + CodenameOneGUIBuilder builder = builder("none"); + String form = invoke(builder, "defaultCompanionSource"); + assertFalse(form.contains("LoginFormModel model")); + assertFalse(form.contains("UiBinding")); + compile(form, null); + } + + @Test + void guidedConstraintsAndLaterSiblingReferencesGenerateCompilableSource() throws Exception { + CodenameOneGUIBuilder builder = builder("none"); + set(builder, "document", GuiDocument.parse("/tmp/project/gui/com/example/LoginForm.gui", + "" + + "" + + "" + + "")); + + String form = invoke(builder, "defaultCompanionSource"); + + assertTrue(form.contains("setReferenceComponents(linked, anchor, anchor, null, anchor)")); + assertTrue(form.contains("size.setWidth(140)")); + assertFalse(form.contains("setPreferredW")); + assertFalse(form.contains("setPreferredH")); + assertTrue(form.indexOf("anchor = new Label") < form.indexOf("setReferenceComponents(linked"), + "all siblings must exist before a name-based relationship is installed"); + compile(form, null); + } + + private static CodenameOneGUIBuilder builder(String strategy) throws Exception { + CodenameOneGUIBuilder builder = new CodenameOneGUIBuilder(); + set(builder, "binding", ProjectBinding.parse( + "projectDir=/tmp/project\nguiDir=/tmp/project/gui\nsourceDir=/tmp/project/java\ncssFile=/tmp/project/theme.css\n")); + set(builder, "document", GuiDocument.parse("/tmp/project/gui/com/example/LoginForm.gui", + "" + + "" + + "" + + "" + + "")); + return builder; + } + + private static void compile(String form, String model) throws Exception { + Path root = Files.createTempDirectory("guibuilder-generated-source"); + Path pkg = Files.createDirectories(root.resolve("com/example")); + Path formFile = pkg.resolve("LoginForm.java"); + Files.write(formFile, form.getBytes(StandardCharsets.UTF_8)); + JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); + assertNotNull(compiler); + int result; + if (model == null) { + result = compiler.run(null, null, null, "-classpath", System.getProperty("java.class.path"), formFile.toString()); + } else { + Path modelFile = pkg.resolve("LoginFormModel.java"); + Files.write(modelFile, model.getBytes(StandardCharsets.UTF_8)); + result = compiler.run(null, null, null, "-classpath", System.getProperty("java.class.path"), + formFile.toString(), modelFile.toString()); + } + assertEquals(0, result); + } + + private static void set(Object target, String name, Object value) throws Exception { + Field field = CodenameOneGUIBuilder.class.getDeclaredField(name); + field.setAccessible(true); + field.set(target, value); + } + + private static String invoke(Object target, String name) throws Exception { + Method method = CodenameOneGUIBuilder.class.getDeclaredMethod(name); + method.setAccessible(true); + return (String) method.invoke(target); + } +} diff --git a/scripts/guibuilder/pom.xml b/scripts/guibuilder/pom.xml new file mode 100644 index 00000000000..c289a7d7071 --- /dev/null +++ b/scripts/guibuilder/pom.xml @@ -0,0 +1,51 @@ + + + 4.0.0 + com.codenameone.guibuilder + cn1-guibuilder + 8.0-SNAPSHOT + pom + Codename One GUI Builder + Modern, Maven-first visual editor for Codename One GUI forms. + https://www.codenameone.com + GPL v2 With Classpath Exceptionhttps://openjdk.java.net/legal/gplv2+ce.htmlrepo + + shaiShai Almogshai.almog@codenameone.com+4 + chenChen Fishbeinchen.fishbein@codenameone.com+4 + shannahSteve Hannahsteve.hannah@codenameone.com-8 + + https://github.com/codenameone/CodenameOnescm:git:git@github.com:codenameone/CodenameOne.git + common + + 8.0-SNAPSHOT + 8.0-SNAPSHOT + UTF-8 + 17 + 17 + 17 + 3.11.0 + cn1-guibuilder + + + com.codenameonecodenameone-core${cn1.version} + com.codenameonecodenameone-javase${cn1.version} + + + com.codenameonecodenameone-maven-plugin${cn1.plugin.version} + org.apache.maven.pluginsmaven-compiler-plugin${maven-compiler-plugin.version} + org.apache.maven.pluginsmaven-surefire-plugin3.2.2 + org.apache.maven.pluginsmaven-jar-plugin3.3.0 + org.apache.maven.pluginsmaven-dependency-plugin3.6.1 + + com.codenameonecodenameone-maven-plugin${cn1.plugin.version} + + + javasecodename1.platformjavasetruejavase + guibuilder-central + org.apache.maven.pluginsmaven-source-plugin3.3.0attach-sourcesjar-no-fork + org.apache.maven.pluginsmaven-javadoc-plugin3.6.3nonefalseattach-javadocsjar + org.apache.maven.pluginsmaven-gpg-plugin1.5sign-artifactsverifysign${gpg.passphrase}--pinentry-modeloopback + org.sonatype.centralcentral-publishing-maven-plugin0.8.0truecentraltrue + + + diff --git a/scripts/guibuilder/tools/guibuilder-mcp-client.mjs b/scripts/guibuilder/tools/guibuilder-mcp-client.mjs new file mode 100644 index 00000000000..1174d9bc7ee --- /dev/null +++ b/scripts/guibuilder/tools/guibuilder-mcp-client.mjs @@ -0,0 +1,95 @@ +#!/usr/bin/env node + +import net from "node:net"; +import readline from "node:readline"; + +const host = process.env.GUIBUILDER_MCP_HOST || "127.0.0.1"; +const port = Number(process.env.GUIBUILDER_MCP_PORT || process.argv[2] || 8765); +let nextId = 1; +const pending = new Map(); +let buffer = ""; + +const socket = net.createConnection({ host, port }); +socket.setEncoding("utf8"); +socket.on("data", chunk => { + buffer += chunk; + while (buffer.includes("\n")) { + const index = buffer.indexOf("\n"); + const line = buffer.slice(0, index); + buffer = buffer.slice(index + 1); + if (!line.trim()) continue; + const message = JSON.parse(line); + const handler = pending.get(message.id); + if (handler) { + pending.delete(message.id); + handler(message); + } else { + process.stdout.write(`${line}\n`); + } + } +}); + +function request(method, params = {}) { + const id = nextId++; + return new Promise((resolve, reject) => { + pending.set(id, resolve); + socket.write(`${JSON.stringify({ jsonrpc: "2.0", id, method, params })}\n`, error => { + if (error) { + pending.delete(id); + reject(error); + } + }); + }); +} + +function toolText(response) { + const content = response?.result?.content; + if (!Array.isArray(content) || !content.length) return response; + const text = content[0]?.text; + if (typeof text !== "string") return response; + try { return JSON.parse(text); } catch { return text; } +} + +socket.on("connect", async () => { + const initialized = await request("initialize", { + protocolVersion: "2025-03-26", + capabilities: {}, + clientInfo: { name: "guibuilder-local-client", version: "1.0" } + }); + process.stdout.write(`MCP_CONNECTED ${host}:${port} ${JSON.stringify(initialized.result?.serverInfo || {})}\n`); + socket.write(`${JSON.stringify({ jsonrpc: "2.0", method: "notifications/initialized", params: {} })}\n`); + + const input = readline.createInterface({ input: process.stdin, terminal: false }); + let queue = Promise.resolve(); + input.on("line", line => { + queue = queue.then(async () => { + const trimmed = line.trim(); + if (!trimmed) return; + if (trimmed === "tools") { + process.stdout.write(`${JSON.stringify((await request("tools/list")).result)}\n`); + return; + } + if (trimmed === "quit" || trimmed === "exit") { + socket.end(); + return; + } + let command; + try { + command = JSON.parse(trimmed); + } catch { + process.stdout.write(`${JSON.stringify({ error: "Expected JSON: {tool, arguments}" })}\n`); + return; + } + const response = await request("tools/call", { + name: command.tool, + arguments: command.arguments || {} + }); + process.stdout.write(`${JSON.stringify(toolText(response))}\n`); + }).catch(error => process.stdout.write(`${JSON.stringify({ error: String(error) })}\n`)); + }); +}); + +socket.on("error", error => { + process.stderr.write(`MCP_CONNECT_ERROR ${error.message}\n`); + process.exitCode = 1; +}); From f0099dabf71d2161edbc066a316759e4095e218f Mon Sep 17 00:00:00 2001 From: Shai Almog <67850168+shai-almog@users.noreply.github.com> Date: Wed, 5 Aug 2026 15:50:32 +0700 Subject: [PATCH 02/33] Fix table cell shuffling and the stale canvas after a drop Two demos did not survive real use. **TableLayout moved components nobody dragged.** Every drop ran the table through normalizeTableCells, which reassigned row/column from sibling order. That is what implicit, code-first TableLayout does, and it is wrong for a designer: dropping one component renumbered every other cell, so the table bounced into a layout the user never asked for. A table drop is now a placement into one addressed cell. TablePlacementAdapter picks the cell from the pointer over the parent's own geometry, an occupied cell swaps with its occupant instead of pushing the sequence along, and normalizeTableCells only assigns cells to children that have none or that collide. Moving a component earlier or later in a table now swaps the two cells rather than renumbering the table, so the reorder means what it says. XML order is left alone: in a table it carries no layout meaning, and churning it churns the generated source for nothing. **Nested containers looked broken because the canvas stopped following the model.** The drop spacer shown during a drag called animateLayout on a preview container. A layout animation captures the component tree and re-applies that captured state when it finishes, and the drop commits and rebuilds the canvas well inside that window -- so the animation restored the pre-drop preview over the new one and left the spacer behind. The model was always right; the canvas showed the component in its old parent, with the old parent's geometry. Hit testing then worked off phantom rectangles, which is why the next drag missed and the whole editor felt unstable. Nested layouts hit it hardest because each level animated. The spacer now revalidates instead. Undo and redo restore by reparsing, so every Element identity changes even though the form is unchanged. The multi-selection was dropped rather than re-resolved, silently deselecting after every undo; it is now rebound by name. Attributes were serialized in Hashtable order, so the same form produced different text on different runs. That made the transaction's did-anything- change check unreliable and churned unrelated lines on every save. GuiDocument now writes them in a stable order -- type, name, layout, then alphabetical. LiveWorkspaceDragTest is the regression harness this needed: it drives the assembled workspace rather than the document model, because the model-level tests passed throughout for gestures that visibly failed in the editor. It asserts one preview per component and that each preview renders inside the parent the model claims. Co-Authored-By: Claude Opus 5 (1M context) --- .../guibuilder/CodenameOneGUIBuilder.java | 218 ++++++++++++++++- .../guibuilder/model/GuiDocument.java | 87 ++++++- .../guibuilder/DesignerInteractionTest.java | 212 +++++++++++++++++ .../guibuilder/LiveWorkspaceDragTest.java | 221 ++++++++++++++++++ 4 files changed, 729 insertions(+), 9 deletions(-) create mode 100644 scripts/guibuilder/javase/src/test/java/com/codename1/guibuilder/LiveWorkspaceDragTest.java diff --git a/scripts/guibuilder/common/src/main/java/com/codename1/guibuilder/CodenameOneGUIBuilder.java b/scripts/guibuilder/common/src/main/java/com/codename1/guibuilder/CodenameOneGUIBuilder.java index b3f8b710487..1c2e6ce73f5 100644 --- a/scripts/guibuilder/common/src/main/java/com/codename1/guibuilder/CodenameOneGUIBuilder.java +++ b/scripts/guibuilder/common/src/main/java/com/codename1/guibuilder/CodenameOneGUIBuilder.java @@ -1360,7 +1360,12 @@ private void showBoxDropSpacer(Container parent, Component target, boolean after boxDropParent = parent; boxDropIndex = Math.max(0, Math.min(index, parent.getComponentCount())); parent.addComponent(boxDropIndex, spacer); - parent.animateLayout(90); + // revalidate, never animateLayout: a layout animation captures the preview tree and + // re-applies that captured state when it finishes. The drop commits and rebuilds the + // canvas well inside the animation's window, so the animation then restored the + // pre-drop preview over the new one -- the model moved, the canvas did not, and the + // spacer stayed behind. Nested containers made it worse because each level animated. + parent.revalidate(); } private void clearBoxDropSpacer() { @@ -1442,7 +1447,29 @@ public void plan(DropPlan plan, Element dragged, Element target, Component sourc private final class FlowPlacementAdapter extends BoxPlacementAdapter { } private final class GridPlacementAdapter extends BoxPlacementAdapter { } - private final class TablePlacementAdapter extends BoxPlacementAdapter { } + + /** + * A table drop is a placement into one addressed cell, not an insertion into a sequence. It + * therefore behaves like BorderLayout rather than BoxLayout: the pointer picks the cell, and a + * cell that is already taken swaps rather than pushing everything along. + */ + private final class TablePlacementAdapter implements PlacementAdapter { + public void plan(DropPlan plan, Element dragged, Element target, Component source, int x, int y) { + Component parentPreview = componentForElement(canvasHost, plan.parent); + plan.tableCell = tableCellAt(plan.parent, parentPreview, x, y); + plan.occupied = childAtTableCell(plan.parent, plan.tableCell[0], plan.tableCell[1], dragged); + String cell = "row " + plan.tableCell[0] + ", column " + plan.tableCell[1]; + if (plan.occupied == null) { + plan.message = "Place in " + cell; + } else if (dragged == null) { + plan.valid = false; + plan.message = cell + " is occupied by " + + value(plan.occupied, "name", value(plan.occupied, "type", "component")); + } else { + plan.message = "Swap with " + value(plan.occupied, "name", "component") + " in " + cell; + } + } + } private String borderRegionAt(Element parent, Element dragged, Component parentPreview, Component source, int x, int y) { @@ -1601,6 +1628,9 @@ boolean applyDropPlan(Element dragged, DropPlan plan, int x, int y) { private boolean applyDropPlanImpl(Element dragged, DropPlan plan, int x, int y) { Element oldParent = document.parentOf(dragged); + if ("TableLayout".equals(plan.layout) && plan.tableCell != null) { + return applyTableDrop(dragged, plan, oldParent); + } int oldIndex = document.componentIndex(oldParent, dragged); String oldLayout = oldParent == null ? "" : value(oldParent, "layout", "BoxLayout"); String oldLayeredInsets = dragged.getAttribute("layeredInsets"); @@ -1682,6 +1712,58 @@ private boolean applyDropPlanImpl(Element dragged, DropPlan plan, int x, int y) return true; } + /** + * Commits a table drop as an explicit cell assignment. Only the dragged component and, when the + * aimed cell was taken, its previous occupant change cells; every other child keeps the cell it + * had. XML order is left alone as well, because in a table it carries no layout meaning and + * reordering it would churn the generated source for no visible reason. + */ + private boolean applyTableDrop(Element dragged, DropPlan plan, Element oldParent) { + int row = plan.tableCell[0]; + int column = plan.tableCell[1]; + Integer vacatedRow = parseInteger(dragged.getAttribute("tableRow")); + Integer vacatedColumn = parseInteger(dragged.getAttribute("tableColumn")); + if (oldParent != plan.parent) { + document.select(dragged); + if (!document.moveSelectedToParent(plan.parent, componentChildren(plan.parent).size())) return false; + } + if (plan.occupied != null) { + document.select(plan.occupied); + boolean canSwap = oldParent == plan.parent && vacatedRow != null && vacatedColumn != null; + int[] destination = canSwap ? new int[]{vacatedRow.intValue(), vacatedColumn.intValue()} + : firstFreeTableCell(plan.parent, dragged, plan.occupied); + document.setAttribute("tableRow", String.valueOf(destination[0])); + document.setAttribute("tableColumn", String.valueOf(destination[1])); + } + document.select(dragged); + document.setAttribute("layoutConstraint", null); + document.setAttribute("layeredInsets", null); + document.setAttribute("tableRow", String.valueOf(row)); + document.setAttribute("tableColumn", String.valueOf(column)); + normalizeTableCells(plan.parent); + document.select(dragged); + return true; + } + + /** The first cell in row-major order that no sibling claims, growing past the last row if needed. */ + private int[] firstFreeTableCell(Element parent, Element... ignored) { + int columns = Math.max(1, integer(value(parent, "tableLayoutColumns", "2"), 2)); + Set taken = new LinkedHashSet<>(); + for (Element child : componentChildren(parent)) { + boolean skip = false; + for (Element candidate : ignored) skip = skip || child == candidate; + if (skip) continue; + Integer row = parseInteger(child.getAttribute("tableRow")); + Integer column = parseInteger(child.getAttribute("tableColumn")); + if (row != null && column != null) taken.add(row + ":" + column); + } + for (int cursor = 0; ; cursor++) { + int row = cursor / columns; + int column = cursor % columns; + if (!taken.contains(row + ":" + column)) return new int[]{row, column}; + } + } + private void persistGuidedConstraints(GuiDocument targetDocument, Element element, DropPlan plan, Container parent, Component source, int width, int height) { String[] insets = GuidedLayoutSupport.insetValues(element); @@ -2276,19 +2358,79 @@ private void clearVerticalReferences(String[] refs, String[] positions) { private int standardGap() { return Math.max(6, Display.getInstance().convertToPixels(1)); } + /** + * Gives every child of a table a cell, without ever moving a child that already has one. + * + *

Reassigning cells from sibling order is what an implicit, code-first TableLayout does, and + * it is wrong for a designer: dropping one component then renumbers every other cell, so the + * whole table jumps to a layout nobody asked for. Cells are the user's placement decision, so + * only components that have no cell yet, or whose cell collides with an earlier sibling, are + * assigned one, and they take the first free cell in row-major order. + */ private void normalizeTableCells(Element parent) { int columns = Math.max(1, integer(value(parent, "tableLayoutColumns", "2"), 2)); List children = componentChildren(parent); - int requiredRows = Math.max(1, (children.size() + columns - 1) / columns); + Set taken = new LinkedHashSet<>(); + List unplaced = new ArrayList<>(); + for (Element child : children) { + Integer row = parseInteger(child.getAttribute("tableRow")); + Integer column = parseInteger(child.getAttribute("tableColumn")); + if (row == null || column == null || row.intValue() < 0 || column.intValue() < 0 + || column.intValue() >= columns || !taken.add(row + ":" + column)) { + unplaced.add(child); + } + } + int cursor = 0; + for (Element child : unplaced) { + while (taken.contains((cursor / columns) + ":" + (cursor % columns))) cursor++; + taken.add((cursor / columns) + ":" + (cursor % columns)); + document.select(child); + document.setAttribute("tableRow", String.valueOf(cursor / columns)); + document.setAttribute("tableColumn", String.valueOf(cursor % columns)); + } + growTableToFit(parent, taken); + } + + /** Widens the declared row count so no assigned cell falls outside the table and disappears. */ + private void growTableToFit(Element parent, Set occupiedCells) { + int requiredRows = 1; + for (String cell : occupiedCells) { + Integer row = parseInteger(cell.substring(0, cell.indexOf(':'))); + if (row != null) requiredRows = Math.max(requiredRows, row.intValue() + 1); + } if (integer(value(parent, "tableLayoutRows", "2"), 2) < requiredRows) { document.select(parent); document.setAttribute("tableLayoutRows", String.valueOf(requiredRows)); } - for (int i = 0; i < children.size(); i++) { - document.select(children.get(i)); - document.setAttribute("tableRow", String.valueOf(i / columns)); - document.setAttribute("tableColumn", String.valueOf(i % columns)); + } + + /** The child occupying {@code row}/{@code column}, or null. */ + private Element childAtTableCell(Element parent, int row, int column, Element ignored) { + for (Element child : componentChildren(parent)) { + if (child == ignored) continue; + Integer childRow = parseInteger(child.getAttribute("tableRow")); + Integer childColumn = parseInteger(child.getAttribute("tableColumn")); + if (childRow != null && childColumn != null + && childRow.intValue() == row && childColumn.intValue() == column) return child; + } + return null; + } + + /** + * The cell under the pointer, derived from the parent preview's own geometry rather than from + * sibling order, so a drop lands where it was aimed. + */ + int[] tableCellAt(Element parent, Component parentPreview, int x, int y) { + int columns = Math.max(1, integer(value(parent, "tableLayoutColumns", "2"), 2)); + int rows = Math.max(1, integer(value(parent, "tableLayoutRows", "2"), 2)); + if (parentPreview == null || parentPreview.getWidth() < 1 || parentPreview.getHeight() < 1) { + return new int[]{0, 0}; } + int width = Math.max(1, contentWidth(parentPreview)); + int height = Math.max(1, contentHeight(parentPreview)); + int column = ((x - contentX(parentPreview)) * columns) / width; + int row = ((y - contentY(parentPreview)) * rows) / height; + return new int[]{Math.max(0, Math.min(rows - 1, row)), Math.max(0, Math.min(columns - 1, column))}; } private List componentChildren(Element parent) { @@ -2318,6 +2460,8 @@ static final class DropPlan { String snapDescription; SnapResult horizontalSnap; SnapResult verticalSnap; + /** {row, column} for a TableLayout drop; null for every other layout. */ + int[] tableCell; } static final class GuidedSimulation { @@ -2497,6 +2641,14 @@ boolean isActiveDocumentElement(Element element) { return document != null && document.containsElement(element); } + List selectedElementsSnapshot() { + return new ArrayList<>(selectedElements); + } + + Container canvasHostForTest() { return canvasHost; } + + GuiDocument documentForTest() { return document; } + private void cancelDesignerDrag() { designerDraggedElement = null; designerDragDocument = null; @@ -2535,6 +2687,7 @@ private void normalizeSelection() { selectedElements.clear(); return; } + rebindSelectionToDocument(); selectedElements.removeIf(element -> !document.containsElement(element)); Element primary = document.selected(); if (selectedElements.isEmpty() && primary != null && primary != document.root()) selectedElements.add(primary); @@ -2543,6 +2696,32 @@ private void normalizeSelection() { } } + /** + * Re-resolves the selection against the live tree by component name. + * + *

Undo and redo restore by reparsing, so every Element identity changes even though the + * form is unchanged. Everything the editor holds -- the multi-selection here, and the drag + * state cleared alongside it -- then refers to a tree the document has thrown away. Dropping + * those entries would silently deselect after every undo; names are unique in a document, so + * they survive the reparse and are the right key to re-resolve against. + */ + private void rebindSelectionToDocument() { + if (selectedElements.isEmpty()) return; + List rebound = new ArrayList<>(); + for (Element element : selectedElements) { + if (document.containsElement(element)) { + rebound.add(element); + continue; + } + String name = element.getAttribute("name"); + Element live = name == null ? null : findElementNamed(document, name); + if (live != null) rebound.add(live); + } + if (rebound.equals(new ArrayList<>(selectedElements))) return; + selectedElements.clear(); + selectedElements.addAll(rebound); + } + private void selectElement(Element element, boolean additive, String source) { if (document == null || element == null || !document.containsElement(element)) return; if (element == document.root()) { @@ -3010,9 +3189,17 @@ boolean reorderSelectedInParent(int delta) { boolean moved = false; document.beginTransaction(); try { + // In a table the neighbour must be identified before the move, because "the component + // one step earlier" is the one whose cell the user wants; afterwards it is the + // selection itself that sits at that index. + Element neighbour = parent != null && "TableLayout".equals(value(parent, "layout", "BoxLayout")) + ? siblingBy(parent, selected, delta) : null; if (!document.moveSelectedBy(delta)) return false; moved = true; if (parent != null && "TableLayout".equals(value(parent, "layout", "BoxLayout"))) { + // Swap the two cells rather than renumbering the table from sibling order: the + // user asked these two components to trade places, not the rest to shuffle. + if (neighbour != null) swapTableCells(selected, neighbour); normalizeTableCells(parent); document.select(selected); } @@ -3022,6 +3209,23 @@ boolean reorderSelectedInParent(int delta) { return moved; } + private Element siblingBy(Element parent, Element element, int delta) { + List children = componentChildren(parent); + int index = children.indexOf(element) + delta; + return index < 0 || index >= children.size() ? null : children.get(index); + } + + private void swapTableCells(Element first, Element second) { + String firstRow = first.getAttribute("tableRow"); + String firstColumn = first.getAttribute("tableColumn"); + document.select(first); + document.setAttribute("tableRow", second.getAttribute("tableRow")); + document.setAttribute("tableColumn", second.getAttribute("tableColumn")); + document.select(second); + document.setAttribute("tableRow", firstRow); + document.setAttribute("tableColumn", firstColumn); + } + private void applyLayeredAction(String action) { Element element = document.selected(); Element parentElement = document.parentOf(element); diff --git a/scripts/guibuilder/common/src/main/java/com/codename1/guibuilder/model/GuiDocument.java b/scripts/guibuilder/common/src/main/java/com/codename1/guibuilder/model/GuiDocument.java index b8a1ec25795..2f561896e95 100644 --- a/scripts/guibuilder/common/src/main/java/com/codename1/guibuilder/model/GuiDocument.java +++ b/scripts/guibuilder/common/src/main/java/com/codename1/guibuilder/model/GuiDocument.java @@ -464,8 +464,19 @@ public List components() { return result; } + /** The direct component children of {@code parent}, in document order, skipping non-component tags. */ + public static List componentsIn(Element parent) { + List result = new ArrayList<>(); + if (parent == null) return result; + for (int i = 0; i < parent.getNumChildren(); i++) { + Object child = parent.getChildAt(i); + if (child instanceof Element element && "component".equals(element.getTagName())) result.add(element); + } + return result; + } + public String toXml() { - return "\n\n" + new XMLWriter(true).toXML(root); + return "\n\n" + xmlOnly(); } public boolean canUndo() { return !undo.isEmpty(); } @@ -600,8 +611,80 @@ private State capture() { return new State(xmlOnly(), selected == null ? null : selected.getAttribute("name"), modified); } + /** + * Serializes the tree with attributes in a stable order. + * + *

Element stores attributes in a Hashtable, so XMLWriter emits them in whatever order that + * table happens to iterate. Two trees describing the same form then produce different text, + * which made undo comparison unreliable -- an edit-then-undo round trip reported a change that + * had not happened -- and made every save churn unrelated lines in the .gui file. Ordering is + * type, name, layout, then the rest alphabetically: the identifying attributes first, so the + * files stay readable. + */ private String xmlOnly() { - return new XMLWriter(true).toXML(root); + StringBuilder out = new StringBuilder(); + writeElement(root, 0, out); + return out.toString(); + } + + private static final String[] LEADING_ATTRIBUTES = {"type", "name", "layout"}; + + private static void writeElement(Element element, int depth, StringBuilder out) { + // String.repeat is outside the Codename One runtime API the compliance check enforces. + StringBuilder indentBuilder = new StringBuilder(); + for (int i = 0; i < depth; i++) indentBuilder.append('\t'); + String indent = indentBuilder.toString(); + out.append(indent).append('<').append(element.getTagName()); + for (String key : orderedAttributeNames(element)) { + out.append(' ').append(key).append("=\"").append(escape(element.getAttribute(key))).append('"'); + } + // Every tag, not just components: a form also carries children for its toolbar, + // and dropping them here would delete the toolbar on the next save. + List children = new ArrayList<>(); + for (int i = 0; i < element.getNumChildren(); i++) { + Object child = element.getChildAt(i); + if (child instanceof Element e && e.getTagName() != null) children.add(e); + } + if (children.isEmpty()) { + out.append(" />\n"); + return; + } + out.append(">\n"); + for (Element child : children) writeElement(child, depth + 1, out); + out.append(indent).append("\n"); + } + + private static List orderedAttributeNames(Element element) { + java.util.Hashtable attributes = element.getAttributes(); + List rest = new ArrayList<>(); + if (attributes != null) { + for (java.util.Enumeration keys = attributes.keys(); keys.hasMoreElements();) { + rest.add(String.valueOf(keys.nextElement())); + } + } + java.util.Collections.sort(rest); + List ordered = new ArrayList<>(); + for (String leading : LEADING_ATTRIBUTES) { + if (rest.remove(leading)) ordered.add(leading); + } + ordered.addAll(rest); + return ordered; + } + + private static String escape(String value) { + if (value == null) return ""; + StringBuilder out = new StringBuilder(value.length()); + for (int i = 0; i < value.length(); i++) { + char c = value.charAt(i); + switch (c) { + case '&' -> out.append("&"); + case '<' -> out.append("<"); + case '>' -> out.append(">"); + case '"' -> out.append("""); + default -> out.append(c); + } + } + return out.toString(); } private void restore(State state) { diff --git a/scripts/guibuilder/javase/src/test/java/com/codename1/guibuilder/DesignerInteractionTest.java b/scripts/guibuilder/javase/src/test/java/com/codename1/guibuilder/DesignerInteractionTest.java index 43c10b36f00..9f357a49745 100644 --- a/scripts/guibuilder/javase/src/test/java/com/codename1/guibuilder/DesignerInteractionTest.java +++ b/scripts/guibuilder/javase/src/test/java/com/codename1/guibuilder/DesignerInteractionTest.java @@ -1110,6 +1110,218 @@ void tableMoveEarlierReassignsCellsAndChangesRenderedOrder() throws Exception { < componentForElement((Container) rendered, b).getX()); } + @Test + void aTableDropLandsInTheCellUnderThePointerAndLeavesEveryOtherCellAlone() throws Exception { + GuiDocument document = document("" + + "" + + "" + + ""); + Element a = document.components().get(1); + Element b = document.components().get(2); + Element c = document.components().get(3); + CodenameOneGUIBuilder builder = builder(document); + Container canvas = canvasFor(document, builder, 400, 400); + + // Drop c into the empty cell (row 1, column 1): the lower right quadrant. + Component source = componentForElement(canvas, c); + CodenameOneGUIBuilder.DropPlan plan = builder.planDrop(c, document.root(), source, 300, 300); + assertNotNull(plan); + assertTrue(builder.applyDropPlan(c, plan, 300, 300)); + + assertEquals("1", c.getAttribute("tableRow"), document.toXml()); + assertEquals("1", c.getAttribute("tableColumn"), document.toXml()); + assertEquals("0:0", a.getAttribute("tableRow") + ":" + a.getAttribute("tableColumn"), + "a was not dragged, so its cell must not move: " + document.toXml()); + assertEquals("0:1", b.getAttribute("tableRow") + ":" + b.getAttribute("tableColumn"), + "b was not dragged, so its cell must not move: " + document.toXml()); + } + + @Test + void aTableDropNeverStacksTwoComponentsInOneCell() throws Exception { + GuiDocument document = document("" + + "" + + "" + + "" + + ""); + Element a = document.components().get(1); + Element d = document.components().get(4); + CodenameOneGUIBuilder builder = builder(document); + Container canvas = canvasFor(document, builder, 400, 400); + + // Drop d onto a's occupied cell: the two must swap, not overlap. + Component source = componentForElement(canvas, d); + CodenameOneGUIBuilder.DropPlan plan = builder.planDrop(d, document.root(), source, 100, 100); + assertNotNull(plan); + assertTrue(builder.applyDropPlan(d, plan, 100, 100)); + + assertEquals("0:0", d.getAttribute("tableRow") + ":" + d.getAttribute("tableColumn"), document.toXml()); + assertEquals("1:1", a.getAttribute("tableRow") + ":" + a.getAttribute("tableColumn"), + "the displaced component must take the vacated cell: " + document.toXml()); + assertEquals(4, distinctTableCells(document).size(), document.toXml()); + } + + @Test + void aComponentDraggedBetweenTwoNestedContainersReparentsAndRenders() throws Exception { + GuiDocument document = nestedColumnsDocument(); + Element leftGrid = document.components().get(2); + Element rightActions = document.components().get(7); + Element nestedA = document.components().get(3); + Element nestedAction = document.components().get(9); + CodenameOneGUIBuilder builder = builder(document); + Container canvas = canvasFor(document, builder, 700, 500); + + Component target = componentForElement(canvas, nestedAction); + assertNotNull(target, "the demo must render the right hand column"); + Component source = componentForElement(canvas, nestedA); + int x = target.getAbsoluteX() + target.getWidth() / 2; + int y = target.getAbsoluteY() + target.getHeight() - 2; + + CodenameOneGUIBuilder.DropPlan plan = builder.planDrop(nestedA, builder.elementAt(canvas, x, y), source, x, y); + assertNotNull(plan, "hovering the far column must produce a drop plan"); + assertSame(rightActions, plan.parent, "the drop must target the hovered container, not the drag source's"); + assertTrue(builder.applyDropPlan(nestedA, plan, x, y), document.toXml()); + + assertSame(rightActions, document.parentOf(nestedA), document.toXml()); + assertEquals(3, document.componentsIn(leftGrid).size(), + "the source container must actually give the child up: " + document.toXml()); + Container reRendered = canvasFor(document, builder, 700, 500); + Component moved = componentForElement(reRendered, nestedA); + assertNotNull(moved, "the moved component must still render: " + document.toXml()); + assertTrue(moved.getWidth() > 0 && moved.getHeight() > 0, + "the moved component must not collapse to zero size: " + document.toXml()); + Component column = componentForElement(reRendered, rightActions); + assertTrue(moved.getAbsoluteX() >= column.getAbsoluteX() + && moved.getAbsoluteX() < column.getAbsoluteX() + column.getWidth(), + "the moved component must render inside its new parent"); + } + + @Test + void aNestedContainerCanBeDraggedWholeWithoutLosingItsChildren() throws Exception { + GuiDocument document = nestedColumnsDocument(); + Element contentColumns = document.components().get(1); + Element leftGrid = document.components().get(2); + Element rightActions = document.components().get(7); + CodenameOneGUIBuilder builder = builder(document); + Container canvas = canvasFor(document, builder, 700, 500); + + Component source = componentForElement(canvas, leftGrid); + Component target = componentForElement(canvas, rightActions); + int x = target.getAbsoluteX() + target.getWidth() - 2; + int y = target.getAbsoluteY() + target.getHeight() / 2; + CodenameOneGUIBuilder.DropPlan plan = builder.planDrop(leftGrid, rightActions, source, x, y); + assertNotNull(plan); + assertTrue(builder.applyDropPlan(leftGrid, plan, x, y), document.toXml()); + + assertEquals(4, document.componentsIn(leftGrid).size(), + "moving a container must carry its whole subtree: " + document.toXml()); + assertNotSame(contentColumns, document.parentOf(leftGrid)); + Container reRendered = canvasFor(document, builder, 700, 500); + for (Element child : document.componentsIn(leftGrid)) { + Component preview = componentForElement(reRendered, child); + assertNotNull(preview, child.getAttribute("name") + " vanished: " + document.toXml()); + } + } + + @Test + void undoAfterAReparentingDragRestoresTheExactTreeAndOrder() throws Exception { + GuiDocument document = nestedColumnsDocument(); + String before = document.toXml(); + Element leftGrid = document.components().get(2); + Element rightActions = document.components().get(7); + Element nestedA = document.components().get(3); + CodenameOneGUIBuilder builder = builder(document); + canvasFor(document, builder, 700, 500); + + assertTrue(builder.applyDropPlan(nestedA, + sequentialPlan(document, rightActions, rightActions, "BoxLayout", true), 0, 0)); + assertSame(rightActions, document.parentOf(nestedA)); + assertTrue(document.undo()); + + assertEquals(before, document.toXml(), "undo must restore the document byte for byte"); + List grid = GuiDocument.componentsIn(findByName(document, "leftGrid")); + assertEquals(List.of("nestedA", "nestedB", "nestedC", "nestedD"), namesOf(grid), + "undo must restore sibling order, not just membership"); + } + + @Test + void repeatedEditAndUndoCyclesNeverDriftTheDocument() throws Exception { + GuiDocument document = nestedColumnsDocument(); + String before = document.toXml(); + CodenameOneGUIBuilder builder = builder(document); + for (int i = 0; i < 4; i++) { + canvasFor(document, builder, 700, 500); + Element rightActions = findByName(document, "rightActions"); + Element nestedA = findByName(document, "nestedA"); + assertTrue(builder.applyDropPlan(nestedA, + sequentialPlan(document, rightActions, rightActions, "BoxLayout", true), 0, 0), + "cycle " + i + " could not move the component"); + assertTrue(document.undo(), "cycle " + i + " could not undo"); + assertEquals(before, document.toXml(), "the document drifted on cycle " + i); + } + } + + private static Element findByName(GuiDocument document, String name) { + for (Element element : document.components()) { + if (name.equals(element.getAttribute("name"))) return element; + } + return null; + } + + private static List namesOf(List elements) { + List names = new java.util.ArrayList<>(); + for (Element element : elements) names.add(element.getAttribute("name")); + return names; + } + + private static String value(Element element, String attribute, String fallback) { + String raw = element == null ? null : element.getAttribute(attribute); + return raw == null ? fallback : raw; + } + + /** + * Indented exactly like the .gui files the editor loads from disk. The indentation is not + * cosmetic: XMLParser turns it into whitespace text nodes, so a document read from a project + * has interleaved non-component children that a document built from one long line does not. + */ + private static GuiDocument nestedColumnsDocument() { + return document("\n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + ""); + } + + private static java.util.Set distinctTableCells(GuiDocument document) { + java.util.Set cells = new java.util.HashSet<>(); + for (int i = 1; i < document.components().size(); i++) { + Element child = document.components().get(i); + cells.add(child.getAttribute("tableRow") + ":" + child.getAttribute("tableColumn")); + } + return cells; + } + + private static Container canvasFor(GuiDocument document, CodenameOneGUIBuilder builder, int width, int height) + throws Exception { + Container preview = (Container) render(document, width, height); + Container canvas = new Container(new LayeredLayout()); + canvas.setWidth(width); + canvas.setHeight(height); + canvas.add(preview); + canvas.layoutContainer(); + layoutNested(canvas); + set(builder, "canvasHost", canvas); + return canvas; + } + @Test void nestedGridPlacementMovesOnlyTheSelectedBranch() throws Exception { GuiDocument document = document("" diff --git a/scripts/guibuilder/javase/src/test/java/com/codename1/guibuilder/LiveWorkspaceDragTest.java b/scripts/guibuilder/javase/src/test/java/com/codename1/guibuilder/LiveWorkspaceDragTest.java new file mode 100644 index 00000000000..84547f12857 --- /dev/null +++ b/scripts/guibuilder/javase/src/test/java/com/codename1/guibuilder/LiveWorkspaceDragTest.java @@ -0,0 +1,221 @@ +package com.codename1.guibuilder; + +import com.codename1.ui.Component; +import com.codename1.ui.Container; +import com.codename1.ui.Display; +import com.codename1.xml.Element; +import java.io.File; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.List; +import javax.swing.JPanel; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +/** + * Drives the assembled workspace -- toolbar, canvas, inspector, hierarchy -- rather than the + * document model alone. The model-level tests pass for gestures that visibly fail in the running + * editor, because the defects live in what the canvas holds after a commit, not in the XML. + */ +class LiveWorkspaceDragTest { + @BeforeAll + static void initializeCodenameOneRuntime() { + if (!Display.isInitialized()) Display.init(new JPanel()); + } + + @Test + void aCrossContainerDragLeavesExactlyOnePreviewPerComponent() throws Exception { + CodenameOneGUIBuilder builder = workspace(); + assertNull(onEdt(() -> builder.mcpOpenForm("com.example.NestedLayoutsForm"))); + flushEdt(); + assertOnePreviewPerComponent(builder, "after opening the form"); + + assertNull(onEdt(() -> builder.mcpDragComponent("nestedB", "nestedAction", "below", null, null))); + flushEdt(); + + assertOnePreviewPerComponent(builder, "after dragging nestedB into the other column"); + assertPreviewsSitInsideTheirModelParent(builder, "after dragging nestedB into the other column"); + } + + @Test + void repeatedCrossContainerDragsStayStable() throws Exception { + CodenameOneGUIBuilder builder = workspace(); + assertNull(onEdt(() -> builder.mcpOpenForm("com.example.NestedLayoutsForm"))); + flushEdt(); + + String[] moved = {"nestedB", "nestedC", "nestedD", "nestedA"}; + for (String name : moved) { + assertNull(onEdt(() -> builder.mcpDragComponent(name, "nestedAction", "below", null, null)), + "dragging " + name + " reported an error"); + flushEdt(); + assertOnePreviewPerComponent(builder, "after dragging " + name); + assertPreviewsSitInsideTheirModelParent(builder, "after dragging " + name); + } + } + + @Test + void undoAfterACrossContainerDragRestoresBothTheModelAndTheCanvas() throws Exception { + CodenameOneGUIBuilder builder = workspace(); + assertNull(onEdt(() -> builder.mcpOpenForm("com.example.NestedLayoutsForm"))); + flushEdt(); + List before = hierarchy(builder); + + assertNull(onEdt(() -> builder.mcpDragComponent("nestedB", "nestedAction", "below", null, null))); + flushEdt(); + assertNotEquals(before, hierarchy(builder), "the drag must actually change the tree"); + + assertNull(onEdt(() -> builder.mcpCommand("undo"))); + flushEdt(); + + assertEquals(before, hierarchy(builder), "undo must restore the tree exactly"); + assertOnePreviewPerComponent(builder, "after undo"); + assertPreviewsSitInsideTheirModelParent(builder, "after undo"); + for (Element element : builder.selectedElementsSnapshot()) { + assertTrue(builder.isActiveDocumentElement(element), + "the selection still points at the pre-undo tree"); + } + } + + @Test + void aDragImmediatelyAfterUndoStillCommits() throws Exception { + CodenameOneGUIBuilder builder = workspace(); + assertNull(onEdt(() -> builder.mcpOpenForm("com.example.NestedLayoutsForm"))); + flushEdt(); + assertNull(onEdt(() -> builder.mcpDragComponent("nestedB", "nestedAction", "below", null, null))); + flushEdt(); + assertNull(onEdt(() -> builder.mcpCommand("undo"))); + flushEdt(); + + assertNull(onEdt(() -> builder.mcpDragComponent("nestedC", "nestedAction", "below", null, null)), + "the first drag after an undo must still commit"); + flushEdt(); + assertEquals("rightActions", parentName(builder, "nestedC"), hierarchy(builder).toString()); + assertOnePreviewPerComponent(builder, "after the post-undo drag"); + } + + // ---- harness --------------------------------------------------------------------------- + + /** Runs work on the EDT, exactly as GuiBuilderMcpController does for every MCP tool. */ + private static String onEdt(java.util.function.Supplier work) { + final String[] out = new String[1]; + Display.getInstance().callSeriallyAndWait(() -> out[0] = work.get()); + return out[0]; + } + + private static CodenameOneGUIBuilder workspace() throws Exception { + System.setProperty("guibuilder.input", demoBinding().toString()); + System.setProperty("guibuilder.canvasMode", "desktop"); + CodenameOneGUIBuilder builder = new CodenameOneGUIBuilder(); + builder.init(null); + builder.runApp(); + flushEdt(); + return builder; + } + + /** The binding holds absolute paths, so it is written per run rather than checked in. */ + private static Path demoBinding() throws Exception { + Path demo = new File("../demo-project").getCanonicalFile().toPath(); + Path input = Files.createTempFile("guibuilder", ".input"); + Files.write(input, ("projectDir=" + demo + "\n" + + "guiDir=" + demo.resolve("src/main/guibuilder") + "\n" + + "sourceDir=" + demo.resolve("src/main/java") + "\n" + + "cssFile=" + demo.resolve("src/main/css/theme.css") + "\n" + + "initialForm=com.example.NestedLayoutsForm\n").getBytes(StandardCharsets.UTF_8)); + input.toFile().deleteOnExit(); + return input; + } + + /** Runs queued EDT work to completion, including refreshes that queue further refreshes. */ + private static void flushEdt() { + for (int i = 0; i < 6; i++) { + if (Display.getInstance().isEdt()) return; + try { Thread.sleep(250); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } + Display.getInstance().callSeriallyAndWait(() -> { }); + } + } + + private static void assertOnePreviewPerComponent(CodenameOneGUIBuilder builder, String when) { + Container canvas = builder.canvasHostForTest(); + assertNotNull(canvas, "the workspace never built a canvas"); + for (Element element : builder.documentForTest().components()) { + int count = countPreviews(canvas, element, 0); + assertEquals(1, count, name(element) + " has " + count + " previews on the canvas " + when + + "; a stale preview shadows the live one for hit testing and geometry"); + } + } + + private static void assertPreviewsSitInsideTheirModelParent(CodenameOneGUIBuilder builder, String when) { + Container canvas = builder.canvasHostForTest(); + for (Element element : builder.documentForTest().components()) { + Element parent = builder.documentForTest().parentOf(element); + if (parent == null) continue; + Component preview = findPreview(canvas, element); + Component parentPreview = findPreview(canvas, parent); + if (preview == null || parentPreview == null) continue; + assertSame(parentPreview, enclosingPreview(preview), + name(element) + " renders inside " + name(elementOf(enclosingPreview(preview))) + + " but the model says " + name(parent) + " " + when); + } + } + + private static Component enclosingPreview(Component component) { + for (Container parent = component.getParent(); parent != null; parent = parent.getParent()) { + if (parent.getClientProperty("gui.element") instanceof Element) return parent; + } + return null; + } + + private static Element elementOf(Component component) { + Object element = component == null ? null : component.getClientProperty("gui.element"); + return element instanceof Element e ? e : null; + } + + private static int countPreviews(Container root, Element element, int found) { + for (int i = 0; i < root.getComponentCount(); i++) { + Component component = root.getComponentAt(i); + if (component.getClientProperty("gui.element") == element) found++; + if (component instanceof Container container) found = countPreviews(container, element, found); + } + return found; + } + + private static Component findPreview(Container root, Element element) { + for (int i = 0; i < root.getComponentCount(); i++) { + Component component = root.getComponentAt(i); + if (component.getClientProperty("gui.element") == element) return component; + if (component instanceof Container container) { + Component nested = findPreview(container, element); + if (nested != null) return nested; + } + } + return null; + } + + private static List hierarchy(CodenameOneGUIBuilder builder) { + List rows = new ArrayList<>(); + for (Element element : builder.documentForTest().components()) { + rows.add(parentName(builder, name(element)) + "/" + name(element)); + } + return rows; + } + + private static String parentName(CodenameOneGUIBuilder builder, String componentName) { + for (Element element : builder.documentForTest().components()) { + if (componentName.equals(name(element))) { + Element parent = builder.documentForTest().parentOf(element); + return parent == null ? "-" : name(parent); + } + } + return null; + } + + private static String name(Element element) { + if (element == null) return "?"; + String value = element.getAttribute("name"); + return value == null ? element.getAttribute("type") : value; + } +} From a745163cb429ab8e91a6443f32bf8b03c73fad22 Mon Sep 17 00:00:00 2001 From: Shai Almog <67850168+shai-almog@users.noreply.github.com> Date: Wed, 5 Aug 2026 16:40:10 +0700 Subject: [PATCH 03/33] Hold the whole tree to account after every nested move Nested containers are the weak spot: a move is a remove from one parent and an add to another, and anything going wrong between the two loses the component from the document entirely. The existing tests checked the component that moved, which is exactly the assertion that passes when a component ends up in neither container. NestedHierarchyTest re-checks the entire document after every single gesture: no component gained or lost, no duplicate names, every parent link agreeing with the child list it claims, the tree surviving a save/load round trip, and every component rendering exactly once at a non-zero size. It covers draining a container child by child, refilling an emptied one, moving populated containers, four-level nesting in both directions, refused cyclic drops, every layout as both source and destination, and undo/redo replaying a drain step by step. LiveWorkspaceDragTest gains the same accounting against the real canvas. Two defects it found: **Components added to a table had no cell.** The preview fell back to sibling order and the generated source fell back to cell (0, 0), so a table that looked correct in the designer compiled to every component stacked in one corner. GuiDocument now owns the rule -- effectiveTableRow/Column -- and both consumers use it, so they cannot drift apart again. New children are given an explicit free cell as they are added, growing the row count when they need it. **A drop aimed at a container it did not fit in landed elsewhere.** The MCP drag path derived the release point from the dragged component's own box and then added the grab offset, so dropping a full width button "into" a narrow column put the pointer past the column's right edge and the component went into the next one. Drops resolve from the pointer, so the pointer is what gets aimed: at the target's centre, or just outside the relevant edge for above/below/leftOf/rightOf, or inside the leading or trailing half for before/after. Co-Authored-By: Claude Opus 5 (1M context) --- .../guibuilder/CodenameOneGUIBuilder.java | 39 +- .../guibuilder/model/GuiDocument.java | 69 +++ .../ui/ComponentPreviewFactory.java | 7 +- .../guibuilder/LiveWorkspaceDragTest.java | 111 ++++ .../guibuilder/NestedHierarchyTest.java | 540 ++++++++++++++++++ 5 files changed, 745 insertions(+), 21 deletions(-) create mode 100644 scripts/guibuilder/javase/src/test/java/com/codename1/guibuilder/NestedHierarchyTest.java diff --git a/scripts/guibuilder/common/src/main/java/com/codename1/guibuilder/CodenameOneGUIBuilder.java b/scripts/guibuilder/common/src/main/java/com/codename1/guibuilder/CodenameOneGUIBuilder.java index 1c2e6ce73f5..13d6e042b95 100644 --- a/scripts/guibuilder/common/src/main/java/com/codename1/guibuilder/CodenameOneGUIBuilder.java +++ b/scripts/guibuilder/common/src/main/java/com/codename1/guibuilder/CodenameOneGUIBuilder.java @@ -4218,8 +4218,9 @@ private void appendGeneratedChildren(StringBuilder out, Element parent, String p .append(GuiDocument.effectiveBorderConstraint(parent, child).toUpperCase()).append(", ").append(name).append(");\n"); } else if ("TableLayout".equals(value(parent, "layout", "BoxLayout"))) { out.append(indent).append(parentName).append(".add(((TableLayout) ").append(parentName) - .append(".getLayout()).createConstraint(").append(value(child, "tableRow", "0")) - .append(", ").append(value(child, "tableColumn", "0")).append(")") + .append(".getLayout()).createConstraint(") + .append(GuiDocument.effectiveTableRow(parent, child)) + .append(", ").append(GuiDocument.effectiveTableColumn(parent, child)).append(")") .append(".horizontalSpan(").append(value(child, "tableHorizontalSpan", "1")).append(")") .append(".verticalSpan(").append(value(child, "tableVerticalSpan", "1")).append("), ") .append(name).append(");\n"); @@ -4579,34 +4580,36 @@ String mcpDragComponent(String componentName, String targetName, String placemen Component targetPreview = componentForElement(canvasHost, target); if (targetPreview == null) return "Target " + targetName + " is not visible"; String where = placement == null || placement.length() == 0 ? "center" : placement; - int grabX = source.getWidth() / 2; - int grabY = source.getHeight() / 2; int gap = standardGap(); - int desiredX = targetPreview.getAbsoluteX() + Math.max(0, (targetPreview.getWidth() - source.getWidth()) / 2); - int desiredY = targetPreview.getAbsoluteY() + Math.max(0, (targetPreview.getHeight() - source.getHeight()) / 2); - if ("above".equals(where)) desiredY = targetPreview.getAbsoluteY() - source.getHeight() - gap; - else if ("below".equals(where)) desiredY = targetPreview.getAbsoluteY() + targetPreview.getHeight() + gap; - else if ("leftOf".equals(where)) desiredX = targetPreview.getAbsoluteX() - source.getWidth() - gap; - else if ("rightOf".equals(where)) desiredX = targetPreview.getAbsoluteX() + targetPreview.getWidth() + gap; + // Aim the POINTER, not the dragged component's top-left corner. A drop is resolved from + // the pointer position, so deriving the release point from the component's own box sent + // the pointer outside the target whenever the component was larger than it -- dropping + // a full width button "into" a narrow column landed it in the next column instead. + int centreX = targetPreview.getAbsoluteX() + targetPreview.getWidth() / 2; + int centreY = targetPreview.getAbsoluteY() + targetPreview.getHeight() / 2; + releaseX = centreX; + releaseY = centreY; + if ("above".equals(where)) releaseY = targetPreview.getAbsoluteY() - gap; + else if ("below".equals(where)) releaseY = targetPreview.getAbsoluteY() + targetPreview.getHeight() + gap; + else if ("leftOf".equals(where)) releaseX = targetPreview.getAbsoluteX() - gap; + else if ("rightOf".equals(where)) releaseX = targetPreview.getAbsoluteX() + targetPreview.getWidth() + gap; else if ("before".equals(where) || "after".equals(where)) { + // Stay inside the target: before/after is an insertion next to it in its own + // parent, and dropAfter() decides the side from which half of it the pointer is in. Element parent = document.parentOf(target); boolean horizontal = parent != null && ("X".equals(value(parent, "boxLayoutAxis", "Y")) || "FlowLayout".equals(value(parent, "layout", "BoxLayout")) || "GridLayout".equals(value(parent, "layout", "BoxLayout"))); if (horizontal) { - desiredX = "after".equals(where) ? targetPreview.getAbsoluteX() + targetPreview.getWidth() - 1 - : targetPreview.getAbsoluteX(); - desiredY = targetPreview.getAbsoluteY(); + releaseX = "after".equals(where) ? targetPreview.getAbsoluteX() + targetPreview.getWidth() - 1 + : targetPreview.getAbsoluteX() + 1; } else { - desiredX = targetPreview.getAbsoluteX(); - desiredY = "after".equals(where) ? targetPreview.getAbsoluteY() + targetPreview.getHeight() - 1 - : targetPreview.getAbsoluteY(); + releaseY = "after".equals(where) ? targetPreview.getAbsoluteY() + targetPreview.getHeight() - 1 + : targetPreview.getAbsoluteY() + 1; } } else if (!"center".equals(where)) { return "Unknown placement " + where; } - releaseX = desiredX + grabX; - releaseY = desiredY + grabY; } String before = document.toXml(); handleDesignerPointerPressed(startX, startY); diff --git a/scripts/guibuilder/common/src/main/java/com/codename1/guibuilder/model/GuiDocument.java b/scripts/guibuilder/common/src/main/java/com/codename1/guibuilder/model/GuiDocument.java index 2f561896e95..46d081c5595 100644 --- a/scripts/guibuilder/common/src/main/java/com/codename1/guibuilder/model/GuiDocument.java +++ b/scripts/guibuilder/common/src/main/java/com/codename1/guibuilder/model/GuiDocument.java @@ -188,11 +188,80 @@ public Element addComponent(String type) { child.setAttribute("name", uniqueName(type)); if (acceptsChildren(child)) child.setAttribute("layout", "LayeredLayout"); parent.addChild(child); + assignFreeTableCell(parent, child); selected = child; modified = true; return child; } + /** + * Gives a new child of a table an explicit free cell. + * + *

Without this a component added from the palette or the menu carries no cell at all, and + * every consumer has to invent one. They did not agree: the preview fell back to sibling order + * while the generated source fell back to cell (0, 0), so a table that looked right in the + * designer compiled to every component stacked in one corner. + */ + private static void assignFreeTableCell(Element parent, Element child) { + if (!"TableLayout".equals(parent.getAttribute("layout"))) return; + int columns = tableColumns(parent); + Set taken = new LinkedHashSet<>(); + for (Element sibling : componentsIn(parent)) { + if (sibling == child) continue; + taken.add(effectiveTableRow(parent, sibling) + ":" + effectiveTableColumn(parent, sibling)); + } + for (int cursor = 0; ; cursor++) { + int row = cursor / columns; + int column = cursor % columns; + if (taken.contains(row + ":" + column)) continue; + setNormalizedAttribute(child, "tableRow", String.valueOf(row)); + setNormalizedAttribute(child, "tableColumn", String.valueOf(column)); + int declaredRows = parseInt(parent.getAttribute("tableLayoutRows"), 2); + if (declaredRows < row + 1) { + setNormalizedAttribute(parent, "tableLayoutRows", String.valueOf(row + 1)); + } + return; + } + } + + /** The declared column count of a table, never below one. */ + public static int tableColumns(Element parent) { + return Math.max(1, parseInt(parent == null ? null : parent.getAttribute("tableLayoutColumns"), 2)); + } + + /** + * The row a table child occupies. Components loaded from a hand written .gui file may carry no + * cell; they fall back to sibling order so that the preview, the generated source and the + * inspector all place them identically. + */ + public static int effectiveTableRow(Element parent, Element child) { + Integer declared = parseIntOrNull(child == null ? null : child.getAttribute("tableRow")); + return declared != null ? Math.max(0, declared.intValue()) + : componentsIn(parent).indexOf(child) / tableColumns(parent); + } + + /** The column a table child occupies; see {@link #effectiveTableRow}. */ + public static int effectiveTableColumn(Element parent, Element child) { + Integer declared = parseIntOrNull(child == null ? null : child.getAttribute("tableColumn")); + int columns = tableColumns(parent); + return declared != null ? Math.max(0, Math.min(columns - 1, declared.intValue())) + : componentsIn(parent).indexOf(child) % columns; + } + + private static int parseInt(String value, int fallback) { + Integer parsed = parseIntOrNull(value); + return parsed == null ? fallback : parsed.intValue(); + } + + private static Integer parseIntOrNull(String value) { + if (value == null || value.trim().length() == 0) return null; + try { + return Integer.valueOf(value.trim()); + } catch (NumberFormatException ex) { + return null; + } + } + public boolean deleteSelected() { if (selected == root) return false; Element parent = findParent(root, selected); diff --git a/scripts/guibuilder/common/src/main/java/com/codename1/guibuilder/ui/ComponentPreviewFactory.java b/scripts/guibuilder/common/src/main/java/com/codename1/guibuilder/ui/ComponentPreviewFactory.java index 6a8ee8813e9..f05f82c01d3 100644 --- a/scripts/guibuilder/common/src/main/java/com/codename1/guibuilder/ui/ComponentPreviewFactory.java +++ b/scripts/guibuilder/common/src/main/java/com/codename1/guibuilder/ui/ComponentPreviewFactory.java @@ -159,9 +159,10 @@ private static Component sizedContainer(Element element, Element selected, Selec if (out.getLayout() instanceof BorderLayout) { out.add(GuiDocument.effectiveBorderConstraint(element, childElement), rendered); } else if (out.getLayout() instanceof TableLayout table) { - int columns = integer(element, "tableLayoutColumns", 2); - int row = integer(childElement, "tableRow", i / Math.max(1, columns)); - int column = integer(childElement, "tableColumn", i % Math.max(1, columns)); + // GuiDocument owns the cell rule so the preview and the generated source cannot + // disagree about where a component with no explicit cell belongs. + int row = GuiDocument.effectiveTableRow(element, childElement); + int column = GuiDocument.effectiveTableColumn(element, childElement); TableLayout.Constraint constraint = table.createConstraint(row, column) .horizontalSpan(integer(childElement, "tableHorizontalSpan", 1)) .verticalSpan(integer(childElement, "tableVerticalSpan", 1)); diff --git a/scripts/guibuilder/javase/src/test/java/com/codename1/guibuilder/LiveWorkspaceDragTest.java b/scripts/guibuilder/javase/src/test/java/com/codename1/guibuilder/LiveWorkspaceDragTest.java index 84547f12857..7ffe765ea3b 100644 --- a/scripts/guibuilder/javase/src/test/java/com/codename1/guibuilder/LiveWorkspaceDragTest.java +++ b/scripts/guibuilder/javase/src/test/java/com/codename1/guibuilder/LiveWorkspaceDragTest.java @@ -57,6 +57,72 @@ void repeatedCrossContainerDragsStayStable() throws Exception { } } + @Test + void drainingAContainerCompletelyKeepsEveryComponent() throws Exception { + CodenameOneGUIBuilder builder = workspace(); + assertNull(onEdt(() -> builder.mcpOpenForm("com.example.NestedLayoutsForm"))); + flushEdt(); + List everything = componentNames(builder); + assertEquals(11, everything.size(), everything.toString()); + + for (String name : new String[]{"nestedA", "nestedB", "nestedC", "nestedD"}) { + assertNull(onEdt(() -> builder.mcpDragComponent(name, "nestedAction", "below", null, null)), + "dragging " + name + " reported an error"); + flushEdt(); + assertEquals(everything, componentNames(builder), + name + " left the document instead of moving; it is in neither container"); + assertEquals("rightActions", parentName(builder, name), hierarchy(builder).toString()); + assertOnePreviewPerComponent(builder, "after moving " + name); + assertPreviewsSitInsideTheirModelParent(builder, "after moving " + name); + assertEveryComponentIsActuallyVisible(builder, "after moving " + name); + } + assertEquals(0, childCount(builder, "leftGrid"), hierarchy(builder).toString()); + assertEquals(6, childCount(builder, "rightActions"), hierarchy(builder).toString()); + } + + @Test + void anEmptiedContainerCanBeFilledAgainByDragging() throws Exception { + CodenameOneGUIBuilder builder = workspace(); + assertNull(onEdt(() -> builder.mcpOpenForm("com.example.NestedLayoutsForm"))); + flushEdt(); + List everything = componentNames(builder); + + for (String name : new String[]{"nestedA", "nestedB", "nestedC", "nestedD"}) { + assertNull(onEdt(() -> builder.mcpDragComponent(name, "nestedAction", "below", null, null))); + flushEdt(); + } + assertEquals(0, childCount(builder, "leftGrid")); + + // An emptied container renders only its "drop components here" hint. Dropping onto that + // hint has to resolve to the container, or the components have nowhere to go back to. + for (String name : new String[]{"nestedA", "nestedB"}) { + assertNull(onEdt(() -> builder.mcpDragComponent(name, "leftGrid", "center", null, null)), + "the emptied container refused " + name); + flushEdt(); + assertEquals(everything, componentNames(builder), name + " was lost on the way back"); + assertEquals("leftGrid", parentName(builder, name), hierarchy(builder).toString()); + assertOnePreviewPerComponent(builder, "after returning " + name); + } + } + + @Test + void movingAPopulatedContainerThroughTheCanvasKeepsItsChildren() throws Exception { + CodenameOneGUIBuilder builder = workspace(); + assertNull(onEdt(() -> builder.mcpOpenForm("com.example.NestedLayoutsForm"))); + flushEdt(); + List everything = componentNames(builder); + + assertNull(onEdt(() -> builder.mcpDragComponent("leftGrid", "nestedAction", "below", null, null)), + "dragging a populated container reported an error"); + flushEdt(); + + assertEquals(everything, componentNames(builder), "moving a container lost part of its subtree"); + assertEquals("rightActions", parentName(builder, "leftGrid"), hierarchy(builder).toString()); + assertEquals(4, childCount(builder, "leftGrid"), hierarchy(builder).toString()); + assertOnePreviewPerComponent(builder, "after moving a populated container"); + assertPreviewsSitInsideTheirModelParent(builder, "after moving a populated container"); + } + @Test void undoAfterACrossContainerDragRestoresBothTheModelAndTheCanvas() throws Exception { CodenameOneGUIBuilder builder = workspace(); @@ -195,6 +261,51 @@ private static Component findPreview(Container root, Element element) { return null; } + /** + * A component that renders at zero size, or entirely outside the form it belongs to, has + * effectively vanished from the designer even though the model still lists it -- which is what + * "they are gone from both containers" looks like from the canvas. + */ + private static void assertEveryComponentIsActuallyVisible(CodenameOneGUIBuilder builder, String when) { + Container canvas = builder.canvasHostForTest(); + Component form = findPreview(canvas, builder.documentForTest().root()); + assertNotNull(form, "the form itself does not render " + when); + for (Element element : builder.documentForTest().components()) { + if (element == builder.documentForTest().root()) continue; + Component preview = findPreview(canvas, element); + assertNotNull(preview, name(element) + " has no preview " + when); + assertTrue(preview.getWidth() > 0 && preview.getHeight() > 0, + name(element) + " renders at " + preview.getWidth() + "x" + preview.getHeight() + + " " + when + "; it is invisible on the canvas"); + boolean insideHorizontally = preview.getAbsoluteX() + preview.getWidth() > form.getAbsoluteX() + && preview.getAbsoluteX() < form.getAbsoluteX() + form.getWidth(); + boolean insideVertically = preview.getAbsoluteY() + preview.getHeight() > form.getAbsoluteY() + && preview.getAbsoluteY() < form.getAbsoluteY() + form.getHeight(); + assertTrue(insideHorizontally && insideVertically, + name(element) + " renders outside the form " + when + "; component at " + + preview.getAbsoluteX() + "," + preview.getAbsoluteY() + + " " + preview.getWidth() + "x" + preview.getHeight() + + ", form at " + form.getAbsoluteX() + "," + form.getAbsoluteY() + + " " + form.getWidth() + "x" + form.getHeight()); + } + } + + private static List componentNames(CodenameOneGUIBuilder builder) { + List names = new ArrayList<>(); + for (Element element : builder.documentForTest().components()) names.add(name(element)); + java.util.Collections.sort(names); + return names; + } + + private static int childCount(CodenameOneGUIBuilder builder, String containerName) { + for (Element element : builder.documentForTest().components()) { + if (containerName.equals(name(element))) { + return com.codename1.guibuilder.model.GuiDocument.componentsIn(element).size(); + } + } + return -1; + } + private static List hierarchy(CodenameOneGUIBuilder builder) { List rows = new ArrayList<>(); for (Element element : builder.documentForTest().components()) { diff --git a/scripts/guibuilder/javase/src/test/java/com/codename1/guibuilder/NestedHierarchyTest.java b/scripts/guibuilder/javase/src/test/java/com/codename1/guibuilder/NestedHierarchyTest.java new file mode 100644 index 00000000000..3a510a6f2e4 --- /dev/null +++ b/scripts/guibuilder/javase/src/test/java/com/codename1/guibuilder/NestedHierarchyTest.java @@ -0,0 +1,540 @@ +package com.codename1.guibuilder; + +import com.codename1.guibuilder.model.GuiDocument; +import com.codename1.guibuilder.ui.ComponentPreviewFactory; +import com.codename1.ui.Component; +import com.codename1.ui.Container; +import com.codename1.ui.layouts.LayeredLayout; +import com.codename1.xml.Element; +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Set; +import javax.swing.JPanel; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +/** + * Nested containers are where the designer is weakest: a move is a remove from one parent and an + * add to another, and anything that fails between the two loses the component from the document + * entirely. These tests hold the whole tree to account after every single gesture rather than + * checking only the component that moved, because the symptom of the interesting bugs is a + * component that is in neither its old parent nor its new one. + */ +class NestedHierarchyTest { + @BeforeAll + static void initializeCodenameOneRuntime() { + if (!com.codename1.ui.Display.isInitialized()) com.codename1.ui.Display.init(new JPanel()); + } + + // ---- draining a container completely -------------------------------------------------- + + @Test + void movingEveryChildOutOfAContainerLosesNone() throws Exception { + GuiDocument document = threeLevelDocument(); + CodenameOneGUIBuilder builder = builder(document); + Set everything = names(document); + + for (String child : List.of("nestedA", "nestedB", "nestedC", "nestedD")) { + Element moved = named(document, child); + Element destination = named(document, "rightActions"); + assertTrue(builder.applyDropPlan(moved, plan(document, destination, destination, "BoxLayout", true), 0, 0), + "moving " + child + " out of the grid was rejected"); + assertHierarchyIsSound(document, everything, "after moving " + child); + assertSame(destination, document.parentOf(moved), + child + " did not land in the destination: " + document.toXml()); + } + assertEquals(0, GuiDocument.componentsIn(named(document, "leftGrid")).size(), + "the source container should now be empty: " + document.toXml()); + assertEquals(6, GuiDocument.componentsIn(named(document, "rightActions")).size(), + "every moved component must be in the destination: " + document.toXml()); + } + + @Test + void anEmptiedContainerStillAcceptsComponentsBack() throws Exception { + GuiDocument document = threeLevelDocument(); + CodenameOneGUIBuilder builder = builder(document); + Set everything = names(document); + Element grid = named(document, "leftGrid"); + Element rightActions = named(document, "rightActions"); + + for (String child : List.of("nestedA", "nestedB", "nestedC", "nestedD")) { + assertTrue(builder.applyDropPlan(named(document, child), + plan(document, rightActions, rightActions, "BoxLayout", true), 0, 0)); + } + assertEquals(0, GuiDocument.componentsIn(grid).size()); + + // Dropping onto a container that renders only its empty-state hint must still target the + // container, not fall through to whatever is behind it. + for (String child : List.of("nestedA", "nestedB")) { + assertTrue(builder.applyDropPlan(named(document, child), + plan(document, grid, grid, "GridLayout", true), 0, 0), + "the emptied container refused " + child); + assertHierarchyIsSound(document, everything, "after returning " + child); + } + assertEquals(List.of("nestedA", "nestedB"), childNames(grid), document.toXml()); + } + + @Test + void drainingAContainerThenItsParentKeepsEveryComponent() throws Exception { + GuiDocument document = threeLevelDocument(); + CodenameOneGUIBuilder builder = builder(document); + Set everything = names(document); + Element root = document.root(); + + for (String child : List.of("nestedA", "nestedB", "nestedC", "nestedD")) { + assertTrue(builder.applyDropPlan(named(document, child), + plan(document, named(document, "rightActions"), named(document, "rightActions"), "BoxLayout", true), 0, 0)); + assertHierarchyIsSound(document, everything, "after draining " + child); + } + // Now move the emptied container itself up to the form. + assertTrue(builder.applyDropPlan(named(document, "leftGrid"), + plan(document, root, root, "BorderLayout", true), 0, 0)); + assertHierarchyIsSound(document, everything, "after moving the emptied container to the form"); + assertSame(root, document.parentOf(named(document, "leftGrid")), document.toXml()); + } + + // ---- moving containers, not just leaves ------------------------------------------------ + + @Test + void movingAContainerCarriesItsWholeSubtree() throws Exception { + GuiDocument document = threeLevelDocument(); + CodenameOneGUIBuilder builder = builder(document); + Set everything = names(document); + Element grid = named(document, "leftGrid"); + Element rightActions = named(document, "rightActions"); + + assertTrue(builder.applyDropPlan(grid, plan(document, rightActions, rightActions, "BoxLayout", true), 0, 0)); + + assertHierarchyIsSound(document, everything, "after moving a populated container"); + assertSame(rightActions, document.parentOf(grid), document.toXml()); + assertEquals(List.of("nestedA", "nestedB", "nestedC", "nestedD"), childNames(grid), + "the subtree must travel intact: " + document.toXml()); + } + + @Test + void aContainerCannotBeDroppedIntoItsOwnDescendant() throws Exception { + GuiDocument document = threeLevelDocument(); + CodenameOneGUIBuilder builder = builder(document); + Set everything = names(document); + Element columns = named(document, "contentColumns"); + Element grid = named(document, "leftGrid"); + String before = document.toXml(); + + builder.applyDropPlan(columns, plan(document, grid, grid, "GridLayout", true), 0, 0); + + assertEquals(before, document.toXml(), "a cycle must be refused without touching the document"); + assertHierarchyIsSound(document, everything, "after a refused cyclic drop"); + } + + @Test + void deeplyNestedComponentsSurviveAMoveToTheOutermostForm() throws Exception { + GuiDocument document = fourLevelDocument(); + CodenameOneGUIBuilder builder = builder(document); + Set everything = names(document); + Element root = document.root(); + + assertTrue(builder.applyDropPlan(named(document, "deepLeaf"), + plan(document, root, root, "BorderLayout", true), 0, 0)); + + assertHierarchyIsSound(document, everything, "after hoisting a leaf from four levels down"); + assertSame(root, document.parentOf(named(document, "deepLeaf")), document.toXml()); + } + + @Test + void aComponentCanTravelDownIntoTheDeepestContainer() throws Exception { + GuiDocument document = fourLevelDocument(); + CodenameOneGUIBuilder builder = builder(document); + Set everything = names(document); + Element deepest = named(document, "levelThree"); + + assertTrue(builder.applyDropPlan(named(document, "topLevelButton"), + plan(document, deepest, deepest, "BoxLayout", true), 0, 0)); + + assertHierarchyIsSound(document, everything, "after pushing a component four levels down"); + assertSame(deepest, document.parentOf(named(document, "topLevelButton")), document.toXml()); + } + + // ---- every parent layout ---------------------------------------------------------------- + + @Test + void everyParentLayoutAcceptsAComponentFromAnotherContainer() throws Exception { + for (String layout : List.of("BoxLayout", "BorderLayout", "GridLayout", "FlowLayout", "TableLayout", "LayeredLayout")) { + GuiDocument document = documentWithDestinationLayout(layout); + CodenameOneGUIBuilder builder = builder(document); + Set everything = names(document); + Element destination = named(document, "destination"); + Element traveller = named(document, "traveller"); + + assertTrue(builder.applyDropPlan(traveller, plan(document, destination, destination, layout, true), 0, 0), + layout + " refused an incoming component"); + assertHierarchyIsSound(document, everything, "after a drop into " + layout); + assertSame(destination, document.parentOf(traveller), + layout + " did not adopt the component: " + document.toXml()); + assertRendersFaithfully(document, "after a drop into " + layout); + } + } + + @Test + void everySourceLayoutReleasesItsLastChild() throws Exception { + for (String layout : List.of("BoxLayout", "BorderLayout", "GridLayout", "FlowLayout", "TableLayout", "LayeredLayout")) { + GuiDocument document = documentWithSourceLayout(layout); + CodenameOneGUIBuilder builder = builder(document); + Set everything = names(document); + Element destination = named(document, "destination"); + + assertTrue(builder.applyDropPlan(named(document, "traveller"), + plan(document, destination, destination, "BoxLayout", true), 0, 0), + layout + " would not release its only child"); + assertHierarchyIsSound(document, everything, "after emptying a " + layout); + assertEquals(0, GuiDocument.componentsIn(named(document, "source")).size(), + layout + " kept a copy of the component it released: " + document.toXml()); + } + } + + // ---- filling a container past what its layout can show -------------------------------------- + + @Test + void componentsPiledIntoABorderLayoutNeverShareARegion() throws Exception { + GuiDocument document = document("\n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + ""); + CodenameOneGUIBuilder builder = builder(document); + Set everything = names(document); + Element destination = named(document, "destination"); + + for (String child : List.of("one", "two", "three")) { + CodenameOneGUIBuilder.DropPlan plan = plan(document, destination, destination, "BorderLayout", true); + plan.constraint = "Center"; + plan.occupied = GuiDocument.childAtBorderConstraint(destination, "Center", named(document, child)); + builder.applyDropPlan(named(document, child), plan, 0, 0); + assertHierarchyIsSound(document, everything, "after piling " + child + " into Center"); + } + assertNoSharedBorderRegion(destination, document); + assertRendersFaithfully(document, "after piling components into one BorderLayout region"); + } + + @Test + void aGridFilledPastItsDeclaredCellsStillShowsEveryChild() throws Exception { + GuiDocument document = documentWithDestinationLayout("GridLayout"); + CodenameOneGUIBuilder builder = builder(document); + Element destination = named(document, "destination"); + Set everything = names(document); + + for (int i = 0; i < 6; i++) { + document.select(destination); + Element added = document.addComponent("Button"); + assertNotNull(added, "the grid refused an extra child"); + everything.add(added.getAttribute("name")); + } + assertHierarchyIsSound(document, everything, "after overfilling a 2x2 grid"); + assertRendersFaithfully(document, "after overfilling a 2x2 grid"); + } + + @Test + void aTableFilledPastItsDeclaredCellsStillShowsEveryChild() throws Exception { + GuiDocument document = documentWithDestinationLayout("TableLayout"); + CodenameOneGUIBuilder builder = builder(document); + Element destination = named(document, "destination"); + Set everything = names(document); + + for (int i = 0; i < 6; i++) { + document.select(destination); + Element added = document.addComponent("Button"); + assertNotNull(added, "the table refused an extra child"); + everything.add(added.getAttribute("name")); + } + assertHierarchyIsSound(document, everything, "after overfilling a 2x2 table"); + assertNoSharedTableCell(destination, document); + assertRendersFaithfully(document, "after overfilling a 2x2 table"); + } + + private static void assertNoSharedBorderRegion(Element parent, GuiDocument document) { + Set used = new LinkedHashSet<>(); + for (Element child : GuiDocument.componentsIn(parent)) { + String constraint = GuiDocument.effectiveBorderConstraint(parent, child); + assertTrue(used.add(constraint), + "two components share the " + constraint + " region, so only one of them is ever" + + " visible:\n" + document.toXml()); + } + } + + private static void assertNoSharedTableCell(Element parent, GuiDocument document) { + Set used = new LinkedHashSet<>(); + for (Element child : GuiDocument.componentsIn(parent)) { + String cell = GuiDocument.effectiveTableRow(parent, child) + ":" + + GuiDocument.effectiveTableColumn(parent, child); + assertTrue(used.add(cell), + "two components share cell " + cell + ", so only one of them is ever visible:\n" + + document.toXml()); + } + } + + // ---- undo across nesting ------------------------------------------------------------------ + + @Test + void undoRestoresEveryStepOfADrainInReverse() throws Exception { + GuiDocument document = threeLevelDocument(); + CodenameOneGUIBuilder builder = builder(document); + Set everything = names(document); + List snapshots = new ArrayList<>(); + + for (String child : List.of("nestedA", "nestedB", "nestedC", "nestedD")) { + snapshots.add(document.toXml()); + assertTrue(builder.applyDropPlan(named(document, child), + plan(document, named(document, "rightActions"), named(document, "rightActions"), "BoxLayout", true), 0, 0)); + } + for (int i = snapshots.size() - 1; i >= 0; i--) { + assertTrue(document.undo(), "undo ran out of history at step " + i); + assertEquals(snapshots.get(i), document.toXml(), "undo did not restore step " + i); + assertHierarchyIsSound(document, everything, "after undoing step " + i); + } + } + + @Test + void redoReplaysADrainExactly() throws Exception { + GuiDocument document = threeLevelDocument(); + CodenameOneGUIBuilder builder = builder(document); + Set everything = names(document); + + for (String child : List.of("nestedA", "nestedB")) { + assertTrue(builder.applyDropPlan(named(document, child), + plan(document, named(document, "rightActions"), named(document, "rightActions"), "BoxLayout", true), 0, 0)); + } + String drained = document.toXml(); + assertTrue(document.undo()); + assertTrue(document.undo()); + assertTrue(document.redo()); + assertTrue(document.redo()); + + assertEquals(drained, document.toXml(), "redo must reproduce the drained tree exactly"); + assertHierarchyIsSound(document, everything, "after redoing a drain"); + } + + // ---- invariants --------------------------------------------------------------------------- + + /** + * A move is only correct if the whole document is still coherent afterwards. Checks that no + * component was lost or duplicated, that parent links agree with the child lists, and that the + * tree survives a serialize/parse round trip -- the form is written to disk in exactly that way. + */ + private static void assertHierarchyIsSound(GuiDocument document, Set expected, String when) { + List all = document.components(); + List found = new ArrayList<>(); + for (Element element : all) found.add(element.getAttribute("name")); + + Set unique = new LinkedHashSet<>(found); + assertEquals(found.size(), unique.size(), + "duplicate component names " + when + ": " + found + "\n" + document.toXml()); + assertEquals(expected, unique, + "the set of components changed " + when + "; something was lost or invented\n" + document.toXml()); + + for (Element element : all) { + if (element == document.root()) continue; + Element parent = document.parentOf(element); + assertNotNull(parent, element.getAttribute("name") + " has no parent " + when + + "; it is detached from the tree\n" + document.toXml()); + assertTrue(GuiDocument.componentsIn(parent).contains(element), + element.getAttribute("name") + " is not listed by the parent that claims it " + when + + "\n" + document.toXml()); + } + + GuiDocument reparsed = GuiDocument.parse("Form.gui", document.toXml()); + assertEquals(structure(document), structure(reparsed), + "the tree does not survive a save/load round trip " + when + "\n" + document.toXml()); + } + + /** + * Every component must render exactly once and occupy real space. A component the model still + * lists but that draws at zero size has disappeared as far as anyone using the editor is + * concerned -- two components sharing one BorderLayout region behave exactly like that. + */ + private static void assertRendersFaithfully(GuiDocument document, String when) { + Container rendered = (Container) render(document, 600, 800); + for (Element element : document.components()) { + if (element == document.root()) continue; + assertEquals(1, countPreviews(rendered, element, 0), + element.getAttribute("name") + " does not have exactly one preview " + when + + "\n" + document.toXml()); + Component preview = findPreview(rendered, element); + assertNotNull(preview, element.getAttribute("name") + " has no preview " + when); + assertTrue(preview.getWidth() > 0 && preview.getHeight() > 0, + element.getAttribute("name") + " renders at " + preview.getWidth() + "x" + + preview.getHeight() + " " + when + "; it is invisible in the designer\n" + + document.toXml()); + } + } + + private static Component findPreview(Container root, Element element) { + for (int i = 0; i < root.getComponentCount(); i++) { + Component component = root.getComponentAt(i); + if (component.getClientProperty("gui.element") == element) return component; + if (component instanceof Container container) { + Component nested = findPreview(container, element); + if (nested != null) return nested; + } + } + return null; + } + + private static List structure(GuiDocument document) { + List rows = new ArrayList<>(); + for (Element element : document.components()) { + Element parent = document.parentOf(element); + rows.add((parent == null ? "-" : parent.getAttribute("name")) + "/" + element.getAttribute("name")); + } + return rows; + } + + // ---- fixtures ----------------------------------------------------------------------------- + + private static GuiDocument threeLevelDocument() { + return document("\n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + ""); + } + + private static GuiDocument fourLevelDocument() { + return document("\n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + ""); + } + + private static GuiDocument documentWithDestinationLayout(String layout) { + return document("\n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + ""); + } + + private static GuiDocument documentWithSourceLayout(String layout) { + return document("\n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + ""); + } + + // ---- helpers ------------------------------------------------------------------------------ + + private static CodenameOneGUIBuilder.DropPlan plan(GuiDocument document, Element parent, Element target, + String layout, boolean after) { + CodenameOneGUIBuilder.DropPlan plan = new CodenameOneGUIBuilder.DropPlan(); + plan.document = document; + plan.parent = parent; + plan.target = target; + plan.layout = layout; + plan.after = after; + plan.valid = true; + if ("TableLayout".equals(layout)) plan.tableCell = new int[]{0, 0}; + return plan; + } + + private static Set names(GuiDocument document) { + Set out = new LinkedHashSet<>(); + for (Element element : document.components()) out.add(element.getAttribute("name")); + return out; + } + + private static List childNames(Element parent) { + List out = new ArrayList<>(); + for (Element child : GuiDocument.componentsIn(parent)) out.add(child.getAttribute("name")); + return out; + } + + private static Element named(GuiDocument document, String name) { + for (Element element : document.components()) { + if (name.equals(element.getAttribute("name"))) return element; + } + return null; + } + + private static int countPreviews(Container root, Element element, int found) { + for (int i = 0; i < root.getComponentCount(); i++) { + Component component = root.getComponentAt(i); + if (component.getClientProperty("gui.element") == element) found++; + if (component instanceof Container container) found = countPreviews(container, element, found); + } + return found; + } + + private static Component render(GuiDocument document, int width, int height) { + Component rendered = ComponentPreviewFactory.create(document.root(), null, + new ComponentPreviewFactory.SelectionHandler() { + public void selected(Element element) { } + public void dragPressed(Element element, Component source, int x, int y) { } + public boolean isDragActive() { return false; } + public void editContent(Element element) { } + }); + rendered.setWidth(width); + rendered.setHeight(height); + layoutNested((Container) rendered); + return rendered; + } + + private static void layoutNested(Container container) { + container.layoutContainer(); + for (int i = 0; i < container.getComponentCount(); i++) { + if (container.getComponentAt(i) instanceof Container nested) layoutNested(nested); + } + } + + private static GuiDocument document(String xml) { + return GuiDocument.parse("Form.gui", xml); + } + + private static CodenameOneGUIBuilder builder(GuiDocument document) throws Exception { + CodenameOneGUIBuilder builder = new CodenameOneGUIBuilder(); + Field field = CodenameOneGUIBuilder.class.getDeclaredField("document"); + field.setAccessible(true); + field.set(builder, document); + Container canvas = new Container(new LayeredLayout()); + canvas.setWidth(600); + canvas.setHeight(800); + canvas.add(render(document, 600, 800)); + layoutNested(canvas); + Field canvasField = CodenameOneGUIBuilder.class.getDeclaredField("canvasHost"); + canvasField.setAccessible(true); + canvasField.set(builder, canvas); + return builder; + } +} From 83742fbb96bbc3b4ca371290b290b01cf69929d9 Mon Sep 17 00:00:00 2001 From: Shai Almog <67850168+shai-almog@users.noreply.github.com> Date: Wed, 5 Aug 2026 20:47:04 +0700 Subject: [PATCH 04/33] Add demo forms covering the nesting cases that keep breaking The demo project only exercised one nested form, and it was the one that failed. These cover the shapes the recent defects came from: four levels of containers, every layout nested inside every other, a column meant to be drained and refilled, an already-empty container, a single-child grid, tables inside tables, and a LayeredLayout container nested inside ordinary ones with ordinary containers nested back inside it. DemoFormsTest keeps them honest. A form that parses but renders nothing is a broken first impression for anyone evaluating the editor, so every form is checked for unique names, a coherent tree, every component rendering at a non-zero size, and a stable save/load round trip. Co-Authored-By: Claude Opus 5 (1M context) --- .../com/example/DeepNestingForm.gui | 20 +++ .../com/example/DrainAndRefillForm.gui | 16 +++ .../com/example/MixedNestedLayoutsForm.gui | 25 ++++ .../com/example/NestedGuidedForm.gui | 18 +++ .../com/example/NestedTableForm.gui | 15 ++ .../codename1/guibuilder/DemoFormsTest.java | 129 ++++++++++++++++++ 6 files changed, 223 insertions(+) create mode 100644 scripts/guibuilder/demo-project/src/main/guibuilder/com/example/DeepNestingForm.gui create mode 100644 scripts/guibuilder/demo-project/src/main/guibuilder/com/example/DrainAndRefillForm.gui create mode 100644 scripts/guibuilder/demo-project/src/main/guibuilder/com/example/MixedNestedLayoutsForm.gui create mode 100644 scripts/guibuilder/demo-project/src/main/guibuilder/com/example/NestedGuidedForm.gui create mode 100644 scripts/guibuilder/demo-project/src/main/guibuilder/com/example/NestedTableForm.gui create mode 100644 scripts/guibuilder/javase/src/test/java/com/codename1/guibuilder/DemoFormsTest.java diff --git a/scripts/guibuilder/demo-project/src/main/guibuilder/com/example/DeepNestingForm.gui b/scripts/guibuilder/demo-project/src/main/guibuilder/com/example/DeepNestingForm.gui new file mode 100644 index 00000000000..091cdd4c05c --- /dev/null +++ b/scripts/guibuilder/demo-project/src/main/guibuilder/com/example/DeepNestingForm.gui @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/scripts/guibuilder/demo-project/src/main/guibuilder/com/example/DrainAndRefillForm.gui b/scripts/guibuilder/demo-project/src/main/guibuilder/com/example/DrainAndRefillForm.gui new file mode 100644 index 00000000000..3af20ca8f6b --- /dev/null +++ b/scripts/guibuilder/demo-project/src/main/guibuilder/com/example/DrainAndRefillForm.gui @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/scripts/guibuilder/demo-project/src/main/guibuilder/com/example/MixedNestedLayoutsForm.gui b/scripts/guibuilder/demo-project/src/main/guibuilder/com/example/MixedNestedLayoutsForm.gui new file mode 100644 index 00000000000..3433827b3f3 --- /dev/null +++ b/scripts/guibuilder/demo-project/src/main/guibuilder/com/example/MixedNestedLayoutsForm.gui @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/scripts/guibuilder/demo-project/src/main/guibuilder/com/example/NestedGuidedForm.gui b/scripts/guibuilder/demo-project/src/main/guibuilder/com/example/NestedGuidedForm.gui new file mode 100644 index 00000000000..8de0f1b9457 --- /dev/null +++ b/scripts/guibuilder/demo-project/src/main/guibuilder/com/example/NestedGuidedForm.gui @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/scripts/guibuilder/demo-project/src/main/guibuilder/com/example/NestedTableForm.gui b/scripts/guibuilder/demo-project/src/main/guibuilder/com/example/NestedTableForm.gui new file mode 100644 index 00000000000..a0debd62c46 --- /dev/null +++ b/scripts/guibuilder/demo-project/src/main/guibuilder/com/example/NestedTableForm.gui @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/scripts/guibuilder/javase/src/test/java/com/codename1/guibuilder/DemoFormsTest.java b/scripts/guibuilder/javase/src/test/java/com/codename1/guibuilder/DemoFormsTest.java new file mode 100644 index 00000000000..36cbf64676b --- /dev/null +++ b/scripts/guibuilder/javase/src/test/java/com/codename1/guibuilder/DemoFormsTest.java @@ -0,0 +1,129 @@ +package com.codename1.guibuilder; + +import com.codename1.guibuilder.model.GuiDocument; +import com.codename1.guibuilder.ui.ComponentPreviewFactory; +import com.codename1.ui.Component; +import com.codename1.ui.Container; +import com.codename1.xml.Element; +import java.io.File; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.util.ArrayList; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Set; +import javax.swing.JPanel; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +/** + * The demo project is the surface anyone evaluating the editor actually opens, so a form that + * parses but renders nothing is a broken first impression. Every form is checked for the same + * things the designer relies on: unique names, a coherent tree, and every component visible. + */ +class DemoFormsTest { + @BeforeAll + static void initializeCodenameOneRuntime() { + if (!com.codename1.ui.Display.isInitialized()) com.codename1.ui.Display.init(new JPanel()); + } + + @Test + void everyDemoFormLoadsRendersAndKeepsEveryComponentVisible() throws Exception { + List forms = demoForms(); + assertTrue(forms.size() >= 12, "expected the demo project to cover more cases: " + forms); + for (File file : forms) { + GuiDocument document = GuiDocument.parse(file.getPath(), + new String(Files.readAllBytes(file.toPath()), StandardCharsets.UTF_8)); + + Set names = new LinkedHashSet<>(); + for (Element element : document.components()) { + String name = element.getAttribute("name"); + assertNotNull(name, file.getName() + " has a component with no name"); + assertTrue(names.add(name), file.getName() + " reuses the name " + name + + ", which collides in the generated source"); + if (element == document.root()) continue; + assertNotNull(document.parentOf(element), + file.getName() + ": " + name + " is detached from the tree"); + } + + Container rendered = (Container) render(document); + for (Element element : document.components()) { + if (element == document.root()) continue; + Component preview = findPreview(rendered, element); + assertNotNull(preview, file.getName() + ": " + element.getAttribute("name") + + " does not render at all"); + assertTrue(preview.getWidth() > 0 && preview.getHeight() > 0, + file.getName() + ": " + element.getAttribute("name") + " renders at " + + preview.getWidth() + "x" + preview.getHeight() + "; it is invisible"); + } + } + } + + @Test + void everyDemoFormSurvivesASaveAndReload() throws Exception { + for (File file : demoForms()) { + GuiDocument document = GuiDocument.parse(file.getPath(), + new String(Files.readAllBytes(file.toPath()), StandardCharsets.UTF_8)); + GuiDocument reloaded = GuiDocument.parse(file.getPath(), document.toXml()); + assertEquals(structure(document), structure(reloaded), + file.getName() + " does not survive a save/load round trip"); + assertEquals(reloaded.toXml(), GuiDocument.parse(file.getPath(), reloaded.toXml()).toXml(), + file.getName() + " keeps changing every time it is written"); + } + } + + private static List demoForms() { + File directory = new File("../demo-project/src/main/guibuilder/com/example"); + assertTrue(directory.isDirectory(), "demo forms are missing at " + directory.getAbsolutePath()); + List forms = new ArrayList<>(); + for (File file : directory.listFiles()) { + if (file.getName().endsWith(".gui")) forms.add(file); + } + java.util.Collections.sort(forms); + return forms; + } + + private static List structure(GuiDocument document) { + List rows = new ArrayList<>(); + for (Element element : document.components()) { + Element parent = document.parentOf(element); + rows.add((parent == null ? "-" : parent.getAttribute("name")) + "/" + element.getAttribute("name")); + } + return rows; + } + + private static Component render(GuiDocument document) { + Component rendered = ComponentPreviewFactory.create(document.root(), null, + new ComponentPreviewFactory.SelectionHandler() { + public void selected(Element element) { } + public void dragPressed(Element element, Component source, int x, int y) { } + public boolean isDragActive() { return false; } + public void editContent(Element element) { } + }); + rendered.setWidth(720); + rendered.setHeight(1200); + layoutNested((Container) rendered); + return rendered; + } + + private static void layoutNested(Container container) { + container.layoutContainer(); + for (int i = 0; i < container.getComponentCount(); i++) { + if (container.getComponentAt(i) instanceof Container nested) layoutNested(nested); + } + } + + private static Component findPreview(Container root, Element element) { + for (int i = 0; i < root.getComponentCount(); i++) { + Component component = root.getComponentAt(i); + if (component.getClientProperty("gui.element") == element) return component; + if (component instanceof Container container) { + Component nested = findPreview(container, element); + if (nested != null) return nested; + } + } + return null; + } +} From 3e0b5246326abf99c0d292c8dbb65df72d1f1589 Mon Sep 17 00:00:00 2001 From: Shai Almog <67850168+shai-almog@users.noreply.github.com> Date: Wed, 5 Aug 2026 21:10:00 +0700 Subject: [PATCH 05/33] Stop an emptied container from pushing its neighbour off the canvas Draining a column in NestedLayoutsForm made every component on the form disappear in phone portrait. The empty-container marker was an ordinary label, so it asked for whatever width "Drop components here" needed -- wider than any real empty container -- and in a horizontal box that left no room for the next column, which was laid out past the right edge of the device and off the visible canvas. The components were never lost; there was nowhere on screen left to draw them. The marker exists to show a drop target, so it must never be the thing that decides a layout. It now has a small fixed preferred size and ellipsises its text. This is why the earlier accounting did not catch it: those tests ran on the desktop canvas, which is wide enough to absorb the overflow. The new test renders at the 720px phone-portrait width -- the narrowest canvas the editor offers, and the one the user hits first. Co-Authored-By: Claude Opus 5 (1M context) --- .../ui/ComponentPreviewFactory.java | 25 ++++++++++++- .../demo-project/src/main/css/theme.css | 4 +-- .../guibuilder/NestedHierarchyTest.java | 36 +++++++++++++++++++ 3 files changed, 62 insertions(+), 3 deletions(-) diff --git a/scripts/guibuilder/common/src/main/java/com/codename1/guibuilder/ui/ComponentPreviewFactory.java b/scripts/guibuilder/common/src/main/java/com/codename1/guibuilder/ui/ComponentPreviewFactory.java index f05f82c01d3..1ec26866f7a 100644 --- a/scripts/guibuilder/common/src/main/java/com/codename1/guibuilder/ui/ComponentPreviewFactory.java +++ b/scripts/guibuilder/common/src/main/java/com/codename1/guibuilder/ui/ComponentPreviewFactory.java @@ -5,6 +5,7 @@ import com.codename1.ui.Button; import com.codename1.ui.CheckBox; import com.codename1.ui.Component; +import com.codename1.ui.Display; import com.codename1.ui.Container; import com.codename1.ui.Label; import com.codename1.ui.FontImage; @@ -177,10 +178,32 @@ private static Component sizedContainer(Element element, Element selected, Selec } } if (out.getLayout() instanceof LayeredLayout) GuidedLayoutSupport.apply(element, out); - if (out.getComponentCount() == 0) out.add(new Label("Drop components here", "BuilderEmptyHint")); + if (out.getComponentCount() == 0) out.add(emptyContainerHint()); return out; } + /** + * The marker shown for a container with no children. + * + *

Its preferred size is deliberately small and fixed. As an ordinary label it asked for + * whatever width its text needed, which is wider than a real empty container would ever be -- + * enough that emptying one column of a horizontal box pushed the next column past the edge of + * the device, so draining a container appeared to delete every component on the form. The hint + * exists to mark a drop target, so it must never be the thing that decides a layout. + */ + private static Component emptyContainerHint() { + Label hint = new Label("Drop here", "BuilderEmptyHint") { + @Override protected Dimension calcPreferredSize() { + int width = Display.getInstance().convertToPixels(8); + int height = Display.getInstance().convertToPixels(4); + return new Dimension(width, height); + } + }; + hint.setEndsWith3Points(true); + hint.setShowEvenIfBlank(true); + return hint; + } + private static boolean additiveSelection(ActionEvent event) { PointerEvent pointer = event == null ? null : event.getPointerEvent(); return pointer != null && (pointer.isShiftDown() || pointer.isControlDown() || pointer.isMetaDown()); diff --git a/scripts/guibuilder/demo-project/src/main/css/theme.css b/scripts/guibuilder/demo-project/src/main/css/theme.css index 38ca4e7f013..27717319966 100644 --- a/scripts/guibuilder/demo-project/src/main/css/theme.css +++ b/scripts/guibuilder/demo-project/src/main/css/theme.css @@ -6,14 +6,14 @@ Form { Title { background-color: #17233b; - color: #ffffff; + color: yellow; font-family: "native:MainBold"; padding: 2mm; } Label, SpanLabel { background-color: transparent; - color: #26324a; + color: blue; padding: 2mm; } diff --git a/scripts/guibuilder/javase/src/test/java/com/codename1/guibuilder/NestedHierarchyTest.java b/scripts/guibuilder/javase/src/test/java/com/codename1/guibuilder/NestedHierarchyTest.java index 3a510a6f2e4..c1dfcb78fbf 100644 --- a/scripts/guibuilder/javase/src/test/java/com/codename1/guibuilder/NestedHierarchyTest.java +++ b/scripts/guibuilder/javase/src/test/java/com/codename1/guibuilder/NestedHierarchyTest.java @@ -280,6 +280,42 @@ private static void assertNoSharedTableCell(Element parent, GuiDocument document } } + @Test + void emptyingAColumnDoesNotPushTheOtherColumnOffTheDevice() { + // Exactly the shape of NestedLayoutsForm once its grid column has been drained. + GuiDocument document = document("\n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + ""); + + // Phone portrait is the narrowest canvas mode the editor offers, and the one the empty + // container hint used to overflow: it asked for its full text width, which left no room + // for the neighbouring column and pushed every component past the right edge. + Container rendered = (Container) render(document, 720, 1200); + Component form = findPreview(rendered, document.root()); + int rightEdge = (form == null ? rendered : form).getAbsoluteX() + + (form == null ? rendered : form).getWidth(); + for (Element element : document.components()) { + if (element == document.root()) continue; + Component preview = findPreview(rendered, element); + assertNotNull(preview, element.getAttribute("name") + " does not render"); + assertTrue(preview.getAbsoluteX() < rightEdge, + element.getAttribute("name") + " starts at x=" + preview.getAbsoluteX() + + ", past the device's right edge at " + rightEdge + + "; emptying a column must not push its neighbour off the canvas"); + } + } + // ---- undo across nesting ------------------------------------------------------------------ @Test From a48a0129f73f2e9c3f48b53fcf7715871675c57e Mon Sep 17 00:00:00 2001 From: Shai Almog <67850168+shai-almog@users.noreply.github.com> Date: Wed, 5 Aug 2026 21:10:22 +0700 Subject: [PATCH 06/33] Revert demo theme colours left over from a manual CSS test Yellow titles and blue labels were experiment values, not intended demo styling. Co-Authored-By: Claude Opus 5 (1M context) --- scripts/guibuilder/demo-project/src/main/css/theme.css | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/guibuilder/demo-project/src/main/css/theme.css b/scripts/guibuilder/demo-project/src/main/css/theme.css index 27717319966..38ca4e7f013 100644 --- a/scripts/guibuilder/demo-project/src/main/css/theme.css +++ b/scripts/guibuilder/demo-project/src/main/css/theme.css @@ -6,14 +6,14 @@ Form { Title { background-color: #17233b; - color: yellow; + color: #ffffff; font-family: "native:MainBold"; padding: 2mm; } Label, SpanLabel { background-color: transparent; - color: blue; + color: #26324a; padding: 2mm; } From e9935e8fb21fe1ec8d36f3190579f05421ad54c8 Mon Sep 17 00:00:00 2001 From: Shai Almog <67850168+shai-almog@users.noreply.github.com> Date: Wed, 5 Aug 2026 21:42:53 +0700 Subject: [PATCH 07/33] Say why a keystroke was refused instead of ignoring it The companion Java editor looked completely dead: keys changed nothing. It was not dead. Almost all of a companion file sits between the generated markers, the caret starts at offset zero which is inside the first generated block, and CodeView dropped every protected edit in silence. Typing did exactly what it was told to do and said nothing about it, which is indistinguishable from an editor that never sees the keyboard. CodeView now reports a refused edit through the existing editor event channel and CodeEditor exposes it as addProtectedEditListener. The GUI Builder uses it to explain what happened and to put the caret back in the user region. Focus and the initial caret are also requested after the editor is in the form rather than before: requestFocus does nothing for a component with no form. The CSS side turned out to work. LiveCssTest drives a real workspace, edits theme.css and asserts the canvas restyles, with and without the CSS editor open, and ProjectCssCompilesTest proves the demo stylesheet compiles. Since the mechanism holds up, the reported symptom -- a canvas showing a different theme than the stylesheet on screen -- is the silent fallback: when the project theme fails to load the editor quietly renders with its own theme. That now says so, and the CSS editor names the file it is editing rather than just "theme.css". Co-Authored-By: Claude Opus 5 (1M context) --- .../src/com/codename1/ui/CodeEditor.java | 29 +++++ .../src/com/codename1/ui/editor/CodeView.java | 4 + .../guibuilder/CodenameOneGUIBuilder.java | 35 +++++- .../com/codename1/guibuilder/LiveCssTest.java | 107 ++++++++++++++++++ .../guibuilder/ProjectCssCompilesTest.java | 47 ++++++++ .../guibuilder/SourceEditingTest.java | 81 +++++++++++++ 6 files changed, 299 insertions(+), 4 deletions(-) create mode 100644 scripts/guibuilder/javase/src/test/java/com/codename1/guibuilder/LiveCssTest.java create mode 100644 scripts/guibuilder/javase/src/test/java/com/codename1/guibuilder/ProjectCssCompilesTest.java create mode 100644 scripts/guibuilder/javase/src/test/java/com/codename1/guibuilder/SourceEditingTest.java diff --git a/CodenameOne/src/com/codename1/ui/CodeEditor.java b/CodenameOne/src/com/codename1/ui/CodeEditor.java index 92739a5b646..92a110c6b0f 100644 --- a/CodenameOne/src/com/codename1/ui/CodeEditor.java +++ b/CodenameOne/src/com/codename1/ui/CodeEditor.java @@ -26,6 +26,9 @@ import com.codename1.ui.editor.CodePureEditor; import com.codename1.ui.editor.PureEditor; import com.codename1.ui.editor.SyntaxHighlighter; +import com.codename1.ui.events.ActionEvent; +import com.codename1.ui.events.ActionListener; +import com.codename1.ui.util.EventDispatcher; import com.codename1.util.SuccessCallback; import java.util.Hashtable; @@ -66,6 +69,7 @@ public class CodeEditor extends AbstractEditorComponent { private boolean showLineNumbers = true; private int tabSize = 4; private CodeCompletionProvider completionProvider; + private final EventDispatcher protectedEditListeners = new EventDispatcher(); /// Creates an empty code editor. public CodeEditor() { @@ -226,6 +230,27 @@ public void setProtectedRegionMarkers(String startMarker, String endMarker) { ? "" : startMarker + "\n" + endMarker); } + /// Registers a listener notified when an edit is refused because it falls inside a protected + /// region. Without it a protected region is silent, and one covering most of the document is + /// indistinguishable from an editor that ignores the keyboard. The event source is this editor + /// and the event is fired on the EDT. + /// + /// #### Parameters + /// + /// - `listener`: invoked once per refused edit + public void addProtectedEditListener(ActionListener listener) { + protectedEditListeners.addListener(listener); + } + + /// Removes a listener added by `#addProtectedEditListener(ActionListener)`. + /// + /// #### Parameters + /// + /// - `listener`: the listener to remove + public void removeProtectedEditListener(ActionListener listener) { + protectedEditListeners.removeListener(listener); + } + /// Moves the caret to a character offset, clamped by the editor backend to the document bounds. public void setCursorPosition(int offset) { command("setCursor", String.valueOf(Math.max(0, offset))); @@ -315,6 +340,10 @@ void onEditorEvent(String type, String value) { handleCompletionRequest(value); return; } + if ("protectedEdit".equals(type)) { + protectedEditListeners.fireActionEvent(new ActionEvent(this)); + return; + } super.onEditorEvent(type, value); } diff --git a/CodenameOne/src/com/codename1/ui/editor/CodeView.java b/CodenameOne/src/com/codename1/ui/editor/CodeView.java index f842688f0f9..ee8b93b7947 100644 --- a/CodenameOne/src/com/codename1/ui/editor/CodeView.java +++ b/CodenameOne/src/com/codename1/ui/editor/CodeView.java @@ -138,6 +138,10 @@ protected void replaceRange(int start, int end, String text, boolean record) { clampedEnd = swap; } if (isProtectedEdit(clampedStart, clampedEnd)) { + // Tell the host rather than dropping the edit in silence. A protected region covering + // most of the document is indistinguishable from an editor that ignores the keyboard, + // so the application needs the chance to explain why nothing happened. + host().fireEditorEvent("protectedEdit", String.valueOf(clampedStart)); return; } super.replaceRange(clampedStart, clampedEnd, text, record); diff --git a/scripts/guibuilder/common/src/main/java/com/codename1/guibuilder/CodenameOneGUIBuilder.java b/scripts/guibuilder/common/src/main/java/com/codename1/guibuilder/CodenameOneGUIBuilder.java index 13d6e042b95..134c3be70de 100644 --- a/scripts/guibuilder/common/src/main/java/com/codename1/guibuilder/CodenameOneGUIBuilder.java +++ b/scripts/guibuilder/common/src/main/java/com/codename1/guibuilder/CodenameOneGUIBuilder.java @@ -2647,6 +2647,15 @@ List selectedElementsSnapshot() { Container canvasHostForTest() { return canvasHost; } + /** Recompiles theme.css from disk and pushes it at the preview, as a live CSS edit does. */ + boolean reloadProjectCssForTest() { + try { + return applyProjectCss(ProjectIO.read(binding.cssFile()), new CodeEditor("css", "")); + } catch (IOException ex) { + return false; + } + } + GuiDocument documentForTest() { return document; } private void cancelDesignerDrag() { @@ -3866,7 +3875,9 @@ private void openCss() { editor.setShowLineNumbers(true); editor.onReady(editor::focusEditor); Component stage = canvasHost.getComponentAt(0); - Container editorPane = editorPane("theme.css", editor, + // Name the file being edited. "theme.css" alone left it ambiguous which stylesheet the + // canvas was actually showing when the two did not appear to agree. + Container editorPane = editorPane(binding.cssFile(), editor, () -> editor.getText(value -> saveCss(value, editor)), this::refreshEditor); editor.addChangeListener(event -> scheduleLiveCss(editor)); if (cssLiveTimer != null) cssLiveTimer.cancel(); @@ -3964,9 +3975,13 @@ private void openSourceEditor(String handler) { int userMarker = source.indexOf("// "); final int editableOffset = userMarker < 0 ? 0 : userMarker + "// ".length() + 1; - editor.onReady(() -> { + // Most of a companion file is generated and therefore protected. Refusing those edits + // without a word is indistinguishable from an editor that ignores the keyboard, which + // is exactly how it was read, so say what happened and where typing does work. + editor.addProtectedEditListener(e -> { + setStatus("That block is generated from the design and cannot be typed into" + + " - edit between the USER CODE markers"); editor.setCursorPosition(editableOffset); - editor.focusEditor(); }); Component stage = canvasHost.getComponentCount() == 0 ? new Label() : canvasHost.getComponentAt(0); Container editorPane = editorPane(handler == null ? "Companion Java source • edit inside USER CODE markers" : "Handler: " + handler, editor, @@ -3976,7 +3991,14 @@ private void openSourceEditor(String handler) { canvasHost.removeAll(); canvasHost.add(BorderLayout.CENTER, split); canvasHost.revalidate(); - setStatus("Editing " + relativeFormName(document.path()) + " Java behavior"); + // Only now: requestFocus does nothing for a component that is not yet in the form, and + // a caret left at offset zero sits inside the first generated block. + editor.onReady(() -> { + editor.setCursorPosition(editableOffset); + editor.focusEditor(); + }); + setStatus("Editing " + relativeFormName(document.path()) + " Java behavior" + + " - type between the USER CODE markers"); } catch (IOException ex) { ToastBar.showErrorMessage("Unable to open source: " + ex.getMessage()); } @@ -4031,6 +4053,11 @@ private boolean applyProjectCss(String css, CodeEditor editor) { } private void refreshProjectThemeOnPreview() { + if (projectTheme == null) { + // Falling back to the builder's own theme silently is what makes the canvas look like + // it is showing a stylesheet other than the one open in the editor. It is. + setStatus("Project CSS is not loaded - the canvas is showing the builder's theme"); + } try { if (projectTheme != null) { UIManager.getInstance().setThemeProps(projectTheme); diff --git a/scripts/guibuilder/javase/src/test/java/com/codename1/guibuilder/LiveCssTest.java b/scripts/guibuilder/javase/src/test/java/com/codename1/guibuilder/LiveCssTest.java new file mode 100644 index 00000000000..3f0899fdab6 --- /dev/null +++ b/scripts/guibuilder/javase/src/test/java/com/codename1/guibuilder/LiveCssTest.java @@ -0,0 +1,107 @@ +package com.codename1.guibuilder; + +import com.codename1.ui.Component; +import com.codename1.ui.Container; +import com.codename1.ui.Display; +import com.codename1.xml.Element; +import java.io.File; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import javax.swing.JPanel; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +/** Editing theme.css has to change what the canvas draws; that is the whole point of live CSS. */ +class LiveCssTest { + @BeforeAll + static void initializeCodenameOneRuntime() { + if (!Display.isInitialized()) Display.init(new JPanel()); + } + + @Test + void editingCssRestylesTheLivePreview() throws Exception { + Path project = Files.createTempDirectory("guibuilder-css"); + Path gui = project.resolve("src/main/guibuilder/com/example"); + Files.createDirectories(gui); + Files.write(gui.resolve("StyledForm.gui"), ("\n" + + "\n" + + " \n" + + "\n").getBytes(StandardCharsets.UTF_8)); + Path css = project.resolve("src/main/css/theme.css"); + Files.createDirectories(css.getParent()); + Files.write(css, "Label { color: #ff0000; }\n".getBytes(StandardCharsets.UTF_8)); + + Path input = Files.createTempFile("guibuilder", ".input"); + Files.write(input, ("projectDir=" + project + "\nguiDir=" + gui.getParent().getParent() + "\n" + + "sourceDir=" + project.resolve("src/main/java") + "\ncssFile=" + css + "\n" + + "initialForm=com.example.StyledForm\n").getBytes(StandardCharsets.UTF_8)); + System.setProperty("guibuilder.input", input.toString()); + System.setProperty("guibuilder.canvasMode", "desktop"); + + CodenameOneGUIBuilder builder = new CodenameOneGUIBuilder(); + builder.init(null); + builder.runApp(); + settle(); + assertNull(onEdt(() -> builder.mcpOpenForm("com.example.StyledForm"))); + settle(); + + // Open the CSS editor first: that is how anyone edits CSS, and it rebuilds the canvas into + // a split pane, which is the one thing the direct path never exercises. + Display.getInstance().callSeriallyAndWait(() -> { + try { + java.lang.reflect.Method open = CodenameOneGUIBuilder.class.getDeclaredMethod("openCss"); + open.setAccessible(true); + open.invoke(builder); + } catch (Exception ex) { + throw new RuntimeException(ex); + } + }); + settle(); + + int before = foreground(builder, "styled"); + assertEquals(0xff0000, before, "the starting CSS colour must reach the preview"); + + Files.write(css, "Label { color: #00ff00; }\n".getBytes(StandardCharsets.UTF_8)); + assertTrue(onEdt(() -> String.valueOf(builder.reloadProjectCssForTest())).equals("true"), + "recompiling the project CSS failed"); + settle(); + + assertEquals(0x00ff00, foreground(builder, "styled"), + "editing theme.css must restyle the canvas, not just the file"); + } + + private static int foreground(CodenameOneGUIBuilder builder, String name) { + Component preview = find(builder.canvasHostForTest(), builder, name); + assertNotNull(preview, name + " does not render"); + return preview.getUnselectedStyle().getFgColor(); + } + + private static Component find(Container root, CodenameOneGUIBuilder builder, String name) { + for (int i = 0; i < root.getComponentCount(); i++) { + Component component = root.getComponentAt(i); + Object element = component.getClientProperty("gui.element"); + if (element instanceof Element e && name.equals(e.getAttribute("name"))) return component; + if (component instanceof Container container) { + Component nested = find(container, builder, name); + if (nested != null) return nested; + } + } + return null; + } + + private static String onEdt(java.util.function.Supplier work) { + final String[] out = new String[1]; + Display.getInstance().callSeriallyAndWait(() -> out[0] = work.get()); + return out[0]; + } + + private static void settle() { + for (int i = 0; i < 5; i++) { + try { Thread.sleep(250); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } + Display.getInstance().callSeriallyAndWait(() -> { }); + } + } +} diff --git a/scripts/guibuilder/javase/src/test/java/com/codename1/guibuilder/ProjectCssCompilesTest.java b/scripts/guibuilder/javase/src/test/java/com/codename1/guibuilder/ProjectCssCompilesTest.java new file mode 100644 index 00000000000..e662f0c19e5 --- /dev/null +++ b/scripts/guibuilder/javase/src/test/java/com/codename1/guibuilder/ProjectCssCompilesTest.java @@ -0,0 +1,47 @@ +package com.codename1.guibuilder; + +import com.codename1.ui.css.CSSThemeCompiler; +import com.codename1.ui.util.MutableResource; +import java.io.File; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.util.Hashtable; +import javax.swing.JPanel; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +/** + * If the project's theme.css fails to compile the editor silently falls back to its own theme, so + * the canvas shows styling that has nothing to do with the CSS being edited. + */ +class ProjectCssCompilesTest { + @BeforeAll static void init() { + if (!com.codename1.ui.Display.isInitialized()) com.codename1.ui.Display.init(new JPanel()); + } + + @Test + void theDemoProjectThemeCompilesIntoAUsableTheme() throws Exception { + File css = new File("../demo-project/src/main/css/theme.css"); + assertTrue(css.isFile(), "demo theme.css is missing at " + css.getAbsolutePath()); + MutableResource resources = new MutableResource(); + try { + new CSSThemeCompiler().compile( + new String(Files.readAllBytes(css.toPath()), StandardCharsets.UTF_8), + resources, "ProjectTheme"); + } catch (RuntimeException ex) { + fail("the demo theme.css does not compile, so the canvas can never show it: " + ex); + } + Hashtable theme = resources.getTheme("ProjectTheme"); + assertNotNull(theme, "compilation produced no theme"); + assertFalse(theme.isEmpty(), "the compiled theme is empty"); + System.out.println("PROJECT CSS keys: " + theme.size()); + int titleKeys = 0; + for (Object key : theme.keySet()) { + if (String.valueOf(key).startsWith("Title")) titleKeys++; + } + System.out.println("PROJECT CSS Title keys: " + titleKeys); + assertTrue(titleKeys > 0, "the compiled theme has no Title styling even though theme.css sets it"); + } +} diff --git a/scripts/guibuilder/javase/src/test/java/com/codename1/guibuilder/SourceEditingTest.java b/scripts/guibuilder/javase/src/test/java/com/codename1/guibuilder/SourceEditingTest.java new file mode 100644 index 00000000000..486d580c2b4 --- /dev/null +++ b/scripts/guibuilder/javase/src/test/java/com/codename1/guibuilder/SourceEditingTest.java @@ -0,0 +1,81 @@ +package com.codename1.guibuilder; + +import com.codename1.ui.TextInputClient; +import com.codename1.ui.TextInputConfig; +import com.codename1.ui.TextInputState; +import com.codename1.ui.editor.CodePureEditor; +import com.codename1.ui.editor.CodeView; +import com.codename1.ui.editor.EditorHost; +import javax.swing.JPanel; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +class SourceEditingTest { + @BeforeAll static void init() { + if (!com.codename1.ui.Display.isInitialized()) com.codename1.ui.Display.init(new JPanel()); + } + + private static final String SOURCE = + "// \n" + + "package com.example;\n" + + "public class F {\n" + + "// \n" + + "// \n" + + "\n" + + "// \n" + + "// \n}\n// \n"; + + @Test void typingInsideTheUserRegionIsAccepted() { + CodePureEditor editor = new CodePureEditor(new Host(), "java"); + CodeView view = (CodeView) editor.getView(); + editor.cmd("setText", SOURCE); + editor.cmd("setProtectedMarkers", "// \n// "); + int userOffset = SOURCE.indexOf("// ") + "// ".length() + 1; + view.replaceRange(userOffset, userOffset, "int typed = 1;"); + assertTrue(editor.query("getText", null).contains("int typed = 1;"), + "typing in the user region must work"); + } + + @Test void typingWhereTheCaretStartsIsAccepted() { + CodePureEditor editor = new CodePureEditor(new Host(), "java"); + CodeView view = (CodeView) editor.getView(); + editor.cmd("setText", SOURCE); + editor.cmd("setProtectedMarkers", "// \n// "); + // Offset 0 is where the caret sits until something moves it, and it is inside the first + // generated block. Typing there is silently dropped, which reads as a dead editor. + view.replaceRange(0, 0, "X"); + assertFalse(editor.query("getText", null).startsWith("X"), + "the generated region must stay protected"); + System.out.println("PROTECTED-AT-CARET-ZERO: generated region correctly rejects typing at offset 0"); + } + + private static final class Host implements EditorHost { + @Override + public boolean isTextInputSupported() { + return false; + } + + @Override + public Object startTextInput(TextInputClient client, TextInputConfig config) { + return null; + } + + @Override + public void updateTextInputState(Object handle, TextInputState state) { + } + + @Override + public void stopTextInput(Object handle) { + } + + @Override + public void editorChanged() { + } + + @Override + public void fireEditorEvent(String type, String value) { + } + } +} From 6fbb1c265cf5c05f170817c47b7bf470f5ab32d3 Mon Sep 17 00:00:00 2001 From: Shai Almog <67850168+shai-almog@users.noreply.github.com> Date: Wed, 5 Aug 2026 21:55:48 +0700 Subject: [PATCH 08/33] Leave the caret alone when an edit is refused Reporting a refused edit was right; moving the caret afterwards was not. The rejected keystroke is already gone by then, so typing PINK inside a generated block dropped the P, jumped the caret to the user region and typed INK there -- and every subsequent edit went to the user region too, which reads as all changes being pushed to the end of the file. A refused edit now leaves the caret exactly where it was put. Typing in a generated block does nothing and says why, which is the behaviour a read-only region should have. Co-Authored-By: Claude Opus 5 (1M context) --- .../codename1/guibuilder/CodenameOneGUIBuilder.java | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/scripts/guibuilder/common/src/main/java/com/codename1/guibuilder/CodenameOneGUIBuilder.java b/scripts/guibuilder/common/src/main/java/com/codename1/guibuilder/CodenameOneGUIBuilder.java index 134c3be70de..26286590b6e 100644 --- a/scripts/guibuilder/common/src/main/java/com/codename1/guibuilder/CodenameOneGUIBuilder.java +++ b/scripts/guibuilder/common/src/main/java/com/codename1/guibuilder/CodenameOneGUIBuilder.java @@ -3978,11 +3978,13 @@ private void openSourceEditor(String handler) { // Most of a companion file is generated and therefore protected. Refusing those edits // without a word is indistinguishable from an editor that ignores the keyboard, which // is exactly how it was read, so say what happened and where typing does work. - editor.addProtectedEditListener(e -> { - setStatus("That block is generated from the design and cannot be typed into" - + " - edit between the USER CODE markers"); - editor.setCursorPosition(editableOffset); - }); + // Report the refusal and nothing else. Moving the caret into the user region looked + // helpful and was awful: the rejected keystroke was already gone, so typing "PINK" in a + // generated block dropped the P, jumped the caret to the user region and typed "INK" + // there. A refused edit must leave the caret exactly where the user put it. + editor.addProtectedEditListener(e -> setStatus( + "That block is generated from the design and cannot be edited" + + " - type between the USER CODE markers")); Component stage = canvasHost.getComponentCount() == 0 ? new Label() : canvasHost.getComponentAt(0); Container editorPane = editorPane(handler == null ? "Companion Java source • edit inside USER CODE markers" : "Handler: " + handler, editor, () -> editor.getText(value -> saveSourceAndModel(sourcePath, value)), this::refreshEditor); From 87972f50d803c2155cd78d3ee8de2cbc6d0ee5ae Mon Sep 17 00:00:00 2001 From: Shai Almog <67850168+shai-almog@users.noreply.github.com> Date: Wed, 5 Aug 2026 22:14:28 +0700 Subject: [PATCH 09/33] Type real keystrokes at the editor in an integration test Every editor test so far drove the document API, which is why editors that were unusable from the keyboard kept passing. LiveTypingTest boots the real workspace, opens the Java and CSS editors the way the toolbar does, clicks into the editing surface with a pointer event rather than assigning focus by hand, and sends keyPressed/keyReleased per character through the form. What it establishes, by running rather than by reading: - JavaSE uses the pure editor. createNativeEditorPeer returns null and isTextInputSupported is false, so there is no native peer and no separate input session; keys arrive through Component#keyReleased. - Clicking the editing surface does focus it, and typing PINK into either editor inserts PINK intact, protected regions included. So the editor, its focus handling and the protected-region logic are not what break typing in the running application. Whatever does is upstream of the form, in how the desktop port delivers key events to it. That is where the next round has to look, and this harness is what will catch it. Also corrects the isTextInputSupported documentation, which still promised a BrowserComponent fallback that initBackend explicitly does not have. Co-Authored-By: Claude Opus 5 (1M context) --- .../impl/CodenameOneImplementation.java | 5 +- .../guibuilder/CodenameOneGUIBuilder.java | 2 + .../codename1/guibuilder/LiveTypingTest.java | 173 ++++++++++++++++++ 3 files changed, 178 insertions(+), 2 deletions(-) create mode 100644 scripts/guibuilder/javase/src/test/java/com/codename1/guibuilder/LiveTypingTest.java diff --git a/CodenameOne/src/com/codename1/impl/CodenameOneImplementation.java b/CodenameOne/src/com/codename1/impl/CodenameOneImplementation.java index 9f3e98c5cc0..bdd00be030d 100644 --- a/CodenameOne/src/com/codename1/impl/CodenameOneImplementation.java +++ b/CodenameOne/src/com/codename1/impl/CodenameOneImplementation.java @@ -5428,8 +5428,9 @@ public String editorPeerQuery(PeerComponent peer, String name, String arg) { /// Returns true when this platform can bind a `com.codename1.ui.TextInputClient` to a low level text /// input source (soft keyboard / IME / hardware keyboard) so a component can capture raw text input - /// while rendering the document itself. When false the pure Codename One editors fall back to their - /// `BrowserComponent` backend. The default returns false. + /// while rendering the document itself. When false the pure Codename One editors read the physical + /// keyboard directly through `Component#keyReleased(int)`; there is no browser or HTML backend. + /// The default returns false. public boolean isTextInputSupported() { return false; } diff --git a/scripts/guibuilder/common/src/main/java/com/codename1/guibuilder/CodenameOneGUIBuilder.java b/scripts/guibuilder/common/src/main/java/com/codename1/guibuilder/CodenameOneGUIBuilder.java index 26286590b6e..689d2a0a617 100644 --- a/scripts/guibuilder/common/src/main/java/com/codename1/guibuilder/CodenameOneGUIBuilder.java +++ b/scripts/guibuilder/common/src/main/java/com/codename1/guibuilder/CodenameOneGUIBuilder.java @@ -2647,6 +2647,8 @@ List selectedElementsSnapshot() { Container canvasHostForTest() { return canvasHost; } + CodeEditor activeEditorForTest() { return activeCodeEditor; } + /** Recompiles theme.css from disk and pushes it at the preview, as a live CSS edit does. */ boolean reloadProjectCssForTest() { try { diff --git a/scripts/guibuilder/javase/src/test/java/com/codename1/guibuilder/LiveTypingTest.java b/scripts/guibuilder/javase/src/test/java/com/codename1/guibuilder/LiveTypingTest.java new file mode 100644 index 00000000000..6b9df0e91e7 --- /dev/null +++ b/scripts/guibuilder/javase/src/test/java/com/codename1/guibuilder/LiveTypingTest.java @@ -0,0 +1,173 @@ +package com.codename1.guibuilder; + +import com.codename1.ui.CodeEditor; +import com.codename1.ui.Component; +import com.codename1.ui.Container; +import com.codename1.ui.Display; +import com.codename1.ui.Form; +import com.codename1.ui.editor.EditorView; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import javax.swing.JPanel; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +/** + * Types real keystrokes into the editor the running application actually builds, through the same + * focus and key routing a keyboard uses. Every previous editor test drove the document API instead, + * which is why editors that were dead to the keyboard kept passing. + */ +class LiveTypingTest { + @BeforeAll + static void initializeCodenameOneRuntime() { + if (!Display.isInitialized()) Display.init(new JPanel()); + } + + @Test + void typingIntoTheJavaEditorReachesTheDocument() throws Exception { + CodenameOneGUIBuilder builder = workspace(); + open(builder, "openSourceEditor", String.class, null); + settle(); + + CodeEditor editor = builder.activeEditorForTest(); + assertNotNull(editor, "the Java editor was never created"); + EditorView view = viewOf(editor); + assertNotNull(view, "the Java editor has no pure editor view; the backend changed"); + assertTrue(view.isFocusable(), "the editing surface must be focusable to receive keys"); + + String before = textOf(editor); + assertTrue(before.contains("// "), "the companion source lost its markers"); + + click(view); + assertSame(view, view.getComponentForm().getFocused(), + "clicking the editor did not focus it, so no keystroke can ever reach it"); + + type(view, "PINK"); + String after = textOf(editor); + + assertNotEquals(before, after, "typing changed nothing at all; the Java editor is dead to the keyboard"); + assertTrue(after.contains("PINK"), + "typing must insert the whole word, not drop characters:\n" + userRegion(after)); + } + + @Test + void typingIntoTheCssEditorReachesTheDocument() throws Exception { + CodenameOneGUIBuilder builder = workspace(); + open(builder, "openCss", null, null); + settle(); + + CodeEditor editor = builder.activeEditorForTest(); + assertNotNull(editor, "the CSS editor was never created"); + EditorView view = viewOf(editor); + click(view); + assertSame(view, view.getComponentForm().getFocused(), + "clicking the CSS editor did not focus it"); + String before = textOf(editor); + + type(view, "PINK"); + + String after = textOf(editor); + assertNotEquals(before, after, "the CSS editor is dead to the keyboard"); + assertTrue(after.contains("PINK"), "typing must insert the whole word, not drop characters"); + } + + // ---- harness ---------------------------------------------------------------------------- + + /** Drives the real key routing: Form focus, then keyPressed/keyReleased per character. */ + private static void type(EditorView view, String text) { + Form form = view.getComponentForm(); + for (int i = 0; i < text.length(); i++) { + final int code = text.charAt(i); + Display.getInstance().callSeriallyAndWait(() -> { + form.keyPressed(code); + form.keyReleased(code); + }); + } + settle(); + } + + /** + * Clicks into the editor exactly as a user does, instead of assigning focus by hand. Forcing + * focus is what made the earlier tests pass while the editor was unusable in the application. + */ + private static void click(EditorView view) { + int x = view.getAbsoluteX() + view.getWidth() / 2; + int y = view.getAbsoluteY() + Math.min(view.getHeight() - 2, 20); + Form form = view.getComponentForm(); + Display.getInstance().callSeriallyAndWait(() -> { + form.pointerPressed(x, y); + form.pointerReleased(x, y); + }); + settle(); + } + + private static String textOf(CodeEditor editor) { + final String[] out = new String[1]; + Display.getInstance().callSeriallyAndWait(() -> editor.getText(value -> out[0] = value)); + settle(); + return out[0] == null ? "" : out[0]; + } + + private static EditorView viewOf(CodeEditor editor) { + return findView(editor); + } + + private static EditorView findView(Container root) { + for (int i = 0; i < root.getComponentCount(); i++) { + Component component = root.getComponentAt(i); + if (component instanceof EditorView view) return view; + if (component instanceof Container container) { + EditorView nested = findView(container); + if (nested != null) return nested; + } + } + return null; + } + + private static String userRegion(String source) { + int start = source.indexOf("// "); + int end = source.indexOf("// "); + return start < 0 || end < 0 ? source : source.substring(start, end + 27); + } + + private static void open(CodenameOneGUIBuilder builder, String method, Class argType, Object arg) { + Display.getInstance().callSeriallyAndWait(() -> { + try { + java.lang.reflect.Method m = argType == null + ? CodenameOneGUIBuilder.class.getDeclaredMethod(method) + : CodenameOneGUIBuilder.class.getDeclaredMethod(method, argType); + m.setAccessible(true); + if (argType == null) m.invoke(builder); else m.invoke(builder, arg); + } catch (Exception ex) { + throw new RuntimeException(ex); + } + }); + } + + private static CodenameOneGUIBuilder workspace() throws Exception { + Path demo = new java.io.File("../demo-project").getCanonicalFile().toPath(); + Path input = Files.createTempFile("guibuilder", ".input"); + Files.write(input, ("projectDir=" + demo + "\nguiDir=" + demo.resolve("src/main/guibuilder") + + "\nsourceDir=" + demo.resolve("src/main/java") + + "\ncssFile=" + demo.resolve("src/main/css/theme.css") + + "\ninitialForm=com.example.NestedLayoutsForm\n").getBytes(StandardCharsets.UTF_8)); + input.toFile().deleteOnExit(); + System.setProperty("guibuilder.input", input.toString()); + System.setProperty("guibuilder.canvasMode", "desktop"); + CodenameOneGUIBuilder builder = new CodenameOneGUIBuilder(); + builder.init(null); + builder.runApp(); + settle(); + return builder; + } + + private static void settle() { + for (int i = 0; i < 4; i++) { + try { Thread.sleep(200); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } + Display.getInstance().callSeriallyAndWait(() -> { }); + } + } +} From b5e94cad0e5c6bccb095dc422870a79a146f970a Mon Sep 17 00:00:00 2001 From: Shai Almog <67850168+shai-almog@users.noreply.github.com> Date: Thu, 6 Aug 2026 08:08:40 +0700 Subject: [PATCH 10/33] Stop the drag guide overlay from swallowing every click on the editor The code editor was never dead. It never got focus, because nothing it sits under would let a pointer through. The designer layers DragGuideOverlay over the whole canvas area at inset zero, and once a source or CSS editor opens it lives inside that same canvas area. Hit testing finds components by containment, so every press over the editor resolved to the overlay. No focus, no key events, no sign of why. The constructor already called setIgnorePointerEvents(true), which governs event delivery and not hit testing, so the overlay was still the component found at the pointer and still decided focus. Overriding contains() to claim nothing is what actually keeps a glass pane out of the way. LiveTypingTest now asserts what is hit testable where the editor is drawn, before clicking and typing. Reverting the contains() override makes it fail, which is the property every earlier editor test lacked: they drove the document API or assigned focus by hand, so they passed regardless of whether a click could ever reach the editor. Co-Authored-By: Claude Opus 5 (1M context) --- .../guibuilder/ui/DragGuideOverlay.java | 22 ++++++++++++++- .../codename1/guibuilder/LiveTypingTest.java | 28 +++++++++++++++++++ 2 files changed, 49 insertions(+), 1 deletion(-) diff --git a/scripts/guibuilder/common/src/main/java/com/codename1/guibuilder/ui/DragGuideOverlay.java b/scripts/guibuilder/common/src/main/java/com/codename1/guibuilder/ui/DragGuideOverlay.java index da8a51c32de..dfa2f39e790 100644 --- a/scripts/guibuilder/common/src/main/java/com/codename1/guibuilder/ui/DragGuideOverlay.java +++ b/scripts/guibuilder/common/src/main/java/com/codename1/guibuilder/ui/DragGuideOverlay.java @@ -7,8 +7,25 @@ import java.util.ArrayList; import java.util.List; -/** Paints insertion and constraint geometry above the visual designer while dragging. */ +/** + * Paints insertion and constraint geometry above the visual designer while dragging. + * + *

This is a glass pane: it draws over the whole canvas area and must never be hit by a pointer. + * It is layered at inset zero on top of the canvas host, so ordinary hit testing picked it for + * every press in that area -- including presses on the code editor, which lives inside the canvas + * host once a source or CSS editor is open. The editor therefore never took focus and appeared to + * ignore the keyboard entirely. + */ public final class DragGuideOverlay extends Component { + /** + * Never claims a pointer position. Hit testing walks children by containment, so returning + * false here keeps the overlay purely decorative and lets presses reach whatever it covers. + */ + @Override + public boolean contains(int x, int y) { + return false; + } + private String layout = "BoxLayout"; private String axis = "Y"; private String constraint; @@ -82,6 +99,9 @@ public DependencyLink(String from, String to, int fromX, int fromY, int toX, int } public DragGuideOverlay() { + // setIgnorePointerEvents governs event delivery, not hit testing: the overlay was still + // the component found at a pointer position, which is what decides focus. contains() above + // is what actually keeps it out of the way. setIgnorePointerEvents(true); setVisible(false); } diff --git a/scripts/guibuilder/javase/src/test/java/com/codename1/guibuilder/LiveTypingTest.java b/scripts/guibuilder/javase/src/test/java/com/codename1/guibuilder/LiveTypingTest.java index 6b9df0e91e7..545ddab1505 100644 --- a/scripts/guibuilder/javase/src/test/java/com/codename1/guibuilder/LiveTypingTest.java +++ b/scripts/guibuilder/javase/src/test/java/com/codename1/guibuilder/LiveTypingTest.java @@ -41,6 +41,7 @@ void typingIntoTheJavaEditorReachesTheDocument() throws Exception { String before = textOf(editor); assertTrue(before.contains("// "), "the companion source lost its markers"); + assertNothingCoversTheEditor(view); click(view); assertSame(view, view.getComponentForm().getFocused(), "clicking the editor did not focus it, so no keystroke can ever reach it"); @@ -62,6 +63,7 @@ void typingIntoTheCssEditorReachesTheDocument() throws Exception { CodeEditor editor = builder.activeEditorForTest(); assertNotNull(editor, "the CSS editor was never created"); EditorView view = viewOf(editor); + assertNothingCoversTheEditor(view); click(view); assertSame(view, view.getComponentForm().getFocused(), "clicking the CSS editor did not focus it"); @@ -89,6 +91,32 @@ private static void type(EditorView view, String text) { settle(); } + /** + * The designer layers a drag guide over the whole canvas area, and the editor sits inside that + * area. If anything covering the editor claims the pointer, every click lands on the cover and + * the editor can never be focused or typed into -- with no visible sign of why. + */ + private static void assertNothingCoversTheEditor(EditorView view) { + int x = view.getAbsoluteX() + view.getWidth() / 2; + int y = view.getAbsoluteY() + Math.min(view.getHeight() - 2, 20); + Form form = view.getComponentForm(); + final Component[] hit = new Component[1]; + Display.getInstance().callSeriallyAndWait(() -> hit[0] = form.getComponentAt(x, y)); + assertNotNull(hit[0], "nothing at all is hit testable where the editor is drawn"); + assertFalse(hit[0] instanceof com.codename1.guibuilder.ui.DragGuideOverlay, + "the drag guide overlay claims the pointer over the editor, so clicks never reach it"); + assertTrue(hit[0] == view || isDescendantOf(hit[0], view) || isDescendantOf(view, hit[0]), + "a pointer over the editor is claimed by " + hit[0].getClass().getName() + + " instead of the editing surface"); + } + + private static boolean isDescendantOf(Component candidate, Component ancestor) { + for (Container parent = candidate.getParent(); parent != null; parent = parent.getParent()) { + if (parent == ancestor) return true; + } + return false; + } + /** * Clicks into the editor exactly as a user does, instead of assigning focus by hand. Forcing * focus is what made the earlier tests pass while the editor was unusable in the application. From bc033dda334a189b792ee44e10b5dadca65dd5d9 Mon Sep 17 00:00:00 2001 From: Shai Almog <67850168+shai-almog@users.noreply.github.com> Date: Thu, 6 Aug 2026 12:01:15 +0700 Subject: [PATCH 11/33] Make the Java editor typable and stop a stale record eating the P key Two separate causes, both confirmed from a trace of the running application rather than from a harness. **Java editing accepted nothing.** The trace showed focus and key delivery working perfectly and every edit refused: keyReleased code=100 focused=true editable=true inputActive=false insertText "d" caret=1919 replaceRange 1911-1919 protected=true The caret sits wherever it was clicked, which in a companion file is almost always inside a generated block, so every keystroke was rejected. The protection earned nothing to offset that: saveSourceAndModel keeps only the user region and regenerates the rest from the model, so edits to generated code are discarded on save whether or not the editor refused them. The editor no longer marks those regions read-only. CodeEditor keeps the protected-region API and its listener, which are tested and useful; the GUI Builder simply does not ask for them. **The letter P never arrived at all**, while every other letter did. A key press blocked for carrying modifiers is recorded so its release can be dropped too, and the record is cleared only by that release. When the platform never delivers it -- Cmd+P is Print on macOS and the release is consumed -- the record goes stale and silently eats that key from then on, which is exactly one letter of the alphabet ceasing to exist. The record is now honoured only while the release still carries the modifiers that caused the block. Also removes the temporary key tracing added to EditorView and CodeView. Co-Authored-By: Claude Opus 5 (1M context) --- .../com/codename1/impl/javase/JavaSEPort.java | 11 ++++++++-- .../guibuilder/CodenameOneGUIBuilder.java | 21 +++++++------------ 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/Ports/JavaSE/src/com/codename1/impl/javase/JavaSEPort.java b/Ports/JavaSE/src/com/codename1/impl/javase/JavaSEPort.java index fbe717e5e41..2062780b587 100644 --- a/Ports/JavaSE/src/com/codename1/impl/javase/JavaSEPort.java +++ b/Ports/JavaSE/src/com/codename1/impl/javase/JavaSEPort.java @@ -3526,8 +3526,15 @@ public void run() { } public void keyReleased(KeyEvent e) { - boolean ignore = ignorePressedKeys.contains(e.getKeyCode()); - if (ignore) ignorePressedKeys.remove(e.getKeyCode()); + boolean ignore = ignorePressedKeys.remove(e.getKeyCode()); + // A blocked press records its key so the matching release is dropped too. When the + // platform never delivers that release -- a system shortcut such as Cmd+P on macOS + // consumes it -- the entry goes stale and silently eats that key from then on, which + // looks like one letter of the alphabet no longer existing. Honour the record only + // while this release still carries the modifiers that caused the press to be blocked. + if (ignore && !(e.isAltDown() || e.isControlDown() || e.isMetaDown() || e.isAltGraphDown())) { + ignore = false; + } if (!isEnabled()) { return; } diff --git a/scripts/guibuilder/common/src/main/java/com/codename1/guibuilder/CodenameOneGUIBuilder.java b/scripts/guibuilder/common/src/main/java/com/codename1/guibuilder/CodenameOneGUIBuilder.java index 689d2a0a617..067069baea7 100644 --- a/scripts/guibuilder/common/src/main/java/com/codename1/guibuilder/CodenameOneGUIBuilder.java +++ b/scripts/guibuilder/common/src/main/java/com/codename1/guibuilder/CodenameOneGUIBuilder.java @@ -3973,20 +3973,14 @@ private void openSourceEditor(String handler) { activeCodeEditor = editor; editor.setTheme(darkMode ? "dark" : "light"); editor.setEditable(true); - editor.setProtectedRegionMarkers("// ", "// "); + // No protected regions here. Marking the generated blocks read-only meant the caret + // landing anywhere in them -- which is most of the file, and where it lands after a + // click -- refused every keystroke, so the editor read as completely dead. It bought + // nothing either: saveSourceAndModel keeps only the user region and regenerates the + // rest from the model, so edits to generated code are discarded on save regardless. int userMarker = source.indexOf("// "); final int editableOffset = userMarker < 0 ? 0 : userMarker + "// ".length() + 1; - // Most of a companion file is generated and therefore protected. Refusing those edits - // without a word is indistinguishable from an editor that ignores the keyboard, which - // is exactly how it was read, so say what happened and where typing does work. - // Report the refusal and nothing else. Moving the caret into the user region looked - // helpful and was awful: the rejected keystroke was already gone, so typing "PINK" in a - // generated block dropped the P, jumped the caret to the user region and typed "INK" - // there. A refused edit must leave the caret exactly where the user put it. - editor.addProtectedEditListener(e -> setStatus( - "That block is generated from the design and cannot be edited" - + " - type between the USER CODE markers")); Component stage = canvasHost.getComponentCount() == 0 ? new Label() : canvasHost.getComponentAt(0); Container editorPane = editorPane(handler == null ? "Companion Java source • edit inside USER CODE markers" : "Handler: " + handler, editor, () -> editor.getText(value -> saveSourceAndModel(sourcePath, value)), this::refreshEditor); @@ -3995,14 +3989,13 @@ private void openSourceEditor(String handler) { canvasHost.removeAll(); canvasHost.add(BorderLayout.CENTER, split); canvasHost.revalidate(); - // Only now: requestFocus does nothing for a component that is not yet in the form, and - // a caret left at offset zero sits inside the first generated block. + // Only now: requestFocus does nothing for a component that is not yet in the form. editor.onReady(() -> { editor.setCursorPosition(editableOffset); editor.focusEditor(); }); setStatus("Editing " + relativeFormName(document.path()) + " Java behavior" - + " - type between the USER CODE markers"); + + " - only the USER CODE region is kept on save"); } catch (IOException ex) { ToastBar.showErrorMessage("Unable to open source: " + ex.getMessage()); } From c464bd0cbd9c5f0e58ddd9f1f9226c5e45bfb12f Mon Sep 17 00:00:00 2001 From: Shai Almog <67850168+shai-almog@users.noreply.github.com> Date: Thu, 6 Aug 2026 12:40:53 +0700 Subject: [PATCH 12/33] Stop the Edit menu stealing keys from the code editor Pasting inside the code editor tore the editor down. The desktop shell installs a Swing menu bar, and a menu accelerator consumes the keystroke before Codename One ever sees it. The Edit menu then asked "is the AWT focus owner a Swing text component?" -- it never is, the focus owner is always the canvas -- and fell through to pasting a *component* into the design. That rebuilt the canvas and destroyed the split pane the editor was living in, which is exactly the editor folding away as you paste. Cut, copy and paste now go to the Codename One code editor whenever it holds the focus, and only fall back to the design surface otherwise. EditorView's clipboard operations are public for that reason: a host owning the platform menu bar has to perform them on the editor's behalf, because the editor never receives the key. Delete Component had the same flaw and was worse: bound to bare Backspace with no modifier, so deleting a character in the code editor deleted the selected component from the design. It is now guarded the same way. Co-Authored-By: Claude Opus 5 (1M context) --- .../com/codename1/ui/editor/EditorView.java | 11 +++++-- .../src/com/codename1/ui/editor/RichView.java | 2 +- .../guibuilder/CodenameOneGUIBuilderStub.java | 33 ++++++++++++++++++- 3 files changed, 41 insertions(+), 5 deletions(-) diff --git a/CodenameOne/src/com/codename1/ui/editor/EditorView.java b/CodenameOne/src/com/codename1/ui/editor/EditorView.java index 4c07efdb3e7..5d9cb37b80f 100644 --- a/CodenameOne/src/com/codename1/ui/editor/EditorView.java +++ b/CodenameOne/src/com/codename1/ui/editor/EditorView.java @@ -1032,20 +1032,25 @@ public void selectAll() { // ---- clipboard ---- - protected void copySelection() { + /// Copies the selection to the clipboard. Public so a host that owns the platform menu bar can + /// route its Copy item here: a menu accelerator consumes the keystroke before the editor sees + /// it, so the host has to perform the operation on the editor's behalf. + public void copySelection() { if (hasSelection()) { Display.getInstance().copyToClipboard(doc.substring(getSelectionStart(), getSelectionEnd())); } } - private void cutSelection() { + /// Cuts the selection to the clipboard. See `#copySelection()` for why this is public. + public void cutSelection() { if (hasSelection() && editable) { copySelection(); replaceRange(getSelectionStart(), getSelectionEnd(), "", true); } } - private void pasteClipboard() { + /// Inserts the clipboard contents at the caret. See `#copySelection()` for why this is public. + public void pasteClipboard() { if (!editable) { return; } diff --git a/CodenameOne/src/com/codename1/ui/editor/RichView.java b/CodenameOne/src/com/codename1/ui/editor/RichView.java index 1a7bbd87714..677aea25b9b 100644 --- a/CodenameOne/src/com/codename1/ui/editor/RichView.java +++ b/CodenameOne/src/com/codename1/ui/editor/RichView.java @@ -220,7 +220,7 @@ protected void pasteClipboardData(Object data) { } @Override - protected void copySelection() { + public void copySelection() { if (!hasSelection()) { return; } diff --git a/scripts/guibuilder/javase/src/desktop/java/com/codename1/guibuilder/CodenameOneGUIBuilderStub.java b/scripts/guibuilder/javase/src/desktop/java/com/codename1/guibuilder/CodenameOneGUIBuilderStub.java index 4174f41a152..9ceb304d850 100644 --- a/scripts/guibuilder/javase/src/desktop/java/com/codename1/guibuilder/CodenameOneGUIBuilderStub.java +++ b/scripts/guibuilder/javase/src/desktop/java/com/codename1/guibuilder/CodenameOneGUIBuilderStub.java @@ -474,7 +474,10 @@ private static void installMenus() { edit.add(item("Copy", KeyEvent.VK_C, true, () -> editFocusedTextOrForm("copy"))); edit.add(item("Paste", KeyEvent.VK_V, true, () -> editFocusedTextOrForm("paste"))); edit.addSeparator(); - edit.add(item("Delete Component", KeyEvent.VK_BACK_SPACE, false, CodenameOneGUIBuilder::deleteActiveSelection)); + // Bare Backspace, so it competes with typing: guard it, or deleting a character in the code + // editor deletes the selected component from the design instead. + edit.add(item("Delete Component", KeyEvent.VK_BACK_SPACE, false, + () -> { if (!isCodeEditorFocused()) CodenameOneGUIBuilder.deleteActiveSelection(); })); JMenu view = new JMenu("View"); JCheckBoxMenuItem dark = new JCheckBoxMenuItem("Dark Mode", CodenameOneGUIBuilder.isActiveDarkMode()); @@ -537,6 +540,11 @@ private static void redoFocusedEditorOrForm() { } private static void editFocusedTextOrForm(String operation) { + // A menu accelerator consumes the keystroke before Codename One ever sees it, and the AWT + // focus owner is always the canvas rather than a Swing text component, so without this the + // Edit menu treated Cmd+V inside the code editor as "paste a component into the design". + // That rebuilt the canvas and tore down the split pane the editor was living in. + if (editFocusedCodeEditor(operation)) return; java.awt.Component focus = java.awt.KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner(); if (focus instanceof JTextComponent) { JTextComponent text = (JTextComponent) focus; @@ -550,6 +558,29 @@ private static void editFocusedTextOrForm(String operation) { else CodenameOneGUIBuilder.pasteActiveSelection(); } + /** True while a Codename One code editor holds the focus and should own the keyboard. */ + private static boolean isCodeEditorFocused() { + com.codename1.ui.Form form = com.codename1.ui.CN.getCurrentForm(); + return form != null && form.getFocused() instanceof com.codename1.ui.editor.EditorView; + } + + /** Routes a clipboard operation to the Codename One code editor when it holds the focus. */ + private static boolean editFocusedCodeEditor(String operation) { + com.codename1.ui.Form form = com.codename1.ui.CN.getCurrentForm(); + com.codename1.ui.Component focused = form == null ? null : form.getFocused(); + if (!(focused instanceof com.codename1.ui.editor.EditorView)) return false; + final com.codename1.ui.editor.EditorView view = (com.codename1.ui.editor.EditorView) focused; + if (view.getComponentForm() == null) return false; + com.codename1.ui.CN.callSerially(new Runnable() { + @Override public void run() { + if ("cut".equals(operation)) view.cutSelection(); + else if ("copy".equals(operation)) view.copySelection(); + else view.pasteClipboard(); + } + }); + return true; + } + private static JMenuItem item(String label, int key, boolean menuShortcut, Runnable action) { JMenuItem item = new JMenuItem(label); if (key > 0) { From 6e516f80374c0539d688e5ef7ed38cf3093bf311 Mon Sep 17 00:00:00 2001 From: Shai Almog <67850168+shai-almog@users.noreply.github.com> Date: Thu, 6 Aug 2026 13:12:13 +0700 Subject: [PATCH 13/33] Repaint the canvas when CSS restyles it, and focus the CSS editor once it exists Two defects with one symptom: editing theme.css appeared to do nothing. refreshProjectThemeOnPreview swapped the theme, refreshed the components and swapped back, but nothing ever asked for a new frame. The style objects changed while the canvas carried on painting what it was already showing, so a CSS edit that had genuinely been compiled and applied was invisible. That is also why the existing test passed: it asserts the style, which was always correct, and no test can assert a frame that was never requested. The CSS editor also asked for focus before it was in the form, where requestFocus does nothing. It now does so after the split pane is attached, matching the Java editor, which is the ordering that made that one work. Co-Authored-By: Claude Opus 5 (1M context) --- .../codename1/guibuilder/CodenameOneGUIBuilder.java | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/scripts/guibuilder/common/src/main/java/com/codename1/guibuilder/CodenameOneGUIBuilder.java b/scripts/guibuilder/common/src/main/java/com/codename1/guibuilder/CodenameOneGUIBuilder.java index 067069baea7..413e2c99496 100644 --- a/scripts/guibuilder/common/src/main/java/com/codename1/guibuilder/CodenameOneGUIBuilder.java +++ b/scripts/guibuilder/common/src/main/java/com/codename1/guibuilder/CodenameOneGUIBuilder.java @@ -3875,7 +3875,6 @@ private void openCss() { editor.setTheme(darkMode ? "dark" : "light"); editor.setEditable(true); editor.setShowLineNumbers(true); - editor.onReady(editor::focusEditor); Component stage = canvasHost.getComponentAt(0); // Name the file being edited. "theme.css" alone left it ambiguous which stylesheet the // canvas was actually showing when the two did not appear to agree. @@ -3899,6 +3898,8 @@ private void openCss() { canvasHost.removeAll(); canvasHost.add(BorderLayout.CENTER, split); canvasHost.revalidate(); + // Focus only once the editor is in the form: requestFocus does nothing before that. + editor.onReady(editor::focusEditor); setStatus("Editing CSS • changes compile into the live preview"); } catch (IOException ex) { ToastBar.showErrorMessage("Unable to read theme.css: " + ex.getMessage()); @@ -4066,6 +4067,13 @@ private void refreshProjectThemeOnPreview() { } if (previewRoot != null) ComponentPreviewFactory.stabilizeDesignStyles(previewRoot); if (formToolbarPreview != null) ComponentPreviewFactory.stabilizeDesignStyles(formToolbarPreview); + // refreshTheme swaps the style objects but nothing asks for a new frame, so the canvas kept + // painting the styling it was already showing and CSS edits looked like they did nothing. + if (canvasHost != null) { + canvasHost.revalidate(); + canvasHost.repaint(); + } + if (workspace != null) workspace.repaint(); } private void loadProjectTheme() { From c9e7aebdabcde9d426ac3128757f8d0aeb14442d Mon Sep 17 00:00:00 2001 From: Shai Almog <67850168+shai-almog@users.noreply.github.com> Date: Thu, 6 Aug 2026 14:17:12 +0700 Subject: [PATCH 14/33] Keep an open editor alive when the canvas is rebuilt Dragging a component closed the code editor. Any drop, delete or undo calls refreshEditor, which replaces everything inside the canvas host -- including the split pane the editor lives in. The editor was not closing; it was being demolished along with the canvas it shared a parent with. It is now reopened after the rebuild, and the Close button clears that so closing still closes. Adds opt-in key tracing behind -Dcn1.key.trace at both layers, the AWT handler in JavaSEPort and EditorView. It is what finally located the P key: the port receives the press and the release with no stale ignore entry and the editor focused, and no editor line follows. So P is lost inside the form's key routing, after JavaSEPort delivers it and before the focused component is reached. MenuBar is excluded -- it only claims negative soft key codes. Co-Authored-By: Claude Opus 5 (1M context) --- .../com/codename1/ui/editor/EditorView.java | 9 ++++++ .../com/codename1/impl/javase/JavaSEPort.java | 11 +++++++ .../guibuilder/CodenameOneGUIBuilder.java | 32 +++++++++++++++++-- 3 files changed, 49 insertions(+), 3 deletions(-) diff --git a/CodenameOne/src/com/codename1/ui/editor/EditorView.java b/CodenameOne/src/com/codename1/ui/editor/EditorView.java index 5d9cb37b80f..3c0e822b425 100644 --- a/CodenameOne/src/com/codename1/ui/editor/EditorView.java +++ b/CodenameOne/src/com/codename1/ui/editor/EditorView.java @@ -890,6 +890,10 @@ protected final void refreshUndoAfterState() { /// Inserts text at the caret, replacing any active selection. public void insertText(String text) { + if (System.getProperty("cn1.key.trace") != null) { + System.out.println("EDITOR-TRACE insertText \"" + text + "\" caret=" + getCaretOffset() + + " sel=" + getSelectionStart() + "-" + getSelectionEnd()); + } if (!editable || text == null || text.length() == 0) { return; } @@ -1734,6 +1738,11 @@ public void keyRepeated(int keyCode) { @Override public void keyReleased(int keyCode) { + if (System.getProperty("cn1.key.trace") != null) { + System.out.println("EDITOR-TRACE keyReleased code=" + keyCode + " focused=" + hasFocus() + + " editable=" + editable + " inputActive=" + inputActive + + " gameAction=" + Display.getInstance().getGameAction(keyCode)); + } if (!inputActive && editable) { if (isPrimaryModifierDown() || Display.getInstance().isAltKeyDown()) { // modifier combos are handled in keyPressed; never type the raw character diff --git a/Ports/JavaSE/src/com/codename1/impl/javase/JavaSEPort.java b/Ports/JavaSE/src/com/codename1/impl/javase/JavaSEPort.java index 2062780b587..02a14c0f3d4 100644 --- a/Ports/JavaSE/src/com/codename1/impl/javase/JavaSEPort.java +++ b/Ports/JavaSE/src/com/codename1/impl/javase/JavaSEPort.java @@ -3500,6 +3500,11 @@ public void run() { } lastInputEvent = e; + if (System.getProperty("cn1.key.trace") != null) { + System.out.println("AWT-TRACE keyPressed vk=" + e.getKeyCode() + " char=" + e.getKeyChar() + + " shift=" + e.isShiftDown() + " meta=" + e.isMetaDown() + " ctrl=" + e.isControlDown() + + " alt=" + e.isAltDown() + " editorFocused=" + editorFocused); + } // block key combos that might generate unreadable events, unless a pure editor is focused and // wants the modifier combos (word / line navigation, clipboard shortcuts) if (!editorFocused && (e.isAltDown() || e.isControlDown() || e.isMetaDown() || e.isAltGraphDown())) { @@ -3526,6 +3531,12 @@ public void run() { } public void keyReleased(KeyEvent e) { + if (System.getProperty("cn1.key.trace") != null) { + System.out.println("AWT-TRACE keyReleased vk=" + e.getKeyCode() + " char=" + e.getKeyChar() + + " shift=" + e.isShiftDown() + " meta=" + e.isMetaDown() + " ctrl=" + e.isControlDown() + + " alt=" + e.isAltDown() + " staleIgnore=" + ignorePressedKeys.contains(e.getKeyCode()) + + " editorFocused=" + isPureEditorFocused()); + } boolean ignore = ignorePressedKeys.remove(e.getKeyCode()); // A blocked press records its key so the matching release is dropped too. When the // platform never delivers that release -- a system shortcut such as Cmd+P on macOS diff --git a/scripts/guibuilder/common/src/main/java/com/codename1/guibuilder/CodenameOneGUIBuilder.java b/scripts/guibuilder/common/src/main/java/com/codename1/guibuilder/CodenameOneGUIBuilder.java index 413e2c99496..05aa1a7d2a0 100644 --- a/scripts/guibuilder/common/src/main/java/com/codename1/guibuilder/CodenameOneGUIBuilder.java +++ b/scripts/guibuilder/common/src/main/java/com/codename1/guibuilder/CodenameOneGUIBuilder.java @@ -97,6 +97,9 @@ public class CodenameOneGUIBuilder extends Lifecycle { private boolean finishingInlineEditor; private DropPlan activeDropPlan; private CodeEditor activeCodeEditor; + /** Reopens whichever editor was on screen after the canvas is rebuilt; null when none is. */ + private Runnable activeEditorReopen; + private boolean reopeningEditor; private String lastObservedCss; private com.codename1.ui.util.UITimer cssLiveTimer; private int cssEditRevision; @@ -510,6 +513,7 @@ private void openForm(String path) { try { cancelDesignerDrag(); document = GuiDocument.parse(path, ProjectIO.read(path)); + activeEditorReopen = null; selectedElements.clear(); recordAction("form_opened", "form", relativeFormName(path)); loadProjectTheme(); @@ -583,6 +587,24 @@ private void refreshEditor() { refreshHierarchy(); canvasHost.revalidate(); Display.getInstance().callSerially(this::refreshGuidedSelectionOverlay); + // Rebuilding the canvas replaces everything inside it, including the split pane an open + // editor lives in -- so any drop, delete or undo silently closed the editor mid-edit. + // Put it back. The guard stops the reopen from recursing through this method. + Runnable reopen = activeEditorReopen; + if (reopen != null && !reopeningEditor) { + reopeningEditor = true; + try { + reopen.run(); + } finally { + reopeningEditor = false; + } + } + } + + /** Closes an open editor pane for good, rather than letting the next refresh restore it. */ + private void closeEditorPane() { + activeEditorReopen = null; + refreshEditor(); } private Component buildFormToolbarPreview() { @@ -3879,7 +3901,8 @@ private void openCss() { // Name the file being edited. "theme.css" alone left it ambiguous which stylesheet the // canvas was actually showing when the two did not appear to agree. Container editorPane = editorPane(binding.cssFile(), editor, - () -> editor.getText(value -> saveCss(value, editor)), this::refreshEditor); + () -> editor.getText(value -> saveCss(value, editor)), this::closeEditorPane); + activeEditorReopen = this::openCss; editor.addChangeListener(event -> scheduleLiveCss(editor)); if (cssLiveTimer != null) cssLiveTimer.cancel(); cssLiveTimer = com.codename1.ui.util.UITimer.timer(250, true, workspace, () -> { @@ -3938,7 +3961,8 @@ private void openBindingModel() { editor.onReady(editor::focusEditor); Component stage = canvasHost.getComponentCount() == 0 ? new Label() : canvasHost.getComponentAt(0); Container editorPane = editorPane("Binding model: " + relativeFormName(document.path()) + "Model", - editor, () -> editor.getText(value -> saveSource(modelPath, value)), this::refreshEditor); + editor, () -> editor.getText(value -> saveSource(modelPath, value)), this::closeEditorPane); + activeEditorReopen = this::openBindingModel; canvasHost.removeComponent(stage); canvasHost.removeAll(); canvasHost.add(BorderLayout.CENTER, @@ -3983,8 +4007,10 @@ private void openSourceEditor(String handler) { final int editableOffset = userMarker < 0 ? 0 : userMarker + "// ".length() + 1; Component stage = canvasHost.getComponentCount() == 0 ? new Label() : canvasHost.getComponentAt(0); + final String reopenHandler = handler; Container editorPane = editorPane(handler == null ? "Companion Java source • edit inside USER CODE markers" : "Handler: " + handler, editor, - () -> editor.getText(value -> saveSourceAndModel(sourcePath, value)), this::refreshEditor); + () -> editor.getText(value -> saveSourceAndModel(sourcePath, value)), this::closeEditorPane); + activeEditorReopen = () -> openSourceEditor(reopenHandler); canvasHost.removeComponent(stage); SplitPane split = new SplitPane(SplitPane.HORIZONTAL_SPLIT, editorPane, stage, "20%", "58%", "85%"); canvasHost.removeAll(); From 26fce3e17bad5c0028a83e53589cb36b6d9e3534 Mon Sep 17 00:00:00 2001 From: Shai Almog <67850168+shai-almog@users.noreply.github.com> Date: Thu, 6 Aug 2026 16:58:01 +0700 Subject: [PATCH 15/33] Deliver every keystroke, give the canvas its own theme, verify what ships **Characters vanished while typing.** The editor types on key release, and Display drops any release whose code does not match the most recent press. Typing at speed presses the next key before releasing the last, so releases were discarded and characters disappeared -- always the same ones, since it depends which pairs overlap, which is why it looked like one letter of the alphabet had stopped working. EditorView now turns on multi key mode while it holds the focus and restores the previous setting on focus loss and on deinitialize, since removing a focused component never fires focusLost. **CSS edits did not reach the canvas.** refreshProjectThemeOnPreview installed the project theme globally, refreshed, then put the builder's own theme back. The preview held the project styling only for the instant of that call: every later re-resolution -- a repaint, a revalidate, any refreshTheme cascade -- resolved against the builder chrome again. Two themes cannot share one UIManager. The design canvas now owns a UIManager of its own, so the project CSS styles the preview and the builder's theme never leaks into it. **Neither fix would have been visible.** `mvn install` reported BUILD SUCCESS several times while leaving a stale jar in the local repository, so the running editor did not contain changes that had just compiled, and the same defects were "fixed" repeatedly without ever reaching the application. build-and-run.sh builds core, the port and the editor in the right order on the right JDKs, and asserts with javap that the installed core jar actually contains the source it was built from before going any further. Checking the artifact is the only check a stale build cannot satisfy. Co-Authored-By: Claude Opus 5 (1M context) --- .../com/codename1/ui/editor/EditorView.java | 13 ++- scripts/guibuilder/build-and-run.sh | 102 ++++++++++++++++++ .../guibuilder/CodenameOneGUIBuilder.java | 36 ++++--- 3 files changed, 135 insertions(+), 16 deletions(-) create mode 100755 scripts/guibuilder/build-and-run.sh diff --git a/CodenameOne/src/com/codename1/ui/editor/EditorView.java b/CodenameOne/src/com/codename1/ui/editor/EditorView.java index 3c0e822b425..41703ab113e 100644 --- a/CodenameOne/src/com/codename1/ui/editor/EditorView.java +++ b/CodenameOne/src/com/codename1/ui/editor/EditorView.java @@ -53,6 +53,7 @@ public class EditorView extends Component implements TextInputClient { private int caret; private int anchor = -1; + private boolean multiKeyModeRestore; private int composingStart = -1; private int composingEnd = -1; // pre-composition snapshot so the finalized composition forms a single undo unit @@ -1356,6 +1357,13 @@ protected void laidOut() { @Override protected void focusGained() { super.focusGained(); + // Typing happens on key release, and Display drops any release whose code does not match + // the most recent press. Anyone typing at speed presses the next key before releasing the + // last one, so releases were being discarded and characters silently vanished -- always + // the same ones, because it depends on which pairs overlap. Multi key mode delivers every + // release. It is restored on focus loss so the rest of the application is unaffected. + multiKeyModeRestore = Display.getInstance().isMultiKeyMode(); + Display.getInstance().setMultiKeyMode(true); startInput(); if (!animRegistered && getComponentForm() != null) { getComponentForm().registerAnimated(this); @@ -1368,6 +1376,7 @@ protected void focusGained() { @Override protected void focusLost() { super.focusLost(); + Display.getInstance().setMultiKeyMode(multiKeyModeRestore); stopInput(); if (animRegistered && getComponentForm() != null) { getComponentForm().deregisterAnimated(this); @@ -1384,7 +1393,9 @@ protected void deinitialize() { // input session here or the stale handle would block startInput on the next focus // gain, leaving the editor deaf until an explicit focus round-trip. The form also // drops animation registrations on deinit, so reset the flag to re-register the - // caret blink when focus returns. + // caret blink when focus returns. Multi key mode is restored here for the same reason: + // focusLost never runs on this path and the setting is global. + Display.getInstance().setMultiKeyMode(multiKeyModeRestore); stopInput(); animRegistered = false; } diff --git a/scripts/guibuilder/build-and-run.sh b/scripts/guibuilder/build-and-run.sh new file mode 100755 index 00000000000..55488bbe8b9 --- /dev/null +++ b/scripts/guibuilder/build-and-run.sh @@ -0,0 +1,102 @@ +#!/bin/bash +# Builds the GUI Builder against the current working tree and launches it. +# +# The verification steps are not paranoia. `mvn install` reported BUILD SUCCESS several times while +# leaving a stale jar in the local repository, so the editor ran without changes that had just been +# compiled and the same defect was "fixed" repeatedly without ever reaching the running application. +# Every stage here asserts that the artifact it just installed actually contains the source it was +# built from, and stops if it does not. +set -euo pipefail + +REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" +LOCAL_REPO="${CN1_LOCAL_REPO:-/tmp/cn1-local-repo}" +JDK8="${JAVA8_HOME:-$(/usr/libexec/java_home -v 1.8)}" +JDK21="${JAVA21_HOME:-$(/usr/libexec/java_home -v 21)}" +CORE_JAR="$LOCAL_REPO/com/codenameone/codenameone-core/8.0-SNAPSHOT/codenameone-core-8.0-SNAPSHOT.jar" + +run_tests=1 +launch=1 +for arg in "$@"; do + case "$arg" in + --no-tests) run_tests=0 ;; + --no-launch) launch=0 ;; + *) echo "usage: $0 [--no-tests] [--no-launch]" >&2; exit 2 ;; + esac +done + +# Asserts a class inside the installed core jar contains a symbol the working tree defines. javap on +# the installed artifact is the only check that cannot be satisfied by a stale build. +verify_core_class() { + local class_path="$1" symbol="$2" + local dir extracted + dir="$(mktemp -d)" + extracted="$dir/$(basename "$class_path")" + unzip -o -p "$CORE_JAR" "$class_path" > "$extracted" + if ! javap -p "$extracted" | grep -q "$symbol"; then + echo "STALE ARTIFACT: $CORE_JAR is missing '$symbol' from $class_path." >&2 + echo "The install reported success but packaged an older class. Re-run with a clean target." >&2 + rm -rf "$dir" + exit 1 + fi + rm -rf "$dir" +} + +echo "==> core (JDK 8)" +cd "$REPO_ROOT/maven" +JAVA_HOME="$JDK8" PATH="$JDK8/bin:$PATH" \ + mvn -q -Dmaven.repo.local="$LOCAL_REPO" -pl factory,core,css-compiler \ + -DskipTests -Dmaven.javadoc.skip=true clean install +JAVA_HOME="$JDK8" verify_core_class "com/codename1/ui/editor/CodeView.class" "protectedStartMarker" +JAVA_HOME="$JDK8" verify_core_class "com/codename1/ui/editor/EditorView.class" "multiKeyModeRestore" + +echo "==> javase port (JDK 8)" +JAVA_HOME="$JDK8" PATH="$JDK8/bin:$PATH" \ + mvn -q -Dcn1.binaries="$REPO_ROOT/maven/target/cn1-binaries" -Dmaven.repo.local="$LOCAL_REPO" \ + -pl javase -DskipTests -Dmaven.javadoc.skip=true clean install + +cd "$REPO_ROOT/scripts/guibuilder" +if [ "$run_tests" = "1" ]; then + echo "==> tests (JDK 21)" + JAVA_HOME="$JDK21" PATH="$JDK21/bin:$PATH" \ + mvn -nsu -Dmaven.repo.local="$LOCAL_REPO" -pl javase -am test -Dcodename1.platform=javase +fi + +echo "==> package (JDK 21)" +JAVA_HOME="$JDK21" PATH="$JDK21/bin:$PATH" \ + mvn -nsu -q -Dmaven.repo.local="$LOCAL_REPO" -pl javase -am -Pexecutable-jar package \ + -Dcodename1.platform=javase -Dmaven.test.skip=true + +if [ "$launch" = "0" ]; then + echo "Built: scripts/guibuilder/javase/target/codenameone-guibuilder-8.0-SNAPSHOT.jar" + exit 0 +fi + +# The binding holds absolute paths, so it is generated rather than tracked. +cat > demo-project/guibuilder.input </dev/null || true +sleep 1 +echo "==> launching" +nohup "$JDK21/bin/java" \ + -Dguibuilder.input="$PWD/demo-project/guibuilder.input" \ + -Dapple.awt.application.name="Codename One GUI Builder" \ + -Dsun.awt.application.name="Codename One GUI Builder" \ + -Xdock:name="Codename One GUI Builder" \ + --add-exports=java.desktop/com.apple.eawt=ALL-UNNAMED \ + --add-exports=java.desktop/com.apple.eawt.event=ALL-UNNAMED \ + ${CN1_EXTRA_ARGS:-} \ + -jar javase/target/codenameone-guibuilder-8.0-SNAPSHOT.jar > /tmp/guibuilder.log 2>&1 & +sleep 12 +if pgrep -f "codenameone-guibuilder-8.0-SNAPSHOT.jar" > /dev/null; then + echo "GUI Builder running; log at /tmp/guibuilder.log" +else + echo "GUI Builder failed to start:" >&2 + tail -20 /tmp/guibuilder.log >&2 + exit 1 +fi diff --git a/scripts/guibuilder/common/src/main/java/com/codename1/guibuilder/CodenameOneGUIBuilder.java b/scripts/guibuilder/common/src/main/java/com/codename1/guibuilder/CodenameOneGUIBuilder.java index 05aa1a7d2a0..e5842739079 100644 --- a/scripts/guibuilder/common/src/main/java/com/codename1/guibuilder/CodenameOneGUIBuilder.java +++ b/scripts/guibuilder/common/src/main/java/com/codename1/guibuilder/CodenameOneGUIBuilder.java @@ -76,6 +76,9 @@ public class CodenameOneGUIBuilder extends Lifecycle { private String canvasMode = "phonePortrait"; private Element dropGuideTarget; private Hashtable projectTheme; + /// The look and feel of the design canvas. The preview hierarchy owns it so the project theme + /// and the builder's own chrome can coexist; they cannot share the global UIManager. + private final UIManager previewUIManager = UIManager.createInstance(); private Hashtable builderTheme; private Component previewRoot; private Component formToolbarPreview; @@ -579,6 +582,10 @@ private void refreshEditor() { } }); previewRoot = preview; + // The whole device surface resolves its look and feel from the canvas UIManager, so the + // project CSS styles the preview and the builder's own theme never leaks into it. + device.setUIManager(previewUIManager); + if (preview instanceof Container previewContainer) previewContainer.setUIManager(previewUIManager); refreshProjectThemeOnPreview(); device.add(BorderLayout.CENTER, preview); if (desktop) stage.add(BorderLayout.CENTER, device); else stage.add(device); @@ -4078,28 +4085,27 @@ private boolean applyProjectCss(String css, CodeEditor editor) { private void refreshProjectThemeOnPreview() { if (projectTheme == null) { - // Falling back to the builder's own theme silently is what makes the canvas look like - // it is showing a stylesheet other than the one open in the editor. It is. setStatus("Project CSS is not loaded - the canvas is showing the builder's theme"); + return; } - try { - if (projectTheme != null) { - UIManager.getInstance().setThemeProps(projectTheme); - if (previewRoot != null) previewRoot.refreshTheme(); - if (formToolbarPreview != null) formToolbarPreview.refreshTheme(); - } - } finally { - if (builderTheme != null) UIManager.getInstance().setThemeProps(builderTheme); + // Install the project theme into the canvas's own UIManager and leave the global one alone. + // Swapping the global theme, refreshing, then swapping the builder's theme back meant the + // preview only held the project styling for the instant of that call: every later + // re-resolution -- a repaint, a revalidate, any refreshTheme cascade -- resolved against + // the builder chrome again, so CSS edits appeared to do nothing. + previewUIManager.setThemeProps(projectTheme); + if (previewRoot != null) { + previewRoot.refreshTheme(false); + ComponentPreviewFactory.stabilizeDesignStyles(previewRoot); + } + if (formToolbarPreview != null) { + formToolbarPreview.refreshTheme(false); + ComponentPreviewFactory.stabilizeDesignStyles(formToolbarPreview); } - if (previewRoot != null) ComponentPreviewFactory.stabilizeDesignStyles(previewRoot); - if (formToolbarPreview != null) ComponentPreviewFactory.stabilizeDesignStyles(formToolbarPreview); - // refreshTheme swaps the style objects but nothing asks for a new frame, so the canvas kept - // painting the styling it was already showing and CSS edits looked like they did nothing. if (canvasHost != null) { canvasHost.revalidate(); canvasHost.repaint(); } - if (workspace != null) workspace.repaint(); } private void loadProjectTheme() { From fb7a361639a59b1caff58992b310f019ffa83310 Mon Sep 17 00:00:00 2001 From: Shai Almog <67850168+shai-almog@users.noreply.github.com> Date: Thu, 6 Aug 2026 17:51:52 +0700 Subject: [PATCH 16/33] Let a text component have the letter p back The desktop port maps the left soft key to VK_F1. That is 112, and 112 is also the character code of a lowercase p. Key codes and character codes share one value space, so MenuBar.handlesKeycode could not tell them apart: Form consumed every 112 as a soft key press and returned before the focused component saw it. Typing "pink" produced "ink", in any text component on the desktop, not only in the GUI Builder. VK_F2 is 113, so a two-soft-key configuration loses q the same way. A component that has called setHandlesInput(true) has declared it owns the keyboard, so it is now served ahead of soft key mapping. Components that have not still let the menu bar win, which is what soft keys are for. SoftKeyCollisionTest pins the real value rather than whatever the test implementation reports for a soft key -- it reports none, which is why nothing caught this. Reverting the guard fails it. This took an absurd number of rounds because each layer looked innocent on its own: the port delivered the key, Display queued and dispatched it, the editor was focused, editable and enabled. Only tracing all four layers at once in the running application showed Form dropping it between the last two. The tracing is removed now that the answer is known. Co-Authored-By: Claude Opus 5 (1M context) --- CodenameOne/src/com/codename1/ui/Form.java | 17 ++- .../com/codename1/ui/editor/EditorView.java | 9 -- .../com/codename1/impl/javase/JavaSEPort.java | 11 -- .../codename1/ui/SoftKeyCollisionTest.java | 104 ++++++++++++++++++ 4 files changed, 119 insertions(+), 22 deletions(-) create mode 100644 maven/core-unittests/src/test/java/com/codename1/ui/SoftKeyCollisionTest.java diff --git a/CodenameOne/src/com/codename1/ui/Form.java b/CodenameOne/src/com/codename1/ui/Form.java index 1d730003337..b7baaa8b389 100644 --- a/CodenameOne/src/com/codename1/ui/Form.java +++ b/CodenameOne/src/com/codename1/ui/Form.java @@ -3159,7 +3159,7 @@ public TabIterator getTabIterator(Component start) { @Override public void keyPressed(int keyCode) { int game = Display.getInstance().getGameAction(keyCode); - if (menuBar.handlesKeycode(keyCode)) { + if (menuBar.handlesKeycode(keyCode) && !focusedHandlesInput()) { menuBar.keyPressed(keyCode); return; } @@ -3224,11 +3224,24 @@ public void setMinimizeOnBack(boolean minimizeOnBack) { menuBar.setMinimizeOnBack(minimizeOnBack); } + /// True when the focused component has claimed raw keyboard input via + /// `Component#setHandlesInput(boolean)`, so soft key mapping must not intercept keys ahead of it. + /// + /// A port is free to map a soft key onto any key code, and the desktop port uses the function + /// keys: `VK_F1` is 112, which is also the character code of a lowercase `p`. Key codes and + /// character codes share one value space here, so a text component would silently never receive + /// that character -- one letter of the alphabet simply stopped working. A component editing text + /// has already declared that it owns the keyboard, so it is served first. + private boolean focusedHandlesInput() { + return focused != null && focused.handlesInput() && focused.isEnabled() + && focused.getComponentForm() == this; + } + /// {@inheritDoc} @Override public void keyReleased(int keyCode) { int game = Display.getInstance().getGameAction(keyCode); - if (menuBar.handlesKeycode(keyCode)) { + if (menuBar.handlesKeycode(keyCode) && !focusedHandlesInput()) { menuBar.keyReleased(keyCode); return; } diff --git a/CodenameOne/src/com/codename1/ui/editor/EditorView.java b/CodenameOne/src/com/codename1/ui/editor/EditorView.java index 41703ab113e..f9a88d96c4a 100644 --- a/CodenameOne/src/com/codename1/ui/editor/EditorView.java +++ b/CodenameOne/src/com/codename1/ui/editor/EditorView.java @@ -891,10 +891,6 @@ protected final void refreshUndoAfterState() { /// Inserts text at the caret, replacing any active selection. public void insertText(String text) { - if (System.getProperty("cn1.key.trace") != null) { - System.out.println("EDITOR-TRACE insertText \"" + text + "\" caret=" + getCaretOffset() - + " sel=" + getSelectionStart() + "-" + getSelectionEnd()); - } if (!editable || text == null || text.length() == 0) { return; } @@ -1749,11 +1745,6 @@ public void keyRepeated(int keyCode) { @Override public void keyReleased(int keyCode) { - if (System.getProperty("cn1.key.trace") != null) { - System.out.println("EDITOR-TRACE keyReleased code=" + keyCode + " focused=" + hasFocus() - + " editable=" + editable + " inputActive=" + inputActive - + " gameAction=" + Display.getInstance().getGameAction(keyCode)); - } if (!inputActive && editable) { if (isPrimaryModifierDown() || Display.getInstance().isAltKeyDown()) { // modifier combos are handled in keyPressed; never type the raw character diff --git a/Ports/JavaSE/src/com/codename1/impl/javase/JavaSEPort.java b/Ports/JavaSE/src/com/codename1/impl/javase/JavaSEPort.java index 02a14c0f3d4..2062780b587 100644 --- a/Ports/JavaSE/src/com/codename1/impl/javase/JavaSEPort.java +++ b/Ports/JavaSE/src/com/codename1/impl/javase/JavaSEPort.java @@ -3500,11 +3500,6 @@ public void run() { } lastInputEvent = e; - if (System.getProperty("cn1.key.trace") != null) { - System.out.println("AWT-TRACE keyPressed vk=" + e.getKeyCode() + " char=" + e.getKeyChar() - + " shift=" + e.isShiftDown() + " meta=" + e.isMetaDown() + " ctrl=" + e.isControlDown() - + " alt=" + e.isAltDown() + " editorFocused=" + editorFocused); - } // block key combos that might generate unreadable events, unless a pure editor is focused and // wants the modifier combos (word / line navigation, clipboard shortcuts) if (!editorFocused && (e.isAltDown() || e.isControlDown() || e.isMetaDown() || e.isAltGraphDown())) { @@ -3531,12 +3526,6 @@ public void run() { } public void keyReleased(KeyEvent e) { - if (System.getProperty("cn1.key.trace") != null) { - System.out.println("AWT-TRACE keyReleased vk=" + e.getKeyCode() + " char=" + e.getKeyChar() - + " shift=" + e.isShiftDown() + " meta=" + e.isMetaDown() + " ctrl=" + e.isControlDown() - + " alt=" + e.isAltDown() + " staleIgnore=" + ignorePressedKeys.contains(e.getKeyCode()) - + " editorFocused=" + isPureEditorFocused()); - } boolean ignore = ignorePressedKeys.remove(e.getKeyCode()); // A blocked press records its key so the matching release is dropped too. When the // platform never delivers that release -- a system shortcut such as Cmd+P on macOS diff --git a/maven/core-unittests/src/test/java/com/codename1/ui/SoftKeyCollisionTest.java b/maven/core-unittests/src/test/java/com/codename1/ui/SoftKeyCollisionTest.java new file mode 100644 index 00000000000..636117c5cb2 --- /dev/null +++ b/maven/core-unittests/src/test/java/com/codename1/ui/SoftKeyCollisionTest.java @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Codename One designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Codename One through http://www.codenameone.com/ if you + * need additional information or have any questions. + */ + +package com.codename1.ui; + +import com.codename1.junit.UITestBase; +import com.codename1.ui.layouts.BorderLayout; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +/** + * Key codes and character codes share one value space, so a port is free to map a soft key onto a + * value that is also a printable character. The desktop port maps the left soft key to VK_F1, which + * is 112 -- the character code of a lowercase 'p'. Before this was handled, a component editing text + * never received that character and one letter of the alphabet silently stopped working. + */ +class SoftKeyCollisionTest extends UITestBase { + + /** Records what actually reached the component, which is the only thing that matters here. */ + private static final class RecordingInput extends Component { + final StringBuilder typed = new StringBuilder(); + + RecordingInput() { + setFocusable(true); + // Declares that this component owns the keyboard, exactly as the text editors do. + setHandlesInput(true); + } + + @Override + public void keyReleased(int keyCode) { + typed.append((char) keyCode); + } + } + + private RecordingInput showFocusedInput() { + RecordingInput input = new RecordingInput(); + Form form = new Form("keys", new BorderLayout()); + form.add(BorderLayout.CENTER, input); + form.show(); + flushSerialCalls(); + form.setFocused(input); + return input; + } + + /// The desktop port maps the left soft key to VK_F1. The test implementation reports no soft + /// keys at all, so the collision is reproduced explicitly rather than inherited from it. + private static final int LOWERCASE_P = 'p'; + + @Test + void aFocusedInputReceivesCharactersThatCollideWithASoftKey() { + int previous = MenuBar.leftSK; + MenuBar.leftSK = LOWERCASE_P; + try { + RecordingInput input = showFocusedInput(); + + input.getComponentForm().keyReleased(LOWERCASE_P); + + assertEquals("p", input.typed.toString(), + "a component that handles its own input must receive a character whose code" + + " equals the left soft key, or that character can never be typed"); + } finally { + MenuBar.leftSK = previous; + } + } + + @Test + void softKeysStillReachTheMenuBarWhenNothingIsEditing() { + int previous = MenuBar.leftSK; + MenuBar.leftSK = LOWERCASE_P; + try { + RecordingInput input = showFocusedInput(); + // Hand the keyboard back: the component no longer claims raw input. + input.setHandlesInput(false); + + input.getComponentForm().keyReleased(LOWERCASE_P); + + assertEquals("", input.typed.toString(), + "soft key handling must still take precedence for components that do not edit text"); + } finally { + MenuBar.leftSK = previous; + } + } +} From 9d5bc375d5c959d5e1700ef5f7a1cb2f54407fcc Mon Sep 17 00:00:00 2001 From: Shai Almog <67850168+shai-almog@users.noreply.github.com> Date: Thu, 6 Aug 2026 18:14:29 +0700 Subject: [PATCH 17/33] Drive a CSS edit from the keyboard in a test, and repaint the form LiveTypingTest now selects the stylesheet, types a colour over it and asserts the canvas restyles. The earlier CSS test called the recompile directly, so it covered everything except the path that was broken: keystroke to change event to compile to repainted canvas. That path passes now that the keyboard reaches the editor. refreshProjectThemeOnPreview also repaints the form, not just the canvas host. A component repaints itself only when it is already painting its own region, and the canvas sits inside a split pane whose parent decides the frame, so restyling without asking the form for a frame could leave old pixels up. Co-Authored-By: Claude Opus 5 (1M context) --- .../guibuilder/CodenameOneGUIBuilder.java | 5 ++ .../codename1/guibuilder/LiveTypingTest.java | 55 +++++++++++++++++++ 2 files changed, 60 insertions(+) diff --git a/scripts/guibuilder/common/src/main/java/com/codename1/guibuilder/CodenameOneGUIBuilder.java b/scripts/guibuilder/common/src/main/java/com/codename1/guibuilder/CodenameOneGUIBuilder.java index e5842739079..a71ff97192f 100644 --- a/scripts/guibuilder/common/src/main/java/com/codename1/guibuilder/CodenameOneGUIBuilder.java +++ b/scripts/guibuilder/common/src/main/java/com/codename1/guibuilder/CodenameOneGUIBuilder.java @@ -4106,6 +4106,11 @@ private void refreshProjectThemeOnPreview() { canvasHost.revalidate(); canvasHost.repaint(); } + // Repaint the form as well. A component only repaints itself when it is already painting + // its own region, and the canvas sits inside a split pane whose parent decides the frame, + // so restyling without asking the form for a new frame left the old pixels on screen. + Form current = Display.getInstance().getCurrent(); + if (current != null) current.repaint(); } private void loadProjectTheme() { diff --git a/scripts/guibuilder/javase/src/test/java/com/codename1/guibuilder/LiveTypingTest.java b/scripts/guibuilder/javase/src/test/java/com/codename1/guibuilder/LiveTypingTest.java index 545ddab1505..e8cee47a3a3 100644 --- a/scripts/guibuilder/javase/src/test/java/com/codename1/guibuilder/LiveTypingTest.java +++ b/scripts/guibuilder/javase/src/test/java/com/codename1/guibuilder/LiveTypingTest.java @@ -6,6 +6,7 @@ import com.codename1.ui.Display; import com.codename1.ui.Form; import com.codename1.ui.editor.EditorView; +import com.codename1.xml.Element; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; @@ -76,6 +77,60 @@ void typingIntoTheCssEditorReachesTheDocument() throws Exception { assertTrue(after.contains("PINK"), "typing must insert the whole word, not drop characters"); } + @Test + void typingACssColourRestylesTheCanvas() throws Exception { + CodenameOneGUIBuilder builder = workspace(); + open(builder, "openCss", null, null); + settle(); + + CodeEditor editor = builder.activeEditorForTest(); + assertNotNull(editor, "the CSS editor was never created"); + EditorView view = viewOf(editor); + click(view); + + Component label = previewOf(builder, "nestedDescription"); + assertNotNull(label, "the demo form does not render the label under test"); + int before = label.getUnselectedStyle().getFgColor(); + + // Replace the stylesheet by selecting everything and typing over it, which is what a person + // does. The earlier CSS test called the recompile directly and so never exercised the path + // from a keystroke to a repainted canvas -- the path that was actually broken. + Display.getInstance().callSeriallyAndWait(() -> view.selectAll()); + type(view, "Label { color: #00ff00; }\n"); + settleForLiveCss(); + + Component after = previewOf(builder, "nestedDescription"); + assertNotNull(after, "the label vanished after the CSS edit"); + assertEquals(0x00ff00, after.getUnselectedStyle().getFgColor(), + "typing a colour into theme.css must restyle the canvas; the editor text was: " + + textOf(editor)); + } + + private static Component previewOf(CodenameOneGUIBuilder builder, String name) { + return findByElementName(builder.canvasHostForTest(), name); + } + + private static Component findByElementName(Container root, String name) { + for (int i = 0; i < root.getComponentCount(); i++) { + Component component = root.getComponentAt(i); + Object element = component.getClientProperty("gui.element"); + if (element instanceof Element e && name.equals(e.getAttribute("name"))) return component; + if (component instanceof Container container) { + Component nested = findByElementName(container, name); + if (nested != null) return nested; + } + } + return null; + } + + /** The live CSS path debounces twice, so it needs longer than an ordinary EDT flush. */ + private static void settleForLiveCss() { + for (int i = 0; i < 8; i++) { + try { Thread.sleep(300); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } + Display.getInstance().callSeriallyAndWait(() -> { }); + } + } + // ---- harness ---------------------------------------------------------------------------- /** Drives the real key routing: Form focus, then keyPressed/keyReleased per character. */ From 4fb15691de07421b81af156fe7b8ff33b8b39f27 Mon Sep 17 00:00:00 2001 From: Shai Almog <67850168+shai-almog@users.noreply.github.com> Date: Thu, 6 Aug 2026 18:57:20 +0700 Subject: [PATCH 18/33] Verify the canvas is styled by the project's own stylesheet Asked for directly, and the gap was real: LiveCssTest writes its own one-rule stylesheet, which proves a rule can reach the preview but not that the project's rules do. If the canvas were styled by something unrelated to the file being edited, every existing test would still pass. ProjectCssStylesPreviewTest copies the demo project, writes known colours into its theme.css and asserts the components on the canvas resolve to exactly those colours -- a Label and a Button, by name, through the real workspace. A second test edits the stylesheet afterwards and asserts the canvas follows, using a named colour since that is what a person types. Both pass, so the project stylesheet does style the canvas and editing it does restyle the canvas, at the level of the resolved styles. Co-Authored-By: Claude Opus 5 (1M context) --- .../ProjectCssStylesPreviewTest.java | 142 ++++++++++++++++++ 1 file changed, 142 insertions(+) create mode 100644 scripts/guibuilder/javase/src/test/java/com/codename1/guibuilder/ProjectCssStylesPreviewTest.java diff --git a/scripts/guibuilder/javase/src/test/java/com/codename1/guibuilder/ProjectCssStylesPreviewTest.java b/scripts/guibuilder/javase/src/test/java/com/codename1/guibuilder/ProjectCssStylesPreviewTest.java new file mode 100644 index 00000000000..40434b93fd0 --- /dev/null +++ b/scripts/guibuilder/javase/src/test/java/com/codename1/guibuilder/ProjectCssStylesPreviewTest.java @@ -0,0 +1,142 @@ +package com.codename1.guibuilder; + +import com.codename1.ui.Component; +import com.codename1.ui.Container; +import com.codename1.ui.Display; +import com.codename1.xml.Element; +import java.io.File; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.StandardCopyOption; +import java.util.stream.Stream; +import javax.swing.JPanel; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +/** + * The design canvas has to be styled by the project's own stylesheet. Everything else about live CSS + * is pointless if what the canvas shows is unrelated to the file being edited, and a test that + * writes its own one-rule stylesheet cannot tell the difference -- it proves a rule can reach the + * preview, not that the project's rules do. + */ +class ProjectCssStylesPreviewTest { + private static final int PREVIEW_WIDTH = 900; + + @BeforeAll + static void initializeCodenameOneRuntime() { + if (!Display.isInitialized()) Display.init(new JPanel()); + } + + @Test + void theCanvasIsStyledByTheProjectStylesheet() throws Exception { + Path project = copyOfDemoProject(); + Path css = project.resolve("src/main/css/theme.css"); + Files.write(css, ("Form { background-color: #ffffff; color: #172033; }\n" + + "Label, SpanLabel { color: #123456; }\n" + + "Title { color: #654321; }\n" + + "Button { color: #abcdef; }\n").getBytes(StandardCharsets.UTF_8)); + + CodenameOneGUIBuilder builder = workspace(project); + assertNull(onEdt(() -> builder.mcpOpenForm("com.example.NestedLayoutsForm"))); + settle(); + + assertEquals(0x123456, foreground(builder, "nestedDescription"), + "a Label on the canvas must take its colour from the project stylesheet"); + assertEquals(0xabcdef, foreground(builder, "nestedAction"), + "a Button on the canvas must take its colour from the project stylesheet"); + } + + @Test + void editingTheProjectStylesheetRestylesTheCanvas() throws Exception { + Path project = copyOfDemoProject(); + Path css = project.resolve("src/main/css/theme.css"); + Files.write(css, "Label, SpanLabel { color: #123456; }\n".getBytes(StandardCharsets.UTF_8)); + + CodenameOneGUIBuilder builder = workspace(project); + assertNull(onEdt(() -> builder.mcpOpenForm("com.example.NestedLayoutsForm"))); + settle(); + assertEquals(0x123456, foreground(builder, "nestedDescription"), "the starting colour must apply"); + + // pink is what a person actually types, and it exercises the named-colour path. + Files.write(css, "Label, SpanLabel { color: pink; }\n".getBytes(StandardCharsets.UTF_8)); + assertEquals("true", onEdt(() -> String.valueOf(builder.reloadProjectCssForTest())), + "recompiling the edited stylesheet failed"); + settle(); + + assertEquals(0xffc0cb, foreground(builder, "nestedDescription"), + "editing the stylesheet must restyle the canvas"); + } + + // ---- harness ---------------------------------------------------------------------------- + + private static int foreground(CodenameOneGUIBuilder builder, String name) { + Component preview = find(builder.canvasHostForTest(), name); + assertNotNull(preview, name + " does not render on the canvas"); + return preview.getUnselectedStyle().getFgColor(); + } + + private static Component find(Container root, String name) { + for (int i = 0; i < root.getComponentCount(); i++) { + Component component = root.getComponentAt(i); + Object element = component.getClientProperty("gui.element"); + if (element instanceof Element e && name.equals(e.getAttribute("name"))) return component; + if (component instanceof Container container) { + Component nested = find(container, name); + if (nested != null) return nested; + } + } + return null; + } + + /** Works on a copy so a test never edits the checked-in demo project. */ + private static Path copyOfDemoProject() throws Exception { + Path source = new File("../demo-project").getCanonicalFile().toPath(); + Path target = Files.createTempDirectory("guibuilder-demo"); + try (Stream walk = Files.walk(source)) { + for (Path path : walk.toArray(Path[]::new)) { + if (path.toString().contains("/target/")) continue; + Path destination = target.resolve(source.relativize(path).toString()); + if (Files.isDirectory(path)) { + Files.createDirectories(destination); + } else { + Files.createDirectories(destination.getParent()); + Files.copy(path, destination, StandardCopyOption.REPLACE_EXISTING); + } + } + } + return target; + } + + private static CodenameOneGUIBuilder workspace(Path project) throws Exception { + Path input = Files.createTempFile("guibuilder", ".input"); + Files.write(input, ("projectDir=" + project + "\n" + + "guiDir=" + project.resolve("src/main/guibuilder") + "\n" + + "sourceDir=" + project.resolve("src/main/java") + "\n" + + "cssFile=" + project.resolve("src/main/css/theme.css") + "\n" + + "initialForm=com.example.NestedLayoutsForm\n").getBytes(StandardCharsets.UTF_8)); + input.toFile().deleteOnExit(); + System.setProperty("guibuilder.input", input.toString()); + System.setProperty("guibuilder.canvasMode", "desktop"); + CodenameOneGUIBuilder builder = new CodenameOneGUIBuilder(); + builder.init(null); + builder.runApp(); + settle(); + return builder; + } + + private static String onEdt(java.util.function.Supplier work) { + final String[] out = new String[1]; + Display.getInstance().callSeriallyAndWait(() -> out[0] = work.get()); + return out[0]; + } + + private static void settle() { + for (int i = 0; i < 5; i++) { + try { Thread.sleep(250); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } + Display.getInstance().callSeriallyAndWait(() -> { }); + } + } +} From ed92f38545ff37feaa5189e1626fa7df8784b83e Mon Sep 17 00:00:00 2001 From: Shai Almog <67850168+shai-almog@users.noreply.github.com> Date: Thu, 6 Aug 2026 20:22:32 +0700 Subject: [PATCH 19/33] Report resolved styles over MCP, and see the canvas ignoring the stylesheet MCP reported geometry but never what the canvas was drawing with, so nothing outside the process could tell whether a CSS edit had reached the preview or stopped on the way. It now reports each component's UIID, resolved foreground and background, and which UIManager resolved them, plus what the project theme compiled to and what each manager resolves for a plain Label. Pointing that at the running editor answers the question the tests could not. For the demo project, whose theme.css sets Label colour #26324a: projectThemeLabelFg 26324a <- the compiled theme is correct previewLabelFg f5f8ff <- what the canvas resolves globalLabelFg 6d6d72 themeApplyCount 2 <- the theme was installed, twice Three different values. The canvas resolves from its own UIManager, that manager is created fresh and handed the compiled project theme, and it still answers with the builder's own colour. So UIManager.createInstance() followed by setThemeProps() does not change what getComponentStyle() returns, which is why every style level test passes -- under test no application theme is installed to lose to -- while the running editor shows styling unrelated to the stylesheet being edited. Also removes the demo project's target directory from the copy the tests make, and hardens build-and-run.sh: mvn clean was leaving a jar mixing fresh and stale classes, so the core output directories are removed outright and three more symbols are verified in the installed artifact. Co-Authored-By: Claude Opus 5 (1M context) --- scripts/guibuilder/build-and-run.sh | 6 +++ .../guibuilder/CodenameOneGUIBuilder.java | 40 ++++++++++++++++++- 2 files changed, 45 insertions(+), 1 deletion(-) diff --git a/scripts/guibuilder/build-and-run.sh b/scripts/guibuilder/build-and-run.sh index 55488bbe8b9..086badb9780 100755 --- a/scripts/guibuilder/build-and-run.sh +++ b/scripts/guibuilder/build-and-run.sh @@ -43,11 +43,17 @@ verify_core_class() { echo "==> core (JDK 8)" cd "$REPO_ROOT/maven" +# mvn clean is not enough: the core module has produced a jar mixing freshly compiled classes with +# stale ones, which is how fixes kept reaching the tests but not the running editor. Remove the +# output directory outright. +rm -rf core/target factory/target css-compiler/target JAVA_HOME="$JDK8" PATH="$JDK8/bin:$PATH" \ mvn -q -Dmaven.repo.local="$LOCAL_REPO" -pl factory,core,css-compiler \ -DskipTests -Dmaven.javadoc.skip=true clean install JAVA_HOME="$JDK8" verify_core_class "com/codename1/ui/editor/CodeView.class" "protectedStartMarker" JAVA_HOME="$JDK8" verify_core_class "com/codename1/ui/editor/EditorView.class" "multiKeyModeRestore" +JAVA_HOME="$JDK8" verify_core_class "com/codename1/ui/CodeEditor.class" "setCursorPosition" +JAVA_HOME="$JDK8" verify_core_class "com/codename1/ui/Form.class" "focusedHandlesInput" echo "==> javase port (JDK 8)" JAVA_HOME="$JDK8" PATH="$JDK8/bin:$PATH" \ diff --git a/scripts/guibuilder/common/src/main/java/com/codename1/guibuilder/CodenameOneGUIBuilder.java b/scripts/guibuilder/common/src/main/java/com/codename1/guibuilder/CodenameOneGUIBuilder.java index a71ff97192f..a71b26eac2e 100644 --- a/scripts/guibuilder/common/src/main/java/com/codename1/guibuilder/CodenameOneGUIBuilder.java +++ b/scripts/guibuilder/common/src/main/java/com/codename1/guibuilder/CodenameOneGUIBuilder.java @@ -78,7 +78,10 @@ public class CodenameOneGUIBuilder extends Lifecycle { private Hashtable projectTheme; /// The look and feel of the design canvas. The preview hierarchy owns it so the project theme /// and the builder's own chrome can coexist; they cannot share the global UIManager. - private final UIManager previewUIManager = UIManager.createInstance(); + private UIManager previewUIManager = UIManager.createInstance(); + /// The device surface the canvas UIManager is attached to, so a new one can replace it. + private Container deviceSurface; + private int themeApplyCount; private Hashtable builderTheme; private Component previewRoot; private Component formToolbarPreview; @@ -584,6 +587,7 @@ private void refreshEditor() { previewRoot = preview; // The whole device surface resolves its look and feel from the canvas UIManager, so the // project CSS styles the preview and the builder's own theme never leaks into it. + deviceSurface = device; device.setUIManager(previewUIManager); if (preview instanceof Container previewContainer) previewContainer.setUIManager(previewUIManager); refreshProjectThemeOnPreview(); @@ -4093,7 +4097,16 @@ private void refreshProjectThemeOnPreview() { // preview only held the project styling for the instant of that call: every later // re-resolution -- a repaint, a revalidate, any refreshTheme cascade -- resolved against // the builder chrome again, so CSS edits appeared to do nothing. + // A brand new manager each time rather than reinstalling props into the existing one. + // Installing into a live manager left components resolving styles it had already cached, + // so the canvas kept the previous look and the stylesheet appeared to have no effect. + themeApplyCount++; + previewUIManager = UIManager.createInstance(); previewUIManager.setThemeProps(projectTheme); + if (deviceSurface != null) deviceSurface.setUIManager(previewUIManager); + if (previewRoot instanceof Container previewContainer) { + previewContainer.setUIManager(previewUIManager); + } if (previewRoot != null) { previewRoot.refreshTheme(false); ComponentPreviewFactory.stabilizeDesignStyles(previewRoot); @@ -4534,6 +4547,21 @@ Map mcpState(long latestSequence) { for (String path : guiFiles) forms.add(relativeFormName(path)); out.put("forms", forms); if (canvasHost != null) out.put("canvasBounds", componentBounds(canvasHost)); + out.put("previewUIManager", String.valueOf(System.identityHashCode(previewUIManager))); + out.put("globalUIManager", String.valueOf(System.identityHashCode(UIManager.getInstance()))); + out.put("projectThemeLoaded", Boolean.valueOf(projectTheme != null)); + out.put("themeApplyCount", Integer.valueOf(themeApplyCount)); + // What each manager resolves for a plain Label, so the stylesheet on disk, the compiled + // theme and what the canvas resolves can be compared from outside the process. + out.put("previewLabelFg", String.format("%06x", + Integer.valueOf(previewUIManager.getComponentStyle("Label").getFgColor()))); + out.put("globalLabelFg", String.format("%06x", + Integer.valueOf(UIManager.getInstance().getComponentStyle("Label").getFgColor()))); + if (projectTheme != null) { + out.put("projectThemeLabelFg", String.valueOf(projectTheme.get("Label.fgColor"))); + out.put("projectThemeKeys", Integer.valueOf(projectTheme.size())); + } + if (binding != null) out.put("cssFile", binding.cssFile()); if (document == null) return out; normalizeSelection(); out.put("activeForm", relativeFormName(document.path())); @@ -4577,6 +4605,16 @@ Map mcpState(long latestSequence) { item.put("attributes", attributes); Component preview = componentForElement(canvasHost, element); if (preview != null) { + // The resolved style, not the stylesheet's text. This is what the canvas is actually + // drawing with, so it is the only way to tell from outside whether a CSS edit + // reached the preview or stopped somewhere on the way. + Map style = new LinkedHashMap(); + style.put("uiid", preview.getUIID()); + style.put("fgColor", String.format("%06x", Integer.valueOf(preview.getUnselectedStyle().getFgColor()))); + style.put("bgColor", String.format("%06x", Integer.valueOf(preview.getUnselectedStyle().getBgColor()))); + style.put("bgTransparency", Integer.valueOf(preview.getUnselectedStyle().getBgTransparency() & 0xff)); + style.put("uiManager", String.valueOf(System.identityHashCode(preview.getUIManager()))); + item.put("style", style); item.put("bounds", componentBounds(preview)); item.put("visible", Boolean.valueOf(preview.isVisible() && preview.getWidth() > 0 && preview.getHeight() > 0)); item.put("accessibilityIdentifier", preview.getSemantics().getIdentifier()); From 2d3fe7c737e79fb44b7120cca5f8eeb6acb49011 Mon Sep 17 00:00:00 2001 From: Shai Almog <67850168+shai-almog@users.noreply.github.com> Date: Thu, 6 Aug 2026 20:46:31 +0700 Subject: [PATCH 20/33] Correct the record on canvas styling An earlier commit here concluded that the canvas ignores the project stylesheet, from a resolved Label colour of f5f8ff where theme.css declares 26324a. That was wrong. The demo project's own stylesheet sets f5f8ff for a Label inside its prefers-color-scheme: dark block, and the builder runs in dark mode, so the canvas was showing the project's CSS correctly all along. The comparison was against the light-mode declaration while the dark one applied. Documents how to read canvas styling so the same mistake is not repeated. Co-Authored-By: Claude Opus 5 (1M context) --- scripts/guibuilder/STATUS.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/scripts/guibuilder/STATUS.md b/scripts/guibuilder/STATUS.md index 77e70f3c949..3eb12bd652f 100644 --- a/scripts/guibuilder/STATUS.md +++ b/scripts/guibuilder/STATUS.md @@ -931,6 +931,21 @@ tools visually distinct, drive align/size actions, and not change simply because another member is dragged. +## Reading the canvas styling + +The design canvas resolves its look and feel from a `UIManager` of its own +(`Container#setUIManager`), holding the theme compiled from the project's +`theme.css`. The builder's chrome keeps the global one. Both work; a canvas +component's resolved style is reported over MCP as `style.fgColor` / +`style.bgColor` alongside the `UIManager` that resolved it. + +When comparing a resolved colour against the stylesheet, account for +`@media (prefers-color-scheme: dark)`. The builder runs in dark mode by +default, so a dark block overrides the light declarations, and the flattened +theme carries both. A resolved value that does not match the light-mode +declaration is not evidence of a problem -- during this review it was +mistaken for the project stylesheet being ignored, and it was not. + ## Review pass 2026-07-28: defects found and fixed A code review of the whole change found the following defects. All of them are From 423875973679dff06265f669c0be7a3a7251f869 Mon Sep 17 00:00:00 2001 From: Shai Almog <67850168+shai-almog@users.noreply.github.com> Date: Thu, 6 Aug 2026 21:04:55 +0700 Subject: [PATCH 21/33] Add the required copyright headers to every new file check-copyright-headers failed on the PR: 27 added files carried no licence header. All of them now use the complete Codename One GPLv2 + Classpath Exception header, and the check passes locally. Co-Authored-By: Claude Opus 5 (1M context) --- .../codename1/maven/OpenGuiBuilderMojo.java | 23 +++++++++++++++++++ .../maven/OpenGuiBuilderMojoTest.java | 23 +++++++++++++++++++ .../ui/layouts/LayeredLayoutTest.java | 23 +++++++++++++++++++ .../guibuilder/common/src/main/css/theme.css | 23 +++++++++++++++++++ .../guibuilder/CodenameOneGUIBuilder.java | 23 +++++++++++++++++++ .../guibuilder/GuiBuilderMcpController.java | 23 +++++++++++++++++++ .../guibuilder/model/GuiDocument.java | 23 +++++++++++++++++++ .../guibuilder/project/ProjectBinding.java | 23 +++++++++++++++++++ .../guibuilder/project/ProjectIO.java | 23 +++++++++++++++++++ .../ui/ComponentPreviewFactory.java | 23 +++++++++++++++++++ .../guibuilder/ui/DragGuideOverlay.java | 23 +++++++++++++++++++ .../guibuilder/ui/GuidedLayoutSupport.java | 23 +++++++++++++++++++ .../guibuilder/model/GuiDocumentTest.java | 23 +++++++++++++++++++ .../project/ProjectBindingTest.java | 23 +++++++++++++++++++ .../CodenameOneGUIBuilderLauncher.java | 23 +++++++++++++++++++ .../guibuilder/CodenameOneGUIBuilderStub.java | 23 +++++++++++++++++++ .../codename1/guibuilder/DemoFormsTest.java | 23 +++++++++++++++++++ .../guibuilder/DesignerInteractionTest.java | 23 +++++++++++++++++++ .../guibuilder/GeneratedSourceTest.java | 23 +++++++++++++++++++ .../com/codename1/guibuilder/LiveCssTest.java | 23 +++++++++++++++++++ .../codename1/guibuilder/LiveTypingTest.java | 23 +++++++++++++++++++ .../guibuilder/LiveWorkspaceDragTest.java | 23 +++++++++++++++++++ .../guibuilder/NestedHierarchyTest.java | 23 +++++++++++++++++++ .../guibuilder/ProjectCssCompilesTest.java | 23 +++++++++++++++++++ .../ProjectCssStylesPreviewTest.java | 23 +++++++++++++++++++ .../guibuilder/SourceEditingTest.java | 23 +++++++++++++++++++ 26 files changed, 598 insertions(+) diff --git a/maven/codenameone-maven-plugin/src/main/java/com/codename1/maven/OpenGuiBuilderMojo.java b/maven/codenameone-maven-plugin/src/main/java/com/codename1/maven/OpenGuiBuilderMojo.java index f4b25a61c1d..525b9c74652 100644 --- a/maven/codenameone-maven-plugin/src/main/java/com/codename1/maven/OpenGuiBuilderMojo.java +++ b/maven/codenameone-maven-plugin/src/main/java/com/codename1/maven/OpenGuiBuilderMojo.java @@ -1,3 +1,26 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Codename One designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Codename One through http://www.codenameone.com/ if you + * need additional information or have any questions. + */ + /* * Copyright (c) 2012, Codename One and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. diff --git a/maven/codenameone-maven-plugin/src/test/java/com/codename1/maven/OpenGuiBuilderMojoTest.java b/maven/codenameone-maven-plugin/src/test/java/com/codename1/maven/OpenGuiBuilderMojoTest.java index 5446600025b..f9735170c7b 100644 --- a/maven/codenameone-maven-plugin/src/test/java/com/codename1/maven/OpenGuiBuilderMojoTest.java +++ b/maven/codenameone-maven-plugin/src/test/java/com/codename1/maven/OpenGuiBuilderMojoTest.java @@ -1,3 +1,26 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Codename One designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Codename One through http://www.codenameone.com/ if you + * need additional information or have any questions. + */ + package com.codename1.maven; import org.apache.maven.project.MavenProject; diff --git a/maven/core-unittests/src/test/java/com/codename1/ui/layouts/LayeredLayoutTest.java b/maven/core-unittests/src/test/java/com/codename1/ui/layouts/LayeredLayoutTest.java index 64b6b422768..708a1486390 100644 --- a/maven/core-unittests/src/test/java/com/codename1/ui/layouts/LayeredLayoutTest.java +++ b/maven/core-unittests/src/test/java/com/codename1/ui/layouts/LayeredLayoutTest.java @@ -1,3 +1,26 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Codename One designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Codename One through http://www.codenameone.com/ if you + * need additional information or have any questions. + */ + package com.codename1.ui.layouts; import com.codename1.junit.FormTest; diff --git a/scripts/guibuilder/common/src/main/css/theme.css b/scripts/guibuilder/common/src/main/css/theme.css index 82d35628b12..71ec5490980 100644 --- a/scripts/guibuilder/common/src/main/css/theme.css +++ b/scripts/guibuilder/common/src/main/css/theme.css @@ -1,3 +1,26 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Codename One designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Codename One through http://www.codenameone.com/ if you + * need additional information or have any questions. + */ + #Constants { includeNativeBool: true; globalToolbarBool: true; diff --git a/scripts/guibuilder/common/src/main/java/com/codename1/guibuilder/CodenameOneGUIBuilder.java b/scripts/guibuilder/common/src/main/java/com/codename1/guibuilder/CodenameOneGUIBuilder.java index a71b26eac2e..2fcd927b94a 100644 --- a/scripts/guibuilder/common/src/main/java/com/codename1/guibuilder/CodenameOneGUIBuilder.java +++ b/scripts/guibuilder/common/src/main/java/com/codename1/guibuilder/CodenameOneGUIBuilder.java @@ -1,3 +1,26 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Codename One designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Codename One through http://www.codenameone.com/ if you + * need additional information or have any questions. + */ + package com.codename1.guibuilder; import com.codename1.components.SpanLabel; diff --git a/scripts/guibuilder/common/src/main/java/com/codename1/guibuilder/GuiBuilderMcpController.java b/scripts/guibuilder/common/src/main/java/com/codename1/guibuilder/GuiBuilderMcpController.java index b0c36e520ad..17a215ad2f0 100644 --- a/scripts/guibuilder/common/src/main/java/com/codename1/guibuilder/GuiBuilderMcpController.java +++ b/scripts/guibuilder/common/src/main/java/com/codename1/guibuilder/GuiBuilderMcpController.java @@ -1,3 +1,26 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Codename One designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Codename One through http://www.codenameone.com/ if you + * need additional information or have any questions. + */ + package com.codename1.guibuilder; import com.codename1.ai.Tool; diff --git a/scripts/guibuilder/common/src/main/java/com/codename1/guibuilder/model/GuiDocument.java b/scripts/guibuilder/common/src/main/java/com/codename1/guibuilder/model/GuiDocument.java index 46d081c5595..792ac0d3dbf 100644 --- a/scripts/guibuilder/common/src/main/java/com/codename1/guibuilder/model/GuiDocument.java +++ b/scripts/guibuilder/common/src/main/java/com/codename1/guibuilder/model/GuiDocument.java @@ -1,3 +1,26 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Codename One designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Codename One through http://www.codenameone.com/ if you + * need additional information or have any questions. + */ + package com.codename1.guibuilder.model; import com.codename1.util.regex.StringReader; diff --git a/scripts/guibuilder/common/src/main/java/com/codename1/guibuilder/project/ProjectBinding.java b/scripts/guibuilder/common/src/main/java/com/codename1/guibuilder/project/ProjectBinding.java index e9c96f8ec33..b30d8289beb 100644 --- a/scripts/guibuilder/common/src/main/java/com/codename1/guibuilder/project/ProjectBinding.java +++ b/scripts/guibuilder/common/src/main/java/com/codename1/guibuilder/project/ProjectBinding.java @@ -1,3 +1,26 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Codename One designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Codename One through http://www.codenameone.com/ if you + * need additional information or have any questions. + */ + package com.codename1.guibuilder.project; public final class ProjectBinding { diff --git a/scripts/guibuilder/common/src/main/java/com/codename1/guibuilder/project/ProjectIO.java b/scripts/guibuilder/common/src/main/java/com/codename1/guibuilder/project/ProjectIO.java index 0daed7f242b..59afd6f0697 100644 --- a/scripts/guibuilder/common/src/main/java/com/codename1/guibuilder/project/ProjectIO.java +++ b/scripts/guibuilder/common/src/main/java/com/codename1/guibuilder/project/ProjectIO.java @@ -1,3 +1,26 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Codename One designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Codename One through http://www.codenameone.com/ if you + * need additional information or have any questions. + */ + package com.codename1.guibuilder.project; import com.codename1.io.FileSystemStorage; diff --git a/scripts/guibuilder/common/src/main/java/com/codename1/guibuilder/ui/ComponentPreviewFactory.java b/scripts/guibuilder/common/src/main/java/com/codename1/guibuilder/ui/ComponentPreviewFactory.java index 1ec26866f7a..2955738e0dd 100644 --- a/scripts/guibuilder/common/src/main/java/com/codename1/guibuilder/ui/ComponentPreviewFactory.java +++ b/scripts/guibuilder/common/src/main/java/com/codename1/guibuilder/ui/ComponentPreviewFactory.java @@ -1,3 +1,26 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Codename One designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Codename One through http://www.codenameone.com/ if you + * need additional information or have any questions. + */ + package com.codename1.guibuilder.ui; import com.codename1.components.SpanLabel; diff --git a/scripts/guibuilder/common/src/main/java/com/codename1/guibuilder/ui/DragGuideOverlay.java b/scripts/guibuilder/common/src/main/java/com/codename1/guibuilder/ui/DragGuideOverlay.java index dfa2f39e790..23c6b8368fd 100644 --- a/scripts/guibuilder/common/src/main/java/com/codename1/guibuilder/ui/DragGuideOverlay.java +++ b/scripts/guibuilder/common/src/main/java/com/codename1/guibuilder/ui/DragGuideOverlay.java @@ -1,3 +1,26 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Codename One designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Codename One through http://www.codenameone.com/ if you + * need additional information or have any questions. + */ + package com.codename1.guibuilder.ui; import com.codename1.ui.Component; diff --git a/scripts/guibuilder/common/src/main/java/com/codename1/guibuilder/ui/GuidedLayoutSupport.java b/scripts/guibuilder/common/src/main/java/com/codename1/guibuilder/ui/GuidedLayoutSupport.java index 71519a4a816..08d7ad0ca63 100644 --- a/scripts/guibuilder/common/src/main/java/com/codename1/guibuilder/ui/GuidedLayoutSupport.java +++ b/scripts/guibuilder/common/src/main/java/com/codename1/guibuilder/ui/GuidedLayoutSupport.java @@ -1,3 +1,26 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Codename One designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Codename One through http://www.codenameone.com/ if you + * need additional information or have any questions. + */ + package com.codename1.guibuilder.ui; import com.codename1.ui.Component; diff --git a/scripts/guibuilder/common/src/test/java/com/codename1/guibuilder/model/GuiDocumentTest.java b/scripts/guibuilder/common/src/test/java/com/codename1/guibuilder/model/GuiDocumentTest.java index 4af37d8f1ab..3002f02f30e 100644 --- a/scripts/guibuilder/common/src/test/java/com/codename1/guibuilder/model/GuiDocumentTest.java +++ b/scripts/guibuilder/common/src/test/java/com/codename1/guibuilder/model/GuiDocumentTest.java @@ -1,3 +1,26 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Codename One designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Codename One through http://www.codenameone.com/ if you + * need additional information or have any questions. + */ + package com.codename1.guibuilder.model; import com.codename1.xml.Element; diff --git a/scripts/guibuilder/common/src/test/java/com/codename1/guibuilder/project/ProjectBindingTest.java b/scripts/guibuilder/common/src/test/java/com/codename1/guibuilder/project/ProjectBindingTest.java index e129ad0461e..f5e6583d6df 100644 --- a/scripts/guibuilder/common/src/test/java/com/codename1/guibuilder/project/ProjectBindingTest.java +++ b/scripts/guibuilder/common/src/test/java/com/codename1/guibuilder/project/ProjectBindingTest.java @@ -1,3 +1,26 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Codename One designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Codename One through http://www.codenameone.com/ if you + * need additional information or have any questions. + */ + package com.codename1.guibuilder.project; import org.junit.jupiter.api.Test; diff --git a/scripts/guibuilder/javase/src/desktop/java/com/codename1/guibuilder/CodenameOneGUIBuilderLauncher.java b/scripts/guibuilder/javase/src/desktop/java/com/codename1/guibuilder/CodenameOneGUIBuilderLauncher.java index c6b19355ae2..11eadebbb1f 100644 --- a/scripts/guibuilder/javase/src/desktop/java/com/codename1/guibuilder/CodenameOneGUIBuilderLauncher.java +++ b/scripts/guibuilder/javase/src/desktop/java/com/codename1/guibuilder/CodenameOneGUIBuilderLauncher.java @@ -1,3 +1,26 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Codename One designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Codename One through http://www.codenameone.com/ if you + * need additional information or have any questions. + */ + package com.codename1.guibuilder; public final class CodenameOneGUIBuilderLauncher { diff --git a/scripts/guibuilder/javase/src/desktop/java/com/codename1/guibuilder/CodenameOneGUIBuilderStub.java b/scripts/guibuilder/javase/src/desktop/java/com/codename1/guibuilder/CodenameOneGUIBuilderStub.java index 9ceb304d850..1ec96d8ef40 100644 --- a/scripts/guibuilder/javase/src/desktop/java/com/codename1/guibuilder/CodenameOneGUIBuilderStub.java +++ b/scripts/guibuilder/javase/src/desktop/java/com/codename1/guibuilder/CodenameOneGUIBuilderStub.java @@ -1,3 +1,26 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Codename One designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Codename One through http://www.codenameone.com/ if you + * need additional information or have any questions. + */ + package com.codename1.guibuilder; import com.codename1.impl.javase.JavaSEPort; diff --git a/scripts/guibuilder/javase/src/test/java/com/codename1/guibuilder/DemoFormsTest.java b/scripts/guibuilder/javase/src/test/java/com/codename1/guibuilder/DemoFormsTest.java index 36cbf64676b..756dbd14ace 100644 --- a/scripts/guibuilder/javase/src/test/java/com/codename1/guibuilder/DemoFormsTest.java +++ b/scripts/guibuilder/javase/src/test/java/com/codename1/guibuilder/DemoFormsTest.java @@ -1,3 +1,26 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Codename One designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Codename One through http://www.codenameone.com/ if you + * need additional information or have any questions. + */ + package com.codename1.guibuilder; import com.codename1.guibuilder.model.GuiDocument; diff --git a/scripts/guibuilder/javase/src/test/java/com/codename1/guibuilder/DesignerInteractionTest.java b/scripts/guibuilder/javase/src/test/java/com/codename1/guibuilder/DesignerInteractionTest.java index 9f357a49745..29167d2d996 100644 --- a/scripts/guibuilder/javase/src/test/java/com/codename1/guibuilder/DesignerInteractionTest.java +++ b/scripts/guibuilder/javase/src/test/java/com/codename1/guibuilder/DesignerInteractionTest.java @@ -1,3 +1,26 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Codename One designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Codename One through http://www.codenameone.com/ if you + * need additional information or have any questions. + */ + package com.codename1.guibuilder; import com.codename1.guibuilder.model.GuiDocument; diff --git a/scripts/guibuilder/javase/src/test/java/com/codename1/guibuilder/GeneratedSourceTest.java b/scripts/guibuilder/javase/src/test/java/com/codename1/guibuilder/GeneratedSourceTest.java index 12c4085bdc0..66851daeb95 100644 --- a/scripts/guibuilder/javase/src/test/java/com/codename1/guibuilder/GeneratedSourceTest.java +++ b/scripts/guibuilder/javase/src/test/java/com/codename1/guibuilder/GeneratedSourceTest.java @@ -1,3 +1,26 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Codename One designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Codename One through http://www.codenameone.com/ if you + * need additional information or have any questions. + */ + package com.codename1.guibuilder; import com.codename1.guibuilder.model.GuiDocument; diff --git a/scripts/guibuilder/javase/src/test/java/com/codename1/guibuilder/LiveCssTest.java b/scripts/guibuilder/javase/src/test/java/com/codename1/guibuilder/LiveCssTest.java index 3f0899fdab6..92b2b40dbeb 100644 --- a/scripts/guibuilder/javase/src/test/java/com/codename1/guibuilder/LiveCssTest.java +++ b/scripts/guibuilder/javase/src/test/java/com/codename1/guibuilder/LiveCssTest.java @@ -1,3 +1,26 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Codename One designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Codename One through http://www.codenameone.com/ if you + * need additional information or have any questions. + */ + package com.codename1.guibuilder; import com.codename1.ui.Component; diff --git a/scripts/guibuilder/javase/src/test/java/com/codename1/guibuilder/LiveTypingTest.java b/scripts/guibuilder/javase/src/test/java/com/codename1/guibuilder/LiveTypingTest.java index e8cee47a3a3..8f01853375b 100644 --- a/scripts/guibuilder/javase/src/test/java/com/codename1/guibuilder/LiveTypingTest.java +++ b/scripts/guibuilder/javase/src/test/java/com/codename1/guibuilder/LiveTypingTest.java @@ -1,3 +1,26 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Codename One designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Codename One through http://www.codenameone.com/ if you + * need additional information or have any questions. + */ + package com.codename1.guibuilder; import com.codename1.ui.CodeEditor; diff --git a/scripts/guibuilder/javase/src/test/java/com/codename1/guibuilder/LiveWorkspaceDragTest.java b/scripts/guibuilder/javase/src/test/java/com/codename1/guibuilder/LiveWorkspaceDragTest.java index 7ffe765ea3b..9fc56bc3722 100644 --- a/scripts/guibuilder/javase/src/test/java/com/codename1/guibuilder/LiveWorkspaceDragTest.java +++ b/scripts/guibuilder/javase/src/test/java/com/codename1/guibuilder/LiveWorkspaceDragTest.java @@ -1,3 +1,26 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Codename One designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Codename One through http://www.codenameone.com/ if you + * need additional information or have any questions. + */ + package com.codename1.guibuilder; import com.codename1.ui.Component; diff --git a/scripts/guibuilder/javase/src/test/java/com/codename1/guibuilder/NestedHierarchyTest.java b/scripts/guibuilder/javase/src/test/java/com/codename1/guibuilder/NestedHierarchyTest.java index c1dfcb78fbf..984b46667d8 100644 --- a/scripts/guibuilder/javase/src/test/java/com/codename1/guibuilder/NestedHierarchyTest.java +++ b/scripts/guibuilder/javase/src/test/java/com/codename1/guibuilder/NestedHierarchyTest.java @@ -1,3 +1,26 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Codename One designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Codename One through http://www.codenameone.com/ if you + * need additional information or have any questions. + */ + package com.codename1.guibuilder; import com.codename1.guibuilder.model.GuiDocument; diff --git a/scripts/guibuilder/javase/src/test/java/com/codename1/guibuilder/ProjectCssCompilesTest.java b/scripts/guibuilder/javase/src/test/java/com/codename1/guibuilder/ProjectCssCompilesTest.java index e662f0c19e5..8af0129c148 100644 --- a/scripts/guibuilder/javase/src/test/java/com/codename1/guibuilder/ProjectCssCompilesTest.java +++ b/scripts/guibuilder/javase/src/test/java/com/codename1/guibuilder/ProjectCssCompilesTest.java @@ -1,3 +1,26 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Codename One designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Codename One through http://www.codenameone.com/ if you + * need additional information or have any questions. + */ + package com.codename1.guibuilder; import com.codename1.ui.css.CSSThemeCompiler; diff --git a/scripts/guibuilder/javase/src/test/java/com/codename1/guibuilder/ProjectCssStylesPreviewTest.java b/scripts/guibuilder/javase/src/test/java/com/codename1/guibuilder/ProjectCssStylesPreviewTest.java index 40434b93fd0..beb374e7591 100644 --- a/scripts/guibuilder/javase/src/test/java/com/codename1/guibuilder/ProjectCssStylesPreviewTest.java +++ b/scripts/guibuilder/javase/src/test/java/com/codename1/guibuilder/ProjectCssStylesPreviewTest.java @@ -1,3 +1,26 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Codename One designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Codename One through http://www.codenameone.com/ if you + * need additional information or have any questions. + */ + package com.codename1.guibuilder; import com.codename1.ui.Component; diff --git a/scripts/guibuilder/javase/src/test/java/com/codename1/guibuilder/SourceEditingTest.java b/scripts/guibuilder/javase/src/test/java/com/codename1/guibuilder/SourceEditingTest.java index 486d580c2b4..22c7f2572ea 100644 --- a/scripts/guibuilder/javase/src/test/java/com/codename1/guibuilder/SourceEditingTest.java +++ b/scripts/guibuilder/javase/src/test/java/com/codename1/guibuilder/SourceEditingTest.java @@ -1,3 +1,26 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Codename One designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Codename One through http://www.codenameone.com/ if you + * need additional information or have any questions. + */ + package com.codename1.guibuilder; import com.codename1.ui.TextInputClient; From f4c07f9bc75b27dc41e0d11d38f32d61afeeb6dd Mon Sep 17 00:00:00 2001 From: Shai Almog <67850168+shai-almog@users.noreply.github.com> Date: Thu, 6 Aug 2026 21:12:58 +0700 Subject: [PATCH 22/33] Copy built artifacts into the local repository instead of trusting install mvn install reported BUILD SUCCESS repeatedly while leaving the previous jar in the local repository. The freshly built maven/core/target jar contained the change; the installed copy did not. Everything downstream then compiled and tested against stale classes, which is why the same defects were fixed several times over without the running editor ever changing, and why unrelated tests failed at random. The freshly built jars are now copied over the installed ones explicitly, and a fifth symbol is verified in the artifact afterwards. Co-Authored-By: Claude Opus 5 (1M context) --- scripts/guibuilder/build-and-run.sh | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/scripts/guibuilder/build-and-run.sh b/scripts/guibuilder/build-and-run.sh index 086badb9780..46be289552c 100755 --- a/scripts/guibuilder/build-and-run.sh +++ b/scripts/guibuilder/build-and-run.sh @@ -50,10 +50,24 @@ rm -rf core/target factory/target css-compiler/target JAVA_HOME="$JDK8" PATH="$JDK8/bin:$PATH" \ mvn -q -Dmaven.repo.local="$LOCAL_REPO" -pl factory,core,css-compiler \ -DskipTests -Dmaven.javadoc.skip=true clean install +# mvn install has repeatedly reported success while leaving the previous jar in place, so the +# freshly built artifacts are copied over it explicitly. Trusting install is what made fixes compile +# and pass tests without ever reaching the running editor. +for module in core factory css-compiler; do + for jar in "$REPO_ROOT/maven/$module/target/"*.jar; do + [ -e "$jar" ] || continue + case "$jar" in *-sources.jar|*-javadoc.jar) continue;; esac + artifact="$(basename "$jar" | sed 's/-8\.0-SNAPSHOT\.jar$//')" + dest="$LOCAL_REPO/com/codenameone/$artifact/8.0-SNAPSHOT/$(basename "$jar")" + [ -e "$dest" ] && cp -f "$jar" "$dest" + done +done + JAVA_HOME="$JDK8" verify_core_class "com/codename1/ui/editor/CodeView.class" "protectedStartMarker" JAVA_HOME="$JDK8" verify_core_class "com/codename1/ui/editor/EditorView.class" "multiKeyModeRestore" JAVA_HOME="$JDK8" verify_core_class "com/codename1/ui/CodeEditor.class" "setCursorPosition" JAVA_HOME="$JDK8" verify_core_class "com/codename1/ui/Form.class" "focusedHandlesInput" +JAVA_HOME="$JDK8" verify_core_class "com/codename1/ui/editor/EditorView.class" "copySelection" echo "==> javase port (JDK 8)" JAVA_HOME="$JDK8" PATH="$JDK8/bin:$PATH" \ From 4a3013483dcf15a5d1da0b2cce72e10483a9e5c0 Mon Sep 17 00:00:00 2001 From: Shai Almog <67850168+shai-almog@users.noreply.github.com> Date: Thu, 6 Aug 2026 22:31:27 +0700 Subject: [PATCH 23/33] Address the PR review: generated source, save safety and IDE bindings The editor is launched by resolving its published POM, so codenameone-javase has to be a real dependency of it rather than a test one; a consumer running cn1:guibuilder got a NoClassDefFoundError before the window opened. Generated companion source now carries what the inspector shows. Enabled, visible, RTL, gap, alignment, ticker, toggle, selected, columns, rows, maximum length, editable, grow by content, input constraint, slider bounds and progress, tab placement and selection, and container scrolling were applied to the preview and dropped from the generated form, so the running app used defaults for everything the user had configured. Table cell percentages are emitted for the same reason. A Container or Dialog root generates that type instead of always extending Form, Accordion generates code that compiles, and component names are made unique and keyword safe before they become fields and methods. Companion sources scaffolded by older versions of cn1:create-gui-form carry the legacy "DON'T EDIT" markers. The editor did not recognise them, so designing a form in such a project saved the .gui and produced an empty screen at runtime. Those files are now migrated, keeping the developer's own methods, and the scaffolder writes the current format. Saving writes a temporary file and replaces the target only once the content is safely on disk; a failed write used to leave an empty file where the form was. save() reports failure, so switching forms no longer discards the edits the user just asked to keep, and refreshing the project asks before reloading over unsaved work. MCP clients are told when a save or an open fails. Core: a protected region no longer blocks the first character after it, input method composition is checked against protected regions rather than only replaceRange, multi key mode is restored only by the editor that changed it, and the soft key bypass is limited to components that consume raw text instead of everything that sets handlesInput. On the desktop port a blocked shortcut's release is dropped even when the modifier came up first, and a record left by a shortcut the platform swallowed is cleared by the next plain press. The Maven plugin's artifact resolution now honours offline mode. It reached the network during mvn -o and installed a remote snapshot over the artifact the same build had just produced, which is how the editor kept running without changes that had demonstrably compiled. Also: IDE shortcuts for the new editor in the generated project (IntelliJ, VS Code, Eclipse; NetBeans already had one), Windows path handling in ProjectIO, table spans honoured when allocating a free cell, line breaks and deliberate whitespace preserved through a save/reload round trip, a JSON guard in the MCP client, and the GUI Builder workflow triggers on the core it actually consumes. Co-Authored-By: Claude Opus 5 (1M context) --- .github/workflows/guibuilder.yml | 24 +- .../com/codename1/components/SplitPane.java | 4 +- .../src/com/codename1/ui/Component.java | 16 + CodenameOne/src/com/codename1/ui/Form.java | 16 +- CodenameOne/src/com/codename1/ui/Tabs.java | 4 +- .../src/com/codename1/ui/editor/CodeView.java | 29 +- .../com/codename1/ui/editor/EditorView.java | 48 ++- .../com/codename1/impl/javase/JavaSEPort.java | 18 +- .../runConfigurations/CN1_GUI_Builder.xml | 29 ++ .../archetype-resources/.vscode/settings.json | 4 + .../eclipse/__mainName__ GUI Builder.launch | 27 ++ .../com/codename1/maven/AbstractCN1Mojo.java | 12 + .../codename1/maven/CreateGuiFormMojo.java | 56 ++- .../codename1/ui/SoftKeyCollisionTest.java | 34 +- scripts/guibuilder/build-and-run.sh | 26 +- .../guibuilder/CodenameOneGUIBuilder.java | 402 ++++++++++++++++-- .../guibuilder/model/GuiDocument.java | 39 +- .../guibuilder/project/ProjectIO.java | 50 ++- .../ui/ComponentPreviewFactory.java | 16 +- .../demo-project/src/main/css/theme.css | 23 + scripts/guibuilder/javase/pom.xml | 12 +- .../guibuilder/DesignerInteractionTest.java | 19 +- .../guibuilder/GeneratedSourceTest.java | 199 ++++++++- .../codename1/guibuilder/ProjectIoTest.java | 108 +++++ .../tools/guibuilder-mcp-client.mjs | 10 +- 25 files changed, 1124 insertions(+), 101 deletions(-) create mode 100644 maven/cn1app-archetype/src/main/resources/archetype-resources/.idea/runConfigurations/CN1_GUI_Builder.xml create mode 100644 maven/cn1app-archetype/src/main/resources/archetype-resources/tools/eclipse/__mainName__ GUI Builder.launch create mode 100644 scripts/guibuilder/javase/src/test/java/com/codename1/guibuilder/ProjectIoTest.java diff --git a/.github/workflows/guibuilder.yml b/.github/workflows/guibuilder.yml index 1fd9a6855c3..dc83b3f6a2a 100644 --- a/.github/workflows/guibuilder.yml +++ b/.github/workflows/guibuilder.yml @@ -15,10 +15,14 @@ on: - '.github/workflows/guibuilder.yml' - 'scripts/guibuilder/**' - '!scripts/guibuilder/**/*.md' - - 'CodenameOne/src/com/codename1/ui/CodeEditor.java' - - 'CodenameOne/src/com/codename1/ui/editor/**' - - 'CodenameOne/src/com/codename1/ui/layouts/LayeredLayout.java' - - 'maven/codenameone-maven-plugin/src/main/java/com/codename1/maven/OpenGuiBuilderMojo.java' + # The editor is an ordinary Codename One app: it renders the design canvas with + # Tabs, TableLayout, SplitPane and the rest of the core UI, compiles the project + # stylesheet with the CSS compiler, and runs on the JavaSE port. Listing only the + # few classes it obviously touches let a core change break it with this job skipped. + - 'CodenameOne/src/**' + - 'Ports/JavaSE/src/**' + - 'maven/css-compiler/**' + - 'maven/codenameone-maven-plugin/src/main/java/com/codename1/maven/**' push: branches: - master @@ -26,10 +30,14 @@ on: - '.github/workflows/guibuilder.yml' - 'scripts/guibuilder/**' - '!scripts/guibuilder/**/*.md' - - 'CodenameOne/src/com/codename1/ui/CodeEditor.java' - - 'CodenameOne/src/com/codename1/ui/editor/**' - - 'CodenameOne/src/com/codename1/ui/layouts/LayeredLayout.java' - - 'maven/codenameone-maven-plugin/src/main/java/com/codename1/maven/OpenGuiBuilderMojo.java' + # The editor is an ordinary Codename One app: it renders the design canvas with + # Tabs, TableLayout, SplitPane and the rest of the core UI, compiles the project + # stylesheet with the CSS compiler, and runs on the JavaSE port. Listing only the + # few classes it obviously touches let a core change break it with this job skipped. + - 'CodenameOne/src/**' + - 'Ports/JavaSE/src/**' + - 'maven/css-compiler/**' + - 'maven/codenameone-maven-plugin/src/main/java/com/codename1/maven/**' concurrency: group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} diff --git a/CodenameOne/src/com/codename1/components/SplitPane.java b/CodenameOne/src/com/codename1/components/SplitPane.java index efcfa218c5e..0a753a7c080 100644 --- a/CodenameOne/src/com/codename1/components/SplitPane.java +++ b/CodenameOne/src/com/codename1/components/SplitPane.java @@ -1096,7 +1096,9 @@ protected boolean isStickyDrag() { protected void initComponent() { super.initComponent(); Form form = getComponentForm(); - if (form != null) form.setEnableCursors(true); + if (form != null) { + form.setEnableCursors(true); + } } diff --git a/CodenameOne/src/com/codename1/ui/Component.java b/CodenameOne/src/com/codename1/ui/Component.java index 179d3b28814..59f963821e6 100644 --- a/CodenameOne/src/com/codename1/ui/Component.java +++ b/CodenameOne/src/com/codename1/ui/Component.java @@ -4263,6 +4263,22 @@ public void setHandlesInput(boolean handlesInput) { this.handlesInput = handlesInput; } + /// Whether this component turns key codes into printable characters, as a text editor does. + /// + /// Key codes and character codes share one value space, so a port is free to map a soft key + /// onto a value that is also a printable character: the desktop port maps the left soft key to + /// `VK_F1`, which is 112, the code of a lowercase `p`. A form serves a component that answers + /// true here before the menu bar, or that character could never be typed into it. This is + /// deliberately narrower than `#handlesInput()`, which components such as lists and editable + /// sliders also set for focus traversal while still expecting their soft keys to work. + /// + /// #### Returns + /// + /// true if key codes reaching this component are text rather than commands + protected boolean consumesRawTextInput() { + return false; + } + /// Returns true if the component has focus /// /// #### Returns diff --git a/CodenameOne/src/com/codename1/ui/Form.java b/CodenameOne/src/com/codename1/ui/Form.java index b7baaa8b389..cd038d8559d 100644 --- a/CodenameOne/src/com/codename1/ui/Form.java +++ b/CodenameOne/src/com/codename1/ui/Form.java @@ -3224,17 +3224,21 @@ public void setMinimizeOnBack(boolean minimizeOnBack) { menuBar.setMinimizeOnBack(minimizeOnBack); } - /// True when the focused component has claimed raw keyboard input via - /// `Component#setHandlesInput(boolean)`, so soft key mapping must not intercept keys ahead of it. + /// True when the focused component turns key codes into text, so soft key mapping must not + /// intercept keys ahead of it. /// /// A port is free to map a soft key onto any key code, and the desktop port uses the function /// keys: `VK_F1` is 112, which is also the character code of a lowercase `p`. Key codes and /// character codes share one value space here, so a text component would silently never receive - /// that character -- one letter of the alphabet simply stopped working. A component editing text - /// has already declared that it owns the keyboard, so it is served first. + /// that character -- one letter of the alphabet simply stopped working. + /// + /// `Component#handlesInput()` is too broad a test for this: lists, editable sliders and map + /// components set it for focus traversal while still expecting the back and menu commands to + /// reach the menu bar, so only components that declare themselves raw text editors take + /// priority here. private boolean focusedHandlesInput() { - return focused != null && focused.handlesInput() && focused.isEnabled() - && focused.getComponentForm() == this; + return focused != null && focused.consumesRawTextInput() && focused.isEnabled() + && focused.getComponentForm() == this; //NOPMD CompareObjectsWithEquals } /// {@inheritDoc} diff --git a/CodenameOne/src/com/codename1/ui/Tabs.java b/CodenameOne/src/com/codename1/ui/Tabs.java index 667033dd206..84e35aa0576 100644 --- a/CodenameOne/src/com/codename1/ui/Tabs.java +++ b/CodenameOne/src/com/codename1/ui/Tabs.java @@ -2472,7 +2472,9 @@ public void actionPerformed(ActionEvent evt) { // A tab can be removed in response to the same pointer gesture // (e.g. an inspector rebuild). Its global swipe listener may still // receive the queued drag after deinitialization. - if (parent != null) parent.clearComponentsAwaitingRelease(); + if (parent != null) { + parent.clearComponentsAwaitingRelease(); + } } } } diff --git a/CodenameOne/src/com/codename1/ui/editor/CodeView.java b/CodenameOne/src/com/codename1/ui/editor/CodeView.java index ee8b93b7947..6f7939d27ab 100644 --- a/CodenameOne/src/com/codename1/ui/editor/CodeView.java +++ b/CodenameOne/src/com/codename1/ui/editor/CodeView.java @@ -137,16 +137,30 @@ protected void replaceRange(int start, int end, String text, boolean record) { clampedStart = clampedEnd; clampedEnd = swap; } - if (isProtectedEdit(clampedStart, clampedEnd)) { - // Tell the host rather than dropping the edit in silence. A protected region covering - // most of the document is indistinguishable from an editor that ignores the keyboard, - // so the application needs the chance to explain why nothing happened. - host().fireEditorEvent("protectedEdit", String.valueOf(clampedStart)); + if (!isEditAllowed(clampedStart, clampedEnd)) { return; } super.replaceRange(clampedStart, clampedEnd, text, record); } + /// Refuses edits that fall inside a protected region, whichever path they arrive through. + /// Input method composition writes to the document directly rather than through + /// `#replaceRange(int, int, String, boolean)`, so checking only there let a composing keyboard + /// insert provisional text into generated source that is meant to be read only. + @Override + protected boolean isEditAllowed(int start, int end) { + int from = Math.min(start, end); + int to = Math.max(start, end); + if (!isProtectedEdit(from, to)) { + return true; + } + // Tell the host rather than dropping the edit in silence. A protected region covering + // most of the document is indistinguishable from an editor that ignores the keyboard, + // so the application needs the chance to explain why nothing happened. + host().fireEditorEvent("protectedEdit", String.valueOf(from)); + return false; + } + private boolean isProtectedEdit(int start, int end) { if (protectedStartMarker == null || protectedEndMarker == null) { return false; @@ -162,7 +176,10 @@ private boolean isProtectedEdit(int start, int end) { protectedStart + protectedStartMarker.length()); int protectedEnd = endMarker < 0 ? source.length() : endMarker + protectedEndMarker.length(); - if ((start == end && start >= protectedStart && start <= protectedEnd) + // protectedEnd is the offset just past the end marker, so a caret sitting exactly there + // is already outside the block. Treating it as protected made the first character after + // a generated region impossible to type. + if ((start == end && start >= protectedStart && start < protectedEnd) || (start < protectedEnd && end > protectedStart)) { return true; } diff --git a/CodenameOne/src/com/codename1/ui/editor/EditorView.java b/CodenameOne/src/com/codename1/ui/editor/EditorView.java index f9a88d96c4a..669490ecd9d 100644 --- a/CodenameOne/src/com/codename1/ui/editor/EditorView.java +++ b/CodenameOne/src/com/codename1/ui/editor/EditorView.java @@ -54,6 +54,7 @@ public class EditorView extends Component implements TextInputClient { private int caret; private int anchor = -1; private boolean multiKeyModeRestore; + private boolean multiKeyModeChanged; private int composingStart = -1; private int composingEnd = -1; // pre-composition snapshot so the finalized composition forms a single undo unit @@ -815,6 +816,27 @@ public int offsetAtPoint(int absX, int absY) { // ---- editing primitives ---- + /// This view edits text, so key codes reaching it are characters rather than commands. + @Override + protected boolean consumesRawTextInput() { + return true; + } + + /// Whether the given range may be modified. Subclasses that make part of the document read only + /// override this so every mutation path is covered, including input method composition, which + /// writes to the document without going through `#replaceRange(int, int, String, boolean)`. + /// + /// #### Parameters + /// + /// - `start`: inclusive start offset of the edit + /// + /// - `end`: exclusive end offset of the edit + /// + /// **Returns:** true when the edit may proceed + protected boolean isEditAllowed(int start, int end) { + return true; + } + /// Replaces the range `[start, end)` with `text`, updating the caret, history and platform input /// state. /// @@ -1358,7 +1380,12 @@ protected void focusGained() { // last one, so releases were being discarded and characters silently vanished -- always // the same ones, because it depends on which pairs overlap. Multi key mode delivers every // release. It is restored on focus loss so the rest of the application is unaffected. - multiKeyModeRestore = Display.getInstance().isMultiKeyMode(); + // Captured once: gaining focus twice without an intervening restore would otherwise + // record the mode this editor itself installed. + if (!multiKeyModeChanged) { + multiKeyModeRestore = Display.getInstance().isMultiKeyMode(); + multiKeyModeChanged = true; + } Display.getInstance().setMultiKeyMode(true); startInput(); if (!animRegistered && getComponentForm() != null) { @@ -1369,10 +1396,22 @@ protected void focusGained() { repaint(); } + /// Puts multi key mode back the way this editor found it, and only if this editor is the one + /// that changed it. An editor that was initialized but never focused must not restore anything: + /// removing it would otherwise switch the mode off underneath the editor currently being typed + /// in, bringing back the dropped keystrokes this exists to prevent. + private void restoreMultiKeyMode() { + if (!multiKeyModeChanged) { + return; + } + multiKeyModeChanged = false; + Display.getInstance().setMultiKeyMode(multiKeyModeRestore); + } + @Override protected void focusLost() { super.focusLost(); - Display.getInstance().setMultiKeyMode(multiKeyModeRestore); + restoreMultiKeyMode(); stopInput(); if (animRegistered && getComponentForm() != null) { getComponentForm().deregisterAnimated(this); @@ -1391,7 +1430,7 @@ protected void deinitialize() { // drops animation registrations on deinit, so reset the flag to re-register the // caret blink when focus returns. Multi key mode is restored here for the same reason: // focusLost never runs on this path and the setting is global. - Display.getInstance().setMultiKeyMode(multiKeyModeRestore); + restoreMultiKeyMode(); stopInput(); animRegistered = false; } @@ -1816,6 +1855,9 @@ public void setComposingText(String text, int relativeCaret) { } start = doc.clamp(start); end = doc.clamp(end); + if (!isEditAllowed(start, end)) { + return; + } if (composingStart < 0) { // snapshot the pre-composition document so the whole composition (including the // selection this first compose replaces) finalizes as a single undo unit in diff --git a/Ports/JavaSE/src/com/codename1/impl/javase/JavaSEPort.java b/Ports/JavaSE/src/com/codename1/impl/javase/JavaSEPort.java index 2062780b587..7760837b4d8 100644 --- a/Ports/JavaSE/src/com/codename1/impl/javase/JavaSEPort.java +++ b/Ports/JavaSE/src/com/codename1/impl/javase/JavaSEPort.java @@ -3506,6 +3506,11 @@ public void run() { ignorePressedKeys.add(e.getKeyCode()); return; } + // This press was not blocked, so any record left over from an earlier blocked press of + // the same key is stale: the platform swallowed that release (a system shortcut such as + // Cmd+P on macOS does exactly that) and the record would otherwise eat this key's + // release forever, which looks like one letter of the alphabet no longer existing. + ignorePressedKeys.remove(e.getKeyCode()); if (editorFocused && !editorHandlesKey(e)) { return; } @@ -3526,15 +3531,12 @@ public void run() { } public void keyReleased(KeyEvent e) { + // A blocked press records its key so the matching release is dropped too. The record is + // honoured whatever modifiers this release reports: releasing Ctrl before P leaves a + // bare P release, and Codename One enters characters on release, so trusting the + // modifiers here typed the shortcut's letter into the focused field. Stale records are + // cleared by the next unblocked press of the same key instead. boolean ignore = ignorePressedKeys.remove(e.getKeyCode()); - // A blocked press records its key so the matching release is dropped too. When the - // platform never delivers that release -- a system shortcut such as Cmd+P on macOS - // consumes it -- the entry goes stale and silently eats that key from then on, which - // looks like one letter of the alphabet no longer existing. Honour the record only - // while this release still carries the modifiers that caused the press to be blocked. - if (ignore && !(e.isAltDown() || e.isControlDown() || e.isMetaDown() || e.isAltGraphDown())) { - ignore = false; - } if (!isEnabled()) { return; } diff --git a/maven/cn1app-archetype/src/main/resources/archetype-resources/.idea/runConfigurations/CN1_GUI_Builder.xml b/maven/cn1app-archetype/src/main/resources/archetype-resources/.idea/runConfigurations/CN1_GUI_Builder.xml new file mode 100644 index 00000000000..20a1b57faf8 --- /dev/null +++ b/maven/cn1app-archetype/src/main/resources/archetype-resources/.idea/runConfigurations/CN1_GUI_Builder.xml @@ -0,0 +1,29 @@ + + + + + + + + diff --git a/maven/cn1app-archetype/src/main/resources/archetype-resources/.vscode/settings.json b/maven/cn1app-archetype/src/main/resources/archetype-resources/.vscode/settings.json index aa2335dd230..11b32105249 100644 --- a/maven/cn1app-archetype/src/main/resources/archetype-resources/.vscode/settings.json +++ b/maven/cn1app-archetype/src/main/resources/archetype-resources/.vscode/settings.json @@ -7,6 +7,10 @@ "alias": "Tools > Codename One Settings", "command": "\"cn1:settings\" \"-U\" \"-e\"" }, + { + "alias": "Tools > GUI Builder", + "command": "\"cn1:guibuilder\" \"-U\" \"-e\"" + }, { "alias": "Tools > Certificate Wizard", "command": "\"cn1:certificatewizard\" \"-U\" \"-e\"" diff --git a/maven/cn1app-archetype/src/main/resources/archetype-resources/tools/eclipse/__mainName__ GUI Builder.launch b/maven/cn1app-archetype/src/main/resources/archetype-resources/tools/eclipse/__mainName__ GUI Builder.launch new file mode 100644 index 00000000000..cf7714a1fd2 --- /dev/null +++ b/maven/cn1app-archetype/src/main/resources/archetype-resources/tools/eclipse/__mainName__ GUI Builder.launch @@ -0,0 +1,27 @@ +#set ( $d = "$") + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/maven/codenameone-maven-plugin/src/main/java/com/codename1/maven/AbstractCN1Mojo.java b/maven/codenameone-maven-plugin/src/main/java/com/codename1/maven/AbstractCN1Mojo.java index b2b2318d36e..a25d865fc70 100644 --- a/maven/codenameone-maven-plugin/src/main/java/com/codename1/maven/AbstractCN1Mojo.java +++ b/maven/codenameone-maven-plugin/src/main/java/com/codename1/maven/AbstractCN1Mojo.java @@ -113,6 +113,15 @@ public abstract class AbstractCN1Mojo extends AbstractMojo { @Parameter(required = true, readonly = true, defaultValue = "${project.remoteArtifactRepositories}") protected List remoteRepositories; + /** + * The legacy resolver used by the goals below does not read the session's offline flag by + * itself, so {@code mvn -o} still let it reach the network and, worse, install what it found + * over an artifact the same build had just produced locally. Every resolution request in this + * class passes this through. + */ + @Parameter(required = true, readonly = true, defaultValue = "${settings.offline}") + protected boolean offline; + /** * Version of the deprecated Resource Editor that {@code cn1:designer} resolves. * It is frozen rather than tracking the framework version, so it does not follow @@ -483,6 +492,7 @@ protected File getJar(Artifact artifact) { ArtifactResolutionResult result = repositorySystem.resolve(new ArtifactResolutionRequest() + .setOffline(offline) .setLocalRepository(localRepository) .setRemoteRepositories(new ArrayList<>(remoteRepositories)) .setResolveTransitively(true) @@ -697,6 +707,7 @@ protected File findArtifactFile(Artifact artifact) { ArtifactResolutionResult result = repositorySystem.resolve(new ArtifactResolutionRequest() + .setOffline(offline) .setLocalRepository(localRepository) .setRemoteRepositories(new ArrayList<>(remoteRepositories)) .setResolveTransitively(true) @@ -964,6 +975,7 @@ protected String getCssCliClasspath() throws MojoExecutionException { List files = new ArrayList(); addCssCliJar(files, artifact); ArtifactResolutionResult result = repositorySystem.resolve(new ArtifactResolutionRequest() + .setOffline(offline) .setLocalRepository(localRepository) .setRemoteRepositories(new ArrayList<>(remoteRepositories)) .setResolveTransitively(true) diff --git a/maven/codenameone-maven-plugin/src/main/java/com/codename1/maven/CreateGuiFormMojo.java b/maven/codenameone-maven-plugin/src/main/java/com/codename1/maven/CreateGuiFormMojo.java index 9414c618607..b375cb8c28d 100644 --- a/maven/codenameone-maven-plugin/src/main/java/com/codename1/maven/CreateGuiFormMojo.java +++ b/maven/codenameone-maven-plugin/src/main/java/com/codename1/maven/CreateGuiFormMojo.java @@ -101,21 +101,7 @@ protected void executeImpl() throws MojoExecutionException, MojoFailureException String fileName = className.contains(".") ? className.substring(className.lastIndexOf(".") + 1) : className; - String javaSource = "package " + className.substring(0, className.lastIndexOf(".")) + ";\n" - + "public class " + fileName + " extends com.codename1.ui." + getGUIType() + " {\n" - + " public " + fileName + "() {\n" - + " this(com.codename1.ui.util.Resources.getGlobalResources());\n" - + " }\n" - + " \n" - + " public " + fileName + "(com.codename1.ui.util.Resources resourceObjectInstance) {\n" - + " initGuiBuilderComponents(resourceObjectInstance);\n" - + " }\n" - + " \n" - + "//-- DON'T EDIT BELOW THIS LINE!!!\n" - + " private void initGuiBuilderComponents(com.codename1.ui.util.Resources resourceObjectInstance) {\n" - + " }\n" - + "//-- DON'T EDIT ABOVE THIS LINE!!!\n" - + "}\n"; + String javaSource = scaffoldedSource(className.substring(0, className.lastIndexOf(".")), fileName); String xmlGUISource; if (getGUIType().equalsIgnoreCase("Container")) { @@ -140,6 +126,46 @@ protected void executeImpl() throws MojoExecutionException, MojoFailureException } + /** + * Builds the companion source in the format the GUI Builder reads and rewrites. The editor + * replaces everything between the gui-builder-generated markers on every save and preserves the + * user-code region, so a scaffold without those markers is one the editor cannot write the + * designed component tree into. + * + * @param packageName the package of the generated form + * @param fileName the simple class name of the generated form + * @return the Java source to scaffold + */ + String scaffoldedSource(String packageName, String fileName) { + boolean container = "Container".equalsIgnoreCase(getGUIType()); + String superClass = container ? "Container" : "Dialog".equalsIgnoreCase(getGUIType()) ? "Dialog" : "Form"; + String layout = "LayeredLayout".equals(getLayout()) ? "new LayeredLayout()" : "new FlowLayout()"; + String superCall = container ? "super(" + layout + ");" : "super(\"" + fileName + "\", " + layout + ");"; + return "// \n" + + "package " + packageName + ";\n" + + "\n" + + "import com.codename1.ui.*;\n" + + "import com.codename1.ui.events.ActionEvent;\n" + + "import com.codename1.ui.layouts.*;\n" + + "\n" + + "// Generated live from " + fileName + ".gui.\n" + + "public class " + fileName + " extends " + superClass + " {\n" + + " public " + fileName + "() {\n" + + " " + superCall + "\n" + + " buildUI();\n" + + " }\n" + + "\n" + + " private void buildUI() {\n" + + " }\n" + + "\n" + + "// \n" + + "// \n" + + "// \n" + + "// \n" + + "}\n" + + "// \n"; + } + protected String getGUIType() { return guiType; } diff --git a/maven/core-unittests/src/test/java/com/codename1/ui/SoftKeyCollisionTest.java b/maven/core-unittests/src/test/java/com/codename1/ui/SoftKeyCollisionTest.java index 636117c5cb2..89eb45503a9 100644 --- a/maven/core-unittests/src/test/java/com/codename1/ui/SoftKeyCollisionTest.java +++ b/maven/core-unittests/src/test/java/com/codename1/ui/SoftKeyCollisionTest.java @@ -41,12 +41,20 @@ class SoftKeyCollisionTest extends UITestBase { private static final class RecordingInput extends Component { final StringBuilder typed = new StringBuilder(); + boolean editsText = true; + RecordingInput() { setFocusable(true); - // Declares that this component owns the keyboard, exactly as the text editors do. + // Lists and editable sliders set this too, which is why it alone must not divert soft keys. setHandlesInput(true); } + /** Declares that key codes reaching this component are text, exactly as the editors do. */ + @Override + protected boolean consumesRawTextInput() { + return editsText; + } + @Override public void keyReleased(int keyCode) { typed.append((char) keyCode); @@ -92,6 +100,7 @@ void softKeysStillReachTheMenuBarWhenNothingIsEditing() { RecordingInput input = showFocusedInput(); // Hand the keyboard back: the component no longer claims raw input. input.setHandlesInput(false); + input.editsText = false; input.getComponentForm().keyReleased(LOWERCASE_P); @@ -101,4 +110,27 @@ void softKeysStillReachTheMenuBarWhenNothingIsEditing() { MenuBar.leftSK = previous; } } + + /** + * A list in single focus mode, an editable slider and a map all set handlesInput so the focus + * manager leaves their arrow keys alone. None of them turn key codes into characters, so a soft + * key must still reach the menu bar and fire its command while one of them holds the focus. + */ + @Test + void aComponentThatMerelyHandlesInputDoesNotSwallowSoftKeys() { + int previous = MenuBar.leftSK; + MenuBar.leftSK = LOWERCASE_P; + try { + RecordingInput input = showFocusedInput(); + input.editsText = false; + assertTrue(input.handlesInput(), "this is the case being guarded against"); + + input.getComponentForm().keyReleased(LOWERCASE_P); + + assertEquals("", input.typed.toString(), + "handlesInput alone must not divert a soft key away from the menu bar"); + } finally { + MenuBar.leftSK = previous; + } + } } diff --git a/scripts/guibuilder/build-and-run.sh b/scripts/guibuilder/build-and-run.sh index 46be289552c..49acd371021 100755 --- a/scripts/guibuilder/build-and-run.sh +++ b/scripts/guibuilder/build-and-run.sh @@ -47,8 +47,11 @@ cd "$REPO_ROOT/maven" # stale ones, which is how fixes kept reaching the tests but not the running editor. Remove the # output directory outright. rm -rf core/target factory/target css-compiler/target +# Offline: an online build resolves codenameone-core / codenameone-javase from the remote snapshot +# repository and installs it over the jar just built here, which is how the editor kept running +# without changes that had demonstrably compiled. JAVA_HOME="$JDK8" PATH="$JDK8/bin:$PATH" \ - mvn -q -Dmaven.repo.local="$LOCAL_REPO" -pl factory,core,css-compiler \ + mvn -q -o -Dmaven.repo.local="$LOCAL_REPO" -pl factory,core,css-compiler \ -DskipTests -Dmaven.javadoc.skip=true clean install # mvn install has repeatedly reported success while leaving the previous jar in place, so the # freshly built artifacts are copied over it explicitly. Trusting install is what made fixes compile @@ -71,19 +74,34 @@ JAVA_HOME="$JDK8" verify_core_class "com/codename1/ui/editor/EditorView.class" " echo "==> javase port (JDK 8)" JAVA_HOME="$JDK8" PATH="$JDK8/bin:$PATH" \ - mvn -q -Dcn1.binaries="$REPO_ROOT/maven/target/cn1-binaries" -Dmaven.repo.local="$LOCAL_REPO" \ + mvn -q -o -Dcn1.binaries="$REPO_ROOT/maven/target/cn1-binaries" -Dmaven.repo.local="$LOCAL_REPO" \ -pl javase -DskipTests -Dmaven.javadoc.skip=true clean install +# The port is copied over the installed artifact for the same reason core is, with one addition: +# an unrelated build in this repository has resolved codenameone-javase from a remote snapshot and +# installed it over the local one, after which Display.init returns a null implementation and every +# test in the suite fails at once. Run other Maven commands against this repository offline (-o). +for jar in "$REPO_ROOT/maven/javase/target/"*.jar; do + [ -e "$jar" ] || continue + case "$jar" in *-sources.jar|*-javadoc.jar|*jar-with-dependencies.jar) continue;; esac + dest="$LOCAL_REPO/com/codenameone/codenameone-javase/8.0-SNAPSHOT/$(basename "$jar")" + [ -e "$dest" ] && cp -f "$jar" "$dest" +done + +# Checked again after the port build: that build resolves core as a dependency and has replaced the +# installed jar with a remote snapshot, undoing everything verified above. +JAVA_HOME="$JDK8" verify_core_class "com/codename1/ui/CodeEditor.class" "setCursorPosition" +JAVA_HOME="$JDK8" verify_core_class "com/codename1/ui/Form.class" "focusedHandlesInput" cd "$REPO_ROOT/scripts/guibuilder" if [ "$run_tests" = "1" ]; then echo "==> tests (JDK 21)" JAVA_HOME="$JDK21" PATH="$JDK21/bin:$PATH" \ - mvn -nsu -Dmaven.repo.local="$LOCAL_REPO" -pl javase -am test -Dcodename1.platform=javase + mvn -nsu -o -Dmaven.repo.local="$LOCAL_REPO" -pl javase -am test -Dcodename1.platform=javase fi echo "==> package (JDK 21)" JAVA_HOME="$JDK21" PATH="$JDK21/bin:$PATH" \ - mvn -nsu -q -Dmaven.repo.local="$LOCAL_REPO" -pl javase -am -Pexecutable-jar package \ + mvn -nsu -o -q -Dmaven.repo.local="$LOCAL_REPO" -pl javase -am -Pexecutable-jar package \ -Dcodename1.platform=javase -Dmaven.test.skip=true if [ "$launch" = "0" ]; then diff --git a/scripts/guibuilder/common/src/main/java/com/codename1/guibuilder/CodenameOneGUIBuilder.java b/scripts/guibuilder/common/src/main/java/com/codename1/guibuilder/CodenameOneGUIBuilder.java index 2fcd927b94a..8a7ccddb1f0 100644 --- a/scripts/guibuilder/common/src/main/java/com/codename1/guibuilder/CodenameOneGUIBuilder.java +++ b/scripts/guibuilder/common/src/main/java/com/codename1/guibuilder/CodenameOneGUIBuilder.java @@ -94,6 +94,7 @@ public class CodenameOneGUIBuilder extends Lifecycle { private Container inspectorHost; private Label status; private String paletteFilter = ""; + private Map javaNames = new LinkedHashMap<>(); private String clipboardXml; private boolean darkMode; private String canvasMode = "phonePortrait"; @@ -530,15 +531,25 @@ private void refreshForms() { private void switchForm(String path) { cancelDesignerDrag(); - if (document != null && document.isModified() && !com.codename1.ui.Dialog.show("Unsaved changes", "Save changes before switching forms?", "Save", "Discard")) { - openForm(path); - return; + if (document != null && document.isModified()) { + if (com.codename1.ui.Dialog.show("Unsaved changes", "Save changes before switching forms?", "Save", "Discard")) { + // Staying on a form whose save failed is the only way to keep the work: opening the + // next form replaces the document and the edits are gone. + if (!save()) { + setStatus("Still editing " + relativeFormName(document.path()) + "; the save failed"); + return; + } + } } - if (document != null && document.isModified()) save(); openForm(path); } - private void openForm(String path) { + /** + * @return true when the form was parsed and is now the document being edited. Callers that + * report success to a client have to know: a malformed file left the previous form on + * screen while the caller announced the new one. + */ + private boolean openForm(String path) { try { cancelDesignerDrag(); document = GuiDocument.parse(path, ProjectIO.read(path)); @@ -549,8 +560,10 @@ private void openForm(String path) { refreshForms(); refreshEditor(); setStatus("Editing " + relativeFormName(path)); + return true; } catch (Exception ex) { ToastBar.showErrorMessage("Unable to open GUI file: " + ex.getMessage()); + return false; } } @@ -3889,23 +3902,40 @@ private void toggleDarkMode() { refreshEditor(); } - private void save() { - if (document == null) return; + /** + * @return true when the form reached disk. Callers that are about to replace the in-memory + * document have to know: a read only file or a full disk otherwise discarded the edits the + * user had just asked to keep. + */ + private boolean save() { + if (document == null) return true; try { ProjectIO.write(document.path(), document.toXml()); document.markSaved(); recordAction("saved", "form", relativeFormName(document.path())); setStatus("Saved " + relativeFormName(document.path())); ToastBar.showMessage("GUI form saved", FontImage.MATERIAL_CHECK); + return true; } catch (IOException ex) { ToastBar.showErrorMessage("Save failed: " + ex.getMessage()); + return false; } } private void refreshProject() { guiFiles = ProjectIO.findGuiFiles(binding.guiDir()); refreshForms(); - if (document != null) openForm(document.path()); + if (document == null) return; + // Re-reading the form from disk throws away everything edited since the last save. Refresh + // is about picking up files added outside the editor, so it must not cost the user work. + if (document.isModified() + && !com.codename1.ui.Dialog.show("Unsaved changes", + "Reloading " + relativeFormName(document.path()) + + " from disk discards the changes you have not saved.", "Reload", "Keep editing")) { + setStatus("Project refreshed; kept your unsaved changes"); + return; + } + openForm(document.path()); } private void showEmptyProject() { @@ -4236,6 +4266,7 @@ private String companionSourcePath() { } private String defaultCompanionSource() { + assignJavaNames(); String form = relativeFormName(document.path()); int dot = form.lastIndexOf('.'); String packageName = dot < 0 ? "" : form.substring(0, dot); @@ -4247,7 +4278,8 @@ private String defaultCompanionSource() { StringBuilder out = new StringBuilder(); out.append("// \n"); if (packageName.length() > 0) out.append("package ").append(packageName).append(";\n\n"); - out.append("import com.codename1.components.SpanLabel;\n") + out.append("import com.codename1.components.Accordion;\n") + .append("import com.codename1.components.SpanLabel;\n") .append("import com.codename1.ui.*;\n") .append("import com.codename1.ui.events.ActionEvent;\n") .append("import com.codename1.ui.geom.Dimension;\n") @@ -4256,8 +4288,19 @@ private String defaultCompanionSource() { if (bindingEnabled) out.append(annotationBinding ? "import com.codename1.binding.Binding;\nimport com.codename1.binding.Binders;\n" : "import com.codename1.properties.UiBinding;\n"); + // A .gui whose root is a Container is a reusable piece of UI, not a screen, and a Dialog is + // neither. Generating "extends Form" for all three produced a class the project could not + // use as the type it was designed as. Dialog takes the same (title, layout) constructor Form + // does; Container takes the layout alone. + String rootType = value(document.root(), "type", "Form"); + boolean containerRoot = "Container".equals(rootType); + String superClass = containerRoot ? "Container" : "Dialog".equals(rootType) ? "Dialog" : "Form"; + String superCall = containerRoot + ? "super(" + layoutSource(document.root()) + ");\n" + : "super(\"" + javaEscape(value(document.root(), "title", className)) + "\", " + + layoutSource(document.root()) + ");\n"; out.append("\n// Generated live from ").append(relativeFormName(document.path())).append(".gui.\n") - .append("public class ").append(className).append(" extends Form {\n"); + .append("public class ").append(className).append(" extends ").append(superClass).append(" {\n"); if (bindingEnabled) { out.append(" private final ").append(modelName).append(" model;\n") .append(annotationBinding ? " private Binding binding;\n" : " private UiBinding.Binding binding;\n"); @@ -4269,15 +4312,13 @@ private String defaultCompanionSource() { if (bindingEnabled) { out.append("\n public ").append(className).append("() { this(new ").append(modelName).append("()); }\n\n") .append(" public ").append(className).append("(").append(modelName).append(" model) {\n") - .append(" super(\"").append(javaEscape(value(document.root(), "title", className))).append("\", ") - .append(layoutSource(document.root())).append(");\n") + .append(" ").append(superCall) .append(" this.model = model;\n buildUI();\n") .append(annotationBinding ? " binding = Binders.bind(model, this);\n }\n\n" : " binding = new UiBinding().bind(model, this);\n }\n\n"); } else { out.append("\n public ").append(className).append("() {\n") - .append(" super(\"").append(javaEscape(value(document.root(), "title", className))).append("\", ") - .append(layoutSource(document.root())).append(");\n buildUI();\n }\n\n"); + .append(" ").append(superCall).append(" buildUI();\n }\n\n"); } out.append(" private void buildUI() {\n"); appendGeneratedChildren(out, document.root(), "this", " "); @@ -4294,8 +4335,7 @@ private String defaultCompanionSource() { out.append("// \n") .append("// \n"); for (String handler : generatedHandlers()) { - out.append(" protected void ").append(handler).append("(ActionEvent event) {\n") - .append(" // Add behavior here.\n }\n\n"); + out.append(handlerStub(handler)); } return out.append("// \n") .append("// \n}\n// \n").toString(); @@ -4311,6 +4351,7 @@ private void appendGeneratedChildren(StringBuilder out, Element parent, String p .append(indent).append(name).append(".setName(\"").append(javaEscape(value(child, "name", name))).append("\");\n"); if (child.getAttribute("uiid") != null) out.append(indent).append(name).append(".setUIID(\"") .append(javaEscape(child.getAttribute("uiid"))).append("\");\n"); + appendGeneratedProperties(out, child, name, type, indent); String handler = child.getAttribute("actionEvent"); if (handler != null && handler.length() > 0 && ("Button".equals(type) || "CheckBox".equals(type) || "RadioButton".equals(type))) { out.append(indent).append(name).append(".addActionListener(this::").append(javaIdentifier(handler)).append(");\n"); @@ -4318,6 +4359,9 @@ private void appendGeneratedChildren(StringBuilder out, Element parent, String p if ("Tabs".equals(value(parent, "type", ""))) { out.append(indent).append(parentName).append(".addTab(\"").append(javaEscape(value(child, "name", "Tab"))) .append("\", ").append(name).append(");\n"); + } else if ("Accordion".equals(value(parent, "type", ""))) { + out.append(indent).append(parentName).append(".addContent(\"") + .append(javaEscape(value(child, "name", "Section"))).append("\", ").append(name).append(");\n"); } else if ("BorderLayout".equals(value(parent, "layout", "BoxLayout"))) { out.append(indent).append(parentName).append(".add(BorderLayout.") .append(GuiDocument.effectiveBorderConstraint(parent, child).toUpperCase()).append(", ").append(name).append(");\n"); @@ -4327,10 +4371,15 @@ private void appendGeneratedChildren(StringBuilder out, Element parent, String p .append(GuiDocument.effectiveTableRow(parent, child)) .append(", ").append(GuiDocument.effectiveTableColumn(parent, child)).append(")") .append(".horizontalSpan(").append(value(child, "tableHorizontalSpan", "1")).append(")") - .append(".verticalSpan(").append(value(child, "tableVerticalSpan", "1")).append("), ") - .append(name).append(");\n"); + .append(".verticalSpan(").append(value(child, "tableVerticalSpan", "1")).append(")") + // The preview honours these percentages, so a form designed against them + // collapses to preferred-size columns at runtime when they are dropped here. + .append(percentageConstraint(child, "tableWidth", "widthPercentage")) + .append(percentageConstraint(child, "tableHeight", "heightPercentage")) + .append(", ").append(name).append(");\n"); } else out.append(indent).append(parentName).append(".add(").append(name).append(");\n"); if (GuiDocument.acceptsChildren(child)) appendGeneratedChildren(out, child, name, indent); + appendGeneratedTabState(out, child, name, type, indent); } if ("LayeredLayout".equals(value(parent, "layout", "BoxLayout"))) { for (Element child : componentChildren(parent)) { @@ -4355,7 +4404,126 @@ private void appendGeneratedChildren(StringBuilder out, Element parent, String p } } + /** + * Emits the inspector properties that the preview applies through + * {@code ComponentPreviewFactory.applyAttributes}. Without these the designer showed a component + * as configured while the generated form fell back to every default, so the running app did not + * match the design. Only attributes the document actually carries are emitted, and only for the + * types that really own the setter, so the generated source stays readable and always compiles. + */ + private void appendGeneratedProperties(StringBuilder out, Element element, String name, String type, + String indent) { + appendBooleanSetter(out, element, name, indent, "enabled", "setEnabled"); + appendBooleanSetter(out, element, name, indent, "visible", "setVisible"); + appendBooleanSetter(out, element, name, indent, "rtl", "setRTL"); + if (isLabelType(type) || "SpanLabel".equals(type)) { + appendIntSetter(out, element, name, indent, "gap", "setGap"); + } + if (isLabelType(type) || "TextArea".equals(type) || "TextField".equals(type)) { + String alignment = element.getAttribute("alignment"); + if (alignment != null) out.append(indent).append(name).append(".setAlignment(Component.") + .append(alignmentConstant(alignment)).append(");\n"); + } + if (isLabelType(type)) { + appendBooleanSetter(out, element, name, indent, "tickerEnabled", "setTickerEnabled"); + } + if ("Button".equals(type)) appendBooleanSetter(out, element, name, indent, "toggle", "setToggle"); + if ("CheckBox".equals(type) || "RadioButton".equals(type)) { + appendBooleanSetter(out, element, name, indent, "selected", "setSelected"); + } + if ("TextField".equals(type) || "TextArea".equals(type)) { + appendIntSetter(out, element, name, indent, "columns", "setColumns"); + appendIntSetter(out, element, name, indent, "maxSize", "setMaxSize"); + appendBooleanSetter(out, element, name, indent, "editable", "setEditable"); + appendBooleanSetter(out, element, name, indent, "growByContent", "setGrowByContent"); + String constraint = element.getAttribute("constraint"); + if (constraint != null) out.append(indent).append(name).append(".setConstraint(TextArea.") + .append(constraintConstant(constraint)).append(");\n"); + } + if ("TextArea".equals(type)) appendIntSetter(out, element, name, indent, "rows", "setRows"); + if ("Slider".equals(type)) { + appendIntSetter(out, element, name, indent, "minValue", "setMinValue"); + appendIntSetter(out, element, name, indent, "maxValue", "setMaxValue"); + appendIntSetter(out, element, name, indent, "progress", "setProgress"); + appendBooleanSetter(out, element, name, indent, "editable", "setEditable"); + appendBooleanSetter(out, element, name, indent, "infinite", "setInfinite"); + } + if (GuiDocument.acceptsChildren(element)) { + appendBooleanSetter(out, element, name, indent, "scrollableX", "setScrollableX"); + appendBooleanSetter(out, element, name, indent, "scrollableY", "setScrollableY"); + } + } + + /** + * Tab placement and selection are emitted after the tabs themselves, because selecting an index + * on an empty Tabs throws. + */ + private void appendGeneratedTabState(StringBuilder out, Element element, String name, String type, + String indent) { + if (!"Tabs".equals(type)) return; + String placement = element.getAttribute("tabPlacement"); + if (placement != null) out.append(indent).append(name).append(".setTabPlacement(Component.") + .append(tabPlacementConstant(placement)).append(");\n"); + Integer selected = parseInteger(element.getAttribute("selectedIndex")); + int tabs = componentChildren(element).size(); + if (selected != null && selected.intValue() >= 0 && selected.intValue() < tabs) { + out.append(indent).append(name).append(".setSelectedIndex(").append(selected.intValue()).append(", false);\n"); + } + } + + private void appendBooleanSetter(StringBuilder out, Element element, String name, String indent, + String attribute, String setter) { + String raw = element.getAttribute(attribute); + if (raw == null) return; + out.append(indent).append(name).append('.').append(setter).append('(') + .append("true".equals(raw) ? "true" : "false").append(");\n"); + } + + private void appendIntSetter(StringBuilder out, Element element, String name, String indent, + String attribute, String setter) { + Integer parsed = parseInteger(element.getAttribute(attribute)); + if (parsed == null) return; + out.append(indent).append(name).append('.').append(setter).append('(').append(parsed.intValue()).append(");\n"); + } + + private String percentageConstraint(Element element, String attribute, String method) { + Integer parsed = parseInteger(element.getAttribute(attribute)); + return parsed == null || parsed.intValue() < 0 ? "" : "." + method + "(" + parsed.intValue() + ")"; + } + + private boolean isLabelType(String type) { + return "Label".equals(type) || "Button".equals(type) || "CheckBox".equals(type) + || "RadioButton".equals(type); + } + + private static String alignmentConstant(String value) { + if ("center".equalsIgnoreCase(value)) return "CENTER"; + if ("right".equalsIgnoreCase(value)) return "RIGHT"; + return "LEFT"; + } + + private static String tabPlacementConstant(String value) { + if ("bottom".equalsIgnoreCase(value)) return "BOTTOM"; + if ("left".equalsIgnoreCase(value)) return "LEFT"; + if ("right".equalsIgnoreCase(value)) return "RIGHT"; + return "TOP"; + } + + private static String constraintConstant(String value) { + if ("EMAILADDR".equals(value) || "PASSWORD".equals(value) || "NUMERIC".equals(value) + || "URL".equals(value)) { + return value; + } + return "ANY"; + } + + private String handlerStub(String handler) { + return " protected void " + handler + "(ActionEvent event) {\n" + + " // Add behavior here.\n }\n\n"; + } + private String generatedModelSource() { + assignJavaNames(); String form = relativeFormName(document.path()); int dot = form.lastIndexOf('.'); String packageName = dot < 0 ? "" : form.substring(0, dot); @@ -4420,6 +4588,10 @@ private String componentSource(Element element) { String source; if ("Container".equals(type)) source = "new Container(" + layoutSource(element) + ")"; else if ("Tabs".equals(type)) source = "new Tabs()"; + // Accordion is a child-accepting type the document model already supports, and it lives in + // com.codename1.components with no String constructor, so the generic "new Type(text)" + // fallback produced source that does not compile. + else if ("Accordion".equals(type)) source = "new Accordion()"; else if ("Slider".equals(type)) source = "new Slider()"; else if ("TextField".equals(type)) source = "new TextField(" + text + ", \"" + javaEscape(value(element, "hint", "")) + "\")"; else if ("TextArea".equals(type)) source = "new TextArea(" + text + ")"; @@ -4457,13 +4629,57 @@ private List generatedHandlers() { return handlers; } - private String javaName(Element element) { return javaIdentifier(value(element, "name", value(element, "type", "component"))); } + /** + * The Java name of a component, unique across the document. A .gui may legitimately name two + * components "foo-bar" and "foo_bar", or name one "class"; both produce companion source that + * does not compile unless the names are disambiguated here. + */ + private String javaName(Element element) { + String assigned = javaNames.get(element); + return assigned != null ? assigned : javaIdentifier(value(element, "name", value(element, "type", "component"))); + } + + /** Recomputed for every generation because a rename changes every name after it. */ + private void assignJavaNames() { + javaNames = new LinkedHashMap<>(); + Set used = new LinkedHashSet<>(); + // the names the generated class itself declares + used.add("model"); + used.add("binding"); + used.add("buildUI"); + for (Element element : document.components()) { + if (element == document.root()) continue; + String base = javaIdentifier(value(element, "name", value(element, "type", "component"))); + String candidate = base; + int suffix = 2; + while (!used.add(candidate)) { + candidate = base + suffix; + suffix++; + } + javaNames.put(element, candidate); + } + } + private String javaIdentifier(String value) { String cleaned = value == null ? "component" : value.replaceAll("[^A-Za-z0-9_$]", "_"); if (cleaned.length() == 0) return "component"; char first = cleaned.charAt(0); boolean validStart = first == '_' || first == '$' || first >= 'A' && first <= 'Z' || first >= 'a' && first <= 'z'; - return validStart ? cleaned : "_" + cleaned; + String identifier = validStart ? cleaned : "_" + cleaned; + // A component may legitimately be called "class" or "new"; the field or method generated + // from it may not. + return isJavaReserved(identifier) ? identifier + "_" : identifier; + } + + private static final String JAVA_RESERVED = "|abstract|assert|boolean|break|byte|case|catch|" + + "char|class|const|continue|default|do|double|else|enum|extends|false|final|finally|" + + "float|for|goto|if|implements|import|instanceof|int|interface|long|native|new|null|" + + "package|private|protected|public|return|short|static|strictfp|super|switch|" + + "synchronized|this|throw|throws|transient|true|try|void|volatile|while|_|var|record|" + + "sealed|permits|yield|"; + + private static boolean isJavaReserved(String identifier) { + return JAVA_RESERVED.indexOf("|" + identifier + "|") >= 0; } private String javaEscape(String value) { return value == null ? "" : value.replace("\\", "\\\\").replace("\"", "\\\"").replace("\n", "\\n").replace("\r", ""); @@ -4485,7 +4701,9 @@ private String mergeGeneratedSource(String existing, String generated) { int oldStart = existing.indexOf(startMarker); int oldEnd = existing.indexOf(endMarker); if (oldStart < 0 || oldEnd < oldStart) { - return existing.indexOf("// Generated live from ") >= 0 ? generated : existing; + if (existing.indexOf("// Generated live from ") >= 0) return generated; + String migrated = migrateLegacySource(existing, generated); + return migrated != null ? migrated : existing; } int newStart = generated.indexOf(startMarker); int newEnd = generated.indexOf(endMarker); @@ -4494,6 +4712,134 @@ private String mergeGeneratedSource(String existing, String generated) { return generated.substring(0, newStart + startMarker.length()) + userCode + generated.substring(newEnd); } + static final String LEGACY_GENERATED_START = "//-- DON'T EDIT BELOW THIS LINE!!!"; + static final String LEGACY_GENERATED_END = "//-- DON'T EDIT ABOVE THIS LINE!!!"; + + /** + * Rewrites a companion source scaffolded by an older {@code cn1:create-gui-form} into this + * editor's format. Those files carry {@code //-- DON'T EDIT} markers and an empty + * {@code initGuiBuilderComponents}, which this editor does not write to, so without this a form + * designed in a project created by the old scaffolder saved its .gui and produced an empty + * screen at runtime. Anything the developer added to the class is carried into the user-code + * region; the old constructors and the old generated block are dropped because the new + * generated region replaces both. + * + * @return the migrated source, or null when this is not a legacy scaffolded file + */ + String migrateLegacySource(String existing, String generated) { + int legacyStart = existing.indexOf(LEGACY_GENERATED_START); + int legacyEnd = existing.indexOf(LEGACY_GENERATED_END); + if (legacyStart < 0 || legacyEnd < legacyStart) return null; + String carried = legacyUserMembers(existing, legacyStart, legacyEnd); + String marker = "// "; + if (carried.length() == 0) return generated; + String merged = dropStubsAlreadyWritten(generated, carried); + int insert = merged.indexOf(marker); + if (insert < 0) return merged; + insert += marker.length(); + return merged.substring(0, insert) + "\n" + carried + "\n" + merged.substring(insert); + } + + /** + * Removes the empty handler stubs for events the migrated code already implements. The old + * scaffold kept the developer's handlers outside its generated block, so carrying them over + * without this produced a class declaring the same method twice. + */ + private String dropStubsAlreadyWritten(String generated, String carried) { + String out = generated; + for (String handler : generatedHandlers()) { + if (carried.indexOf(handler + "(") < 0) continue; + out = out.replace(handlerStub(handler), ""); + } + return out; + } + + private String legacyUserMembers(String existing, int legacyStart, int legacyEnd) { + int bodyStart = existing.indexOf('{', classDeclaration(existing)); + int bodyEnd = existing.lastIndexOf('}'); + if (bodyStart < 0 || bodyEnd <= bodyStart) return ""; + String body = existing.substring(bodyStart + 1, bodyEnd); + int offset = bodyStart + 1; + if (legacyStart > offset && legacyEnd > legacyStart) { + int endOfLine = existing.indexOf('\n', legacyEnd); + int cut = (endOfLine < 0 ? existing.length() : endOfLine + 1) - offset; + body = body.substring(0, legacyStart - offset) + body.substring(Math.min(cut, body.length())); + } + return removeConstructors(body, className(existing)).trim(); + } + + private int classDeclaration(String source) { + int index = source.indexOf("class "); + return index < 0 ? 0 : index; + } + + private String className(String source) { + int index = classDeclaration(source); + if (index == 0 && !source.startsWith("class ")) return ""; + int start = index + "class ".length(); + int end = start; + while (end < source.length() && isIdentifierChar(source.charAt(end))) end++; + return source.substring(start, end); + } + + private static boolean isIdentifierChar(char c) { + return c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z' || c >= '0' && c <= '9' || c == '_' || c == '$'; + } + + /** + * Drops every constructor of the scaffolded class. The generated region declares its own, and + * the scaffolded ones call an {@code initGuiBuilderComponents} that no longer exists. + */ + private String removeConstructors(String body, String className) { + if (className.length() == 0) return body; + String out = body; + int from = 0; + while (true) { + int index = out.indexOf(className + "(", from); + if (index < 0) return out; + char before = index == 0 ? ' ' : out.charAt(index - 1); + int close = matchingBrace(out, out.indexOf(")", index)); + boolean instantiation = index >= 4 && "new ".equals(out.substring(index - 4, index)); + if (isIdentifierChar(before) || before == '.' || instantiation || close < 0) { + from = index + className.length(); + continue; + } + int lineStart = out.lastIndexOf("\n", index); + int cut = lineStart < 0 ? 0 : lineStart; + out = out.substring(0, cut) + out.substring(close + 1); + from = cut; + } + } + + /** + * Finds the closing brace of the block that starts after {@code afterIndex}, ignoring braces + * inside string and character literals so a constructor that builds text is not truncated. + */ + private int matchingBrace(String source, int afterIndex) { + if (afterIndex < 0) return -1; + int depth = 0; + boolean started = false; + char quote = 0; + for (int i = afterIndex; i < source.length(); i++) { + char c = source.charAt(i); + if (quote != 0) { + if (c == '\\') i++; + else if (c == quote) quote = 0; + continue; + } + if (c == '"' || c == '\'') { quote = c; continue; } + if (c == '{') { depth++; started = true; } + else if (c == '}') { + depth--; + if (started && depth == 0) return i; + if (depth < 0) return -1; + } else if (c == ';' && !started) { + return -1; + } + } + return -1; + } + private String resolveInitialForm() { String requested = binding.initialForm(); if (requested == null || requested.length() == 0) return null; @@ -4530,9 +4876,14 @@ private boolean hasText(String type) { || "CheckBox".equals(type) || "RadioButton".equals(type) || "TextField".equals(type) || "TextArea".equals(type); } + /** + * The fallback applies to an attribute that is absent, not to one the user deliberately + * emptied: clearing a label's text must leave it empty rather than bringing the sample text + * back. Structural attributes are removed rather than blanked, so they are unaffected. + */ private static String value(Element element, String attribute, String fallback) { String result = element == null ? null : element.getAttribute(attribute); - return result == null || result.length() == 0 ? fallback : result; + return result == null ? fallback : result; } private FontImage material(char icon, String uiid) { return FontImage.createMaterial(icon, UIManager.getInstance().getComponentStyle(uiid)); } private void setStatus(String message) { @@ -4680,7 +5031,7 @@ String mcpOpenForm(String formName) { String relative = relativeFormName(path); String simple = relative.substring(relative.lastIndexOf('.') + 1); if (formName.equals(relative) || formName.equals(simple) || formName.equals(path)) { - openForm(path); + if (!openForm(path)) return "Could not open " + relative; recordAction("form_opened", "form", relative, "source", "mcp"); return null; } @@ -4754,7 +5105,10 @@ else if ("before".equals(where) || "after".equals(where)) { String mcpCommand(String command) { if (command == null) return "command is required"; - if ("save".equals(command)) save(); + // A client that is told the save succeeded will happily go on to close the editor. + if ("save".equals(command)) { + if (!save()) return "Save failed; the form is still only in the editor"; + } else if ("undo".equals(command)) undo(); else if ("redo".equals(command)) redo(); else if ("refresh".equals(command)) refreshProject(); diff --git a/scripts/guibuilder/common/src/main/java/com/codename1/guibuilder/model/GuiDocument.java b/scripts/guibuilder/common/src/main/java/com/codename1/guibuilder/model/GuiDocument.java index 792ac0d3dbf..d6b07633ca1 100644 --- a/scripts/guibuilder/common/src/main/java/com/codename1/guibuilder/model/GuiDocument.java +++ b/scripts/guibuilder/common/src/main/java/com/codename1/guibuilder/model/GuiDocument.java @@ -231,7 +231,7 @@ private static void assignFreeTableCell(Element parent, Element child) { Set taken = new LinkedHashSet<>(); for (Element sibling : componentsIn(parent)) { if (sibling == child) continue; - taken.add(effectiveTableRow(parent, sibling) + ":" + effectiveTableColumn(parent, sibling)); + markOccupied(taken, parent, sibling); } for (int cursor = 0; ; cursor++) { int row = cursor / columns; @@ -247,6 +247,23 @@ private static void assignFreeTableCell(Element parent, Element child) { } } + /** + * Marks every cell a sibling covers, not just the one it starts in. A component with a span + * greater than one occupies a rectangle; treating it as a single cell handed the next component + * a slot underneath it, and TableLayout then placed two children in the same space. + */ + private static void markOccupied(Set taken, Element parent, Element sibling) { + int row = effectiveTableRow(parent, sibling); + int column = effectiveTableColumn(parent, sibling); + int rowSpan = Math.max(1, parseInt(sibling.getAttribute("tableVerticalSpan"), 1)); + int columnSpan = Math.max(1, parseInt(sibling.getAttribute("tableHorizontalSpan"), 1)); + for (int r = row; r < row + rowSpan; r++) { + for (int c = column; c < column + columnSpan; c++) { + taken.add(r + ":" + c); + } + } + } + /** The declared column count of a table, never below one. */ public static int tableColumns(Element parent) { return Math.max(1, parseInt(parent == null ? null : parent.getAttribute("tableLayoutColumns"), 2)); @@ -684,13 +701,25 @@ private static void collect(Element element, List result) { } } + /** Attributes whose value is the user's own text. Trimming these, or dropping them when they + * are set to the empty string, changes what the form says: the leading spaces in a label + * disappear, and clearing a text field brings the sample placeholder back instead of leaving it + * empty. Structural attributes stay normalized because a stray space in a cell index or a + * layout name is never intentional. */ + private static final String TEXTUAL_ATTRIBUTES = "|text|hint|title|"; + /** XMLParser stores case-insensitive attribute keys in lowercase. Always mutate that canonical * key and remove a possible camel-case duplicate created by older GUI Builder versions. */ private static void setNormalizedAttribute(Element element, String name, String value) { String canonical = name.toLowerCase(); element.removeAttribute(canonical); if (!canonical.equals(name)) element.removeAttribute(name); - if (value != null && value.trim().length() > 0) element.setAttribute(canonical, value.trim()); + if (value == null) return; + if (TEXTUAL_ATTRIBUTES.indexOf("|" + canonical + "|") >= 0) { + element.setAttribute(canonical, value); + return; + } + if (value.trim().length() > 0) element.setAttribute(canonical, value.trim()); } private void beforeMutation() { @@ -773,6 +802,12 @@ private static String escape(String value) { case '<' -> out.append("<"); case '>' -> out.append(">"); case '"' -> out.append("""); + // XML attribute normalization turns a literal newline, carriage return or tab into + // a space when the file is read back, so a multi line TextArea value quietly + // collapsed onto one line the first time the form was saved and reopened. + case '\n' -> out.append(" "); + case '\r' -> out.append(" "); + case '\t' -> out.append(" "); default -> out.append(c); } } diff --git a/scripts/guibuilder/common/src/main/java/com/codename1/guibuilder/project/ProjectIO.java b/scripts/guibuilder/common/src/main/java/com/codename1/guibuilder/project/ProjectIO.java index 59afd6f0697..c862da20653 100644 --- a/scripts/guibuilder/common/src/main/java/com/codename1/guibuilder/project/ProjectIO.java +++ b/scripts/guibuilder/common/src/main/java/com/codename1/guibuilder/project/ProjectIO.java @@ -82,16 +82,47 @@ public static boolean exists(String path) { return path != null && FileSystemStorage.getInstance().exists(fsUrl(path)); } + /** + * Writes through a sibling temporary file so a failed write cannot destroy the previous + * content. Opening the target directly truncates it before the first byte arrives, which turns + * a full disk or a killed process into an empty form, stylesheet or source file. + */ public static void write(String path, String content) throws IOException { + FileSystemStorage fs = FileSystemStorage.getInstance(); + String url = fsUrl(path); + ensureParent(url); + String temporary = url + ".cn1tmp"; OutputStream out = null; try { - String url = fsUrl(path); - ensureParent(url); - out = FileSystemStorage.getInstance().openOutputStream(url); + out = fs.openOutputStream(temporary); out.write(content.getBytes("UTF-8")); + out.close(); + out = null; + } catch (IOException ex) { + Util.cleanup(out); + fs.delete(temporary); + throw ex; } finally { Util.cleanup(out); } + if (!fs.exists(temporary)) { + throw new IOException("Failed to write " + path); + } + // rename() cannot replace an existing file on every platform, so the target is removed + // first. The window this opens is one rename wide and only after the new content is + // safely on disk, where the truncating write left it open for the whole serialization. + if (fs.exists(url)) { + fs.delete(url); + } + fs.rename(temporary, fileName(url)); + if (!fs.exists(url)) { + throw new IOException("Failed to replace " + path + " with the file just written"); + } + } + + private static String fileName(String url) { + int slash = url.lastIndexOf('/'); + return slash < 0 ? url : url.substring(slash + 1); } private static void ensureParent(String path) { @@ -104,8 +135,17 @@ private static void ensureParent(String path) { fs.mkdir(parent); } + /** + * Normalizes to forward slashes before building the URL. The Maven plugin hands this editor + * native paths, so on Windows every path arrives with backslashes; leaving them in place makes + * the separator arithmetic in {@code ensureParent} and {@code fileName} silently find nothing. + * A drive-lettered path becomes {@code file://C:/...}, which the JavaSE port maps back to + * {@code C:\...}. + */ public static String fsUrl(String path) { - if (path == null || path.startsWith("file://") || path.indexOf("://") > 0) return path; - return "file://" + path; + if (path == null) return null; + String normalized = path.replace('\\', '/'); + if (normalized.startsWith("file://") || normalized.indexOf("://") > 0) return normalized; + return "file://" + normalized; } } diff --git a/scripts/guibuilder/common/src/main/java/com/codename1/guibuilder/ui/ComponentPreviewFactory.java b/scripts/guibuilder/common/src/main/java/com/codename1/guibuilder/ui/ComponentPreviewFactory.java index 2955738e0dd..80c843441c0 100644 --- a/scripts/guibuilder/common/src/main/java/com/codename1/guibuilder/ui/ComponentPreviewFactory.java +++ b/scripts/guibuilder/common/src/main/java/com/codename1/guibuilder/ui/ComponentPreviewFactory.java @@ -301,9 +301,10 @@ private static Dimension guidedSize(Component component, Dimension natural, int return natural; } + /** See CodenameOneGUIBuilder#value: an emptied attribute is a value, not a missing one. */ private static String value(Element element, String name, String fallback) { String value = element.getAttribute(name); - return value == null || value.length() == 0 ? fallback : value; + return value == null ? fallback : value; } private static boolean supportsInlineContent(String type) { @@ -318,11 +319,22 @@ private static void applyAttributes(Component component, Element element) { component.setRTL("true".equals(value(element, "rtl", "false"))); if (component instanceof Label label) { label.setGap(integer(element, "gap", label.getGap())); - label.setAlignment(alignment(value(element, "alignment", "left"))); label.setTickerEnabled("true".equals(value(element, "tickerEnabled", "false"))); + } else if (component instanceof SpanLabel span) { + // SpanLabel is a Container rather than a Label, so the inspector's gap silently did + // nothing here while the generated source was free to set it. + span.setGap(integer(element, "gap", span.getGap())); + } + // Applied only when the document carries it: the generated source emits nothing for an + // absent attribute, and forcing LEFT here would make the preview disagree with a themed + // alignment. Labels and text inputs are the components that own the setter. + if (element.getAttribute("alignment") != null) { + if (component instanceof Label label) label.setAlignment(alignment(element.getAttribute("alignment"))); + else if (component instanceof TextArea area) area.setAlignment(alignment(element.getAttribute("alignment"))); } if (component instanceof Button button) button.setToggle("true".equals(value(element, "toggle", "false"))); if (component instanceof CheckBox check) check.setSelected("true".equals(value(element, "selected", "false"))); + else if (component instanceof RadioButton radio) radio.setSelected("true".equals(value(element, "selected", "false"))); if (component instanceof TextArea area) { area.setColumns(integer(element, "columns", area.getColumns())); area.setRows(integer(element, "rows", area.getRows())); diff --git a/scripts/guibuilder/demo-project/src/main/css/theme.css b/scripts/guibuilder/demo-project/src/main/css/theme.css index 38ca4e7f013..adb839c8da7 100644 --- a/scripts/guibuilder/demo-project/src/main/css/theme.css +++ b/scripts/guibuilder/demo-project/src/main/css/theme.css @@ -1,3 +1,26 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Codename One designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Codename One through http://www.codenameone.com/ if you + * need additional information or have any questions. + */ + Form { background-color: #ffffff; color: #172033; diff --git a/scripts/guibuilder/javase/pom.xml b/scripts/guibuilder/javase/pom.xml index 22d9cb1230c..30e8d6daa33 100644 --- a/scripts/guibuilder/javase/pom.xml +++ b/scripts/guibuilder/javase/pom.xml @@ -18,16 +18,16 @@ com.codenameone.guibuilder${cn1app.name}-common${project.version} com.codenameone.guibuilder${cn1app.name}-common${project.version}teststest - com.codenameonecodenameone-coretest - com.codenameonecodenameone-javasetest + + com.codenameonecodenameone-core + com.codenameonecodenameone-javase org.junit.jupiterjunit-jupiter-api5.10.2test org.junit.jupiterjunit-jupiter-engine5.10.2test executable-jarjavase - - com.codenameonecodenameone-corecompile - com.codenameonecodenameone-javasecompile - org.codehaus.mojoproperties-maven-plugin1.0.0initializeread-project-properties${basedir}/../common/codenameone_settings.properties com.codenameonecodenameone-maven-plugin${cn1.plugin.version}generate-desktop-app-wrappergenerate-sourcesgenerate-desktop-app-wrapper diff --git a/scripts/guibuilder/javase/src/test/java/com/codename1/guibuilder/DesignerInteractionTest.java b/scripts/guibuilder/javase/src/test/java/com/codename1/guibuilder/DesignerInteractionTest.java index 29167d2d996..dbadf2d4274 100644 --- a/scripts/guibuilder/javase/src/test/java/com/codename1/guibuilder/DesignerInteractionTest.java +++ b/scripts/guibuilder/javase/src/test/java/com/codename1/guibuilder/DesignerInteractionTest.java @@ -362,7 +362,15 @@ void layeredDropPersistsTheGuidedRectangleAndRerendersThere() throws Exception { plan.snapX = 150; plan.snapY = 150; assertTrue(builder.applyDropPlan(card, plan, plan.snapX, plan.snapY)); - assertEquals("78px 298px 238px 98px", card.getAttribute("layeredInsets"), document.toXml()); + // The exact numbers include the component's own margins, which the theme states in + // millimetres and therefore differ with the display density. What has to hold everywhere is + // that the drop was recorded as a fixed rectangle; where that rectangle lands is asserted + // below, on the rendered result. + String insets = card.getAttribute("layeredInsets"); + assertNotNull(insets, document.toXml()); + for (String side : insets.split(" ")) { + assertTrue(side.endsWith("px"), "a dropped rectangle must be pinned in pixels, was " + insets); + } Component rendered = ComponentPreviewFactory.create(document.root(), card, handler()); rendered.setWidth(500); @@ -439,8 +447,13 @@ void dockingToTheSurfaceEdgeReflowsWhenTheSurfaceWidthChanges() throws Exception assertEquals(narrowCard.getWidth(), wideCard.getWidth()); assertEquals(300, wideCard.getX() - narrowCard.getX(), 1, "right docking must reflow by the surface width delta, not preserve an absolute x"); - assertEquals(600, narrowCard.getX() + narrowCard.getWidth(), 4); - assertEquals(900, wideCard.getX() + wideCard.getWidth(), 4); + // The distance kept from the docked edge comes from the display density, so it is compared + // between the two surfaces rather than against a pixel count measured on one machine. + int narrowMargin = 600 - (narrowCard.getX() + narrowCard.getWidth()); + int wideMargin = 900 - (wideCard.getX() + wideCard.getWidth()); + assertEquals(narrowMargin, wideMargin, "docking must keep the same distance from either edge"); + assertTrue(narrowMargin >= 0 && narrowMargin <= 12, + "a component docked right must sit at the edge, but it was " + narrowMargin + "px away"); } @Test diff --git a/scripts/guibuilder/javase/src/test/java/com/codename1/guibuilder/GeneratedSourceTest.java b/scripts/guibuilder/javase/src/test/java/com/codename1/guibuilder/GeneratedSourceTest.java index 66851daeb95..f3ec706c1e9 100644 --- a/scripts/guibuilder/javase/src/test/java/com/codename1/guibuilder/GeneratedSourceTest.java +++ b/scripts/guibuilder/javase/src/test/java/com/codename1/guibuilder/GeneratedSourceTest.java @@ -25,18 +25,31 @@ import com.codename1.guibuilder.model.GuiDocument; import com.codename1.guibuilder.project.ProjectBinding; +import java.io.ByteArrayOutputStream; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; +import javax.swing.JPanel; import javax.tools.JavaCompiler; import javax.tools.ToolProvider; +import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; class GeneratedSourceTest { + /** + * Constructing the builder creates a UIManager, which needs a Display. Relying on another test + * class to have initialized it first passes locally and fails wherever the runner happens to + * order this class first. + */ + @BeforeAll + static void initializeCodenameOneRuntime() { + if (!com.codename1.ui.Display.isInitialized()) com.codename1.ui.Display.init(new JPanel()); + } + @Test void generatedFormAndBindingModelCompileTogether() throws Exception { CodenameOneGUIBuilder builder = builder("properties"); @@ -93,6 +106,169 @@ void guidedConstraintsAndLaterSiblingReferencesGenerateCompilableSource() throws compile(form, null); } + /** + * The designer applies these through ComponentPreviewFactory, so a component the user + * configured looked right on the canvas and came up with every default at runtime. + */ + @Test + void inspectorPropertiesReachTheGeneratedSource() throws Exception { + CodenameOneGUIBuilder builder = builder("none"); + set(builder, "document", GuiDocument.parse("/tmp/project/gui/com/example/LoginForm.gui", + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "")); + + String form = invoke(builder, "defaultCompanionSource"); + + assertTrue(form.contains("caption.setEnabled(false);"), form); + assertTrue(form.contains("caption.setVisible(false);")); + assertTrue(form.contains("caption.setRTL(true);")); + assertTrue(form.contains("caption.setGap(6);")); + assertTrue(form.contains("caption.setAlignment(Component.CENTER);")); + assertTrue(form.contains("caption.setTickerEnabled(true);")); + assertTrue(form.contains("blurb.setGap(4);")); + assertFalse(form.contains("blurb.setAlignment"), "SpanLabel has no setAlignment"); + assertTrue(form.contains("email.setColumns(20);")); + assertTrue(form.contains("email.setMaxSize(64);")); + assertTrue(form.contains("email.setEditable(false);")); + assertTrue(form.contains("email.setGrowByContent(false);")); + assertTrue(form.contains("email.setConstraint(TextArea.EMAILADDR);")); + assertTrue(form.contains("email.setAlignment(Component.RIGHT);")); + assertTrue(form.contains("notes.setRows(5);")); + assertTrue(form.contains("remember.setSelected(true);")); + assertTrue(form.contains("choice.setSelected(true);")); + assertTrue(form.contains("submit.setToggle(true);")); + assertTrue(form.contains("volume.setMinValue(10);")); + assertTrue(form.contains("volume.setProgress(40);")); + assertTrue(form.contains("volume.setInfinite(true);")); + assertTrue(form.contains("scroller.setScrollableX(true);")); + assertTrue(form.contains("scroller.setScrollableY(true);")); + assertTrue(form.contains("sections.setTabPlacement(Component.BOTTOM);")); + assertTrue(form.contains("sections.setSelectedIndex(1, false);")); + assertTrue(form.indexOf("sections.addTab(") < form.indexOf("sections.setSelectedIndex"), + "selecting a tab before the tabs exist throws at runtime"); + assertFalse(form.contains("submit.setEnabled"), "untouched properties must stay out of the source"); + + compile(form, null); + } + + /** + * An out of range index is worse than no index: the designer clamps it, the runtime throws. + */ + @Test + void anImpossibleTabSelectionIsNotGenerated() throws Exception { + CodenameOneGUIBuilder builder = builder("none"); + set(builder, "document", GuiDocument.parse("/tmp/project/gui/com/example/LoginForm.gui", + "" + + "" + + "" + + "")); + String form = invoke(builder, "defaultCompanionSource"); + assertFalse(form.contains("setSelectedIndex"), form); + compile(form, null); + } + + @Test + void tableCellPercentagesReachTheGeneratedSource() throws Exception { + CodenameOneGUIBuilder builder = builder("none"); + set(builder, "document", GuiDocument.parse("/tmp/project/gui/com/example/LoginForm.gui", + "" + + "" + + "" + + "" + + "")); + String form = invoke(builder, "defaultCompanionSource"); + assertTrue(form.contains(".widthPercentage(30)"), form); + assertTrue(form.contains(".widthPercentage(70).heightPercentage(50)"), form); + compile(form, null); + } + + @Test + void aContainerRootGeneratesAContainerAndADialogRootGeneratesADialog() throws Exception { + CodenameOneGUIBuilder builder = builder("none"); + set(builder, "document", GuiDocument.parse("/tmp/project/gui/com/example/Panel.gui", + "" + + "")); + String container = invoke(builder, "defaultCompanionSource"); + assertTrue(container.contains("public class Panel extends Container"), container); + assertTrue(container.contains("super(BoxLayout.y());"), container); + compile("Panel", container, null); + + set(builder, "document", GuiDocument.parse("/tmp/project/gui/com/example/Confirm.gui", + "" + + "")); + String dialog = invoke(builder, "defaultCompanionSource"); + assertTrue(dialog.contains("public class Confirm extends Dialog"), dialog); + assertTrue(dialog.contains("super(\"Confirm\", BoxLayout.y());"), dialog); + compile("Confirm", dialog, null); + } + + /** + * Projects scaffolded by cn1:create-gui-form before this editor existed carry the old markers. + * Refusing to touch them meant designing a form, saving, and running an empty screen. + */ + @Test + void aLegacyScaffoldedCompanionIsMigratedAndKeepsUserCode() throws Exception { + CodenameOneGUIBuilder builder = builder("none"); + String legacy = "package com.example;\n" + + "public class LoginForm extends com.codename1.ui.Form {\n" + + " public LoginForm() {\n" + + " this(com.codename1.ui.util.Resources.getGlobalResources());\n" + + " }\n" + + " \n" + + " public LoginForm(com.codename1.ui.util.Resources resourceObjectInstance) {\n" + + " initGuiBuilderComponents(resourceObjectInstance);\n" + + " }\n" + + " \n" + + " private void onSubmit(com.codename1.ui.events.ActionEvent event) {\n" + + " System.out.println(\"handled { } \");\n" + + " }\n" + + " \n" + + "//-- DON'T EDIT BELOW THIS LINE!!!\n" + + " private void initGuiBuilderComponents(com.codename1.ui.util.Resources resourceObjectInstance) {\n" + + " }\n" + + "//-- DON'T EDIT ABOVE THIS LINE!!!\n" + + "}\n"; + String generated = invoke(builder, "defaultCompanionSource"); + String merged = merge(builder, legacy, generated); + + assertTrue(merged.contains("email = new TextField"), "the component tree was never generated:\n" + merged); + assertTrue(merged.contains("private void onSubmit(com.codename1.ui.events.ActionEvent event)"), + "the developer's own method was lost:\n" + merged); + assertFalse(merged.contains("initGuiBuilderComponents"), "the legacy generated block survived:\n" + merged); + assertFalse(merged.contains("DON'T EDIT"), merged); + assertFalse(merged.contains("getGlobalResources"), "the legacy constructors survived:\n" + merged); + compile(merged, null); + } + + @Test + void aHandWrittenCompanionIsLeftAlone() throws Exception { + CodenameOneGUIBuilder builder = builder("none"); + String handWritten = "package com.example;\npublic class LoginForm extends com.codename1.ui.Form {\n}\n"; + assertEquals(handWritten, merge(builder, handWritten, invoke(builder, "defaultCompanionSource"))); + } + + private static String merge(CodenameOneGUIBuilder builder, String existing, String generated) throws Exception { + Method method = CodenameOneGUIBuilder.class.getDeclaredMethod("mergeGeneratedSource", String.class, String.class); + method.setAccessible(true); + return (String) method.invoke(builder, existing, generated); + } + private static CodenameOneGUIBuilder builder(String strategy) throws Exception { CodenameOneGUIBuilder builder = new CodenameOneGUIBuilder(); set(builder, "binding", ProjectBinding.parse( @@ -107,22 +283,35 @@ private static CodenameOneGUIBuilder builder(String strategy) throws Exception { } private static void compile(String form, String model) throws Exception { + compile("LoginForm", form, model); + } + + /** + * Compiles into the temporary directory rather than beside the sources: without {@code -d} the + * class files land in the working directory the test suite runs from. + */ + private static void compile(String className, String form, String model) throws Exception { Path root = Files.createTempDirectory("guibuilder-generated-source"); + Path classes = Files.createDirectories(root.resolve("classes")); Path pkg = Files.createDirectories(root.resolve("com/example")); - Path formFile = pkg.resolve("LoginForm.java"); + Path formFile = pkg.resolve(className + ".java"); Files.write(formFile, form.getBytes(StandardCharsets.UTF_8)); JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); assertNotNull(compiler); + ByteArrayOutputStream errors = new ByteArrayOutputStream(); int result; if (model == null) { - result = compiler.run(null, null, null, "-classpath", System.getProperty("java.class.path"), formFile.toString()); + result = compiler.run(null, null, errors, "-d", classes.toString(), + "-classpath", System.getProperty("java.class.path"), formFile.toString()); } else { - Path modelFile = pkg.resolve("LoginFormModel.java"); + Path modelFile = pkg.resolve(className + "Model.java"); Files.write(modelFile, model.getBytes(StandardCharsets.UTF_8)); - result = compiler.run(null, null, null, "-classpath", System.getProperty("java.class.path"), + result = compiler.run(null, null, errors, "-d", classes.toString(), + "-classpath", System.getProperty("java.class.path"), formFile.toString(), modelFile.toString()); } - assertEquals(0, result); + assertEquals(0, result, "the generated source did not compile:\n" + + errors.toString("UTF-8") + "\n" + form); } private static void set(Object target, String name, Object value) throws Exception { diff --git a/scripts/guibuilder/javase/src/test/java/com/codename1/guibuilder/ProjectIoTest.java b/scripts/guibuilder/javase/src/test/java/com/codename1/guibuilder/ProjectIoTest.java new file mode 100644 index 00000000000..3e9f41857f0 --- /dev/null +++ b/scripts/guibuilder/javase/src/test/java/com/codename1/guibuilder/ProjectIoTest.java @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Codename One designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Codename One through http://www.codenameone.com/ if you + * need additional information or have any questions. + */ + +package com.codename1.guibuilder; + +import com.codename1.guibuilder.project.ProjectIO; +import java.io.File; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import javax.swing.JPanel; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; + +import static org.junit.jupiter.api.Assertions.*; + +/** + * Saving is the one operation in this editor that can destroy work that already exists on disk, so + * it is checked directly rather than through the designer. + */ +class ProjectIoTest { + @BeforeAll + static void initializeCodenameOneRuntime() { + if (!com.codename1.ui.Display.isInitialized()) com.codename1.ui.Display.init(new JPanel()); + } + + @Test + void writingReplacesTheFileAndLeavesNoTemporaryBehind(@TempDir Path dir) throws Exception { + File target = dir.resolve("Form.gui").toFile(); + ProjectIO.write(target.getAbsolutePath(), ""); + assertEquals("", read(target)); + + ProjectIO.write(target.getAbsolutePath(), ""); + assertEquals("", read(target), "the second save did not replace the first"); + + for (File file : dir.toFile().listFiles()) { + assertFalse(file.getName().endsWith(".cn1tmp"), + "a temporary file survived the save: " + file.getName()); + } + } + + /** + * The truncate-then-write that this replaced left an empty file behind when the write failed. + * The target must not even be touched until the replacement content is complete on disk. + */ + @Test + void aFailedWriteLeavesThePreviousContentIntact(@TempDir Path dir) throws Exception { + File target = dir.resolve("Form.gui").toFile(); + ProjectIO.write(target.getAbsolutePath(), ""); + + // A directory where the temporary file has to go makes openOutputStream fail the way a full + // disk would, after the editor has already decided to save. + assertTrue(new File(target.getAbsolutePath() + ".cn1tmp").mkdir()); + try { + ProjectIO.write(target.getAbsolutePath(), ""); + fail("the failed write reported success"); + } catch (Exception expected) { + // the save is expected to fail; what matters is the state it leaves behind + } + assertEquals("", read(target), + "a failed save destroyed the previously saved form"); + } + + @Test + void writingCreatesMissingParentDirectories(@TempDir Path dir) throws Exception { + File target = dir.resolve("com").resolve("example").resolve("Form.gui").toFile(); + ProjectIO.write(target.getAbsolutePath(), ""); + assertEquals("", read(target)); + } + + /** + * Every path in this editor arrives from the Maven plugin in the platform's own notation, so on + * Windows the separator arithmetic runs over backslashes unless the URL is normalized first. + */ + @Test + void windowsPathsBecomeUsableFileUrls() { + assertEquals("file://C:/Users/dev/app/src/main/guibuilder", + ProjectIO.fsUrl("C:\\Users\\dev\\app\\src\\main\\guibuilder")); + assertEquals("file:///home/dev/app", ProjectIO.fsUrl("/home/dev/app")); + assertEquals("file:///home/dev/app", ProjectIO.fsUrl("file:///home/dev/app")); + assertNull(ProjectIO.fsUrl(null)); + } + + private static String read(File file) throws Exception { + return new String(Files.readAllBytes(file.toPath()), StandardCharsets.UTF_8); + } +} diff --git a/scripts/guibuilder/tools/guibuilder-mcp-client.mjs b/scripts/guibuilder/tools/guibuilder-mcp-client.mjs index 1174d9bc7ee..368273e7bcc 100644 --- a/scripts/guibuilder/tools/guibuilder-mcp-client.mjs +++ b/scripts/guibuilder/tools/guibuilder-mcp-client.mjs @@ -18,7 +18,15 @@ socket.on("data", chunk => { const line = buffer.slice(0, index); buffer = buffer.slice(index + 1); if (!line.trim()) continue; - const message = JSON.parse(line); + // A malformed or truncated line used to throw out of the data handler and kill the client, + // taking every pending request with it. Report it and keep reading the stream. + let message; + try { + message = JSON.parse(line); + } catch (error) { + process.stderr.write(`MCP_BAD_LINE ${error.message}: ${line}\n`); + continue; + } const handler = pending.get(message.id); if (handler) { pending.delete(message.id); From 9287cf7592d901336a2cdc38d4834808e5ca48f1 Mon Sep 17 00:00:00 2001 From: Shai Almog <67850168+shai-almog@users.noreply.github.com> Date: Thu, 6 Aug 2026 22:32:11 +0700 Subject: [PATCH 24/33] Give the GUI form scaffolder the project's copyright header Co-Authored-By: Claude Opus 5 (1M context) --- .../codename1/maven/CreateGuiFormMojo.java | 24 ++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/maven/codenameone-maven-plugin/src/main/java/com/codename1/maven/CreateGuiFormMojo.java b/maven/codenameone-maven-plugin/src/main/java/com/codename1/maven/CreateGuiFormMojo.java index b375cb8c28d..35232afd6d8 100644 --- a/maven/codenameone-maven-plugin/src/main/java/com/codename1/maven/CreateGuiFormMojo.java +++ b/maven/codenameone-maven-plugin/src/main/java/com/codename1/maven/CreateGuiFormMojo.java @@ -1,8 +1,26 @@ /* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. + * Copyright (c) 2026, Codename One and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Codename One designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Codename One through http://www.codenameone.com/ if you + * need additional information or have any questions. */ + package com.codename1.maven; import java.io.File; From ac77fcfd29b52573ccad47e1a66a02ad0c72a661 Mon Sep 17 00:00:00 2001 From: Shai Almog <67850168+shai-almog@users.noreply.github.com> Date: Thu, 6 Aug 2026 22:51:49 +0700 Subject: [PATCH 25/33] Document the GUI builder that ships, with screenshots of a new project The guide described the previous editor: an events tab that was disabled, a widget control pad, autosnap and smart-inset checkboxes, GUI files under res/, and "DON'T EDIT" comments. None of that exists in the editor this branch builds, and every screenshot showed the old window. The section now covers the three-column workspace, the palette and hierarchy, the device canvas, the three inspector tabs, auto layout in terms of the controls that are actually there, nested containers, and the built-in CSS and companion-source editors including what the generated and user-code regions mean. The IDE shortcuts the archetype now ships are listed, so a reader does not have to reach for the command line. The screenshots come from a project created with the archetype and a form created with cn1:create-gui-form, not from the test fixtures, so they show what a reader sees on their own first form. Images of the old builder are deleted; the guide's unused-image check would otherwise fail. The .gui example is replaced with one in the format the editor writes. Co-Authored-By: Claude Opus 5 (1M context) --- .../main/snippets/developer-guide/basics.xml | 12 +- docs/developer-guide/basics.asciidoc | 214 +++++------------- .../img/gui-builder-code-editor.png | Bin 0 -> 198006 bytes .../img/gui-builder-css-editor.png | Bin 0 -> 166486 bytes .../img/gui-builder-events.png | Bin 66795 -> 0 bytes .../img/gui-builder-in-place-edit.png | Bin 20952 -> 0 bytes .../img/gui-builder-java-and-gui-files.png | Bin 304470 -> 0 bytes .../img/gui-builder-workspace.png | Bin 0 -> 78299 bytes .../img/guibuilder-2-inset-control.png | Bin 1898 -> 0 bytes .../img/guibuilder-2-inset-fixed-button.png | Bin 961 -> 0 bytes .../img/guibuilder-2-insets-dropdown-menu.png | Bin 5259 -> 0 bytes .../guibuilder-2-link-button-unselected.png | Bin 907 -> 0 bytes ...er-2-smart-insets-auto-snap-checkboxes.png | Bin 3275 -> 0 bytes .../guibuilder-2-subcontainer-add-child-2.png | Bin 33331 -> 0 bytes .../guibuilder-2-subcontainer-add-child.png | Bin 33158 -> 0 bytes .../img/guibuilder-2-widget-control-pad.png | Bin 11060 -> 0 bytes ...ilder-component-tree-palette-inspector.png | Bin 111340 -> 0 bytes .../img/new-gui-builder-drag-button.png | Bin 109578 -> 0 bytes .../img/new-gui-builder-left-toolbar.png | Bin 27424 -> 0 bytes .../img/new-gui-builder-property-sheet.png | Bin 115542 -> 0 bytes .../img/new-gui-builder-right-toolbar.png | Bin 23464 -> 0 bytes 21 files changed, 58 insertions(+), 168 deletions(-) create mode 100644 docs/developer-guide/img/gui-builder-code-editor.png create mode 100644 docs/developer-guide/img/gui-builder-css-editor.png delete mode 100644 docs/developer-guide/img/gui-builder-events.png delete mode 100644 docs/developer-guide/img/gui-builder-in-place-edit.png delete mode 100644 docs/developer-guide/img/gui-builder-java-and-gui-files.png create mode 100644 docs/developer-guide/img/gui-builder-workspace.png delete mode 100644 docs/developer-guide/img/guibuilder-2-inset-control.png delete mode 100644 docs/developer-guide/img/guibuilder-2-inset-fixed-button.png delete mode 100644 docs/developer-guide/img/guibuilder-2-insets-dropdown-menu.png delete mode 100644 docs/developer-guide/img/guibuilder-2-link-button-unselected.png delete mode 100644 docs/developer-guide/img/guibuilder-2-smart-insets-auto-snap-checkboxes.png delete mode 100644 docs/developer-guide/img/guibuilder-2-subcontainer-add-child-2.png delete mode 100644 docs/developer-guide/img/guibuilder-2-subcontainer-add-child.png delete mode 100644 docs/developer-guide/img/guibuilder-2-widget-control-pad.png delete mode 100644 docs/developer-guide/img/new-gui-builder-component-tree-palette-inspector.png delete mode 100644 docs/developer-guide/img/new-gui-builder-drag-button.png delete mode 100644 docs/developer-guide/img/new-gui-builder-left-toolbar.png delete mode 100644 docs/developer-guide/img/new-gui-builder-property-sheet.png delete mode 100644 docs/developer-guide/img/new-gui-builder-right-toolbar.png diff --git a/docs/demos/common/src/main/snippets/developer-guide/basics.xml b/docs/demos/common/src/main/snippets/developer-guide/basics.xml index 9e3bc8f3a19..971835f27f0 100644 --- a/docs/demos/common/src/main/snippets/developer-guide/basics.xml +++ b/docs/demos/common/src/main/snippets/developer-guide/basics.xml @@ -3,11 +3,11 @@ // tag::basics-xml-001[] - - - - - + + + + // end::basics-xml-001[] diff --git a/docs/developer-guide/basics.asciidoc b/docs/developer-guide/basics.asciidoc index 8c337d0a5fd..848326ad2cd 100644 --- a/docs/developer-guide/basics.asciidoc +++ b/docs/developer-guide/basics.asciidoc @@ -743,6 +743,7 @@ This code is shorthand for resource file loading and for the installation of the * Localization Bundles * Data files +[[working-with-css-themes]] ==== Working with CSS themes Modern Codename One projects ship with a `src/main/css/theme.css` file (or an equivalent stylesheet). Editing this file allows you to define UIIDs using standard CSS syntax together with Codename One–specific extensions such as `cn1-derive` for inheritance and the `#Constants` block for theme constants. Each time you build or run the project, the build tool compiles the CSS into the `theme.res` resource file automatically. Saving the CSS while the simulator is running will also trigger a refresh so you can iterate on styling. @@ -751,14 +752,14 @@ Because the CSS compiler produces the final resource file, you should treat the === GUI builder -The GUI builder allows you to arrange components visually within a UI using drag, property sheets etc. With the GUI builder you can create elaborate, rich UI's without writing the layout code. Forms designed in the builder use auto layout mode by default, which lets you position and resize components on a canvas while using `LayeredLayout` behind the scenes. +The GUI builder arranges components visually: you drag them onto a canvas, position them, and edit their properties in an inspector, without writing the layout code by hand. It's a desktop application that the Codename One Maven plugin launches, so it works the same way whichever IDE you use for the surrounding Java code. Forms designed in the builder use auto layout mode by default, which positions and resizes components on the canvas using `LayeredLayout` behind the scenes. ==== Hello world -The GUI builder is invoked through the Codename One Maven plugin and works the same way regardless of which IDE you use to edit the surrounding Java code. Two goals to know about: +Two goals do the work: -- `cn1:create-gui-form` generates a new GUI form (the `.gui` XML file plus the matching Java source). -- `cn1:guibuilder` opens the GUI builder so you can edit an existing form. +- `cn1:create-gui-form` generates a new GUI form: the `.gui` XML file plus the matching Java source. +- `cn1:guibuilder` opens the GUI builder on the project. Both goals accept a `className` parameter that points at the fully qualified class name of the form. From the root of a multi-module Codename One project, create a new form like this: @@ -781,205 +782,94 @@ To open the form in the GUI builder: include::../demos/common/src/main/snippets/developer-guide/basics.sh[tag=basics-bash-002,indent=0] ---- +Projects generated from the archetype also carry a ready-made shortcut for this: a *CN1 GUI Builder* run configuration in IntelliJ IDEA, an *Open in GUI Builder* action in NetBeans, a *GUI Builder* launch configuration in Eclipse, and a *Tools > GUI Builder* entry in the Visual Studio Code Maven favourites. They all run the same goal. + The full goal reference, including every parameter, lives in <> and <> in the Maven goals appendix. -===== Basic usage +TIP: The GUI builder runs on JDK 17 or newer. If Maven itself runs on an older JDK the goal stops with a message telling you so, rather than failing inside the editor. -Notice that the UI of the GUIBuilder might change in various ways but the basic concepts should remain the -same. +==== The workspace -The GUI builder is controlled through its main toolbar, notice that your changes will be applied when you click -the #Save# button on the right: +.The GUI builder editing a form created with `cn1:create-gui-form` +image::img/gui-builder-workspace.png[The GUI builder editing a newly created form,scaledwidth=90%] -.The features of the left toolbar -image::img/new-gui-builder-left-toolbar.png[The features of the left toolbar,scaledwidth=50%] +The window has three columns: -.The features of the right toolbar -image::img/new-gui-builder-right-toolbar.png[The features of the right toolbar,scaledwidth=50%] +- *Project forms and palette* -- on the left. The upper half lists every `.gui` file in the project under the *Forms* tab, and the component tree of the form you are editing under the *Hierarchy* tab. Picking a component from the tree is often easier than hitting it on the canvas, particularly when it's behind something else. The lower half is the component palette, with a search field for finding a component by name. -The main UI includes four important parts: +- *Design canvas* -- in the middle. This is a live Codename One rendering of your form, styled by the project's own `theme.css`, so what you see is what the application draws. The buttons above the canvas switch it between phone portrait, phone landscape, tablet, and a full-width desktop canvas. -- #Main Form# -- This is where you place the components of the UI you're building +- *Inspector* -- on the right. It shows the selected component under three tabs: *Properties*, *Layout*, and *Events*. -- #Component Tree# -- This is a logical representation of the component hierarchy within the #Main Form#. It's often easier to pick a component from the tree rather than the form itself +The toolbar carries *Save*, *Undo*, *Redo* and *Refresh* on the left, and *CSS* and *Code* on the right. Changes are written to the `.gui` file when you press *Save*. -- #Property Inspector# -- When you select an element in the tree or form you can see its details here. You can then edit the various details of the component in this area +The two dividers between the columns can be dragged, so you can give the canvas more room while positioning components and give the inspector more room while filling in properties. -- #Palette# -- Components can be dragged from the palette to the #Main Form# and placed in the UI +==== Designing a form +Drag a component from the palette onto the canvas to add it. As you drag, the builder highlights where the component would land: the container that would receive it, and, in auto layout mode, guides showing the edges it would line up with. Dropping outside a valid target leaves the form unchanged. -.The four parts of the GUI builder -image::img/new-gui-builder-component-tree-palette-inspector.png[The four parts of the GUI builder,scaledwidth=50%] +Select a component by clicking it on the canvas or by picking it in the *Hierarchy* tab. A selected component shows resize handles; drag its body to move it, or a handle to resize it. Hold Shift while clicking to select several components at once, which is what the alignment actions in the top-left corner of the canvas operate on: they align edges, centres or baselines, match widths or heights, or disconnect a component from the relationships it has picked up. -You will start by selecting the #Component Palette# and dragging a button into the UI: +Double-click a component that displays text -- or long-press it -- to edit that text in place, without going to the inspector. -.You can drag any component you want from the palette to the main UI -image::img/new-gui-builder-drag-button.png[You can drag any component you want from the palette to the main UI,scaledwidth=50%] +The *Properties* tab holds what the component is: its name, its text, the UIID that connects it to a CSS selector, and the settings that belong to its type, such as the input constraint of a text field or the range of a slider. The *Layout* tab holds where it sits: its position in its parent, the layout manager of a container, and, for auto layout, its alignment reference and its horizontal and vertical size policies. The *Events* tab binds an event, such as a button press, to a method in the companion Java source. -By default the auto layout mode of the GUI builder uses layered layout to position components. Sides can be bound to a component or to the `Form`. You then use distance units to determine the binding behavior. The GUI builder tries to be "smart" and guesses your intention as you drag the components along. +[[auto-layout-mode]] +==== Auto layout mode -When you select the component you placed you can edit the properties of that component: +New forms use auto layout mode. In this mode you place components where you want them rather than accepting the positions a layout manager dictates. -.Properties allow you to customize everything about a component -image::img/new-gui-builder-property-sheet.png[Properties allow you to customize everything about a component,scaledwidth=50%] +.All forms designed in auto layout mode use `LayeredLayout` +NOTE: Auto layout mode is built on the inset support in `LayeredLayout`. Component positioning uses <>, not absolute coordinates. -There are five property sheets per component: +An inset can be fixed (stated in millimetres, pixels or a percentage) or flexible, and it can be measured from the parent or from a sibling component. That's what makes a form designed on one canvas size still work on another: a component pinned to the bottom of the form stays there on a taller screen, and a component pinned below its neighbour follows that neighbour when it moves. -- #Basic Settings# -- These include the basic configuration for a component for example: name, icon, text etc. +The builder chooses insets for you as you drag, and it prefers a relationship with a nearby component over a distance from the edge of the form. The *Layout* tab shows the choice it made and lets you change it: -- #Advanced Settings# -- These include features that aren't as common such as icon gap, mask etc. +- *Alignment reference* -- the component this one is positioned against, or the nearest component if you haven't chosen one. +- *Horizontal size policy* and *Vertical size policy* -- whether the component keeps its preferred size, keeps a fixed size, fills its parent, or matches the size of its reference component. -- #Events# -- By clicking a button in this tab a method will be added to the source file with a callback matching your component name. This binds an event to a button, text field etc. +Resize the canvas with the device buttons above it after every few changes. A form that looks right on one canvas can fall apart on another, and switching between phone portrait and desktop is the quickest way to find out before a device does. -- #Layout# -- You can determine the layout of the parent `Container` here. For auto layout this should stay as layered layout, but you can nest other layout types in here +==== Nested containers and other layouts -- #Style Customization# -- This isn't a theme, if you want to customize the style of a specific component you can do that through this UI. The theme works on a more global/reusable level and this is designed for a specific component +A form doesn't have to be one flat surface. Drag a *Container* from the palette to group components together, then use the *Container layout* picker in the *Layout* tab to give it whichever layout manager suits that part of the form: box, border, flow, grid, table, or layered again. Components dragged into that container are then arranged by it, and the drop guides change to match -- a box layout shows where in the sequence the component would go, a border layout shows which region would receive it, and a table layout shows the cell. -For things like setting the text on the component you can use a convenient "long click" on the component to -edit the text in place as such: +Containers can be nested as deeply as you need, and a component can be dragged out of one container and into another; the builder rewrites its constraints for the layout it lands in. -.Use the long click to edit the text "in place" -image::img/gui-builder-in-place-edit.png[Use the long click to edit the text "in place",scaledwidth=50%] +==== The theme and the companion source -===== Events +The *CSS* button opens the project stylesheet inside the builder. -IMPORTANT: As of now, the events tab was disabled. https://github.com/codenameone/CodenameOne/issues/3593#issuecomment-1133647486[See issue 3593 for more info.] +.Editing the project stylesheet; the canvas re-renders as you type +image::img/gui-builder-css-editor.png[Editing theme.css inside the GUI builder,scaledwidth=90%] -When a component supports broadcasting events you can bind such events by selecting it, then selecting -the events tab and clicking the button matching the event type +The stylesheet is compiled and applied to the canvas as you edit it, so a color or a font change is visible immediately on the form you are designing. This is the same `src/main/css/theme.css` the application builds with, described in <>. -.The events tab is listed below supported event types can be bound above -image::img/gui-builder-events.png[The events tab is listed below supported event types can be bound above,scaledwidth=50%] +The *Code* button opens the companion Java source. -Once an event is bound the IDE will open to the event code for example: +.The companion Java source, generated from the form +image::img/gui-builder-code-editor.png[The generated companion source,scaledwidth=90%] -[source,java] ----- -include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/BasicsJava039Snippet.java[tag=basics-java-039,indent=0] ----- +Everything between the `// ` and `// ` markers is written by the builder from the `.gui` file and is replaced whenever the form is saved. The region between the `// ` markers is yours: event handlers and anything else you add there is preserved across saves. The editor makes the distinction visible, and the status line reminds you which region takes your changes. -TIP: Some IDEs generate the project source code after you explicitly build the project so if your code needs to access variables etc. try building first +Forms scaffolded by older versions of `cn1:create-gui-form` used a different marker style. Opening such a form in the builder converts the file to the format above and keeps the methods you had written. -Within the code you can access all the GUI components you defined with the `gui_` prefix for example: `Button_1` from the -UI is represented as: +==== What's stored on disk -[source,java] ----- -include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/BasicsJava040Snippet.java[tag=basics-java-040,indent=0] ----- - -===== Underlying XML - -Saving the project generates an XML file representing the UI into the res directory in the project, the GUI file is created in a matching hierarchy in the project under the `res/guibuilder` directory: +A form is two files that belong together: -.The java and GUI files in the hierarchy -image::img/gui-builder-java-and-gui-files.png[The java and GUI files in the hierarchy,scaledwidth=50%] +- `common/src/main/guibuilder//.gui` -- the XML description of the component tree. This is the file the builder reads and writes, and the one to keep under version control. +- `common/src/main/java//.java` -- the companion Java source. -IMPORTANT: If you refactor (rename or move) the Java file it's connection with the GUI file will break. You need -to move/rename both +IMPORTANT: The two files are connected by name. If you rename or move one, rename or move the other to match, or the builder can no longer pair them. -You can edit the GUI file directly but changes won't map into the GUI builder unless you reopen it. These files should be under version control as they're the main files that change. The GUI builder file for the button and label code looks like this: +The `.gui` file is plain XML and readable enough to review in a diff: [source,xml] ---- include::../demos/common/src/main/snippets/developer-guide/basics.xml[tag=basics-xml-001,indent=0] ---- -This format is simple. The file triggers the following Java source file: - -[source,java] ----- -include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/BasicsJava041Snippet.java[tag=basics-java-041,indent=0] ----- - -WARNING: Don't touch the code within the don't EDIT comments... - -The GUI builder uses the "magic comments" approach where code is generated into those areas to match the XML defined in the GUI builder. Various IDEs generate that code at different times. Some will generate it when you run the app while others will generate it as you save the GUI in the builder. - -You can write code within the class both by using the event mechanism, by writing code in the constructors or through overriding functionality in the base class. - -[[auto-layout-mode]] -==== Auto layout mode - -New forms created with the GUI Builder use auto layout mode. In this mode you can move and resize your components as you see fit. You aren't constrained to the positions dictated by the form's layout manager. - -.All forms designed in auto layout mode use `LayeredLayout` -NOTE: Auto layout mode is built upon the inset support in LayeredLayout. Component positioning uses <>, not absolute positioning - -As an example, drag a button onto a blank form and see what happens. The button will be "selected" initially after adding it, so you'll see its outline and resize handles for adjusting its size and position. You'll also see four floating labels (above, below, to the left, and to the right) that show the corresponding side's inset values and allow you to adjust them. - -When a component is selected you can drag it to reposition it or use the resize handles to change its size. The floating inset labels update as you drag so you can fine tune spacing without leaving the canvas. - -Press the mouse inside the bounds of the button and drag it around to reposition it. You will notice that the inset labels change to reflect the new inset values. If you drag the button close to the edge of the form, the corresponding inset value will change to millimetres. If you move farther away from the edge, it will change to percentage values. - -===== The inset control - -Take a closer look at the inset control (the inset controls are the black buttons that appear to the top, bottom, left, and right of the selected component). - -.The inset control allows you to change the inset size and units, toggle it between fixed and flexible, and link it to another component. -image::img/guibuilder-2-inset-control.png[Inset control,scaledwidth=5%] - -Each control has three sections: - -// vale-skip: Microsoft.Percentages: "percent" is one of the named unit options in the GUI builder dropdown, used here as a unit name on equal footing with "millimetres" and "pixels", not as a quantity. -1. **The inset value drop-down menu**. This shows the current value of the inset (for example: 0mm, 25%, auto, etc.). If you click on this, it will open a menu that will allow you to change the units. If the inset is in millimetres, it will have options for pixels, and percent. If the inset is in percent, it will have options for pixels and millimetres. Etc. It also includes a text field to enter an inset value explicitly. -+ -image::img/guibuilder-2-insets-dropdown-menu.png[Inset drop-down menu,scaledwidth=6%] -2. **The "Link" Button** image:img/guibuilder-2-link-button-unselected.png[Link button,scaledwidth=6%] - If the inset is linked to a reference component, then this button will be highlighted "blue," and hovering over it will highlight the reference component in the UI so that you can see which component it's linked to. Clicking on this button will open a dialog that will allow you to "break" this link. You can drag this button over any component in the form to "link." -// vale-skip: Microsoft.Percentages: "percent" is a named unit option in the GUI builder dropdown, paired with "millimetres", not a quantity. -3. **The "Lock" Button"** image:img/guibuilder-2-inset-fixed-button.png[Inset fixed button,scaledwidth=6%] - This button allows you to toggle the inset between "flexible" (that's: auto) and "fixed" (that's: millimetres or percent). - -===== Auto snap - -Notice the "autosnap" checkbox that appears in the top-right corner of the GUI builder window. - -.Autosnap checkbox -image::img/guibuilder-2-smart-insets-auto-snap-checkboxes.png[Autosnap checkbox,scaledwidth=10%] - -Autosnap does what it sounds like: It automatically snaps two components together when you drag them near each other. This is handy for linking components together without having to explicitly link them (using the "link" button). This feature is turned on by default. If autosnap is turned off, you can still start a "snap" by holding down the ALT/Option key on your keyboard during the drag. - -===== Smart insets - -Beside the "autosnap" checkbox is another checkbox named `Smart Insets`. - -.Smart insets checkbox -image::img/guibuilder-2-smart-insets-auto-snap-checkboxes.png[Smart insets checkbox,scaledwidth=10%] - -Smart Inset uses some heuristics during a drag to try to determine how the insets should be linked. The heuristics are basic (it tries to link to the nearest neighbor component usually), but you will be working on improving this for future releases. This feature is turned off by default while it's still being refined. The goal is to improve this to the point where it *always* makes the correct link choices - at which time you will be able to use the GUI builder without having any knowledge of insets or reference components. - -===== The widget control pad - -.Widget control pad -image::img/guibuilder-2-widget-control-pad.png[Widget control pad,scaledwidth=10%] - -When a component is selected, you should see a black floating panel appear in the lower right of the screen. - -This is the widget control pad, and it provides an alternative view of the component's links. It also provides a useful list of incoming links (that's: components that "depend on" this component's positioning). Sometimes, you may want to disconnect incoming links so that you can drag the component without affecting the position of dependent components. - -This control pad also includes game-pad-like controls (up, down, left, right), that allow you to "tab" the component to the next guide in that direction. Tab positions exist at component edges in the form. This is useful for aligning components with each other. - -===== Keyboard Short-Cuts - -1. **Arrow Keys** - Use the up/down/left/right arrow keys to nudge the selected component a little bit at a time. This is a convenient way to move the component to a position that's more precise than can be achieved with a mouse drag. -2. **Arrow Keys + SHIFT** - Hold down the SHIFT key while pressing an arrow key and it will "tab" the component to the next tab marker. The form has implicit tab markers at the edge of each component on the form. -3. **ALT/Option Key + Click or Drag** - Holding down the option/alt key while clicking or dragging a component will resulting in "snapping" behaviour even if autosnap is turned off. - -===== Sub-Containers - -Sometimes, you may need to add sub-containers to your form to aid in grouping your components together. You can drag a container onto your form using the "Container" palette item (under "Core Components"). The default layout the subcontainer will be LayeredLayout so that you're able to position components within the sub-container with precision, like on the root container. - -You can also change the layout of subcontainers to another classical layout manager (for example: grid layout, box layout, etc.) and drag components directly into it. This is useful if parts of your form lend themselves to a different layout. As an example, drag a container onto the canvas that uses BoxLayout Y. (You can find this under the "Containers" section of the component palette). - -Drag the button (that was earlier on the form) over that container, and you should see a drop-zone become highlighted. - -.Dropping container on child container with box layout y -image::img/guibuilder-2-subcontainer-add-child.png[Dropping container on child container with box layout y,scaledwidth=25%] - -You can drop the button directly there. You can As you drag more components into the sub-container, you'll see them automatically laid out vertically. - -.Box Layout Y dropping 2nd child -image::img/guibuilder-2-subcontainer-add-child-2.png[Box Layout Y dropping 2nd child,scaledwidth=25%] - -===== The canvas resize tool - -When designing a UI with the GUI builder it's ** important that you periodically test the form's "resizing" behavior so that you know how it will behave on different devices. Components may appear to be positioned when the canvas is one size, but become out of whack when the container is resized. After every manipulation you perform, it's good practice to drag the canvas resize tool (the button in the lower right corner of the GUI builder) smaller and bigger so you can see how the positions are changed. If things grow out of whack, you may need to toggle an inset between fixed and auto, or add a link between some components so that the resizing behavior matches your expectations. +You can edit it by hand, but reopen the form afterwards: the builder doesn't watch the file while it's running. diff --git a/docs/developer-guide/img/gui-builder-code-editor.png b/docs/developer-guide/img/gui-builder-code-editor.png new file mode 100644 index 0000000000000000000000000000000000000000..7cf0d63e18275ec577c8a9708d11ee6a411ebf52 GIT binary patch literal 198006 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 z®6#M8o99gE!VEKy?4JG3QEq6x!Z73WqXB|0=52(zauvM(H6T3 z+o)Li#1Fstu{D!GDtnXuX3Ev~)x~&vJh5Il662sx*BY7EmowK^*6FpBX6wmA!`}N~ z{W8LmA@1relA|!2yJyTFXiV`G9rq8iLS`Ht z9n;L$HyBvv5D3*V$ahNYf1R?u^~%2F$l@!JI^r<{KH~tAV$C@C}4SM03E%43-46+?BO}V-=ilb6eYupO8eyGY zf_{~9^(9pvL;k}%?bkdC@wZ84wEDq|Cg&pAPyKS_UzI9mXt#Q2sz^@*2?CNC3YK|k zldVl=zB>!|nDs{$-g@r0${x^z;|hE;AKP`vy)MB9Gg=G9D1ZAM1hA7AlMidri`GM$ zE~9IBJ|t^nRbks3Na{82caRI7e%ZZ#+2h~)?PS;X6O+=Of`y+TeAHT8E}J`4nD%fx zo}jtqVMfUZ)O;T9WRqG-iRKmpSVW@2m^Hzo6;st1Y^eFyjcjAwNL;!^*QD-)(9~}P zkI!y1)`;WYWVfFt+dn7GU>M!()#Q5dtYteSG}h*w07U8SC?B!ZY^-S>dD1$w7a|BO ze1(oe;iAV6U9E$&BdJxVI&3J_77=q6N2g7l)3!0h56B2J?M^82<`5Lt93R;aq#NyE zjtf$xAJM(LS0TO9dEXOXu_0Pc^O0NpxvP!aZb6?0H)g0@S1S*O zw8Z+nxTLZf{){E`QSJ`uDf&B`>6ex)7C>c_$Hh7+<&J~ zJm-qKX9+)ok+s_(tU#L>ag_*j^VQsUI1+vOXdtKSv$Ka?Gew?&Nb9R@RiT&CPp-6A zobSd=d!(-Oh_H1IeD=4!B{Fmrt?zwKzCpO8pP2daHaNR!lU_@#ZTZ(5x!|@vTa_ee zrZ|2*e0}~QzA=%Nh>C2ZF}AD%WeNEE=q_JKP<)+5f()8xaUI)H^Jb*K^?|oCH@}M) z8P}CfTrmVZ^Xm0#M zs-jRD7+j)g2@d$UgYa7gYT<&m!qdjoF@_I=?Ya=B-2*hf*xQ9 z`n7sr@2n51K|G7<^KH83Jx>Csc!f?bdb}~4O9J)q9lk92^Jmz4e?P5S^Gd=)Bx&N7 zB__Lu=eyXOArF^tI*$^$dlTA+kA=?8T1Hc9-FLkYrg%wn@(Ovq3}R;8oxXpyz2fA# z+_?$&wBDCt&FFBDxyywD-#S~5lTF^@rpvuuCe9yU3`j^C&(52673w(!&v!agye;ud z(awC`Pw*|Y#~D`y-<#cE5YC7lp6!gCeH}au7dFQ2o|~qDx-ghEpYEsSpP!c(DU$_6 zno;D6P$(M?5Z*O>z@_#T7UtpY>*+C?NK+h8|0`wE!df`7-9G>Wq`Pq7H7y}h_iUGR zJB)ihKFNNO#~n-N!s@5C4X<}PU3*Vwc|5+RjwJI`3vc2bL!nTEuh%tFLBaL#Ji)s< zTZd&$C-*&+ePUw**SH9+rZ$OEZ^^@*Ilnh#Dv24CWydzwg7^v3Bq>coc_*0Nt&2%X z{cGyazt-Z2Ds^VTgaHG33g1R|EUHCpLtXX!7ElfuQg1_Bg4zSUEO~fCUW|34a26<*Hywt-zBf1M8owTSSJ$9$`kbbf`gWy5n*BAWJ>YtfEWI5l7^$ZD~<{loH4>;r)Uk<`_8XnAJ|9%D{G&h}&M1VN zU%%#LWtEhH@*ej;nNE_=GAJe3Pft8in)+rLR-MH6Ynr?c2Qth8uVqto1N6fF$oIhn zCF>(a_^b;cd;lF;pwlHIBinj_W3hb)jApEe3-B?L7D55pgb>Of9~BsdG`$5TNd|~M z)c?=(fm=6W|D&|Ow~z|53Lqc<^Y*|yeS-J@@1@2Yz<{zz`cVepoAIgr_VI=Zh!el= z5?qrpbrA<#Fsw1}K6KY&0!5EYyKA0Ci#VK7X#KQ2bq*8!PP>fQmGUcOA)lvwE4 zpaVPrw1B$5tE;OFGNh0Tl+qvZ0hD5_PB)kjYRUTUB{E3hdu*kY=; z{oEgmV}{IbrDla?pPdvb)A3{lb(Q4EsHAF;tqwT$kJa8L>9wZne)S5edbQy&0!$1U7-Rqsrn?|N9{~|j>>Q|9fQCQuz6*fmfW>o7O%2$fAd}z# zI(TR2(dLg69Cdhn@Lk7Nm#)p2-Sk#~Gd z{<~B6ouh-y^kMl#;vfr7!aK=!92?#j{GQGcnL^d9F2mG@- zx09&!vLr=iuIs{w~Fy2~n5stCtB106358j!WqdQ4m za=+q6W6mu?7g8Q!kKpqcZBE<3WB2hN8XZ+NUYU)<47LG_21b~P0^?$0Ls_wgC@Fo* z`(H*rPvdZ4Hq$i}L}($a;*!g4zz}FX&?XRLYb;Qej25LmGL=xbv`<8cXDI9bh=;cT z<=`dDjb`gRv$VfsxUlbf*li$3rt=O{4*i)NZX`Cl8a@L@AQ5sxI;1V6T-6cnH7GS) z$}1kNSfHrU)J`{Ob0%E)$3@X$xZB61TbjZI*5)rfvKzh?>LqtjUQ-pBTUm)`a~Q|0 z?ZV|w*)$0R=h-c8*uM@VM50hV=9^frZuk>i;)6vjNp4IV^9j!`{Dc*&Y0 zQ*;v(FySFA`i(kIo;ZUp6i3n8&ko?0Z5yfnn2j#?#|x^ zfF2J)a<}{NHa*P9 zkC^Eo0qENAd2T^pp$pmfTR2Nik$Gt#i*u&9Ja1wzU%({x1xjISBKF#0x(iaZ`iNF51a=~59s1Kv*Bc)Cdm_%!t& zxUrW$Cn|v|+5g#*y(YuRmN3hqq}*PXoBN=0DK$?j2gR#}*DkD*0ROI@S1sq}Nu_@1 zr}&cCDQZ}%X;~cSNwtc8^E#{Z&050FXGyKV$HVSzpLsZ`wH&oY+<6m3by~aC7yR;M zJWOE9re;D8rn7O20D|H~$d6+67$1`{?k+a=;*%5s*C@$rR=BHMpdNS6UlkYP{2l32 zhc?DIz(Mau{i^^a1n%OR5o2@*&cxcGp?z^G)Dv529TwoF0I9Sh5aLA+m+`sm^P3CH z(MSS=2QfQi<6HT&pFacJzNeThky4<`CxyZ0h=Gb4GpuqKG+fgXn?M?QgE;&=ycL z4|5fkdAf{7rxg&wjoO==NQ7uM>V%e5gTGQghAx;F$Mkt zw|1H@^;cf@z@Y=H>{pVKlA315nT2VBb223ew%Q?qb)SlC@dN9WQPi|JDW7L6VzX4* zW*#MA+OVah(rT^*-ch;KscOG1VwaodQ-ukw)_Nn1dP%UU*d#oFw#NH>rJ<4;#n7Bh zq%a^+`{5%~2NLGVt|UZ~!hKdo!uL6AHkl{2qFYuXPDtY;qZ%tj1nfSoeZg-u3{nfF zrI0=NyB^i>uLK}F_&qG(?e9|?8X4ADc>_R*ua0EpI4IWm?~9g#FJ1yIXn&DL@xR7O z&V|7dPU!x@;rBaopSt2>RNJZ7nSSp>%V;IwE35nh0>;m_G3Djw17?E?<75^E9v)l}9iUW!9zV!SXbqY2|!`;3-Bq&|2`0Qhe-PxM^ zS!Tsd_N}1R09e#9$rz;z9w<)EDnzIY80+l{gEJw@+M$(kdd0DC=c5EgcDZ1!qdatw zu%2o@z87(u1k1)56Jw{>EXBXGPB#TZEpo@9KvXL5a%|L;{1%ieM=~P+YlR_Q$51r? zmK<7l5NbI$e$C4ohpri8$J972_q$&=r3%H;j5NinLUcG`N!lhVL!7vhtr+jC>*?vq z&TTtUgF&04T6ka+5H)Ds)Ct9HKg%t9ICKo`g{G!$UOuyrrYogomRV zXv}aBApN@sy!e6Z5N7wQf%WKz9ATvV2(2z0mSQsDb4Er+6a+*>l<$gBp%ErTz-T&C z25Uj%>Mm}L&;1XZcx^0vb&mxo2`rP8jGQ`0A7F6@`M+s2*dW0fgg(4jkUL46`fwQy z$9?3YfwiFM7*g%y?-7}sn=5W}qSChjqed+-<#s==1KGH;y;WEBrImf;!nT0OfC$d{xm8WHl62sw>l(e(LyIxN5Yu6Go1g;{$kKC{^O-GVlIR+mM;Q61*+4hVi^ z3e~BT*lzV(`VmViLG}LkSb`0mj1(Rk9E1Y#v#~)tpbXs8*U&&>-_OS(XaFEVG{MVN0XN(`^n$nxlVxTY zZQw%!u^8n0#*D};!03b%GW$KZ?-4vvk>v*A{)q>6F#!Na1RvTpcQL<~+d#4=UM|Sz zqne0FH#@yE)gSab&TDFp1_uFSMCJ0*Qmr3>!W09&l(s-eymlsfNbvOv+NKH7s*)Gg zh(eS6?edm3A7gvJJWvdWtrfkNQg%Pwk}(+Z zVvXj+VV%iBA5&%3T++qOW(XrG0prB@=wJ)=Lu{m%H+Kd9Gra`N?|BDDl$kT)MF5zB zU#f;dwDfaNUo%VXQWF*Riz#%0i)z#54)~79>`wg1;N@S?_08D8kHsN;+R#16#Gs> zWbTCGc(#l%u${p$POW4Bz&T7Dvlrq zAimnV&4{B6?_R?(K1P$_S(-xE)fwi7#!$`eX`_!i*dGIpsxK?^!Sof2f{?8 zy6owQGJ#p~ck$1}ScHeTdm3pf3%rf`Xj^kEd_oPvA@Bm zLGuF(LV(CN-2JPrXny&U#z5%~z+>CmW9c=;L35Rv`5kouPU4R^fDX9@Ct_f*2t09s zl+R|q5hz6-&&|T706jl&e*^ZaVEq7RCuTT=DOE;N(hMBJY>t5|Ku{`et8g3Qa-f$3 zOeontHUT2};2@?d0L-KEbA~B>;4_UDH*&o%y_7Q!J;Zk3-~RVNP0BqAgsT zhwnX?0Z+Ow8vTXs5xA$yOfA-`j-Y0MohQv-kO0PE3hMDh(fc^_P`m{?diuyuuWMKH ziy4R|c5&@|Gjod=s^{f~e7U?Waug_GQ&R95Uc?zOQ%_j7&n)?sbC0@^G{g2Giwjp5gDfF%e@XI9$SybD z_NUh`sKTe^)zIX*W;Kvb;g=wAnxF!as z=OZ1RooVJ)0#zEOq+m4j4?F-qRH8WU1`S|j{|TR#`xrR^G~z<;CW#*!5}EsA`0N=U z2M3&(%+B}dLniRX37*Y2xT{MBPCte}e2qgwg4)yod?ceOr{)fUSjgBi{E+@%qW(`j zq+>vpGV)cg9RV2`O%K;osi6C=aln&wvA?{cqHw+Ch%*)#$2T`O1HTvG#RS;*qs1mF^ zKJb9`UKTTD6?~K&yzKRxuG#w{&(3cXBTKD$Gq+!LTSqS4KQiFg8&+69s(Do~W0M(^ zvb5&?+S`nNZiW+CKSeu0;*TQa7vdElnwDJrd;i8Qr;Z+F;F~5cMecJ_5F(C}2J7!! z3pva)(0oAo4C`y`ZmV=@*{SbN66yY^GFqP?d-s9Y{DTh&hVaLIgVK-B`_j+U(@bJA zv7^{ebvyMvWAxqAEOvz_#Pb?EO|M@1`J2%=lQrJYLctlmkb?7h7y) zv$?)jyPF8n~;-8bjym9#SkFxd`h~|YC{OZ-yoET6ZJ*ht298mkcOg(!luwmC` z)V@5#eijP4t||lD-2&*jpJyJ*ELpo3#q#RXlz4w{+>tH59%ou_P7Ip#V~GOJDJhB` zA6Z@X2UdB#Ok(a3CZ$dv#gj+%2ZaXUg7*pD>a}&87zfQ9E>;aG89Q?mTjrCNE;#fj zxRbp(chT`_w*0QF#l8?dczN{JWdXMdB_DLYw;Sx9zo>5QvSfU=l~j&jB$RM9&Ptfs z8Y>)GbC>JQP0#9gghYav|0v6WOS-hyfT56hnxfPL(B%zC4-I{6Zk{WA?`f5G@zVY& zu-H`28=ab}DS$3FWTzLFzyeH;q%U-IOqBwrPCKa-<9z6QIXmsU0_0pYnZnRR|94hi zy{1|3Ls%IZlis`u4hk};5~FYW&r;*Upnvln!A^hM+N;!U@~~!X{hmwry~4U(r`=F@ zsjI5&DG5DoZ|k=epdC4vtQ7do%`_U_s5n?feNJx1QKqjkVl!}(Cim#XGCb;&+ZXgX zn+*cL*OFE}B_ZMV4Ej^g?V0MnZ-sF9_-n7?Hhl#@=1|bTEx4CjUNS&q?tZz*Ul$f$ z-C2I|i4NOv1KUEO{%DfA6l;2dwEDB>rrmO+0cVN~9p0#()aUZq8j6iWqdz`}f5Qy> zER3F6C#h&eYO5Mp?)GkrjNcsKQovGGre|a%TK70(b$k2qk2k!xe|dgPAulIqT;;9@ z;uDxiiHwFu-42jtfF%OWDlhR-hudd zi=~W{dsI;+0A|a;s6%i|QntF~tz!`b=C{-OmUp)?J5kcGp}a}Elf&wVdh(n2PfVep`uYT?nL3JO;Yga=FM+C zjk<$Oip)fc8UF|w;+n95&N}v$nVHCMRWwB4(riCi5i?uA%RCTJHd$ZzxM|?~51=MT z1!!4UCyb9jQJyU|L8++++Cffh;06hw`2IZ;&Hig+V`CurMBsg-ub(2x=~uvA^8$;& z+)|LY8MgiY>7D1!crF^n7mwbDtPg#)S>ORJU1S!=pvTL^c z)(b{GCTX8{>k)%JcYM&W*kH%oeLW44ovC^KUJUlSgGx5x!}&=;_{Xvol7g>Vjd`mw zoi#wUk_cY=Ixvkce_pvgTJ0@EY%H(mRdznSY*Z{r`J{sRDGXv&e%bSJemfiFJM8d~ zkEQ69??~=TgYB}=(E9DL3WBMEN2@HruqriVMUa5G1 z5%vPYU8hkQ8gF~=*?u(q|04*iGv@i}X=FDJ5Z4a60#-M$h#B!0zJ~-*JP%;>!{rkP zhyrqj?{#!_9n5R$>U!3Ich90DlyJpSz^%3o_zo-@g5g(bVuC3HFzAX;pWb`c0Y_rr zM5AMY#Y{_^v$bVqKawg|Efw@A>h0V2IXRMAckNgaR6(&p3)%^k?K?}EBdd9_dm_tq zGHHwm&_}g1Ij422x7=&Bm^BL0vx@MRHzSCba2HK;iq4G3!iAeL?8{#s{lYyZzcHa6 z4#Uru`Uxms>VRa|_6AI%EK^c^O!ovD6NjyE1o%L^B4NEcIQ3IL#L9`+q+C{tboX{_=KCh z0bkz*70&5AJ+!^k+aMuQ*EJ3XJw7)ZJ6?~r*iD*go93%NXd$z_|FA=;@+c$d&z$Y8 zPwQk`ukOa~mqpp&ej8C~X|>ww*U!M@lQ@-XZhmjsY~v9HbO^BQNf`2ykwMP;FJPDO z@OmGNW4dv??FP8T$H`cdyJTeO;@QEkV8TGZK~zrbV{_SUw5*Vq$2@YKj208sv>j>?k!Z+A0){3Q=&=L%TArmPj=( z54jc@JMp4bcht&xb}) zZ}#2YC5XdNMI^8mFel!!&Ye!*0ooyjIVKeJZpa7g0q(oRuGSCT44lE94&c{&c=cs@ zLm*f%)0PjdyM7!0QxZ!`{r7A!mxeIQ(?#{{FX*%Vg%2jYEb4a_w&U04b(e4_rkxCU zO3K}3EroQ3SwlHIqI3IX7Pm}7JRS*tIT5ht}5d4JvP5ntdY zEB~yEF@Z&R(^*%5o=htoiP|}WXc;=Y_rQA9Nx+Oq7DmQ8=%QbYDVl&AQ9JN!NSz!Rfp7i(ZH51ksTYI z5(jyCY3UvTF|mIdH;JHZPk%oP6Vujco{I9>SD<@l=Y$mLHwi)@NFq*7+<^6}c)(5A z3`_d)u@S)_7#+pHz(6MGfn;Z6!*Y^A zPTf3D!AxoDq*}rs*s)^8L-rS3vfrJil5o-X3Q1i(1jmTe2y%K(|#!%|vJ6nfaPF!Ti353`v<9nAFpc3UA?pgY4`7IIrYJpOJ-Ntl*xO3 z5-N?S6K;tU!0BcAW;>}Iqo{At!Da$uKC^+?&rS(LpWP6M6_2S!NFi2tic5a zKu550fdyW8P!K&66KFTE+0Z@&j`z0_5JX|bo^yD&%+IH&(!-H}`GzYTz>f)|9e8ZN z>Q;RPYy_0Yfd5S_B4oj@y9UUoLG`~_mYUk!(gJ%HGmLW|jCtqdbA9?$_(3@F%1(k@IQ}*-4jF3+0DO%EEKd`QTkpa{ zUQ~|OCfuV9lFu@zy6p+UN0g>PApe+?PxB3&os%eA-UF&2eG+Iy(-r8{P2tj+ zfQ;lP>2lxE;xsSjd-QC*Jz=A`@-qxj9R8VA{Ug;l+N~}a)|cqXxU*vKCop0a9$mq0 zSK)u~=x9xhQVupYANKkfy&aJUw5X!_1b;)LMQ{CuMxzU+c%OV*JdIX?zoAZa&DT1G zNOMr4Py;ldZOyo2L%2X-$PWSr19XJ^L!Zk8Q@8`3>H zOH_1tk|&~ac{_C}=m3S^4 z!^%hE874Pf>)(|)Aavrz@yvNq*d#l+L)qNJzI-JAE*=< zl8UggvtwalQAA;-4A8n8gK2cVQ&Wk((-Fh>uR*ea1SaW9tpkyQv96pmPX&p84gs_| z_Up#Ri-3SJ&}^w-0~!Xl+(x_QmoeD{r4w?_$2vd?kdPqY^36x5(2)#EclM?SktCSK z^pq1)?Q_2+DINEIU;APa@kgCxjplW7#`L2uNL2iSu>r}#({`9Ja|J{so6*(Cesu&$ zS8YOXb4{O4{4Ek=1VjgIMInrT!z}>O5n#huFMw!ttw((k;SG(Pw1 zp2|Me;Ho0;_=Q~>SX#;&20elk7ZYpsJU@zzkBzY7pJI)6RYQJ#VbH8#NYEjsrl&`C zgAm^%NC(4O0Bo$T9=~-d0uvY)_XHRx=@7KtC^s&q%FIs7yQob}Oj^WcHbXhVTonp( zQc^ImPgrlR#BzcqcN%!M0&EWgL_3$1ndHe!#WfzFscV?<5Yp9{uLFR8o& zG1ft1S0*^f-Me=)55AKqg9mrCvy1e^!jK{S9gY95@NS>dYrqV(>3J(^ako=TJ00#H z!Cf=Cu70(q2Mvu)d1QEQs?dkZ+rWwTf?r+A`!Qy;K%0XybKNDg8lvdUp#8Y z1ym4Vz<}U*_N{`h0n5v+bp{+k z*X>vD@nA_(-_b8-x|o@@XfRe_Y*0v(tPrb+V;|t9iJ{%jgq`&B(%ib5)ljaVP;FCv zh`Ru-?Cf0R0Dd>vn3%%f0hbHZAy8K+8ki&qCVtYo0(B`E?kHLX-sv}74Ih6=1uY6& zPU@QkQ%7)Nfxa;zD(Y@t?@3BlJ8YoOzyJ#Fk(x<(MT|}{u5Ad)@ObLn3gDIJr7w1T zj9gm+U4iqycBs?~;tBytNwEe#<|P}W#mnraR@c$1R0WhD5HfzHX;?*^-e;CZrFcZO z!DLlC@o_XTz&?udA~Dt6%BubYu1xp8why$LOKJg-=#>qkB?uSyY@JHV%c-!*!MQlK z9isyb>Dbnq+>eXEkyK~%Smv=BRG35-0IvqQ?q;;y(o|!%Vfr6#fL8B{pFpeX4bZwG z&PliWo6q)l3c*kGZrO|>QtHu~Bq|0*v=gv>d4h-hO_hO(>4hz54rk-EUa_fbbL=j! zh1J`+>GqCo8v z=1~^Ey!gEqC7FHx7W#uTuOx~+wQo&0csCD>{L$I@NgWhUQ_Njw{Pk>M)Zh#$WV-o7 zzm)<10O@KBgR6Wp1c=jb#pM4FmkK>B1k5XE3u{IuCh&ek--)Ni$ovK>WkJ5BrFnxW z|7KtP!~h2*obPD2V#vG4keH}{bMf%a2O2#Ow9|kW^w={TQ9VFPYS<1kg z7ff6b9s_o6cx2@d0B^t#49)GKsQio5C(#rA@?}SBAO@hdf^@2{NGvTz^51Vl1_k1H zcmOY&K5Q>jic(2svQp=M`pm*2n!y3(>ufr59N7Lf@7Ta+{zSTCXOTEPBy?6(dPuND zd30~G;G*s0lbH7(?b8@O?9qTb?g9NPnGh66wVGiGK`_|gAi@FZwR^lb`&6mpUtQ4BHaQ$b1%>@FD@pX=whPGJ>k#^ zT*Jb{uYr;x4h-7xS2zmarN5zuf^_b`NSES#us3i|3*H}aPr_)e`>6;!aLtyK3}>&x z*8BndeuW0@9;u6yhI?AF*t}v{+dD;FU~@((3+v)NmQ@`~&{j*c+(x!$@*7TQr7*&c zvm{L}sA}<-E(KGViNIh9yb$1f#>B*AT)@Q4Tvl2NhAn`Zy_!B^N@}1`Z)2Og*N3zK z@w#x)&>*<@3KF2m3WT<)DZ&T7MPQVV@jjFqP=ugTQc|zBK4QNTr^NdI@b=#ET<`t= zxJ}tIDzZ1(tB{>{C?k8XjEFZ%R`$qVku6e6_TGDshRjHBJ0Y?ud>`s`u5+F1I@jm- z*YBV6$GM$@*Lpl3kNe|3=3r(+RkfoLea?CD;^AGe9>8_7v0Cfs{0n0a9Lj6TY1*eoC$T|PxwTG!S;(GsN&`}|6 zW6tLl)<=&tpN01!-ti)^I+7xjGp76;GQ2xk7bauC zhgyEMtU{FpJG2nK=m>ZDMQ<4L> z9<{NM)V(5 zG1SW6-6I9r>S8tK(+lFRG5lWexBkf)Gm(=Epbv2QNg6Y5^hzc{*U}nRI1wD>K7USc z_1u=EnO|Id0-EESH{}qDUS1NKkh&%#AtF+Ft1l5a1bX_A;RAO#&_Vh;pt+?rI9N!8 zv2n&DRw^~n;7hLuGB%@E05*^xuf$CDFzXm*Ju!|NeH^4OAV>7G%ggvqnwsQ>Q-ww@ z;Q^pd5Dq2z5hlZ)1Ily7dS%}k@l*Du(jsQ?vD{fL!EHG6yE*rROC+w)%i&!)`J6Fh2 zVqzBO;-1~IC(IOy4Z2Jj<&yro@8d^FR@Udm#Uxjc_)xjhL7I8Fxm0J*;-N z>k$G^?BPjP#VL1>Mu=4! z!)nQ2<=z<`x2%Uf`I8xSRcM!fcI%=i`Mrk=qq$f@lYE=kla|%5LU1oMYvh?RvA=8< zupo{vCpkKr_~pr|mD4*lWvuiD-R!{nj)2FPArAI9s=6r{J!#mvWVya9FYE2Yn<;$+ zadR3l0>pH%hM_5fIfL!hBn7>59lpAfG?$#^Ye|qaQrK+&fF4E>mG z?@g3_S;Eqn;a9){4?uuITiUL;L9Q)t+}X-z$Y0 z$hd3+l5Q{2znpr|!jPYr^Yu}Nfw~tw*lamk{(Em(uMX~Hdz&NlmqExO``)0&TIT6v z$G>y~C;ka$wzI2LGYglhaB<5PUo~4Be#Q(7xSTGouB%=>i0uK(Ba@WAapBPVUB>DGiym{dF;PsqXi` zY&S`yJs_URnUR}3ll89eZ`KCI_wNG9b)FL7IX&rj1MeU6I54gG4?ME>&(6J%)=s^W zhWEX=xP1B-J&dJh?knXziC@afQb%=$iZHYgqaRie8>$*GLVPh(9c12d2A&^-O?^8Q zcb#~WUTN1|%c&4^;N(GvXda7bPyO^mR_SJhLf*;X83l4b^e!-#&Dq+agK7o){>ztZ zv$D_&IXO5^ZJe|qH-KyjgG^|t#0_^COXleoTFn=Wo0F#a4i{9 z0(Z-mPyi^MByAi(NYHgml=-Q$Cn0VAHXTCO%X+S1^R;+U)6wOjsT!9>FoLD?T1RK5 zlk~tk-V8a8^MZUVl|Io=UM@&vaZ{6zrR5xWpELGI9es;_d;D*Y1is|%1D#F|YJ6g8 zQ;;{Euf94=W+j8BI9xb&Jg6G@WB(`=6j*NW=$D$dL~lrX`^>z{G!anoct`)OKIFmIsy6qteSu7BnjmOH$~0_l9JQP-EQy-K(KQ86mnWwu>R4UFi(tKw>(nV zYq1r+*l-Jj=#EQBG3HG*g4+#ppRev^=^-GkK6W%b=DH|2(By1+rvZc%^E#Yh2?#qX zVbE8pP`GRLGHOxr82ak6lgITL0Amft<2E>4OxNk3nN*(OP4K|jd-nFkPUURL6N|ME z3`DL-UFPR+skfSci5Bwe71z=k2aNZZFvXzxK%IYLjbYPn&ObE6`jnl841*{eK5ZYL zxznK_yhXQr+G z-kC6=K^FbG-t)O*nkS=>NAh2%34ocavUj+8oU!R%jeU#4#2YX^7(aq(r^N-WC z*m^R29$>Lno*Z@^}Hsq7N3kTXc4Z$&5p?^cs#lgpS60%4EK3` zRf9Q-9POH)G}^Il)BUxK@Nj%`P&G`Otp1fH73h<%sG#ue^SdJ$%ey)|>D{+JtG8m{ zb?D~g<>X+^goT}UCJaX-ih26f0#2v23kQDcfj}_s2?-9~0e2@=RY(Mkucz7pt$7Y7 z#Ls}2u^Qy`BEQWjEZxK)QbQo{mVUr&3j1_5Ej_(!dEH-9f#@1z?(Kbr+$>NojD6?> zIIJ*Yhylfrh=)8m2sF*a73*|(8)h zUV#eV!l<{s-O|hJ2i$3eyVcvK63mHU$ihR zKhu894v-s+)Bf^CfF{qd8ae$zHkRWoP~)L>Iflpy#4NZ(y+j2Jk=KrNk7FM*BTbwK|6r2@c+`%v`6x_~I7ew91#(vOV`55eOAR*na_ zbg@7KIZ*uTclnEsH1g_%D`oQI&Ye3T2R7Qhsmx4v&6W@*d<7m*lrnM$_m7T2?uV@{ ztj+oPPl`VwVI;N@$Ui}n{V>rmTNV#n9fG_dO9&w$F5ZV01+jc!t;-HCKSV||qI4H$ zj{3Ffp(j6hiH58DsK)wWyUFWYmCp@{0~g%|hi-b5v=rS5Lj8w!@()^<2gM0&>K>=X zXLtP<5WshI?%X-ZiesaWvazvIQBg?~zkkDZCbXeJ@+)7M1`nJ(`eaC;eeo;Q_|9&R znuMU>n9lUt=xKs*ujnfWcwqpNNztQpi+=w}AhVQyI-2sI0=5g`Zx#VKDrz}L6V47) z4K}+ckBs1gy(LgU6pmZXDk>rlo15o=11S=#zp|NWIfq|v2o<|2|rEi-JpYW;eWI( z|Hp45|8mY&%i9Vxzi`@?m;O}>{|B>;z2g^K$^_U_b}rWA+f4t~UH|us$#(~;JA~#y zO^=%y8ylOOvotkDwQ4ULL99YnRu+bzuThCw_+$edhA&`bXH)ns6ojU=?zHHX9p{Jh z)ay^4wZEI}?s`q;fBoIjdJV6*hnc8qdB4>w7C&FQFQqMmYSH9tB9yJ}hZpkct&Dv1 z^v2;U1k*GBS@k#SCqRXDj@EwiNp=1^6r--+ob@V~j)W{B@n)P+F$)LNr;mNAH*XqL z`lScBSQ_50t>)&r6yhn?2emat=f({pf-+nj90Y0bb2Wm~r}f8fUx$$BI~slvurOBe zIIFn07}5{HF-${6Rn>Y|{E|D=J6M$*A3dnP2&+GQaZ|h4y3vmx3AF&TU{EO0C4i9Q z8&Ft)@&FKH@7|NpkGEW(2Yc@%lGums1iWbo>%p$l%WGN=NF+;yt1OK+j0Iu?O8)UH z7lc!J1y-0})Cfsg;isV?-~*93qz44F`SYl~QU2f8t=*h;;c?nUj+YpVgqV zW|_eJQe*oOBMJauPVz5$y&WBZV1jBO<*!0n@W3iAE@fD6?fpO*cGMC}l50g3uP?5s z!@fXyXLIY+(0kE2+SefBF7beb8L*@d@M(eb?&Ygj@s?13)Ya9INF-DtZ|@_>JAva7 zTr+Ooyvafqs>c3kwWYNcW838ff_GrfXuq`DjS zpRU-^iz_Q>(267^+_DzEf4bUs9UE{H1gQflIpk=zn|}kIO;#ohMBVHAUKnEVon_t? z<^6o-1K)#?rXBDbLcdns3}R^aBI0CvTrs8FPA#)&J0PBTULB!aBWGUYh01?jpE!z z9GCG<5E=F~^3=3s$lMUYxRS}7e)0N~5KAc?4NG*Q@#V#L_(<5w6Mc#uj{sTOiP*45 z_{s~dcL!KBC&8a<>MyK$_nwvbc&P#l`2RDi!_O0%GD*r9wn=gTxMFIGp0lUv2?Lzv zl9(9ysK@v7qCxQRFdVNN09LA{B`2HZ0g6H^;nqyPxw(0bm@flBWUCEWz7G%O9KCTB z$ZK+*KW+OQ4~bxo%MMb454XYzkq2V~krW?cW;?og5x&sE*HF1$S61H3Ke}1-@Zt*N zMA$Dgk4oF0OOd;v;X`PCT@F9*Ra^AXT>{J3d~6E7PeCiU?+;tPprD~I5h#?Y#$6ck z@7+s(#^wt$)wXFBicZ*)*!M)fWPEY{la|tRH(y>&OCn)% zJ_V4q*6Sj_>~tFNNe(RysJm6R~=5TG!BUY3A#Iq~R~JBt3~? zjWc#<2q0Q~iC>qO1F9iJLX=nx=;aXN)Z>p2BHp*pucvl@J~%r3?setU*jUnA!n4V3 zd>Je3=;)5)zH*c)B-^{YWgmb$0r&pWt8s=~o_l5-saLR?Q#MA>d< zM=8ElndpB#DRpk+iq>LQq|W{+f2nd6`~J^6jlxuVx%6@PV&H&Ee+ejp0bgosFPAn4 zsuV#7)bz-h-wBfY@Sx9g+rpuVi5*t7*et%YR~jg6ls&Ngs6@XoKa2J5 z3Xh|2JP{IZM%=CFG~mE1y?BM4sd@GFjWA#7DwRcN^bkW9R@T=0fpiSfN9dUlKLxYM zo@>J5&$ITwNuvZXiOvY(2b<54I|z%21mtAJp6X$?rZ|1*-(yeoV)(rvYNz?9O3K~S zQ+dhuyycWU@M8q)*?cY5rW$fs7^o~8P7$y^D~Gfo5T2Py0ImeUlDe+0+y2I_ZdmDw=5!n`J{xq*qg`5iVef-o?lk z*WhXWIxTikKJ@NuZgsyhIkZu^jaTfT57G+}yMr3Qj2ekR3_2HQFj8?e4(Yg5@-bZ8 z781TIEG(NYijWO_dF0@p)f!Hb98+`y&+4+t71a9fZb*^);lVvu>@VZL%?|#q63FiN zl~aAwJh1f&CkhSC7www7jOP2%K1keQGSSdbm`XIazkLHOB=ltv>tSiaB;#!=4RKtm zUjX5-nvzm)JbG0G$OcZKeElxlx_{rf!_LagEN$=TsG4%_tlBFHI1Fz^W3NI+>4_@o z2McG$tuDm7x)}oUI~Fggp5jyxkhw9~)dd!)%{M)zGw&+DuzsmaMkI5qz?}j^j&0u{ zq!(Ch5W)ID=4HQuvXZj$HLH_69R_FgF8GMJyQW{Gbm!zt??-cUm>bu_{!lI@wkdov zh)+sG`8CSsMl}7!qS3Idvy1b1CFw62&}l4v-hOCT)4L*2g2a74k4ePKTV%Z@tgifz z`VZg|whneq@U{=QI1m$Ay-|PgK#nhTfII)%o!}gruqYndN$(T$oLAro%6yg!=+W}> z@((TB7dIcD67%x%qEE`mfE7qpW#!i|U*fK?{8vrhxLkt8?evE!?K0Ot!OrEhO*O4I zk`)|v#*VJ}eV8=P~Hb$PF*o>6>h*bS>ylNrq0^(WyJdM|&bBaVNJ9JA9`MiV{znygW=YuZ<_lFjf7 z;x`P}q=4}I(fyrU(zwyc#(C|T!z-VxGhL=`F7)Roy1xz)w2qoBK4Eh?+AzS1Z$Tyy_ zus|8pfqlX0S~A4ebc1( zAp&?QAllE*&qq>47h#JvPM?nQRhqZTez(k0>7%4 zWX0+jvsneC8ZWrMPEcw_AC+&hZ-PWmwx0{5ks_05?x)q&XO}_H%4tQ*hJI(fRG~!z zg*hzLq!5HJCPeelb2}!3M$!Z1IpmE3?Tmn*qv}u>6w;s6K)C_oP7})eflG7jnFah% zyX8EU$jJPh=WIIcbn3|$P`*;K*qijkR&W?jlaEy$$24k@qUd9{^svObElUekd;?T6 zB7>^95=aATPKo6#Q0{MhIBkXbZl-tsA)k5tV98KhAMp$c$4iHn>_-t^W^9I=x#uqr zLRUu-VVW2<5d`P2PL41trpHq3B`q~|TcEksynmvv9JMVPjfP@$VESxt!udC!u8r}x zGLt8U2V`}>l}{;OB$CI-bCntSAV`BA!aYCr-6T-sl$eBs1n|Qch;n@$5dhcSP*aOZ zPrtw;#qrbQLW7Su3ngxIo|>AX-%gE{jSbdZ6<9Q1h?r}SzhGlwp+toNtZHn+$+W!W zzQLU1&iiec{_LfpPr!ADTf`7>_S2x7ogCrl)nGDVU%7o(V;)q@xIRP8*-2gT(*3DA zO%WbtEAdfUBxIVkB?0w@KitGGJUs02QQTb~3>xORE8%Fk$toiWr;sr{WhLvb&jif* zMA31_Iys@&B#T{hVCMy}bQ|j1rKXE=ILq1Hph2gpT^#Z^FbaYbA za|q2x&iK&~S1%z0yVdFlpmqeWWJr2G!7C&%JYCHSdF27i;1*2s@=^=}xeZ!iRP|fP zq&j|iGnX8?-o{4bmgFB&YFb)aYCeYt+durjKXZ;(FD}W-I^R5fk!+=fbaB?Tw8E|Y z)wHbjxc34!7n?SFX6b&dFQ#Z|$3=Y^Kp$f|2(KePRsPc_nubkNE9{de!+6Tl(UJJf z5jaXGr>1W9X`V6x_LW*Q?^_+%b+!0L(_He`v%CYVK8o;;T^;5JT#U^!j7w$kb9*d} zlHu7%P~$u5a5CXwiJgVllsS$QHi^w?O?7qO{am~a3?^WN1Y99GDQUfLOM?#u`y=p4 z0;lN+5RGI4EvP6P_T2SGFC)&9piKKSZ^t0=K!4&y zr2!N`?__B(gCL$X)AWI_Q%F)W^3Am@0zyJ?5fIZtd@B5hD@I2r>iqE%Cp71(d5IVo z*Rmg%*g~46z&VE+B@fO2^@EynZ)C!;z3Al?;P8DiiCBQ@SK>GeAismY`EJno% zd21OdTUk>q&IW%E)n*QCl`$#2*(jJG8Ubqo*n~qz#oRVy<(OuBZyN;dd?O}x zZB2o%&=br#TGN`En&`^-T7vEE?N<&fteEie!}YJiYrwd$ng!e%@aBi()oS^~?9CsT zSSG^XM>LI%iE=CNuNi|Q5JL5FXhGzFZR~o@p#ct!dCBJwF2l$t`D%3C+H65G2S|%6 zIqq#R3WF8sc|#JYV&0IBKJxnsdrgSkE&4j?`hjPzu_eiESoZDZ;%mB@i-RMW92*X1 zX%cIP(zSjb2YNbY6N-O*5&N~883y{fUcPX|eLPZb&M3r46einhF|XAJVdxs7_Z?-} zOp;G75*(i*A|k*I`b^nnEZEqov~&aGpcD#c(#P6+Tn3BaQ%n-uHxC!)YH4RD4h_77 z1URv?q%h#Kl*p{t+_sfysw~`=)!Nx!%nPuLV$|{t5RZ8Dh`gFUgqUt&Tbj|9_m9dL zB2tO3ceMZbc&^rK9^Fhh!Ii)SecNKA5*ZL~Ax?ibrA7v>ZQ}jfilm3~S++Nxo7XxI zJ!W3!X1Zxylg==nW48bP;&10sn$brRgX-Q%25O2btU+v+d#x$nepzad3^XH`#fMHV zF&)lhpee&R1o#4=4E#wkF=_?|)KpZDGct^|v=V?Jm7uh`vU2+LX`r#o%E{G8iiwNM z(Re(Jj(&__KD!0|EG^i@1nUj>VsbtfI88t>*BNAikB4XD=%|=o;ym9Alm$wp@0g;g zn{OK^n_oYxuczq@(8aQJaERiPfmTdON$CmsWnd}*-h+&CcXtQlCEYXg%gYAZ+Bgq9 zK*rjVl5$>*gy2*ef^smR0aeDs;$N^ZYsnT($QeO&x2mbCzkRa3nv8XI?VfLk;W%( zFIlCjPFUF3w=6A-KT(BlN&i0T%lR>)Mh4jG$#k+JCGvuXHB-=V-CY-$2%|FGzAPOJ z-PpUYzK(HH>LiMPA8R4$4Hl+0k9^dytR7%KB7a(3AFDIauacr;h^YVxq^&oqPnsoW z=ySPr3X6SF;IkMN)jN*{tI(JRmqj9jq0!O%(7B5kfQVA^(N8^a**A->DBxqr8uC1r z+nKC!(Y=m|(uH8_2^y@B&g$M0;3ztM;nY7{GJbUAEpeF&38AmB0Sqn>ymaZ*8Bfx- zTYRmO0{{t^+KOE+>_Y<>z4RMMXGVsGOnAG9dA1}Jy_!vPA}q4L#JWmy>UFPn%T5DWrCl& z!&^^>477GU-}JpMT55k{pv0u^YK4q^X?5OO7zU&0RiEw6Dkcg%iT02xK~(7(W$VI8^Hv)p z&v@znSDJojSQ6q10>pj=0Ud9+6&g%Fx^PHV-kCs}`^L!OW)<~Uq7r^GNYFzAWWp-%^r%12=1lmaCErIR(j2k<~A2`HCvG~$T?s(E+y zgD?Vih7Z5u(m?p67N1wnlP9KN!5PyCrdI?R*fdZ}9PI5a&3d7mguV;53k*i^=}1DT zw5-S7yW~+7Rs$(@5zrq30r*>ejt0kCz3wf(K6ZyYFYYLXf9VgU`!tB>T2*C*-qm-n z<{k$r)#X0Ex~CA0rN8y|`DH|(h5wTfE#+B;2hR(bMOz)~BnSs}MzsaLhvi|@a8}%1 z(m2cM^?-5Vl@wUOp-}V3w(S!`;CJ`w%+q3rL6;5r5?@4Dv z{Hq7)ztwEE_Bnz$k5Akwzqrh0POF=TJtQ0Q8_f(d_HPmzu>;ewCEo;K^@||!`TW^; zWqrNa1_x>XsqumxV@>nobyKAX`euM_TU#0V`N4uM58PNFeGe?ENlY3%DiC(fq@YT3 zV!z14bJb1V$Y^zb{+XFQ=PpX@-j|^`7Ukklz^|#0SU5O9E^b?gstTdE0e8&Sxlu?L zmooz^VV6M%!OOC=x~kQm)(83_a)R3&92`A;eamZWI(#c_Jw2z$pc|bOoUt{z{s)suov82$x2zpf4dwfdnz1K}g$Sj4g_nX+X6h(i2kuATzNJAW& z#KaNw7E*?3{u%`>itW{8_h*wQ=QEC1xxW55-oysbx_xHux7JJ}mLGOZ(ub5Tk zJ40%EI-xB%DC7q_UYwRR#n7*FEr2J(Kp74hkYkm6hn-Jt&Zb^&MqdJ)%k%&kJ7MRV zrS3mbo<6gWDi|_Sh7ov$2Mo~k|cLdf`y>q0ED{l>;4}b&q1J4C8%$UnXYeIlreY zu|mJQvaHBq$?R5)sEfRY9?Lp((&lfnmK~eEKc+~%kw4{xLyAL%lvviqSc2#?Zj{aV z`Uh*1A5UNH4lZPbf2ONTJpM}T^T1*BlEi7anIlTs-`Z~;Gn_WC(OUnwc1>%L@Z?1D z`qzn67H*(x=_S41ASpH`saPP!&2%vrkFn(1QtN{3No7+5fm**C8!Ibxu5p*h!aMXa zhzx!BA&LgFN#c8e{*k+v;@j5V-aa`g0UO)xFT>Snb5L{mC`S222z}e8rZVc(@i3wS zaEWC-KxYF@DOD6iu-qi?WK`e@kX`|-i}3t)>JGT>_v8E_mN~G zR3{y95V)N=1)Hd=qw7&qrvmQ_<*9N4axnZ0+_|R${%K^VP(feXEdWbe(of|MrTIhik3m6A+h}AC2 zSFlxE_Rhr{@P)rEF5^2BuAqz|IXcIRXg6O)?{de9MUCWO#~#5=Ti@nL!o9<{49xb$ z#jZxt5P6R$f33!E=O&8MGi7tT>$EVUW*6Cx_ok`34p^tWcX`#$>l9O+%+8J{6F*Rt%AVOqbaGPz5K3?3b=@EB4oStl^etXUNTa;?kURT9L6HT%=ZWta? zBEyQb^NgxMgdD|3UFj2i1ozi;wZMvsNimq~vm-Sl*Wu}!r=ILM-?QXSq8FFUYyWES z$IIZTa;Mp=X>-KH#IvqhFojdt5{kiC_j(-|AbeR#?u21D&3=d(=#<7L+S{=#i;U`m znK6Q==bxpOyrND8BXW`Cv^2wt+Z4n|FiYJNY!1c~6LFl%H>C_lQr#C5Y~8{{4+#nB z`uzEY|2P*G&Qnv@1H983U(q~Z>49Cw#aT(#`*G@EBsc{+9wwvh(NP^$zM|`=9$k8Y zPV;Om3|~xyoLSDpOL>;?`K1rZ?s_XwIQKk00;8>eW*F(@R8vG2nq!P5hG2d`iB5Ef z+t@I4BD|CcVYEp4=~Sq~j`ItW)TRp8VN%48DvN91HXf&Bcn))TVLv)}mM?s_C^(Qi zufTOXHmp)kDppHc_)L*Lo<6RK%mHqtg6*w3ebMypmtR|^n%^FWU1xhTk(QD{?p}pN z%(*-Abhyp*JsJnB{7-e z>_(%?v72LR&*1F4aE&n*#_;BR+UH+^_XtDio-D~l(67V34^~)kT0!)(KdX7Kic$=H9u;OY4tvetgXCtKHm z8*(~mraP7?NkQozMA3NNA_&ci{QV(WmUS?ecD|jQ(#ZKqRn~$xkcX zzh5=giUHl>zhfpzu$5aYLNMBr{Rt+Fs7hJX&MjFRWg5@rw0J0pQj!5s0S2Y3O1Ijf zm9aR|49F3M;tY-EO?Gl`Z|@%hhiX9a+u9PkViqVMBoq-EI<3hNOABc3f5qJ3qvQv; zXpk+mV+N?s4Mr^t@jXaoO|qrb-{;alK{x+5>=BE|3E}$RznC!=L9Fxp8xcs(=Q?w8 zffj$cz}auwp#GQIDt?rdWMgUB9vz2>_WSd`|2snc3k>S4Vv1g&xaRph;ETx|)kAsn z(38x-mj!@aQgaCj-HnUQ%9_RuhqX2A`^ZmYfA4(ncnu0Lq)mYKdl0y1e8EXcXV098 zhh5}}-n4S}MbJ-cKgV9HZ?5>|x``Ad~e- z3QEf7rp#D0t=StJ&fRdf?iv_fg|-EVZ76Bw$2>tR0AuF>D5%iSrNhwh;Jd3g_5b=i z{}bv^ema|Inv?I!!rJlWc3BQb-XhC+MZXuRjAh|dxgVg)yt zDa(A@c<9v=HRj1{hThIim7S#&@Od#yQx|cEh!pOfo<{Qg482Hqd{&kj&{*Bw_e*c} zrKP9WC;*t|TvHDVhvMQGT(Kbvz%!+58yeClqKk@nhXM~k)_4~7fJL|FW@m3|XkaK3 z`D1@3mHd^!fnpo|*g!Sp0yeH8)@= z{@ezE@UXHz!yq3+gpY4Q{BC@_XQak+*8#L~SU_sxcV2vbp>hi6euF$%1HUI zomJJ1!yNZ=zb=2*T;|$^9E&sWg9d7TZiVWabKX)Eu|Kvirzs`AW}$;*-mafEsDH=h zqeUinnWn8weixHSxk~z0#o}6T92ZsYFxzfDx*pe?qH4_Bs~;-FHhJ0E$7)>IavZ1X zIVi)yq1;X@ZqpaMQB5e0nU}JsV6uoP@ zEd<2%C#Tpr(AfZ+gax2nEkrN2;VA%t@ddz{jG(KIxpDn^5WoGn^BO-Ld|SU;&%YPN zKW9lz_WLusX4Z=jo!G4P+>NMnUktyVx;B<(ck*Rv)Zi+9-jGWobo3!C4S!Fq0GDa^ z2FZdNbLnC+cCca#P_1GSUU)sVdpTVlXx|R47VBj_;@fJ_!2(upC_7;%Ydxqr@wa-Y?-wL39RyenR4gVV*5pC39i73{8J zfRj=YOd3XuC5GZ9WW03gl6|0j%ICw7RiPvP`etQ(l3aC~x(8hWiy3kHcGC z4_7=To#&R9JpsnZ!O71rDA?TCpa(i_^l)oiTS+(2)#PGxb94Rm%vyGQV5aa)cOHWA z8iNS*5yOBx(mwzen>`pS@^lv()oJH=2WI1EijsJZiXOLob)j~|S&Ao3skbjHMKQ^~vCo4o%d+iIzgN`Wf(#)0Y z4e91Ahm*gO$gm|gy#Ql52C=F24=0|f0|t@mn(yg(15<<53+W8hBs49SIw`3JVwY(3Xb##(6hAUsF%{Z*?(7&c$V>&$%R z=L`eB5ZlAJt^uU1-G`s4$Q=oI~Z*TGpcas@4r{z+~wZW$cg_N0xR3Ucot zf05zB`SW3Io9pY%mjTNL%Gc?I3Jw_=nGcvmgiJ$dV*LXH)6$?gd8iMH$E-&p=U4_j z|3`IeKFNKC_<)3g`FnTZIDh+4Oi<8;yR^JC^m;Elr(H3jRDE2la^dx>|VCW-eSxG}oN>WsAXxU}j;3+Z30gOvXM0BvfFQd(w z;Ck;~WJCne{lFf6W?31u*9rtoW3~4{fz6I(;`L2|86UwfL_Rz^Dpy-kLD7Y_v9mJ; z?gTU#oOG9*nIXNP-TnFsOg6!!aCYy}4B+$D`;O=3adrd$jkb(=(CeR8Rlj>*T8M#| zh>nberAFW6WSYrNF|6pKFNi-^W6yq?prR-wrvmTOS7=lRME~KIVPeok?B9&+(h?RH zJ{^=@S4aJ6|6o06S=e>;LlBrzPW-t$oHU7*PjUQL={Z>P+0Sb8XUoukqON*gVl!fi z>~H&;UyDPD!atkdJ@S6J6KNzM5#MIJYLpQYl9IL|>VvcNe{`N*qTf^v`%*{gsOq$C zvkUy^nrJLW<*HPS!;=i}5v{Qu_5WOcMwhgkJ85f=Jk@WBO-WG-7e++q{_a3OxV>qy zqx1eDb=*}$P!_Yd&UL9QKX z5pKJu#n0cu(y>gC&K9}VepB^0*QvEB+@FdBdg(>KpNxcGkA#pcRNkZd_wBZsUDKm# z++LQta9P-Yysaqu=*KE?lcUk+`0|5I-s5w{mLG{%!**$EcaB0#yw=}6{%&&NAoeDQ zL7?yKT*s2skEPA~Q{U~(-X8_+Vn(g6T%Wz~v-=IqbT94LjlK|>e%BM7yRT_+6(LHg zzwlsJ%DeQh=h_4(te|%j=V##q;W;Q(2Yv(@P z&zPgV_@?2aORbnRAtrR&O1j%WvX*@gw|ojPsG$*GFxov*G_fCZnUXAgx)x^c_NA&- zE}l-6>9yO2_;H@!zk@t~EVl>c9tQ--si~1jDQPh2=!`0sQhcpq`{VryNpuahKZG{?);-CS=FO*3g~69rkSCvWhrJ3q3F_^Td%e;D@i{>2=ZWn+L`>*rYz5>?BF ziPB?nf3Cx9)nn&*8Le_~e?mYF?DX&u0|yZ_nr{FcGg^1?cEUX_qci z7|uTgCyg`k3r1|C5W0`|HI8T*8OM$%5?Jn~xJ9G`$L13G85b_(Mv%Rc_N# zp3CGdO3|SMpK;HxSKroVJwaEROZCFW~V1S z_uXz98EA9MOVHTwJs3dfu6DW%BXFDLsO?mX9*O2ECZD6;H26{?OC{d249R}%&83O8qi3&n z8Hs;FlD0MrI0(gg*AibQg{eqV_CwwY^W2Bp(@yY-#G0_53la0m(rOX3dMW$26~%uU z+0JN`@@Z(-kzMIq{$Th~0{iFG7SN8!M8{RJTYHU%d#W|RSK$otBNf)R8!-9sJ1>u1 zbf|h!US1BiKH&BZ8k(t3=q4X9qaa%x0sW!ed342V_9R|~`saDgyE}qj3#5%1C|`Fs zDk0ORp3=l!`gT!?i4~2`q5Cm{c0kLDN;ieE+^}l=!AB4F-(Sj($Y$Z;Tv<~9 zgCA+4x%`6tQD5%y_YeC$C&gvEtf`B4q7#z_K<#hKhxeyEkKa$+Y&!J2Ly9j(|G*U$ zNi21gIlIWZR=*wMg6W7Ct?Vr^z!<@nVR<%$^KbX%dh_(kmted$tp%d-7u75=JnSz6 z$_vslDc$$6piy9ZHZObG;-%r(sZr?Xcr5MhBe-D4h5^Y^bIvs&zpKJf$$?NPPA)ER z;b0*O?~cWW!~C_Fs8L>BU2EKAtg+3^OUQ`Ut&HBrHni~Vn| zUvElbhKMk^ion3Yu&^+2?joqIu5Pw5>xb8N(tU3 ze|RdiJ`~E332&v%BX;@n%wxv*9{K)cb4pI2SIzc7l8S(YgrN|o*jQg*@25|6DclB? z5s`Sr%o^BYn#Q_-WI*I1&~sq5|Be8KAl~tRQ~L;yRE{S~h@!dO+g>?9H$U+@-YW)F z_vvlF^4Kh9@gdx6irG!#B2J~1-Qnw8;rLP8PVq>O#FVpbXuY#?0wBLgWSgU?#ZLu;FE%?A-ghAw9`w2MAY_Chuu}<6yIPHTm0UNdrm`mc^^m zE7$FGyX%Q6y$9YsUo^fKy^5kdSF0l@OF67vcYsis$ja>Id@|Jjiqe0??|RIU-yL=R z@xqboY|eR}l;eFtxGYi`3mWiJd3_N--r*K)g^4a%V)5cnR#N} zee!(^i{I9bs|ZiGeHne@3NV!aD1qb%NlvaQrW^VBV%ge;#_PI=#mOLqe)()7_V%GtmGlR@@$+99bYVV& zn2WQDM%Bocckf7_wh`$^p0F2XG6LwA|@q z(U33dilKFjF{OLuR8o>}LdH<0<&D!zLtiR1Datj()@h)fd^YT!rjl+XzL?tKD=vBs zS~`rS!D`gILJ!wlUBD8UBbvHV`FK>b^qb#3EpMKkFEb96+A*{fgQgC*2}XCm=-&$3 zGMyJ>qCOqR@GcS`P;~a%Def~ z@8Rq8dzpY&s9O*SfW}U{ApV?4^rQyZ&fSJJrb@=AnKW27yLF3c#qVS`!7uT}TzCVCZ9o9`+k#Q=gbB2=660{f zb+dQ3vB4XR@zp*-t+%q|LK1n54!*o9l05)PR?99$Ew=oOw$!=<2t-PNFK?%n*wJ|4UpMv7(fe6d)~h5VQc)*%D4%W{w>Y0(BCYzfMm4 z83=G&X+2+04x$O}_G`*F9l~0$Y0eY1!#{ZJU0ej2KTJ%Fw=63eo-9)>9!z#t3Q03x z?Cu+Bwa-^hHcqB6Aisb-thtS#QoxlRSmY95IMi4jTv5x^Igd|&XddZ29Z|ct|5^ai z<F|J18E&O0=JeDoV_$~&~XXw`3wvm zu#mD|4aQu~qj%1E`9$Q>4^2D%9orXH>Nr1W$oto1+r6le70@CrexA_!-K_s_m_Dfw zwwCYCO)CUBvqe$3x<-+J_3I;@3TfuLClZXTFQ1eXap~&npYk*vI9WAwqt>^#Dfk^L z+U>z;2D%b+b910}s;iUshdI@Tzq)9ek#CAp08$6A)o?lp)B|!=NX~+p$n-z5+7u8IOIJUT$4=T0kxV(xAfk=B7Fw)0zk=Tmq(qFWZ zy6&9Bmeet5OE6tZe*oIA&h6@FBem_6?u++iMZCN4$oG3hA{a~QA7A>NBeQ&ADu682yvaj3d##w+FYoDg0`ehH(na!jX8 z^AvBM3!|hF(A?DYNtTKjjtsrY+EX={Qr~B>*MDmyG}3JRS0n`*_jo|_)weutnczJcIC ze?20!%=HH{LEzkm*xlYP!K?#TfIMKq3WC0%F{2Cz*LCu=-2hNo&5{QB1{e)BD_uZA zAAqGtooRz}yB^(kF$sfvMvt5SF5ZBzyiFfhtldGJCwbPz5QgPs$WSsXP^><5VZov~ z;WapCtjp(UEP;I;gSd!DXQQCP1=Q!&)msci-uDfgv!dy4eD>V9VN-?J@QC3)raF}>;YOON zwriTMI#!oImZgE91!{Bt^86=V^}F;rs)c)Buy_kOLl8HT$bK-O9)8ny2xT;VWVX}+a!PGnJj^oDdD z0(hkySNFPv0YXiAy89)%<6nT&ZgC52%BW3JdqC z(e}BPslv)c$plC>e&l2Oyi>rS56(xU`Ezoqw;7IPwJK}H?%rY%zVK;~uXQEGsrU-7 zV7uqf3#jPny{_TXc39ny(dcW}e}FI5*?&r{R^)x+rod` zRYBvtbU`g^@^bkw)Vfk&)Tp>VtEs^@P`vFhJxiydQQ&3R(I=|*`{rX1rR^`y&9NsD zDgq4|sVyNS1WYokTepI!Me~j7M!LK4GU-CYZ(iZ{Jzgb#Z-388;%(>|wsq;@;bEkj z>)Ig7blyN2_!0d0Wggw||8XQKa;~fA94( zUt2_8wEw|XdvWp+wX|=m_ziP8z{Rg>^g+d&TJAEc5q_=&sVbf$ctkMH0$z#U7F-Vf zt%DKYQzqx5)GMatkJeuN-BX;|#pul>G0F znDJr%x8kV+aPt$J<&lEZR%O9(q8q+MKVwDcq-JNwwS$_@`oa4R6PHN(0BAcaeF9bX zBe2z$8_d-kzvCsP6~NwAvRX`$*{|qaAY$ZJa=Wi=6e%jz*sk>qCz+>3dH23 zWyI`-Uckd>Uw%pmx(F!}D&)frKQChuMyXU0l;vuwya&Tly884XaJh;6xT=rH_yO~hF zWp#Y_XwJ-!b<@$n35@Nz6_t+2DQcxgM%8~SB`&dCSkX5*S>stiU{jYRtV6U zy)@)*l?pdaV_l?2#$; zp8jwP{^b;1obl!lwEcOHlup_@l*)8z^Cg%gU_;y7vAeU=dGmFTdLugX#f#Tl@~vBT z9mvVaCu{HFH|B$BS?q|f+`OMWMswEJR_FwyV2f1OeETGw*T&A;x{J5kIocnAqx>RCtqMCYGWj>}%IS20n|gdT^72@~mI z<&6RdV66OK*$R9hN^tn2`AT}qTfi(S1gz!OPp32zaA7z)!wICzglC%31?z+|icymj z4_pgP&i%vB`o(<6!iO`;mIK5J)S?a%(a~jGq(XSMc#T+#PKn^A0OFk!rC6|@&~6dI zKP+~CX*}KrL6cdoqo(#2DDJQoZjz=4XJViccyE1!6$CIc?z&cPT<3XF<9+MTcJ#lo z7jQzi-pE@Ks~*4iwo2h-87F2Iuy#aEg7A88015FZ24U%<5bsk0mfwjJ_Fw}|44fJxzm#uA~2076#? z2%Q1}s+n7*Oj1Ik_y$Z|@<5cjXVT}W2yQvZd@#8|Fk!HV68gUZ()GU9A_(gP?z;jB z7dUne_{m%#a3mmhi|8L;f&i{XK%=Z5Mcb`5j0p|1b=aZ~B z(2zh&#KaV$2>A(&7sHrB{H0M-YilO0{30Te%#vba^^TB|SEuSrWQYWZkCi8IivV0D zh{j?Chdl5Cy^Bo|`-Vu3)I1!_F}89)fA(_<5CE$uLh&gPAVdShV67WB zAf+2x2=(U$44wlSDhfQUKfYcN73^##_p{jGyn3=q6Y?~GAJcGZ&iHL{AHUD3`R%6w zNW^A5hr5loZ?^wD;!B{io79NXNT(9XlG|jX9?ZUTwyt-zmJ}+g_8|3`=w|1MoQssa z;$9poed=o<@}nt4^mMVl%Ovz*&uW{)p;#S8qWw(DpAqLtR4@@9)j}AM!zsm8N>Gaa z#RmKj)5(6-UImu(vTyxBXWTc<6~nSUO@DKbszt3 z0?z#+k>sG?7+r}mumFOmNXJS5N1vFOK#;}H7H@#KF&IT;mS{r-JqO%zGxvP!&BlOi z4h#{Sqd&5gaPUb_gg!lvUI>lFnLdS0;rxb3i@fvQMG%r#cpXY4z#HlnVGIWDu+lCV zwdi#;HWhha4QRi2z{GzCCwOcL&24|ZRdSC|(3YI4Fb`Rr`S zagOmjR8uWrK;jLhv=@=jtS!kt0I8kR>v;xCFX(m7v?@#LUA@uxpr^c2#cniw1+h{r zeLjrY#Kzv%=F$G`U$^ZX{IFT-iYp{P_h-|!yK+7dq4HjWrD22Ls|;++|0dQ;IAo@t zK+Q{|HQI!7vg|2IUICtLjvZuENT_^yq#!&n5GwbV0@PRs@?l=zP}Z- z;_#YjA_op5q&Xy(ySliv&TXcqa%w2!-2XLR!O$B-!Fs>^*%Dacku1G2@zz&n;adkT zA-Gq$l|d(`%yMok^2J+OmetLdq(y3pbw@eX)3Ma=l`CY_m1kYrT*ta zE%L5VNDtg$&-f!NmfVx%szi1?Mos-}J4lD}8ZEOgvt9`M*Lcl_L zIEx()l|s%vL&LCjoTiz%xj+vWg;VhN5vc@cw-(5H3hi6N%$qTMWDQxU8ZqrX4a6se z<0|bI+Pr;DggRoW2n@|P6G@P?fAI`xLhZyGU=QM|PhcrY_ff&c-dWlgy- zyUh=^Vx*UsLo$<2QvC~lrT>xH?#`d61xOZr`!f}!ep1#d%OQ~eWD1KyE7rIEyZ*VA zpud4vOg0NQg06YcX_IXOE7z!kei~?5u;Cb?uUJ`KE%~amTA3n5QFT84G)UfQp6vR( zBQdU>8}=l_U5GfnB|_;~@Lz$M-H9B8urOs27~#dMMy66>Q55Qp{rKUpN@Dps@qKRM z(Za$)v0`4zH)&K9{E3BypiuGMwaI)?CZw&x3m7V~WxmJE$mlCbfr3iL`o4jIHDf!_ zk!Z!b8neL8`+dfq@rMgmE{n4LpL5n`9uC%&NRSF84WnHW`*(NHG|X`6v!?!z0r2@kw;l4F!b3o@-LV!ksX#aMXGsl{Ym z!>M~GL%U)T*^jVTAhX7S+HRuqyD*0w*FR`Wb;t}!2`~LGs-_<2?1+Y0Tk58o)Ekvg zFvAlH8yfmTx+nLlZkJD6m{Ge^94x7Jg7U@O!|C5hwugdBL)6@0~w*HsiOmi z(sJ${_zk_?ZY`T7bD^XFoczuz8;eVX4J8HMzaSbS3e$VZv9b7Kr;y8WK7P1DPY}+P zAb@Dxp)MW>dJH()GiwVA=e<>0;-{bMp0r@IVR z5tr%&CxbVi{<0QkTKdes{Dg7ju2s9?dT4fQY&+KqTXAGFzf@aD99I-L%l_7sQXxr; zktqrOf(81Nnd!#RfA0Wg=21Lxuai0Xo;9EC#_!jD+(W*i@|oLSv|iu~-06`&>`0c_ zGn~a|9$*VKEZ5i5H!@soEMyf4>ux8n<65V*3HAP+P3e7HA-^W|%xh|JDK5v}`*%<{ zyfLb#8oT#Fk1?{IzpFhb1_zbKjF#-UVzq!81+4(a~o*s{o7s+l<45D833@)26n#9UWV%cpT;MukZIaZYc2G zOhn=8=<3!h4!@EDU*y!j@p0B-%rvN@wH53n>_A-_is$R!NY%m$Q&rJWUw^M2f>kre zl_614?y(t|Bo#L+xtN-p7n?Uzcs7Huk(3^c3Kc13QD#q`ycFKiZcltuevPO1tivP5 zus9Eg%JRzYzM+=(p~bKCb9rgYTd~XyNo(3dFf)KwW}+_tA}i9>K`->qfgCQkzR@|dvbND zbMok9=9=UL&Al0YUiALX+Ue0)UU6OP&uJxV0+s<}YwE|iUv)XVTfyFZ7{E(j(>D6ACo0ZT4kTg2D@FpV4(afUokF+@yeV8X#nFzv0JB5k`JL-2iwD-d`< z?`3*s5ih(Gmn8+rvtb=k+R|@ zUNH#nuRV|-$QaYx-u+m9>zrsRwFBDe`g*g!z9;#s(9q2u z#$IlaZu}|al$p8FE!iH}#iP4M-fR9{92+24D%PbXjXe$4N%~o{wV-t(ap|}-cVm)8 zN~|Ojn`MDuBGGfpD41Q={qRsi6*ecn5U%?{gRR|ZrQ4(V7d=a^S59W_&0T%icsvUC(g`%j}|G#qzsiiXWzZh*b6KIfbHU)O~9p0vJ(Na znqX{fm=j^Rq_jpg-d_EmbsVmoolXIw0sex%KR`+N#a4Wls_k1GQ0_T!JR+3?!4Cf* zjR=f)pxkHV3v&U>*hU7EhC9A&Zo(r(#Kck#Xw=XUW8w%Tji;xt*#;2zr}!|EZOi`g zPN_|Ls;F3-YfzfvZEKrL@_kH2c8cfwUwii!;8^`wKG7=3!wuUK7tcQ+HQYr!ahc!C**(<~ zh3uj~I6O3`uhwiC89vWRu@v=>LM8RpH(%V|X!}YJ_jM~$Dc|(NjBJc7aNlB-hNQG+ z?<7+Y9shi*3cbDKY#pw&1ynV8m-9Y{;}I3lg-=S&iA#O##)gXLBYb{WF07N6#_t~I zQ&gQ!F`P3NA8Qpvi)`h2oTdv)tVpe~4feRIipuVc0(GujEMsY|BH5Y$)_+?A<%Q~- zmZbr&>h>j{xi4JJ=QXM;ON#%i#~?@V&;Q}T(>ru$OYUd`6MwzG58Ta28KJV!@m{-l zVM}Y@?mg&EOzCU8ff=B`srr2XAN1#uIb6ADaDEsHzL!EQ5)|ACA8$Y@s<6U`A{2Q zReY6`EzHauK@z{Q;pjq)tGmVQFf=j(8A;H)PmzYHij@(X@i@`@A=dbpg&(;&at0x2C4R}cJ;e(SaiEs_*8$`JJ{guzv*j!=_$kx?R?g_!e2gZiO8{IshbYCgrZ#v zLVU6dDD+G35hAL2&!D?|>n3x$l~&ig|Df=Ah;2h46a%#wDD_8v2NVbZP(c>Ak;)d6 znra4P+f0%r>VK`!h%CYLX~C^(TZA4eifiqK%3V7KywkRlq4Q~UG_ru&Iy^is{rl|M z!geTYxYA7iQKw2)WH(5r=J$t%eqwWZ-c5w}(n5aX=~7Dqd*Ix!YU$mH>gXTJuJVkB zOUc(pOk`Z9tX`ViD!*%uGQ{sc!s%%|X`3=0V#vXB==)6&l%(MtwHxpc`8hp&V;X6d_6q{MAI$eS?jI%EHJ5D(J z5dHiAp=c$=_f;OBYWZ#d&t?FFjbf`5vl;yYrxmC0p5=-C4y(6c33HTXw2Y4}2EFHE zm_K$e5Ke}?SquEO*I&5j!gls6$#>Ee2LLR#BfofQ|MuIG=ykGsnW(=tuy;MtTwMSynx4zQqjc>YVLS5D=YG0kA zO8Bch_duW;7qb#@Isapb?!|3O)}a9LAFVE5?WCjbj2E{UQ&4X_nA2vhyB{$AdLiyB zl3}FJJwA!j*StA3%2{r(HuQ|7khPSA+XAlOMLqBBf$hW4`18+}2SOPurFV@VA6iGY z*kAF>KyQ?uBdswI>W<>)>;zEd^WV{U)^mJo5ydv{}E z@Z(vDr1AAS@_m;NNZYaHQ}c+AUw*#%Ik3lCu`d=3D|-}HYv$3MdTjq#0zIFt66Smr zw3d=atYTis&ZkM#C8#$P_mzRG&m3!mRuCU(i?w{UV1cwEK zcKsL1_+kT)>N17K4}j*rRdKra-ASg)dvd-yNV8>De_#Gabn@l>F8p^p4bi8Y${v5d z2mylQcz!h5qMsEOGQ#nI0YL~Ss@*sx;+|d2m(#xY$!-$OZ6^Cm_VUfgGA*+5R1TH< zBYlOT^2Nj^zmmRu+WjVmS@_ZNr`N`RU&)T_^tYOD%T1<4xUjLwJ)OE3iViOdKf`VB zxzDZd4<#w(P8If~hu(yc3u8p<*ktfJfts&BfC*r4b`z}6zbrJ$-hsqFFgBL|-2uF? zzwN(vM)4_&3cho1dE&C5+eiE*&z-t0yh%pIj6SrCz7^~1U+Pyco8>k6U8Z||ewIB{ zmyLlfyi$V{Wkx+#xHh|OWSKu;aj}_x+~;`f4SQAYzQf{bGDVX6%o-a#Sse8A^!hGcl9aUa2WKXzUI0abrUKZTPwed%;(lshh{($T z4&4+9isJwu2je2k3AvU-L@ah7XnXn;`uQRdgglPY)^1%=6Zj;LKV^qHn{te5 zSo)DUyit$jFaViQUK`l5(9(mN(6H*cGkBO@dK+dt+SiBYLnq=bvDxw0psRxumD>B~ znTldlY_e>{_PA;po&@TnG|hVI|3*x3QD*1FyDsJcm`n)dQ8N(^1eb|%sA7RQcKEPn zkT34JprKf>n^Xfis*qusui`xn>MPUMpBkV(Xbdbsk;8G2H`XYm05n_63B&y%4RGR8 zd6$sD36u8Kim;*RMnOmn_?d0|CAwS;9(eseM*gy4uMG3!75xG}XK1`vO+w2z1dS{A zVeYTjAR+I7_Eq6R_AVrOghM)*I2|i1xg1D+6kWc*=H+^^2}Doz+Hdm?&ss{pdUCJb2L3aJ8k zUbdK$mge{VGwhSUso03GcZd;3$H&*0AhY@D4E$0=CwTIg5uM6=nBdU$NA0Y&@4Y`* zNL=$|T1W_s9xjhkjr39J@TK&174R$_H7FWrC{)xo5O!@MK)~F*vvI`)S zFwjIrg3nA2*r|i>^EL%Gk6w!P0Npd>9V=_=wzH|{!kOmDpNE8n&E`Fz>g@HcqI8?W z_2w2K$O<8)GG$wr`C3%u_Z_HEW|V`zm(?mU`A{SfNGS9ATL2pMJ44ndTp(1fXPM{M9`*>ZEXXCaE=ZS4$KVL+1X)m5OeYA)2FE1 zq@)Q9Nw5Os#gED?+UPZ4)~9?{TB!>n=4eGHZ$f5`G}6Y|`D?&it1`TGVk02V`ei_R zOS3PIIxw_?zdbfP*OLHx)+`fr3xp8nKi!=D4(&i=ZpD8t3Q!OY*$+%~cVp2ra)w2Va|PfKww@6j zl228pBJDY64aqMEl_e@Ar=OaqC<;Yxm88gr{^E2=oi(

e=?)lw@fLguQU#wp#D_Q78%)v;9#!^RVa&E_5 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%0AAm&_ZScm5qyVIM>}zBQ;PD+CFv#o$U$*$O zgu&sLIPM6$^0nrIvjy!3H+n!Einnnbl0R}tlI^T9Q_Ocf)?3>}B{@%brEP64eX@C_ z=xm+)%IS< zd=})esLFXl)RxE+?!Au7KyB5|AsTsBT2gXuMVb4UL`z?vhy6NjRK7}e1d6AT`kD4L zW`!)1eAKB|g3h3-ra=&r$h|q0bSfOahrtT?&6lR!?~))e$&Fd7#kbHckw^AY#Ld08tq-n z#o`ohzL7TB#eGwy{*v@F!2lG5u+J-dqR{5PB%QhK?cMfKskrJqJO6L@hWB>xV^kS` zwEq@#i1=UQ=Dy#ysf~q+xxFfJgS4OR9`0-at_oHw;kcDVM9LJ`1-LDfC*RIvvgq0?)BtFXnN9Wn8UF#_*GToq4ofv`!- zIOL(?Wi7w{*mr%c^=cFb2IVbLPjNsiL#i)^eee07uK`C|l_9UDr+AMVUAH*lqf5W@QQkaE1Uf83; zj{bs{LTMpc>$TZRLa}0a5KfBL0}Ad!I{r79r?qJB>2vQ5Uab8OEIGN%ej<&CjtEJP zSvm?CN%0#O-XUPzZrr@dMG1lQK`$ZM5&(RM($KUEiU#U2Y>ZkaRHMg_Z?)H{%q4Rg zOPZN6@~T1>pVBRenKOzC50B*x@sOZQRY(p1_X{p)MPVo;WIWr)Dd_8`2TrQwGKg~L z!2TgdT&k?BL=fFQM?kKe>FvppLLvnERa^*U(j%-Pdiq<~Amc#a+l^LL|A5ljp_(eC zjNv!W3EC}((R{<`<~S0I!Jp1%h$OW+zt4BtY*t$Vn=T6oKsk}8Zo~W!mK)kcw}eRs zE;DobP{FJ}iPnG{@GnO}K&p`WY+oYY}cn97D>7iDf<$=z~ zjQ7LUO6z+Dqe(8J+0E8)mU^leW z$M!}!_8oSr274bfUVoib(s(`|5!W>1kKzL>Zs_KVyj2|K^kO827XAUGcf_IgDqz91pjR^;#>vjvOm=b##o$R08dk>mWq{(s_xOmzVD*Yo);Kw3cW$HTA6Pl|!MG-TOFkJuFDAluf$VQ6sRcqU4Loc=%V zHo#9gNZhU2Lx+X$&Ka>uLATrSI}9ZdIV3z>9*TbHIh7~JKh{7t;^iLl=SJ;@d?P3m z=~rN)(osj4x7{fD0PI+x4gzZcG>Zm5B**pZR-je>_6=+k{rEwu9y&oo{Vv&+q)N3Rr9$kxk>UtOnE&!`~5LX9-`fIge=kujiit7H~q zox{l2E*Q@=a0cAkP+=r_i1zd;LT9${KNJC@hlaSecx@>C&Hk zQ(zJi%!|DxQSWaj}vhNq0S>9w#Ig#}krrqff-*KXB0)g2()dX7C7 z-P#iq(I_k#D)X$Jb~M<~B!uh?0Kh)f(|spXnUkR7rC_zEi;-&zMaCfNRU$^XG4hN_ zi1-j-2Z8D{>IfSzMoyaFKt{w3o(Bm_Zulb%FLpW!pHLTuj1gs4KnaY}j?sk( zCA(Jw{*d&p%*9W5oYT^JEv2|?@i|nW1#2IkYTx)YKK}3lVQ2_8F@gY72_2hCjxm;F zm)ZWXpgbs$Ak51QGm7^_urO{1$@m;47#TyxqQt>>F8)46qt8U4*_CR@Q(9lwahJWS zGsSiFJH^@AS%5q&gwg~fF*dXrx03pc)g<|_A*8^p4Ma)<{0YXEmJ_qHvtwiCCMIn* zQgk$+>ilE16y^G(nYzT@$mOlXl|MAFg}(+}Ow?5hT7vzMqYng#P3cJYDzw-fw8)|F z9o*BJs2b=>ih~oTY}{&>GmMt0MXxnYjIf8Rr>m8kPT8Fdeio`vLf$vQ;tjef+;3C+ z5X9g;2}U?no6N=P*kdr~Z%}Uz-V$YIuc(l^ArMTby?pQ{e9}(>!gl}sCdtv!0#)av zG7NmtUZQ(eS74>@D{+g_ZQ}1;qq8S}kz8O-vXu1kQo5c<=!f31IPI^z-k{_ze4?Rv zVvFC4eV zlqUR9Gqt*_{T;7L309~V*f}a$YwSJjOETuu-!cwJXT0y`7m1_9BlWD5Ccep)IWI*) z@qTplS!igqd_EjSs7`+{LoPF1EMd~I2??D9h%T$xUNegx=i?ND@jq98IMt`p2b!s9@ zDTJo>3~?0VP_@o;sVF5Ue7j1pvV7IXTr^!Y@wP=tD@Kmy&#u?A1*{*(t|7}RzDVN^ zz9E)vZG@Wsjln5lY|-qc?x*|5+6N7Zs;?3%YHGy14ms~0B0-=L3}ee<6B42agcE6K^uEIJ~pPB2>pDY88;``j*0rgK40_+9L%cAp!~XDDv1!~ z6%go`#TR#6>VaX$MOMbf*?!FSDsaJGur52B3k%3(IjLabar4*ynR_sRZmwzeNu2TK*=o{I+vboU5@0K&BzjAvTzrMeg zOqzUy{HA@fup4{5Z7o|kTQZ>`(WnbfiCWs$S3F|Un-8QNcOxPqzy&7(TBd%dd8uYz5)-r*WuyqbY42SH ztNYd15zm!Yy{VmIUN_VxgTfY#Ia z`I+qAwzj7?j-ha2JPDI{13)}GdudG#X$b@^qzOn~8s?dJIG8FUz(9bH^Vd_w%m_@_smDd&W1h6XYy^i(9U39uri`-6Alu6<94fF;#l>W(G0NbOm)Trg;w^0U zDlZ~pe^N)@T}JL=zb=`5Pt|jv=Y%a=VOSAIzHTuF9b_ONnTig~L|!_DoNN_@_c8Ap zdawASkiSu{*wzfc`wEcY|dKb0Wr3Ls~H>EUfsR1nc_&FYGlD7GIjz_3p=2 zzkctF+oJFU%&H9cd>m2OOs!)z`{0m2;L!i&gI&i4HN=rq@9y2yt*L$YE-s(KF&{fR zIhj?fY4&#g2aO1a)UF4ux5v?j(gaa1Vp+&3zm^v%vf4*1-?oKcdX$iiEGaP&JqWxh z@a)$+Wvlcw5h7PT4bVgLcl-K3{SNrZ-5;|&YA-WJ1xA~s(Owca+EDP3a z-oZ4%ICUtjLE(N^P+XigwLm!?dz`_ps=_Ytk_D{WjN~yS-f}AE+@_;p{HOYWi*ib` zDZ~9uZ$Qh=`>pv8*8+(bPcQXQJRIxY@2dG~S=iEQlwtExfz{{sy!6Mc*HM8wlbfnf zE_Uu*vbORkL+NtbPXhc}s&kwv-x&-9P{+1co=H!K&e&gx0O#M*rTC>Kv#+0^Intt$ zmK3)Q%=-#{iadzPlI4rZz2Egn2zadcVji8z`ise+6nwnAA4f;8X?=H}UtG+sZ{bP| zc}Id+dVOREZv6-=8yiN_ZI}#z8w9ls()e@Cc-hZw`Po+qpXA+V`_Za&hY+9ee>{^? zS9(+2Ow-(19;Ruqe7^cQXO9(|O2V#r?gv%M>sqVGCkC~M)`88Tjq9c{DOTI4C~7$8 z;nW|lJ`NTz9O2T^BUC`Y+lkFIRQSdfoq2@giD|rgxmDoL_bBy|g3q z4Ee*E-oHMx2oZ93V_b@rNFqG&O+Jti#I6O1X^L$WX`tjh+$zhC?v_Q8fG5hRF|&gz>{Q5EEnCeO~(<&evwq<%ZJTRxK$KEd9CZ;FGZ_6d%0w-TWd z`ahP<6gOK1THjO4^10Aw&)PdFF_r#Vq2i2|+aE7i*#}rs-KMzOBDW=l8X2*x+@&r0 zHoWb)(Od!luC)uHzp0gke*09M=53r(Et+44)wH*MG^Oah4iZ~BqA-7C{@F^C`WeT> zj|gupE@hlYtoj}mHj~+F#?M00`sgXF)xr&58n_Se7^7*E<7GOrDqWwtKkazqj-#wq ziajPhpVYzON>CY#jU?p?hc8>R%kXTP56Kh1>Tp9v_Gx~#DWnCa)x8>)>t zK6(60%yjs=$$SG}ZLa(CkZ>~E6LVhvsax)QdZcw;iC0o6j4FdqxF!tvxkT0RI3g%4^)mRSu>kB4w?}dW+e$6pE!jvnEVH>fZEUuW&M6e0bi2;1w>3)qd`B>3WG6%g z{OJ6xmyNs}d}p6)iRZa)2yuVp@U^RQ)J!@#cdyswVuwyA-WDsfng$!G7{(^r^<5v* zeK&Csr7s)OvfCC>^>+58zGRv87f}??pOt5-+)&5W#R1`q7kP4Kf|QyV5&vJ?eN)Mq zgck4~bGb{;73n#2Fu4e-5Z9qb$UNH**nu6b64}3{A~VOTSDQpBxicNrq(9;DG(O1& z@v9uaIsGLu;sL*5^2?sb*Nx>|mX+(PSN6)!_x5^u{o4i1#}dwbn+|qdx4gD5Bd~_F``xWGy>_m7$41C7 z{9nIJXg5C0MuM_Hv6LT>cW9J($}nyJb*k%I9JJQEmbeU5Q`G9NM6CGm#c7ZtT2Nk7 zdb07Cac&nqiL&$2RNtM_&5R)v^0R(x=ySA{shA-*$7zb{I$_SRPV z>(}MAw2~(M+~l^mx4(iL%i8+$jTd9n+UhCf9%aQH&h<>yvzA}iv2#T%CY1Od@)x0z zG@=Pjwtnebl$u*z|KbKWEZ``wUflw3mW5WJCPYR4+1#qD&eiHVo9>3Ts5b7hE`1BM z5aWYutNHM_nfhJ1JM*gvZ=2+s_Fp^}7k{zXO$BoloF<=)dBHi}aM5#kX8n|2y5Dl< z$r)V4mouxyxINoFX}#ZhE!Op#Vp7GouVr7(^!=F$+waR_DmS}JbOPZbe)dWhbZ0Z& zvob3-emdUUy{^(vvr!|PoA)|n_Bh@R*i2PaUrh_NC|hiJQ}e2n)_Y${{sSrIhfgTu zqVc+!3@p|XBjNx3*^@m%!xN4%&9L#$v-Hoc7M}jTv>?XAc1__yVtht7O(z}CSZyYN zWzfYX^e=;$AD|s5+nFUtF%A@%Bgc!<;@pZaAJNGdw?VJQvr0$giZS zT>50PR9zryhiB6p(lR7AvWL+Dq)wQ$&GIw_! zS?$=Bg;iPTkwb#Q2(>9ka`_TD%kHNtae|9hqr)EG`cGm7cOXu?qOkCxrKKeVa3klpeBk5A6%sr> z4!^(s=&=oEkbfe#j$&3*#&>nK#mF`tm1$M}t3vNS>yU+`j0A`l<4itCHV<}N4m@#V zec^}hQSVV-pSNjg8$-C6LN>2ZhLj!f-8PM0dQNWJ**~+h@^%Nv?69*f?d(`IOifK| zL3AK{U&a#c?{C%v?TgAZc?hfh7p?#8{hqpEul+!&KTczXRMTuVX%sCo@3mghoi4_s z!w|QVpb5ol(eUL1V8 zZ!Y{tj#}V5MY)luxCPMW-4~(A;2)L7$Zm%KnHkyP7DT}>eol{RPPR+7?bULn#bwqL zyL<7XPuAF^o;~XN#6vvBUzZV6EKzjzgCj}FC~|Cev+N8u>pL_4_Z+v$5SKhWJb-Q3 zjUjWfvbN4{aDcV12MbOSP{i_#MC+d}!2~~mOrGEz5@H9zK)@%a-rk}+Jj#rnCS2+; zH|>r`0eU^l$-AJsaO{pVl{dV6%%%L?chn;!SUz_RBnMR!E3m8tdPt3Cmk2K?=bc1Y zD|JRJ(-ho6zk`09=T%l5NbFY~;FBPO`E{m?!bD)K!OSAM=l;&9u9;MqGWeA<(qIJ1 zwQHRvS3>dYWK%r@3;w}O)n(?AN$cI_r*K+g&ATr?5LBlFr8Av1*WCYvasFFxkUNMh z>m{%0bN6|aujQS`Tuk=K$;o%aONz{?B_27v330vTiTI60yScmHjujLUNz2SM)YDUp z$MN-*nNlP}WFx zMvNNOuO9p1(=nqEf0dfu$O6Nsm{c$^RYpdJiHQlK&w|zgXDv|HuyNx`Y=Qf$s=B&2 ztu+AR7_@K9fX!VU5J?&`G8STN7+bL{Pm&8Zr1`cWM!jkawog$JS9&jQHmMN9LIma2 z-MFx(is~EJ#QTOnEsSeD`WhRU-854tgQhaAmZ3v@rS_djTtp5I)%$8je#@k)HGAjp z<>N-~Y7UsxfIS9Z#lpV-a?Q^VW(kyOS3aG@%5MD!nb9Bw2v>>jeJbH#z2ckK%=LFXA_v3~PhYO|{0oBgqT}QwAR2<*&@*t?N6huR9t`M% zvk|bg{qaEnAPMj_0v_m9Ko(XJDH$0~7Xbnp8956s#&Er)h@81^-}DFCwAm2_2zEUk z51HG79_^P|brBe`36yvaGA{E@dZQazly&uFsUnW4S)1|q*W#=EaHI5`2j73WUhqrv z$MIE<;9TW{to^wxQwXS8qK*S#$RFF&9)^oxlmNI(HwJMsy|Ot1%8V~y>k zCU~2#u;M8Z;-yW&7610BF3m}&@H|VTF(J|mxZE$Iv8?R*(vlyXl5u+_cwI} zze$tyd|S@_4P3-A$F?RWBw)c>_I$bu^AjAGtcAOvI$-tx9x@?tiRBGyhzH zJJOM*qBYzlPKbR*A{=lvvg38{FLoD+F~KrYAM_hvb56)2Fsa7N4Kwqlfx32gGCS2$ zN4<`D5nrK_zrU3%>dCJ@oF;gUTp;!W<8*X!;eSMl^J#@y&{!-ze@)u8NEweTB9ZYv z%5=7u%@Qs#zOZ#o%e;BHTW+9D&WC162WKI~?*-dgc*arUcn$OUUo2xJ7O;!=b$k97 z+yvk;LYUvHj(ay|!S4^ib0MBh?XGibS?H6DIC1=vS2SXOV1u+^SiBg4`>Ye-XUOkm z$_aqMLq@MJ&OI0F>dK7!$=n$hmFv%NY~8tJnpiyZoqBrfOl=`F$?T@y_+)I5K z&i@?6f71Z}tr`19jF4n7al&3zV+_CZ{YyBfgUIWGLAF3E=_j#pFmYJ~MyzF=lJZ`3Cutq7#N785@cu=NA4DVHOE=$5b>xP zO+!@?N&WIbfyc_*RKYKTl*|?Tmngn+sukFo&@|4kP>V-m-wwe8+p(>!5`UC-l^K@$ z{P!%c(?bVf&e+-6b94y&+lPiCzZ8H*MUcuB!q-&vK}Duc-v+uBl^|Atvu<D?JhqlH5C;j+R-mUgnBCiH2r+^zo2gPP{r>j1)(?fKZ0*lI;Z-Wl%%rW9_}ZhHwM*#V z{{DS*#}(u^_Vbdhp_It3*rYILGxQr!OUr!+hQ}u-%YBUscVoqs8KE%DZh-J$tKcVJ zv4g0cNLJgBJyXNWqc%HK3GydyBszr4TN@xhYOH!0N{mhu-(qZKg+2ci=6hV0(%hJ1 z&YeR08!jV4!^^9Qz974ETC&5I6dSmP8sd0{O5(mfxBcKw3)`Fq~d^US`n)&h-iCD3^iW@bwu}*tKAVzD!0Td$1jI%HhN|y= zAW4AeO2Uz!YVtj@sCW{0?w|+aJ=IZHN7Vp>21JO2eujh;MMYwso$c+3dS~lqA_4&` z?>CDoP+| zNWh5HyZvo*6Y8_2zBrYXUjzr2L4ZTx#E|+0Cr8BMMmn{vO%?|+c1bLWu5;R>xtXZ| zL(HHyIGOD?Rj?^11^KRBGkqc(c;}yT>E9HyC4=CVt;&TogzMhWV)=8K?jH1g0!I43 zfb~BSRHUa5o;d&;IzMmBnkMWDBe3Hq@f~K3EG%5W<9v4JbMM|gi_g)UU&ZeM>~cA@>o^l(8G z;yU!gze#OiCe;`|jVAp>n{|U@;_jrBAAt~NmhCpO%N3Bmbp2*I{`eRuJBO{HYMwQ$ zbT0Ztf9n<=nY9S=KNtK>sYLRVj|?%X_b%E=cUpMAF_xUIhnUG45n|T5!#1K`FDPZ8 z_3RO1eRn<1zA*GbPnQDOH)iUrxhu_ib-x&SZ6_SKtY=oN1J2`{_ggm?@GQL_dl*a*<|~u59X-?gW$l`$&;Ao*f$Yp(mQ_yV(X6tKrq-hbKe*nchcRZoLn_ zo)4WEHtq}$^M7wRjq>&W;Cv|L37qv%hv!Sg=v^`d-C0B@_vMWtHPK1rZsOsp1F>V5 zjkAmZmXPzGt>bMJXawiwkc2yNq#H8)<#b`=93k^j)HLq1@acZ%jDQQ(`pIGSRw0^t zR?F=J;Nt$+o}iEl#rT_ixR5T1!8Ex?rJD_@qSh{Yt`Syq#0b_~22R8gxc(?5Y2`%& z1NwUaS(Anm_qr?wUx^OCcORQ7=#4NlTar)1*{8yY6iepcCt7i}5tE8^X3wH`DtlW% zRg4#{ke_7Swee_Z^_~vvgYwJ2s@)fh>cj|nN|(mn6_z|**fg%n*x*Dsw72bI~{dKi1wm9_#-7AGY_%DC<;4 zWo1M-?HM9MqL7hU#A#*kEqi2UgpeXs_NIi45RyH!vt`}K*)=}b_j6s>_x|0F-`&60 z^{DfFpU3<4I*#Y@9N+fm#4@Bz;x=nu1%Put7qT?-v|r`3>8-R4?sFa+qr{J5uZ2nB z5N8tNl2!d#+V4Gs{Qc#$wBi9IEyKJ_0ZUL|TaJAFnpty4sIa;upa1ff?#<98M-_7& zdU@@eEZ-0Tvdm?^wm5*Zn*NTn?!kZwu8qL3Fqgvd`}0mlSJ9ZFuUyGT%cW$~;WJ^~ zY8_m+D>B?nSaMwoeO2r}E%xb=*I$od!TQjZSX4aXmF2v*;_bQqwnXp8Rzo{f@%`>z zUn&|K@9j_Cxwl3x>dkv>4ex&F8ZRX_P#>;XgC~_-Zc0vDOj=KxE#zo$IP>`2^o6C1 zSq+y#8Sa|tyz*{S`u_ILLfV(&`h)u>gR{hgV|zOXhWASK9xqkb?fsMjxADB7k7p5Z z)CC!M)vvu5d!^S^cl^b|xJ>8w3bd?mFlMvwjN^``=0-U_3Vz{@!igVt+}tvW%5A$Y zxXQ-#sScR7`aHn2HhWh!;4nuy`iL=*92fV;%#1q3H=|D^&O4zG3jF zaKn`eSyUz52KhEzYD;$v^q(Rmdo<@v1rmncaZY6stIWw}n0gDFr?!VUX4t(dz(IO7 z_&;5lke&U}xbASvrUv}-p=B^?;^E=xEFx(6^HTrgeENZ#`tICWr|Xw!Jt}oS?!lO=Gh~9z| z%Eo!>rRfX%-H3+x+0z|6uC3fWdNm_rHYM|&t;4f=O#-@{47)e;KGztreM&yLcQyMN zgI3*-d}uv{|@f{1wm;x8uHhBxIy#OU3n#bZ$yz*rNK90 zWwT2MPQO54`LVriCX^ozI@n6eT_?H_rSmL0C16s+j!UK)7(V*(W9Zh$_I4~Z6ILOj zMk>FkD99=yF)?oO-P9W@CrTF7?IhZWRWTw4;+s4?$xEy$@2%D8?nTj|PG=SoVJX^F zkRR$N(Gxxa>PONhu&9#mQiSyWk5}6h?#E}jwzKb=%TGja{g}dzjY|tH=d{zlJ0Yz` z&-!~a+H3FI%*!6WKoj54m>FCybtU&xvXv>2I?=_n=MN}jIwe4A@fs{K)Td8t(ECEH z5ac1s$jB%wE2CNZfk8oj5=bO%I~bh)2BMb6%JS*{LQ$74{f?qCT0ADAqKX4K()yFM zG;4En1zUS(=it!UnVINNnBO3;HM$T4e7JacnppUmg@un_ngOE|sERPUpa_$Ma_bxJ zHjuEguz>m;m;k^{!W94{$uAw~z_3*5NBNYLTuwIC*G~iT*=2|nfmC;Br&N>q18=`O zo#*ta1LouUx@YmawI-lQLRp!*~(qQ$UhT?jd*xT>6Q+A_(l?Q=$7i11F5r< z&vPRSvDf$_!!jaGpD@`&w^C0}*gSBVUv%`9yP-o<#evY(GsSVAySnEVrNs6hs1$3~ z?Fy=CI~bMg6Kz^MZf;{E%J--r1vH`d9F$n(%pHySd%8HSNrS(!XktY3+B ztIa;^WpQ!6=fR^F-9WIIw77eh5iA9bQK;w#5_VHK-x6Rb$2_C?d>01N9Mu^zFu4S7 zxcv6_)zzJi4Hi|@)pjz6=#Qq9p#h~BK(kuF!e^apQ!jwR>>}W%=SdiT}pi=aetD zQMZapeCyzo8=Gd{Xx~aN^B2!b9o5>5O_pI%X}w|F%NUqjS7%T}k||v<8f|#f-k$&d zRVIQz+G#Xvdk=*}HnaC%16i-RFRSDQuKJw!AEd6ZmM7huKt0;TIzZe{4h^OI3*32@ zco-5Nyg*f;!PBORTh)O1Du*Gb5o~qk$$+5^_NI=$&0zPL&%ieVyxr0P0%{!AzY-)5^?wm05p&pP3zw*gCLc}kWLCk? zpM3oho}Y4EDe4@uu|w=9g-xo^1@Wrg=7Jyx z#oIC#x75=txOXRfd>{9<-nteR$gHlq<#aln8HIS!&Jjxb^;SCJZ1X^6wz(X(yj~GW zwREK}x+36@OBInH<{yJF`qKS}3Ak4mqD@EH>T@m%7B&nGnMS?|zd51wG1^ap2^lMq z_KUF#axCUojP&&FU0r8F<<}inzg3!+U90+K^~gRrI2flP?+;wV{**xQmXLW{QE?ma zWytnrQm-SCEmBeFm3b0oJz6Dr{(Q^eAkAD{LIUoiiU9or4)A7to|lJKMZTa6HGL_8 zoSK?~2ycu)QU;+O$Py6{D7meF`!*8nLE-6v4tT%q6_80_A8C3~{(!0DI@17~4OsV0 zo|$P6JPV-nMCI?q$(~mQ1%&NzBC$F*2h?Y;NUPkjb-1RTpLo>IafjIeSL3*=k28BRS++6U|< ziIf0`^bb1HZ>oX6gZEGW!o|EG779y!d_^6;xsPX(ZX~zwG{Ew80$VogH2|AAtLN)g zuU%tSb@cVGiIjPoUkbYofQU`NG%bg4ihKZcFO9TV_``1gIeo&mW9PFMAjfraW#z(C z@F$0TAUF5v{S(08LSiC;(hE>;MOAPx{+*X4kdz~`a{BapJrSR)kIxV){KmtyLGv&< z2}3}2@%_HB@nvv|_<5TM^&KYQcEz#BlL1BQ6$-IRIrtMXDK+v^I-lifkZS$cGSm3& zpS8Q)r<9wp^{UG6LPj zKLNlp#=WK{8umELN|l@}7h@Kwq^!tjo3p8gnup;E_hwulJb`=-uWXATtD$;yDIC=*2OO421A25=!swzscGkUaZl(ccw@tYAtF zA*&+`I}!ulZ`KP0yG8Km@a8rq0b)kjeq-jEIX0HeAGC?z6(WBlyP-i_N(vPz;wJ=% zN(Zi{mLEC(ufVJmaJVl*eZ#+fPoHJF68Bx3EJ}HKCI(HY{Zi)EKVizYA}VnrM1L|P z#2c8>M1>Hvj?h70vq52eZ+mgTe)b(Bm~`+B{La)o1(Cl~!;Ou;h(XyG@)Lag{4DeS z>Rut@cH4`$l0E}o+*bQ7pK$~T1(%aZ5 zqZz%)hT`j5w!D8ohO2xYX+J}7r?CVq9AASu1fbh7&jFLJw5K6iH@CD@vcXKdq+b!u zjKYwWlM4+bKmc{&TN8kc1@6oiAM)8z(r-&jVzi7e&TuEeD&{pTP&*VKU7Zbx`_vDx z5S+$&OPf(#<8t4vcrdf!D4w*j7|8o}8>St?VdE_?K&*Ewj2f{tFIapS8s;E-ewB&+ zk2+Tm^ey0`nzwoGpsOxe&A?B>?~Mw`&CmQjk==nvG%-r_wEb0>t^cn zVfFbZCnYCSuvzaRua8_(S+Tjac%O63AolFC^^IjmU+hz>bEr)I>f*|+7TNE~L0IVK zf$Ezu9C4X5-wTk6XE3_4wII(+VOLgs^^elYBFc6Up3z<@GH`k@|6|a;a7WrzL0$qL zC2R#GCFOnpcFt}p5g>Ne%wPP#$b#^Yu`vL$3C!RPDE09}*!US`!j9VSrj}NVbp^x1 z()Xgci$BFUADu3F`rwh>$8UfHhEIqsVy}TIj*kd1Vq-Wp93VL)&*d?AKK?-p3SQj0 zWl8eTrReZM-_@IndPCJSk#Q_I_p_X|%3 zm3vPLIPUQDs!ZiDo)t)xFw0s%#J_9jc%ade=7h&ne_VO6{H7*)OS>Yvat(UI)_Vw=y8beJq%NwloFNwQ#iETvY zPsNRxGpe6{QiA6pX~Bir(WgDkcNs;=HmZa}mL!e(%85d9s}CexYWv{JjzVLpw&4P6 zGvQ^6rtT(sj(`zBjfqRETU~}Ik?Uh^KFfo@pwlezpdBEdz%IA{0f8XXj^p_y-Yy=_ ziZOknKq}(g=Rvz>iXj@h-aEO|<@%MMfk8f+Oi|r~% zH2+GhXz-=v-CmEH&4t2?tGSpQ+#^vnn#18(ZIypxUCMyXz>CZ~MchqQlxhPTgO zxUe!_?~OvC2_c_S1ZUbPao9XOJeZV$Dc>1XE#p_*QMA!%rFsFd=ex;N7FL(%#CUf@ zAlHYKoc!hoY~pyf-he<3gJ2mZ+CSe2Q93L|lMe~~v9Hu!8r7;+)*rTQj+!LO_`Y;g z;u@{9o}Rr18_?*j0d-Wwozm%{!tN2}OrzCXWKY637>NhdQ*)@18!vWycoz%eHQF9L zK0kbBJm++p&i$3;$6XKoWleW}Mji^6ZvhH!aL%{{SCm}E2%1@33{<8UH%0cnPyi+vc0I3FY8fgn7Ez#D|vJPJyD5+1B*aygP4J=pNj5 z_5wgpiF;U~-f|{w8)~ADJ52{uMeChxB|&nn+ffnbXXPS-0IU*eDn}nI$BSx14wmBr z)H&4mngqT`dpi`{C%r1s5b;&*mrH#&IYDSO>TkLX;>UYr01>2P%T2g68W5E^o#9QnQtCJD zDt3P+&8b^8kpMx&AXa4BrSpxEQUp-FPye-eCnG{AgQ5cBiCkX)wa$W_W&6G~^Qt`9 z;jyW%aA|JM<1Wj1*KY8|&UN^GN9BOKfw*IbiTQL+FMNd5@CU79XH3Z>a~XKK#~m*e zyLCVJx8Hb9s5gOrL+7}4XkOJv8&6>9+ClL+(30s7$=Pdr=ew6W*yY}CzU9EZQ;*FNd)Q=jh0bh=Z z&6S1mIv-@IX}G?>D9rxQ>1EE-ugNS%UzA~O#NHYIW&}r@Lrvpay+c_0=DW}9oo^?T zt(Ik%8oJNVa&RtVTV_1vAxs{nj~g_fG7$Q5i)H?0#+lo=xLozhrF8g}&jN4Yo+4_Y zd|KVu?u|*EDL|6kA{d!wj*w?G%X_}nbgX9PC*7t-6aAfaF zaIIOgs)(64OEiy#Zg0uD_PA@qadPGD;zjj5Q6!5ESYYSbu5+5Di zU=5V^s{m)NLzdUc6`myQQ;S$~o;pw!zbTsk_OE~CMGb`_Hcg2^Qigas8rnKE)Gvj! zf25|d`mKG-I@!=DsicI^o$1#C^|q^GcL?vhtuU5b^ZkoSJ0o73?;=-oeQIxGsTy~5 z@q?Th6BE<^!VngI^)=1}*|*u*kUHe;IBpn|n27HQe%7DRP+Lh70her6^ss{eZ_s9D z>f^`6Wu(3?GqC;6eDh}OBU{Fy_jD7eAR~mwGNZnhnsw0A3)y^g0ge;IYXptW8&5(C zU?4~B;YLSiP%!DX>=6>SjlXHS|K73x7iW{{eNRYo3k$~!mcWK+_MO}4rTLBZ_##mM zt}x$`c>&>kH%FVF=@z@6E;14NCNRYL{z;*c8~v zXeiPiQkU+Uk61z!jchr@lkqW}3Q+`&3pxO|{$O+sw7!KO6o7qrehgaNEnkQ!T?Quq z$O&Det=A^Q!e$dOucfU0ak2Li@x{Keu`X+ue08z@C;_;9e^a^tZQ26N)Jw)#z6nNk zk-*KY#d8S4S=gq>aB!GK<)X1GObth`o+i1=E9L%+WL+V=X9xmewLW{{OzRmna7>A>pfYm#PzHOnOc2_k;!{pN_Cl%ynN7QdggC8PSp9wfA4(+TE$>BOUF60q2H z-5Iuku^nLp2)Y zhADCWi^6L1_%h=5;pXq?VTI|zEnCo@hrv6?f&%gohe_KMRTZ8>&oB?a&K(K0_f=-x zxlvBtR&=wvgjQB)-!iIP3_c{1AX(~uSY%`n=YYJl7KjQV0GE6g%z&p_AhHUYEv*dT zvh(utzMh|%k=_Oe^_hz+pkhrcf zvjAYR^77sJ4DZ$D16QpQ}SBOraSq8-xQ>JC<`2MxU$!5ADIwUoDKwyw*p zs7rPIFDJ{T|58pz<|mhBii#9Zq8S?XSdVxUY)jLy0^< zbW`_}ZBPH{jPbYG3uuw}cT!?fZW&oy^DF8q&>)|WgaNG4U&sY6653z-hR-g*3-h}o zJ4~PP)c?68JNf#PjEwP~9%)l72Zv3t?Lr$I#Jsxl51MQ=AK5-PA9K{h(-V?NiM>A& zD-a`ECP5qmhHzPnv$Izbp8-)he;$M*MKy1XNPPWABYdt?9zV#tM9{(t;ZFTsRT-W; zca+vzmE{!_swykt7dYH5_GScH)E6k@Be-D#ii@I}={CMLn z{kd})GzKo1MDi0pyyzP^t;pt(p9LjNb=XWg0Q;o;cW>TE??dSMVe`3Tj1!jd>Ew;< zNBQS00C9kw@xP!fot@kv`Nq8V{F>HP8Zw0FCB=OQqO6p(^!antF|o|I;w@B$)PDPE zZcbzh(Op;)L&UY?hU;hr$Ywm-Ns8IMDVvy{d?w!h11l+1Ee(%8(;@~g5RxESO=KlL z4h&~M=ln7l04A?~C0@YBIcnwzDM#N-!qN-Jwa* z6ZoP1M3T0K4PHLy&byvMKF1pcuTD2o<(2%kih?e+d;9OqS#ql z`!;AnT1Q8pzRZc{!i@x=g&lSYp^n*wpyIu(jcPd84tD@c48bwK4Uh=d*4PFq!_Oc? zn&H~uU-TkCw4gD`c0XzHSZQCmm6xfxuP;xOKuIxuvC)L5KU@-p?cN zU^WcSp&w@De0uib!oq?USX}UXj8X(nq3af%;HM=_Yh`OoE_?t7llcBjoMX8U_yV8u z%?8k#UKW%Tkb1{90B+RzvAbqo8a86FPo4-xTC_)f&GJe^JB3`?DKI{K|w7q4sez3!(lNKM13lzFTHMl3$1KCrONeMOTC2I)5@rd%r6?WIn=3 z8EgNrjOR7?YLb^z63Hn;&d12h3H-UW8&% zAlX;^Z%*u)S$D@@{q2_gpM1{$*o$zX_>`oCaFfLKWaT5${KUR0HoX-2KUK(w3&=xG zX2=;7#C$%Zz-TirS)b=E*xBCJP@Tz-w@V>oFDC%;3tOMnFQ~QoKTs>?5_JM#sAY^B z>?pVZebAZqIm$L)fago*3L z0mD`1U!84IG;Ce|?$ndbiGrmH{n@8TMcs?sMP0G+fePeb{d&KkdINFOP#|Et1PcYy%0!v`| z_Ox`4e8$BO;2L)QKXgtY+*1}@{N;c}hf5arON@X0VqC{8w7oC52++zn=4chL$cuhU z3o@DEYuw;boenF@pUV^87vDyieo%r4JAOD6B=H(o^$rq)k z8{EE4`-&Lr#{vc}nIBx6-=(JgL@=^=`1+lJV)Ykmm>HRghG`X7w)r2qf4DE~`OvtPt2J!PEfsm?TpDo&n)-11XX#8I3We^m^T|x56k1oRUeS&gEMS3=Wpp*F(l~ zM@vh<6*_u){!3AO`8V=m6ShzAjl2ZC|AlG9k@(L~Sn#f)#F)01Tcdy$j6fI1~#?blIXc2fs7b#;iBE75nncxaF_J!?Ov>L-ONbMXCau?-)fy!p*Tq`5gZ z@fP|`paQi-%QQBeA~Q#4kbV}$`*Q~w^+(5vKK%pB+FjI7`}U%o>#eP&ns@lfG$veH z{#B_|*)T~%t?-6K8EoN7nQzeu+nSj4E$m%kXL~;P`orgIHw0V)u3z+D&VT+qNQ>Ff z569lpGUT(^`;Ye9TU#*W!Zsg_KNB!zE5Q&8q>Wl&x}7NsSEq`@!t6p5nEu*sUH_33;Z1TTl)Iq^hXB<5;mwPDX~1Krl+r+o+G*C?d=VSObB!G z^;@@YLHMZjzGN#LLQVihZ=ae3^P&oN z`KT5|W^wV2Ide0!3+l(_StQ-(=RIhpo`q|-- z|8l%PrUT63qh8`-bq>+InDy*#FAU<>n>`p>a{ONm>i1VcfM0&{#XXD8>e{(?F+f!k zbzn$?IvT9JRoxa1b{DpFo2@kChr68v!Y}=(6a5!U9v2^P))vnTEoT?;IPwHn?}1Cq z*vU-wT;yi=*F=;7&lLwoeY3vwF>=YHW5(87Rb=Uv+pU8ql|L4PBd5%ICYJ~5<=Bfz zPly#5tJjqu&2|C9zBSMAaOlmCZ?`vHS6+4RWYmbb9C*@}9t^EJkmbgGAnngq{WnQ_ zDlVBy;eFTA_HRvL%ZK*L6FwR0T`;cv91=su*UgLP%Ug^c9k1v={&Ls%2QFFL&!FJof-V4(f^Zo47Xf#W zP5}Gt?}+$=g9GNmfUQy>HY}3o7Zs;e(6WVb_a51U-1Pw1g+ShItE z_&VM*MWXXZbBElhu8a?c5~ibxJa?Z3rAc|Gw<(-@c<;y7R=@*jP2wkan?&H$A@VtY zZk4RmQ~9#7-oot~m&>EvZ}nxjlYrd9;@y*>Kx=9JaxWjSmfp4U>gv?bjL37#XmKIC zj%2rqtC5E+hp)eQCk{9_o}JhAyDUjzB+SRRu)LhJjDg+|a$~`9rRn5rqOi5m@=PsE zEUI3+&n@zW{pyFTCDD`VZf~$6F*2CWf(R>_g=c%jbd(#s_4{kjYikWcxvC<`V&F7W z+V6EPbXlq`*0!l*Lh%T1796kltqHHL+J)Y&+iq||SkE88_cY z@I>nBpzzIh?Q<>8d7?5n?#KVlMg3<=`Ph*?+UZ8wcRHe6iu|K~s!r|FiN#uV^NY66 zTi)Ge-KEvu%aH=$JKnuYEmNwbZXcVeAD!876_=XTk4Yqy;_78%oSa)NmoD}QS>%>b zP~haa$=>L8Oz9F4>-)Yt`m-kQv8WTT&ZTvOUw*oIb)rs-{myDm2X*C()XTMfo6ZB_ zFSy-pWL+KKcz7GQ9>tC6Nct#bs;F}dc?KWLON?qg=ps+*oZwSvC$fEF(aMo((R{63 zL9z4t@PtwdnE2P+^KiNTz?|YT(&G>-a8UCJSX4L8(URMGWSY#hX*d!G+)K`)mhSeU z#-Wotn{pfa_VHas`@5Vp1DDvVV47SRDc|&~1fN*2UJUx&2AK;VKHzsiM+GB1x_{Wn zk_(o$gfJJfc8EDjYn#BhM&b_q#$KBW_g$X+b|AeOU;-PIY5uhow9cSB2Zq}R>}^^E zP~h`trI%%%LaMqP9notmKsD6SDc|wnW|^h0r`JJvgJsTc6uDXfhFHtNdSR z7!bx6+_MmMGi^!v!&KT=WbVA(i>|O!J5?Kvk6eR0Bhz-BM$8XWd zNlDw;)Y96zyQRQokvCj0JcY;Kf0bN9hXTOiKqFJroOA*fu*R6;qk~?+s6A_q>g?HL zC6O#rGRr(bbIfVF;vf@l=$4^jH}NzX6-E(#J3bj=;*BJ|M9IJdCy3+OVZMBsBDSCq z_amb+30Vcr86N!*6H>YCd40Fl5HOH6NZNmGb@X#vJ%bKGLUIwu9JQs=Od3gdK`~O_ z<0=jL(-SIBrT3-ThMou6H(Qhw+Xlb_!sO>?)|82jY=}Z^(v}vvoOkv@m9_deYS*i{ z^uD!U8aslBJ3?gXSW0Dvb+iJ%d^Yk!Hfjb`3(E}`J!g*!UwPB<&>~V7&q>~p z*$b}sdUG_veTi*$h49)Ki@WF*WDnh)l9eX>49NyLHADCPjaNEB#CVF+xd*g|%N=Vp z55=)@ElDF&`0~sv^nVrZEBDkajQP)LW*=j`sJ5wZ7Klh0@%uN^a&Xn$Nk5%uoleR& zO3!#%;Eq2lpQVS|kFY!UfiL>#`!I26EeaIqNzbg>5 z^2>3A(+S{hT?aW{sP)%3ge;lRjV87;3ft%dubDtLpJapZ)|2dPR?9M2Cz!D2C$6)3 z>hogD8-e$3+f_#*Fr!7jYQ$uT6X*1XM$F)pJ2+%G%yr6qOi4%x#7Yv+ILF8sF+X5} zHW2l3>+I-Ixp5=~Qi4b#pv5vq2k_4N{OgM%yhiyuL_Z%oV}WIge=K6!~Bi3C;# z@*z1;&1G2#?pmRc`&MKAD6&w0`zE!sT#rd3*IR=HpTB=5r)FY;@NBNhJLA&w{4xC( zj4~XSNB#L4Y1%`ZevMfc)C9uHsMC{+rix=MH^7l8~9?yj(YT)lMsQ6-sloa%}0mLJDMGwkv|-wTDJjjHIP) z`QAngetu>PR;DxwjpMkuD&G}as5?k&U4-aV!g+>+GmP@%F6wsyVb8%4Ny}Sr;^&GETo*ce zh7#j1T_tDB4O-n9Ml2E#(GsI3^oY`db+x=Q<(AazW|~N(R`S- zME3E6?IO~^7T`U65tY}>6f<5?2^`fSaWFo}8+Ze6)gI1=*$#3;A!gdlMq?YB`zE$a zi?f0v79PDeP7i5Xnw14@c|v^eKwd(tYRRAH!M)%Rs&G6H?t2i613-t1OFbSwoCTI# znYXgI{J^|3h$Mdig57kcO^AEs5|p=hoX2noRMz1I zr=M|@Xrxe}L;Uvk>o|SxJ10riLPCaz*M}v5{5d;8hX5P3qnSXnz{nbRX5uU^MWqVt1rD|ZxVB;AJ z{+U;_ekz7knIv*>e1B>=LaJtSQg~(ege;Fi*mDU66b!b9-(46*zqqYMOp203Cb_;< z&#FAKEw3K)fF|x6TTzU})0T-n^@Ff59)A-%Lw~t@XJhA!;Iw z7TRrlJ3GvhM1VoPj@*ceiCK^`FYLZvz;>y{!*AD7??*)dBe>=CnUF@`s4Z6MyC$z; zx~4fR2k>i2Sy{6a>6ce}_|?*N8%;kcqD2=NbeSztE-o%cVoVz!tA&`#TqgxFzny<= zwXJ!~+I%RFaQ*kj;`!5Y2&`LM zReL(!6nt0fr{s#Y3yrB91$irIi;9X+ENQ+yuCxoFMEcgMag;UBiDekpNONT&1j&so zH%>_=ZYcD*zxk_dUYC|OIiv2G2sQ_h_e=AOj8ZW?dN^E)`v@lQ$=1aOGK2I8#hTu}B-^@UD315vEnw9hddVDb|k2E>719YAe_6tA{qLN?CD5e%EAn z@kJ55HFq|>>vGi}T2IYQ9$i@Ec&_Q-)nJMjd|6Z5rA(YlZCIQ^tx8bzo_PvhaKN4{ z5p-F|huU~`X;|T@;!h>zW#Lpgl|Kfkxt#zrv+p2}MTm65$iRS;PC!Tq!Qlm1Y3u^8 zIlsWAmwa@uADc-)AnwXlN!8C^z65n3Afdv+9xoWYI?@7Z(qA zYb-xnM9(=pDH#vHuZqUsdV76w4Ypq6AwrhyU^#tChlPm=!|<+e{ob^QKC1zJoqZFr z)Lu5u>{~p1CDlU|;miY&>kLspFe**M_YF*VTlLPpbWSiXy`g^qj~<~;_VH_w;??3x zpBt!vnFnVcw3b_=oUS#1jPkYZ)TK!sEyoC~QE%Mfvjbwh;yQk~u`C|hIJ(Vgo4U{@ zN0}DcH|1Gm@$2K-Nt~y!(}i?{l19L@9EBkbT5y;B*J-o?RqtxN1K=cCo;m<+dCvE; zo$*|K9Dol8Rb?N%cp2&VRHd7df}TDskz6MN_b(TYHL8`V# z9pb&xoykudFOg=O*2wQ)7D9$^K0tLUed=nvnVvVA`oALa%IeQ4q^aeQ%53Q}y z4$W@SY1*^O7@{DfB9o`kzIdaT?c&8LjlCCGmgn`6=zd8=C~4W;DaA)YV4&W|LT3h=I+F@PyM;L~I6$;f?6%7cg*Ysd&UJH*hZ~_#8e6xDEV$ZBMTInjg1;d zF`c*64*1DjO+q4-K0nhen9zrAaT7>SfI`s`ThNU8v`0hBE3Y6{QYC>LJIoR z&haHT8i&W99=p^Xc2lT3D9$D#GD+ez-iPU%mjtK-3JbXwB%)D!o06F|H3%H?m<4QX z9-&I_6R+=Ntp30_lx)QOtW&jC;%i8bY0NQtc@HBQ4Y5SGfFnFIlPNYg0T16_cq_J& zC=reEAZdPZEVSJuyt>-s>x-VO-jkpwE2ym$qJ)%9?27`(@R-u3)Y&_B?Q_GZ&#!tZ z1k6zc@yevL>gwtkZUxVu<9S242nuvB-_NPM}#nK_dpUeJBQyWf)z{=H>6Y|*6zfg#@Usil|1y-~ zKbap7H;AtOskPouy2YdL$0`4wgv?j7pb?yC_}=`}uOm`{7p%C|-je2av=z>gNAB?+ zCfx0rJ2WRFBA&NaB3Nk*MLdpgWL;zUPSpM98AT_6#8J)-`kcgX7Ut6T=~DzZ^AF_u z*4FvQ@A%n?(+VHF{o3BHjL2iXaXGz>u^ru%nczWFc0mA1s~U(t_#9BAk@0?>nMKG zkaw|QYRWxGzXG7g5HM@f(k_iM;}df&1u#lTE1skK_+V*q8bRNktf%BG?#|>O2sB+# zB)~}v?Q2EH*RQ&iu3?w2IOhW=Kw8=d+4L`u1+(Xob?^At&zIX{R_>AYTVJXTS4Dc2 z2QNqG6_42>Jrg4z@4ul(z&AI7A1K0I&wDN5@mYLpGUrcBr_U6L)J>~z=F*((ee%zE zbk>Way|+xbZ$-)SoEFLUhl?-IUd-7g_uj`Kldn<)y^OQf%=P_w`2_?N;d2c-gvqzs zQli9bmKJLm)z%MJnpjQd&8f}jc#{R{nJ%#eN$R89gEPS7%%laY=O2T%?(PR*Eej_J zNM;)^J_()^%Z>_zM!~=*H`h#8mwsy#Jzy%!!JaQz7IQCI6OxADR$YjdXJL71&FC2$ z=JP}-rTvWdm$;>448-@sSZzV!sKN*ScTJv|t2lEIT|22{lh|_fuBdYWe|_q9=-^Cw z8C|dUcv@Op2cQLduWhg5W@;Bs%A-#*M(Xp<3rvmQz8&tYj6`eNMgPNbIX3~1&oO0} zEAPrtfasJQf8z4VVd0uLx6+S=JEV@ZqnFVA`;&e!8+EyV+fA zQ1!!f%1_s$J=<|KH1p%>;STcCFGbs3=SkuOkPF#kP^qzkA6mJwv!1pxq?pgF{j{Cm zGPa-cc>UXZCF>}>-blKI)Z)-%QW^}fI-F~~QBS|UX~I8cHD6=jUjCGhs*+?XIwKjk zT4?a@7g`olUeTaSIen?Ie;lNgG?j;r2y3=rE(^8_dsA_F^@h}-@c7Nh)}XZ_;4mA4YzJUDsSx;rFB<^ z)XBU_INm*fSZHArL0odIHa#a;ff`!?clomcHZ8`)Z zk5l55&cNyd!^P1o_Jw1q;jFv^*5pEfT6t^Mv9y>(Ga|(=E=U?3Jf`7Y)ziR4hvVsv z)wq^9;sbyv>M~SrYk2Ki=)b=SHP85kjJFfV4`1gVFShk$jOV$``xhH}ZpKvwJrN$; zKWw9m$RB&Nw82&ES!4X66})U7v_7F>+JJYa=I491>ZosSD{C2y5+j$dcTHqQxvgc} z8h9UUjw;$V8d|%W9~Y5JxxOx(Y?V6RZhjh^8l0Tc$oxn+nppeF=YeQSfB(826AQ7f zaTix>*;bhIPDy$;SAE@7uQxv-J(##BTFcfoKH^?;>m6gP91;hwOWaR6x#SC}QU3E+$)@M_hY3a4mHZtfD{uJbm5vlM56Y64_M*xz=i3P-U^v(ub8qn2`oPb3P$R=n#NBdm)O+IEJlX}Qn-u+SXrC9yUAGzKoLUFR0Lkg{TWB}h@M&yLL+VU z^$~4GA=0UP(_r*M%67;Znc9Oq3uFU73v!4MT9H>&lzR&99^V2an}L|*0=9KCr^L!U z_BRNrE~1)jCm_VhH^lTZnc|xI$uL)M?|R1FIqFl_NXW|9O{0eP4G2ZZ#-^1#Z#X@< zp&2u}5Pa?Rn-b^L=qU+7TgMp^6+L`SRjdkATEPmOic>XLVo`2rG<&&6F6gGF=0r}k zM5jnYdpps=+|*PPAuF+2Qd}vz*K2@~cP338ojf)cOj8qIAG>LXXqHVJ@#Xjgqwx6 z_u83Cp`as1yy-5|B3!u`GSy4x_6!Y9T$0inWzKDnDUM&d?S?@VH$Q09v3s($y74KG ziBiISbKcPNWTZFwqA99p70#4AWO!BO6!^4CxC>@PpgD?yDGpbI@04mlqA0@(NzQn9Zp%bc&0{t@m_u$$ zJz9Dgu}hJ4t+@7JXo#CPMzmz)E22iWHG18Oa%TX2oSybK1BL_; z!(sH2-tWnhV_F290e?0ngqP@nOi`t(tf2INJ)_)-(!YC+g^vg76AGk zs`fVKId&v42>&nkbbB%9HZbNToEE9KNRRJ*fKXJY2tVtS1bR2fV?&cNA!QaFc?w?A zbAcs68hvJ{9yFje1vA$vh~w+0k_iY3l0{sUQ&S_mF0$p>)ZBcIgY8Wt=8BfKcGGf` zD|}(BYS8Ss8h;b2Yo43J8)UjR(ofD4HXYyHehAV)G3T zL~8iV)z75ld5UQ5Vxg_qmEEpD0?FbMyTJ?1q)=ZQIKiT|KLP!|vitF|4Q_9YD0W+p{VXBoq$G(9Gq(SJ0S|waY_8|CFnKIO z*p@-whzXkUEU2;GlA8jXiRYhp5=VtUjY&z!zGG)nkewZucthIIznOxRjQqyADhhN% zN@Vgr98&_MYicZjYWoqeOH=md+JA^(NK8v3Ng?9&*OJ?xSy&+WyZ{o$AtgwpE?xl5f!0XP3gx5LoT!KaJk1&0PfwDQMZhd3{$=I|b&RiYG$+99PWApyxeyWSvPF-#2qlsBbBSqcb>-YV)9oz0(hiBU+?e=3u4LNEw(;GdJ z{uo6s7}iJRZor4!3J2^eH%L-Q=uA6U@-**WQiwVmVSwSNkcfsr&}gWQtAUl&_b{f5 z*OMBEtG z#w_$w_eJ4m%f$N5PS68z_MQmDx9F+NFD#sX>+D8cb4k$|T zK6H4y*6&QH&a~MPf6vZb9~72)Wo79KUE3r*Vv}a=Wb5(T4#K9&`$pWL2#VKy2_9>a&Ab>^+Y{Z&ao74R^;nfdPBleNP8P6gjGGc#da ziI>9xuqbWEXUAIm?DIU&`Mj_Hu8*!c=RNK*uJKK?LU=CcPGf*ItdUTF4q~F#ls>GT zK&KV=6`?rH;c3Uq8Q?7KgXVpkYh z9WEmtnZ+Hr)AEQRbv#LZAy@(dS+5aQYme8l1)yRPL|~o{1_lP2nvo?XHi>sN9iq-7 z?*6`UY%vpQp9QH{N5VOI-_rz4=qiP7+h2fKX+6oWBMR{cfRIe3tE}Yqe6+N;=7Z$3 zZRSJ=v|m37;mh+jITDw@E|Zhnn0t=LBWmTt>q?7H30zLB@a z8*1X_owD|&JOPWV+&D=2l!Waj889v)z%DvHMPXj0X~D64kIWcneJG@Af#!Rt(&Fbb zVcM80YOAHG3(fft>t4JN2x~CV+c|ZenJJ;vY3fF&u&m-AAX=Xo0)&69s!bR!8Kf58 zT6ciyf6H;xnv_gcm6vIqky%I?_>#liim9u}$#WmUJ%$|^T=`^?)7jbCEar6pKx#B$ z={8uUXIVhSD=5*;5IkI=i}H)@$5VWH(J+dDyuvj@T;wb*cbGfbBD0 z2n3%PfL`?$H~#AkQ63QBucMMoGItZ)3VBx`s!zbkd{HN;jdyi8;NyIV0TnW?zGVpeEQzrsn_G@!B5(@+; zvP`{bnV3J0d2;`;{NuzCda;ZPSP$G)qSh{*8a+kH5*_>FJtH+vPO@<9@7LG=o-5)D z{}Zl=*#AG}it3gBGp?vE=@(ZNR#ql{zXHd#3}Ai-B5`4(E>wL(#|_gFS)uFWLm<-NxfG;vFQ&lG*}Z;-A{F&;iLmETTFMbZ7!#@2GL`d4fGu&~11 z!QtT$)#grP?bPcPGWQhU@}G~F)Bi>1H8(d`s|Ah>4wk-q7q`tWmEZA{ou41bBPZ|$ z7$(VFdY+j0Ky@mYi2WUM3L`iqV1d7p zM{|0AMjl}lxD#TW^#jqA_BNEw{y*i8wpjQ_ykHiV%>)h4Zwdr@28O4cf5OY?N+4)n zooVignN-4W2Bj z`!e%@H)T~t5+d8-%HGor*5jqx)=&VpL8<{z6)Gs`xRb(&Eu0ppk=JKnF4rj)!h-U4U*^@Ku4wuGdc`6s~jNTul^ZWo7O4i;jfv z0s7yc#;nTN@H1CbLVeH_CmAgExG(fLBn zW#a#BK8i0A#+e6_W22*kR8-N}P>4`$D}XAlfB!~57lPQDTrO;_#cs6eE49` zk7SbEkhv;nOVm+aR(21@W1s|C;QCvI175kyyRvsq0RO5gi0d<|R3-!3fKb{yAWH81 zJa-i4K|XE>pf~i_L!SgP0mwRT_($$wL_3dH5B2xMHiw&H$eO(RNRA(@ zC!UyK_ykm;ryC^pY67IEn3SS}3fLS~-qm@vfAA{NC>7 z&2XA4J=(51tv-`u+$eq()5ng`XshTr8#!%k;=OUVgf0 zyVot`M)ZIiFU;7pSasRAFqj#C)#YVv_Jt3;uiKoq8o3RJ2dgyI*3*(DFSv$gPVe7)_Glh_oF6KG73HRTzLJEy7~Hz^Jo;lI<6`I zK!5)>q*wDDW55~G_evAB2L&k>n=ptU=mz;lc$=-18khY!#}AvBsvQE>r;m z^FKVBPTM1MsBedxYqbmU6BCir8F)_Pwf=?=KmKQYn8x&<@!@;_AwCQiT3bn=1?co{ z0UKS20d78cv6rvH+pMnM4#`F93bJ@%Ait?(Vi(Nl0=DWC!_|3(>+AH|;KaBlTaD^IsbO{v~*!%p3WP?;Xdy$F_)ckc$o zPu@YhML0e|_l!3O@faYAhd}FOx)w$i%>=aKZdh9G1CS&s!Az%f6$d0{L@tRWKv)US z^-u!C=l?Rb2M@ng*T{&lgm?kUNQsu;6c#RoxP%d67?93kh?$ zVj@BN=l-BsG9EltU0v!&;ujcU>x*$SZG0&}UO7E{aOc0~wYf}2=1i+E^Z8Q7K3n4% zAgbMO`0&lWPON6JXl{__uBM90t-sPm!P!E|A}ucJuWs2;xI-)x`CT*xxBAn%4RDD*^qS80rQA+Gah|A+j6U2P&X&@?Dr8(vVmw%M6T9%R z(*(fP_WXhRJ3gLZrlnF`yn$+LH{WCpc2i@1LhZg`Xao=VoLruWI4z4SBh$VS7NjR5_}*qt?n*Xk@2ar-Ij*gn?t z+B}zUfjc1a8FDX@bu{9-_%(S2pT{wi{L|Ivd)ueV5>#={l@Z;jboe0kZSvM#7DCix ziAIj&Z&fkl&3m8!jVA`%*4{o07kEUYkPo0r*u%w%tGPNlxwgBzu%CV(C{e`oXNx^G{8OH#$UUG6jW~rBEeJ-8|#2B5BAC2xF zv^5*h=E>mBP0sT3MQgkc+&Gxi)Rw!uay9Uwar5>VZ^0pmNi65{ou~}dx<;@S-{tfx zs;9^wY_Z)u!x&+`-Xi*TNt!$RH2Tq;>J-HXg`N7g61BgO=$-gZq^0-QDMb|EG19Ad zpXoefCL+Q8=(E@KKLMUBA(c-C-kAI);(6+y5YHCktCLpZ$|PiY;Ld}T94zlsvwQ#m zV1TlxdM#HxjSO}1{>*$3F0sYt$0X5I{XY2qlhn*75cE*+{}}e9=;dpGus`s{M1<|y zBYWIV1(pXHhWAkh15RYEkPqQoHH>Vtfvj)z-l%pw1TO?L0dLUB_y-{+7o?@VIQ|Rw zO#enmCWa6c6fAO8QVJ@#+4~gV)J2AJdPodmkq%bHqmFvv#J0}PkhT4MyK8cqh zUZx?Uzdxuz9D&SR&(DQ&91*LQMaI*nUOlH;sX5Y&P&4aKd9hKDBh`7L^CT5RgiD$X zdZ)`;dvDJldh~R$=gEzV2xVRHyTkP)14{~rH0txEFdogUvv)kf8wSd-kbd{W;Cf$XpdQcC^^%U;T4FTV&HNqha-s_Pb2)&!g6f+C%h! z{qs7k*hH^xsj>Qry){ukvE9LMn)HklDuC2}ZQOIo4X7kAz zjj6D$6;E|VO>MI50|Te_94P+APH}K@V)?7%ow>Kr#qwFJdNIt&_bX-$G^fBq3Xju6WKar}#Zncm*=Bu+DiK zVv{v(BfyySKmdwA0tyT2%@1Um3nC=zW5uoO7=aola8Yl`MgLBaf%mcv#TJ2_Ga$^d>hDy{`#2ZcZ`W?G-zH%0Qe zlgrM`LyD7&IX(A(ttR)hMRj9uEFw2rwm5p`r4Qog(hQ?avF2VLpk^t=5RB)>3Ug{m z1aIVey=y*ew+&LDy6m^IcZcIM7pi#h!ukh!3hOqT%WFDQ+s;wof0{JBcJJoFBgb8{ z`wJ=~<{1s~vXbV0GkrNyBe!YU_BOt;QMv7=qu+XUclIn98(%OBJD)1++O;z<0sLp5 z>5y_0UDdtSPRWVG^X^AcPa6DsMqoKFoRh`sZ_%81m27kf%kHTYLQ}QrtS}kgzgbHG zGkVwD&Jz_!#An}5(9+?RM#%V@;@%;_Q-#W%b2^i-bHM0lEvu%L>&o*WJREwxN25X*mbFI>}e2hPFMvJ zZRmrZWt(PC%fJSmdj&D~E?iR;`#tX7O;-)T!&lu_J#QfO{R3h)lH&rWdc@_dAUE{G z^iHeRsOF|7i_x`IdRwHSvGF<6N}2+i0y>)yZ?5M)z|FL`1Y`_78~hX42&DV(3o!M< zJ)$7wTcn4@!ZyjT^+zp?LIvK8NpBOg`t6Xityo)-&EEU*A?52;IbV(t12$fm2@`>ng@`J1Tz+YIbz;IDJx!n5~_o|cMn$-Rq6QIDwi-sA!UGP7qkIlK5z;`;jK^Yt=bW z7pF3F?uTDSC&y77$WQc6MLQz(zWc0q7y6NeT&>$H$eP&(BYCVS(Jr{B=Zw4k*V>g) zGoMc#ZD{z%%3k!X1P!a@q1GL?SJtaem81JBVG|%*%^p0@a+6u_-{ZW$kb9iJM(&k9 zonm7}VSb53!0RXCCtHsIG6}P|r@~l%jX;fX! z_E5{%0MBh+ML1raR!1aP559w6R(t1~vu9V;OLG)T4u|MC*N-#$XAo{Y1v1JEM;uHq(mnL)u%ie<<#Ppnlu zl5GTEZCK$dboO`a$vFf?q}czq@g(L1@juitLSdgCbnBMI-`$4Q2XPE6R8-S*bK4NO z$Fd9ZYk$!5%Co)3G4ns%2I#1zgnXJc4s!C0#Qz{Mp&Q2r^ze=I|?H zceMP5V8SR*d#7bO72EFRleQ`3wKUGO>bthRWPh%1dpw~O?K1bpeB5{U!+EqcJ2(8q zTtZe0+2+0j?RBJ%_qT@RU+CEL;I)Pg8|Xa%*{LN{<-tt%v5~fmYlTf@?{IhRLBT(m zSd*UA&TJOehBZc~tsYH(V>^BO`zx+#GST05|5sQbA)6^tZzoln`1o;>9n43UFe^oP zctQjt>Io;JDGKM?)^ae0P!X!&5jk%_O-&8-O>-6anjZ1GhRMy~;d_$;1Up@0|0ORl z#gzG`1w96k={8q>U0DtKB{WFLRIf{!f_~SULC95+UQWa6rXikCyZKlt@^GpCbqYIz z({m~3fh%9d#e$$3ZL{K~<Va-UC|q`;_-SK9x+ z+x16G%oBdQ&z?UwH!&HtFRVG8&#mBq1Qh-7B)}Nd!WjpE+GwUoB&DQ8kdi_EG=r!O zdhgLG7U33nVM*%@%xx2% zw9zEzb`wjFq|S8?r#^b5to)~+OZg7?;V)G$&Cj>Q$9OV(dV7;~Rz`QTBMaX2+X)B= zxTHqOG<$M-EQ&fIF(7?87fMi%z8cgKk&u{xI|3q?`3ufSAS^8{B^-yj$5Z0tPfH*s zGMETg34K^DNzcOY`>ZGve{qqE%gE8xFI9O)%8hs_>NdG`L1;+GJ$H8q$@~mvQ4b?y zV+~HQWqm%eWi`Q5_xZWC1mnrUMnc`A=xG1r!(9;}3B>*N2y?aNXeiHnKnhK8MC;?g zu!4QS1&2cfarAxSNKpOzd_uRH}Ck}-un9< ze6!<3a95buaEoeqNV;BVPnttTRx(pq)W1FT2kz807m63g6uSK4Pp}VknP{w_`QYfM zqqDO=(6?X%hYYvDri~uo+gsNtDJcUd+8}Nj#}VVO+GQrRv{xGr?F?;<`>#HCb^X#HWm^5#JW2^n1KStEnb@kljl-BEI=mL>zC0SK8Auo?d<5+=+ zGMRXPk~%#HDw-4S8Z0bg5)w*U3QartIT)Yq#8U}A7yo8qc>*gBXYKga$B$R&=n_^T zsh&3$93(shuvCFvXaa@$D8X(rA}EHQ`S@t{Sm^bd`SG^7bKPM%cbkQ4k7y?e{y+HQ zbTLsg56v6G={dD4Nnt4ftrbyFC!833^z8U>{>0!jW&ZX5w=CVm z+u->foe%#jALI!i z`;Ug2==C)f-Np}+7!sLQ*$Psk&V*w@i8`+{D2RxN6a>-o`AF)cEHbQO2n5?>@zyH< zKN@7u9%!%}nQHwLt4Da8*QyB&C%6|N!VognqUc4iQ2bAcz5xKmD=p|N7>^cI5z*z2JLjU#L@32FnbCJF~qk7q-0E73*n2p5ER*w1dWhf$%& z%jdHL)GUz|A-q0KUAvdtT-I1szxuo>@qHY=tRjCU2-JTg!8EI{y)wCO8xCKHv7@Tz zY0N?M)Q3!F#Rm6fvXA)R3S&K=3LqPA$nwoDOc0C>N`kaRi{6N@G4;uJaJ^&MKW7BR&ptR4&p?EQbE=JhFHG7A zHu^ysu#vw`*O&tb3skq#&apv7vHfuA9f~mjvc~_`%A#;qp{Tk#^&-TqD2`g9fW1Wu zUAsrR#l)P>15!@4IS>Y;>hUi59Uu7(Hk}esR9AWrx_dpIoLVr)>aZ1o+|t2ruH$LZ zd*IhcW!#tP9paxn380}FFfXt4U63;QjmoU75N50f z@?W$dv>sTvO*cK%4t0tB_52l-8~Ou>d80sKX!U)#Y}%%MhAMR7q60GhkRvS;nVz3N zuMe`gyghH|2j#%_6dHOs_T`I8P_^*$du0j}8OXOUZ-98XKo{~dt#Gnsv3P~C&MRD@ z?p1;-t`z0&M#b4mFUM1c97mx2-GKc~hu9`z zE0`iPAN&>O33AfEcb+WS#l?Rba7M%iYP#4}gPlTSQu^6cJ+fT2CvQ$3M9VtU3SUh#w#Yx<$8~Xa? zphu($-s27uLN$K)K&%t|OkG0*^{ts`PXRsE80JEK6Jm5I6V`j0A85W~qE;Y@S}ddU z%T7>>3&M^!&0$TEZVk}~|NoK;_mix-bQG3g z6flYX_lPW!^$!(-sg)OgEIcrW-^LEw5p%bVlPMjMq zVQBA__Y0Hq7b^@Lknnz59PF)wMn;?T{PO%fUSA5F6~8V;|J_;f&xGS%VkkIAD#(_p zraz3T`1JhR4KCmOm?)BN4S^c!I7j_tl-dE@FEr+&m?VXu)fI7-Xz6ZN!%6L`A7faW3m?oAU*< zZ~lOg4No&5olz+4oL0yWfYOV_v)-J07x5mD@RP&Ob4cowY04*2@%F>TZX@ksVcwq&!i_$ctQE+C?yeiP8KsAnx zFugfzSQJ3VnuQ*?`St5p&+mR5PMEDBFc=1$tKGwDfw=l@xZT}xa5xEBZxd>h7+tpB zgqu+SA@*(?(B9+F(~(1de5%28jqL>p<8`QehKB(?K?Lm`?XN;GG?KCpDiK~)SDWZy z4u=V;2a%k{hhQN%En=_z?GExU8EPQchh2~`j_d$+F5N7$G4aZVwC*yenm<` z+RKmHR|=rin=t76s`DN+Jb<9_B@v{irXs$#3PuqGV99aFd#sqK-wMvk;$dWNjb`JG zoTzY7zH!x46$6KfM&Xi$_>;V9GZZS*>G#&*GJu?54!Gyyg? zy8cxX&VMUVgh;Ewp`qyL2bdQSQB7)pxG5?a=$Ye9_2P9+Xa#r2i8N0PT@Y)wwc1#z zCv?3jsvHfk{EqFNoq60OqQ&puFS!ud#natyc@E}uYf1(8UOfETPZ*Y0P5QdK zollN%=R0@Mb^y>DW@l&f^6=okWNEu*CNTRu$td?5$$0wci{Jc52CdvXq>_j=LNZP+ zLOfe!Vv6zb;7Fynmu44(d8x7K2=023PG<_SrA7P=V`CRx2jt>!&!J|JV;UzD)0`$5 zTCno-&$5CWLb3Zg&M=Yf^~G>@C_v!Awkz|7_NzU1@-1*@%j#&6h#)2v5R}X*&z_kY z7=$_dD^w!Js8hZHfqw^vC_wTHbm^Ut4~*+^T?kL=MWpgUI4&G_rACw%1nt`{Jhx=AUby|WH*1JT{xwQ|Z@_7u}n zW(nn%tLfxVvOt!MTU>xy@Tjf5W~xwQ?gdf9Z$67TtUr7f0Xhu?b|u#`RjwNt%p?ff zqOc8`DgblqLT&mMV#7&cswcSXI zkzOETsH@vZ9L+HZe};yp7-_$K$w!&8n|s|nBv517BFzWZ1GH~NEEGRxO_^ebs@F>8 zou~#8KItJhzs*~bm63`fKAz2|a^F!Jl0wiYp1F$573GK zoAzQo!yvc6DCzOz24bO52T*R zK99pJ@LIVrkNPl6zMWKRoB)5v?cOtyk}o0rrIdb6jQS)ahidqywHV=1muNy-J(N*G z2Apae;XlUWewoz1q@|?G#pNtQ3s&v0iimHvzNS1?If7@af`vUzaK;ESF5cn8)mw#t zl8Pi~VYE*Wy_nwd{p;6C0VOEbIC?xfGIFy0(ELy7Ib*!Pr{}sdu$C7W*(Xxi$?fX{ zD_C!9Xb`*bU}0gc^`F{{kWXtApU#>R;H_Rm$zRrnFesoVX2LYs4RZDK8(wK+$IZzQ z2|R0VCN?a_K9xrKj`_D`n0!oMlUp%y&#O6Q!ApbPF`Ygbo9{* zo*wG$JS)$Cz|Zw~RAO6^`p49-^ld97KOGtLV129GX9g>Pjj`XRt#WuOf8g1&w19 zhRLsDcYOGu_?6LozJG945%RG3T*Jsnx?iDPHfGxyNJvoRFtNgr^2xL9 zUqfF1mWM?{Lla>3=JjhdTR#gkGeA%vKe_c>dXWxbRM4#M8N2xUR^NRmf+++cd~`pD zK7ZyO+Z{X+Xv0;~!^1;cJLc}hBsQB^fMQbu4F;t2^j^MfMG$HxW3aigkq#Nv8autf zZbf^6(IHI}C_uCxEccV7fq(Ose*~(SpZa11a&mGWtK%^LKCP~C z4$jHTTV7cSm71BF;((9DclPY}rKKcAzYkWnli+DH@Bm@BNz+Zpe(7aI1weY(d4ucx z{h)(`W2HS#wZbeE0qT60m7dl=6s3??trz|$QQ^NOHPPTMwm?>Ou^<+&ue?%qGFM|} z16-=;qOG-QtqTCbfs;DI;}f_#uFjI(`dcj7U%b!1#aZEDV`De=05tnsyxPAv&itdz z;;)w3pM`h*^4VMeYaZioNnIIYudXCtq zsv60f_{8jUAT@U{tF03luh_*S*r5q;S@;r>Ej)Uhr#IYI-*3r^;UQ=WClRQmJ?-**vt2 zoN+)b+d4!Bk&t0}y}2njG&m?|Tz)0<(*t>1qNE>ix0Y}omnqHcKO-Wt{uVe#X|ECT zd`QOt6p4n8?gNu&ahXTk$R3S$2vSF%ca@@hkU1+dzj~^bcikisu<5}uuJdH)ydY?J^T43FGS}7oX4fDa&Ur*0 zrPH*gi-hzjf{9F;(KwU-Eo4>tYnyIfVdxSn=$U=vs?jZ!mpFW5WbnGa_*o&2{!gEh zSQNu$`h}lE+5(5wD3mfBZI3iJ&%wSH%_gs$_231{yFW1(PUItmcahP*ekvTXQQ)EOVklQP zw<#0^y3dk6vYyZ`c%}7>DC1-xt47nKyP)^j^oBAX{zL0dPE;d>!&nRNYsUfv01m|I%%0-xH^ z(S1ot$-E?4ABmSLV5zI{+~09D%qu83a$?Di{XCiT`gLGKzVW-+XQ3K!p{fu8bG@N1 z1QJF-ow2gC-Q4V{;Hc2d?D7&hl=ABP39>$641c;FR85rLRF+lQT82bKT*mo&h>iO6 znSg{5JK~PfOuC8LNJA9+rDU7LYk+CR2;TGXn0Wzrd+wO4X)=_DWU@vDFxLah3SaQj zrI1AEoPo$nfCT8=AaVaKvJ>OxUHd2 zI~7&A@*K*2Ji~2~d)&16*4OdpUYnw3nvMeB-{ZJ+wyPFfvB>T2y1Eb{t7NC-trySY zatAd}58gu96%iM4RoGkB4`tSm-QQ&Oet=Y0C-mjJNNz<|{O(<=BOE$9M5?qMNG;cQ zs_q3rr!|vt#Er`xKIw8%U$lCYUQw~inJ0!gt%Kqc7cVoaSisWaH+-9ycg4GeFtr82 zzFe03EvB(4ZID8p`3K<1!lzTEDpf4%GA@s6O(`af`!&k4O(HBA%5iEnix_%$y<^ad zoRNaD=yfH%H!>1Q+ywSqW~cU1bbJFuRMDtB6>FXz!!%J$nzAJNJBbR2+QYB)nt$;^Vd7!t<*6ktVu1XYk^pzR^U> z?~5QtTk*!zCr^SS;4v3fK)uLYRDs~Y$jCkj`xB1>S>^A`fWjchyvPDmFb&~?RRx=% z))fLk{~=|cv%nRYTyt~t9Js7f3MfK|Nni&!O5VXv{k! zec4x=yTZs=f=(J+Ae9x(S^JjiygHw}?;b_*WwmpV>CC@_5Bg#9uw>EOWP%Wkd5Vsl zRfZq}{$^P9wPC0SBbKfY@4ci4QA>a-FE%l}K*!b@aedjp{lw}x?|R{#nrLoEPtyag z68&-7ee3)c57O|bWKlsgdyAjAM(H20(h48Wv*NDoq^$R}wzR-;u4{6#5!x~-mcGR+ zcFICdU_P0uK>m8#?CoJSCTeIqe{9LXO&o$x&O41^oH*FjX}znYJDVbceWNk#Skf7<{B3R^e%(pC-jRL3E7mye0rLY zP-y&4iz|^BG;81nOFWGW22IH0kP;O92(b#iXZavVitceG{NTzkq8XeKL9p|L>O-ti zDbvbC9l6mQ_~p#$Xc!s4PEH1lpq%j$APg(WX`Ni#*iiDorqWp49JVi1WFYD(bz9Kl zgGEBs0eL|o8xUUbwBZ>6Bgb3{G)9lUcl(5omZpTj5ZG5KI7btKQ5ZB|4v?f-h8TFd zYg*%qm_y>&LMr-COUx^5kuT56NZKg*T<&(^%RW~baF~Usda%W!t+hP7ns6h2)Q_fv z@R?5*N+3jjLz#Kf&D?MM`k=%fog@j>4O7*?s>AsUTRS^DP$U?-0Z=s)6VugHLffyZG z4%e1$-uPa#!Ewx&Ct?l_Rs(PM)IK6~$tKq7BTJ@=GT(}aQ`#e6-ipk1T;>;it#+k7 z+0cx)+QlmL@?2E@c^}fj?$}+jI%$(TKOjJ3hgjwKv8G(wRpaAgnQ5oX>Q15=q`Chd;#-M>i zj-Zm>y`vc!RkISi^hRVIJ(G-8{DvFbKIB@^%K_jsr4q62gM;H+ zRW~dMLg?Ek;($nSy6`1^zuA4A#&=E}wG&sssORchtb*{ei0-L|Fi#S4a!LNvd1#LD zD=u4$*9?Q>7&?$&?Qk@(v;y|_Jonh?jb86J>7QSn>=6%IW_n|v{SpFBHWkGXYqg1~ zsm#oz_`E38MMfh|)JzC>?u(%-Fy>H1v;7ojrWHe6(WoAwWgf2B{rQ8**>7{e-+9;r zyCWvJ1oZ*=&@SXC)@!pV8NXXDi1B%oPSt0lP-D^lC>f?f6VwJCLn zGX_=BI}<)XrwZlvKAtIVHosg=`_?)aEtVB^YF<)6%f}~ z@0?ljX7p=v*(x<$Y(+*h)IUWwF%se~B&sP&=o|>A5i**VoG&D4bDQgA-j%FV^ zUs_o_nv;_Q?Z{T{+#O?Mrc!%WwEMV#xNjfBcvC_y!qHH2xL@~tk$y2Bh)^d(6_s)q z_=-lH3zW&*q5kr@+}Wsr)^~Y%ACV}Otid%PcY8_3)XMR0ft%KA(}r1>t3nHCUcIlf zqC<&Ac#ym(T}0@?;BNtT$T|2>6)Q04`S|#NEi_Dh;9Uj z6t;Oy;Qi5Wg>_M~CpQ$8__j$;4hKeciZ2hdjmdnl$a&#Oo4FM3J@m=qDEg-5FnZ8w zetbwBy_)=DW5Gj6ifH?I$|#FOXE#r7Dd9ZzSNjVdPbD19-p&Pmp;)TQ2)~2s=vih} zd%w?i?0qz>TTyACbbGgh+T~Kjt5PRa^D${o?#k#;`C`%4-T>@rsg#z&}?Ci6Yo(C`1Top7?*ge%Q7DTyRk~5>IuxB1ANS zMT6fcTz1ItP^PHXy=4VDLrxBmKEsQC4- z?l9I^U#a$2K$YBbq#-D&Kn*%{(|c_8y+37}v7CQo#=TTn7ww(6npL+16tSF+{cj)>nr`#l-FcFrW?c9L?pE93#IpIlmh^ zI#@sWUuGsNqOePCIvFF^cgZ(y!%aD`25tn2R?zIf+3D9-L>A&aC5Y*v5%5F&27P0Cu;yw8w7vuD166hxoG) zP4bywO6RNG+o#Hq+VS^`?Bp70cKT$792A57BQ9ZRa!(FqZ@n#@Z2mg~oqm$z-n%oJ z`vuCKXkGFy6F;u=&PSVt$4Q8r?Ju@(r~9uK)*P2$_WXj?uJo$kMz}?~ z^%XNhmiyaNw^R0=jy`7ZjQ5c{kM3Wec(@P$Fx|vUp?8Vp6NgLR##(|2LIROS|Wr}zAI@{a3NZ}x6DRJ9uG}PS3@l=+v5JYdw3JhDTK#iq`^74L)d_D zZ{IC>`!mV;yK<-Vij1#+_ot)oG=HWfNpjMt=lEabJ$F9Exit=4!^3Q|eyig7`l*PsCVO8)vU$(db*&_4O|=Xm2R{umHn)i( z0_^UERhCK1onh(V$cSCE+{4ESyVRtVQQ-CJU7@^Kb4xZsL`krD%u{ksPEFB|JnJ=h zdo~w&HiPSQJmMo>GafxOI}oS-=&Gn1#f(mdWKqk`L=meZJU3Bt^5`=Af$3hIoauX4QjVzUW^;aGVWx+G7q@fgGnL0qf62(cs753&s632^l0UlnODX zR=1!aO{Ibq4P)HU5W|E~k?ki=+n}xMT-EYeO86tclAM5CMv!s;m@bisv9S+Ur9xhN zl6f2B2ECRXheXc^NFH0dda)qdt6<35Bhk{2-w~c7rJ=ZzwL;C5tY2V4s0pko`dRJH zKuUEkSd)ve;euBZY1DM+cc1QRM}p`B~>{e_fMS# zYV~nDoMf4yaxXTXoAt9Dto(TL#nIi0F++6xAy^NF*@XAlhwA;z9dB~_19LXg0kBA8nG>_x?RC^9%VK3YLndjIMN~Kr;(4`&&C9K zmqOp>nho;jzPf$yBO$E_@McX!3~W31R?`MWcw zkx`mfJ-ro0fKWMz$t zE~u~DYjqn`9ToSTy~uTdX4BQ~Oh2iK3y{~te48xoAw(7;RkU}3{95@A+52zLXYJWU zy%?{_bt|B-J4Ei~y5yJQ-d-n*O2qpfabx`q)B1J{#ECRH#;FkcU%E7KGH*Q!V=eKB zM6zMhHc?Db^9VDxn26dUi$=Jl4HS!1dl+_rSx7^;Sr~H&G9UDTPa)3y7403 zvms4Jt%`?m!OKKb>Q-!>+P%Dms8|X8IGI>8-T#iq1F4|)EUXqagPORH8R3wZJkJ`S zpdQAr?;M=&cR1kxylVaQ_&Kg|^t$}fz1DLwvOeEQ`Ht87oIA3eTd$9Mt;V&meiyJf ze0;mUQngF&F&V<)@8>rK6Q!@S$*+FK8(rW2p7g%Cpa44>=pN4nTCF^%#T@4(*{oMm zB0L}3+e3aE<1zt&7Jp0vB{%CZ*-Y=VKSdc8Tmc(nS6A0b;LZx5x|cc&U~)kZ)GApzN=2bV}dTyp8+MTN(NGmUM~#Tf9J_#1}+YLt9KEC+JC?t{MZ-e8NN}k}kPLjNTts!=JR8%PQMh1JEMYH)O zOo%xct8l^I=#2;xL*a$#?2RCTiLrWtITPRej3MXG5$CVswexkrRZ@mf$W8<_oM{KDZWU~Qsp|vRw9k(?w0ZGtKy#l+aZo$bf_+u0=s ztgYwe9j-Ig$s#u}1uv+U5#Y5RTx;kPh#uej-cmCVI79dRNWB76(GID1 zZ~35+cjCZVVT|v4{}t@nSxvXyy4_C;@|&*%gMEWf%tAG%;!7}gvZ^zqzd6B+(L|_G zd-Sbtorh6=`_29Rnya*h@*fzP9rD71r4dDVH#qd0zebwh-|T)#sByfvWd5piiHS!9 z5fJjNtNL)fRogS?2kAKaCiUprkIlLT{rr%PF0MjZuT2kg-|c7$bPl7t>Y_&fyOo_Idz{qY8K@<49=aSJc# z4~HN@l3oM2>&v9aaDR|CQi+$|Dt0^N{vD#3*Nk3VMSrP&a(;@*u3+;fHzPBS;z3cO zFbA>_Z)m6mXHZOs&P`VNqB&~rMd7)s@F~I6o}%i!qT_z<`uvn=^ns3z-0@eorCS0=&pkSy%k6&{vUhGeB@~p# zLz;qYHph7*?|j`>zQXTY2PuT%?wr|JF5zgx@%shP( z=!~Jt2XiE9}gu$|cRksIXkL(YRcN*(n{sqWlp zU?QDlpLU~2|M}C~i`UJbo~&leHT*-hs zTWcKVMdi7c-@mx2v-0E;j2~no>o!wz@tiz}nkoQZ?zAwO7Ny%SeM&AEt0T~pRuNOq zz3q$tIYHdH*_Y5cD(?}SPitSh#<6-6le95|uDc&d zXK>%KNV#6D-Xxev-~gKL*0SVhlLc%Bc=hNb2>(C!zA~%|ZEIKQZfPW?JD1WOf^-Nd z-L>cx1f)ZdZV73S?k=UIq#FbUY3aNZ_u1z=dvEvm?DOM3_s{+9Vma5GV~#oEecyo# zb7C2l2IzhY)JJ9g{4bkbVGEovW1OY@SCu}zvFOF+eUi3c`}qB~&9k=1kZ*htAwl~! zkqg8BJU_wRrI)ld!jv5fP0rmZmMO+*obUV<)I5rrKy0|T~R*jqerVX<>w z={My7Zr^c295ukSuM6~5ouJSt-f|wca_=ZR`d_R~-><#Av;mjEm2dEB_TA)x7jEWU zI?vkG3%b^3hm-9z=^`Uk6(B($KRGofSkx-y^ZhJ|9l^U-1|>D&y-H*84~eAsg{d{P5u4ZiELL&m_7v@={#lgH`y(yopSd1 z@_XFyTOU-EDq5zKi4%h4$BvIn3%4^)!9SaLCq-&=`GSzNWFtl&`{E~Hi$n# zK7!4o%zNm)3%O!6A*pmj{Po6E_rzfZ{Ob1``=fz<$?Y_C*<>i1gp_i2HgK+nJN7>LNT`#94 zwBp7BTiWok=x3ki#cEQV@^TtQzJYMi5B6t0^~1*r9?#wK$Q~@$)kB?kERECu-AN6@56w2kLu5553Pywx!=9yLqplmlO4?&?q`WC+`XG zy9NzqBsSypJNAr!ro&@nvkDYC^OjXoC5=T`Gcg%7hkovg@b61g5M0!)izqsLKYixb z*@D-;~nKaYs`M0^=jnyu-PBfLiX&eI$4fyGEt9i+?-#bd_W~{Qtox`;i%h31+x}B)>7p>N5z&`R znbILwgb%b{3&oKE)^UCiRk)f%jRCc0^I5ms>1crYl4z^ng4 zA@B3j%e$>$3^lZ=#^Y;D#0v&-^vKS%$2W(Y59qzzkJlF_&a|faUMv&jz0Z?DixgOt zAarzp%H@hWZOZ@}`IumVh=1&UQ}~OEq+3SG(2VYdf`g<8?|y~s>&f%+6EAIBZnOvc z^wP)UNFS?<=#%60mkx@{YWceZ6W1?g8a(AqoV-K7V8^+hqz+pP2FFX7$P2ZMuf(h- zU^<(VBQKXS!B7im)gq#lod>{w$Cp>CHB<1t2HJoO9sQ8EXg7W6k?s7D90WvL%TS$I z)9;yd9f8d)YZMEMzT}5al{IgiS?hE@x;&2F!ZV;ShGLQa3Sp3`0|ev%Sp)+EBcAW} zSDaw|;t+tt*S!4nRY?RyQLG-rj#q8X z#KvYMxcXxE9qAuv^B8u+%jNzVgec8(SOzFxJcd@MF>m=b8Z!@EzxvWP` z~KHu&;kU&B9y2cW9J#4P^&FjX&SC+s^E8Frl+mEgAusu>NIBMPvHqQReap z$nU=t1U^4HIdEtP zzmU}tZ1&3I9n4RgP%qhGZ)^uOtAY`80CHfl10Lp=Cwi4rm^Df`WeaEE0+s^-MVZjb z>39WiL3QH*0VhO)QypA0 zI0I%{d8D}_QI{HyySx!JNZ()m=HPQ7DP;dybYZd5wK38(kd-u)o9H@t3S3Pk~j-uUOuGAK0W zjiEtelGxz&{o8Lv#7c;IRG(YWVxpn`!qe2Yyy$z|Dx(Us0rYkZXZ8pf_an0)0^*+B zKR+vPRn~s?Yf1JCr!?pK{%((~p^4b;g_$~lYzYXo!{*$$wDM84;1awkp!!qRav3y> zCqp*v1xocOC*n}FHy28XYwyQB}96upK9fazA zOat!xmr?Bo5r4c_@L+oBtDi5;NFPSrO_dMXLw%M{o!$V zK9z&&Uiho{9yrp6w`YJG{X%r=rcJ7y#NG97EN>KUD7gJyOg_~Z( zk_dvDQd@5?5o#%8ZNqxf=ETnM-ZuhX`{+Dka1FSd{&+jlfrx4+8Uo@AyFrXsu`2>- zzh_2<#>AwbKCv3bjNa9ZBbiVum>n;fkLK9-NS?9>&hMWaap&Xd2XiM=mS7A%+AiPp^G~M3i!A9 z=uAPa3APwl#z&9N0pbm0F1A)yo}h3CVvC#$tm&B<04samZ}`i}n=)h|&Xu^nc>nZ*fWg;}scQkkDTf^JO+wwAgffDcIWN1kpvk$!<>H;sNav z@y9bGw}l?F;V(pK@M~>zkLb;q>#25gbfOG2G&RZjUA92?0+3PWDopwT0<+v=^K$`l z#5$PiC_c&l1hj2^Y0z<=xpykvf>itH#z9wY?P?$gVAk4CK@}gUl|)VO@$f85*H%@@ zeAmkd;5CtAz_(K|w*7 z&n!g1II@?{w{SQ-JQ_PL`Mqr<2Ftr!q=+l}SD=SCiZp5mZQJ(~0`SN;e0zU-jDKwx zx4pxEJpv}fOEUe;SWJ&|KbIHv-&XSCm%+$HHK`}N{!LKq*mH zPSuG)i;dC5^5!QMy#2X9U945x@0t2Nkp+9K?{B?tg zl`!$m%oZ^oNumyU)U(wu_ktC8U5=-?wb$UofJG}|uv)9gp<%RERlIFFZ`;zA+3ezY zhY)0?gjT$k@szS=<{{*QCIXYu{H2eLG=Jz3eViN+Sm%<-0qP+1LOqDkNkj^c#Fz3sBP5E|!1tq(^R0iS3RHz6((eI~A2!u^I|rD*)$p zYA7VK-+f|t`;Tv2bWuB~G&VUPb>-c(baZs5NT+W*I>d^@BO78W;j~3~zGTs{4v^1|yfUfg97%z-EZC5yb z5}2VWH~*E9o`T|z7~={@|~J(kM~qYwa?sWU2RIKqstAE?8Z4OvJCGe`)xhjyVb@W~;+mD1=yI zU~-b+jd8gY*J=GknQ9re(n6&fCU2&`*9IgDh(5i&?O;}TtSvo_=;AfsrF7VT{SsHJ zSYFR-qP1( zt&0am$@C~O@J`56(^x=|te_&cXvnMfP#hnCGK`4I;^XDL zMZ))e{gl=@fTj8!v^BPYBW-Rb!&R0^1i1FwF?4f`G7$bpkJ#;ewyP&8nl&*z?v|(1R6Y) z`q1BBB&JlnnA8#~!bdMsJVj!l^iLv_ZcyJDt>_u&b-5R>Mw4r-!2Djd)cBY?#4ZV^!DOB~B#S)Lz2`VGsP^&zbwYXw1B777g}mGVH0Psmo_rDv zPuVUY_l%)=+;v;+yag|}{RA)9-T?79rJaq9cG`_G($h_pkDB0w0W8{UWAuSmaZ!=# zCK#XEg|9bprxDCb*M0i*cCM?|*6yz6rZ?gT%tlq9CO3)p0vQR=7SS2mo;T5#3=9o{ z-m<`FyLm>w`B;CmYDEWR)`p-5tCN&*eA_>~l{^Hz=Zqy}jv-_YL=Z$3)5rH6nIJ{<@4&bGM z%*NcUD&aLbG3f#rRZ8mu_wytdiAe88YsFL^m-4R4k-n$nFw)~pu3n*KK1Xp+1_Cqu znOJ?(Mb$~8sB5dMT+W+n19qZ7qgWk-2SSlIF5|5XPtH1w#sk?KCrNC&jccZW>a)w% zl&?afu+ap|Li-3+G??O(Ge*1|az_{5>1b0upMi-(mh7v`CLg4R5_^8U61S z&EJXue~#~OVT`G1&w6Oz!Im=e6&I+~7U?un8?DUG2>|K#Bg{9vk`k{?K49NQOb_@V z*4B=b!hw?S>%0U*h^(Mr4i|zXcVCIUv-R)flDhb6PN>Hq5Nlv1JTuKUFn!YKD((lk z3?NEu0IlG1gqxRMfCadwnBBX*YoyDIvpW0L*l<04eX2sR znT(8#d)~NSQcJ+?v0sBKXLre)WQM->#lZj!r5<%4Szac)3BTlsQpRzHQ?BS3Jc_Gx zY90Yq6%iHnb>g0S@(khDx;=K5>YxT6=f&;q?cFqy_`fpi_7Qj zyGI=yIOtYY{Tw$!D!|7_fDZ=8b9Y=_0>*XhbsrEfE!ja5U60kk!HJN_ZGY{P2k2zROjcL@2_;9t`g|_ ziz`}p&rA2;5dsh*f?zOTNM98SWyi2Z=kEp`2vV4cJ-4tboLDf3SEv?2~Z|i-|8ZHhw z>6NPvNF|=)Y|HKzzwmy(VM?&_qm|zXy|)nuGDFfg-OuvhyTt4*cz6Q>r6+3#0+VNY zSgjceg#&(4WQSQcb~BCNd!VD*E0C1)WWIQ<^h0W=K^k^GQBS$|*$Eb^kxuQZHsBX1 zNp$=94r$N7h9AI`6!mMcnq(b#4-_hCT0*kR%awaCL;HYCZu!ym)rHou*22=A5|H_Q z_+ZAw?XNgYPt@AhCZWc#5A(K3za7RJz$=t7j$m#_qzkyc#eU9ywzn)E5f!mYhFe}< zPRMF8H;jsej^2&czJ7FaV)crdz`qmlA{vZ}Vg>r{iZ3JD%+@F{G$rHtxUGTol@QBn zs-US!fF8t@p86l)mhA}^&nY(pTC=DgVZY6vS#!hqTHGA;X?|*B2Df)&fztIV#%o&< zRWO2n@|rZf^!D9%2Uy7kN9Wt&n$&__r=_~jg0xJbOS?5Pda%rY>J;Aw>G*sQ+S=_| zKwEoUvp}rVboo_uQ*(~V=YD=9)AJg)euaFt-ET&kls~~s7kMrOg|+1(W2Zf2SYNUN zw++!L1OB5`cxMGig>I62ymp|l&~9je#l@M-O&1<1A~;EH`lZ6gbO7KSZEfCm&1_$s zFflqRez9?TGkd|_;9Z@T&HEbup~oqm(~iFYi|c%64iRE^m%J`hi;Wp;arKUYyf~ZM ziZ%LfqDH<*akWh+e z)AQb45d0SSTHqxqS@nCa_KE;B3>g?u9sx-`O#LE&5T+z2?S`rhUXi-lV} zX#)KgTB5?9sh_~6TX*Ms$Nj-1t`8t+oV?8k$$Ztp+`YJoa@DfII)c+I1-xr>8Ts(z zBp53xkrMCp`_= z178cn!fxf)ZyA0gvjCM@8UR#$#F@Du!|?|P2OW}0f34b-6}KxZ)xzU!6%rv%r675) zC_f#kC)YL!sa22>AKHv*OI4?{G~22nTR?SD*z}kcObVAzXSHD7!i?MAe$Is8&4xO9oWc*qEy1fAN2ip!culddd_Gqv<&KKH zC-YwC6T6F7dyW^wQZ7&ibLb@?%{aHW-yEm(x!l1>^F9UK8tXaFtGy&?03v?h%c3(a z1Snf1bgY8@evj3dtfh<>1ZsxZL316Tn=ru_bgAyJ-hC3y0>;sQ<$t}f2q1{ktv3!A z%lz2@fwUg0&hSqBJ_YzG0k>fD-wKINRuUwnrze-7s%=UKden+!Ipw?(0nDYVdUz?AywxrCSVjEmvdBlR$|k7dv6I!< z+|?KXsZ$skRJ_M{a6{XJ+vot`(@8s0JT)@Z5covKd@d9Msvjkgq7Nm7rD3LdyB*Hq zGLyHS9)7F;pk%J&Mo*S66^?ZUOSo^Fs!q!OX{iCPMwfCrA{Gs{wkh2ihUOxSe1yhS!I*Pxro-H9>zk z9}|Tx$(Pg**BlNM&bxM3=Kvk*3&pD+2|gL44TCJX+pa$69w+73s|4xS>zH~ouee{C z4xU|}Eh_4bq&8l!Yc5_Ec~7KA4LmHP`Z5(2v*><$Jx@n&J?-2Lx7~=r!?)9X zarVP*SLkf`JmV_5{awP`xJgqLoa?-Yv~2nCtW8@WGD(!C*a?2xzjd?mkMzz@p9EU@5-MZp}x#uc7e+dme6>r z(650-i(0(K(4~8`QBmmhob9H?>3bT-Q%5NdgrFDZtCK!wn$O3z^DPo9F~twJwik!A z_Zjg<`!u}v$Atl87$0K{Fl@F0p9;_YOK9Zpp;qALM=rBNUfO&}FGrB}aA)I=18kG* zulG;LS`h=j`N1olg^3(p$6zkF-^57UUE(ux(Y_aOI$usVf<1Y$M9rAhKJw^hX?FL8 zpTa$xS)Yp!@1@hg#JB&s<}V=jkh-H}4&0FZ!J6wWxw96WU{q z-zx8?ju5%m2UJ~y-dhRAQ zmjCD?IrWG0x%hIzuFs5s>kWPHHgd`_Iw9#NWfs47 z4)Q)so>l-wv-Tqq^0_{lE3DQ4MH#}Q;J!_=1Uo@DOueCK0ehmYVpl^jeYuQ;YrClR zlBv;<=d*muSP*ace4(42g_lf3j zVD@IpBuB8~?U2VqpPqUC0PdC26Y;}*9{B>GCg;B36FU2`7`n+Dj)N9iyLjbxm2TDe z`pD_#a$fqv=cwH*<=po2oX@9c>qXCKcI(BM=5`oHCxy@X7Mt1oX6&d(mBur< zLm4+OzF()f-f#Bt+#@5^Fo!m+JP#30U>2YCN_|E(4DcRh4K=Z8pD z0rvI8VRxRM{Uoc{IazdTzS~S>aUpt*5rX$wwMph4SGn?7ceS+`v4=)Rm#gs|@GQ~h z^F`w9aDLNv;I4rh%yx*|2h1cqjqFVwt3K4~iB+%Wa*+nZz^_C5ZcZmf?=Jvuzt$7X zNY&9YWNqK1{^x^@nzo8)%&S$Xw`pw-_|dO`hb#32s{Pgn^el@ZQQWYXU?=Rrh!SKe|Sw&3Vs+ZE@r#!RHe zrQ(ofN>tA&GE;(0Bl^*bj?B;h*C+Me+xGSeT+ZKNwEqe2Bba>kWdL;HaT>}P+~nc- zgM2Utm3qE&zs2vDDVjcEn!Rx(Jl$|IXqA0j)jU?=Kj*XdNxa7m#-?CN65$ zS;dN4#?0IRWblx`v4UFW@39yEQoZ>To-R6`r?jTLs4Rzybp-9jJm62mou(XM%50lnZi@ zF$Z;q+k%u%=1aEqz2oI3uULSI`xOrQZ%ee&x4=4Lp0P*eO1j!CHR}Y5fywr`xn)ca z|Lt+y1(>2Ir51&DN$Tp0=}9_Un~(PDLK9x=z38_F#a|^E!bW_8zc-j)=y%bOkeUL_ z;y?dA|7&mp5Kw>aF8}|}|L@)R-}Xlr1$(P#7$$Wx3IG9RDQLPj5^7f4eY^)VkY?Ezu^$l|i%UBtO ziFFZ+8_#Gj_DVf8G0_<8x#VDIIiP*#P!%J{efPd^=>hM2=m7F7bvc4}t5nwana?HN zF~E{VJz=;bSdioRn($D9k3xwEig{j#M=R!&^rWI;ym|27A~7V{A)}Q&%Z8wx$xM^C zRz@v@?TEGrb^WN%wP{G>9^{~FMx|ER6wx@+Syi3WmjZgjjI&%45A4{-m-fOA^R?1q z3L?9^$xGU!g?JE-Sxac;7Ty9dU=%asKW(l4;eoI&OqxJe02`_F1Q%ea8#Vykyo;2& zgjX)*hqTYh=fJQ$>d$zeaFW>l>`uc%9Y_B_hz z$UI)rKDczC-uR(4@= z&yCc|`V#RLfhiDT*|qs*F5w!$O_`WsB5olEy{+SiKQ481;Vh;Sy3=KgjDqn%O$^sM z+#KqLP!wJfFOx&?i(1BCod%Fvl(sZ&0n&PPRgZ)isCk9P`)3uclYz2evRG#}m^8(M z-#3m<26mXey^O?0r<~U8phA8l;~Oweio5_k&EjZeoQu@;avqX{6}rHXxQc2RLxCcX zYFoc}^o~}NAJY6_oY0ivG__0$%Y~$eNWF*n{Zby2fMe{qB?)qSE#<6sN|kC>HfZt0 zXPHzygs?K%sY<964)aP3XPKZ9Yf?g>f{K6V#HX6K;+~U%cq2+z`E zyQqdBnv0d@J3M|&*wJM4HFA>`)wOBNTK9$@*3e7nb~7Votc z?c}W#u>{k$lcy#*Vg0d)h#SJre9y{Z<*`^yKXp|`BPF?=V;gp%ha>6?ZoM5%L?3S8 zvy24p2LkSLa9ug3!$7bJ^ul8Y6vLP>7}87Tt3yz$X045kU`a5;&bE;`R{iLZt|d*I zkKL(8mR^1oFRBcpj(lh@>S5mCllut2Pc`0*!KNx*(iiH3;)EJ{5=fk;V`vu^bf*LL zZrAHlY%~llWv60Vmfn{U218glz4X7RKp^;cQk&fOaz^HYp?n9yga!F&$z+Brn>3y%4|~jQsta|?6<%;rZei4U2*3!X z(`Z7DFww|zdm*eyk$tFgVzV%YJ2};q#z0f6`T=YZEJkXj3(F$X_Qg|61iSMx*D6$n zlTHzf+A==Pb7R~PiJ$0Dr zgUU|Jj5T1ZPUxUJ|7{*^3Pns_Y^*xkc%-F9oE?Hn*i(%=6ZCLax; z(0_nrk96;0A{`&0c5$kSshYcSvwFCnRrm*sxNvwskvj?$@G^RUQlb0tnJOc7>tj(u zcZ7P=&mYuECWY?%u@RX}I_!^LXUPpNWs@5C*HVVNRQapbYiqg67iRE2CeIrjjo^d~ z^CJ;|j|gK;e`4P~bCME2pjcU5>8uvfGmHD&9BM2d5$LcSPSZG213X~jkL>p7r|+v_ zX<22)7V-_V(Lzt2l!kXT5`vHBpmyR9J*jam^S|n@H3bqCR$8Dp-uHnhCF`IH^CP4# z{8N6|KGKA?0O;Q^&I$OvM@8@7`*05tEmC`1!@>t`O6o!)tdzz6$f2WpDX~Laxx-4% zl7@{4{$Lgd#;`ed#jZT+FSMB7vW$OkARsl+bid?R_UT>z2C00ry z(5S4tXcVbP<`Wc6wlh`?^Ow8yRljm@ag<5K3#{jMEh)B`Nzk1@dR?WT2~a0*8SEO4 zyU9h?W_)S0(J(d=-6j#d4%=Qf*Pj|j62qclyr$5s(k|3z$y5S*G5KC;`_ymm`}n~H z9^rU59DzQT^wLt<41psWS{SengCj!Hx1TMycTXpf-Y>L8JL*(<4ib}E@GKs6!7Obp zBPrN-8v)r_6siKN$8XJCVvfyW;@-fT8Cl^buP3MwZS-8g>*4fr|J6N<0{%>5@D^&v zH6m{oItB$RrL%*A>Wlv3!t>*SIYZ4!-w*0)w)d0VSBo}h0q#xn`sR26wkg!|;^OO7 z^UckO=X-d`?xLs#&)u}c1ze;J$~6%jIT^~s1y5-arDP-|!}oZo6pS)47$`_ER0?)y z^B1K_WP!_D%TNbMJLzTV*B^TW)1Jr4NjzNql`UJ-s`H7d3*v@Z1-j;%4g zA^+eh(Y;D8N5l{AuOJK_O5E?E36~=wUuxV`b~BF&w#QR5KtW;x^(AG6~5pjxb8gBmfE{_s>rY)cZP8zNM7B*nr^`a-OZG!HPk>* zB~66SLt*2QSZDA$R|}gjiF4&k$SGu}OyK1)SiGjM=c(-Mv@u=JC4!}z_kXp|T=Qwd z+Dk%7u3w&z>9Vo)U-Wrq|!PgvYTES|KO*J6D;$ zqO3fzw1G>3hehzWAcVuXBVsj@Ew1E6{Kb>xj>zm?-)zCGL^tYB2+|y%8ah2!AFE+O zB)C=43DFQd%Mg5F?+j}ne#64cKsZ}KxHIe`vSj|6o^~O)vR(NPYXINge!uvytHy%N zP=|)_tyn|xR*U(Y_*x|3eZ?iX#$UGUmmyk#1G!To`5HrX-y&+)=FS}(M7YS~V8fM& z-}Vh-H-b%BbO~!jo=2aZaI2m8|Jw3beNzgO3S9X#bqLnj!n^Pg)jti&Z%-;ziWN;a zPP<9gB_<}1I=Dr$xqq~X9M|pb>SAe&E<~C^9Bc@huh`7qXruN-FxP0)$DE^p-npY4 zcAv~|WsG8R_RgId3?$NR?CvfSdtOMW{RG(piCz|`wr}THe{F+pK0=Mlv6_jI6+uGc zb1-r0ghW?MW1E1mk8e_>k3(KTA+(3?BstpFz=ZW40RpsxcKC7M~d@+WoyHoldgXcEneCG`TqJF`aT4R}|@?U%0!6 zeWmZgS^hUmrDmtes_}k>;F8rA9j)5ttucw7?PG<2ak7uPBKR@d0qwOuU7s6p2gdF} zvbK)Q_a4<%FNkvir8#6Zp5?wG#d(_7yjR zLK@8xQhh}2ffqkJ{ZK}!KCFmemCsL{+QxdT!XtLrVaSkYS2Qx#h~@|*JA9IB?kQur zV>$jiUr2$($kx2|oPXL>MM1${i>y}sd!MEtv%1F;r5Vz}OT&U)A@MJa=B6yw9t%H% zCAFBI{*WH;uhL|D*4UZY7CnxopwLnA;ziaR4To6R&@dXgw#!}_k1`JATbEV_B@a>BU9UH*{La5U__ED)M zrw0U()TV7MCVW2ADiYV(yMzNu|8(y_q7U}&Aha-RRi#0auA#xb;-GB_%BXo_Wh(sQ z5@uQngM@^CAq4I*JH0q-Mx64a2kJucnL3z0eZCBR9Hta5_FObzTGB?f*> z*S*AFN4@EZLr_(>wvW>0#mc^jf}Hm*cv0R|>9W(xuAkO$_=-Q-S%T!ko9{p6RdACG z;Pk0Z-j;APJ?s`x0U4g8jNq&>N?o%WyLsQ#$E*TdW z2^wGO8k$p=vrB8)v<)7?=E-oU!s^C;8V2^ME0TZWKBSnS#x87cT2ocHpl({VT8t-{ zXxKg&j!Vm4!#(mq-%>RjZCMs!*j*Xvfyjj|T zUn@tp2xu;}`YwG)#NO53-n+0MI&a5*#k{2+6ZK}iXh?(>I>iq;ctWG)7p+dNAX^-B zvdu@VdqtK{)7%=x+dtUR*JSL_KjoW++&k}aI{OHD6+_-u7R}y-g;93Bjegn83J&AA z8&_&58kx2hE^^@9e1g4H{xpe;UaE1Z5t)=_w6lqsNk_DL9|=2`OgRiDNGFP&9P!;g z*vIyQ=hhP9?};|vAkK)Z?pHD!uT_q|yE=?;8KZ*Mh1oKFHHFV}pRJ!6Ze4!p9hvTO zQu)wKnVYhBJAlt{4uF8dew$M7zRIBK==bQF7 z6dP@ZjS<+W8XX#z7p>yN<{?JmM`h57i0jl3{A;8D3nT3*!LcuuiXLg0>Kt^^laL>- zu|xsL+|z!|{zlI8!!zgDzU|A0T=Y3Q8FxDrc`N)a$h{mN#y->H*HX!ycT~#~Skq_I zIF+a#fKn#KrNKXT0;j4}`pGm+Uqe9IZQ1Wd<12YL&v$V!(pvF$X9H+buMPZYGC6w9 z%OGNz(A2UD{0vuI2)$?CTGl>`p0xp^zcwBOCMJOKe&LGQKE=(b_qjy9CGg)03Zoa& zatkOQ=qmT|z}{q-^J!;VH@porC*+4Nev7Z{iL7-K3q^KyNz8AD!#GRf#5ve_sOz zwv?kHKq9UErGNrI;xGCRbTRiIU$(q9 zQMGZ5!V+?9vIJ|Efky1U9xFE>v%xeqGaV$=b-2UUREk52-|HDAJ}n6@No+(V5o?mN z_hV!mG0AV+c(1q!^e$qPgZtb&ZZ<1jsoq??8gp3Kb385^dPh1t zFgGyM{$_2t0hRRgaNk77+-P{SpC}3E$mS1Qn4sn&^#1XUI~!(+5S9R7FQjue(+*nk zdzOK5X(n>cuPQSrwB}Up->$10l7hX251`W2%0MPnKD#S6OKDG=>&L6cEjlH;{P@91 z`9mwu$2AXmRs3I|=DPW@Ge4-N^^12*F=Ehg!k-dg*b>MMHVZm_RS4q!pN2J22-SVV ze*eSgS93j@uPQ<`Jrxx57(>m}yx(4;{NtT0X~T|*>XSfsZZ)lm(sc4nlmbD7=feP* zp-*2^td_o*UD%Q!`+S)EdntuAkmb@45*^0naOrtWE~SMWzHzW%!9 z8TTAHr&BOCCy+?}hTNaVIldy2DtJi3p4I(z9uA}8@Gc8mxY|IUKI*29p0aE)j!2=6;6cKb3Hi6(YQK=~WHS#Tkwf;Q8(3Gz=&*g}SVl`?y;Y*?SChDor< zVOsktG|8n;V+^-iH-1S~P~F_}{Jy1B*LAy$&i2k4-Ef4svGb0OOJ-gb>P8M)_7T|v79x|H; zTrjRUdu3I5ern27H><2rjU>&w$JgH^`eMT9-o-w_cCb4bDiq^-A(xw@vaZW5l%!NE zwe*oBU5S$+U=Ba>al9g`17C(V90sb@k1q?$u|t{zp9V;$vgFu7{&z$5;Wb8+<#W^C zFQ2F0=mXna@Xwllq+`a{tu%L=gr2x2f;1C}14B)nH)>0q@?(L2AVR{Q=*WiyxmaV$KUvyuxmBI;k1)qw z4uKX2TT}4f6;xweA0vYA7&H7QFZ%zQZ;b@Pd{BLIuRMSY<(zFy?Dfop8Uu8%S>cDF zA+?67CE}Y5f$*^L8)EXyPLCLxVuV?K&v^XWD6^6*+w|F|{CWP8F=wO0M^1GR6)g&S zDVml{_10d+WGA{Y^(19|2vS3}m*jG%^U?lC`J*%m3aQ{l>j2$Us5!S+UjbEtvG$Lg zqs{hyg{$*}^rJRGm%YggZ>M1P$PucaYi95O3*KK- zaSM}V7a}B!YHZJ;Yplcg$HloRC5l{k`m9@0jz?q_tsB8^OGr z^WDfcKYKb^xT~m3%J-E;`?}FAF}t9fC3Bt0c$5SNGscXzXs1+s2j<^xULhzjK{6M35RlI`QR&LdEATMM%QF`bg0Kze$%0F=vn!t}m+G!ojo-;#6 z+>`qPVrjd&kIRbB%?m@fh~7TkFH6yRNsY?fEmw(wVDqAt%#irWibw!}1C1~u zGgbM#d@i#N$5?8nA6gDO+HMZHW=!pG7bF0f-sN{;Pqm@!Lh?_~1U)tnR>o%)FWpQA z*WcrW<&8e_#7txQRy)j*wZ9cW^4|K?VfDh(`*I_>z(?=qb98JkzckMgS&N@QQ{!4& z59YCM>lt}#EOu<|?wq5?_fOeHyxZqz2X^=E{Z-}i#`>fc^`2K*lKALi-uY=NAY#L- z&uimw9=Qlo)J?8hn&|;K%nBp4pAXal4(YH?M{GFMA23%?Ft#uj8`Q zJMFrIe=i=zMWd4Xwj0!+H8WZg|9E^M698zlAcQyBY*@HLOwd3Nj(ogMVo8Banwv{2 zxf%nK_u4Yf1f1Kvw$H8cXfHv|iQ`Ipp7&?bVH1A(F7o|kbz0hZUVJr4$igKa2{|NQ?R_hbP9Q?jD{$=CtEia z_zjcHH5koC&%+r|7iX{LUlW%)cn)2yZ3-)RSy{Lrbey>CJe*g?gPcd`N}{5#PTYU! zUXTs6_47XFX5D2Q5z}1MmRrIN%j<)W9Q#@-w5=xE=z2Xm*eBXg-z)61!FPPzm&YF& znRbPb3)$-U&f~J`n|bSlrpsWYHO8$Mzbsd=zvvCh@UqVVn|w%gfWl&|Iaq%^>mz@8 zp5Ex@IIuN{SDLe8A-W<$vMTv|F|CIgO7m|0^Tca?yjb3t7O zcay2le5H$*Lqn~XpAs+n=)fOrQ8q>I2fh7$lb#P=0tV6cL${s7`XU|I``_tP0AisR6t1mhr zNCOm<|qB-9NTpLHYu>4o@rMxU$a)|2Z>#=ta%{jmD65XT&fH$GU?rr`&HU9 z4Yg@jVQ*IMuSk@5fpXCQsqCx6s_53Y5v36XM358}DGBLDC8ay1r8}fyO9)b%?(PQZ z4(Scj-QBU-bnkEQ_?~m#_+8ie<{vKhteLfDttam1ex6w)19!r{+#&)t&@dl)N266J zF1YweOUK{-Vejclh@H`P(8opV!zw)X$aVUKlmilN+fDo>+~_qr0XIm+E+#-?a9PkL zRuMvVP3&B|gZ!Hk^hGRao>6S^OKUkcH0$fq-yxOTSdDl{!KT|#m2n^tn-U@V)f)6l zUy9VnsZ~^e?Ur6#re%+n4y%d9@{@7WmoFyG`Tth zVMKfWDxzOpty=#x-BDkX4~yUE(V8^*1vJ8SVo^j)rFvT))tXE_1(FrRG_CiTd$M!N zpP?2V&ylFPId|_>l?guEIP8m(6c1a)2E2^z0H(u3PM2wX8SyZ5j4sLACp92gvm@|L9>h^Cn(qv>%>mC-b|WtW~B!A2p}z5i7$J6XK&C_qiIafj62| zfY;1jJVYry^}AP3j2J&!#UHptyYyEk!0Vm*PU(5Tz^?7hQ%ZI~P|E0{0#q^27B>p~ zR6B)*MZ_rdWW!C6z>Lj&l~?uT1Vz)Q0EFRb!k@pNj)XoTQBak14(r+GaOB%jKk2(= zfm(7gdF;<@Fw6qDCvG7CCymf(ZmAJQNxSlx)7)aOV8{0ZH0Kf9eLp|ZPPwQOP^v0( zVv&gzv|IV$_;lq)jLEZ2pg$>GwMTtTO>}88v!_-~+2P6Id%SlzOeY~{-Na!n@(cMF zYJm957d1r1eO1e&RvPSyoRN^id~UrOBK|Uk&$nZQxkzSaIGx5AhqJZZeauTd{DWVL z?9=?@l&q#Ktnc3=2YS9SpawxHdr*$br&`e0(`=;;r416!o*+nynT>_5nn~X# zn{XH`XRS(?|6E_7JVEcd?aiSmMv=Uk&qR!B%h)ochw#xBneD{VOQu-xpF=5;WfOwW zRMkWXU!L20A2{~$nS}hp$0m)2SaD@(`OZ&?E8jINDrt^2xDB>{e)p9Ah@Vx;Dal=q z5&&RrHI<|t*lx$9rOYKFp#sYoIhe^6O&Vvg;FSpx2XaPufcQ<##7k_7I4?hfMzhhr zUKE)p<4|f3;jhXbomt}gU1Ws6Ff3GI@j!y?ooEk#?EBculPrXBEbXT%OwQe_4;A0P z3F>~zVZ9qa@2#z?Ew{C-&?YTpKQH8g*ZK9|>Z<_MrrPM22?N2OxgHU5-;hzuBNP-W zZTKP&5*A8Tc6EpVRMZQNMD1iKvaF|BVtE_-mU8Rng_RLSx|(X-U9?JL3AlOjAd4g) z_@q(xP!Ee_B>lR9+7uczt>%2X+_wm@CIm8sO+%L9ElbN(KL>RUjN~ZAarfVW)mf3~ z5m7Xxa2;1maz6zg*?htdK>KipC3%>+l9iGc=M~?^d)`$k#-)k2d!!@t=}DM%5FQ@~Of;;8~6AVTrK9;B)K8f%i!=8LOD0JkdxF9a&$hM$O(= z15zD8nWJwKQh&n%t z-wxmUB5Lnp8Y>&`j7ks3&!1Ag**Pci$(Dv5T|SXsaq;IuwPm_krZ701#?Q!`=3LpM zwAB?)vD;&t%G#Ka(fT+(PFlb{W3Iv&UHt*rtF*PA@W(PCdITWI?7q(d{!J#cz~?#% zrvv((seYzE7WgXd(oFrdkHKXo_P4@V`g4fsH}=}*zETd+OfDYtq(Txt^CEzHQlv1E ze6{?N$W+cp&nO4r3F%@mexV21H&hY*m2O7H8-l})N5XG}>633pr!9-c$?H9T*mL|D z*W%u#px@WyaRL$0H@bK=)#{HJ&h9bOzHEmU@KqqO8emg}X3@P8%g~PB7dkI; zj}(ggACvqoyvGB`=M0w}T9}*P+!u@sSlZvx*MH$IOzEi{=r2=baS;$e4;9h_KBdAZ z`bx2HKu*+&F} z^GIP8e~bILd^8r|?h`!Rb%552G#QaupyoIo>F;>OEEBs>p<^B%R~uL9O-Y<@E#vtj zONNu)ksQ~^(!|if=!YWmD{Da1a=%`%MfiBqX1@ZI9)_Q0{-fzD9*Xp)72M^ zyb$e?pWGlUes*DfxyPrJ<<1Srsj9{ifC<3QnTFba%qi1%cJaJ7?#8N=!K@Jeq##+hEqy?%SacHeu;;?GpaOF(K#VIS}wy zGI=e6O&$+l3CpJ)ODWJB?ik}MkhCLX0p*4}6Tw+MYvt-%J+gs_C`txwsCb}9_~jb; zK)FP>dzmM0#nux)fF+#4GbMe1Wu)zz&8a9)XYPo9@hV6Cn~n57y}2}fKCU?hkTh%! zJHFRSgQpJZ$BIzIE$|sYF6hYPP*r6{L4Vh883ZVIWD^mF_gwRz>58x~E%&}Wd1lSa z`{$(oUD8!ZM@+~@cp=`ijn*tae58J6YO36pn0V%Hvt|C1vCnNTCwUKq3_n6Z@yNE(X~e*CupFH`vB}|KV*iXfFj;Y zji2R3kv0bp0kN{BsUcAuK?}PID+L#C*TC_3^%wfWz6e*p@;(DB^Pz;;mUnQG)$<{= zqWBm!W-hj>m$A}oMN&U>d|CfNoe>{^{eT?>{*>Q0^lbYAe6_aZkNOf9xo8QR(%1KD ztN&A9!U5EmkgovsC0q9~k-g&pLyAyA_D8wO|Ftl86(6T@lU3!sHgAR25+zUjzg~_z z%IGo?2L07f0Yf#uhuGET{kM7w6En}Y=^xCcw1xJj>T3-@fjXRbH1yxj95Z&HwPQG1zAw(NBLj*B4@i zA?Z@D*R5Wki*wD5-2TF3c4kI~s{_1VFwKJX<^_$=_?$0H3H09O7?iaZkAv17-&!&< z|D^?X00g0bsbG0#-J&Ov+;dm- z@OrEJH?K`TCMh<3H$CT8H(JT*;y*v^x~@~%OBZ34^q6^Wj#{KcH2ZP3rq1D*I6M}f=#ekl_#=JCS0vtuT4Iml44cle>}dhs;PjlBekXjiXL z!DxOAI&!fan5ULvE=L7ZO@d(N3(TtX@j#ZDh{vM6r`+-8=uS@5=0B*<|4kO-BI9s= za}?m=GrZ05Z+s%VG;Y-|qh59+4DaON=daru(N~DJT(CPfH9V15x7hIPEj$fu6d2H~ z3>2?B)41(h(x#%Hzl9Z-M^?&1>aek(d=7hk^i;qE*DPgZKtV7;%LRj5HHB^#N_gaQ zQ|%gO5sT4Su~id%jK#q1Bcy>(6xihP;vW9+I`@_F{x&%(qqVGh(@x8pTFJg@+3gJJ z6{DIjwngG$zbMautdbU7h5B)1h0#QVBu+xZr>kt45Uca^O^n;%wZ0Pfstv@Ww-}9n zCB(cpe22UaKXSmO8RvUdGOQ1<%0msge?|rfS4ig6dn{{QLy8E?90rEWN-XC^m$;p_ z4aN3ZFFZ>;HWtmGq(uBzZBVebj0(o{upgfPs1W~L(j~IYj|6^mBw&(w-Jd?uJnwkz zqB@WH%9^tsmBvv7O6eM+S&oX|`4JO%X3F9Le?(D}5mWW<-EyBprQYupcM~?})2E25 z%??`dC)Pwvpl(j8_{1|4a2ycu(%K$c?{;>kBH3>9&};$jF*xez8lu{~H*=&#gSVS5 zBD=S6g*?}65$wLket2Ar-!hm6}Ne_4FP^<_v7Qu9xUoyuPP$u?I0bi+w|=u_y3 zm(sax07ce55hBWsTZ@$`;REidih&@RCT&nblq`gwuBt8Py>61ZjY`|PzAK3nib zmOE>HgZq7XW>>{=Rxa+^gNJN`|6G&i4^Y(Wg|l2sSw(##^Cts|l3xL;jEK6yb=YVX zIl3Je>4eUatmZh8+WPT0hShVYY{^u%t%VyZOIa-VURVKU*QDGIKPRuEBs4_|%m4K2E4+)ne`ni>u%t{deFio!Sg><8rWoU0Tf;zr^@QPxpd`EvL%kK}Kq!IxX}` zl5BQyM1JBg`|Du!7uT?_us9g2s>opdNr@3(#QIygA!ZWnN2g2(9~C+YWpFDhII<^( zIx1_Lz8*xx38NpMm6vo`L+lcgUZgfv6~1UT`F?;Z)yH!+N)^Q@Nu2<1uoB!#WoylS zH1vVTlYn!yZNdV6`ab5%EA9s$7IO(Nxz~<(PPJX$k$4=Qo<;yvsKMkQ6s##x0dJMW8v@D}_>DWF(IQVT4dGX?RD35;D_ zFN=$lTAQ@my4T1Rl-&5C#9ViHNFluG8{1ay!r|mSUhTmi8$vAB_)HVPTnKiQjm?Ym2A1=-Y`Bx;I&iYGc z^(yA`)$sPIxp{VPxrq|QqdZb zumBZ!7_nf;NF-K6g|^TU`VzTEMS@7>jJTYJDaXLWCkch&jI_MeWz|>yLPgQ#=ca(A zg2@Qck?ayN_2|ncd;WrLBF{lX^L$kN~9AD28)mXM4I-uIeYLi}5%1fR<1zgw2y(i-(pYY@#DQa+d zun7AiwrUNc4kUc|ktuaBW05kr)SK}G{QmmwPh=C);0Otu)Pk={W8ithL^?OHD7Q6$ zB`TOTb;2&H1&vhyE-C1nHju z9UyBj{vvCJ(MFafZjG_Y? zfIx_LX9jHlC?gg@1b$TiN7#md&>{vzBnY7yf7Da|3Yh;9BHTkj5J3cH?+eMFG4m}z zNOI|7&egSikrl()K=}f&T%s$;rK`7J)ZPQKhOqLNsrof^P;AS+mXv=Lhu?902tlZK z;>q<#e)YvY;759(>7r-4jrRelvxC>!BfQk2xAN1*XEO#u)*z%PzT+UGBv_T{3nb#%nt-S+Ryqm97Q0GXNwbxcsLN z;CezDY@lNVLK5K4oBo{-TjBTam}TJ5$plWc5CR$DFUiKU&#uXt?gkAUBIv-(1mXPu zUJ#~ycRl^@#{=#|NCS+QNd$OU9l~<~ox61ej-batGr|{lr!fDiPeSBd?7LF)yW<5C z@I0NUz<-$c;V77Q@&HU+7$Pun`gf1&>j}UAYr^l={_a8E0H?lG-O0$WhI)^HmIq&e zGjBESW*a#EvE9FKXo2*%o~P0OHZ2wSe|{E#fCObAB4D;g(`%f+7alLs9w0#cH3Afw zg?Ht)0VuFF-U=c-U;;p#zc0Nzt#f?0G{288L-+qR!e9T3VE-P--G799|Kmj=)Yg-q zVEDi92>j`MPnA%Jm1NF*j?NX$pCw3D94SUaNLj)M+A_4g?d~pIKe2*F#vD;%qrmul zwd)SCn?ClJpVk`O7_ou1$R^xl^8;lMx9cn*7mn?FmJN%u5nhsH=d{6iIAQ$Xv_%ED zr`AK;iY<}cqQJZJuK6+}h)o~87{XH+;hiyN@6J-U{1|e=VHYZ*$e7x#fCxK7qlav@ zT1OIkP<|Tr{(zjEWJKo?ua`bMI*SYjF;>m;?vL%3!9Z+MV+SNrW0GXzSACJ#wdj(C z1;nKlH4ey0@07ze_`?!0iLOj7JKCvUYgq2JZkrtTeJ&d^8jL1$JmyQ-6>zI61WA1R zJr9c>PMusOZ7k#Qd6CFVCczvaIP@6FN;xv&?G?1&_BrZDr9;gsEzD&DOPIzEe-d@D7&3QHc+t1Zt?=n`0Gsi+3r^=H7)=G*<_BWY*F^Z6WC z{)Nvz7J*Ld(L}?v$2A$yDIsi6!EN2&GxWCK&Ju|o$rm)Xftw>tE@~go zbR`$919SH|>}|wsZ*o$|GatOJc6&~j#zwcj?vur?JT_2}pE5H<8zFvc8n3R(R|Nk~ zIme8AVwuS3Xtt*^4w_u%mpV#u+@*-L{~+E&KE>qXEI$xtPW^!LGX>4V)!IyGP>y+- z%SnnRixW=o@=LO<@I)=%Y@6f5Z^y4L{XC;?9R`=ic}p&P`_Hydy4D1v+hWhs@M{f#!>lvm&hT+3^5YzW6BtCYYK z(#5}qI7mgamm02Y1Zv%Ud2&07)j`UwMsq zJ8ojGvFIB$c&xS$L>O9e+8%Dmjc8?j?oVt!9h;9(*s!Xz-daEFV*L5>c)Xa8YQYUu z?dE61Fnzr?(bYMi4r~imQj703YI`vOl1F!3?BuSGZ zfE?tI>e`W+Q>d}dG3C06NoiYk+ei(eQlXo$dQn}AA3^P!$@+P3ol&KsX>Y;|&U`{0 z4#;f4KtNw(0EqK!C*bppa`A%4YWhSN1dn4PWz_IR)hJ|UvKv0fdXTS$`kN9>Ab#45GO!VeDn z2zilbBXnI!J2!N#30YXd%!AZbJ2adORRe0&aNY}*Q}fQkk8OEl2Gh_6Io6d6Vqbl+ z(&SWUna~^3E~0!7nUvvq`|i=03y6+al-d07rp&!Rp$8 z9rWnJYo{NFXL)Y0fDmKP1#-x5znj`PG*wavB#jB1-eXC0Qg0FQ?Xh{`p0C@K?4Mu8 z%Fa;w<05}pl!YLN_9B7na3oC9Om^u~JUk8yCs9s@KA$F!}=1SdLGv=%%@rvD$MD2L*god9w_uiQvfFEQ21* zya3$ha)M%(fePuB&9&Eb%CiCO82!#KatfzF=d-mK6XmW_vU9kcM52uP#Lqn*4U4^7 zOl3U`V}Lx!x+$#{TtoB|!RNuj9hlm{BD(4!s1ITGi_B*Ot4Y)B6kExB4|nYpe-Ff> z_0m6yrKOcEC;P`U+g0b7N@Pa705_{*lNP}%+pjj#5c-5#FOtroYGCXew&u&o*|N3^ zpjvCC9=m>{N_DV;{Kbn#C(Pb?@@BSePpghDh2ebu*{YD@>Q1>{<9CO7uz;SoHjyZr z2pY}2nQOarKt4im9##CXFOo0i#{7#qm^f| z8`T1rhCh!h??eZ^jV6=!b&(upQ^#9>ypD~bVw%?~NYO`2X z8LW*JOYw#}*x(-uB$vY&T1PR5UxFW>qLq&;^oVn@)72UkCcYm%^~A5xp&+Xf{q%6n zLsUjt8xqBeB5G~RS8I!vH~cNc4GWwiEGKzY zrC9v{Yp<(2wT*<|nT-OC#Ep(WIlM(~-0I2N#cFwjC3scA1`F#lCb}~5CPaXT$>DOx z+<7&U4GwA4ulYJ2fcvrLOawr+mog4G%;7HJ8rwbX-97yD zh0vHf159)=!nZpTN3Fw^H@;2_5G1e(27BWbglue1TQ2N5gd7J{RK}wYD;}-sqJ+y+ z4R%z1=|2Q)bmBFPfDuOesPneX2yy|qkspSJnq`MCY&%XpH@CIkau`-pCw~1l5yjN7 zCheJ_bS0SiBv`G~a%SpiVx8VKq&XRG8ZYiT>3ER7lEVn^c=>gKy!H>dsbBv3ZJspw z4fZPCs`2VrNL<#MvLvtLQk}kal!{_s$pjpDS+G-5`)cU)reYd+0U;_BGbPh)_h~(e zIQ=T3s0WlLQd72U4`1nO&y!rse(R#IX+&Y&YTG!9&0;A*J3wD#Uvm1Z3Pon-981~w zo0@R>NW2`z2snU{(#w!tMaK9|;yyT!jv^&cC!B!yCAcU^v6QC#R|Wts14fDTVX+1% zjWv-1JpU1al|xBvdzqdGU8IYg>80_+>aG?ZwI}w6Jm{wcKE1>36pyCc7!9Dpa;heU zVcz;7bdMyx$$ri8<^h(oa-ssir#zcqsyMgx^dq*~`W73}LPEf)Ps93b6hd03e*{wJ zdGt5&B;knrZyig;Hr{rnJQzv~l;9`Df_d59iP6}8W6qPJ^U%{^w|zb*-wEDNUoGNxj?TIzALp0$sxHRuvxtNv>7Cffr(Y&3%xoDl=OOd71FBw)4p zEhZ%Ky0%>3@pBt5H=kDTI0C|hlMh1g6wUVM@>9wO=(l*h!C{s^rzGZ`TbcDQTl-ww zo8E8hzND&y%9u3A*+&iEeu@sx7_nF`w&qhv-E5iid3Aj&R{RIV3vL!Y+Zu$7u7qh= zUD2h(4BLCn_%8xKpi`PhQkY*X59YC`01^g-8Q2@e#k1cv16yzsSq~P6QUJ%h7dR){ zJO%fzpy&Y^zMcj`AF0C+c}P)CB4e_hwrkVPRZ#9_+$$4M`IDZ;T?OlEmo=^q; zuz@-aN^9O>O6Tjs&jL4Y-p`rzdWfcv0UZifeN$J14*=F`dt*^)tz9`TfAQJYDjDr*#@?R1a`FyM3TCC zGykv&pIioDFO@CMcPphF8;pbo4J7HDgvU%#Noe;sGhYo%uDu3XV30o3obMfX>*04? zex!J@epSz?d2s_^0SRSy$vjSw420@wa386|Nhh2edEwS8$kCpD&dfd=i@aIZ*(}yS z%8JI$6+p+8%dRA}a*NLdMiYl`z4#T>mRwWz!>quKK3U;tK!@MX&6MggF6zUZyPnEN zwDON&rJP@M9Zk$%9uQw}qOzl)%$<{)&xsCYC)fjWV~?M7b#~ucJfK%?0(T z(0-&z&LXu^Fa!QRDpLK8Uq;=q&}D^Xi_h!S{~AB)YNKR%LqGZZ&(xROmwoW_Szs5U z1HZS`p8l>&^WH|x0Z?v(t&~s)+amFj+~3mrZ(4Og0b+_cZ#b?QGKXyO#M$4H0BAk9Ts{kceh{I-ZhrMZ*}$npg8zwc*kWnTetVPMMeHijQnck%GfvtIdk#)WYF zbV`SwZyY5gpHR2B{fij9yU4hjcbe@&EyN2rMHUDhN)z;hAA#V_%~7llPP`ebu%P9x zkw9`8I+xd6(7fE^4ZaBODMMbv_){?gDT04@ivU6mvBzmby(*L#APB$YSK>d0-HRZX zpxYWuYlFknFZ89#HP#np`7XY7XXI0nDe>KIYA0XM;0-wLecFZvIWMwAFC`V~0Wl-5 ze$sW&XH%+#AZd2BB=hoL+kPTj19jRt0O>drrxp}IAF uuhs7u1k$%4y~T>Iog6rQks>bVEjh*4Djwmd=QovDt@oy_5T171eyN; literal 0 HcmV?d00001 diff --git a/docs/developer-guide/img/gui-builder-css-editor.png b/docs/developer-guide/img/gui-builder-css-editor.png new file mode 100644 index 0000000000000000000000000000000000000000..b652d3f6b5e27f210d8898b07fd12e0e77f951a6 GIT binary patch literal 166486 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 @r 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 literal 0 HcmV?d00001 diff --git a/docs/developer-guide/img/gui-builder-events.png b/docs/developer-guide/img/gui-builder-events.png deleted file mode 100644 index 0ef81c631bb3bdd3922aa1063141e41582516a9b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 66795 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 diff --git a/docs/developer-guide/img/gui-builder-in-place-edit.png b/docs/developer-guide/img/gui-builder-in-place-edit.png deleted file mode 100644 index 9529932c45ab61366be58712fad417169a1a3193..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 20952 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 z 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 diff --git a/docs/developer-guide/img/gui-builder-java-and-gui-files.png b/docs/developer-guide/img/gui-builder-java-and-gui-files.png deleted file mode 100644 index c8a81c400712eb8fb94270bd48e1f922eaa5e189..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 304470 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π+d!`$Epbi0?H;NMV2Bwnyo9Hrk_J| zH=?%>VcJx_4s>1YOtU#hFihCBgOVBQH*O(?6;JFP0i2Kl*$8%@%O#P@C2pL|ZaDpIY`FH3 zl9F>E-k0B<(Bo&ILCx;BR~NH;v7i{glg)9jJ;ueezdc+X>r@=1?C`26&)9OlJz=;z zT*C{ek2sj`9k^fCvh@gE@m$FCW(Y0pmcG^cdb#ahyv4k?x)G0wgk3GaZv?TaA|poK zl$8_;#|^FGbq3es5ZPPbz@^@=cH?L9W4KQCH=Tyr+i)u-07lt=O?z_RAk-%+Zs3zV zW81-Z!5Wl63z?2iuEtYF=S-xN6U!OAq|nxuCk-HkyMlaEMLd2w7C(5ptNq+>X3N!a zuA%g&e&12b$(qORoP6|%S5l5V^u*B+x)UMhKu{aGskQALR_}`2h;N{_o?nr6?(E`O z@>AaYfrHv=`4D^_^2ZcZ~kaE`=XmJ3l5ykTzl5LHMzw<-pz>T`#q;cPLv z`n9sy9cA5vTlZ%@t82+PO;jB1u=Sls>rEy6i787Z`5<|dm(k?%PZ-^e)AeiA3U!bH zxE4^j*b{|GslAGCJSTMt)jI*%VkLr#)xDqiu7W7B&(u4yb~(Kueig!_#W1 zLJLE8sSuO@9>(<~iSc`J>3U4M4Dupq;nyOaM1j&WIsW(-h0CWE)nCK}-#P%DE&MapTT`4JCEDmNEOZQpPH7Vg0Ne zrq9uhY3JvzU0JG5roR!X=3tY;J|8ArAFRxM{Y?FnHVKd&BF(A6RXC&k(wP&J~(;%vDghz7JSy z-(ZW`$-h79pLaV5ExI2^VVr?)0&1ciGNZFaMi*DwG_?O@YaD0!qpYe`xiqbueHC*^ z)KT6Uc>K|u%IcE2E`?-of9C2Efp(xVVLh16^waSxe|~R)oUF14Nf-0~Bkdi-E9;hi z?>p*FI<}LJZM$RJX2-T|c5K_WZL?$BcHY%}WAFXE=XuUK*ZH)rtd+Uu95ri=IjZVE zeicBt*%IU#1Vnd>@PN4O7TKR9t50hR&>Xo@Jo(gJNEAYb_HBkZW#@{PDl1OlVAcPi zuPWqH3bk-swMh5&t;5~~)5mIomMS*#5j1<4+K#H8cyAM1wO*a6%uld^$OMSw<`1af z2`+FBs?#LM#s@`P>!WU%yXi#CGlRidc1{>l4R?CKA5GxbhyKu}@YnX3N$Q@Vo3y<=S_1Tw)T7cCH=Eog) zi}Sc)&4z!g);kKs=gDZR03~BdLxoVeU_Xi|nN%Q|5>G#2ulb3>x66vytH~>?%_p$k zD*Il}Q<^47kJW@}n*j9z`v(BwbRb)Nl6-6f8Qd%i0u4?W_7DZZRm0CPX%DGxwTAHT z?iX3>;G|+;7L48U__s6pnp1)I#{07kbR9H84O)Tu2Jm?loc`h4bO4}QOq<;Vk>r^W z>^+=76TB9%<2bZm)lqE(w?*JUp$;wfyRQ_gzl>w~?p_iZYc(o_6sE#SlD_)GjBS(B z{hqbb>58X}nV3ls5H`UZlbfgs35#ch74!F%i|J1f?}kN#FIp6%UtHal@SCPHoEYglbry0_Hd8Mpan){a5}OB; zEVbQKMPvCZBkycR-VIqJhOobzGH$;~n5vQA8FN{~`7Vy*iC4&#ZTtv)XK}PyzrRem zyWbF@4TLd==3me<`ioShOztz2sr;5qt&y7qF>{genkV=5;yeAKsyz<& z-{_PS8GPtge-HvR8kuwEYk8s4NJd@~ZTwglP=jaI+{(IJp|eXTz=Fdq#mgUJWR1@< zTBb&VD14n5s|!}$!EwAvo6Hl6akh$o?DSX^Mhf_5@*?wmw{4v0@}=@zF(6rc$MRf~TKi8ZDXV_0d>lN3ZuK$axT;s6^O6BO|A z{zuR$%w&Y9{#90{=DfYn*5PU~BGnhr9CqmbB?Tu6G7!1IjS!Ae?y4}Y{H3ZJR@d-L zo`K?q#(d#w`>koy+8wtIU(tCrqn|oZXMsND2l`+TQD7^~Tf0qCB3a$3_4J5lPA2*^ zM7pG|{^&z@-|8Fx>{-x-%K4bEJ>ueX=c$zBTglr=? z?s0iq+j$KT#f?}>(7mf`Lca=g;v!)106AK-na&lNa*Vh@?^Uh56`aC`ypbOr>68*` z)eKBDuu*w?O8muG3kv?lSOcB;H^$nJ-8H}F$dKb3tLPd<_|SQ>``*5m$X^XS%`l}- zm+};4QwYf8FiLr=PzvuD2K|4Azts{k)-&(=k> z-Ah5y1}V7JRvC6ANEk(?RVBjm7B#m#lzgD-2;Muk-8DASIqfN7ab*Pf0@pa%go@2O zf`q(17wdllfYkJV-?jC(6(#Lv7`A}jkHdD{`nAKR=c(aY+NOLDyGZuBK}7&bZGLv6 zm$?4;TOYTwXN2A{F%2ii zj-NDfU&B!#rq>Hn!MZcvhUDuT#*~>cPd$eHV6|gm1L@Uk1+)~8nxAmXzoFD@Av9iT z*gh_VIt5Co*HboJJHP48R+T=3bur$n*C1LsPI%lF9Z<^ulD}>8DKQyHC%f4JGx}XTW@X4=7Ms zU}{ZNm>kj?pj}>g%uV2>4V4RIPGyBDG5I_749$t1<`J0N8?i`VB?b&jEq^mnUG{^Yt@Q7qi3>VwXvM92B|W`L>?EG@D!?ec zH>%@7io4`?Ha$Ls91~ENmF@l>0S>h*8~3q!A@wf)T#(Yb5q>~i@N77TPejDg5FCZzYmu9YL~$MUT8+z4GNXeOudEA>8Q4*?LW$;#vKc8(l_;w?`zP>(!$ zZ;9q!UHZMv#dA&wGzQbLUISl%H~L0=CGU|Vctki}26LQ9PfivU?^jvZl`Yu9c5Ho^ z7dp8D&?S`vJ}n1)WiK9GS;7wiBW?hO%IOu;RJb9{Ol$^lxDdjR)kA)Z)sPir-gM=F zup@Gp#6m`^8%C969li*?cqcmQz32{mu5#u#N;4QWOmrVmz!K0>f;ma0d#jYAnl}4d z9q4?B>rVxYm#+f9=q}f}f6kYA5rgtoR2(n&{%h0gH*iKZ;M<0Jnt)mZECHqYKOE3w zjhrZ~x6x<@wjXdUw7P3KvFsyx?U3(JNAblo2$_Dq<$kqjoTO(}A7(3zkE7FN&2Z82 z59-8EJu7oV#IeDtdO%x(Nlh>)de6B3Uf;|6VrhN#CJ(P9Ej~o`^g?SlfL{|f!#9Xo zu&KQEcJX|CxcUAxOJ~$*NLP3M1KP{=5d)VZZw+#NP;U9B6!d32$LoPnWJmmDLl3L-KY^rp1y`JYI*}d;IBBn(h1$M3vn+5XdK1 zx8bWm$ECA2birFf=quP@L865QmG$i{Vo0Gj`-p=D-$UOBXB8`5n_w`4j(vKprI-*3 zMcMBFp;?;=_+T?vJ!g6|8mtGBI+eud;x+;Sdu*TS`}8H|x19?W=e}8uRYQ)Dqgl_O zquIipbY&ERps}*0?1$jg0b@zD_b{H0huU1W12&JX6K6ll6jp)ND~dcEtM=U^?H)LVLkLEBdQL44>ctV_3%D7sa^AC` zfGT*Hnaw#jjYrPR`x9KKfyx|eytI`qkufNja~`Z;9^^u)C>odgD^#FPcFs{tx-Ds- z|11o4(MWiISSC*SL);R0fxO&LBhY-o>P+Jm))ztfF>+|0qm6LF%>0X^W;?`F0tBmB zmD~_Na{WroaicUkm3%TKiuhxRJ!B>8x$QL~s6r!iW%s*d+iFDxcLEep9rE=DRM)lF zk64R0DNdI)@YA;!9=d96ie2m#pURJSEI{J(-A*LMHgT4|c&BfW?wO{vDh-S8%*E=< zGj3AgA`Yfc(jOG}V{X`9k5lKZAzNu|NuK zCdj74OVMnPt%=iFoyi{bLgB2;ngr-WorLO=LDFj1cMbO}R<`?P11Q~{j&!8#)t_&S zsCvhn$7jmUW=BQiRnqx;JxbWD`;i$Nx`YrE@ES)t-oALFp(=h)T(~oM|&9{?m zZGW=TZ$dQhAj4y;(=JIhYw0X)^c$k4UBLKxogKxRA1# zx0x4d>{nmnY{=FIXXTj@re1;R>%VUpx0lgX!nUECnhU=SZzZz$ya7Ks-wZkU=!QN= z1b*|-N_;++{+TUc2=X6@n(^_kl+yM_Rx829=vLJVwzB;XpMq;~IFr|YL9#;Gy&C+hRU zUJlO30ln3^_0A1qFV95hrz`?i6hzjFC-aB=;|xNZf0mL`0H|84ro%ZjFm+x!+R8LpY3Mytm`MRdjM1|e{oR%GI)Wt+rS9Rz$bJN3Me?7)~-4%Fo!@r9e`8| zsI9WTKQ%oDBS>>JKM`w9w+TMy#PNE+an7u1Gw?jYXzj#vvz%zyGicwHSCFK2gu&A- z4#HSGL9{;MTHJ(gtt|A-#k?AR!F*F-)IGGaY$*bZa}#W=?Bi+{x;j2~#I}Wo5dWxe z)a?OmgSe<$*(xm+Vu|DXvlKja2vH>)>`yG^jqP${)6B1>$~H1A?}5igLVj?CD#^>FpL;h@TM^v|je^_}xdI#(&1x(TM1`dB~6KG5nkzl=o z-ry?b8|W0JE>V8+&~fkqB-=)IrL`DXGRYNOtWo>icUy(1@{~EgIHPQvqKr@Gu95QW zGyx0>N?EitpQBDW>FS^DW9S z%hHep6aBjf+>`-$Tp~(#+CKMff~{-jX9DID!wYrDc?x1Xjdr1LhconJhU!@2~y^Tr)@>&I^j%c0~Zx@tI}-z%{Plz_l3waP2(&AK)4% zo^d`s-rRl@t6$5DW;-XPDhOCmz=V^KNCts!Z#UfG|5!!qP3T4 z5dbQeEoFHwHw1e*SfE{9^1w;te7VNhhn(D|ofm|o=x&W?WHQZu-|28^gsW6^kUwX8 zD)L{{H4IFEx&mW`Wk%q+_dQKcosnP88mOeCaD1X~4x6Q`& zq|r(%S{MvOJ$#F22uZBlpMo}krs?_Doc_GAMRN`MNsSu?9^OYsjc*^&t?)3iDjw-S zTNQTA^LHs}dA|+T8bff=R2@tK$DZRHZPlrZ7h)esEjif`e27693cCnbr2PZ7@_8oy z%AIhWT2x2!R{|HHT>`?BsRT7wG^%#cXQ%3{^k!-zaxEzMI(DedSVGPw-Vr>a^TjH% zsc4yXw_=f6T>tMOR$u_+K$=Y2^_)fjoe1YR@sAhf|B<_9`ir}!_W#6P`#+HBgwNxV zQc;yti_iRH3ZTCqN`DW>9{>!C{h!1TGre;UPzlm0FDUr&LJHdq39}cM;sYa_Q==s4rx)*t?kyC;x$r)E3WJr zGRD*+=*^xz!dL_mvHDv-^B?*hMm@ldWZI??ZgD&UEmNXM$XM*ff)5cv6cziTLv%JI zU<6Y}5U;GLYbk|8j2;G$j#BV_sPI3>{D&=oWcqw*CgzipbpZb3W`6z`~bH9 zv{aN9S&77DKj*!kWzpSJ0m z7wrn2$>CF&qLlh3!pH|)*LW~L%$Mi~uLZ=PF(;+}`b& zAIpU_ErbfSNS%*pj#y*!pRnfFXF@k0sSlU%o0bvI(O=csYEEdsIA3m)ph4jUo6N@$ z5Ak`}avfu&WXqf4k8zqgMQ6>%PjkVVJ}?S=C93D*GnKi(9~b)_Cjug41mJ6BD}pEd zJWOcetbfCkeUkl0T?D|B!T$We#FNp={|iqBaM{%da;m>{kzwB1_>iT_l@;k#0`nXL z_PnKeftWBUM>v#@R|Dvhc!zh*OP6`tP&oYWQcZ}Rw zom$zm_a?F#^-&XMsyS^)&oh@6h0Uvx^L6i*JQP-OqdR#EFhu=i9mJXqxZfcO;>94A$DZ?kg&JNbq)MhuG*2~<8UdTvL%HtkUH}6-Ab5ufdHh(07<$E;m1w62&&hIp~ zbBirtsU06*WaZEK3*m&>ge^V}ynE7}7bO+w`v`Nj4^XNME_xRjYlL!ixEelbQ(6WV zFA^0mMn=Wh^+%FI%NcX>dt72`s=(=11WbLW?ZJC2j;M8DVk*Hie8L zw-ZOZBXP%y8Yq$TA2=!OON00Wg*}RCIzt=bV8VxjligDgu{an}N2hko6D zj@z};%6OS_BQRw`14A&>@If=7UzoOvvpaa~qE?JU^nBZSmWy9C*p=QGaX;UN;tN-V3k(kre>h)?+F<@u zuJDuF!#T8N?z^9)!}54*S9K;4VhArK+*J${fcTSdqlFxS4RY8C9?(x9mFovaN)RHX z=w4t3p#JcR)l1|spz{_VnR)#`r@m9r4LTHKu+t1a2S{g zo>~&<6K)cqNUy*$qL<1E9&8_=?Q4t_E-LYzuIGxAaT7cp>lSz#=ffJtF&gBOR47Q6 zMu)VOP8VQ)N~WELaUH1I7YT-3EbHJ@yK1Y7-`#XxV7n&J^<`TPubM(*4ExGJ!^0~K zGix`ompfumz+E$I_hJ`q2C46ihM?B(vO3xCZ@7}H_UJ)pY-x%w zW@-to^E)RyCF<{001NP^!OjfgW%nMcHEjhWir$|)g*Fv+z??tzp`e89-GOs_H7>Bd z?hem_e9I77S_2D+?h5LbuHgz8~b)@fEH!P!hw~?5+fnm}2?7 zD5}U*G$13vL)Ypv56XH^uxk>1u@f%}hv$Xg@*HhKe!wgzr?xJ5NM;=X$nkS>K(kIB zAy}`UQAG-Qza16t)SZ}%7kXvKmUi^TXrB7OFL};*lboMcaD%*{kPqSA7?$J;Kn*|_ zSFa|iMe^Lra@m{|L5*qHgjZZ!pK7ozH`sEupS&<=+`iZ`tAzy*`_(DYkusyRta`Cf zTFus~4LNjOm9v^0g$ghyUPHJnwGluLgJ9_;O9Q3-Y*s~{aJM5A{8M>df%I#kNVZ`> z;1W4Er;wMQpPkjk#9Au42%vwoK*N{SNkxEt#TjG^9QYJD)r|plu|J>&|Dv>whe+e4 ze?o}ktvr+5PtqWjQytI#y1j*`UF$`B-`HnS?Gy8ii>RI>uWzX@Bv#}ybreeLHe%ft zgX4Yv(B`jxx3QBi&E-J><-Q5h_NL-xP?-_w=JfzH(aKWUx4Gri*~iasyea0h^0L(c z?|jl0lJK$??&7A!XQqg5r#N$BcR3t9{Ro?EL8d5QZN5PeT%AZZtm5CveGAM+-~?^k zT!63!DgCxb^LX|HRGI%Ct+DYmJ@M*vOp)6~)~|3oJrS2b(R#Am~fYeNzGicDMmA$EAM^kC2=m!DIDk!u|`(<7Vv^}RuzAtHmDbv+vr!#=k! zl~*(LtkX5OcZH7GhuK)#JJz$nH0lxdwd2-X;zua@C_|gwWvTGS;Entz{JFC*$F)k- zcWw^^rPnPNTuY0=51@+?Bl64@~_6_uPBkYcJ0M zhBx>0;%9rAKQ*xHpi6D{z<3W`IxP?Ra{e|%P)?&n}Z(f8y)*UYVBE-zfv+lO6lt$G^zOvg6= zu+d#|+n>btQM4@cauu~XVDN#6zV2k(sbmk3AqIYf5d9`b;i-Lzxq*yCgj5~MYkP-w zV`}E#$h5^r->|C5lJe9^xw7YHOG(kcpF5;JVUk)*(}OXVA9A2eIo|=~wE7ON#Vvr} zhUdLkJ@&SCUL^+s@Ta2bJb?8dwkoNfH~~tE^Xixl1-5wzpkZAJy=)pqts>%Q%dk;v zdUg8hsXh~p!6NDs`LOxtUZfIe@(1_#n5d$@2#d5Q+JQAs4$E7VcvksDKUkbjn9S2D z$7&FD-HjeAh%CztAl|KfxeYu*JHT=jLCGO$lo08s#7L(|&JSSX>` zq1v1WfB^$xaQFpk%V!<8snn^$>TP7$cMTTHNYza1t&!%*B~n^$>O#u&O}J;SX}lm2 z$6M^YQ>#G^ax_{N@5@og)ZQA-aOsZCp4X8`W(I>@bK=@=6CkQEE@BN?JHDp0JW`0x%<%tug@a?Txxb? zLZQ))+^cJ|JUM6FjcWm=+E7bO+GZ}?MX6^o70^&`U9>#mHRn+}dNBMG(Y8+tK(s9n z4_WDo{#LAu;hniM!;AL!7({+01lSAb`?F1X9c+ z6ypc13Y5d2%`ffpX|VeUX;B`XyUbn&_za;iKKpG;#C#ij*Pc)$I)wF2dh+a=*M^|+ zfO+TeP*`(9Y;N4yY(h*;>+$1fD@<2OXxUqP%sPnA6hGfZ`$LWjoGfo&jiY9DmYM3? zVoPgFMeBV7c`)=TG^7|jD1z$+C^$BsE6v5;+rjF+sVguJKv$CmvbYOz%s8ihb;?D3j{NiZ0YC9w_uNx ziR8pQlOAJ-Vc$MBj)lE6rNMG zZq;@gYm#lZTIh?aA{x9?jMQ2-=eQboI-LXwZaX!-#Yg0Z@b&)s6?~2e_`MnP>Pc-u zOqM?iVsX3T09K;?&T_24wf4gu zIh*6JO z-Q?!=Uy>aS3ch3RrM1t*nj5c~fwxq3{_$l+`QjcQ2fY@%w6PSKl8ZJ^nL&_2L-I%G zZ;OBfA0Prxu=LyyS_*z@l z*zv|8)DZENo)XTc*Q)RHhy8gHs?*zw^VLGPF@lcJHvvqI_ruhr?v)kUA+`Gcua;s2 zpSN1JN(yGOKi>RvsUHHy7d$<9M2qz=JQ#$Sb0M>y! z@(~RVgDG%>3tj4vzkF`vDV{l6hQ*?h*}?G=AAEz%oqyRY;h`go9t_IaYDgCe;C-Z>J)RN5x*`QLKF(61W@#1)J%Tb0{y=ZkGnU_&e5#tr zrFHEy4oC9vJ(&O;mwUrUiBS z?Dd~oZ`M$q%D`GLTLV>ZH+hZGb0+9Y7U;-`z`=n5Mwm*1n=%BAC?1_z9Ps|*MdEm) z(<@(dkNyro%lld9r$Pvz<;|)SCps_pw)WD==1DBp5xl72Zxk@{5?=??FR_>TN4^ql zv4`ebW_ZbBTznd=S>i#Q4El)#;G@l(UmGdB498kLD%@UO3L>p!bJS-Aj#3|$3da+? z@S}M+xDlO$L(4q(kiZCk9T;m96=hLH)!D;25Wpr;+QOFXJ)AKkA?5a4qM41{Q&(VX zFuUxEKZ|QR!4)qoPP*ARxLmcH+?pg%==}CXa*t*(rzov;u{Rp>Vlxf$fT`D^ynRc)MAl;IvoUXnXE0bA`{^JQ=go! znS040E1P>Kv0y zPnkxr;lY83pfxr2HpAR|Y>4hGZplsMLrJM-#UF^g-{9atCqO_$zt~2ns;?JzN%qFE z6q0tDQ(r@R&`dE3Bxx*gVCX&MNk^dn(jOX4;_MA>pRo5BFD5+$Ab!hQ5NA_{#D%eC zkKOXj$`e6M(T~Di?(i?lz0cknA{vAmPfp(_=k0oL zJ3Z-Vaa4?1Qg^iD95R8Lj2hXNLO8ypdhweiVmml~#i6x8_QetaE%cnZ%_sRi!yw{f zaH4lD1dH0pbVe3W*`5lky~2iYJV&Y=IWB^*F?i6yCW9*ntOY@8ZzPZ2@5R{j>#9Gz ztBpta`|d%@!(wzbt~d(KU}U6(y9J*;&yqUa=z(_HXUWRfY+zoUx)r zQJiM+@%3GE9D#c4wI{kipV1w6H6ri?e8~*0!~Eh4T-RCp4?Hg8hz@?cp2BCK*kYvo ze&qfy~izvM>6M zyE-)RkF9c2_yi=V4X+4DE9MyRTW{f16k!8h@6${ChHfQntJd}qdDz^VW1Bs04-cZ= zO%3X6&Nd@@W16-yrH1`Qn3Vz)W@q3CwQQeD_dUb8QhM#gUj>_%2P;g!duAUlWrO@C z2;zi9V;^y&b^D^cQTK-DNHfk*;HEm}XN&VJ(hF@}6Mzk?R7()-yT_R>Tzt`370i!p zt39iFa_Xsa469C(fVX2V>pzNCCVwxcWQ|l*pXXhfc0_9V;ev1RO2P~9K2V<<3?OFR zb;bv-vYR<(hvs7(KqC$ai^quQb$uyTmO^VXJpt@)Kbr&W-ihOVXrZ0?@VWEx#*_%r zD;^Z!({Q)GVED(UgG>)C)`R)PUdzQ#srY+BNft2)3ZJ5^IdR>{yg#$_AiHY9r9RGIglmLQVF6d90O5fSIh z&Xm$To-~{!NtHZOqWC}5!*d^Q`8iTv-a zmxet?5Tk7`O;YfwEuXPH(jIO57F6+PX;YGJaF|UZlnWj5LCV#%S`fPfYPwejm&Mz3 zgq1>;GPA^4+U%5Gha>EL!%vH~R%RtC95aLU-`z~{lk-Y{VF@=)yo5T7mWK}#BdH+w zb<_7jSK&Dj8l{f-!qnH%5|Z_3UuVQMTX&JIxSY}O7d;AfRO9*$7Zh+*vD(GC} z1pH{zSxkPSzXe7JJuHPXb~$U*93zuU1;aGUUc@#=PLx5w7pChljh!c@ZeBUKLf;>kC(cQVU<=v zYnBny1`%EzV~VsseR*)7iG%I?5xqhgLh^pd#Es2zENJPC6TL?tcEmcn_D2CZ%J&Hq zM>kP^=yr-zN>4l1-1ODzzO_9|v_m}JI*{&L4@cqSxtHsHa=Mtq6S6wvg-H6=5yWf- z(KW44ViNjueP~CVilz@19ipuvm6kPtLDl}PtZo;6jA#&gW`v;hT`HvPr)0+up%(T$ zYZJ*0Jay&3YiV^~Oihve>{;AC#hsQM6K6PI;9R&Z6~AOAR0mBRa3pN#o{U+Wqc zAZ1+>5rsD)zn9@_N)?Tp6BI2fHtAy_+A_iJGkZ++w!m{32LSr##ygz(QHg#Q`MRdx zLgS$}1y#0cR_4+wC;xk{2n_rq)d#li-qoH;G4p2qjX=9n@~K<_yNq%?aO*j_eN+>H z?sEXk7Ym|}iREy?bym^PubUu1zf`=3vMKUKJXN&-)l)s@_dK;pHIcH)9yO6yhW#T- z8kib3Ohk$NTZlYZGh#BAxXQ6D(#+9X9wL}fta7MQdkUVfN_6pQjjEQ(??uvLZ#@#K*aIntb8x$IN)`lJsRC+;!2%vT>Q+&MEAD}7K zm2`TCBUS2>*2NBA81D-p+g>^6tI&&ye=jpX12a0Ch?m}jv<@4Tr|J#tss+E%Vv7$K z5E3(&g?^$vNF94_(&^3cKtE(zQkO3{gd4So-OsV-7ay5%#5!-PX-w_Jul>13*Jx&u zQT2d@7L?Z?gzP2#!jPg1o2+AF%$xzPTwm+l^SBtqOjO=|e$@=Qj7W{}BV^%ZshKHJ5Qy*h%uUDHcw$zLZO&W;TPE!&XgQHeAy$r1S}pJ>ZQ3tn36$b04PbA z?XwRzQAxa;iqe4whvD)K;}N%qol%eT0_%mG^2b5VoBDVAzC0hQ`KIk|&4@tZlGdf8 zc749HTIG-2%!MO5!^7_-c2+&F72@yy{Ub{L9>Z)WKYNR%Xhi%WfPKID344d6_~Vcl zPZ&ywE8{*K+Z|;it0Wglp<-8iOfTfn%f63qcV)!0<`KX9@)NZIpFp9c4de*Ar9*J* zz2i+=<{_XC6We-7gLRPgwBF)%N@CGhAM&eh%538uE=o!&N|{{+0W_}HXSh&&l)63R z2Fv|nG{`|?lqBu??6HCP7Q)i)O3N)Ym-B%VcX1;5W*2Q)a$YiqJVvdU%s5km%~)qk z@1yj7f0)5v@GCCm+j|HRv_j9Ei*qwqu9n@AeFAB1rJ0yB!WC!$WJv%qRaZ0rw{)_! z*8BWD9&lif=UX6-lYQ91Nkbh}Sc$LI%m}UUGDBbS1Cl895Lw=<2H-zupfFVT5nHD9 zsfn8}54b}hN~Y~+@xrN4`agQc62v=l8ku-C88jkmP!6LfU?#9wUsqM`J-6~6D zRRl{Ed(k^367O8|42IV3deZUva{gFTL{zkUeSBjlyL4bDU>>y|-b}s8$73`O9ua|@wa@LrWf_swj)}4 zlZUjnG6i#OW?Z`j>;>1I7oFO=es545m-KFU4$v#!mQV31p5#}EZLim(w=SqBvvc=p zOzwk8zj_bL-I@!F*WK_0Sk#%NyaYMPa0I)fUKh!KbhuLWMV#*yaP_y2c%&T?p5?Um z!GAVgew%Szfccu-l)uWlb3kzcSJhC9z3qx=*Vv$H?LdOP`36g(xxN1g_Sc#u|45+csm#zzkz_Q!wg zgGZF`Bn&GQli;g9m8&UcIO-v751!98MO(cyH6`^gd9uY)#$Z5TiKWRz9ZG}^s(E~& zEx>kI?R@~^c=0@XF`}~@(O;5gR5RTExHNip4#eDWQUBR3A#LUKbn`fwf$n@+O8}~c zvF^Id+?H=-ifHY2!Et#Ly94gF(HB$c*{Ziqq5Tk>$Fk>Q&zSuZYuQ8Ml^Gx%E^&v$ zhHur=(;1QTUKFb>{xW)b>)*LEOw>*Ly{w1lLiPKcOtE0%tZh!m^UyZMc_A_gO?&dI zPP?H<``NMXfYY2vT<~v8IRyWKD8Nv07}40dULrF7^8u54<6!Mut_d-GUbfLoJ<8;zdWQL ztMP6B4&cCnBOriWJrcfxr(uI`Z*O;H)?(8`0$}*2{8Ay%&HcC8`o!2bN3oJu;rN&{ zY?+bn6MHpe8PbCc0r_Iskq6U3C)Xqq+!_gcdGg8TC7FUY?*^A7lg-;IYbVgqvJi4}w7u}-pJN|ZY@Y-6?yeGOZ`Yz;2xh{hxCBuDs7tkcUg0cDcOqF%S`#HI zOpqyx@_fOoD`p3C=yN^bz*15}SHiq%G(F8;C&nEW+K#aTaEYF{TFfPzr^$-RAp8q#|!SiNa1O$AKI0%!FEqSZ%u`TkS zTjV`bILXu*4v+(m*Uszm*;x(z)+gig(=>7(>z@Nzh4TwM3b&4{xs2U_qjW4nL|In- zL(b-|#?_=U-?m+|AS*#_zLw{BVz0KV(k91>riPRM>`tE=;7=7Dzj`;jf1tZ`MU|Jw zg1=-?N*|%83Mc@euPtg!@pDc8>*9 zyY{Q89bM36ZV+Ff472VeBjLR`2iisROctT(z;e=X{R-I7ortl&(s| zvaZ`|R0SMB`1vPG?j?uco6&6VPVD7hw%77tr+JEyh~40AA?NECN;HEao-K3Sw7&@J z=(Im<04gxb;Vt6R2w|T~ewX#lpH*13v*F+p zXHyfu-)U#@Qn>DY(T+$JZm=G;DkBkWHerp;_zWqj_XTnkZlMh<4F&Xf zG-+H1-h_6+xNP<#5)yb1zl&q)?rOBRMjxyS5q&p{tTZHz>L&ob7`7(h8KW?`J`m|3 zKwN#48gD|(!ytjgf_S7LP!^pm(Kxo7G|WpFlT5sJs@@z|??Kw*nYM%)n?3?MJuIM> z&ydn_^iPs;E@?BOany-b^#p z^K)e{-=dCvKKY_9m?m?;arxoGu9{>4{_Fko&6nc7J0q?6QewTl$ltD0&)3C6D)7_a zx1or(POSugO5#o-effB<_G^+mjV*WuhL>O%57BOzNeCZ?O*_yV;I}PRo6cskJ2=R) zlXrs;8ptGw`78p+y8Mc<;XpAkfcYqy>NxP|b%F^j|k#e$|;hzO6cRG_o4`+UX-(0(1_qc{O z|5*!K8lwn2%6yBiv7E4N>qXutMGpqVyBX#82OtoCou|hdW(}Ym*G1o;A=akI>vzH@U9s-mP1C zQB8Jxt+VBd+>8v~(;PN{&hOsPKFa#**SsV2&7G3z{Dq_)6|}_v+j8W05q7C}8hXh? zW>YBFddAc45`|7W6g@976QdfLV^seFN&MqA9yv)m`o#3U8Qey^PDaGYq6{>)lF#_1 z$Gs&55n^0_-X3&dL*Y*i=A7a5@^t3BRMYk19R6`ToGY?%J`CuBv_bt@CP|i1YeUp3 zEfl{)JHRC|0vcARmtBce#|tzp;O=0ob4B9-B8dO^ zmXC7S>3_IX-Yg;TH~`KuB2SbEaD_$iz%W4|gR=}*qet{etP-R)Dx1PFp`#SE*n#+WdHemhh3 zbyFZKTw-0u&@V!Shyq?zf;##RbL6mhS?pb*E@>Nx7l@QeP{&dX9_t=zsTk+uhDB9I zXivg@pSD*3cnpPsf?JjpgiN5D7b-qEr5av!Pue8fo<6%7S8Y%r#3bD}_7(nrK3g5ZG4-zK9a0SYEVJrz721m zavZ>;5Z1FPKhQkY>(EkR3doZ41j2@x*JzJoAmMPwaXyZT4Y1cj&dAWKJ_+SMcw2%m ze9miu*DjPBxt7MWd9pQM^FjjS+>ANs4PLrOV>KszCT1+HDqr4KoqrifAL43(7Edhe zdyoA&C&=>{YiJisTs~#C1b%CZD67j;nqeY*T~N;|fBN#0J6g@=J8dUJBXI4XpR(1r z@Li?O6hsO+3ETvDwGyHQ8Y@V&mxJ66O?z969!95kVwP5MDx+7IUuE?C5FPbQ++SIZ z+C#W8&tSG~Xpu<;9s}Nw`RWV~{V=_9agR+|AV}+Q9e+-|qtbshcFxeY!8SeGW^KBB z;ykr-fl%^yO!>_FZJqe!vvM>!xB{jRe$Ef(!oBE%7?A$HKc2|U;n>cJ>xnYl7<~HF zBv5S6Em+ue@?Yi12Mv(S*po#Fio-|cc;$5P%Zc#ggB5FHYl-FbLT~1&|k@L zzRY;%uz=$wAj#}T{hpEzz<^gKIy>ob-QTHv{CTW_6jqS#^(b%?a+iYxGjcvQJ z8{62ivE#Gbe!qLqbI*70bN}DT-tSuTU31Pc#~Pzz(tFqEO!J8lt%7s!lqh2!9fjaz zTpVYq{v*}1ZZH?!nIOB+`dh743|Kvig(==bAs?)#1@^iF*262~5Bb%9}^DLIit}lpZ zy~ernWJ$vh-c>eNUBx&L!?ODgr+n5-L8NzuB{8&6CrLTp>+cJWh4Uw^w`daKmf1pd zJ)d)ZBO_ZZOM}Pd)EiBtXpG-h=NK~yV(E#2B4(;6_$h?lN3kMN5iA5_6TT7`82W7| zLa*@gbAkk#hS)uE(QwYFLtEI)DQJTCMfQET4IV9h9zxCov#{PA_ew#cXk90m8#`^X z7Bd^--|^yq1LnUo%f;Y3iO#NYECU8qkZLrx9`K|sh;KAEjl^0)Gv`v#U!4~C6jLy@ z!M_#d+gswIJljNhWQ7^8&trh_9ms06R*vM{ zlwW?efG&M`dD~^PUIv#v-)g&Nb{*ebt9NIf{~8M9JzVF+D89EI>{2}Llw1Y!Y`{SE z;UaTk>{}q&FEPd*2~a-9nV?uUwtnK1Acrfx_g=rRq$=GDp#>Mob(SMVQnisEmYF?j zbbIvmce|Zr(D^2PZcc_du%aP>zQ#E~Ru~EkeJepj*ZYX8EP1#nv6B?U-nkLSqPu&B z1Y;t7UPRX0SViHD+mwwJ_PrKWQL;%_R7q~;+J9|ZU%mMGDZUAn_)3r?1};kos6Wvkw&EoG0v_9 zHYVWw(Ys6yt+RvlZmCL}k-ojkca2~q{ds71;03Le*pN0(ZZIT;^q-@C5(Wak`W5L0 z?X1AEbfw;V_5tQQq>=>Gu^ZwWqhoBDKZ6-Y8(o&<3cYAjhp}$;FTZ7URDfmkHr87=7!M*Xk#ZbMxiTPNs!d389Jf%>+Gs4o?^T4IGC zRo8+fJqAm%Ft`xSJ;ip9nUr^r=Z?Q+Vj;(<@tN}7y{X(ClGF0HskBVpdeRq`Hy>!a z*WoltSxKz;%?9-zvbufUA17d{v{*elOKd+6?%HlX&k^oWYj_s}^qAKv^jImf zjbo?(*yj~5S|}#P8TwtZU1e#miY09Dq4HNiJjI`+%MUJ^dyp>$-c5g$O(2H{78e(X z!E7eyzMNEqfX9tNoqsNvw$qT|>Tk#=S;;U=H zDIP%7_%yL+ManO) zIFG|Jszbt}Ks_loF_K0Nv$5|j7G9pxB53zHcG1HB;s#rGIo2qZc2L(XB0*`!W1(Rz zsHGsIsL+rz)SHYO4!1FYgz=n{H8d)63O4b)$ZI_{5=TUzQ9pq$P;r%xB1 z(e2?b!>@xYu6`Hv0@i%t-<)2r-C?@dR}8;9LD2gxoHd55y6fcG%iD1?3MLFgUIJj| z_6nM95JPW!0I*$=Je$=g3Uo`)m_LniAiB5vTaZMb~e z(PECc<-W*A{Vq+s?OON28q(#8v2*8Ie?nao5UVghs99x^ci^US(Zzw2wp90s(kKDm zmQOPXZ&B*nd$#1c5_qVVYqO1rqvfnf&qlZ+zA@sBRTX!*#5k@R=P=92m<1zUbW4r$ zyFbud@ye~u6u}pK{sd#KJS-*BugQCwZ->oloV7Q(4)Zhg6EUG$7IqC^nc#@U>u`&U zsd7e@a5rJ7nul0#K|`pi$$n6RTI(J24&+mpXffMsbi%%JlGDHJjQJ-!*I|>cS7Jjf znfQ#SG63&aZOchwPwdv8pi~TeQHa8VZnz`F@JRzu7{j99G&qYXNbQ^^g-~QImrSX7+u^S zF+an*0n%r#p4Jn#9(;(-u@D9KRM`h7uwvMD~C@}_&yz`>U?+!1tM_raUy?E&qXx>;EK?6dRN8j&4^0U(%Ty_H4Dvc=j zzZKj%WQ!(HVC>qaM_pxO(gH%7&le|&rt^YknahjZR35sL_H%j}CN|?574aL2gi~iJ zR%;wpp(pp^ZAmN%(i=ksoAK6vb8{~h0-O=yeg>5j;Vd_O;!=>Aj&9t;W~4{5M&kUb zC;*cqr1g;JU2}aX5_EIQ-4^p5`nt+Yj}F0^LE?U#2~>=s=k~+rv^q7GCCvBf5UloyJvo-J_>e*bN5Hd>% z%X1N1Y^@DObKxy~sJM|yBeTBWylMJ10P`>xQ9Fm$=;tuc>C+t-i+@9wO=`t?ssUdo z2FU9h$v6qFuO9!!Pr4wi$@okb2)GNJzZNoh_15{>P9IZG*xcc_kKL@dxZn)j%UQ%~ zT|HO}W?MAD0(239&K=3@g1;@i91{e!*=(g|k{F3{mkdDYr+ERMtL-ulQUpBWF?(E2 z`nk#Ap3e1YW?JSb9WQp|nJ)t#u$%01$xwD@1DjR=rc-HK;Hg~!E=D3Ukab6N`#K9Z5t?wnAO4X(m7QbhW=@8mW7f`d~b*3I@ z7S#Nhy0LnUb5rloxI=bz<*VECyh%Oi!N|7O;m(xS@)}&t=kxj9Dg8i=9P&wJ%9G$5cY@!v0Fn@t$i5-da)BFL|rvHg}W3#h^_#{gE zo)ZBxAX&xs^wu&d|6lI<_y?Qm(~jp|!2Z7((Wgtb{}|Cu390`W(RD8W!-x(iMdmtK zNQzn-kDAy4GA^Ycjnzztuq3j_Ol-Sd4^DP+WKAujClEy_=U8BmKIxsyz7PJ8nb2>9 z%p3aTy8UWO$67F`{mi-1`VQ6)@l5!wji>5)h+E23nG#w(Y&Eu2dX#!XbSpit>^)NO zg8CO>%KGsVtj)-0aS2?{YCe6hj1MH)InFcmwHYk$#!}b|nEDM$xJc`Wvv|I!^+F0>f2@JTcck0yg5=jYCHg@jYDmuSh)M3Grv6hbZdpj2fqZ0BgGtJWj9p5@Zt zrzr(DmQG0X)ZK4FJo@XI$tDO`?rGlyfOL_% z==!59&rtJeG;705lLK;|06N_q(F2QposRYo&GpwJ>N9Pu4Kot8}G3@n%UK>Rig6oso$8*uc3^CiZ9QKWjKBz@@pQF9-nDc5sn5s41eP5eV&1#@Du{%26wHS3qhD*A>oMh8` zf5TqeJ0T-gWDb@&j$qqZI+~8w35dS`;&v&!!CS8Z+xaO5_LVMM7CK;aMz@y97TUw} z2~Z-fokA<7^cQ<@jPOfh4J+}Yw)@?){Z_!&+<9-+`TRjzu%CpuL0i>>j>K{Cf3yHv zH%ox=oQYCLGVKye#1yyJ9d#)f%JXo|I1Kl@ zd5lF8l@mdRCc4)jF0@=_nfNdJo;Zij#oHOfh50Pul)U9O*#@WQD)IIobf+GnJW+2t z5`f1cxNdH3Wy)IfkO5$*hVNx)59(DzKThoTFWqRSqP?lQdII*3&#jA*x_g`a5pq?H zoV$8WV}Yw!Mn5u=21v^1!!waRYh1>@|I&TM>si4>f4a0cCNdbA;)5q1_J3+_57(0v zFTjO(#^!=rad3^#5li|Cd&~(@Ir7+|=(`Ho7yk(g3=l93?NBe*wdn)%i>NR1Dv>Q~ zcoHOB+qZ)sPR|H#3!7(uTgN~^;ce9bRl{@+vIi$16IHZ*Xo2qXGDW|sVFlQ$!BsOHs zg~171;yl5h$t1>l)^KE&hm){QC5cfPtS}dYW=Nxs)#zMal))Ul---M$kA6-FQAa65 zW0=?#;IepYc|J(&x#|Nhx(!Cst=YZo_@ax@i5?E+jS7pgSERDf4#7^nAqjYYOuHbQ zF6Lbv%6}-G?@J5@gIp$_E2;`1=Ro~~6oPEtLw3mEjdh3O|)SF*J;l#(k zw6V>}8qPx%T`;C4mA$CP9g9b`B)Q<;wG_?G;GskpX|-d8N946db^gu+q0v%|X=YUw>^nuLA4UP%+wAKy&P!sE#b#D; zJ@9RJSqEw;H^h6{AK$1muFGn~q#G%}kYxAElk8IsH~qNnWtkO>0}AuPch;kX;9*9& zu;-=qs>?IFlR~ytuqW?d48f?%^m~3*`&|A+n5Wv2_qwONYJAm`BDlOVcV{8_5A_qIsm;~u5D)21R|$`LZJ9B~Q8f}I8+hvaop`kCR-UAh zu=|g<{<`|awamItkh7RfsVMN%U2H!Tk;E}Qj1c~Oa%RG(wk4LikrEnLBucPf!+EIp zm(=`R*U#&&YqvL53#W!ese_E4j_-&qdREmEz6i-^-V7~A1{Kmw+jGZJVz~3s=h|2c zq-E_-9O%EY$I8yuTZ#h_<8=Gv=j zlwF$F^5qF%F=jzLu?NQ6_163$_9jkmZ!eTBQnYlnR=6ndgIt6|>u_M0XY2`v;?dsk z6vxSxIp?DQp}zwkb}}f4A6FI>md7T9k7{^rmgU7&Srn2yk@*?|Vfi-2k%bioV|n2a zff_)ow?@N>;%fZ?3TK52sEe3o*GM|od7#x!DP1A^3)mQ8t63_7tpwN8cfwq+DE)bX z8J$RjfL;qYIkXP?O|JfRg5iwmW>WH`vCF=Vz+dF;abXiqFHos^~dO z+3arp%b9h@ML8?-<5@q|zw`TP2J7&19ILgGs2qJmQsuE@YYR!DYiDHW7~9I-=Bc^o zT8Zs%LOwyz4zMqBUMe?dyv3Q;REM4O`H~ct@CTnYgMhlc@L99=9qe&H->3|=z%$ko zcUyB?b3f^EXhf^*jpNOU3ig9UDP2_;0B@r|3MY`$hFIcom5_i=3uox;E0B8SI(0cS z#7v8VY+(I5(|6a%V~&!JL} z&<*i|!j8juQa$lSFdEUbmegdYZa<(P;BNNpvhH4!Q%|oW<|th4;0sOo8^5ZbnQ2mT z&s%P~A`nNKj`x2&^~|a&CT}f-+G+$Rm^a{CO(GT~CqFWCjd9g!&sz`?(rC1?FW-|i z1TZgXJYNlyG5DnVv}9NW1o>?2Dn&sg*sq#snB&pmOasSj3>N|_U?RpTik3F#YDwv zF?;8^Tab)cl8}}yp?1R5e(GYlROx(-uuh>IMU2_*%ydjoA5HN9)u_KOze2^B!*8|~ zYDzWxP0F%Ei4)&d@~(UVkhJ#1(f|ZIdG%7OkWeAw-M0I&6?AY z>;y4im$B&!jc=}@far@AcHialRw}&?-S$oWU#$%X0haD6NXB=!tlIK!`Yqe;X>j-c z%>DTi8Bx~4%>ONm@?lz0#STT%=N-3u7O-0A+1PfRS8vB=n{Lap028Zi$iXMd<&n6se<(T9%1b>U?0o z1vR5si;LNbzz1U^&Qe@O&PQtIK}{IW+moNp#UEh0H39p5mhaVkv1}qN%x8;>QA>_@ zEJ+O}%_^gJdP7eazO12zthnjzCfSWG%WGJHkmV2x+j)%*5xRLZF0;LXl&>;bjWQB~ zf0t+OBiM@%#h)PnGXx|&4wTEn1b%eRgdWjBi1fM|b{H$Z{0Kq%7=t>9g!-Z%MGRlD zGSMG@9uxM6#%k>-r=4AI&+z#*@7n+yGJa{OfEb9Z-g7eOyki))H|KoleZ=fmwYyF~ z@0o(R9u)Za`y};ld}yh`lkNUuBiL4_3oift)*-1a>28(j(0iV*#%Wi@YP}88@$CY7 z9JmObe~!Eu!@83l2g;%Ub5SW%8bYdw92OI`nngW);G#>KOSy1%tP{7j*wx^z{07x@ zT#KLtpx6J&5?t<67IP4b{CGx7sP+4U+j|NedqP6Y=^Qxlt&$^{*vRsRG}wX<8J%#u z-(-E)0I^QA{~=kHlsEvd*GF@45H@dI=8$G)6vneULs?axu?u zT3a^x&~|H6oQ1XpVB6@a#zx4?9frg{GjY=E zi*dVKCmG}k$7ku8=;(Ns187nDIN>*UkamjjzeBDl_zYO;R(($RCnbNV(2 z8#Visix;rVnO~V~u=tFf4Zduj1zhDO^gA^S3 zqy1cmUIhM34qRM8_-_Xj9O1_r&$b3HF$LjYANGfOy2|~BdRk5MAL^;OqiYZbQGBR# z_$@x=1(d3kRd`Z#II)dE#FA-z*zxkH*lyU41i?<|7gE0X0n3PR7vxt8k_^rS+Q^v* zHJ-BI-hpOkIFge2jz!)u3`-t7wp-dk$@D>a_2+hbeN$H!`Hj}I(QY1pKrv4>l*AJd z^P9JpkE&N=eoB+o^Hgee^96D$?HZo@bMmtP^4uL9^HeF7M0;n~Vl0B={mq;eUGa!$J2S)sVR&5_nE2@{ z%X)x+f{4L#`&g2q=99|!`=-hRC9NsQ&896~7jwY8R+z1eX@)YiW_d9*UHIjIx1sUEWs9Zy{nQpOiApQ?WkG2NyyBCl(+o^N?AK(t*yA-aWyio!2{Uew#r zv`lPy#^;Od(3gm62=wwPUJFX`Jb2=16uIw-WreAg>Ww)ttrYdpmKAnPbij^-{f7>J z1-sRt{Bt~LA?k^4yl|`%*4ID@?>+K`WOnYy3yx}Wn#?fd@GhfF#IWE&-% zsz-oR%~V&f_2^Vz_8Xai+tsy4&1?+3z0S~3LPN5kL{7TBOy$Jltz31o2`T`Ri0%JA z)-lY-i20*7_E2U0PJs`dPJBNoz@63+9>5UKhV<@FDfVqj&vi6-BCx%gOrp>7%oaTM zKQ|A|t(xYkB3pkFruw?%z3NL9fowYb%Jee0-$b_VUneRJ5VYu|3o6`*{$F}LXbiD)Ne zD>>xiTSZnNGqS0Ao3Vx**ovsq8H-=mbadCzM=eHzjR)_-IXuJa!QJLa1~F>7no)5m zYsp3XXC?fHY$_pxz4?0jLfOcvmF=tDtDcF3BTg?vJ?j7hERf0j~k((~MyGgD#HGc#g+wJTy-}zT zQtIxX+M)wz^eimO98Q0MdE1Kw}?GtBA)T$&XgkD?qe@rtEFgjo_yoq*9jyKpd zD%!5fW`O!q+WP*E3EiMa&1L!P-ACz)O~~6<)Ri zAa#-l)ID}zBm+LZeGe}QSkwCCxN%Jy>5_DVYN5E8!Yz*cr|r(oBalmXgnmZI`A(1) zfFAd#A$HSbwm7~@=%D<3LcmlyJ0wd;tn0jngMk;}QkmbWQ?kClY_2sb=R$Ckk2@7W z)aS(+?ovHVKXr^X6<)>2cf%}Gyx%7PEk?qOBEo9xpK51o{Y~>+rsHiVMGw3y)Mu97 z)1f)M|7m>o!m=vs-oJi7e-0~31tdzX;&QB3E*F{b8J?h${e%93!5$Jx>etPxQ3qvM z2feH2-Tx^b3YQxy{)Q(pEo#3gDxk2-NHSDghby>*%d{Z zW%(@KDZOVkBD31AeoA_sP{4W5>QX_>7$&`0A;t7KJxTw6QApREGDa8@0>!>DF^Dy^ zVsYG%nuHOZ4uzp_ohp(3Fp{ybk$35~G;>44eL;NpviP@xvdlz3K6|nnf&2 z2sPSk9ZYpzUu_XtgF=Zt`(p8;~Qw1om)Q2X|NiCBWRV3$5yHtcdK4^S%5{xKA zxsw+e*ji&hyr0GuNBRq&AcTNO3%)IywCHPJ5%qj!IyIRI0VT^Il~fXiPz(Z+L(wUn zlki|KC$?RO>f)4LrDAI2Qm)6xINwhzUoNbk;}xC(&$ibwbh~^x)xTzh?yK{ve0Y5} zzqwVnKULBF!)pJ|8MmXTLT?D)`%NqURLg!Z~Bx+oa}!#G7yp9|5uJ@FM)1vR8<(guV&DTeoh{{AmF z(7!%{-`t1t(SY&?ew6n_z7RU_^e0{Azuw4)n4o{=(c5?b_;S^WpML>^L;Dy4UbKT_)WK5niTjSQ zCHGJYJ`Q;?JUI&SoOKZ*-})pPijC}qZvkxM%x zRub+6QquOn1ZfEdXz3{jMIAsWPA(DzC2=zMjq_PiPY*v)rXRY^Q4AR!tRi!Yl*@*; z%y6LuL5kIXc{D6oFjOv4bNcnfXiz}%8;baikla`Hi}Ea;Rpbf1bz8G?b9QnCfZy*R zIviU=n?r^K;RbMxQGVM0M+FSv&ll+VKJ{XW4EA*wA(KPr$K!HT+4BqtwE0_GLx%eG z84Xx#S3>0t9MZH))(EVUmwa@&LzpKaOn#GV)}&SWTA~Rd)xe;o97xsL&oxq5R76>z zU~gaRkU96MMkZ=dw~OSm$pY`zCsB!rw8yBgSl4K~lW2OajANzXVSy8A#Fg#4qvRK~E?<G)RR4)!|7@*+qJXB z3VANwb=8jS)5#SJAh`T2=$B)@hyW|x4(05lt)La0Pr;FTBN#okSw+U}o3(_(76~PM z!;gHnsThppv2eDW6{y-Z-YYQm3l9)|&Q#h7EM3{Nu`L#$#jLkuH*CUT-$~Hzz$D80c zHH0c`go0S+acB%*l5oGmtMq6@36m7{@`+wvF6W|hMjbb#*R!3bRG6bo0%kFpkIT@A zKmWJ4e7Il{Pwr~)io_9UEsZaC|GtaxWaxs2`uJ!+H> zs=hkCFVb%!vMhX#PF-9x8DuP7|C$ol)FHkj7ufA-WUmLWI)Z-vEE@Hz1}xUc#8Yjt@qX zb)RsS+;VXE$ofkBb2Dj~_m16NtAc5{hXt<>))bEVFFrm6v?y|Ybf^TeYK(G9g@6~pn6Ew z|CW;D!W^qlhHHpk^atO8RLsDHH+T|doVLN03%l!I?Y-g^{~1 zkRBHYOJp;V(upD2@su>K*|≪V|Lwvf6qnOl{FGK;gqK|Bq(h6Jp|Fxejcp9GHwT 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 ydXG4^gPCI+ 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 AyZ`_I diff --git a/docs/developer-guide/img/gui-builder-workspace.png b/docs/developer-guide/img/gui-builder-workspace.png new file mode 100644 index 0000000000000000000000000000000000000000..f1ad23cec27124b28bdd86dac6227c69971eacf9 GIT binary patch literal 78299 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

*fpU4i22nWQNAewyZIyVj|$IcM%Rs_3x^acCC9 z?6MnB{9DvZzsT1*Zwm;KS1k>uM2#aw%I_0D%g?p9bNLLa8}$>MSJ9Q09LMYH$Z1Og zo_970L3>*Sg{iL%rcI=JC*0)}&7k&Bff?d82N9nEo8u?+!O;}tZGpN|GQiAd1jAN^Kv4+1H<#L=cyzX3)|*~ z(9Z^MxXKyhWcv-f(Ts5P3P1h%shbcW9tRZ_gF;%LE!1`hs)2l1ga+rD#wrLAfw{5B z8ke3qvwFb-De#c9vgghdKgZq~itX3ruSCt=<~R~BV$wmxbFTZUJoY#zx+nr=TC58L zQr9lYL7PwV#&@d#-4+g>{%}qszVM?)H152@xUZlOWi;=-aJcr%6rWYsRUgDK7fZ7K zZbWzhZ)fOy8;lb5D*JU^WH!avwchC2@9^|e|G~dY#i~o(@gD|p9x36{yAG&kW6}#) z>3Od)-tyCrl_;be^CB5>%pBAzveOS6d>oh;oX>X8M(P4_V+dRR88Cng*7D~`Y3wx4 zTS}14rE zEVjov>QZUtag6mo8zkYaPqKTXb&2WhfH1Jf!b%u3#~ZrNBonG8Wl_qUa3Th_F$$Fa zHPL!Fv2Hha0SK&Q(4q5GjP+P}&Fe>9aJ6$UjC6JabN1SGqZQpW=G4Cmd$t!BPF!2< z+|D8;KLTOTAW6yYid%x?pQi5jmmU_al)YQfNRf_k^hNXrOjiOrvYUSr@Ogj>Nob_& z;@IB!G?Ein)4=;iJF9B6HX+r=vjBSqvZm%__(jx=So_d1>VxZaoJqDb1c=`nR*i5U zlpk-!vnG3pYJ?QqZSc*74qOGc;4pY~%R<(HXq=FgUhj)vYqLBfaduzYv8Sx^dTV2P zeSk|_fpsPm>elnrv(hubcfLKE7x);D;qp)osaX$YXTz7)7mn@{tAuL<77bhcH5cA7 zJ|E_w#?>&xA)S8HRlAhDLJeu^M9GY|&ZMM6uVjB#eiKrq9EW+@yIq!b?2Mj zI66) z2}93wc!xH3OBd68u}{m|C+p*5+0eso467+~^sv*m6(I>d@+iL4HY0f&kxA{TLoXFD z?)VeMe+*6}GQX_8iEF2=I_FHcL{T=Y3Ot}3V|ju^&eDww(9%f=v)nu_x4$D Xsi3>r{u;JFXhm1c@J7k?`%nG>3eLP8 literal 0 HcmV?d00001 diff --git a/docs/developer-guide/img/guibuilder-2-inset-control.png b/docs/developer-guide/img/guibuilder-2-inset-control.png deleted file mode 100644 index a4f9590d4042d085183f4462b7b956dc3d48632a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1898 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 vM(8|y@}fmb%%p)iOYyf@It9TAdr%~{S$~0 zOyh5L!bq9d5rc7qfWcl~UgBQT;;!xvU`Yi91+at^SV~HaAR&hKL17+wi=ohbe;N6g z9T*&K=Z`eH-*@@gM*K+vNr&eLg$f%jD5=er;v zIq6lbFnIzS5i{H{A#~7?p@?1{OA(bSyv-BJPsPxaNJeX$Ch#aZn2Lfzm0lHF^r9Zf zE3swOvs1aXbW1v@%pq`OZ@b{~{?TG})zr6zs=BXRRk-cZt$YhKRoIlqeJ+3cc;qU2 zC5T(M71E-pnbm05UWtp7vqH^jkKC=SX_dxfJx9q}i!&o=fUVuKwrutu3rXc&r1$$T zGt+BRDu`95EZ&JY?Qsu-CXd|8ntAWa8AGqpm~tUZdgDRd&bATq8VU|c_4|<9OuC=;m7TjBnh={-( zFL()=Xo^NKAvWGd063LBBRvCs22n#>5}!SuEV}l{* zs|~}B(NJ~d-uO}zwqr&o9%GzbN%ZDp>P*iOTHjM~7n9&ou!Y#{m1+;fy%g`U4W=oV zH0XPtZ_n3FD(`8Ug*!Dla;u+x6r~u@O^}V+e(4>sRzvIq_`G7kpxP_pw1VudUrSl& zIsK!!89i~|%<%lj7s{t4fQ>dzUsfh=49WTqJPRi|+}0OI3MetDG)jvlxwiwWYYM@9 z@SEr<&I>B>jHKnfn+NeI>hL=@dhmd&*0MRKOfP5MQs8HZLeZGeuC6>i`G$h0DOtPK z=x`{V0xEVGw9c(V#8|C+0wd94bi@~t#$T5V&G$dhW`p|6H#<$l zKsJ8}b$;K%iw zo_ugz?T>%yx&)n=4Y1TW>Ro&8f>2DdWTIRL`d2FmN5u_C(TVy%)p9x9f%j zTd*koh@sM(5nL#D5T?Ssk=NtfVxH4vO{ED0y-xEXS0R;*mO~3ZOjZ#5b?Hq3^kkVB z@^mHK5NWK~Ad>I2Lo`yd3~KEPRN3`p9X-`jBzD{@pF0d0mJ*gv9A+;!ET9!6W#k0W z$FWrX3a!|1DN>)ilj4&eE$AiG{#AX)2T6;c`~>@UnYqbT-SqOD37<5{qP5Lug?s95cABH;`Zv?L~?6 zlu+eV_I(ND>6H}v1nA+jP0N;l%KK%pBgf)LX?Wgy=+l1d;H^Q$F&ZhG45HwyBNYYc zy-1&BzP#8V(L%Ym%!;cm9??&JwD=Yt9bVRcfQ4-^G{5INJCMMmc2wjqnGCQtZws$$p*0-mQupj zbQqWyoo&i4SyHfRK>Ed5`w67Yto&iKzo~U_VNFvMIw0D4yh2sPw5CD3?Y*t+$fu2h zpt8V=vr5sgT9z#??`n7(n>feTdM%Rtn)IW%mqimi^1r&2H7zZ>1p4CK+i5-@5+fDo zmOWqTr?rCPtg}QsV$rp@274j=yC&7IPSm0vHMqweRH~=<)dbu{gLB8qWP^S(<9pL# zB4iA=>eOX0MEHlY;hz_bT!UCjfWiQZjRku?e6F%}W}p2F0!%OcPuuMtg-iL0X;jSGP`KzMN zEyy=YZ>QiARu_EZ&XdQqJ}(hX8RJjb@SUkJ>emsQAD*?o+~9A5gm3mtww8nrhi2wd z#;akBFCO59^q%!@XccS3zZerd!xw9&(};sJZJyllmkiD?C@{ERZCYCY=JMG)`>Lod zfc4S#q{sKi<}EJS?bY3m;ip%k!pP~Wuu=!@`Hrb!Uo%q*9XM|X&XZB5tt{I67L+_B z!l%teS7lHyb}Kc>d*5t(eQMKc>-(OusM0wMarv z<~95D{-l*_^Te<1>Uu(zQOBHrX*KTfJ#sl)>5D}s1xM@TFR3-EuCy{qZ1sR7_fz$} zLVL#_K!Qs))8;U;h`R&UJj~rQ`XvrG+o3`RmQ%rwDPLyh|d&*m%1SpWX_@; zQ~7Lexv}L&uCkx0a*6c*h1!UB672QP-Vg)MGl4@v&A43usOpTdR%jJ0hvUi@buO>U zv)|&bT#O^fqU-42UB~VjP3ZGN`mJPFRa2_C@~b>oJTSa#Qe6C%+n*|(gcuwK{u?VM zm}KNg`bJ4rlpkKdWNt0xP&-+u4%~6bh@5#*I+%Dzpz?F+sBBU@rlRtvq|fzBPA>UK&1~0*9kg#FO}NeEdw=W*>%4r- ziXu|zh-Mi))03a;oPS?%y8wE=yOIO_B$e{ISIjo-Gh=I5ZXgLYg%)1P7df# z(KslqdYYL;s>H4>!HfAiKeMP$^?F^#WqS0i%V=-^>#Zs4JLcTg`Cu z7(Q1cZB~3#dN+Odz|8t-MA4^!dy4}!R{H9xpL_)pmVo(j{BD;*1^#Ms3z*beN{-WB zCWBGACI4bi(fMZQYlLk6u+FzX$d@?kT;EZa;Q3|tMiNiPGML&M$mnuJvQcD)7Jk^N zq`cQf>;%8alC&F#K04Z&GApL^RA2uBws`RP7vZx&`uH%+zM<8Lc;d}q8DzDXn#|7} zqr)!XKt!bdZU&09WJxNCufD5q_`eUuCz+-Q$crQOb+5*whFR_e1<;i3ZIv=_=yLLe5);X$?wii!|u^!Y~VQ)P#V<5r?1+21vAmQ(P z8WF_IkaRL7*NN9vSz$N(f0)USFS;Bc@m`BQngPMA?3=5pWj5`Y8!vj9;Eg}O_t4#z z5vRU20X}4G0d!K=aLe>tx>>~8)|ODmaVbM=#kOe7-!PI#(y%f%K$o~o!(h;AUce{H$31oxGlQfI%7X}W8RV?Y z5vi`|bo-}B_VJ-k0iBGBTgoHQb5Xr~rEJ1Fs;lgKxO|geKZKN#jB@AXWHqrm-6P=a zkdl}Z{EI_r`s|N3mibNd*f7#{^NZto^a#TwV99-0%n7?W41S|~a(dP>S2L2Mqg6@D znBQi(TIhbWGd1iB-};9fO~Tf($>8m)pQ)@UjQ{si_q8FDW~ckj(nKvKDw~TPuPs5e zT`C%CPGU(CTC&L565oqTaq4~51sa(Tk4#+ht(^0KQ~Z5O0yIV+{p7X3JmP5nn#`rT^EFS*LR-X3?LMBCoiB& zc$kmQEBCc=F5$M%qUH_3zQ#S-0WH@w!b z$ZhYU2TY0csPz&O1nuK8Sglfn`R#0W7O<3Be>B1SC(~p>3IS)BEGGS2`RC9Z>>6ph zMxx6KT3o)HJXOZ!2?4fZ;J3RoO_l1gESRGSGxmqxo~hj*4MaKnr0|3{F{^Q1-VMYF z^`FM+@cOW5Z7N`MEZeRvwLLAitfCCm*Qd33Oqs-w2=UrH!#+s*~nQQBm& zqu@6`-J1o;ox}DoItj^b9M5)ZlYsK)6-FUEFh7Ccya)@=H$T9ByLilb<-mEe#?UWg z**V`0RsW!4^Wu1kPEjnCX6W~#84^3SW_yj;|EHmvljyw~I(nEf;`{oCa5IiZJ^)L6 z(65W*a6EA_jBw1@UAg5vX`-Wm3{97KXIo7fOfsyw58rQgo~mNt2ZIg3;)q1C!l2-; zvEeHmG4&zWu3i;RKR^CvKsaAICCiB``Ys!WAhP_j>=(UPuQa=H6o2IOnpYhwHzWdD2TK`NfIVT4 zJzl6vOq%7fGg-Mn_T+um^8@R|-bHhjf$(xPBLJw(NyJC|V~edZvS;!vg2_9TG#R*Bt$x$Eh{nyHW{-K3yw2zSuWMhYgGJzmn( z_ZebVz{!PFn{l>EV4-&O0x;RXY-;7S-xqZ;Jl-^%3H~O?r6K*!)~JSD*F8@r4~+OG zy1bR!tJqv3c-ulnWktyK#N-+g)_`+WsF{R6@F3E_gHTq-B?FCg{$gf^bCva-$8;JX zd7M~9>E*QSKg4$zv1(-u>ybewc8nR5ypedC8UvM-5dEXGG!p&?Rx-ppOiA{I#vM=r zBJ32)hpsace%4KsdIN2hm1cG62Iy22;zT%sq!D0MIGEX##nWgWEcUOR#Q&L$dqET< WOol0|#h~B64Yk$vVRdS@q5lQFioVzY diff --git a/docs/developer-guide/img/guibuilder-2-link-button-unselected.png b/docs/developer-guide/img/guibuilder-2-link-button-unselected.png deleted file mode 100644 index 26e3705b470093339cc2e0f3910a107acb191eae..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 907 zcmeAS@N?(olHy`uVBq!ia0vp^azL!i!3HFouk5-Aq!^2X+?^QKos)S9WY5kFi71Ki z^|4CM&(%vz$xlkvtH>8? ztx|+t?VyL%(9@GcP=o{)8=)=thYPA80S_NcQq~=7pWag&k6$9xNP*{0dL2}e^bO97)|p$T^vIq zINydFt~%r(P+O;TV?{)Ub_MfB{%LUvW|}&Cnl*#ji&(j%{S(qZFx^u)a>=AAlcn|l z+^6RS#DrO5>pvO`Y~K9YDF4}8weGDZzWYCB?~UUxpC7yUyZ+*v-jlA&91oOTe?8l0 z`DLLO)lO%WkG%K%T_ZMeme$V}L;tHE)vtufT$PfZzU$W2W2)CUb>=$h`R`PxC>BeeYKrl+QRmi7BPUa}vwyt6K8Y3_o{ViRn}* zYR=oqqhH!*{G({pW68`^5|u!`cHjPG_dRaQM`JT>02z!)>?Ma$Vjf zHZ%8j$KxFuJkJDIa!>s3u==W#0E@+3KMNT?F0V_I=4|27soj6yefj0a=am-o&!=iC z&hFOIH1gUMp|j!s`^3m;F)inRi>Sv)YJRr4S{UfK{*3+AkJ>E1HisVHrF86XR&TF& n%-gKTLa$lsx3B(xSN^knXUyHd7p@9%fU=FJtDnm{r-UW|ra)lD diff --git a/docs/developer-guide/img/guibuilder-2-smart-insets-auto-snap-checkboxes.png b/docs/developer-guide/img/guibuilder-2-smart-insets-auto-snap-checkboxes.png deleted file mode 100644 index a133f369415fd176d7c43016617879ef75f0eb49..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3275 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 diff --git a/docs/developer-guide/img/guibuilder-2-subcontainer-add-child-2.png b/docs/developer-guide/img/guibuilder-2-subcontainer-add-child-2.png deleted file mode 100644 index 57ecc6b17e12e802cccd67f77aa5a768bbe4ba1a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 33331 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 diff --git a/docs/developer-guide/img/guibuilder-2-subcontainer-add-child.png b/docs/developer-guide/img/guibuilder-2-subcontainer-add-child.png deleted file mode 100644 index edb03dcf8ae594566a81274351e2e5af83c9b12d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 33158 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 DT`Y;< diff --git a/docs/developer-guide/img/guibuilder-2-widget-control-pad.png b/docs/developer-guide/img/guibuilder-2-widget-control-pad.png deleted file mode 100644 index a722c90b6f7a13b2d2bb85743589468b7a969527..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 11060 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 diff --git a/docs/developer-guide/img/new-gui-builder-component-tree-palette-inspector.png b/docs/developer-guide/img/new-gui-builder-component-tree-palette-inspector.png deleted file mode 100644 index d7d4da2abc4507b5f743c37453891da2fb250982..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 111340 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%0DEuY(S7$$#8_Xw(sXcIk_8ht#~R@Dut3)}^OP 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 WOxCvPS6gtv&vkjFt68!}5C0GUk8QC4 diff --git a/docs/developer-guide/img/new-gui-builder-drag-button.png b/docs/developer-guide/img/new-gui-builder-drag-button.png deleted file mode 100644 index 5a355952efe011d0170aada3dfbcce88a5cbe97c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 109578 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%0D>K6ew{p4PQEK1Ho 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

tshlG}V@hN)%m%{bi8k8H=1t_mDG4YEb zJySKsu8G+7)hehJ_X%^{XbGuB=Hg!@3^|W=>dwziOvR6zT5PyiCsMlj+Q#bnd0#eq z{FeC6_-@9Jg|Q3|7u6`drGmXw+gZdoG41*gvJHtZr_I{=DKX_(Z_;g~m?@GmX+5L8C=nvQGod%^8$0yCzLwnaG)=b62pGiJk+Ya!9%fn``_Nb{4autqsz6Bh z{8f2iNl5pykAXYjRp43Bt#ZDz$(u>+x4%}Npg*1vS~pIh*rK4Mydcog^Ve_HWgPz7 zk+{SlEhq79gZ1rFJXTa&(@|D^L8!uAYGS&;FH+<&nC@1u-3hjcdD2kN=Fwf>BcQkz z^WZY6J{loPWG1jgpfPyJ84KE?gp;&Vf4#q2x)yEX0%CRhG$aN<@Je)DMmFi z;_?5`lK{lFyPK`v{cp7FnR8M`39*`(PV^UMLoc$1u@gw(RV#v=$yF4&wbdJy;AC?u zAvwlL7tpzgm#uVho%%9QCc%4X-|*wOWpX6mrqY&;>(9;e3;~(B4{Syp)YdJ|Hzuc! z#)qW7eY1XjMAo4@d--TQPDa+Q>)A*xWFV3Yo#DcjheQhWWP+6ua>?VT5=jUvY2p$o zXDISLENiw8_`LN*=u7-hA0}!heu|&9WhtLZVqCij)PzfSfNzL!Cm;RRk@v8<#(9`M znG^S1{v+mmfEX3{_RK;e3i`V_WeGe)9KC-n5Gcn_7slP5SGnX@%ntL|$wU?hr^8#DZLCo|aSU@H@O2HvOpH(l9?-xZsP6xY- z$rq)qX@WY*OGZm7NUiGn7KEpQVAH5t#+LIQ+aTKo)NKYg(35_>g%UrSv10+p#8-%! zB0Mt#&8W{HrS1S7R__;M9!EA4ycjw$47 zr?^qlq^t^|c5IUel@#xs>JLD>Lu9kLjFS#w0xddKB0}j(ew!#sCV>evOle6`=-nzF z3Xv<%Q)3z;8M*x*mleC};2NFkC>>pwzYgd)1eF{$TTww6?2e=J$fa|Ni4wj($8izN z;X;kAz1W-weLxa^$A`idYtnJv;GXrR^=+cVm1yBvseFV7p2$10VBWtk>ILtL1NGuR zgbDT%B0Z7r5mA^kUVY`yfg7Nx)2)2Sv&g2qD542wi6=={(7{=(8pZ3gTW(ckk+)ao zeoxCAghFAn1DEJT+4xUzr4uC-R02MywRb*@QxH&o)H2}2|Ez>y!Q_fKqLok#H)niZ zZneCRy--rGzI>tIN@}N!YT8a2?DxI?Ds6>z`LPqB^W+nu^xK~;x)$>Wi_fvLpYCp> zGtk%3pE(hkm7Y*VoLI=*Y_TWq>SaP(?BW?<8{ZBqj_Qv>J>JWvq_iA83dlS9P~W)r zK?Vcz%O1uS;fKlhCmnW;S$O0u)WYTAf(j^IE**T#XQcVZ577EZFo zQs$9W*1Tl@N=<7OFnlyw{qLgWY9+rgv^oIXNB9glpH!;mEJsil91pQuL%(