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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
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;
Expand Down Expand Up @@ -162,4 +164,49 @@ public interface ComponentBuilder<S extends ComponentBuilder<S, C>, C extends IF
* @see #displayIf(Predicate)
*/
S hideIf(Predicate<C> condition);

/**
* Identifies this component with a constant key.
* <p>
* Components with explicit keys are only re-rendered when their key changes.
* This can be used to prevent unnecessary re-renders during updates.
*
* <p><b><i>This API is experimental and is not subject to the general compatibility guarantees.
* It may be changed or removed completely in any further release.</i></b>
*
* @param key The constant key to identify this component
* @return This component builder
*/
@ApiStatus.Experimental
S identifiedBy(String key);

/**
* Identifies this component with a key provided by a {@link Supplier}.
* <p>
* Components with explicit keys are only re-rendered when their key changes.
* This can be used to prevent unnecessary re-renders during scheduled updates.
*
* <p><b><i>This API is experimental and is not subject to the general compatibility guarantees.
* It may be changed or removed completely in any further release.</i></b>
*
* @param keyProvider A supplier that provides the key to identify this component.
* @return This component builder.
*/
@ApiStatus.Experimental
S identifiedBy(Supplier<String> keyProvider);

/**
* Identifies this component with a key provided by a {@link Function} based on the context.
* <p>
* Components with explicit keys are only re-rendered when their key changes.
* This can be used to prevent unnecessary re-renders during scheduled updates.
*
* <p><b><i>This API is experimental and is not subject to the general compatibility guarantees.
* It may be changed or removed completely in any further release.</i></b>
*
* @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<C, String> keyProvider);
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
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 me.devnatan.inventoryframework.InventoryFrameworkException;
import me.devnatan.inventoryframework.Ref;
Expand All @@ -17,7 +17,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<IFContext, String> keyFactory;
private final VirtualView root;
private int position;
private final Object stack;
Expand All @@ -30,10 +30,13 @@ public class ItemComponent implements Component, InteractionHandler {
private final Set<State<?>> watching;
private final boolean isManagedExternally;
private final boolean updateOnClick;
private boolean isVisible;
private final Ref<Component> reference;

private boolean isVisible;
private volatile String lastKey;

public ItemComponent(
Function<? extends IFContext, String> keyFactory,
VirtualView root,
int position,
Object stack,
Expand All @@ -48,6 +51,8 @@ public ItemComponent(
boolean updateOnClick,
boolean isVisible,
Ref<Component> reference) {
//noinspection unchecked
this.keyFactory = (Function<IFContext, String>) keyFactory;
Comment thread
devnatan marked this conversation as resolved.
this.root = root;
this.position = position;
this.stack = stack;
Expand All @@ -66,7 +71,7 @@ public ItemComponent(

@Override
public String getKey() {
return key;
return lastKey;
}
Comment thread
devnatan marked this conversation as resolved.

@NotNull
Expand Down Expand Up @@ -135,6 +140,8 @@ public boolean intersects(@NotNull Component other) {

@Override
public void render(@NotNull IFSlotRenderContext context) {
lastKey = keyFactory.apply(context);

if (getRenderHandler() != null) {
final int initialSlot = getPosition();

Expand Down Expand Up @@ -181,10 +188,15 @@ public void render(@NotNull IFSlotRenderContext context) {
@Override
public void updated(@NotNull IFSlotRenderContext context) {
if (context.isCancelled()) return;
// Key-based skip optimization should always take precedence
if (keyFactory != null
&& lastKey != null) {
String currentKey = keyFactory.apply(context);
if (Objects.equals(lastKey, currentKey)) return;
}

// Static item with no `displayIf` must not even reach the update handler
if (!context.isForceUpdate() && displayCondition == null && getRenderHandler() == null) return;

boolean isWatchingAnyState =
getWatchingStates() != null && !getWatching().isEmpty();
if (isVisible() && getUpdateHandler() != null) {
getUpdateHandler().accept(context);
if (context.isCancelled()) return;
Expand Down
Original file line number Diff line number Diff line change
@@ -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;

Expand All @@ -17,8 +16,6 @@ public interface IFSlotClickContext extends IFSlotContext, IFConfinedContext {
@NotNull
ViewContainer getClickedContainer();

Component getComponent();

int getClickedSlot();

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -59,4 +61,16 @@ public interface IFSlotContext extends IFContext {
*/
@NotNull
ViewContainer getContainer();

/**
* The component associated with this slot context.
*
* <p><b><i>This is an internal inventory-framework API that should not be used from outside of
* this library. No compatibility guarantees are provided.</i></b>
*
* @return The component associated with this slot context.
*/
@ApiStatus.Internal
@UnknownNullability
Component getComponent();
}
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,11 @@
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;
import java.util.function.Supplier;
import me.devnatan.inventoryframework.Ref;
import me.devnatan.inventoryframework.context.IFContext;
import me.devnatan.inventoryframework.state.State;
Expand All @@ -16,6 +19,10 @@
public abstract class DefaultComponentBuilder<S extends ComponentBuilder<S, C>, C extends IFContext>
implements ComponentBuilder<S, C> {

protected static final Function<IFContext, String> RANDOM_KEY_FACTORY =
__ -> UUID.randomUUID().toString();

protected Function<? extends IFContext, String> keyFactory;
protected Ref<Component> reference;
protected Map<String, Object> data;
protected boolean cancelOnClick, closeOnClick, updateOnClick;
Expand All @@ -24,6 +31,7 @@ public abstract class DefaultComponentBuilder<S extends ComponentBuilder<S, C>,
protected Predicate<C> displayCondition;

protected DefaultComponentBuilder(
Function<? extends IFContext, String> keyFactory,
Ref<Component> reference,
Map<String, Object> data,
boolean cancelOnClick,
Expand All @@ -32,6 +40,7 @@ protected DefaultComponentBuilder(
Set<State<?>> watchingStates,
boolean isManagedExternally,
Predicate<C> displayCondition) {
this.keyFactory = keyFactory;
this.reference = reference;
this.data = data;
this.cancelOnClick = cancelOnClick;
Expand Down Expand Up @@ -122,4 +131,20 @@ public S hideIf(Predicate<C> condition) {
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(Supplier<String> key) {
return identifiedBy(__ -> key.get());
}

@Override
public S identifiedBy(Function<C, String> keyFactory) {
this.keyFactory = keyFactory;
return (S) this;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import java.util.Map;
import java.util.Set;
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;
Expand All @@ -29,6 +30,7 @@ public final class BukkitItemComponentBuilder extends DefaultComponentBuilder<Bu

public BukkitItemComponentBuilder(VirtualView root) {
this(
null,
root,
-1,
null,
Expand All @@ -46,6 +48,7 @@ public BukkitItemComponentBuilder(VirtualView root) {
}

private BukkitItemComponentBuilder(
Function<? extends IFContext, String> keyFactory,
VirtualView root,
int slot,
ItemStack item,
Expand All @@ -61,6 +64,7 @@ private BukkitItemComponentBuilder(
boolean isManagedExternally,
Predicate<Context> displayCondition) {
super(
keyFactory,
reference,
data,
cancelOnClick,
Expand Down Expand Up @@ -188,7 +192,10 @@ public BukkitItemComponentBuilder onUpdate(@Nullable Consumer<? super SlotContex

@Override
public @NotNull Component create() {
final Function<? extends IFContext, String> componentKeyProvider =
keyFactory == null ? RANDOM_KEY_FACTORY : keyFactory;
return new ItemComponent(
componentKeyProvider,
root,
slot,
item,
Expand All @@ -208,6 +215,7 @@ public BukkitItemComponentBuilder onUpdate(@Nullable Consumer<? super SlotContex
@Override
public BukkitItemComponentBuilder copy() {
return new BukkitItemComponentBuilder(
keyFactory,
root,
slot,
item,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -14,16 +15,23 @@ 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
public final Component getComponent() {
return component;
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Loading
Loading