Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
From e2e239792a6887ca3294079e476cbe48cf416595 Mon Sep 17 00:00:00 2001
From 61029b133c68d4b916990d2b58cdd74489be35d8 Mon Sep 17 00:00:00 2001
From: AlexProgrammerDE <40795980+AlexProgrammerDE@users.noreply.github.com>
Date: Sun, 18 Oct 2026 16:55:37 +0200
Subject: [PATCH] feat(ui): share bounded native UI programs and resource
Expand Down Expand Up @@ -563,11 +563,15 @@ Unicode through export/import, and bound cyclic references.
Resolve event-driven factory children from live creation snapshots instead of eagerly instantiating their templates. Keep event properties and named control bindings local to each creation, retain creation IDs through binding refreshes, and select control_ids from each event. Legacy collection factories keep their existing path.

Pinned Bedrock 1.26.60.5/protocol2225 original factory prefix141917070 and complete retirement1419225b0/detach141922870 verify strict JSON boolean exclusivity and removal of all children with the exact runtime name. Original property application141c691d0 copies event properties to descendant property bags after construction. Regression tests cover exact-name replacement, immutable event data, late retirement, dormant factories, scoped text, variables and transport. Animated target resolution and native rendered parity remain separate.

Initialize collection_details event properties even without binding_name. Keep named collection lookup, zero fallback, prefixed source fields and unnamed collection maps in the owning control bag. Export final bound values rather than the original property_bag declarations so subsequent view/global updates reach input consumers. Do not inherit a parent control event bag merely through expression scope.

Bedrock 1.26.60.5/protocol 2225 executable SHA-256 988f6a61403b4381aa41e6c66d64cce441b14e561a9b10f647c2758ef3f2672a supplies the original binding-type parser1400f7750, collection-details initialization14191e310, ancestor index lookup141c42d50, enumeration141c6b230 and map callback141932700. Fifteen native controls execute those paths and original JSON setters; eight parser controls establish collection_details=3. Named misses resolve to zero; unnamed maps retain outer values for repeated names, and no scopes produce null. Source getter14500e920 reads the typed bag value. Authored parsing and installed controller input remain separate validation gates.
---
build.gradle | 11 +
.../api/model/NativeContainerAction.java | 93 +
.../api/model/NativeContainerData.java | 70 +
.../api/model/NativeContainerMessage.java | 112 ++
.../api/model/NativeContainerMessage.java | 112 +
.../api/model/NativeContainerStatus.java | 60 +
.../api/model/NativeCookingAction.java | 96 +
.../api/model/NativeCookingActionResult.java | 43 +
Expand Down Expand Up @@ -596,7 +600,7 @@ Pinned Bedrock 1.26.60.5/protocol2225 original factory prefix141917070 and compl
.../api/resourcepack/ui/HudTitleUi.java | 28 +
.../resourcepack/ui/InventoryItemFilter.java | 43 +
.../resourcepack/ui/InventoryStackCount.java | 21 +
.../api/resourcepack/ui/JsonUiProgram.java | 1789 +++++++++++++++++
.../api/resourcepack/ui/JsonUiProgram.java | 1827 +++++++++++++++++
.../resourcepack/ui/NativeRecipeSearch.java | 124 ++
.../ui/NativeSearchNormalization.java | 189 ++
.../api/resourcepack/ui/NpcDialogueUi.java | 316 +++
Expand Down Expand Up @@ -645,16 +649,16 @@ Pinned Bedrock 1.26.60.5/protocol2225 original factory prefix141917070 and compl
.../ui/FormCollectionActivationTest.java | 97 +
.../api/resourcepack/ui/HudRootUiTest.java | 90 +
.../ui/InventoryItemFilterTest.java | 33 +
.../resourcepack/ui/JsonUiProgramTest.java | 751 +++++++
.../resourcepack/ui/JsonUiProgramTest.java | 803 ++++++++
.../ui/NativeRecipeSearchTest.java | 110 +
.../ui/NativeSearchNormalizationTest.java | 46 +
.../resourcepack/ui/NpcDialogueUiTest.java | 108 +
.../resourcepack/ui/NpcEditorStateTest.java | 209 ++
.../resourcepack/ui/RecipeCollectionTest.java | 227 +++
.../resourcepack/ui/RecipeCollectionTest.java | 227 ++
.../ui/ServerCustomFormUiTest.java | 146 ++
.../ui/ServerFormUiSliderStateTest.java | 126 ++
.../api/resourcepack/ui/ServerFormUiTest.java | 486 +++++
.../ui/UiAsepriteMetadataTest.java | 112 ++
.../ui/UiAsepriteMetadataTest.java | 112 +
.../resourcepack/ui/UiBitmapFontsTest.java | 313 +++
.../ui/UiCollectionWindowTest.java | 131 ++
.../api/resourcepack/ui/UiExpressionTest.java | 77 +
Expand All @@ -664,7 +668,7 @@ Pinned Bedrock 1.26.60.5/protocol2225 original factory prefix141917070 and compl
.../api/resourcepack/ui/UiTexturesTest.java | 231 +++
.../ui/native-inventory-item-filter.json | 311 +++
.../generator/SearchUnicodeDataGenerator.java | 126 ++
100 files changed, 15731 insertions(+), 60 deletions(-)
100 files changed, 15821 insertions(+), 60 deletions(-)
create mode 100644 src/main/java/net/raphimc/viabedrock/api/model/NativeContainerAction.java
create mode 100644 src/main/java/net/raphimc/viabedrock/api/model/NativeContainerData.java
create mode 100644 src/main/java/net/raphimc/viabedrock/api/model/NativeContainerMessage.java
Expand Down Expand Up @@ -4138,10 +4142,10 @@ index 00000000..5dfbb3f8
+}
diff --git a/src/main/java/net/raphimc/viabedrock/api/resourcepack/ui/JsonUiProgram.java b/src/main/java/net/raphimc/viabedrock/api/resourcepack/ui/JsonUiProgram.java
new file mode 100644
index 00000000..c89bbd52
index 00000000..36db9013
--- /dev/null
+++ b/src/main/java/net/raphimc/viabedrock/api/resourcepack/ui/JsonUiProgram.java
@@ -0,0 +1,1789 @@
@@ -0,0 +1,1827 @@
+/*
+ * This file is part of ViaBedrock - https://github.com/RaphiMC/ViaBedrock
+ * Copyright (C) 2026 RK_01/RaphiMC and contributors
Expand Down Expand Up @@ -5244,8 +5248,12 @@ index 00000000..c89bbd52
+ }
+ }
+ node.add("propagate_alpha", values.get("#propagateAlpha"));
+ if (control.has("property_bag")) {
+ node.add("property_bag", resolveValue(control.get("property_bag"), values, 0));
+ if (!bound.isEmpty()) {
+ final JsonObject eventProperties = new JsonObject();
+ for (final String property : bound) {
+ eventProperties.add(property, values.get(property));
+ }
+ node.add("property_bag", eventProperties);
+ }
+ node.addProperty("visible", visible);
+ if (stateBranch) {
Expand Down Expand Up @@ -5859,6 +5867,40 @@ index 00000000..c89bbd52
+ if (!bindings.isJsonArray()) {
+ return bound;
+ }
+ // Collection details initialize the control's event bag before binding updates.
+ for (final JsonElement element : bindings.getAsJsonArray()) {
+ final JsonObject binding = element.getAsJsonObject();
+ if (!"collection_details".equals(bindingName(binding, "binding_type", values))
+ || UiExpression.truth(resolveValue(binding.get("ignored"), values, 0))) {
+ continue;
+ }
+ final String collection = bindingName(binding, "binding_collection_name", values);
+ final String declaredPrefix = bindingName(binding, "binding_collection_prefix", values);
+ final String prefix = declaredPrefix.isEmpty() ? "" : declaredPrefix + "_";
+ if (collection.isEmpty()) {
+ final JsonObject collections = new JsonObject();
+ for (UiBindings.Scope candidate = scope; candidate != null; candidate = candidate.parent()) {
+ if (!candidate.collection().isEmpty()) {
+ collections.addProperty(prefix + candidate.collection(), candidate.index());
+ }
+ }
+ final String property = "#" + prefix + "collections";
+ values.put(property, collections.isEmpty() ? JsonNull.INSTANCE : collections);
+ bound.add(property);
+ } else {
+ int index = 0;
+ for (UiBindings.Scope candidate = scope; candidate != null; candidate = candidate.parent()) {
+ if (collection.equals(candidate.collection())) {
+ index = candidate.index();
+ break;
+ }
+ }
+ values.put("#" + prefix + "collection_name", new JsonPrimitive(collection));
+ values.put("#" + prefix + "collection_index", new JsonPrimitive(index));
+ bound.add("#" + prefix + "collection_name");
+ bound.add("#" + prefix + "collection_index");
+ }
+ }
+ for (final JsonElement element : bindings.getAsJsonArray()) {
+ final JsonObject binding = element.getAsJsonObject();
+ final String bindingType = bindingName(binding, "binding_type", values);
Expand Down Expand Up @@ -13979,10 +14021,10 @@ index 00000000..bc575caa
+}
diff --git a/src/test/java/net/raphimc/viabedrock/api/resourcepack/ui/JsonUiProgramTest.java b/src/test/java/net/raphimc/viabedrock/api/resourcepack/ui/JsonUiProgramTest.java
new file mode 100644
index 00000000..bf7dd528
index 00000000..de572c3a
--- /dev/null
+++ b/src/test/java/net/raphimc/viabedrock/api/resourcepack/ui/JsonUiProgramTest.java
@@ -0,0 +1,751 @@
@@ -0,0 +1,803 @@
+/*
+ * This file is part of ViaBedrock - https://github.com/RaphiMC/ViaBedrock
+ * Copyright (C) 2026 RK_01/RaphiMC and contributors
Expand Down Expand Up @@ -14029,6 +14071,58 @@ index 00000000..bf7dd528
+ }
+
+ @Test
+ void collectionDetailsPopulateLocalEventBagsWithoutBindingNames() {
+ final var ui = program("""
+ {"namespace":"example","root":{"type":"panel","bindings":[
+ {"binding_type":"collection_details","binding_collection_name":"first"},
+ {"binding_type":"collection_details"}],"controls":[
+ {"grid":{"type":"grid","collection_name":"first","grid_item_template":"example.row"}}]},
+ "row":{"type":"button","property_bag":{"#collection_name":"authored","#collection_index":99},"bindings":[
+ {"binding_type":"collection_details","binding_collection_name":"first"},
+ {"binding_type":"collection_details","binding_collection_name":"second","binding_collection_prefix":"other"},
+ {"binding_type":"collection_details","binding_collection_prefix":"source"}],"controls":[
+ {"plain":{"type":"button"}}]}}
+ """);
+ final var bindings = new Bindings();
+ final var scene = ui.resolve("example.root", bindings);
+ final var rootBag = scene.root().getAsJsonObject("property_bag");
+ assertEquals("first", rootBag.get("#collection_name").getAsString());
+ assertEquals(0, rootBag.get("#collection_index").getAsInt());
+ assertTrue(rootBag.get("#collections").isJsonNull());
+ final var rows = scene.root().getAsJsonArray("children").get(0).getAsJsonObject().getAsJsonArray("children");
+ assertEquals(2, rows.size());
+ for (int index = 0; index < rows.size(); index++) {
+ final var row = rows.get(index).getAsJsonObject();
+ final var bag = row.getAsJsonObject("property_bag");
+ assertEquals("first", bag.get("#collection_name").getAsString());
+ assertEquals(index, bag.get("#collection_index").getAsInt());
+ assertEquals("second", bag.get("#other_collection_name").getAsString());
+ assertEquals(0, bag.get("#other_collection_index").getAsInt());
+ assertEquals(JsonParser.parseString("{\"source_first\":" + index + "}"), bag.get("#source_collections"));
+ assertFalse(row.getAsJsonArray("children").get(0).getAsJsonObject().has("property_bag"));
+ }
+ final var imported = JsonUiProgram.fromProgram(ui.export(scene.requiredDefinitions(), new JsonObject()));
+ assertEquals(scene.root(), imported.resolve("example.root", bindings).root());
+ }
+
+ @Test
+ void bindingUpdatesReplaceEventPropertiesInsteadOfReexportingAuthoredValues() {
+ final var ui = program("""
+ {"namespace":"example","root":{"type":"button",
+ "property_bag":{"#collection_name":"authored","#collection_index":99},"bindings":[
+ {"binding_type":"view","source_property_name":"#desired_source","target_property_name":"#collection_name"},
+ {"binding_type":"view","source_property_name":"#desired_index","target_property_name":"#collection_index"},
+ {"binding_type":"collection_details","binding_collection_name":"first"}]}}
+ """);
+ final var bindings = new Bindings();
+ bindings.globals.put("#desired_source", new JsonPrimitive("second"));
+ bindings.globals.put("#desired_index", new JsonPrimitive(7));
+ final var bag = ui.resolve("example.root", bindings).root().getAsJsonObject("property_bag");
+ assertEquals("second", bag.get("#collection_name").getAsString());
+ assertEquals(7, bag.get("#collection_index").getAsInt());
+ }
+
+ @Test
+ void serverFontDeclarationsKeepNativeRegistrationsAndAliasesAcrossTransport() {
+ final var content = new InMemoryContent();
+ content.putString("manifest.json", """
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -139,3 +139,5 @@ A client-facing projection regression accepts the scene depth boundary and rejec
A rebuilt Java dialog shows the captured Navigator labels and sends response 8 when the source Close action is clicked.
The local diagnostic control disables authored-form capability without changing the recorded scene packets.
The original reported dialog remains unmatched.

Collection-details bindings now populate each control event bag without requiring binding_name. Named lookups, zero fallback, prefixes and unnamed scope maps match fifteen original Bedrock 1.26.60.5 execution controls; eight parser controls establish the binding type. Local regressions cover evaluated property replacement, control-local bags and export/import. Full controller input and installed parity remain separate gates.
Loading
Loading