From 85ac2bd0895a607146a27fb649bcda1f77c09031 Mon Sep 17 00:00:00 2001 From: AlexProgrammerDE <40795980+AlexProgrammerDE@users.noreply.github.com> Date: Wed, 4 Nov 2026 20:39:57 +0100 Subject: [PATCH 1/2] chore: continue compatibility maintenance From 970287f721c151d2d384b5ee2a672d5540aa7d58 Mon Sep 17 00:00:00 2001 From: AlexProgrammerDE <40795980+AlexProgrammerDE@users.noreply.github.com> Date: Wed, 4 Nov 2026 20:54:35 +0100 Subject: [PATCH 2/2] fix(inventory): read evaluated native event properties Populate collection-details metadata without requiring a binding name, then export the final control-local property bag after binding updates. Read inventory hover sources from that bag instead of render-slot annotations. Fold the change into the shared UI and container owners. Original Bedrock 1.26.60.5 controls verify binding type, ancestor collection lookup, prefixed maps and retained typed source reads. Add boundary regressions for program transport and routed slot events. --- ...are-native-ui-programs-and-resources.patch | 120 ++++++++++++++++-- ...are-native-ui-programs-and-resources.pr.md | 2 + ...9-present-authored-native-containers.patch | 100 ++++++++++++--- ...9-present-authored-native-containers.pr.md | 2 + 4 files changed, 193 insertions(+), 31 deletions(-) diff --git a/patches/viabedrock/upstreamable/0095-share-native-ui-programs-and-resources.patch b/patches/viabedrock/upstreamable/0095-share-native-ui-programs-and-resources.patch index 659e8832..409bd836 100644 --- a/patches/viabedrock/upstreamable/0095-share-native-ui-programs-and-resources.patch +++ b/patches/viabedrock/upstreamable/0095-share-native-ui-programs-and-resources.patch @@ -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 @@ -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 + @@ -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 +++ @@ -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 + @@ -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 @@ -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 @@ -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) { @@ -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); @@ -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 @@ -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", """ diff --git a/patches/viabedrock/upstreamable/0095-share-native-ui-programs-and-resources.pr.md b/patches/viabedrock/upstreamable/0095-share-native-ui-programs-and-resources.pr.md index 2ffa9a71..b35edeb8 100644 --- a/patches/viabedrock/upstreamable/0095-share-native-ui-programs-and-resources.pr.md +++ b/patches/viabedrock/upstreamable/0095-share-native-ui-programs-and-resources.pr.md @@ -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. diff --git a/patches/viafabricplus-bedrock/upstreamable/0029-present-authored-native-containers.patch b/patches/viafabricplus-bedrock/upstreamable/0029-present-authored-native-containers.patch index de185703..8e4664a5 100644 --- a/patches/viafabricplus-bedrock/upstreamable/0029-present-authored-native-containers.patch +++ b/patches/viafabricplus-bedrock/upstreamable/0029-present-authored-native-containers.patch @@ -1,4 +1,4 @@ -From 4aae8ba73b66f2a165e0944ff7e5001e4cafe3ce Mon Sep 17 00:00:00 2001 +From 1ebedcbd2231f05b320d4e99dd25743033ce92e4 Mon Sep 17 00:00:00 2001 From: AlexProgrammerDE <40795980+AlexProgrammerDE@users.noreply.github.com> Date: Sun, 18 Oct 2026 16:55:36 +0200 Subject: [PATCH] feat(inventory): present authored native containers @@ -828,6 +828,10 @@ The selected-cell helper remains behind the active bundle input/rendering admiss Render physical bundle item references from the same owned preview snapshot that built the scene. Reject missing, stale, empty, fractional and ambiguous references. Preserve full child counts and components; draw icons and native count labels independently of Java slots, quickcraft decorations and action dispatch. Reuse the authored item rectangle and per-extraction native opacity/filter style. Physical durability and storage controls retain the scene snapshot values. A screen extraction regression draws count255 as99+ with authored scale and native filter state, restores the extraction transform/style, and preserves both the retained snapshot and Java menu. Sparse source-to-scene regressions retain holes and reject malformed references. Pinned native count producer141913620 covers536 complete strings; existing original bundle layout controls establish physical cell order. Active admission remains guarded pending the full action graph, description formatter, visibility source and installed native comparison. + +Read inventory hover sources from the control event property bag instead of render-only collection/slot annotations. Preserve native typed defaults and integer narrowing, and retain live bag reads rather than copying a source when the event is created. Structural collection annotations still identify render slots. + +Original Bedrock 1.26.60.5/protocol2225 input producer14192b090 retains the UIControl bag from141c3a4e0; source getter14500e920/140085b60 reads its string #collection_name. Eight native composition controls preserve heap strings, reject boolean values and observe replacement after enqueue. The collection_details initializer14191e310 and original ancestor lookup/enumeration populate #collection_index and prefixed variants, with fifteen execution controls. The shared program prerequisite now exports those final values. Local event regressions use a routed slot event, change its retained bag and reject structural fallback and invalid JSON types. Full inventory FSM and installed parity remain separate gates. --- .../bedrock/ViaFabricPlusBedrock.java | 4 + .../access/IBedrockGuiItemStyle.java | 14 + @@ -852,7 +856,7 @@ A screen extraction regression draws count255 as99+ with authored scale and nati .../container/NativeContainerDraws.java | 36 + .../NativeContainerFamiliesPayload.java | 30 + .../container/NativeContainerGeometry.java | 273 +++ - .../container/NativeContainerInput.java | 510 ++++++ + .../container/NativeContainerInput.java | 521 ++++++ .../container/NativeContainerPayload.java | 22 + .../container/NativeContainerPending.java | 65 + .../NativeContainerPresentation.java | 45 + @@ -894,14 +898,14 @@ A screen extraction regression draws count255 as99+ with authored scale and nati .../container/NativeContainerButtonTest.java | 280 +++ .../container/NativeContainerDrawsTest.java | 70 + .../NativeContainerGeometryTest.java | 389 +++++ - .../container/NativeContainerInputTest.java | 562 ++++++ + .../container/NativeContainerInputTest.java | 610 +++++++ ...veContainerPresentationControllerTest.java | 91 + .../container/NativeContainerScrollTest.java | 51 + .../container/NativeContainerSessionTest.java | 40 + .../container/NativeCookingActionsTest.java | 346 ++++ ...tiveCookingPresentationControllerTest.java | 216 +++ .../NativeCookingPresentationTest.java | 144 ++ - .../container/NativeCookingScreenTest.java | 1409 +++++++++++++++ + .../container/NativeCookingScreenTest.java | 1410 +++++++++++++++ .../NativeFurnacePreferenceStateTest.java | 45 + .../container/NativeInventoryFlightsTest.java | 70 + .../NativeInventoryItemPresentationTest.java | 282 +++ @@ -914,7 +918,7 @@ A screen extraction regression draws count255 as99+ with authored scale and nati .../container/NativePlayerScreenTest.java | 720 ++++++++ .../PlayerInventoryAdmissionTest.java | 147 ++ .../PlayerInventoryScreenLifecycleTest.java | 139 ++ - 85 files changed, 12322 insertions(+), 62 deletions(-) + 85 files changed, 12382 insertions(+), 62 deletions(-) create mode 100644 src/main/java/com/viaversion/viafabricplus/bedrock/injection/access/IBedrockGuiItemStyle.java create mode 100644 src/main/java/com/viaversion/viafabricplus/bedrock/injection/mixin/features/inventory/MixinNativeContainerGeometry.java create mode 100644 src/main/java/com/viaversion/viafabricplus/bedrock/injection/mixin/features/inventory/MixinNativeContainerScreenAdmission.java @@ -1466,7 +1470,7 @@ index 0000000..1583ce0 +} diff --git a/src/main/java/com/viaversion/viafabricplus/bedrock/screen/container/BedrockNativeContainerScreen.java b/src/main/java/com/viaversion/viafabricplus/bedrock/screen/container/BedrockNativeContainerScreen.java new file mode 100644 -index 0000000..2e57f48 +index 0000000..26e352c --- /dev/null +++ b/src/main/java/com/viaversion/viafabricplus/bedrock/screen/container/BedrockNativeContainerScreen.java @@ -0,0 +1,1511 @@ @@ -2072,8 +2076,8 @@ index 0000000..2e57f48 + this.fallback(new IllegalArgumentException("Native inventory host action is not supported")); + handled = true; + } else if (event.button() != null && event.button().target() == NativeUiInput.symbol("button.container_slot_hovered")) { -+ if (event.button().pressed()) player.select(event.node().string("collection", ""), event.collectionIndex()); -+ else player.deselect(event.node().string("collection", ""), event.collectionIndex()); ++ if (event.button().pressed()) player.select(event.collection(), event.collectionIndex()); ++ else player.deselect(event.collection(), event.collectionIndex()); + } else if (event.button() != null && event.button().transition() + && event.button().target() == NativeUiInput.symbol("button.search_bar_clear")) { + BedrockNativePlayerInventory.query(this.owner, this.menu, ""); @@ -2625,7 +2629,7 @@ index 0000000..2e57f48 + for (final var event : result.events()) { + if (event.button() == null || event.button().target() != NativeUiInput.symbol("button.container_slot_hovered") + || event.button().phase() != 0 && event.button().phase() != 1) continue; -+ final Source source = new Source(event.node().string("collection", ""), event.collectionIndex()); ++ final Source source = new Source(event.collection(), event.collectionIndex()); + if (event.button().pressed()) this.tooltipSources.hover(source, this.tooltipData(source) != null); + else this.tooltipSources.leave(source); + } @@ -2659,7 +2663,7 @@ index 0000000..2e57f48 + if (event.button().target() == NativeUiInput.symbol("button.filter_toggle_hovered") && event.button().pressed()) + cooking.select("filter_toggle", 0); + else if (event.button().target() == NativeUiInput.symbol("button.container_slot_hovered")) { -+ final String collection = event.node().string("collection", ""); ++ final String collection = event.collection(); + if (event.button().pressed()) cooking.select(collection, event.collectionIndex()); + else cooking.deselect(collection, event.collectionIndex()); + } @@ -4219,10 +4223,10 @@ index 0000000..d88f727 +} diff --git a/src/main/java/com/viaversion/viafabricplus/bedrock/screen/container/NativeContainerInput.java b/src/main/java/com/viaversion/viafabricplus/bedrock/screen/container/NativeContainerInput.java new file mode 100644 -index 0000000..4a8ef9d +index 0000000..7de71b6 --- /dev/null +++ b/src/main/java/com/viaversion/viafabricplus/bedrock/screen/container/NativeContainerInput.java -@@ -0,0 +1,510 @@ +@@ -0,0 +1,521 @@ +/* + * This file is part of ViaFabricPlus Bedrock - https://github.com/ViaVersionAddons/viafabricplus-bedrock + * Copyright (C) 2026 ViaVersion and contributors @@ -4230,6 +4234,7 @@ index 0000000..4a8ef9d + */ +package com.viaversion.viafabricplus.bedrock.screen.container; + ++import com.google.gson.JsonElement; +import com.viaversion.viafabricplus.bedrock.screen.ui.NativeUiButton; +import com.viaversion.viafabricplus.bedrock.screen.ui.NativeUiFocus; +import com.viaversion.viafabricplus.bedrock.screen.ui.NativeUiInput; @@ -4669,8 +4674,13 @@ index 0000000..4a8ef9d + boolean captures(final String source) { return this.router.captures(NativeUiInput.symbol(source)); } + + record Event(NativeUiScene.Node node, NativeUiInput.ButtonEvent button, NativeUiToggle.Change change) { ++ String collection() { ++ final var value = this.property("#collection_name"); ++ return value != null && value.isJsonPrimitive() && value.getAsJsonPrimitive().isString() ? value.getAsString() : ""; ++ } ++ + int collectionIndex() { -+ final var value = this.node.properties().get("collection_index"); ++ final var value = this.property("#collection_index"); + if (value == null || !value.isJsonPrimitive() || !value.getAsJsonPrimitive().isNumber()) return -1; + final String encoded = value.getAsString(); + if (encoded.indexOf('.') >= 0 || encoded.indexOf('e') >= 0 || encoded.indexOf('E') >= 0) return -1; @@ -4679,6 +4689,11 @@ index 0000000..4a8ef9d + return integer.bitLength() <= (integer.signum() < 0 ? 63 : 64) ? integer.intValue() : -1; + } catch (final NumberFormatException invalid) { return -1; } + } ++ ++ private JsonElement property(final String name) { ++ final var bag = this.node.properties().get("property_bag"); ++ return bag != null && bag.isJsonObject() ? bag.getAsJsonObject().get(name) : null; ++ } + } + record Result(boolean consumed, List events) {} + private record Identity(String name, String type, String slot, String collection, String index, String factoryInstance, int occurrence) {} @@ -8855,10 +8870,10 @@ index 0000000..e120ebc +} diff --git a/src/test/java/com/viaversion/viafabricplus/bedrock/screen/container/NativeContainerInputTest.java b/src/test/java/com/viaversion/viafabricplus/bedrock/screen/container/NativeContainerInputTest.java new file mode 100644 -index 0000000..5f56d8f +index 0000000..4e518ba --- /dev/null +++ b/src/test/java/com/viaversion/viafabricplus/bedrock/screen/container/NativeContainerInputTest.java -@@ -0,0 +1,562 @@ +@@ -0,0 +1,610 @@ +/* + * This file is part of ViaFabricPlus Bedrock - https://github.com/ViaVersionAddons/viafabricplus-bedrock + * Copyright (C) 2026 ViaVersion and contributors @@ -8868,6 +8883,7 @@ index 0000000..5f56d8f + +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; ++import com.google.gson.JsonParser; +import com.viaversion.viafabricplus.bedrock.screen.ui.NativeUiLayout; +import com.viaversion.viafabricplus.bedrock.screen.ui.NativeUiInput; +import com.viaversion.viafabricplus.bedrock.screen.ui.NativeUiPresentation; @@ -8878,6 +8894,53 @@ index 0000000..5f56d8f + +final class NativeContainerInputTest { + @Test ++ void slotEventsReadTheRetainedPropertyBagInsteadOfRenderSlotAnnotations() { ++ final var input = new NativeContainerInput(_ -> { }); ++ final var properties = toggle("slot", 0, false).properties(); ++ properties.addProperty("inventory_slot", 5); ++ properties.addProperty("collection", "hotbar_items"); ++ properties.addProperty("collection_index", 5); ++ final var bag = new JsonObject(); ++ bag.addProperty("#collection_name", "bundle_items"); ++ bag.addProperty("#collection_index", 2); ++ properties.add("property_bag", bag); ++ final var node = new NativeUiScene.Node("slot", "button", properties, List.of(), Integer.MIN_VALUE); ++ refresh(input, node); ++ input.pointer(5, 5, 1, 0, true); ++ final var event = input.button("button.menu_select", true, 1, 0, true).events().getFirst(); ++ assertEquals("bundle_items", event.collection()); ++ assertEquals(2, event.collectionIndex()); ++ bag.addProperty("#collection_name", "inventory_items"); ++ bag.addProperty("#collection_index", 17); ++ assertEquals("inventory_items", event.collection()); ++ assertEquals(17, event.collectionIndex()); ++ bag.addProperty("#collection_name", true); ++ bag.addProperty("#collection_index", "17"); ++ assertEquals("", event.collection()); ++ assertEquals(-1, event.collectionIndex()); ++ properties.remove("property_bag"); ++ assertEquals("", event.collection()); ++ assertEquals(-1, event.collectionIndex()); ++ } ++ ++ @Test ++ void collectionIndicesRequireNativeIntegerValuesBeforeNarrowingToSlotWidth() { ++ final var bag = new JsonObject(); ++ final var properties = new JsonObject(); ++ properties.add("property_bag", bag); ++ final var node = new NativeUiScene.Node("slot", "button", properties, List.of(), Integer.MIN_VALUE); ++ final var event = new NativeContainerInput.Event(node, null, null); ++ for (final var value : List.of("null", "true", "1.0", "1e0", "18446744073709551616", "-9223372036854775809")) { ++ bag.add("#collection_index", JsonParser.parseString(value)); ++ assertEquals(-1, event.collectionIndex(), value); ++ } ++ bag.add("#collection_index", JsonParser.parseString("4294967298")); ++ assertEquals(2, event.collectionIndex()); ++ bag.add("#collection_index", JsonParser.parseString("-9223372036854775808")); ++ assertEquals(0, event.collectionIndex()); ++ } ++ ++ @Test + void viewTabsUseAuthoredIndicesAndRetainTheirPhaseAcrossRefreshes() { + final var input = new NativeContainerInput(_ -> { }); + final var root = tabRoot(); @@ -10347,10 +10410,10 @@ index 0000000..0d86d83 +} diff --git a/src/test/java/com/viaversion/viafabricplus/bedrock/screen/container/NativeCookingScreenTest.java b/src/test/java/com/viaversion/viafabricplus/bedrock/screen/container/NativeCookingScreenTest.java new file mode 100644 -index 0000000..a3823ec +index 0000000..684afed --- /dev/null +++ b/src/test/java/com/viaversion/viafabricplus/bedrock/screen/container/NativeCookingScreenTest.java -@@ -0,0 +1,1409 @@ +@@ -0,0 +1,1410 @@ +/* + * This file is part of ViaFabricPlus Bedrock - https://github.com/ViaVersionAddons/viafabricplus-bedrock + * Copyright (C) 2026 ViaVersion and contributors @@ -11690,7 +11753,8 @@ index 0000000..a3823ec + """ + {"type":"button","size":[18,18],"button_mappings":[ + {"from_button_id":"button.menu_select","to_button_id":"button.recipe_select","mapping_type":"pressed"}, -+ {"from_button_id":"button.menu_secondary_select","to_button_id":"button.recipe_secondary","mapping_type":"pressed"}]} ++ {"from_button_id":"button.menu_secondary_select","to_button_id":"button.recipe_secondary","mapping_type":"pressed"}], ++ "bindings":[{"binding_type":"collection_details","binding_collection_name":"recipe_book"}]} + """) + .getAsJsonObject())); + int y = 60; diff --git a/patches/viafabricplus-bedrock/upstreamable/0029-present-authored-native-containers.pr.md b/patches/viafabricplus-bedrock/upstreamable/0029-present-authored-native-containers.pr.md index 95fb38cd..c197499a 100644 --- a/patches/viafabricplus-bedrock/upstreamable/0029-present-authored-native-containers.pr.md +++ b/patches/viafabricplus-bedrock/upstreamable/0029-present-authored-native-containers.pr.md @@ -172,3 +172,5 @@ Complete native pixels, fallback variant order, variant cycling, and broader rec Container layout supplies the desktop UI pointer to shared cursor placement and invalidates active placement when the pointer moves. Existing bundle admission guards remain until source lifetime, physical-slot rendering and input actions are complete. Tooltip sources retain primary, pending, secondary and hover contexts independently of recipe selection. Desktop hover resolves the retained bundle against actual menu slots and native collection aliases. Regressions cover restoration, carried-item priority, source replacement, touch retention, alias leave events and physical-cell selection. Thirteen original source controls and 22 cell-input controls establish the ported state boundaries. Selected physical children request exclusive detail creations with their supplied native hover text. Popup snapshots retain independent lifetime and ignore late retirement of a replaced creation. Client retirement runs after animation traversal, and input reconciliation discards captures from replaced creations. Regressions cover these boundaries. The full action graph, transfer quantity, native detail visibility, child-description formatting, and installed renderer comparison remain open; active tooltip admission stays guarded. + +Inventory hover events now read the evaluated control property bag. A routed slot-event regression distinguishes the event source from render-slot annotations, observes retained updates and rejects missing or wrongly typed fields. Native 1.26.60.5 controls establish the retained bag and typed source getter. This does not enable the unfinished active bundle controller or establish installed input parity.