From 3b5dd1c1b1c3487ffc6747635c5f9b6cd64f0565 Mon Sep 17 00:00:00 2001 From: Natan Date: Thu, 13 Nov 2025 12:39:30 -0300 Subject: [PATCH 01/16] feat: expose Component in slot contexts --- .../context/IFSlotClickContext.java | 2 -- .../inventoryframework/context/IFSlotContext.java | 14 ++++++++++++++ .../internal/ElementFactory.java | 2 +- .../context/AbstractIFContext.java | 2 +- .../context/SlotRenderContext.java | 12 ++++++++++-- .../internal/BukkitElementFactory.java | 4 ++-- .../internal/MockElementFactory.java | 3 ++- 7 files changed, 30 insertions(+), 9 deletions(-) diff --git a/inventory-framework-api/src/main/java/me/devnatan/inventoryframework/context/IFSlotClickContext.java b/inventory-framework-api/src/main/java/me/devnatan/inventoryframework/context/IFSlotClickContext.java index 78a390b63..584d56c1f 100644 --- a/inventory-framework-api/src/main/java/me/devnatan/inventoryframework/context/IFSlotClickContext.java +++ b/inventory-framework-api/src/main/java/me/devnatan/inventoryframework/context/IFSlotClickContext.java @@ -17,8 +17,6 @@ public interface IFSlotClickContext extends IFSlotContext, IFConfinedContext { @NotNull ViewContainer getClickedContainer(); - Component getComponent(); - int getClickedSlot(); /** diff --git a/inventory-framework-api/src/main/java/me/devnatan/inventoryframework/context/IFSlotContext.java b/inventory-framework-api/src/main/java/me/devnatan/inventoryframework/context/IFSlotContext.java index 86c338d13..e77693f6e 100644 --- a/inventory-framework-api/src/main/java/me/devnatan/inventoryframework/context/IFSlotContext.java +++ b/inventory-framework-api/src/main/java/me/devnatan/inventoryframework/context/IFSlotContext.java @@ -1,8 +1,10 @@ package me.devnatan.inventoryframework.context; import me.devnatan.inventoryframework.ViewContainer; +import me.devnatan.inventoryframework.component.Component; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.UnknownNullability; /** * Represents a context in which there is a specific slot related to it, the main context @@ -59,4 +61,16 @@ public interface IFSlotContext extends IFContext { */ @NotNull ViewContainer getContainer(); + + /** + * The component placed at that slot. + * + *

This is an internal inventory-framework API that should not be used from outside of + * this library. No compatibility guarantees are provided. + * + * @return The component placed at that slot. + */ + @ApiStatus.Internal + @UnknownNullability + Component getComponent(); } diff --git a/inventory-framework-api/src/main/java/me/devnatan/inventoryframework/internal/ElementFactory.java b/inventory-framework-api/src/main/java/me/devnatan/inventoryframework/internal/ElementFactory.java index 97de9a70a..d5aad0dbc 100644 --- a/inventory-framework-api/src/main/java/me/devnatan/inventoryframework/internal/ElementFactory.java +++ b/inventory-framework-api/src/main/java/me/devnatan/inventoryframework/internal/ElementFactory.java @@ -68,7 +68,7 @@ public abstract IFSlotClickContext createSlotClickContext( boolean combined); public abstract IFSlotRenderContext createSlotRenderContext( - int slot, @NotNull IFRenderContext parent, @Nullable Viewer viewer); + int slot, @NotNull IFRenderContext parent, @Nullable Viewer viewer, Component component); /** * Creates a new close context for the current platform. diff --git a/inventory-framework-core/src/main/java/me/devnatan/inventoryframework/context/AbstractIFContext.java b/inventory-framework-core/src/main/java/me/devnatan/inventoryframework/context/AbstractIFContext.java index aa576eb6b..3d9b3494c 100644 --- a/inventory-framework-core/src/main/java/me/devnatan/inventoryframework/context/AbstractIFContext.java +++ b/inventory-framework-core/src/main/java/me/devnatan/inventoryframework/context/AbstractIFContext.java @@ -114,7 +114,7 @@ private IFSlotRenderContext createSlotRenderContext(@NotNull Component component final IFRenderContext renderContext = (IFRenderContext) this; final IFSlotRenderContext slotRender = getRoot() .getElementFactory() - .createSlotRenderContext(component.getPosition(), renderContext, renderContext.getViewer()); + .createSlotRenderContext(component.getPosition(), renderContext, renderContext.getViewer(), component); slotRender.setForceUpdate(force); return slotRender; } diff --git a/inventory-framework-platform-bukkit/src/main/java/me/devnatan/inventoryframework/context/SlotRenderContext.java b/inventory-framework-platform-bukkit/src/main/java/me/devnatan/inventoryframework/context/SlotRenderContext.java index 414de0ad2..61474cae7 100644 --- a/inventory-framework-platform-bukkit/src/main/java/me/devnatan/inventoryframework/context/SlotRenderContext.java +++ b/inventory-framework-platform-bukkit/src/main/java/me/devnatan/inventoryframework/context/SlotRenderContext.java @@ -3,6 +3,7 @@ import me.devnatan.inventoryframework.BukkitViewer; import me.devnatan.inventoryframework.RootView; import me.devnatan.inventoryframework.Viewer; +import me.devnatan.inventoryframework.component.Component; import org.bukkit.entity.Player; import org.bukkit.inventory.ItemStack; import org.jetbrains.annotations.ApiStatus; @@ -14,19 +15,26 @@ public class SlotRenderContext extends SlotContext implements IFSlotRenderContex private final Player player; private final Viewer viewer; + private final Component component; private ItemStack item; private boolean cancelled; private boolean changed; private boolean forceUpdate; @ApiStatus.Internal - public SlotRenderContext(int slot, @NotNull IFRenderContext parent, @Nullable Viewer viewer) { + public SlotRenderContext(int slot, @NotNull IFRenderContext parent, @Nullable Viewer viewer, Component component) { super(slot, parent); this.viewer = viewer; this.player = viewer == null ? null : ((BukkitViewer) viewer).getPlayer(); + this.component = component; } - @Override + @Override + public final Component getComponent() { + return component; + } + + @Override public final @UnknownNullability Player getPlayer() { return player; } diff --git a/inventory-framework-platform-bukkit/src/main/java/me/devnatan/inventoryframework/internal/BukkitElementFactory.java b/inventory-framework-platform-bukkit/src/main/java/me/devnatan/inventoryframework/internal/BukkitElementFactory.java index 245696266..3fbaedbd5 100644 --- a/inventory-framework-platform-bukkit/src/main/java/me/devnatan/inventoryframework/internal/BukkitElementFactory.java +++ b/inventory-framework-platform-bukkit/src/main/java/me/devnatan/inventoryframework/internal/BukkitElementFactory.java @@ -115,8 +115,8 @@ public IFSlotClickContext createSlotClickContext( @Override public IFSlotRenderContext createSlotRenderContext( - int slot, @NotNull IFRenderContext parent, @Nullable Viewer viewer) { - return new SlotRenderContext(slot, parent, viewer); + int slot, @NotNull IFRenderContext parent, @Nullable Viewer viewer, Component component) { + return new SlotRenderContext(slot, parent, viewer, component); } @Override diff --git a/inventory-framework-test/src/main/java/me/devnatan/inventoryframework/internal/MockElementFactory.java b/inventory-framework-test/src/main/java/me/devnatan/inventoryframework/internal/MockElementFactory.java index d1d311faa..78979e7fd 100644 --- a/inventory-framework-test/src/main/java/me/devnatan/inventoryframework/internal/MockElementFactory.java +++ b/inventory-framework-test/src/main/java/me/devnatan/inventoryframework/internal/MockElementFactory.java @@ -86,12 +86,13 @@ public IFSlotClickContext createSlotClickContext( @Override public IFSlotRenderContext createSlotRenderContext( - int slot, @NotNull IFRenderContext parent, @Nullable Viewer viewer) { + int slot, @NotNull IFRenderContext parent, @Nullable Viewer viewer, Component component) { IFSlotRenderContext mock = mock(IFSlotRenderContext.class); when(mock.getSlot()).thenReturn(slot); when(mock.getParent()).thenReturn(parent); when(mock.getViewer()).thenReturn(viewer); when(mock.getContainer()).then(ignored -> parent.getContainer()); + when(mock.getComponent()).then(component); return mock; } From ccce68cb719b58a8c8593494b83d99f9e96f8f2e Mon Sep 17 00:00:00 2001 From: Natan Date: Thu, 13 Nov 2025 12:39:41 -0300 Subject: [PATCH 02/16] feat: key factory to item builder --- .../component/ComponentBuilder.java | 24 +++++++ .../component/ItemComponent.java | 30 +++++++-- .../component/DefaultComponentBuilder.java | 22 +++++++ .../component/BukkitItemComponentBuilder.java | 13 ++++ .../component/MinestomItemComponentBuilder.kt | 62 ++++++++++--------- 5 files changed, 117 insertions(+), 34 deletions(-) diff --git a/inventory-framework-api/src/main/java/me/devnatan/inventoryframework/component/ComponentBuilder.java b/inventory-framework-api/src/main/java/me/devnatan/inventoryframework/component/ComponentBuilder.java index 03217b9f4..724db372f 100644 --- a/inventory-framework-api/src/main/java/me/devnatan/inventoryframework/component/ComponentBuilder.java +++ b/inventory-framework-api/src/main/java/me/devnatan/inventoryframework/component/ComponentBuilder.java @@ -1,7 +1,10 @@ package me.devnatan.inventoryframework.component; import java.util.function.BooleanSupplier; +import java.util.function.Function; import java.util.function.Predicate; +import java.util.function.Supplier; + import me.devnatan.inventoryframework.Ref; import me.devnatan.inventoryframework.context.IFContext; import me.devnatan.inventoryframework.state.State; @@ -162,4 +165,25 @@ public interface ComponentBuilder, C extends IF * @see #displayIf(Predicate) */ S hideIf(Predicate condition); + + /** + * *** This API is experimental and is not subject to the general compatibility guarantees such + * API may be changed or may be removed completely in any further release. *** + */ + @ApiStatus.Experimental + S identifiedAs(String key); + + /** + * *** This API is experimental and is not subject to the general compatibility guarantees such + * API may be changed or may be removed completely in any further release. *** + */ + @ApiStatus.Experimental + S identifiedAs(Supplier keyProvider); + + /** + * *** This API is experimental and is not subject to the general compatibility guarantees such + * API may be changed or may be removed completely in any further release. *** + */ + @ApiStatus.Experimental + S identifiedAs(Function keyProvider); } diff --git a/inventory-framework-api/src/main/java/me/devnatan/inventoryframework/component/ItemComponent.java b/inventory-framework-api/src/main/java/me/devnatan/inventoryframework/component/ItemComponent.java index 85cac589c..432dfb2e5 100644 --- a/inventory-framework-api/src/main/java/me/devnatan/inventoryframework/component/ItemComponent.java +++ b/inventory-framework-api/src/main/java/me/devnatan/inventoryframework/component/ItemComponent.java @@ -5,7 +5,10 @@ import java.util.Set; import java.util.UUID; import java.util.function.Consumer; +import java.util.function.Function; import java.util.function.Predicate; +import java.util.function.Supplier; + import me.devnatan.inventoryframework.InventoryFrameworkException; import me.devnatan.inventoryframework.Ref; import me.devnatan.inventoryframework.VirtualView; @@ -17,7 +20,7 @@ // TODO Make this render abstract and remove `getResult` (Object) from IFSlotRenderContext public class ItemComponent implements Component, InteractionHandler { - private final String key = UUID.randomUUID().toString(); + private final Function keyFactory; private final VirtualView root; private int position; private final Object stack; @@ -30,10 +33,13 @@ public class ItemComponent implements Component, InteractionHandler { private final Set> watching; private final boolean isManagedExternally; private final boolean updateOnClick; - private boolean isVisible; private final Ref reference; - public ItemComponent( + private boolean isVisible; + private String lastKey; + + public ItemComponent( + Function keyFactory, VirtualView root, int position, Object stack, @@ -48,6 +54,8 @@ public ItemComponent( boolean updateOnClick, boolean isVisible, Ref reference) { + //noinspection unchecked + this.keyFactory = (Function) keyFactory; this.root = root; this.position = position; this.stack = stack; @@ -66,9 +74,13 @@ public ItemComponent( @Override public String getKey() { - return key; + return lastKey; } + public String useKey(IFContext context) { + return lastKey = keyFactory.apply(context); + } + @NotNull @Override public VirtualView getRoot() { @@ -182,8 +194,14 @@ public void render(@NotNull IFSlotRenderContext context) { public void updated(@NotNull IFSlotRenderContext context) { if (context.isCancelled()) return; - // Static item with no `displayIf` must not even reach the update handler - if (!context.isForceUpdate() && displayCondition == null && getRenderHandler() == null) return; + // Not a force update, item is not dynamic (`onRender` or `displayIf` not set) + if (!context.isForceUpdate()) { + final boolean staticItem = displayCondition == null && getRenderHandler() == null; + if (staticItem) return; + + // Item is dynamic but key hasn't changed + if (Objects.equals(lastKey, useKey(context))) return; + } if (isVisible() && getUpdateHandler() != null) { getUpdateHandler().accept(context); diff --git a/inventory-framework-core/src/main/java/me/devnatan/inventoryframework/component/DefaultComponentBuilder.java b/inventory-framework-core/src/main/java/me/devnatan/inventoryframework/component/DefaultComponentBuilder.java index 818ba2c84..15614082d 100644 --- a/inventory-framework-core/src/main/java/me/devnatan/inventoryframework/component/DefaultComponentBuilder.java +++ b/inventory-framework-core/src/main/java/me/devnatan/inventoryframework/component/DefaultComponentBuilder.java @@ -6,7 +6,10 @@ import java.util.Map; import java.util.Set; import java.util.function.BooleanSupplier; +import java.util.function.Function; import java.util.function.Predicate; +import java.util.function.Supplier; + import me.devnatan.inventoryframework.Ref; import me.devnatan.inventoryframework.context.IFContext; import me.devnatan.inventoryframework.state.State; @@ -16,6 +19,7 @@ public abstract class DefaultComponentBuilder, C extends IFContext> implements ComponentBuilder { + protected Function keyFactory; protected Ref reference; protected Map data; protected boolean cancelOnClick, closeOnClick, updateOnClick; @@ -24,6 +28,7 @@ public abstract class DefaultComponentBuilder, protected Predicate displayCondition; protected DefaultComponentBuilder( + Function keyFactory, Ref reference, Map data, boolean cancelOnClick, @@ -32,6 +37,7 @@ protected DefaultComponentBuilder( Set> watchingStates, boolean isManagedExternally, Predicate displayCondition) { + this.keyFactory = keyFactory; this.reference = reference; this.data = data; this.cancelOnClick = cancelOnClick; @@ -122,4 +128,20 @@ public S hideIf(Predicate condition) { public S hideIf(BooleanSupplier condition) { return displayIf(condition == null ? null : () -> !condition.getAsBoolean()); } + + @Override + public S identifiedAs(String key) { + return identifiedAs(() -> key); + } + + @Override + public S identifiedAs(Supplier key) { + return identifiedAs(__ -> key.get()); + } + + @Override + public S identifiedAs(Function keyFactory) { + this.keyFactory = keyFactory; + return (S) this; + } } diff --git a/inventory-framework-platform-bukkit/src/main/java/me/devnatan/inventoryframework/component/BukkitItemComponentBuilder.java b/inventory-framework-platform-bukkit/src/main/java/me/devnatan/inventoryframework/component/BukkitItemComponentBuilder.java index 81ea7741b..343eaa600 100644 --- a/inventory-framework-platform-bukkit/src/main/java/me/devnatan/inventoryframework/component/BukkitItemComponentBuilder.java +++ b/inventory-framework-platform-bukkit/src/main/java/me/devnatan/inventoryframework/component/BukkitItemComponentBuilder.java @@ -4,7 +4,9 @@ import java.util.LinkedHashSet; import java.util.Map; import java.util.Set; +import java.util.UUID; import java.util.function.Consumer; +import java.util.function.Function; import java.util.function.Predicate; import java.util.function.Supplier; import me.devnatan.inventoryframework.Ref; @@ -14,12 +16,15 @@ import me.devnatan.inventoryframework.state.State; import me.devnatan.inventoryframework.utils.SlotConverter; import org.bukkit.inventory.ItemStack; +import org.checkerframework.checker.units.qual.C; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; public final class BukkitItemComponentBuilder extends DefaultComponentBuilder implements ItemComponentBuilder, ComponentFactory { + private static final Function RANDOM_KEY_FACTORY = __ -> UUID.randomUUID().toString(); + private final VirtualView root; private int slot; private ItemStack item; @@ -29,6 +34,7 @@ public final class BukkitItemComponentBuilder extends DefaultComponentBuilder keyFactory, VirtualView root, int slot, ItemStack item, @@ -61,6 +68,7 @@ private BukkitItemComponentBuilder( boolean isManagedExternally, Predicate displayCondition) { super( + keyFactory, reference, data, cancelOnClick, @@ -188,7 +196,11 @@ public BukkitItemComponentBuilder onUpdate(@Nullable Consumer componentKeyProvider = keyFactory == null + ? RANDOM_KEY_FACTORY + : keyFactory; return new ItemComponent( + componentKeyProvider, root, slot, item, @@ -208,6 +220,7 @@ public BukkitItemComponentBuilder onUpdate(@Nullable Consumer?, @@ -35,6 +37,7 @@ class MinestomItemComponentBuilder isManagedExternally: Boolean, displayCondition: Predicate?, ) : DefaultComponentBuilder( + key, reference, data, cancelOnClick, @@ -55,20 +58,21 @@ class MinestomItemComponentBuilder constructor( root: VirtualView, ) : this( - root, - -1, - null, - null, - null, - null, - null, - HashMap(), - false, - false, - false, - LinkedHashSet>(), - false, - null, + root = root, + key = null, + slot = -1, + item = null, + renderHandler = null, + clickHandler = null, + updateHandler = null, + reference = null, + data = HashMap(), + cancelOnClick = false, + closeOnClick = false, + updateOnClick = false, + watchingStates = LinkedHashSet>(), + isManagedExternally = false, + displayCondition = null, ) init { @@ -205,6 +209,7 @@ class MinestomItemComponentBuilder override fun create(): Component = ItemComponent( + key ?: UUID.randomUUID().toString(), root, slot, item, @@ -223,19 +228,20 @@ class MinestomItemComponentBuilder override fun copy(): MinestomItemComponentBuilder = MinestomItemComponentBuilder( - root, - slot, - item, - renderHandler, - clickHandler, - updateHandler, - reference, - data, - cancelOnClick, - closeOnClick, - updateOnClick, - watchingStates, - isManagedExternally, - displayCondition, + root = root, + key = key, + slot = slot, + item = item, + renderHandler = renderHandler, + clickHandler = clickHandler, + updateHandler = updateHandler, + reference = reference, + data = data, + cancelOnClick = cancelOnClick, + closeOnClick = closeOnClick, + updateOnClick = updateOnClick, + watchingStates = watchingStates, + isManagedExternally = isManagedExternally, + displayCondition = displayCondition, ) } From 1623ae3379bf44426cd56f60dc8d2a35aca984f9 Mon Sep 17 00:00:00 2001 From: Natan Date: Wed, 26 Nov 2025 13:06:17 -0300 Subject: [PATCH 03/16] refactor: `identifiedAs` -> `identifiedBy` --- .../inventoryframework/component/ComponentBuilder.java | 6 +++--- .../component/DefaultComponentBuilder.java | 10 +++++----- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/inventory-framework-api/src/main/java/me/devnatan/inventoryframework/component/ComponentBuilder.java b/inventory-framework-api/src/main/java/me/devnatan/inventoryframework/component/ComponentBuilder.java index 724db372f..67f30eeda 100644 --- a/inventory-framework-api/src/main/java/me/devnatan/inventoryframework/component/ComponentBuilder.java +++ b/inventory-framework-api/src/main/java/me/devnatan/inventoryframework/component/ComponentBuilder.java @@ -171,19 +171,19 @@ public interface ComponentBuilder, C extends IF * API may be changed or may be removed completely in any further release. *** */ @ApiStatus.Experimental - S identifiedAs(String key); + S identifiedBy(String key); /** * *** This API is experimental and is not subject to the general compatibility guarantees such * API may be changed or may be removed completely in any further release. *** */ @ApiStatus.Experimental - S identifiedAs(Supplier keyProvider); + S identifiedBy(Supplier keyProvider); /** * *** This API is experimental and is not subject to the general compatibility guarantees such * API may be changed or may be removed completely in any further release. *** */ @ApiStatus.Experimental - S identifiedAs(Function keyProvider); + S identifiedBy(Function keyProvider); } diff --git a/inventory-framework-core/src/main/java/me/devnatan/inventoryframework/component/DefaultComponentBuilder.java b/inventory-framework-core/src/main/java/me/devnatan/inventoryframework/component/DefaultComponentBuilder.java index 15614082d..b3005c65d 100644 --- a/inventory-framework-core/src/main/java/me/devnatan/inventoryframework/component/DefaultComponentBuilder.java +++ b/inventory-framework-core/src/main/java/me/devnatan/inventoryframework/component/DefaultComponentBuilder.java @@ -130,17 +130,17 @@ public S hideIf(BooleanSupplier condition) { } @Override - public S identifiedAs(String key) { - return identifiedAs(() -> key); + public S identifiedBy(String key) { + return identifiedBy(() -> key); } @Override - public S identifiedAs(Supplier key) { - return identifiedAs(__ -> key.get()); + public S identifiedBy(Supplier key) { + return identifiedBy(__ -> key.get()); } @Override - public S identifiedAs(Function keyFactory) { + public S identifiedBy(Function keyFactory) { this.keyFactory = keyFactory; return (S) this; } From 9e3d98c531321623bf6056d7ee25e787604c9e0c Mon Sep 17 00:00:00 2001 From: Natan Date: Wed, 26 Nov 2025 13:06:45 -0300 Subject: [PATCH 04/16] refactor: lint codebase --- .../component/ComponentBuilder.java | 41 +++++++++---------- .../component/ItemComponent.java | 37 ++++++++--------- .../context/IFSlotClickContext.java | 1 - .../context/IFSlotContext.java | 22 +++++----- .../component/DefaultComponentBuilder.java | 33 ++++++++------- .../component/BukkitItemComponentBuilder.java | 19 ++++----- .../context/SlotRenderContext.java | 14 +++---- .../internal/MockElementFactory.java | 2 +- 8 files changed, 81 insertions(+), 88 deletions(-) diff --git a/inventory-framework-api/src/main/java/me/devnatan/inventoryframework/component/ComponentBuilder.java b/inventory-framework-api/src/main/java/me/devnatan/inventoryframework/component/ComponentBuilder.java index 67f30eeda..c35509734 100644 --- a/inventory-framework-api/src/main/java/me/devnatan/inventoryframework/component/ComponentBuilder.java +++ b/inventory-framework-api/src/main/java/me/devnatan/inventoryframework/component/ComponentBuilder.java @@ -4,7 +4,6 @@ import java.util.function.Function; import java.util.function.Predicate; import java.util.function.Supplier; - import me.devnatan.inventoryframework.Ref; import me.devnatan.inventoryframework.context.IFContext; import me.devnatan.inventoryframework.state.State; @@ -166,24 +165,24 @@ public interface ComponentBuilder, C extends IF */ S hideIf(Predicate condition); - /** - * *** This API is experimental and is not subject to the general compatibility guarantees such - * API may be changed or may be removed completely in any further release. *** - */ - @ApiStatus.Experimental - S identifiedBy(String key); - - /** - * *** This API is experimental and is not subject to the general compatibility guarantees such - * API may be changed or may be removed completely in any further release. *** - */ - @ApiStatus.Experimental - S identifiedBy(Supplier keyProvider); - - /** - * *** This API is experimental and is not subject to the general compatibility guarantees such - * API may be changed or may be removed completely in any further release. *** - */ - @ApiStatus.Experimental - S identifiedBy(Function keyProvider); + /** + * *** This API is experimental and is not subject to the general compatibility guarantees such + * API may be changed or may be removed completely in any further release. *** + */ + @ApiStatus.Experimental + S identifiedBy(String key); + + /** + * *** This API is experimental and is not subject to the general compatibility guarantees such + * API may be changed or may be removed completely in any further release. *** + */ + @ApiStatus.Experimental + S identifiedBy(Supplier keyProvider); + + /** + * *** This API is experimental and is not subject to the general compatibility guarantees such + * API may be changed or may be removed completely in any further release. *** + */ + @ApiStatus.Experimental + S identifiedBy(Function keyProvider); } diff --git a/inventory-framework-api/src/main/java/me/devnatan/inventoryframework/component/ItemComponent.java b/inventory-framework-api/src/main/java/me/devnatan/inventoryframework/component/ItemComponent.java index 432dfb2e5..3b7fec560 100644 --- a/inventory-framework-api/src/main/java/me/devnatan/inventoryframework/component/ItemComponent.java +++ b/inventory-framework-api/src/main/java/me/devnatan/inventoryframework/component/ItemComponent.java @@ -3,12 +3,9 @@ import java.util.Collections; import java.util.Objects; import java.util.Set; -import java.util.UUID; import java.util.function.Consumer; import java.util.function.Function; import java.util.function.Predicate; -import java.util.function.Supplier; - import me.devnatan.inventoryframework.InventoryFrameworkException; import me.devnatan.inventoryframework.Ref; import me.devnatan.inventoryframework.VirtualView; @@ -35,11 +32,11 @@ public class ItemComponent implements Component, InteractionHandler { private final boolean updateOnClick; private final Ref reference; - private boolean isVisible; - private String lastKey; + private boolean isVisible; + private String lastKey; - public ItemComponent( - Function keyFactory, + public ItemComponent( + Function keyFactory, VirtualView root, int position, Object stack, @@ -54,8 +51,8 @@ public ItemComponent( boolean updateOnClick, boolean isVisible, Ref reference) { - //noinspection unchecked - this.keyFactory = (Function) keyFactory; + //noinspection unchecked + this.keyFactory = (Function) keyFactory; this.root = root; this.position = position; this.stack = stack; @@ -74,12 +71,12 @@ public ItemComponent( @Override public String getKey() { - return lastKey; + return lastKey; } - public String useKey(IFContext context) { - return lastKey = keyFactory.apply(context); - } + public String useKey(IFContext context) { + return lastKey = keyFactory.apply(context); + } @NotNull @Override @@ -194,14 +191,14 @@ public void render(@NotNull IFSlotRenderContext context) { public void updated(@NotNull IFSlotRenderContext context) { if (context.isCancelled()) return; - // Not a force update, item is not dynamic (`onRender` or `displayIf` not set) - if (!context.isForceUpdate()) { - final boolean staticItem = displayCondition == null && getRenderHandler() == null; - if (staticItem) return; + // Not a force update, item is not dynamic (`onRender` or `displayIf` not set) + if (!context.isForceUpdate()) { + final boolean staticItem = displayCondition == null && getRenderHandler() == null; + if (staticItem) return; - // Item is dynamic but key hasn't changed - if (Objects.equals(lastKey, useKey(context))) return; - } + // Item is dynamic but key hasn't changed + if (Objects.equals(lastKey, useKey(context))) return; + } if (isVisible() && getUpdateHandler() != null) { getUpdateHandler().accept(context); diff --git a/inventory-framework-api/src/main/java/me/devnatan/inventoryframework/context/IFSlotClickContext.java b/inventory-framework-api/src/main/java/me/devnatan/inventoryframework/context/IFSlotClickContext.java index 584d56c1f..b95ae3883 100644 --- a/inventory-framework-api/src/main/java/me/devnatan/inventoryframework/context/IFSlotClickContext.java +++ b/inventory-framework-api/src/main/java/me/devnatan/inventoryframework/context/IFSlotClickContext.java @@ -1,7 +1,6 @@ package me.devnatan.inventoryframework.context; import me.devnatan.inventoryframework.ViewContainer; -import me.devnatan.inventoryframework.component.Component; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; diff --git a/inventory-framework-api/src/main/java/me/devnatan/inventoryframework/context/IFSlotContext.java b/inventory-framework-api/src/main/java/me/devnatan/inventoryframework/context/IFSlotContext.java index e77693f6e..17128d1b1 100644 --- a/inventory-framework-api/src/main/java/me/devnatan/inventoryframework/context/IFSlotContext.java +++ b/inventory-framework-api/src/main/java/me/devnatan/inventoryframework/context/IFSlotContext.java @@ -62,15 +62,15 @@ public interface IFSlotContext extends IFContext { @NotNull ViewContainer getContainer(); - /** - * The component placed at that slot. - * - *

This is an internal inventory-framework API that should not be used from outside of - * this library. No compatibility guarantees are provided. - * - * @return The component placed at that slot. - */ - @ApiStatus.Internal - @UnknownNullability - Component getComponent(); + /** + * The component placed at that slot. + * + *

This is an internal inventory-framework API that should not be used from outside of + * this library. No compatibility guarantees are provided. + * + * @return The component placed at that slot. + */ + @ApiStatus.Internal + @UnknownNullability + Component getComponent(); } diff --git a/inventory-framework-core/src/main/java/me/devnatan/inventoryframework/component/DefaultComponentBuilder.java b/inventory-framework-core/src/main/java/me/devnatan/inventoryframework/component/DefaultComponentBuilder.java index b3005c65d..2506d7cb1 100644 --- a/inventory-framework-core/src/main/java/me/devnatan/inventoryframework/component/DefaultComponentBuilder.java +++ b/inventory-framework-core/src/main/java/me/devnatan/inventoryframework/component/DefaultComponentBuilder.java @@ -9,7 +9,6 @@ import java.util.function.Function; import java.util.function.Predicate; import java.util.function.Supplier; - import me.devnatan.inventoryframework.Ref; import me.devnatan.inventoryframework.context.IFContext; import me.devnatan.inventoryframework.state.State; @@ -19,7 +18,7 @@ public abstract class DefaultComponentBuilder, C extends IFContext> implements ComponentBuilder { - protected Function keyFactory; + protected Function keyFactory; protected Ref reference; protected Map data; protected boolean cancelOnClick, closeOnClick, updateOnClick; @@ -28,7 +27,7 @@ public abstract class DefaultComponentBuilder, protected Predicate displayCondition; protected DefaultComponentBuilder( - Function keyFactory, + Function keyFactory, Ref reference, Map data, boolean cancelOnClick, @@ -37,7 +36,7 @@ protected DefaultComponentBuilder( Set> watchingStates, boolean isManagedExternally, Predicate displayCondition) { - this.keyFactory = keyFactory; + this.keyFactory = keyFactory; this.reference = reference; this.data = data; this.cancelOnClick = cancelOnClick; @@ -129,19 +128,19 @@ public S hideIf(BooleanSupplier condition) { return displayIf(condition == null ? null : () -> !condition.getAsBoolean()); } - @Override - public S identifiedBy(String key) { - return identifiedBy(() -> key); - } + @Override + public S identifiedBy(String key) { + return identifiedBy(() -> key); + } - @Override - public S identifiedBy(Supplier key) { - return identifiedBy(__ -> key.get()); - } + @Override + public S identifiedBy(Supplier key) { + return identifiedBy(__ -> key.get()); + } - @Override - public S identifiedBy(Function keyFactory) { - this.keyFactory = keyFactory; - return (S) this; - } + @Override + public S identifiedBy(Function keyFactory) { + this.keyFactory = keyFactory; + return (S) this; + } } diff --git a/inventory-framework-platform-bukkit/src/main/java/me/devnatan/inventoryframework/component/BukkitItemComponentBuilder.java b/inventory-framework-platform-bukkit/src/main/java/me/devnatan/inventoryframework/component/BukkitItemComponentBuilder.java index 343eaa600..c4daec90e 100644 --- a/inventory-framework-platform-bukkit/src/main/java/me/devnatan/inventoryframework/component/BukkitItemComponentBuilder.java +++ b/inventory-framework-platform-bukkit/src/main/java/me/devnatan/inventoryframework/component/BukkitItemComponentBuilder.java @@ -16,14 +16,14 @@ import me.devnatan.inventoryframework.state.State; import me.devnatan.inventoryframework.utils.SlotConverter; import org.bukkit.inventory.ItemStack; -import org.checkerframework.checker.units.qual.C; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; public final class BukkitItemComponentBuilder extends DefaultComponentBuilder implements ItemComponentBuilder, ComponentFactory { - private static final Function RANDOM_KEY_FACTORY = __ -> UUID.randomUUID().toString(); + private static final Function RANDOM_KEY_FACTORY = + __ -> UUID.randomUUID().toString(); private final VirtualView root; private int slot; @@ -34,7 +34,7 @@ public final class BukkitItemComponentBuilder extends DefaultComponentBuilder keyFactory, + Function keyFactory, VirtualView root, int slot, ItemStack item, @@ -68,7 +68,7 @@ private BukkitItemComponentBuilder( boolean isManagedExternally, Predicate displayCondition) { super( - keyFactory, + keyFactory, reference, data, cancelOnClick, @@ -196,11 +196,10 @@ public BukkitItemComponentBuilder onUpdate(@Nullable Consumer componentKeyProvider = keyFactory == null - ? RANDOM_KEY_FACTORY - : keyFactory; + final Function componentKeyProvider = + keyFactory == null ? RANDOM_KEY_FACTORY : keyFactory; return new ItemComponent( - componentKeyProvider, + componentKeyProvider, root, slot, item, @@ -220,7 +219,7 @@ public BukkitItemComponentBuilder onUpdate(@Nullable Consumer parent.getContainer()); - when(mock.getComponent()).then(component); + when(mock.getComponent()).then(component); return mock; } From 0d21eacd0f11d539a07bd2409583911ef24bcbe2 Mon Sep 17 00:00:00 2001 From: Natan Date: Wed, 26 Nov 2025 13:18:55 -0300 Subject: [PATCH 05/16] fix: minestom compilation issues --- .../component/DefaultComponentBuilder.java | 4 ++ .../component/BukkitItemComponentBuilder.java | 3 -- .../component/MinestomItemComponentBuilder.kt | 48 +++++++++++-------- .../context/SlotRenderContext.kt | 5 ++ .../internal/MinestomElementFactory.kt | 3 +- .../internal/MockElementFactory.java | 2 +- 6 files changed, 39 insertions(+), 26 deletions(-) diff --git a/inventory-framework-core/src/main/java/me/devnatan/inventoryframework/component/DefaultComponentBuilder.java b/inventory-framework-core/src/main/java/me/devnatan/inventoryframework/component/DefaultComponentBuilder.java index 2506d7cb1..4708d1a10 100644 --- a/inventory-framework-core/src/main/java/me/devnatan/inventoryframework/component/DefaultComponentBuilder.java +++ b/inventory-framework-core/src/main/java/me/devnatan/inventoryframework/component/DefaultComponentBuilder.java @@ -5,6 +5,7 @@ import java.util.LinkedHashSet; import java.util.Map; import java.util.Set; +import java.util.UUID; import java.util.function.BooleanSupplier; import java.util.function.Function; import java.util.function.Predicate; @@ -18,6 +19,9 @@ public abstract class DefaultComponentBuilder, C extends IFContext> implements ComponentBuilder { + protected static final Function RANDOM_KEY_FACTORY = + __ -> UUID.randomUUID().toString(); + protected Function keyFactory; protected Ref reference; protected Map data; diff --git a/inventory-framework-platform-bukkit/src/main/java/me/devnatan/inventoryframework/component/BukkitItemComponentBuilder.java b/inventory-framework-platform-bukkit/src/main/java/me/devnatan/inventoryframework/component/BukkitItemComponentBuilder.java index c4daec90e..d7b5d5de7 100644 --- a/inventory-framework-platform-bukkit/src/main/java/me/devnatan/inventoryframework/component/BukkitItemComponentBuilder.java +++ b/inventory-framework-platform-bukkit/src/main/java/me/devnatan/inventoryframework/component/BukkitItemComponentBuilder.java @@ -22,9 +22,6 @@ public final class BukkitItemComponentBuilder extends DefaultComponentBuilder implements ItemComponentBuilder, ComponentFactory { - private static final Function RANDOM_KEY_FACTORY = - __ -> UUID.randomUUID().toString(); - private final VirtualView root; private int slot; private ItemStack item; diff --git a/inventory-framework-platform-minestom/src/main/kotlin/me/devnatan/inventoryframework/component/MinestomItemComponentBuilder.kt b/inventory-framework-platform-minestom/src/main/kotlin/me/devnatan/inventoryframework/component/MinestomItemComponentBuilder.kt index 82ffdff61..f2ce691e0 100644 --- a/inventory-framework-platform-minestom/src/main/kotlin/me/devnatan/inventoryframework/component/MinestomItemComponentBuilder.kt +++ b/inventory-framework-platform-minestom/src/main/kotlin/me/devnatan/inventoryframework/component/MinestomItemComponentBuilder.kt @@ -4,6 +4,7 @@ import me.devnatan.inventoryframework.Ref import me.devnatan.inventoryframework.ViewContainer import me.devnatan.inventoryframework.VirtualView import me.devnatan.inventoryframework.context.Context +import me.devnatan.inventoryframework.context.IFContext import me.devnatan.inventoryframework.context.IFRenderContext import me.devnatan.inventoryframework.context.IFSlotClickContext import me.devnatan.inventoryframework.context.IFSlotContext @@ -16,13 +17,14 @@ import me.devnatan.inventoryframework.utils.SlotConverter import net.minestom.server.item.ItemStack import java.util.UUID import java.util.function.Consumer +import java.util.function.Function import java.util.function.Predicate import java.util.function.Supplier class MinestomItemComponentBuilder private constructor( private val root: VirtualView, - key: String?, + keyFactory: Function?, slot: Int, item: ItemStack?, renderHandler: Consumer?, @@ -37,7 +39,7 @@ class MinestomItemComponentBuilder isManagedExternally: Boolean, displayCondition: Predicate?, ) : DefaultComponentBuilder( - key, + keyFactory, reference, data, cancelOnClick, @@ -59,7 +61,7 @@ class MinestomItemComponentBuilder root: VirtualView, ) : this( root = root, - key = null, + keyFactory = null, slot = -1, item = null, renderHandler = null, @@ -207,29 +209,33 @@ class MinestomItemComponentBuilder return this } - override fun create(): Component = - ItemComponent( - key ?: UUID.randomUUID().toString(), - root, - slot, - item, - cancelOnClick, - closeOnClick, - displayCondition, - renderHandler, - updateHandler, - clickHandler, - watchingStates, - isManagedExternally, - updateOnClick, - false, - reference, + override fun create(): Component { + val componentKeyProvider = + if (keyFactory == null) RANDOM_KEY_FACTORY else keyFactory + + return ItemComponent( + /* keyFactory = */ componentKeyProvider, + /* root = */ root, + /* position = */ slot, + /* stack = */ item, + /* cancelOnClick = */ cancelOnClick, + /* closeOnClick = */ closeOnClick, + /* displayCondition = */ displayCondition, + /* renderHandler = */ renderHandler, + /* updateHandler = */ updateHandler, + /* clickHandler = */ clickHandler, + /* watching = */ watchingStates, + /* isManagedExternally = */ isManagedExternally, + /* updateOnClick = */ updateOnClick, + /* isVisible = */ false, + /* reference = */ reference, ) + } override fun copy(): MinestomItemComponentBuilder = MinestomItemComponentBuilder( root = root, - key = key, + keyFactory = keyFactory, slot = slot, item = item, renderHandler = renderHandler, diff --git a/inventory-framework-platform-minestom/src/main/kotlin/me/devnatan/inventoryframework/context/SlotRenderContext.kt b/inventory-framework-platform-minestom/src/main/kotlin/me/devnatan/inventoryframework/context/SlotRenderContext.kt index 6ad529c50..166ee180f 100644 --- a/inventory-framework-platform-minestom/src/main/kotlin/me/devnatan/inventoryframework/context/SlotRenderContext.kt +++ b/inventory-framework-platform-minestom/src/main/kotlin/me/devnatan/inventoryframework/context/SlotRenderContext.kt @@ -3,9 +3,11 @@ package me.devnatan.inventoryframework.context import me.devnatan.inventoryframework.MinestomViewer import me.devnatan.inventoryframework.RootView import me.devnatan.inventoryframework.Viewer +import me.devnatan.inventoryframework.component.Component import net.minestom.server.entity.Player import net.minestom.server.item.ItemStack import org.jetbrains.annotations.ApiStatus +import org.jetbrains.annotations.UnknownNullability class SlotRenderContext @ApiStatus.Internal @@ -13,6 +15,7 @@ class SlotRenderContext slot: Int, parent: IFRenderContext, private val viewer: Viewer?, + private val _component: Component, ) : SlotContext(slot, parent), IFSlotRenderContext { override val player: Player = (viewer as MinestomViewer).player @@ -48,6 +51,8 @@ class SlotRenderContext override fun isOnEntityContainer(): Boolean = container.isEntityContainer + override fun getComponent(): @UnknownNullability Component = _component + override fun getViewer(): Viewer? = viewer override fun closeForPlayer() { diff --git a/inventory-framework-platform-minestom/src/main/kotlin/me/devnatan/inventoryframework/internal/MinestomElementFactory.kt b/inventory-framework-platform-minestom/src/main/kotlin/me/devnatan/inventoryframework/internal/MinestomElementFactory.kt index 11e290abf..3de131cf1 100644 --- a/inventory-framework-platform-minestom/src/main/kotlin/me/devnatan/inventoryframework/internal/MinestomElementFactory.kt +++ b/inventory-framework-platform-minestom/src/main/kotlin/me/devnatan/inventoryframework/internal/MinestomElementFactory.kt @@ -164,7 +164,8 @@ class MinestomElementFactory : ElementFactory() { slot: Int, parent: IFRenderContext, viewer: Viewer?, - ): IFSlotRenderContext = SlotRenderContext(slot, parent, viewer) + component: Component, + ): IFSlotRenderContext = SlotRenderContext(slot, parent, viewer, component) override fun createCloseContext( viewer: Viewer, diff --git a/inventory-framework-test/src/main/java/me/devnatan/inventoryframework/internal/MockElementFactory.java b/inventory-framework-test/src/main/java/me/devnatan/inventoryframework/internal/MockElementFactory.java index 30c10d132..9728c80f2 100644 --- a/inventory-framework-test/src/main/java/me/devnatan/inventoryframework/internal/MockElementFactory.java +++ b/inventory-framework-test/src/main/java/me/devnatan/inventoryframework/internal/MockElementFactory.java @@ -92,7 +92,7 @@ public IFSlotRenderContext createSlotRenderContext( when(mock.getParent()).thenReturn(parent); when(mock.getViewer()).thenReturn(viewer); when(mock.getContainer()).then(ignored -> parent.getContainer()); - when(mock.getComponent()).then(component); + when(mock.getComponent()).thenReturn(component); return mock; } From 8109a0c2b10d62aaf412d2279e334e3296e63595 Mon Sep 17 00:00:00 2001 From: Natan Date: Wed, 26 Nov 2025 13:42:44 -0300 Subject: [PATCH 06/16] fix: initial render issues with dynamic keys --- .../inventoryframework/component/ItemComponent.java | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/inventory-framework-api/src/main/java/me/devnatan/inventoryframework/component/ItemComponent.java b/inventory-framework-api/src/main/java/me/devnatan/inventoryframework/component/ItemComponent.java index 3b7fec560..5d2fcbd03 100644 --- a/inventory-framework-api/src/main/java/me/devnatan/inventoryframework/component/ItemComponent.java +++ b/inventory-framework-api/src/main/java/me/devnatan/inventoryframework/component/ItemComponent.java @@ -196,8 +196,13 @@ public void updated(@NotNull IFSlotRenderContext context) { final boolean staticItem = displayCondition == null && getRenderHandler() == null; if (staticItem) return; - // Item is dynamic but key hasn't changed - if (Objects.equals(lastKey, useKey(context))) return; + String key = null; + final boolean firstRender = lastKey == null + && (key = useKey(context)) != null + && keyFactory != null; + + // Item is dynamic but key hasn't changed + if (!firstRender && Objects.equals(lastKey, key)) return; } if (isVisible() && getUpdateHandler() != null) { From c80c1fa93dd1e1794935e2736a35dc721acb019b Mon Sep 17 00:00:00 2001 From: Natan Date: Wed, 26 Nov 2025 13:43:32 -0300 Subject: [PATCH 07/16] Revert "fix: initial render issues with dynamic keys" This reverts commit 8109a0c2b10d62aaf412d2279e334e3296e63595. --- .../inventoryframework/component/ItemComponent.java | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/inventory-framework-api/src/main/java/me/devnatan/inventoryframework/component/ItemComponent.java b/inventory-framework-api/src/main/java/me/devnatan/inventoryframework/component/ItemComponent.java index 5d2fcbd03..3b7fec560 100644 --- a/inventory-framework-api/src/main/java/me/devnatan/inventoryframework/component/ItemComponent.java +++ b/inventory-framework-api/src/main/java/me/devnatan/inventoryframework/component/ItemComponent.java @@ -196,13 +196,8 @@ public void updated(@NotNull IFSlotRenderContext context) { final boolean staticItem = displayCondition == null && getRenderHandler() == null; if (staticItem) return; - String key = null; - final boolean firstRender = lastKey == null - && (key = useKey(context)) != null - && keyFactory != null; - - // Item is dynamic but key hasn't changed - if (!firstRender && Objects.equals(lastKey, key)) return; + // Item is dynamic but key hasn't changed + if (Objects.equals(lastKey, useKey(context))) return; } if (isVisible() && getUpdateHandler() != null) { From ceb4ac20e01ad16470da0f50527b6c0f816a18e9 Mon Sep 17 00:00:00 2001 From: Natan Date: Wed, 26 Nov 2025 14:06:33 -0300 Subject: [PATCH 08/16] fix: ensure item is updated if watching states is not empty --- .../component/ItemComponent.java | 19 +++---- .../component/DefaultComponentBuilder.java | 4 +- .../component/BukkitItemComponentBuilder.java | 1 - .../component/MinestomItemComponentBuilder.kt | 49 ++++++++++++------- 4 files changed, 41 insertions(+), 32 deletions(-) diff --git a/inventory-framework-api/src/main/java/me/devnatan/inventoryframework/component/ItemComponent.java b/inventory-framework-api/src/main/java/me/devnatan/inventoryframework/component/ItemComponent.java index 3b7fec560..0aa7632fd 100644 --- a/inventory-framework-api/src/main/java/me/devnatan/inventoryframework/component/ItemComponent.java +++ b/inventory-framework-api/src/main/java/me/devnatan/inventoryframework/component/ItemComponent.java @@ -74,10 +74,6 @@ public String getKey() { return lastKey; } - public String useKey(IFContext context) { - return lastKey = keyFactory.apply(context); - } - @NotNull @Override public VirtualView getRoot() { @@ -144,6 +140,8 @@ public boolean intersects(@NotNull Component other) { @Override public void render(@NotNull IFSlotRenderContext context) { + if (keyFactory != null) lastKey = keyFactory.apply(context); + if (getRenderHandler() != null) { final int initialSlot = getPosition(); @@ -191,14 +189,13 @@ public void render(@NotNull IFSlotRenderContext context) { public void updated(@NotNull IFSlotRenderContext context) { if (context.isCancelled()) return; - // Not a force update, item is not dynamic (`onRender` or `displayIf` not set) - if (!context.isForceUpdate()) { - final boolean staticItem = displayCondition == null && getRenderHandler() == null; - if (staticItem) return; + boolean isWatchingAnyState = + getWatchingStates() != null && !getWatching().isEmpty(); - // Item is dynamic but key hasn't changed - if (Objects.equals(lastKey, useKey(context))) return; - } + if (!isWatchingAnyState + && keyFactory != null + && lastKey != null + && Objects.equals(lastKey, keyFactory.apply(context))) return; if (isVisible() && getUpdateHandler() != null) { getUpdateHandler().accept(context); diff --git a/inventory-framework-core/src/main/java/me/devnatan/inventoryframework/component/DefaultComponentBuilder.java b/inventory-framework-core/src/main/java/me/devnatan/inventoryframework/component/DefaultComponentBuilder.java index 4708d1a10..f6fc74da4 100644 --- a/inventory-framework-core/src/main/java/me/devnatan/inventoryframework/component/DefaultComponentBuilder.java +++ b/inventory-framework-core/src/main/java/me/devnatan/inventoryframework/component/DefaultComponentBuilder.java @@ -19,8 +19,8 @@ public abstract class DefaultComponentBuilder, C extends IFContext> implements ComponentBuilder { - protected static final Function RANDOM_KEY_FACTORY = - __ -> UUID.randomUUID().toString(); + protected static final Function RANDOM_KEY_FACTORY = + __ -> UUID.randomUUID().toString(); protected Function keyFactory; protected Ref reference; diff --git a/inventory-framework-platform-bukkit/src/main/java/me/devnatan/inventoryframework/component/BukkitItemComponentBuilder.java b/inventory-framework-platform-bukkit/src/main/java/me/devnatan/inventoryframework/component/BukkitItemComponentBuilder.java index d7b5d5de7..dddd629b4 100644 --- a/inventory-framework-platform-bukkit/src/main/java/me/devnatan/inventoryframework/component/BukkitItemComponentBuilder.java +++ b/inventory-framework-platform-bukkit/src/main/java/me/devnatan/inventoryframework/component/BukkitItemComponentBuilder.java @@ -4,7 +4,6 @@ import java.util.LinkedHashSet; import java.util.Map; import java.util.Set; -import java.util.UUID; import java.util.function.Consumer; import java.util.function.Function; import java.util.function.Predicate; diff --git a/inventory-framework-platform-minestom/src/main/kotlin/me/devnatan/inventoryframework/component/MinestomItemComponentBuilder.kt b/inventory-framework-platform-minestom/src/main/kotlin/me/devnatan/inventoryframework/component/MinestomItemComponentBuilder.kt index f2ce691e0..410e8f76e 100644 --- a/inventory-framework-platform-minestom/src/main/kotlin/me/devnatan/inventoryframework/component/MinestomItemComponentBuilder.kt +++ b/inventory-framework-platform-minestom/src/main/kotlin/me/devnatan/inventoryframework/component/MinestomItemComponentBuilder.kt @@ -15,7 +15,6 @@ import me.devnatan.inventoryframework.context.SlotRenderContext import me.devnatan.inventoryframework.state.State import me.devnatan.inventoryframework.utils.SlotConverter import net.minestom.server.item.ItemStack -import java.util.UUID import java.util.function.Consumer import java.util.function.Function import java.util.function.Predicate @@ -210,25 +209,39 @@ class MinestomItemComponentBuilder } override fun create(): Component { - val componentKeyProvider = - if (keyFactory == null) RANDOM_KEY_FACTORY else keyFactory + val componentKeyProvider = if (keyFactory == null) RANDOM_KEY_FACTORY else keyFactory return ItemComponent( - /* keyFactory = */ componentKeyProvider, - /* root = */ root, - /* position = */ slot, - /* stack = */ item, - /* cancelOnClick = */ cancelOnClick, - /* closeOnClick = */ closeOnClick, - /* displayCondition = */ displayCondition, - /* renderHandler = */ renderHandler, - /* updateHandler = */ updateHandler, - /* clickHandler = */ clickHandler, - /* watching = */ watchingStates, - /* isManagedExternally = */ isManagedExternally, - /* updateOnClick = */ updateOnClick, - /* isVisible = */ false, - /* reference = */ reference, + // keyFactory = + componentKeyProvider, + // root = + root, + // position = + slot, + // stack = + item, + // cancelOnClick = + cancelOnClick, + // closeOnClick = + closeOnClick, + // displayCondition = + displayCondition, + // renderHandler = + renderHandler, + // updateHandler = + updateHandler, + // clickHandler = + clickHandler, + // watching = + watchingStates, + // isManagedExternally = + isManagedExternally, + // updateOnClick = + updateOnClick, + // isVisible = + false, + // reference = + reference, ) } From 9dda8557edc62e07f25859959d7a9360544ec43f Mon Sep 17 00:00:00 2001 From: Natan Date: Wed, 26 Nov 2025 14:25:07 -0300 Subject: [PATCH 09/16] fix: update TestItemComponentBuilder --- .../component/TestItemComponentBuilder.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/inventory-framework-platform/src/test/java/me/devnatan/inventoryframework/component/TestItemComponentBuilder.java b/inventory-framework-platform/src/test/java/me/devnatan/inventoryframework/component/TestItemComponentBuilder.java index 663600e34..200e5dc80 100644 --- a/inventory-framework-platform/src/test/java/me/devnatan/inventoryframework/component/TestItemComponentBuilder.java +++ b/inventory-framework-platform/src/test/java/me/devnatan/inventoryframework/component/TestItemComponentBuilder.java @@ -2,6 +2,7 @@ import java.util.Map; import java.util.Set; +import java.util.function.Function; import java.util.function.Predicate; import me.devnatan.inventoryframework.Ref; import me.devnatan.inventoryframework.context.IFContext; @@ -14,10 +15,11 @@ public class TestItemComponentBuilder extends DefaultComponentBuilder keyFactory, Ref referenceKey, Map data, boolean cancelOnClick, @@ -27,6 +29,7 @@ protected TestItemComponentBuilder( boolean isManagedExternally, Predicate displayCondition) { super( + keyFactory, referenceKey, data, cancelOnClick, From d68c261d513d45e4409bc088864b18e13f8e4d76 Mon Sep 17 00:00:00 2001 From: Natan Date: Thu, 27 Nov 2025 11:47:00 -0300 Subject: [PATCH 10/16] docs: `identifiedBy` docs --- .../component/ComponentBuilder.java | 48 ++++++++++++++----- 1 file changed, 36 insertions(+), 12 deletions(-) diff --git a/inventory-framework-api/src/main/java/me/devnatan/inventoryframework/component/ComponentBuilder.java b/inventory-framework-api/src/main/java/me/devnatan/inventoryframework/component/ComponentBuilder.java index c35509734..32954a5ca 100644 --- a/inventory-framework-api/src/main/java/me/devnatan/inventoryframework/component/ComponentBuilder.java +++ b/inventory-framework-api/src/main/java/me/devnatan/inventoryframework/component/ComponentBuilder.java @@ -165,24 +165,48 @@ public interface ComponentBuilder, C extends IF */ S hideIf(Predicate condition); - /** - * *** This API is experimental and is not subject to the general compatibility guarantees such - * API may be changed or may be removed completely in any further release. *** - */ + /** + * Identifies this component with a constant key. + *

+ * Components with explicit keys are only re-rendered when their key changes. + * This can be used to prevent unnecessary re-renders during updates. + * + *

This API is experimental and is not subject to the general compatibility guarantees. + * It may be changed or removed completely in any further release. + * + * @param key The constant key to identify this component + * @return This component builder + */ @ApiStatus.Experimental S identifiedBy(String key); - /** - * *** This API is experimental and is not subject to the general compatibility guarantees such - * API may be changed or may be removed completely in any further release. *** - */ + /** + * Identifies this component with a key provided by a {@link Supplier}. + *

+ * Components with explicit keys are only re-rendered when their key changes. + * This can be used to prevent unnecessary re-renders during scheduled updates. + * + *

This API is experimental and is not subject to the general compatibility guarantees. + * It may be changed or removed completely in any further release. + * + * @param keyProvider A supplier that provides the key to identify this component. + * @return This component builder. + */ @ApiStatus.Experimental S identifiedBy(Supplier keyProvider); - /** - * *** This API is experimental and is not subject to the general compatibility guarantees such - * API may be changed or may be removed completely in any further release. *** - */ + /** + * Identifies this component with a key provided by a {@link Function} based on the context. + *

+ * Components with explicit keys are only re-rendered when their key changes. + * This can be used to prevent unnecessary re-renders during scheduled updates. + * + *

This API is experimental and is not subject to the general compatibility guarantees. + * It may be changed or removed completely in any further release. + * + * @param keyProvider A function that provides the key to identify this component based on the context. + * @return This component builder. + */ @ApiStatus.Experimental S identifiedBy(Function keyProvider); } From cfcb9ce6156cbd8da644056f4ead316006bc5457 Mon Sep 17 00:00:00 2001 From: Natan Vieira <24600258+devnatan@users.noreply.github.com> Date: Thu, 27 Nov 2025 12:00:03 -0300 Subject: [PATCH 11/16] Update inventory-framework-api/src/main/java/me/devnatan/inventoryframework/component/ItemComponent.java Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Signed-off-by: Natan Vieira <24600258+devnatan@users.noreply.github.com> --- .../me/devnatan/inventoryframework/component/ItemComponent.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/inventory-framework-api/src/main/java/me/devnatan/inventoryframework/component/ItemComponent.java b/inventory-framework-api/src/main/java/me/devnatan/inventoryframework/component/ItemComponent.java index 0aa7632fd..732c97cf1 100644 --- a/inventory-framework-api/src/main/java/me/devnatan/inventoryframework/component/ItemComponent.java +++ b/inventory-framework-api/src/main/java/me/devnatan/inventoryframework/component/ItemComponent.java @@ -33,7 +33,7 @@ public class ItemComponent implements Component, InteractionHandler { private final Ref reference; private boolean isVisible; - private String lastKey; + private volatile String lastKey; public ItemComponent( Function keyFactory, From 17d81b2bbce8d1e614ca201f92e0620cbf4cd750 Mon Sep 17 00:00:00 2001 From: Natan Vieira <24600258+devnatan@users.noreply.github.com> Date: Thu, 27 Nov 2025 12:00:36 -0300 Subject: [PATCH 12/16] Update inventory-framework-api/src/main/java/me/devnatan/inventoryframework/component/ItemComponent.java Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Signed-off-by: Natan Vieira <24600258+devnatan@users.noreply.github.com> --- .../me/devnatan/inventoryframework/component/ItemComponent.java | 1 - 1 file changed, 1 deletion(-) diff --git a/inventory-framework-api/src/main/java/me/devnatan/inventoryframework/component/ItemComponent.java b/inventory-framework-api/src/main/java/me/devnatan/inventoryframework/component/ItemComponent.java index 732c97cf1..990ea91c4 100644 --- a/inventory-framework-api/src/main/java/me/devnatan/inventoryframework/component/ItemComponent.java +++ b/inventory-framework-api/src/main/java/me/devnatan/inventoryframework/component/ItemComponent.java @@ -193,7 +193,6 @@ public void updated(@NotNull IFSlotRenderContext context) { getWatchingStates() != null && !getWatching().isEmpty(); if (!isWatchingAnyState - && keyFactory != null && lastKey != null && Objects.equals(lastKey, keyFactory.apply(context))) return; From 7af74eab7f24ee620f5063db9b47f54b31bcb7e8 Mon Sep 17 00:00:00 2001 From: Natan Date: Thu, 27 Nov 2025 11:49:23 -0300 Subject: [PATCH 13/16] docs: `IFSlotContext#getComponent` --- .../context/IFSlotContext.java | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/inventory-framework-api/src/main/java/me/devnatan/inventoryframework/context/IFSlotContext.java b/inventory-framework-api/src/main/java/me/devnatan/inventoryframework/context/IFSlotContext.java index 17128d1b1..46182c6d7 100644 --- a/inventory-framework-api/src/main/java/me/devnatan/inventoryframework/context/IFSlotContext.java +++ b/inventory-framework-api/src/main/java/me/devnatan/inventoryframework/context/IFSlotContext.java @@ -62,14 +62,14 @@ public interface IFSlotContext extends IFContext { @NotNull ViewContainer getContainer(); - /** - * The component placed at that slot. - * - *

This is an internal inventory-framework API that should not be used from outside of - * this library. No compatibility guarantees are provided. - * - * @return The component placed at that slot. - */ + /** + * The component associated with this slot context. + * + *

This is an internal inventory-framework API that should not be used from outside of + * this library. No compatibility guarantees are provided. + * + * @return The component associated with this slot context. + */ @ApiStatus.Internal @UnknownNullability Component getComponent(); From b329cefff7e1a5b45564eda3cb23ea0c90630750 Mon Sep 17 00:00:00 2001 From: Natan Vieira <24600258+devnatan@users.noreply.github.com> Date: Thu, 27 Nov 2025 12:05:50 -0300 Subject: [PATCH 14/16] Update inventory-framework-api/src/main/java/me/devnatan/inventoryframework/component/ItemComponent.java Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Signed-off-by: Natan Vieira <24600258+devnatan@users.noreply.github.com> --- .../inventoryframework/component/ItemComponent.java | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/inventory-framework-api/src/main/java/me/devnatan/inventoryframework/component/ItemComponent.java b/inventory-framework-api/src/main/java/me/devnatan/inventoryframework/component/ItemComponent.java index 990ea91c4..380225b99 100644 --- a/inventory-framework-api/src/main/java/me/devnatan/inventoryframework/component/ItemComponent.java +++ b/inventory-framework-api/src/main/java/me/devnatan/inventoryframework/component/ItemComponent.java @@ -188,14 +188,13 @@ public void render(@NotNull IFSlotRenderContext context) { @Override public void updated(@NotNull IFSlotRenderContext context) { if (context.isCancelled()) return; - - boolean isWatchingAnyState = - getWatchingStates() != null && !getWatching().isEmpty(); - - if (!isWatchingAnyState + // Key-based skip optimization should always take precedence + if (keyFactory != null && lastKey != null && Objects.equals(lastKey, keyFactory.apply(context))) return; + boolean isWatchingAnyState = + getWatchingStates() != null && !getWatching().isEmpty(); if (isVisible() && getUpdateHandler() != null) { getUpdateHandler().accept(context); if (context.isCancelled()) return; From 568e3cf4edf59f89e1871fe9abfce11b0a18395b Mon Sep 17 00:00:00 2001 From: Natan Vieira <24600258+devnatan@users.noreply.github.com> Date: Thu, 27 Nov 2025 12:05:56 -0300 Subject: [PATCH 15/16] Update inventory-framework-api/src/main/java/me/devnatan/inventoryframework/component/ItemComponent.java Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Signed-off-by: Natan Vieira <24600258+devnatan@users.noreply.github.com> --- .../me/devnatan/inventoryframework/component/ItemComponent.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/inventory-framework-api/src/main/java/me/devnatan/inventoryframework/component/ItemComponent.java b/inventory-framework-api/src/main/java/me/devnatan/inventoryframework/component/ItemComponent.java index 380225b99..1bb62d293 100644 --- a/inventory-framework-api/src/main/java/me/devnatan/inventoryframework/component/ItemComponent.java +++ b/inventory-framework-api/src/main/java/me/devnatan/inventoryframework/component/ItemComponent.java @@ -140,7 +140,7 @@ public boolean intersects(@NotNull Component other) { @Override public void render(@NotNull IFSlotRenderContext context) { - if (keyFactory != null) lastKey = keyFactory.apply(context); + lastKey = keyFactory.apply(context); if (getRenderHandler() != null) { final int initialSlot = getPosition(); From 7c98a3f04417f750f21349bbfdabc4d4bf6fedf8 Mon Sep 17 00:00:00 2001 From: Natan Vieira <24600258+devnatan@users.noreply.github.com> Date: Thu, 27 Nov 2025 12:07:49 -0300 Subject: [PATCH 16/16] Update inventory-framework-api/src/main/java/me/devnatan/inventoryframework/component/ItemComponent.java Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Signed-off-by: Natan Vieira <24600258+devnatan@users.noreply.github.com> --- .../inventoryframework/component/ItemComponent.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/inventory-framework-api/src/main/java/me/devnatan/inventoryframework/component/ItemComponent.java b/inventory-framework-api/src/main/java/me/devnatan/inventoryframework/component/ItemComponent.java index 1bb62d293..d09772cac 100644 --- a/inventory-framework-api/src/main/java/me/devnatan/inventoryframework/component/ItemComponent.java +++ b/inventory-framework-api/src/main/java/me/devnatan/inventoryframework/component/ItemComponent.java @@ -190,8 +190,10 @@ public void updated(@NotNull IFSlotRenderContext context) { if (context.isCancelled()) return; // Key-based skip optimization should always take precedence if (keyFactory != null - && lastKey != null - && Objects.equals(lastKey, keyFactory.apply(context))) return; + && lastKey != null) { + String currentKey = keyFactory.apply(context); + if (Objects.equals(lastKey, currentKey)) return; + } boolean isWatchingAnyState = getWatchingStates() != null && !getWatching().isEmpty();