From 58c699ad825a2c5da92610f19ee1b28b4aab46e1 Mon Sep 17 00:00:00 2001 From: Natan Date: Fri, 21 Aug 2026 14:34:26 -0300 Subject: [PATCH 1/2] feat: option to allow entity container interactions --- .../inventoryframework/ViewConfig.java | 20 ++++++++++++++++- .../inventoryframework/ViewConfigBuilder.java | 22 +++++++++++++++++++ 2 files changed, 41 insertions(+), 1 deletion(-) diff --git a/inventory-framework-api/src/main/java/me/devnatan/inventoryframework/ViewConfig.java b/inventory-framework-api/src/main/java/me/devnatan/inventoryframework/ViewConfig.java index 6d010e68..3e21fec5 100644 --- a/inventory-framework-api/src/main/java/me/devnatan/inventoryframework/ViewConfig.java +++ b/inventory-framework-api/src/main/java/me/devnatan/inventoryframework/ViewConfig.java @@ -31,6 +31,7 @@ public class ViewConfig { private final Set modifiers; private final long updateIntervalInTicks, interactionDelayInMillis; private final boolean transitiveInitialData; + private final boolean entityContainerInteractionsAllowed; private final TimerState updateIntervalState; public ViewConfig( @@ -43,6 +44,7 @@ public ViewConfig( long updateIntervalInTicks, long interactionDelayInMillis, boolean transitiveInitialData, + boolean entityContainerInteractionsAllowed, TimerState updateIntervalState) { this.title = title; this.size = size; @@ -53,6 +55,7 @@ public ViewConfig( this.updateIntervalInTicks = updateIntervalInTicks; this.interactionDelayInMillis = interactionDelayInMillis; this.transitiveInitialData = transitiveInitialData; + this.entityContainerInteractionsAllowed = entityContainerInteractionsAllowed; this.updateIntervalState = updateIntervalState; } @@ -92,6 +95,17 @@ public boolean isTransitiveInitialData() { return transitiveInitialData; } + /** + * Whether interactions coming from the actor's own container (e.g. their inventory) are + * allowed to happen even when this view is configured to cancel interactions with its own + * container. + * + * @return If interactions on the actor's container are allowed. + */ + public boolean isEntityContainerInteractionsAllowed() { + return entityContainerInteractionsAllowed; + } + @Nullable public TimerState getUpdateIntervalState() { return updateIntervalState; @@ -154,6 +168,7 @@ public ViewConfig merge(ViewConfig other) { merge(other, ViewConfig::getUpdateIntervalInTicks, value -> value != 0), merge(other, ViewConfig::getInteractionDelayInMillis, value -> value != 0), merge(other, ViewConfig::isTransitiveInitialData), + merge(other, ViewConfig::isEntityContainerInteractionsAllowed), merge(other, ViewConfig::getUpdateIntervalState, Objects::nonNull)); } @@ -245,6 +260,7 @@ && getInteractionDelayInMillis() == that.getInteractionDelayInMillis() && Arrays.equals(getLayout(), that.getLayout()) && Objects.equals(getModifiers(), that.getModifiers()) && isTransitiveInitialData() == that.isTransitiveInitialData() + && isEntityContainerInteractionsAllowed() == that.isEntityContainerInteractionsAllowed() && Objects.equals(getUpdateIntervalState(), that.getUpdateIntervalState()); } @@ -259,6 +275,7 @@ public int hashCode() { getUpdateIntervalInTicks(), getInteractionDelayInMillis(), isTransitiveInitialData(), + isEntityContainerInteractionsAllowed(), getUpdateIntervalState()); result = 31 * result + Arrays.hashCode(getLayout()); return result; @@ -275,7 +292,8 @@ public String toString() { + modifiers + ", updateIntervalInTicks=" + updateIntervalInTicks + ", interactionDelayInMillis=" + interactionDelayInMillis + ", transitiveInitialData=" - + transitiveInitialData + ", updateIntervalState=" + + transitiveInitialData + ", entityContainerInteractionsAllowed=" + + entityContainerInteractionsAllowed + ", updateIntervalState=" + updateIntervalState + "}"; } } diff --git a/inventory-framework-api/src/main/java/me/devnatan/inventoryframework/ViewConfigBuilder.java b/inventory-framework-api/src/main/java/me/devnatan/inventoryframework/ViewConfigBuilder.java index addca176..586457c4 100644 --- a/inventory-framework-api/src/main/java/me/devnatan/inventoryframework/ViewConfigBuilder.java +++ b/inventory-framework-api/src/main/java/me/devnatan/inventoryframework/ViewConfigBuilder.java @@ -34,6 +34,7 @@ public final class ViewConfigBuilder { private long updateIntervalInTicks, interactionDelayInMillis; private TimerState updateIntervalState; private boolean transitiveInitialData; + private boolean entityContainerInteractionsAllowed; /** * Inherits all configuration from another {@link ViewConfigBuilder} value. @@ -53,6 +54,7 @@ public ViewConfigBuilder inheritFrom(@NotNull ViewConfigBuilder other) { if (other.updateIntervalState != null) this.updateIntervalState = other.updateIntervalState; if (other.interactionDelayInMillis != 0) this.interactionDelayInMillis = other.interactionDelayInMillis; if (other.transitiveInitialData) this.transitiveInitialData = true; + if (other.entityContainerInteractionsAllowed) this.entityContainerInteractionsAllowed = true; this.options.addAll(other.options); this.modifiers.addAll(other.modifiers); return this; @@ -205,6 +207,21 @@ public ViewConfigBuilder cancelOnDrag() { return addOption(ViewConfig.CANCEL_ON_DRAG); } + /** + * Allows interactions on the actor's own container (e.g. their inventory) to happen even + * though this view cancels interactions with its own container. + *

+ * This only affects interactions on the actor's container, interactions with the view's + * container are unaffected and follow {@link #cancelOnClick()} and other cancel options + * normally. + * + * @return This configuration builder. + */ + public ViewConfigBuilder allowEntityContainerInteractions() { + this.entityContainerInteractionsAllowed = true; + return this; + } + /** * Schedules the view to update every fixed interval. * @@ -295,6 +312,7 @@ public ViewConfig build() { getUpdateIntervalInTicks(), getInteractionDelayInMillis(), transitiveInitialData, + entityContainerInteractionsAllowed, getUpdateIntervalState()); } @@ -341,4 +359,8 @@ long getInteractionDelayInMillis() { public boolean isTransitiveInitialData() { return transitiveInitialData; } + + boolean isEntityContainerInteractionsAllowed() { + return entityContainerInteractionsAllowed; + } } From 680af2c56353ffe591740b2d05a3c76983345248 Mon Sep 17 00:00:00 2001 From: Natan Date: Fri, 21 Aug 2026 14:34:36 -0300 Subject: [PATCH 2/2] feat: click cancellation on entity container --- .../pipeline/GlobalClickInterceptor.java | 6 +++++- .../inventoryframework/pipeline/GlobalClickInterceptor.kt | 8 +++++--- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/inventory-framework-platform-bukkit/src/main/java/me/devnatan/inventoryframework/pipeline/GlobalClickInterceptor.java b/inventory-framework-platform-bukkit/src/main/java/me/devnatan/inventoryframework/pipeline/GlobalClickInterceptor.java index ec3bd553..dea48fd8 100644 --- a/inventory-framework-platform-bukkit/src/main/java/me/devnatan/inventoryframework/pipeline/GlobalClickInterceptor.java +++ b/inventory-framework-platform-bukkit/src/main/java/me/devnatan/inventoryframework/pipeline/GlobalClickInterceptor.java @@ -20,8 +20,12 @@ public void intercept(@NotNull PipelineContext pipeline, @NotNull V final SlotClickContext context = (SlotClickContext) subject; final InventoryClickEvent event = context.getClickOrigin(); + final boolean allowedOnEntityContainer = + context.isOnEntityContainer() && context.getConfig().isEntityContainerInteractionsAllowed(); + // inherit cancellation so we can un-cancel it - context.setCancelled(event.isCancelled() || context.getConfig().isOptionSet(CANCEL_ON_CLICK, true)); + context.setCancelled(event.isCancelled() + || (context.getConfig().isOptionSet(CANCEL_ON_CLICK, true) && !allowedOnEntityContainer)); context.getRoot().onClick(context); } } diff --git a/inventory-framework-platform-minestom/src/main/kotlin/me/devnatan/inventoryframework/pipeline/GlobalClickInterceptor.kt b/inventory-framework-platform-minestom/src/main/kotlin/me/devnatan/inventoryframework/pipeline/GlobalClickInterceptor.kt index c06c2b32..4a50c7ef 100644 --- a/inventory-framework-platform-minestom/src/main/kotlin/me/devnatan/inventoryframework/pipeline/GlobalClickInterceptor.kt +++ b/inventory-framework-platform-minestom/src/main/kotlin/me/devnatan/inventoryframework/pipeline/GlobalClickInterceptor.kt @@ -18,10 +18,12 @@ class GlobalClickInterceptor : PipelineInterceptor { val event: InventoryPreClickEvent = subject.clickOrigin + val allowedOnEntityContainer = + subject.isOnEntityContainer && subject.config.isEntityContainerInteractionsAllowed + val cancelOnClick = subject.config.isOptionSet(ViewConfig.CANCEL_ON_CLICK, true) + // inherit cancellation so we can un-cancel it - subject.isCancelled = - event.isCancelled || - subject.config.isOptionSet(ViewConfig.CANCEL_ON_CLICK, true) + subject.isCancelled = event.isCancelled || (cancelOnClick && !allowedOnEntityContainer) subject.root.onClick(subject) } }