From 88477152eeb760b91f9d1d1e9384407875781c45 Mon Sep 17 00:00:00 2001 From: Natan Vieira do Nascimento Date: Mon, 28 Aug 2023 13:17:00 -0300 Subject: [PATCH 1/6] Use Viewer as source of truth --- .../inventoryframework/ViewContainer.java | 6 - .../devnatan/inventoryframework/ViewType.java | 1 + .../devnatan/inventoryframework/Viewer.java | 4 + .../BukkitViewContainer.java | 13 - .../inventoryframework/BukkitViewer.java | 96 ++++---- .../IFInventoryListener.java | 227 +++++++++--------- .../inventoryframework/ViewFrame.java | 30 +-- 7 files changed, 176 insertions(+), 201 deletions(-) diff --git a/inventory-framework-api/src/main/java/me/devnatan/inventoryframework/ViewContainer.java b/inventory-framework-api/src/main/java/me/devnatan/inventoryframework/ViewContainer.java index d9ab395b..d1548714 100644 --- a/inventory-framework-api/src/main/java/me/devnatan/inventoryframework/ViewContainer.java +++ b/inventory-framework-api/src/main/java/me/devnatan/inventoryframework/ViewContainer.java @@ -77,18 +77,12 @@ public interface ViewContainer { */ int getColumnsCount(); - @NotNull - @Unmodifiable - List getViewers(); - void open(@NotNull Viewer viewer); void close(); void close(@NotNull Viewer viewer); - void changeTitle(@Nullable String title); - void changeTitle(@Nullable String title, @NotNull Viewer target); boolean isEntityContainer(); diff --git a/inventory-framework-api/src/main/java/me/devnatan/inventoryframework/ViewType.java b/inventory-framework-api/src/main/java/me/devnatan/inventoryframework/ViewType.java index c583ed32..31e1456b 100644 --- a/inventory-framework-api/src/main/java/me/devnatan/inventoryframework/ViewType.java +++ b/inventory-framework-api/src/main/java/me/devnatan/inventoryframework/ViewType.java @@ -20,6 +20,7 @@ public final class ViewType { public static final ViewType SHULKER_BOX = new ViewType("shulker-box", 27, 3, 9); public static final ViewType SMOKER = new ViewType("smoker", 3, 2, 2, false, new int[] {2}); public static final ViewType VILLAGER_TRADING = new ViewType("villager-trading", 3, 1, 3, false, new int[] {2}); + public static final ViewType PLAYER = new ViewType("player", 54, 3, 9, false); private static final int[] EMPTY_RESULT_SLOTS = new int[0]; diff --git a/inventory-framework-api/src/main/java/me/devnatan/inventoryframework/Viewer.java b/inventory-framework-api/src/main/java/me/devnatan/inventoryframework/Viewer.java index 087dbbdd..2801f488 100644 --- a/inventory-framework-api/src/main/java/me/devnatan/inventoryframework/Viewer.java +++ b/inventory-framework-api/src/main/java/me/devnatan/inventoryframework/Viewer.java @@ -1,5 +1,6 @@ package me.devnatan.inventoryframework; +import me.devnatan.inventoryframework.context.IFContext; import org.jetbrains.annotations.NotNull; public interface Viewer { @@ -31,4 +32,7 @@ public interface Viewer { */ @NotNull ViewContainer getSelfContainer(); + + @NotNull + IFContext getContext(); } diff --git a/inventory-framework-platform-bukkit/src/main/java/me/devnatan/inventoryframework/BukkitViewContainer.java b/inventory-framework-platform-bukkit/src/main/java/me/devnatan/inventoryframework/BukkitViewContainer.java index 910981bc..6ecd9113 100644 --- a/inventory-framework-platform-bukkit/src/main/java/me/devnatan/inventoryframework/BukkitViewContainer.java +++ b/inventory-framework-platform-bukkit/src/main/java/me/devnatan/inventoryframework/BukkitViewContainer.java @@ -70,14 +70,6 @@ public int getColumnsCount() { return type.getColumns(); } - @Override - public @NotNull @Unmodifiable List getViewers() { - return Collections.unmodifiableList(new ArrayList<>(inventory.getViewers().stream() - .filter(humanEntity -> humanEntity instanceof Player) - .map(humanEntity -> new BukkitViewer((Player) humanEntity)) - .collect(Collectors.toList()))); - } - @Override public void renderItem(int slot, Object item) { requireSupportedItem(item); @@ -136,11 +128,6 @@ public int getLastSlot() { return getSlotsCount(); } - @Override - public void changeTitle(@Nullable final String title) { - for (final Viewer viewer : getViewers()) changeTitle(title, viewer); - } - @Override public void changeTitle(@Nullable String title, @NotNull Viewer target) { changeTitle(title, ((BukkitViewer) target).getPlayer()); diff --git a/inventory-framework-platform-bukkit/src/main/java/me/devnatan/inventoryframework/BukkitViewer.java b/inventory-framework-platform-bukkit/src/main/java/me/devnatan/inventoryframework/BukkitViewer.java index 29982a22..8bd2e404 100644 --- a/inventory-framework-platform-bukkit/src/main/java/me/devnatan/inventoryframework/BukkitViewer.java +++ b/inventory-framework-platform-bukkit/src/main/java/me/devnatan/inventoryframework/BukkitViewer.java @@ -1,64 +1,70 @@ package me.devnatan.inventoryframework; -import java.util.Objects; +import me.devnatan.inventoryframework.context.IFContext; import org.bukkit.entity.Player; import org.jetbrains.annotations.NotNull; +import java.util.Objects; + public final class BukkitViewer implements Viewer { - private final Player player; - private ViewContainer container; + private final Player player; + private ViewContainer selfContainer; + private IFContext context; - public BukkitViewer(Player player) { - this.player = player; - } + public BukkitViewer(@NotNull Player player, @NotNull IFContext context) { + this.player = player; + this.context = context; + } - public Player getPlayer() { - return player; - } + public Player getPlayer() { + return player; + } - public ViewContainer getContainer() { - return container; - } + @NotNull + @Override + public IFContext getContext() { + return context; + } - @Override - public @NotNull String getId() { - return getPlayer().getUniqueId().toString(); - } + @Override + public @NotNull String getId() { + return getPlayer().getUniqueId().toString(); + } - @Override - public void open(@NotNull final ViewContainer container) { - getPlayer().openInventory(((BukkitViewContainer) container).getInventory()); - } + @Override + public void open(@NotNull final ViewContainer container) { + getPlayer().openInventory(((BukkitViewContainer) container).getInventory()); + } - @Override - public void close() { - getPlayer().closeInventory(); - } + @Override + public void close() { + getPlayer().closeInventory(); + } - @Override - public @NotNull ViewContainer getSelfContainer() { - if (getContainer() == null) - container = new BukkitViewContainer(getPlayer().getInventory(), false, null); + @Override + public @NotNull ViewContainer getSelfContainer() { + if (selfContainer == null) + selfContainer = new BukkitViewContainer(getPlayer().getInventory(), getContext().isShared(), ViewType.PLAYER); - return getContainer(); - } + return selfContainer; + } - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - BukkitViewer that = (BukkitViewer) o; - return Objects.equals(getPlayer(), that.getPlayer()); - } + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + BukkitViewer that = (BukkitViewer) o; + return Objects.equals(getPlayer(), that.getPlayer()); + } - @Override - public int hashCode() { - return Objects.hash(getPlayer()); - } + @Override + public int hashCode() { + return Objects.hash(getPlayer()); + } - @Override - public String toString() { - return "BukkitViewer{" + "player=" + player + ", container=" + container + '}'; - } + @Override + public String toString() { + return "BukkitViewer{" + "player=" + player + ", selfContainer=" + selfContainer + '}'; + } } diff --git a/inventory-framework-platform-bukkit/src/main/java/me/devnatan/inventoryframework/IFInventoryListener.java b/inventory-framework-platform-bukkit/src/main/java/me/devnatan/inventoryframework/IFInventoryListener.java index 418514c0..4470e04d 100644 --- a/inventory-framework-platform-bukkit/src/main/java/me/devnatan/inventoryframework/IFInventoryListener.java +++ b/inventory-framework-platform-bukkit/src/main/java/me/devnatan/inventoryframework/IFInventoryListener.java @@ -9,133 +9,122 @@ import me.devnatan.inventoryframework.pipeline.StandardPipelinePhases; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; import org.bukkit.event.Listener; import org.bukkit.event.inventory.InventoryClickEvent; import org.bukkit.event.inventory.InventoryCloseEvent; import org.bukkit.event.inventory.InventoryDragEvent; +import org.bukkit.event.inventory.InventoryOpenEvent; import org.bukkit.event.player.PlayerDropItemEvent; import org.bukkit.event.player.PlayerPickupItemEvent; import org.bukkit.inventory.PlayerInventory; final class IFInventoryListener implements Listener { - private final ViewFrame viewFrame; - - public IFInventoryListener(ViewFrame viewFrame) { - this.viewFrame = viewFrame; - } - - @SuppressWarnings("unused") - @EventHandler(ignoreCancelled = true) - public void onInventoryClick(final InventoryClickEvent event) { - if (!(event.getWhoClicked() instanceof Player)) return; - - final Player player = (Player) event.getWhoClicked(); - final RootView root = viewFrame.getCurrentView(player); - if (root == null) return; - - final ElementFactory elementFactory = root.getElementFactory(); - final Viewer viewer = elementFactory.createViewer(player); - final IFContext mainContext; - try { - mainContext = root.getContext(viewer); - } catch (final IllegalArgumentException exception) { - event.setCancelled(true); - throw exception; - } - - final ViewContainer container = event.getClickedInventory() instanceof PlayerInventory - ? viewer.getSelfContainer() - : mainContext.getContainer(); - final Component component = mainContext.getComponent(event.getRawSlot()); - if (component != null && !component.isVisible()) return; - - final IFSlotClickContext slotContext = new SlotClickContext( - root, - container, - viewer, - mainContext.getIndexedViewers(), - event.getRawSlot(), - mainContext, - component, - event); - - root.getPipeline().execute(StandardPipelinePhases.CLICK, slotContext); - } - - @SuppressWarnings("unused") - @EventHandler(ignoreCancelled = true) - public void onInventoryClose(final InventoryCloseEvent event) { - if (!(event.getPlayer() instanceof Player)) return; - - final Player player = (Player) event.getPlayer(); - final RootView root = viewFrame.getCurrentView(player); - if (root == null) return; - - final ElementFactory elementFactory = root.getElementFactory(); - final String viewerIdentifier = elementFactory.convertViewer(player); - final IFContext mainContext = root.getContext(viewerIdentifier); - - final Viewer viewer = mainContext.getIndexedViewers().get(viewerIdentifier); - - final IFCloseContext closeContext = elementFactory.createContext( - root, - mainContext.getContainer(), - viewer, - mainContext.getIndexedViewers(), - IFCloseContext.class, - mainContext, - mainContext.getInitialData()); - - root.getPipeline().execute(StandardPipelinePhases.CLOSE, closeContext); - } - - @SuppressWarnings("deprecation") - @EventHandler(ignoreCancelled = true) - public void onItemPickup(PlayerPickupItemEvent event) { - final Player player = event.getPlayer(); - final RootView root = viewFrame.getCurrentView(player); - if (root == null) return; - - final IFContext context = root.getContext(root.getElementFactory().convertViewer(player)); - - if (!context.getConfig().isOptionSet(ViewConfig.CANCEL_ON_PICKUP)) return; - - event.setCancelled(context.getConfig().getOptionValue(ViewConfig.CANCEL_ON_PICKUP)); - } - - @EventHandler(ignoreCancelled = true) - public void onItemDrop(PlayerDropItemEvent event) { - final Player player = event.getPlayer(); - final RootView root = viewFrame.getCurrentView(player); - if (root == null) return; - - final IFContext context = root.getContext(root.getElementFactory().convertViewer(player)); - - if (!context.getConfig().isOptionSet(ViewConfig.CANCEL_ON_DROP)) return; - - event.setCancelled(context.getConfig().getOptionValue(ViewConfig.CANCEL_ON_DROP)); - } - - @EventHandler - public void onInventoryDrag(InventoryDragEvent event) { - if (!(event.getWhoClicked() instanceof Player)) return; - - final Player player = (Player) event.getWhoClicked(); - final RootView root = viewFrame.getCurrentView(player); - if (root == null) return; - - final IFContext context = root.getContext(root.getElementFactory().convertViewer(player)); - - if (!context.getConfig().isOptionSet(ViewConfig.CANCEL_ON_DRAG)) return; - - final boolean configValue = context.getConfig().getOptionValue(ViewConfig.CANCEL_ON_DRAG); - final int size = event.getInventory().getSize(); - for (final int rawSlot : event.getRawSlots()) { - if (!(rawSlot < size)) continue; - - event.setCancelled(configValue); - break; - } - } + private final ViewFrame viewFrame; + + public IFInventoryListener(ViewFrame viewFrame) { + this.viewFrame = viewFrame; + } + + @EventHandler(priority = EventPriority.LOW, ignoreCancelled = true) + public void onInventoryClick(final InventoryClickEvent event) { + if (!(event.getWhoClicked() instanceof Player)) return; + + final Player player = (Player) event.getWhoClicked(); + final Viewer viewer = viewFrame.getViewer(player); + if (viewer == null) return; + + final IFContext context = viewer.getContext(); + final RootView root = context.getRoot(); + + final Component component = context.getComponent(event.getRawSlot()); + if (component == null || !component.isVisible()) return; + + final ViewContainer container = event.getClickedInventory() instanceof PlayerInventory + ? viewer.getSelfContainer() + : context.getContainer(); + + final IFSlotClickContext slotContext = new SlotClickContext( + root, + container, + viewer, + context.getIndexedViewers(), + event.getRawSlot(), + context, + component, + event); + + root.getPipeline().execute(StandardPipelinePhases.CLICK, slotContext); + } + + @SuppressWarnings("unused") + @EventHandler(priority = EventPriority.LOW, ignoreCancelled = true) + public void onInventoryClose(final InventoryCloseEvent event) { + if (!(event.getPlayer() instanceof Player)) return; + + final Player player = (Player) event.getPlayer(); + final Viewer viewer = viewFrame.getViewer(player); + if (viewer == null) return; + + final IFContext context = viewer.getContext(); + final RootView root = context.getRoot(); + + final IFCloseContext closeContext = root.getElementFactory().createContext( + root, + context.getContainer(), + viewer, + context.getIndexedViewers(), + IFCloseContext.class, + context, + context.getInitialData()); + + root.getPipeline().execute(StandardPipelinePhases.CLOSE, closeContext); + } + + @SuppressWarnings("deprecation") + @EventHandler(priority = EventPriority.LOW, ignoreCancelled = true) + public void onItemPickup(PlayerPickupItemEvent event) { + final Viewer viewer = viewFrame.getViewer(event.getPlayer()); + if (viewer == null) return; + + final IFContext context = viewer.getContext(); + if (!context.getConfig().isOptionSet(ViewConfig.CANCEL_ON_PICKUP)) + return; + + event.setCancelled(context.getConfig().getOptionValue(ViewConfig.CANCEL_ON_PICKUP)); + } + + @EventHandler(priority = EventPriority.LOW, ignoreCancelled = true) + public void onItemDrop(PlayerDropItemEvent event) { + final Viewer viewer = viewFrame.getViewer(event.getPlayer()); + if (viewer == null) return; + + final IFContext context = viewer.getContext(); + if (!context.getConfig().isOptionSet(ViewConfig.CANCEL_ON_DROP)) + return; + + event.setCancelled(context.getConfig().getOptionValue(ViewConfig.CANCEL_ON_DROP)); + } + + @EventHandler(priority = EventPriority.LOW, ignoreCancelled = true) + public void onInventoryDrag(InventoryDragEvent event) { + if (!(event.getWhoClicked() instanceof Player)) return; + + final Viewer viewer = viewFrame.getViewer((Player) event.getWhoClicked()); + if (viewer == null) return; + + final IFContext context = viewer.getContext(); + if (!context.getConfig().isOptionSet(ViewConfig.CANCEL_ON_DRAG)) + return; + + final boolean configValue = context.getConfig().getOptionValue(ViewConfig.CANCEL_ON_DRAG); + final int size = event.getInventory().getSize(); + for (final int rawSlot : event.getRawSlots()) { + if (!(rawSlot < size)) continue; + + event.setCancelled(configValue); + break; + } + } } diff --git a/inventory-framework-platform-bukkit/src/main/java/me/devnatan/inventoryframework/ViewFrame.java b/inventory-framework-platform-bukkit/src/main/java/me/devnatan/inventoryframework/ViewFrame.java index 99134489..190dae22 100644 --- a/inventory-framework-platform-bukkit/src/main/java/me/devnatan/inventoryframework/ViewFrame.java +++ b/inventory-framework-platform-bukkit/src/main/java/me/devnatan/inventoryframework/ViewFrame.java @@ -3,12 +3,15 @@ import com.google.common.collect.Lists; import java.util.Collection; import java.util.Collections; +import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.Set; import java.util.UUID; import java.util.function.UnaryOperator; import java.util.stream.Collectors; + +import me.devnatan.inventoryframework.context.IFContext; import me.devnatan.inventoryframework.feature.DefaultFeatureInstaller; import me.devnatan.inventoryframework.feature.Feature; import me.devnatan.inventoryframework.feature.FeatureInstaller; @@ -36,6 +39,7 @@ public class ViewFrame extends IFViewFrame implements FeatureInstalle private final Plugin owner; private final FeatureInstaller featureInstaller = new DefaultFeatureInstaller<>(this); + private final Map viewerByPlayerUuid = new HashMap<>(); static { PlatformUtils.setFactory(new BukkitElementFactory()); @@ -226,24 +230,14 @@ private void checkRelocationIssues() { } } - /** - * Returns the current {@link RootView} the player is viewing based on open inventory. - *

- * Only views registered in that view frame are returned. - * - * @param player The player. - * @return The current view the player is viewing or {@code null} if it was not found, or it was - * not possible to determine it. - */ - public RootView getCurrentView(@NotNull Player player) { - final Inventory topInventory = player.getOpenInventory().getTopInventory(); - if (!(topInventory.getHolder() instanceof View)) return null; - - final View view = (View) topInventory.getHolder(); - if (!getRegisteredViews().containsKey(view.getUniqueId())) return null; - - return view; - } + /** + * This is an internal inventory-framework API that should not be used from outside of + * this library. No compatibility guarantees are provided. + */ + @ApiStatus.Internal + public Viewer getViewer(@NotNull Player player) { + return viewerByPlayerUuid.get(player.getUniqueId().toString()); + } /** * Creates a new ViewFrame. From 021fafc4832eef769b6da486ba86d8d43f0887b1 Mon Sep 17 00:00:00 2001 From: Natan Vieira do Nascimento Date: Wed, 30 Aug 2023 21:46:58 -0300 Subject: [PATCH 2/6] Platform-oriented API --- .../devnatan/inventoryframework/RootView.java | 28 +-- ...rtedOperationInSharedContextException.java | 17 ++ .../inventoryframework/ViewContainer.java | 2 - .../devnatan/inventoryframework/ViewType.java | 2 +- .../devnatan/inventoryframework/Viewer.java | 10 +- .../component/ItemComponentBuilder.java | 8 +- .../component/PaginationElementFactory.java | 4 +- .../context/IFCloseContext.java | 9 +- .../context/IFConfinedContext.java | 1 - .../inventoryframework/context/IFContext.java | 22 +- .../context/IFOpenContext.java | 20 +- .../context/IFSlotClickContext.java | 10 +- .../context/IFSlotContext.java | 40 +--- .../context/IFSlotRenderContext.java | 26 ++- .../context/IFUpdateContext.java | 3 + .../internal/ElementFactory.java | 52 +++-- .../inventoryframework/DefaultRootView.java | 10 - ...iewContext.java => AbstractIFContext.java} | 135 ++++-------- .../BukkitViewContainer.java | 4 - .../inventoryframework/BukkitViewer.java | 107 +++++----- .../IFInventoryListener.java | 194 ++++++++---------- .../me/devnatan/inventoryframework/View.java | 3 +- .../inventoryframework/ViewFrame.java | 85 +++----- .../context/CloseContext.java | 90 +++----- .../inventoryframework/context/Context.java | 6 +- .../context/OpenContext.java | 171 +++++++-------- .../context/RenderContext.java | 31 ++- .../context/SlotClickContext.java | 122 +++++++---- .../context/SlotContext.java | 157 +++++--------- .../context/SlotRenderContext.java | 76 +++++-- .../internal/BukkitElementFactory.java | 84 ++++---- .../inventoryframework/IFViewFrame.java | 62 +++--- .../PaginationStateBuilder.java | 67 +++--- .../inventoryframework/PlatformView.java | 133 ++++++------ .../context/ConfinedContext.java | 74 ------- .../context/PlatformConfinedContext.java | 41 ++++ .../context/PlatformContext.java | 18 ++ .../context/PlatformRenderContext.java | 166 ++++++++++----- .../pipeline/PlatformOpenInterceptor.java | 22 +- .../inventoryframework/TestUtils.java | 2 - .../internal/MockElementFactory.java | 108 ---------- 41 files changed, 1044 insertions(+), 1178 deletions(-) create mode 100644 inventory-framework-api/src/main/java/me/devnatan/inventoryframework/UnsupportedOperationInSharedContextException.java create mode 100644 inventory-framework-api/src/main/java/me/devnatan/inventoryframework/context/IFUpdateContext.java rename inventory-framework-core/src/main/java/me/devnatan/inventoryframework/context/{BaseViewContext.java => AbstractIFContext.java} (56%) rename {inventory-framework-api => inventory-framework-platform}/src/main/java/me/devnatan/inventoryframework/IFViewFrame.java (59%) delete mode 100644 inventory-framework-platform/src/main/java/me/devnatan/inventoryframework/context/ConfinedContext.java create mode 100644 inventory-framework-platform/src/main/java/me/devnatan/inventoryframework/context/PlatformConfinedContext.java create mode 100644 inventory-framework-platform/src/main/java/me/devnatan/inventoryframework/context/PlatformContext.java delete mode 100644 inventory-framework-test/src/main/java/me/devnatan/inventoryframework/internal/MockElementFactory.java diff --git a/inventory-framework-api/src/main/java/me/devnatan/inventoryframework/RootView.java b/inventory-framework-api/src/main/java/me/devnatan/inventoryframework/RootView.java index edb90a49..e91bc00d 100644 --- a/inventory-framework-api/src/main/java/me/devnatan/inventoryframework/RootView.java +++ b/inventory-framework-api/src/main/java/me/devnatan/inventoryframework/RootView.java @@ -1,6 +1,5 @@ package me.devnatan.inventoryframework; -import java.util.List; import java.util.Map; import java.util.Set; import java.util.UUID; @@ -127,18 +126,6 @@ public interface RootView extends VirtualView, Iterable { @NotNull Pipeline getPipeline(); - /** - * Opens this view to more than one viewer. - *

- * This is an internal inventory-framework API that should not be used from outside of - * this library. No compatibility guarantees are provided. - * - * @param viewers The viewers that'll see this view. - * @param initialData The initial data. - */ - @ApiStatus.Internal - void open(@NotNull List viewers, Object initialData); - /** * Closes all contexts that are currently active in this view. */ @@ -158,25 +145,26 @@ public interface RootView extends VirtualView, Iterable { /** * Runs a task in the next tick. + *

+ * This is an internal inventory-framework API that should not be used from outside of + * this library. No compatibility guarantees are provided. * * @param task The task to run. */ + @ApiStatus.Internal void nextTick(Runnable task); /** - * The IFViewFrame for this view. - *

* This is an internal inventory-framework API that should not be used from outside of * this library. No compatibility guarantees are provided. - * - * @return The current framework that holds this view. - * @throws UnsupportedOperationException If this view doesn't support a framework. */ @ApiStatus.Internal - IFViewFrame getFramework(); - Job getScheduledUpdateJob(); + /** + * This is an internal inventory-framework API that should not be used from outside of + * this library. No compatibility guarantees are provided. + */ @ApiStatus.Internal void setScheduledUpdateJob(@NotNull Job job); diff --git a/inventory-framework-api/src/main/java/me/devnatan/inventoryframework/UnsupportedOperationInSharedContextException.java b/inventory-framework-api/src/main/java/me/devnatan/inventoryframework/UnsupportedOperationInSharedContextException.java new file mode 100644 index 00000000..ad752386 --- /dev/null +++ b/inventory-framework-api/src/main/java/me/devnatan/inventoryframework/UnsupportedOperationInSharedContextException.java @@ -0,0 +1,17 @@ +package me.devnatan.inventoryframework; + +/** + * Thrown when an operation is called in a shared context, but it's not supported for some reason. + * + * @see Shared Contexts on Wiki + */ +public final class UnsupportedOperationInSharedContextException extends InventoryFrameworkException { + + public UnsupportedOperationInSharedContextException() { + super("This operation is not supported in shared contexts."); + } + + public UnsupportedOperationInSharedContextException(String replacement) { + super(String.format("This operation is not supported in shared contexts. Use #%s instead.", replacement)); + } +} diff --git a/inventory-framework-api/src/main/java/me/devnatan/inventoryframework/ViewContainer.java b/inventory-framework-api/src/main/java/me/devnatan/inventoryframework/ViewContainer.java index d1548714..7abe3024 100644 --- a/inventory-framework-api/src/main/java/me/devnatan/inventoryframework/ViewContainer.java +++ b/inventory-framework-api/src/main/java/me/devnatan/inventoryframework/ViewContainer.java @@ -1,9 +1,7 @@ package me.devnatan.inventoryframework; -import java.util.List; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import org.jetbrains.annotations.Unmodifiable; public interface ViewContainer { diff --git a/inventory-framework-api/src/main/java/me/devnatan/inventoryframework/ViewType.java b/inventory-framework-api/src/main/java/me/devnatan/inventoryframework/ViewType.java index 31e1456b..37b2c240 100644 --- a/inventory-framework-api/src/main/java/me/devnatan/inventoryframework/ViewType.java +++ b/inventory-framework-api/src/main/java/me/devnatan/inventoryframework/ViewType.java @@ -20,7 +20,7 @@ public final class ViewType { public static final ViewType SHULKER_BOX = new ViewType("shulker-box", 27, 3, 9); public static final ViewType SMOKER = new ViewType("smoker", 3, 2, 2, false, new int[] {2}); public static final ViewType VILLAGER_TRADING = new ViewType("villager-trading", 3, 1, 3, false, new int[] {2}); - public static final ViewType PLAYER = new ViewType("player", 54, 3, 9, false); + public static final ViewType PLAYER = new ViewType("player", 54, 3, 9, false); private static final int[] EMPTY_RESULT_SLOTS = new int[0]; diff --git a/inventory-framework-api/src/main/java/me/devnatan/inventoryframework/Viewer.java b/inventory-framework-api/src/main/java/me/devnatan/inventoryframework/Viewer.java index 2801f488..8cd698f7 100644 --- a/inventory-framework-api/src/main/java/me/devnatan/inventoryframework/Viewer.java +++ b/inventory-framework-api/src/main/java/me/devnatan/inventoryframework/Viewer.java @@ -1,8 +1,10 @@ package me.devnatan.inventoryframework; -import me.devnatan.inventoryframework.context.IFContext; +import me.devnatan.inventoryframework.context.IFRenderContext; +import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; +@ApiStatus.Internal public interface Viewer { /** @@ -33,6 +35,8 @@ public interface Viewer { @NotNull ViewContainer getSelfContainer(); - @NotNull - IFContext getContext(); + @NotNull + IFRenderContext getContext(); + + void setContext(IFRenderContext context); } diff --git a/inventory-framework-api/src/main/java/me/devnatan/inventoryframework/component/ItemComponentBuilder.java b/inventory-framework-api/src/main/java/me/devnatan/inventoryframework/component/ItemComponentBuilder.java index 70bbe815..c0d64cf2 100644 --- a/inventory-framework-api/src/main/java/me/devnatan/inventoryframework/component/ItemComponentBuilder.java +++ b/inventory-framework-api/src/main/java/me/devnatan/inventoryframework/component/ItemComponentBuilder.java @@ -3,8 +3,8 @@ import me.devnatan.inventoryframework.context.IFContext; import org.jetbrains.annotations.ApiStatus; -public interface ItemComponentBuilder, C extends IFContext> - extends ComponentBuilder { +public interface ItemComponentBuilder, Context extends IFContext> + extends ComponentBuilder { /** * Sets the slot that the item will be positioned. @@ -12,11 +12,11 @@ public interface ItemComponentBuilder, C ex * @param slot The item slot. * @return This item builder. */ - S withSlot(int slot); + Self withSlot(int slot); // TODO needs documentation @ApiStatus.Experimental - S withSlot(int row, int column); + Self withSlot(int row, int column); @ApiStatus.Internal boolean isContainedWithin(int position); diff --git a/inventory-framework-api/src/main/java/me/devnatan/inventoryframework/component/PaginationElementFactory.java b/inventory-framework-api/src/main/java/me/devnatan/inventoryframework/component/PaginationElementFactory.java index a07719b2..317b7a2b 100644 --- a/inventory-framework-api/src/main/java/me/devnatan/inventoryframework/component/PaginationElementFactory.java +++ b/inventory-framework-api/src/main/java/me/devnatan/inventoryframework/component/PaginationElementFactory.java @@ -1,7 +1,7 @@ package me.devnatan.inventoryframework.component; @FunctionalInterface -public interface PaginationElementFactory { +public interface PaginationElementFactory { - ComponentFactory create(T context, int index, int slot, V value); + ComponentFactory create(Context context, int index, int slot, V value); } diff --git a/inventory-framework-api/src/main/java/me/devnatan/inventoryframework/context/IFCloseContext.java b/inventory-framework-api/src/main/java/me/devnatan/inventoryframework/context/IFCloseContext.java index 6ea74f30..1c664b11 100644 --- a/inventory-framework-api/src/main/java/me/devnatan/inventoryframework/context/IFCloseContext.java +++ b/inventory-framework-api/src/main/java/me/devnatan/inventoryframework/context/IFCloseContext.java @@ -1,8 +1,15 @@ package me.devnatan.inventoryframework.context; +import org.jetbrains.annotations.ApiStatus; + public interface IFCloseContext extends IFConfinedContext { - IFContext getParent(); + /** + * This is an internal inventory-framework API that should not be used from outside of + * this library. No compatibility guarantees are provided. + */ + @ApiStatus.Internal + IFRenderContext getParent(); /** * Cancellation state of that context. diff --git a/inventory-framework-api/src/main/java/me/devnatan/inventoryframework/context/IFConfinedContext.java b/inventory-framework-api/src/main/java/me/devnatan/inventoryframework/context/IFConfinedContext.java index b35c32fe..6a638df3 100644 --- a/inventory-framework-api/src/main/java/me/devnatan/inventoryframework/context/IFConfinedContext.java +++ b/inventory-framework-api/src/main/java/me/devnatan/inventoryframework/context/IFConfinedContext.java @@ -17,7 +17,6 @@ public interface IFConfinedContext extends IFContext { * * @return The {@link Viewer} in the current scope of execution. */ - @NotNull Viewer getViewer(); /** diff --git a/inventory-framework-api/src/main/java/me/devnatan/inventoryframework/context/IFContext.java b/inventory-framework-api/src/main/java/me/devnatan/inventoryframework/context/IFContext.java index b626e86f..4ed45591 100644 --- a/inventory-framework-api/src/main/java/me/devnatan/inventoryframework/context/IFContext.java +++ b/inventory-framework-api/src/main/java/me/devnatan/inventoryframework/context/IFContext.java @@ -19,9 +19,13 @@ public interface IFContext extends VirtualView, StateValueHost { /** * An unique id for this 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 unique identifier for this context. */ + @ApiStatus.Internal @NotNull UUID getId(); @@ -45,26 +49,37 @@ public interface IFContext extends VirtualView, StateValueHost { List getViewers(); /** - * An unmodifiable view of all viewers that are tied to this context. + * A Map containing all viewers in that context. + *

+ * This is an internal inventory-framework API that should not be used from outside of + * this library. No compatibility guarantees are provided. * - * @return All unmodifiable view of all viewers. + * @return A Map containing all viewers in that context. */ @NotNull - @UnmodifiableView + @ApiStatus.Internal Map getIndexedViewers(); /** * Adds a new viewer to this context. + *

+ * This is an internal inventory-framework API that should not be used from outside of + * this library. No compatibility guarantees are provided. * * @param viewer The viewer that'll be added. */ + @ApiStatus.Internal void addViewer(@NotNull Viewer viewer); /** * Removes a new viewer to this context. + *

+ * This is an internal inventory-framework API that should not be used from outside of + * this library. No compatibility guarantees are provided. * * @param viewer The viewer that'll be removed. */ + @ApiStatus.Internal void removeViewer(@NotNull Viewer viewer); /** @@ -79,7 +94,6 @@ public interface IFContext extends VirtualView, StateValueHost { * * @return The container of this context. */ - @NotNull ViewContainer getContainer(); /** diff --git a/inventory-framework-api/src/main/java/me/devnatan/inventoryframework/context/IFOpenContext.java b/inventory-framework-api/src/main/java/me/devnatan/inventoryframework/context/IFOpenContext.java index d457c099..063151ec 100644 --- a/inventory-framework-api/src/main/java/me/devnatan/inventoryframework/context/IFOpenContext.java +++ b/inventory-framework-api/src/main/java/me/devnatan/inventoryframework/context/IFOpenContext.java @@ -1,7 +1,9 @@ package me.devnatan.inventoryframework.context; import java.util.concurrent.CompletableFuture; +import me.devnatan.inventoryframework.ViewConfig; import me.devnatan.inventoryframework.ViewConfigBuilder; +import me.devnatan.inventoryframework.Viewer; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; @@ -9,11 +11,18 @@ * This context is created before the container is opened, it is used for cancellation by previously * defined data also can be used to change the title and size of the container before the rendering intent. */ -public interface IFOpenContext extends IFConfinedContext { +public interface IFOpenContext extends IFContext { + + /** + * This is an internal inventory-framework API that should not be used from outside of + * this library. No compatibility guarantees are provided. + */ + @ApiStatus.Internal + Viewer getSubject(); /** * The task that will run before this context transitions from opening to rendering context. - * + *

* This is an internal inventory-framework API that should not be used from outside of * this library. No compatibility guarantees are provided. * @@ -56,4 +65,11 @@ public interface IFOpenContext extends IFConfinedContext { */ @NotNull ViewConfigBuilder modifyConfig(); + + /** + * This is an internal inventory-framework API that should not be used from outside of + * this library. No compatibility guarantees are provided. + */ + @ApiStatus.Internal + ViewConfig getModifiedConfig(); } 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 5f3c84a9..52896c7f 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,5 +1,7 @@ package me.devnatan.inventoryframework.context; +import me.devnatan.inventoryframework.ViewContainer; +import me.devnatan.inventoryframework.component.Component; import org.jetbrains.annotations.NotNull; /** @@ -8,7 +10,13 @@ * * @see IFSlotContext */ -public interface IFSlotClickContext extends IFSlotContext { +public interface IFSlotClickContext extends IFSlotContext, IFConfinedContext { + + // TODO needs documentation + @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 94867ec1..2335ffad 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,6 @@ package me.devnatan.inventoryframework.context; -import me.devnatan.inventoryframework.component.Component; import org.jetbrains.annotations.ApiStatus; -import org.jetbrains.annotations.Nullable; /** * Represents a context in which there is a specific slot related to it, the main context @@ -25,7 +23,7 @@ public interface IFSlotContext extends IFContext { * @return The parent context of this context. */ @ApiStatus.Internal - IFContext getParent(); + IFRenderContext getParent(); /** * Clears this slot from the current context. @@ -88,40 +86,4 @@ public interface IFSlotContext extends IFContext { * @return If this context originated from the actor's container */ boolean isOnEntityContainer(); - - /** - * Checks if the item in this context has been changed. - * - *

This is an internal inventory-framework API that should not be used from outside of - * this library. No compatibility guarantees are provided. - * - * @return If the item in this context has been changed. - */ - @ApiStatus.Internal - boolean hasChanged(); - - /** - * Marks this context as changed. - * - *

Improperly changing this property can cause unexpected side effects. - * - *

This is an internal inventory-framework API that should not be used from outside of - * this library. No compatibility guarantees are provided. - * - * @param changed If the context item was changed. - */ - @ApiStatus.Internal - void setChanged(boolean changed); - - /** - *

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 - boolean isRegistered(); - - // TODO documentation - // can be null if context origin is outside root view container - @Nullable - Component getComponent(); } diff --git a/inventory-framework-api/src/main/java/me/devnatan/inventoryframework/context/IFSlotRenderContext.java b/inventory-framework-api/src/main/java/me/devnatan/inventoryframework/context/IFSlotRenderContext.java index 9e0bde13..5f5ed650 100644 --- a/inventory-framework-api/src/main/java/me/devnatan/inventoryframework/context/IFSlotRenderContext.java +++ b/inventory-framework-api/src/main/java/me/devnatan/inventoryframework/context/IFSlotRenderContext.java @@ -2,7 +2,7 @@ import org.jetbrains.annotations.ApiStatus; -public interface IFSlotRenderContext extends IFSlotContext { +public interface IFSlotRenderContext extends IFSlotContext, IFConfinedContext { @ApiStatus.Internal Object getResult(); @@ -10,4 +10,28 @@ public interface IFSlotRenderContext extends IFSlotContext { boolean isCancelled(); void setCancelled(boolean cancelled); + + /** + * Checks if the item in this context has been changed. + * + *

This is an internal inventory-framework API that should not be used from outside of + * this library. No compatibility guarantees are provided. + * + * @return If the item in this context has been changed. + */ + @ApiStatus.Internal + boolean hasChanged(); + + /** + * Marks this context as changed. + * + *

Improperly changing this property can cause unexpected side effects. + * + *

This is an internal inventory-framework API that should not be used from outside of + * this library. No compatibility guarantees are provided. + * + * @param changed If the context item was changed. + */ + @ApiStatus.Internal + void setChanged(boolean changed); } diff --git a/inventory-framework-api/src/main/java/me/devnatan/inventoryframework/context/IFUpdateContext.java b/inventory-framework-api/src/main/java/me/devnatan/inventoryframework/context/IFUpdateContext.java new file mode 100644 index 00000000..c3027bec --- /dev/null +++ b/inventory-framework-api/src/main/java/me/devnatan/inventoryframework/context/IFUpdateContext.java @@ -0,0 +1,3 @@ +package me.devnatan.inventoryframework.context; + +public interface IFUpdateContext extends IFContext {} 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 1263198e..fc489695 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 @@ -1,14 +1,21 @@ package me.devnatan.inventoryframework.internal; +import java.util.List; import java.util.Map; +import java.util.UUID; import me.devnatan.inventoryframework.RootView; +import me.devnatan.inventoryframework.ViewConfig; import me.devnatan.inventoryframework.ViewContainer; import me.devnatan.inventoryframework.Viewer; import me.devnatan.inventoryframework.VirtualView; import me.devnatan.inventoryframework.component.Component; import me.devnatan.inventoryframework.component.ComponentBuilder; +import me.devnatan.inventoryframework.context.IFCloseContext; import me.devnatan.inventoryframework.context.IFContext; -import me.devnatan.inventoryframework.context.IFSlotContext; +import me.devnatan.inventoryframework.context.IFOpenContext; +import me.devnatan.inventoryframework.context.IFRenderContext; +import me.devnatan.inventoryframework.context.IFSlotClickContext; +import me.devnatan.inventoryframework.context.IFSlotRenderContext; import me.devnatan.inventoryframework.logging.Logger; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -38,30 +45,37 @@ public abstract class ElementFactory { public abstract ViewContainer createContainer(@NotNull IFContext context); @NotNull - public abstract Viewer createViewer(Object... parameters); + public abstract Viewer createViewer(@NotNull Object entity, IFRenderContext context); - @NotNull - public abstract String convertViewer(Object input); + public abstract IFOpenContext createOpenContext( + @NotNull RootView root, @Nullable Viewer subject, @NotNull List viewers, Object initialData); - @NotNull - public abstract T createContext( + public abstract IFRenderContext createRenderContext( + @NotNull UUID id, @NotNull RootView root, - ViewContainer container, - Viewer subject, + @NotNull ViewConfig config, + @NotNull ViewContainer container, @NotNull Map viewers, - @NotNull Class kind, - @Nullable IFContext parent, + Viewer subject, Object initialData); - @NotNull - public abstract T createSlotContext( - int slot, - Component component, - @NotNull ViewContainer container, - Viewer subject, - @NotNull Map viewers, - @NotNull IFContext parent, - @NotNull Class kind); + public abstract IFSlotClickContext createSlotClickContext( + int slotClicked, + @NotNull Viewer whoClicked, + @NotNull ViewContainer interactionContainer, + @Nullable Component componentClicked, + @NotNull Object origin); + + public abstract IFSlotRenderContext createSlotRenderContext( + int slot, @NotNull IFRenderContext parent, @Nullable Viewer viewer); + + /** + * Creates a new close context for the current platform. + * + * @param viewer The viewer that is currently the subject of the event of close. + * @return A new close context instance. + */ + public abstract IFCloseContext createCloseContext(@NotNull Viewer viewer, @NotNull IFRenderContext parent); /** * Creates a new platform builder instance. diff --git a/inventory-framework-core/src/main/java/me/devnatan/inventoryframework/DefaultRootView.java b/inventory-framework-core/src/main/java/me/devnatan/inventoryframework/DefaultRootView.java index 198c4eab..7150d4d5 100644 --- a/inventory-framework-core/src/main/java/me/devnatan/inventoryframework/DefaultRootView.java +++ b/inventory-framework-core/src/main/java/me/devnatan/inventoryframework/DefaultRootView.java @@ -105,11 +105,6 @@ public final void setConfig(@NotNull ViewConfig config) { return pipeline; } - @Override - public void open(@NotNull List viewers, Object initialData) { - throw new UnsupportedOperationException("Missing #open(...) implementation"); - } - @Override public final void closeForEveryone() { getContexts().forEach(IFContext::closeForEveryone); @@ -154,11 +149,6 @@ public void setScheduledUpdateJob(@NotNull Job job) { this.scheduledUpdateJob = job; } - @Override - public IFViewFrame getFramework() { - throw new UnsupportedOperationException("Missing #getFramework() implementation"); - } - @NotNull @Override public final Map getMetadata() { diff --git a/inventory-framework-core/src/main/java/me/devnatan/inventoryframework/context/BaseViewContext.java b/inventory-framework-core/src/main/java/me/devnatan/inventoryframework/context/AbstractIFContext.java similarity index 56% rename from inventory-framework-core/src/main/java/me/devnatan/inventoryframework/context/BaseViewContext.java rename to inventory-framework-core/src/main/java/me/devnatan/inventoryframework/context/AbstractIFContext.java index e6b3e64e..4c2a0b75 100644 --- a/inventory-framework-core/src/main/java/me/devnatan/inventoryframework/context/BaseViewContext.java +++ b/inventory-framework-core/src/main/java/me/devnatan/inventoryframework/context/AbstractIFContext.java @@ -8,12 +8,11 @@ import java.util.LinkedList; import java.util.List; import java.util.Map; -import java.util.Objects; import java.util.Optional; -import java.util.UUID; +import me.devnatan.inventoryframework.InventoryFrameworkException; import me.devnatan.inventoryframework.RootView; +import me.devnatan.inventoryframework.UnsupportedOperationInSharedContextException; import me.devnatan.inventoryframework.ViewConfig; -import me.devnatan.inventoryframework.ViewContainer; import me.devnatan.inventoryframework.Viewer; import me.devnatan.inventoryframework.component.Component; import me.devnatan.inventoryframework.pipeline.StandardPipelinePhases; @@ -23,50 +22,16 @@ import org.jetbrains.annotations.Unmodifiable; import org.jetbrains.annotations.UnmodifiableView; -class BaseViewContext extends DefaultStateValueHost implements IFContext { +abstract class AbstractIFContext extends DefaultStateValueHost implements IFContext { - private final UUID id = UUID.randomUUID(); - private final @NotNull RootView root; - // Container can be null on pre-render/intermediate contexts - private final @Nullable ViewContainer container; private final List components = new LinkedList<>(); private final Deque markedForRemoval = new ArrayDeque<>(); - private final Object initialData; - protected final Map viewers; + private final Map indexedViewers = new HashMap<>(); protected ViewConfig config; - public BaseViewContext( - @NotNull RootView root, - @Nullable ViewContainer container, - @NotNull Map viewers, - Object initialData) { - this.root = root; - this.container = container; - this.config = root.getConfig(); - this.initialData = initialData; - this.viewers = new HashMap<>(viewers); - } - - @NotNull - @Override - public UUID getId() { - return id; - } - - @Override - public @NotNull ViewConfig getConfig() { - return config; - } - - @Override - public final @NotNull RootView getRoot() { - return root; - } - @Override - public @NotNull ViewContainer getContainer() { - if (container == null) throw new IllegalStateException("Unable to get null container"); - return container; + public @NotNull Map getIndexedViewers() { + return indexedViewers; } @Override @@ -74,22 +39,17 @@ public UUID getId() { return Collections.unmodifiableList(new ArrayList<>(getIndexedViewers().values())); } - @Override - public @NotNull @UnmodifiableView Map getIndexedViewers() { - return Collections.unmodifiableMap(viewers); - } - @Override public final void addViewer(@NotNull Viewer viewer) { - synchronized (viewers) { - viewers.put(viewer.getId(), viewer); + synchronized (getIndexedViewers()) { + getIndexedViewers().put(viewer.getId(), viewer); } } @Override public final void removeViewer(@NotNull Viewer viewer) { - synchronized (viewers) { - viewers.remove(viewer.getId()); + synchronized (getIndexedViewers()) { + getIndexedViewers().remove(viewer.getId()); } } @@ -100,7 +60,7 @@ public final void removeViewer(@NotNull Viewer viewer) { @Override public final @NotNull String getInitialTitle() { - return container.getTitle(); + return getConfig().getTitle().toString(); } @Override @@ -110,16 +70,16 @@ public final void removeViewer(@NotNull Viewer viewer) { @Override public final void updateTitleForEveryone(@NotNull String title) { - getContainer().changeTitle(title); + for (final Viewer viewer : getViewers()) getContainer().changeTitle(title, viewer); } @Override public final void resetTitleForEveryone() { - getContainer().changeTitle(null); + for (final Viewer viewer : getViewers()) getContainer().changeTitle(null, viewer); } @Override - public void closeForEveryone() { + public final void closeForEveryone() { getContainer().close(); } @@ -128,11 +88,6 @@ public final void openForEveryone(Class other) { openForEveryone(other, null); } - @Override - public void openForEveryone(Class other, Object initialData) { - getRoot().getFramework().open(other, getViewers(), initialData); - } - @Override public @UnmodifiableView @NotNull List getComponents() { return Collections.unmodifiableList(components); @@ -161,18 +116,13 @@ public void removeComponent(@NotNull Component component) { } private IFSlotRenderContext createRenderContext(@NotNull Component component) { - final Viewer subject = this instanceof IFConfinedContext ? ((IFConfinedContext) this).getViewer() : null; + if (!(this instanceof IFRenderContext)) + throw new InventoryFrameworkException("Slot render context cannot be created from non-render parent"); + final IFRenderContext renderContext = (IFRenderContext) this; return getRoot() .getElementFactory() - .createSlotContext( - component.getPosition(), - component, - getContainer(), - subject, - getIndexedViewers(), - this, - IFSlotRenderContext.class); + .createSlotRenderContext(component.getPosition(), renderContext, renderContext.getViewer()); } @Override @@ -212,42 +162,43 @@ public void update() { } @Override - public boolean isMarkedForRemoval(int componentIndex) { + public final boolean isMarkedForRemoval(int componentIndex) { return markedForRemoval.contains(componentIndex); } @Override - public Object getInitialData() { - return initialData instanceof Map ? Collections.unmodifiableMap((Map) initialData) : initialData; + public final boolean isShared() { + return getIndexedViewers().size() > 1; } - @Override - public boolean isShared() { - return getViewers().size() > 1; + /** + * Throws a {@link InventoryFrameworkException} saying that the method that's being executed is + * not supported if this context is a shared context. + */ + final void tryThrowDoNotWorkWithSharedContext() { + if (!isShared()) return; + throw new UnsupportedOperationInSharedContextException(); } - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - BaseViewContext that = (BaseViewContext) o; - return Objects.equals(getId(), that.getId()); - } - - @Override - public int hashCode() { - return Objects.hash(getId()); + /** + * Throws a {@link InventoryFrameworkException} saying that the method that's being executed is + * not supported if this context is a shared context, with a replacement message. + * + * @param replacement The alternative method to be used. + */ + final void tryThrowDoNotWorkWithSharedContext(String replacement) { + if (!isShared()) return; + throw new UnsupportedOperationInSharedContextException(replacement); } @Override public String toString() { - return "BaseViewContext{" + "id=" - + id + ", container=" - + container + ", viewers=" - + viewers + ", config=" - + config + ", markedForRemoval=" - + markedForRemoval + ", initialData=" - + initialData + "} " + return "AbstractIFContext{" + "id=" + + getId() + ", container=" + + getContainer() + ", viewers=" + + getIndexedViewers() + ", config=" + + getConfig() + ", initialData=" + + getInitialData() + "} " + super.toString(); } } diff --git a/inventory-framework-platform-bukkit/src/main/java/me/devnatan/inventoryframework/BukkitViewContainer.java b/inventory-framework-platform-bukkit/src/main/java/me/devnatan/inventoryframework/BukkitViewContainer.java index 6ecd9113..3ea995d7 100644 --- a/inventory-framework-platform-bukkit/src/main/java/me/devnatan/inventoryframework/BukkitViewContainer.java +++ b/inventory-framework-platform-bukkit/src/main/java/me/devnatan/inventoryframework/BukkitViewContainer.java @@ -1,10 +1,7 @@ package me.devnatan.inventoryframework; import java.util.ArrayList; -import java.util.Collections; -import java.util.List; import java.util.Objects; -import java.util.stream.Collectors; import me.devnatan.inventoryframework.runtime.thirdparty.InventoryUpdate; import org.bukkit.entity.HumanEntity; import org.bukkit.entity.Player; @@ -14,7 +11,6 @@ import org.bukkit.inventory.PlayerInventory; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import org.jetbrains.annotations.Unmodifiable; public final class BukkitViewContainer implements ViewContainer { diff --git a/inventory-framework-platform-bukkit/src/main/java/me/devnatan/inventoryframework/BukkitViewer.java b/inventory-framework-platform-bukkit/src/main/java/me/devnatan/inventoryframework/BukkitViewer.java index 8bd2e404..61a90b8f 100644 --- a/inventory-framework-platform-bukkit/src/main/java/me/devnatan/inventoryframework/BukkitViewer.java +++ b/inventory-framework-platform-bukkit/src/main/java/me/devnatan/inventoryframework/BukkitViewer.java @@ -1,70 +1,75 @@ package me.devnatan.inventoryframework; -import me.devnatan.inventoryframework.context.IFContext; +import java.util.Objects; +import me.devnatan.inventoryframework.context.IFRenderContext; import org.bukkit.entity.Player; import org.jetbrains.annotations.NotNull; -import java.util.Objects; - public final class BukkitViewer implements Viewer { - private final Player player; - private ViewContainer selfContainer; - private IFContext context; + private final Player player; + private ViewContainer selfContainer; + private IFRenderContext context; + + public BukkitViewer(@NotNull Player player, IFRenderContext context) { + this.player = player; + this.context = context; + } - public BukkitViewer(@NotNull Player player, @NotNull IFContext context) { - this.player = player; - this.context = context; - } + public Player getPlayer() { + return player; + } - public Player getPlayer() { - return player; - } + @NotNull + @Override + public IFRenderContext getContext() { + return context; + } - @NotNull - @Override - public IFContext getContext() { - return context; - } + @Override + public void setContext(IFRenderContext context) { + this.context = context; + } - @Override - public @NotNull String getId() { - return getPlayer().getUniqueId().toString(); - } + @Override + public @NotNull String getId() { + return getPlayer().getUniqueId().toString(); + } - @Override - public void open(@NotNull final ViewContainer container) { - getPlayer().openInventory(((BukkitViewContainer) container).getInventory()); - } + @Override + public void open(@NotNull final ViewContainer container) { + getPlayer().openInventory(((BukkitViewContainer) container).getInventory()); + } - @Override - public void close() { - getPlayer().closeInventory(); - } + @Override + public void close() { + getPlayer().closeInventory(); + } - @Override - public @NotNull ViewContainer getSelfContainer() { - if (selfContainer == null) - selfContainer = new BukkitViewContainer(getPlayer().getInventory(), getContext().isShared(), ViewType.PLAYER); + @Override + public @NotNull ViewContainer getSelfContainer() { + if (selfContainer == null) + selfContainer = new BukkitViewContainer( + getPlayer().getInventory(), getContext().isShared(), ViewType.PLAYER); - return selfContainer; - } + return selfContainer; + } - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - BukkitViewer that = (BukkitViewer) o; - return Objects.equals(getPlayer(), that.getPlayer()); - } + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + BukkitViewer that = (BukkitViewer) o; + return Objects.equals(getPlayer(), that.getPlayer()); + } - @Override - public int hashCode() { - return Objects.hash(getPlayer()); - } + @Override + public int hashCode() { + return Objects.hash(getPlayer()); + } - @Override - public String toString() { - return "BukkitViewer{" + "player=" + player + ", selfContainer=" + selfContainer + '}'; - } + @Override + public String toString() { + return "BukkitViewer{" + "player=" + player + ", selfContainer=" + selfContainer + '}'; + } } diff --git a/inventory-framework-platform-bukkit/src/main/java/me/devnatan/inventoryframework/IFInventoryListener.java b/inventory-framework-platform-bukkit/src/main/java/me/devnatan/inventoryframework/IFInventoryListener.java index 4470e04d..b2c4cbf8 100644 --- a/inventory-framework-platform-bukkit/src/main/java/me/devnatan/inventoryframework/IFInventoryListener.java +++ b/inventory-framework-platform-bukkit/src/main/java/me/devnatan/inventoryframework/IFInventoryListener.java @@ -3,9 +3,8 @@ import me.devnatan.inventoryframework.component.Component; import me.devnatan.inventoryframework.context.IFCloseContext; import me.devnatan.inventoryframework.context.IFContext; +import me.devnatan.inventoryframework.context.IFRenderContext; import me.devnatan.inventoryframework.context.IFSlotClickContext; -import me.devnatan.inventoryframework.context.SlotClickContext; -import me.devnatan.inventoryframework.internal.ElementFactory; import me.devnatan.inventoryframework.pipeline.StandardPipelinePhases; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; @@ -14,117 +13,96 @@ import org.bukkit.event.inventory.InventoryClickEvent; import org.bukkit.event.inventory.InventoryCloseEvent; import org.bukkit.event.inventory.InventoryDragEvent; -import org.bukkit.event.inventory.InventoryOpenEvent; import org.bukkit.event.player.PlayerDropItemEvent; import org.bukkit.event.player.PlayerPickupItemEvent; import org.bukkit.inventory.PlayerInventory; final class IFInventoryListener implements Listener { - private final ViewFrame viewFrame; + private final ViewFrame viewFrame; - public IFInventoryListener(ViewFrame viewFrame) { - this.viewFrame = viewFrame; - } - - @EventHandler(priority = EventPriority.LOW, ignoreCancelled = true) - public void onInventoryClick(final InventoryClickEvent event) { - if (!(event.getWhoClicked() instanceof Player)) return; - - final Player player = (Player) event.getWhoClicked(); - final Viewer viewer = viewFrame.getViewer(player); - if (viewer == null) return; - - final IFContext context = viewer.getContext(); - final RootView root = context.getRoot(); - - final Component component = context.getComponent(event.getRawSlot()); - if (component == null || !component.isVisible()) return; - - final ViewContainer container = event.getClickedInventory() instanceof PlayerInventory - ? viewer.getSelfContainer() - : context.getContainer(); - - final IFSlotClickContext slotContext = new SlotClickContext( - root, - container, - viewer, - context.getIndexedViewers(), - event.getRawSlot(), - context, - component, - event); - - root.getPipeline().execute(StandardPipelinePhases.CLICK, slotContext); - } - - @SuppressWarnings("unused") - @EventHandler(priority = EventPriority.LOW, ignoreCancelled = true) - public void onInventoryClose(final InventoryCloseEvent event) { - if (!(event.getPlayer() instanceof Player)) return; - - final Player player = (Player) event.getPlayer(); - final Viewer viewer = viewFrame.getViewer(player); - if (viewer == null) return; - - final IFContext context = viewer.getContext(); - final RootView root = context.getRoot(); - - final IFCloseContext closeContext = root.getElementFactory().createContext( - root, - context.getContainer(), - viewer, - context.getIndexedViewers(), - IFCloseContext.class, - context, - context.getInitialData()); - - root.getPipeline().execute(StandardPipelinePhases.CLOSE, closeContext); - } - - @SuppressWarnings("deprecation") - @EventHandler(priority = EventPriority.LOW, ignoreCancelled = true) - public void onItemPickup(PlayerPickupItemEvent event) { - final Viewer viewer = viewFrame.getViewer(event.getPlayer()); - if (viewer == null) return; - - final IFContext context = viewer.getContext(); - if (!context.getConfig().isOptionSet(ViewConfig.CANCEL_ON_PICKUP)) - return; - - event.setCancelled(context.getConfig().getOptionValue(ViewConfig.CANCEL_ON_PICKUP)); - } - - @EventHandler(priority = EventPriority.LOW, ignoreCancelled = true) - public void onItemDrop(PlayerDropItemEvent event) { - final Viewer viewer = viewFrame.getViewer(event.getPlayer()); - if (viewer == null) return; - - final IFContext context = viewer.getContext(); - if (!context.getConfig().isOptionSet(ViewConfig.CANCEL_ON_DROP)) - return; - - event.setCancelled(context.getConfig().getOptionValue(ViewConfig.CANCEL_ON_DROP)); - } - - @EventHandler(priority = EventPriority.LOW, ignoreCancelled = true) - public void onInventoryDrag(InventoryDragEvent event) { - if (!(event.getWhoClicked() instanceof Player)) return; - - final Viewer viewer = viewFrame.getViewer((Player) event.getWhoClicked()); - if (viewer == null) return; - - final IFContext context = viewer.getContext(); - if (!context.getConfig().isOptionSet(ViewConfig.CANCEL_ON_DRAG)) - return; - - final boolean configValue = context.getConfig().getOptionValue(ViewConfig.CANCEL_ON_DRAG); - final int size = event.getInventory().getSize(); - for (final int rawSlot : event.getRawSlots()) { - if (!(rawSlot < size)) continue; - - event.setCancelled(configValue); - break; - } - } + public IFInventoryListener(ViewFrame viewFrame) { + this.viewFrame = viewFrame; + } + + @EventHandler(priority = EventPriority.LOW, ignoreCancelled = true) + public void onInventoryClick(final InventoryClickEvent event) { + if (!(event.getWhoClicked() instanceof Player)) return; + + final Player player = (Player) event.getWhoClicked(); + final Viewer viewer = viewFrame.getViewer(player); + if (viewer == null) return; + + final IFRenderContext context = viewer.getContext(); + final Component clickedComponent = context.getComponent(event.getRawSlot()); + if (clickedComponent == null || !clickedComponent.isVisible()) return; + + final ViewContainer clickedContainer = event.getClickedInventory() instanceof PlayerInventory + ? viewer.getSelfContainer() + : context.getContainer(); + final RootView root = context.getRoot(); + final IFSlotClickContext clickContext = root.getElementFactory() + .createSlotClickContext(event.getRawSlot(), viewer, clickedContainer, clickedComponent, event); + + root.getPipeline().execute(StandardPipelinePhases.CLICK, clickContext); + } + + @SuppressWarnings("unused") + @EventHandler(priority = EventPriority.LOW, ignoreCancelled = true) + public void onInventoryClose(final InventoryCloseEvent event) { + if (!(event.getPlayer() instanceof Player)) return; + + final Player player = (Player) event.getPlayer(); + final Viewer viewer = viewFrame.getViewer(player); + if (viewer == null) return; + + final IFRenderContext context = viewer.getContext(); + final RootView root = context.getRoot(); + final IFCloseContext closeContext = root.getElementFactory().createCloseContext(viewer, context); + + root.getPipeline().execute(StandardPipelinePhases.CLOSE, closeContext); + } + + @SuppressWarnings("deprecation") + @EventHandler(priority = EventPriority.LOW, ignoreCancelled = true) + public void onItemPickup(PlayerPickupItemEvent event) { + final Viewer viewer = viewFrame.getViewer(event.getPlayer()); + if (viewer == null) return; + + final IFContext context = viewer.getContext(); + if (!context.getConfig().isOptionSet(ViewConfig.CANCEL_ON_PICKUP)) return; + + event.setCancelled(context.getConfig().getOptionValue(ViewConfig.CANCEL_ON_PICKUP)); + } + + @EventHandler(priority = EventPriority.LOW, ignoreCancelled = true) + public void onItemDrop(PlayerDropItemEvent event) { + final Viewer viewer = viewFrame.getViewer(event.getPlayer()); + if (viewer == null) return; + + final IFContext context = viewer.getContext(); + if (!context.getConfig().isOptionSet(ViewConfig.CANCEL_ON_DROP)) return; + + event.setCancelled(context.getConfig().getOptionValue(ViewConfig.CANCEL_ON_DROP)); + } + + @EventHandler(priority = EventPriority.LOW, ignoreCancelled = true) + public void onInventoryDrag(InventoryDragEvent event) { + if (!(event.getWhoClicked() instanceof Player)) return; + + final Viewer viewer = viewFrame.getViewer((Player) event.getWhoClicked()); + if (viewer == null) return; + + final IFContext context = viewer.getContext(); + if (!context.getConfig().isOptionSet(ViewConfig.CANCEL_ON_DRAG)) return; + + final boolean configValue = context.getConfig().getOptionValue(ViewConfig.CANCEL_ON_DRAG); + final int size = event.getInventory().getSize(); + for (final int rawSlot : event.getRawSlots()) { + if (!(rawSlot < size)) continue; + + event.setCancelled(configValue); + break; + } + } } diff --git a/inventory-framework-platform-bukkit/src/main/java/me/devnatan/inventoryframework/View.java b/inventory-framework-platform-bukkit/src/main/java/me/devnatan/inventoryframework/View.java index f9c5198e..5a0b10b5 100644 --- a/inventory-framework-platform-bukkit/src/main/java/me/devnatan/inventoryframework/View.java +++ b/inventory-framework-platform-bukkit/src/main/java/me/devnatan/inventoryframework/View.java @@ -26,6 +26,7 @@ @ApiStatus.OverrideOnly public class View extends PlatformView< + ViewFrame, BukkitItemComponentBuilder, Context, OpenContext, @@ -51,7 +52,7 @@ public final void registerPlatformInterceptors() { @Override public final void nextTick(Runnable task) { - Bukkit.getServer().getScheduler().runTask(((ViewFrame) getFramework()).getOwner(), task); + Bukkit.getServer().getScheduler().runTask(getFramework().getOwner(), task); } @NotNull diff --git a/inventory-framework-platform-bukkit/src/main/java/me/devnatan/inventoryframework/ViewFrame.java b/inventory-framework-platform-bukkit/src/main/java/me/devnatan/inventoryframework/ViewFrame.java index 190dae22..5e026ac5 100644 --- a/inventory-framework-platform-bukkit/src/main/java/me/devnatan/inventoryframework/ViewFrame.java +++ b/inventory-framework-platform-bukkit/src/main/java/me/devnatan/inventoryframework/ViewFrame.java @@ -1,17 +1,12 @@ package me.devnatan.inventoryframework; -import com.google.common.collect.Lists; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.Iterator; import java.util.Map; -import java.util.Set; import java.util.UUID; import java.util.function.UnaryOperator; -import java.util.stream.Collectors; - -import me.devnatan.inventoryframework.context.IFContext; import me.devnatan.inventoryframework.feature.DefaultFeatureInstaller; import me.devnatan.inventoryframework.feature.Feature; import me.devnatan.inventoryframework.feature.FeatureInstaller; @@ -19,14 +14,13 @@ import me.devnatan.inventoryframework.internal.PlatformUtils; import me.devnatan.inventoryframework.runtime.thirdparty.Metrics; import org.bukkit.entity.Player; -import org.bukkit.inventory.Inventory; import org.bukkit.plugin.Plugin; import org.bukkit.plugin.ServicesManager; import org.bukkit.plugin.java.JavaPlugin; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; -public class ViewFrame extends IFViewFrame implements FeatureInstaller { +public class ViewFrame extends IFViewFrame implements FeatureInstaller { private static final String BSTATS_SYSTEM_PROP = "inventory-framework.enable-bstats"; private static final int BSTATS_PROJECT_ID = 15518; @@ -39,7 +33,7 @@ public class ViewFrame extends IFViewFrame implements FeatureInstalle private final Plugin owner; private final FeatureInstaller featureInstaller = new DefaultFeatureInstaller<>(this); - private final Map viewerByPlayerUuid = new HashMap<>(); + private final Map viewerByPlayerUuid = new HashMap<>(); static { PlatformUtils.setFactory(new BukkitElementFactory()); @@ -54,45 +48,24 @@ public Plugin getOwner() { return owner; } - /** - * {@inheritDoc} - *

- * This is an internal inventory-framework API that should not be used from outside of - * this library. No compatibility guarantees are provided. - */ - @ApiStatus.Internal - @Override - public void open( - @NotNull Class viewClass, @NotNull Iterable viewers, Object initialData) { - for (final Viewer viewer : viewers) - if (!(viewer instanceof BukkitViewer)) - throw new IllegalArgumentException("Only BukkitViewer viewer impl is supported"); - - final RootView view = getRegisteredViewByType(viewClass); - if (!(view instanceof PlatformView)) - throw new IllegalStateException("Only PlatformView can be opened through #open(...)"); - - view.open(Lists.newArrayList(viewers), initialData); - } - /** * Opens a view to a player. * * @param viewClass The target view to be opened. * @param player The player that the view will be open to. */ - public void open(@NotNull Class viewClass, @NotNull Player player) { + public void open(@NotNull Class viewClass, @NotNull Player player) { open(viewClass, player, null); } /** * Opens a view to a player with initial data. * - * @param viewClass The target view to be opened. - * @param player The player that the view will be open to. + * @param viewClass The target view to be opened. + * @param player The player that the view will be open to. * @param initialData The initial data. */ - public void open(@NotNull Class viewClass, @NotNull Player player, Object initialData) { + public void open(@NotNull Class viewClass, @NotNull Player player, Object initialData) { open(viewClass, Collections.singletonList(player), initialData); } @@ -108,7 +81,7 @@ public void open(@NotNull Class viewClass, @NotNull Player p * @param players The players that the view will be open to. */ @ApiStatus.Experimental - public void open(@NotNull Class viewClass, @NotNull Collection players) { + public void open(@NotNull Class viewClass, @NotNull Collection players) { open(viewClass, players, null); } @@ -126,13 +99,10 @@ public void open(@NotNull Class viewClass, @NotNull Collecti */ @ApiStatus.Experimental public void open( - @NotNull Class viewClass, + @NotNull Class viewClass, @NotNull Collection players, Object initialData) { - final Set viewers = players.stream() - .map(player -> PlatformUtils.getFactory().createViewer(player)) - .collect(Collectors.toSet()); - open(viewClass, viewers, initialData); + internalOpen(viewClass, players, initialData); } @Override @@ -154,9 +124,9 @@ public void unregister() { // Locks new operations while unregistering setRegistered(false); - final Iterator iterator = getRegisteredViews().values().iterator(); + final Iterator iterator = getRegisteredViews().values().iterator(); while (iterator.hasNext()) { - final RootView view = iterator.next(); + final View view = iterator.next(); try { view.closeForEveryone(); } catch (final RuntimeException ignored) { @@ -188,24 +158,21 @@ private void tryEnableMetrics() { } } - @SuppressWarnings({"rawtypes", "unchecked", "CallToPrintStackTrace"}) + @SuppressWarnings("CallToPrintStackTrace") private void initializeViews() { - for (final Map.Entry entry : getRegisteredViews().entrySet()) { - final RootView rootView = entry.getValue(); - if (!(rootView instanceof PlatformView)) - throw new IllegalStateException("Only PlatformView can be registered on this view frame"); + for (final Map.Entry entry : getRegisteredViews().entrySet()) { + final View view = entry.getValue(); - final PlatformView platformView = (PlatformView) rootView; try { - platformView.internalInitialization(this); - platformView.setInitialized(true); + view.internalInitialization(this); + view.setInitialized(true); } catch (final RuntimeException exception) { - platformView.setInitialized(false); + view.setInitialized(false); getOwner() .getLogger() .severe(String.format( "An error occurred while enabling view %s: %s", - rootView.getClass().getName(), exception)); + view.getClass().getName(), exception)); exception.printStackTrace(); } } @@ -230,14 +197,14 @@ private void checkRelocationIssues() { } } - /** - * This is an internal inventory-framework API that should not be used from outside of - * this library. No compatibility guarantees are provided. - */ - @ApiStatus.Internal - public Viewer getViewer(@NotNull Player player) { - return viewerByPlayerUuid.get(player.getUniqueId().toString()); - } + /** + * This is an internal inventory-framework API that should not be used from outside of + * this library. No compatibility guarantees are provided. + */ + @ApiStatus.Internal + public Viewer getViewer(@NotNull Player player) { + return viewerByPlayerUuid.get(player.getUniqueId().toString()); + } /** * Creates a new ViewFrame. diff --git a/inventory-framework-platform-bukkit/src/main/java/me/devnatan/inventoryframework/context/CloseContext.java b/inventory-framework-platform-bukkit/src/main/java/me/devnatan/inventoryframework/context/CloseContext.java index f9339d8f..6fa06974 100644 --- a/inventory-framework-platform-bukkit/src/main/java/me/devnatan/inventoryframework/context/CloseContext.java +++ b/inventory-framework-platform-bukkit/src/main/java/me/devnatan/inventoryframework/context/CloseContext.java @@ -1,65 +1,35 @@ package me.devnatan.inventoryframework.context; -import java.util.List; -import java.util.Map; -import java.util.Objects; import java.util.UUID; -import java.util.stream.Collectors; -import me.devnatan.inventoryframework.BukkitViewContainer; import me.devnatan.inventoryframework.BukkitViewer; -import me.devnatan.inventoryframework.RootView; +import me.devnatan.inventoryframework.View; import me.devnatan.inventoryframework.ViewConfig; import me.devnatan.inventoryframework.ViewContainer; import me.devnatan.inventoryframework.Viewer; import org.bukkit.entity.Player; +import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.UnmodifiableView; -public final class CloseContext extends ConfinedContext implements IFCloseContext, Context { +public class CloseContext extends PlatformConfinedContext implements IFCloseContext { - private final IFContext parent; + private final Viewer subject; private final Player player; + private final IFRenderContext parent; + private boolean cancelled; - public CloseContext( - @NotNull RootView root, - @NotNull ViewContainer container, - Viewer subject, - @NotNull Map viewers, - @NotNull IFContext parent) { - super(root, container, subject, viewers, parent.getInitialData()); + @ApiStatus.Internal + public CloseContext(@NotNull Viewer subject, @NotNull IFRenderContext parent) { + this.subject = subject; this.player = ((BukkitViewer) subject).getPlayer(); this.parent = parent; } - public IFContext getParent() { - return parent; - } - - @NotNull - @Override - public Player getPlayer() { + // TODO Needs documentation + public final @NotNull Player getPlayer() { return player; } - @Override - public @UnmodifiableView List getAllPlayers() { - return getViewers().stream() - .map(viewer -> (BukkitViewer) viewer) - .map(BukkitViewer::getPlayer) - .collect(Collectors.toList()); - } - - @Override - public void updateTitleForPlayer(@NotNull String title, @NotNull Player player) { - ((BukkitViewContainer) getContainer()).changeTitle(title, player); - } - - @Override - public void resetTitleForPlayer(@NotNull Player player) { - ((BukkitViewContainer) getContainer()).changeTitle(null, player); - } - @Override public boolean isCancelled() { return cancelled; @@ -71,43 +41,37 @@ public void setCancelled(boolean cancelled) { } @Override - public @NotNull UUID getId() { - return getParent().getId(); + public final @NotNull Viewer getViewer() { + return subject; } @Override - public @NotNull ViewConfig getConfig() { - return getParent().getConfig(); + public final RenderContext getParent() { + return (RenderContext) parent; } @Override - public void closeForEveryone() {} + public final @NotNull UUID getId() { + return getParent().getId(); + } @Override - public void closeForPlayer() {} + public final @NotNull ViewConfig getConfig() { + return getParent().getConfig(); + } @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - if (!super.equals(o)) return false; - CloseContext that = (CloseContext) o; - return isCancelled() == that.isCancelled() - && Objects.equals(getParent(), that.getParent()) - && Objects.equals(getPlayer(), that.getPlayer()); + public final ViewContainer getContainer() { + return getParent().getContainer(); } @Override - public int hashCode() { - return Objects.hash(super.hashCode(), getParent(), getPlayer(), isCancelled()); + public final @NotNull View getRoot() { + return getParent().getRoot(); } @Override - public String toString() { - return "CloseContext{" + "parent=" - + parent + ", player=" - + player + ", cancelled=" - + cancelled + "} " - + super.toString(); + public final Object getInitialData() { + return getParent().getInitialData(); } } diff --git a/inventory-framework-platform-bukkit/src/main/java/me/devnatan/inventoryframework/context/Context.java b/inventory-framework-platform-bukkit/src/main/java/me/devnatan/inventoryframework/context/Context.java index 2bbedc26..a89115d4 100644 --- a/inventory-framework-platform-bukkit/src/main/java/me/devnatan/inventoryframework/context/Context.java +++ b/inventory-framework-platform-bukkit/src/main/java/me/devnatan/inventoryframework/context/Context.java @@ -4,7 +4,6 @@ import org.bukkit.entity.Player; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.UnmodifiableView; public interface Context extends IFContext { @@ -16,15 +15,14 @@ public interface Context extends IFContext { * * @return A player in this interaction context. */ - @NotNull - Player getPlayer(); + // @UnknownNullability + // Player getPlayer(); /** *

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 - @UnmodifiableView List getAllPlayers(); /** diff --git a/inventory-framework-platform-bukkit/src/main/java/me/devnatan/inventoryframework/context/OpenContext.java b/inventory-framework-platform-bukkit/src/main/java/me/devnatan/inventoryframework/context/OpenContext.java index 95705e44..feebd8b9 100644 --- a/inventory-framework-platform-bukkit/src/main/java/me/devnatan/inventoryframework/context/OpenContext.java +++ b/inventory-framework-platform-bukkit/src/main/java/me/devnatan/inventoryframework/context/OpenContext.java @@ -1,163 +1,142 @@ package me.devnatan.inventoryframework.context; -import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.UUID; import java.util.concurrent.CompletableFuture; -import java.util.stream.Collectors; import me.devnatan.inventoryframework.BukkitViewer; import me.devnatan.inventoryframework.InventoryFrameworkException; -import me.devnatan.inventoryframework.RootView; +import me.devnatan.inventoryframework.UnsupportedOperationInSharedContextException; +import me.devnatan.inventoryframework.View; import me.devnatan.inventoryframework.ViewConfig; import me.devnatan.inventoryframework.ViewConfigBuilder; +import me.devnatan.inventoryframework.ViewContainer; import me.devnatan.inventoryframework.Viewer; import org.bukkit.entity.Player; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.UnmodifiableView; +import org.jetbrains.annotations.Nullable; -public class OpenContext extends ConfinedContext implements IFOpenContext, Context { +public class OpenContext extends PlatformContext implements IFOpenContext { - private final Player player; - private boolean cancelled; + // --- Inherited --- + private final UUID id; + private final View root; + private final Viewer subject; + private final Object initialData; + private final Map viewers; + + // --- User Provided --- private CompletableFuture waitTask; private ViewConfigBuilder inheritedConfigBuilder; + // --- Properties --- + private final Player player; + private boolean cancelled = false; + + /** + * Creates a new open context instance. + *

+ * This is an internal inventory-framework API that should not be used from outside of + * this library. No compatibility guarantees are provided. + * + * @param root Root view that will be owner of the upcoming render context. + * @param subject The viewer that is opening the view. + * @param viewers Who'll be the viewers of this context, if this parameter is provided it + * means that this context is a shared context. + * Must be provided even in non-shared context cases. + * @param initialData Initial data provided by the user. + */ @ApiStatus.Internal public OpenContext( - @NotNull RootView root, Viewer subject, @NotNull Map viewers, Object initialData) { - super(root, null, subject, viewers, initialData); - this.player = subject != null ? ((BukkitViewer) subject).getPlayer() : null; + @NotNull View root, @Nullable Viewer subject, @NotNull Map viewers, Object initialData) { + this.id = UUID.randomUUID(); + this.subject = subject; + this.root = root; + this.viewers = viewers; + this.initialData = initialData; + this.player = subject == null ? null : ((BukkitViewer) subject).getPlayer(); + } + + /** + * The player that's currently opening the view. + * + * @return The player that is opening the view. + * @throws UnsupportedOperationInSharedContextException If this context {@link #isShared() is shared}. + */ + public final @NotNull Player getPlayer() { + tryThrowDoNotWorkWithSharedContext("getAllPlayers()"); + return player; } @Override - public boolean isCancelled() { + public final boolean isCancelled() { return cancelled; } @Override - public void setCancelled(boolean cancelled) { + public final void setCancelled(boolean cancelled) { this.cancelled = cancelled; } - private ViewConfigBuilder getInheritedConfigBuilder() { - return inheritedConfigBuilder; - } - - public void setInheritedConfigBuilder(ViewConfigBuilder inheritedConfigBuilder) { - this.inheritedConfigBuilder = inheritedConfigBuilder; - } - @Override - public void closeForEveryone() { - unsupportedOperation("#setCancelled(true)"); - } - - @Override - public void closeForPlayer() { - unsupportedOperation("#setCancelled(true)"); - } - - @Override - public void openForPlayer(@NotNull Class other) { - unsupportedOperation(); - } - - @Override - public void openForPlayer(@NotNull Class other, Object initialData) { - unsupportedOperation(); - } - - @Override - public void resetTitleForPlayer() { - unsupportedOperation(); + public final CompletableFuture getAsyncOpenJob() { + return waitTask; } @Override - public void updateTitleForPlayer(@NotNull String title) { - unsupportedOperation(); + public final @NotNull View getRoot() { + return root; } @Override - public void updateTitleForPlayer(@NotNull String title, @NotNull Player player) { - unsupportedOperation(); + public final @NotNull Map getIndexedViewers() { + return viewers; } @Override - public void resetTitleForPlayer(@NotNull Player player) { - unsupportedOperation(); + public final @NotNull UUID getId() { + return id; } @Override - public CompletableFuture getAsyncOpenJob() { - return waitTask; + public final Object getInitialData() { + return initialData; } @Override - public @NotNull Player getPlayer() { - return player; + public final Viewer getSubject() { + return subject; } @Override - public @UnmodifiableView List getAllPlayers() { - return getViewers().stream() - .map(viewer -> (BukkitViewer) viewer) - .map(BukkitViewer::getPlayer) - .collect(Collectors.toList()); + public final ViewContainer getContainer() { + throw new InventoryFrameworkException("Container is not available in open phase"); } @Override - public void waitUntil(@NotNull CompletableFuture task) { + public final void waitUntil(@NotNull CompletableFuture task) { this.waitTask = task; } @Override - public @NotNull ViewConfig getConfig() { + public final @NotNull ViewConfig getConfig() { return inheritedConfigBuilder == null - ? super.getConfig() - : inheritedConfigBuilder.build().merge(getRoot().getConfig()); + ? getRoot().getConfig() + : Objects.requireNonNull(getModifiedConfig(), "Modified config cannot be null"); } @Override - public @NotNull ViewConfigBuilder modifyConfig() { - if (inheritedConfigBuilder == null) inheritedConfigBuilder = new ViewConfigBuilder(); - - return inheritedConfigBuilder; - } + public final ViewConfig getModifiedConfig() { + if (inheritedConfigBuilder == null) return null; - private void unsupportedOperation() { - throw new InventoryFrameworkException( - new IllegalStateException("This operation cannot be called on open handler.")); - } - - private void unsupportedOperation(String usage) { - throw new InventoryFrameworkException(new IllegalStateException( - String.format("This operation cannot be called on open handler. Use %s instead.", usage))); - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - if (!super.equals(o)) return false; - OpenContext that = (OpenContext) o; - return isCancelled() == that.isCancelled() - && Objects.equals(getPlayer(), that.getPlayer()) - && Objects.equals(getInheritedConfigBuilder(), that.getInheritedConfigBuilder()); + return inheritedConfigBuilder.build().merge(getRoot().getConfig()); } @Override - public int hashCode() { - return Objects.hash(super.hashCode(), getPlayer(), isCancelled(), getInheritedConfigBuilder()); - } + public final @NotNull ViewConfigBuilder modifyConfig() { + if (inheritedConfigBuilder == null) inheritedConfigBuilder = new ViewConfigBuilder(); - @Override - public String toString() { - return "OpenContext{" + "player=" - + player + ", cancelled=" - + cancelled + ", waitTask=" - + waitTask + ", inheritedConfigBuilder=" - + inheritedConfigBuilder + "} " - + super.toString(); + return inheritedConfigBuilder; } } diff --git a/inventory-framework-platform-bukkit/src/main/java/me/devnatan/inventoryframework/context/RenderContext.java b/inventory-framework-platform-bukkit/src/main/java/me/devnatan/inventoryframework/context/RenderContext.java index 732e2d0e..8b800db2 100644 --- a/inventory-framework-platform-bukkit/src/main/java/me/devnatan/inventoryframework/context/RenderContext.java +++ b/inventory-framework-platform-bukkit/src/main/java/me/devnatan/inventoryframework/context/RenderContext.java @@ -7,7 +7,7 @@ import java.util.stream.Collectors; import me.devnatan.inventoryframework.BukkitViewContainer; import me.devnatan.inventoryframework.BukkitViewer; -import me.devnatan.inventoryframework.RootView; +import me.devnatan.inventoryframework.View; import me.devnatan.inventoryframework.ViewConfig; import me.devnatan.inventoryframework.ViewContainer; import me.devnatan.inventoryframework.Viewer; @@ -18,31 +18,40 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -public final class RenderContext extends PlatformRenderContext implements Context { +public class RenderContext extends PlatformRenderContext implements Context { private final Player player; + /** + * This is an internal inventory-framework API that should not be used from outside of + * this library. No compatibility guarantees are provided. + */ @ApiStatus.Internal public RenderContext( - UUID id, - RootView root, - ViewContainer container, + @NotNull UUID id, + @NotNull View root, + @NotNull ViewConfig config, + @NotNull ViewContainer container, + @NotNull Map viewers, Viewer subject, - Map viewers, - ViewConfig config, Object initialData) { - super(id, root, container, subject, viewers, config, initialData); + super(id, root, config, container, viewers, subject, initialData); this.player = subject != null ? ((BukkitViewer) subject).getPlayer() : null; } - @NotNull @Override - public Player getPlayer() { + public final @NotNull View getRoot() { + return (View) root; + } + + // TODO documentation + public final @NotNull Player getPlayer() { + tryThrowDoNotWorkWithSharedContext("getAllPlayers"); return player; } @Override - public List getAllPlayers() { + public final List getAllPlayers() { return getViewers().stream() .map(viewer -> (BukkitViewer) viewer) .map(BukkitViewer::getPlayer) diff --git a/inventory-framework-platform-bukkit/src/main/java/me/devnatan/inventoryframework/context/SlotClickContext.java b/inventory-framework-platform-bukkit/src/main/java/me/devnatan/inventoryframework/context/SlotClickContext.java index 07af0bb6..6f80ed0c 100644 --- a/inventory-framework-platform-bukkit/src/main/java/me/devnatan/inventoryframework/context/SlotClickContext.java +++ b/inventory-framework-platform-bukkit/src/main/java/me/devnatan/inventoryframework/context/SlotClickContext.java @@ -1,6 +1,5 @@ package me.devnatan.inventoryframework.context; -import java.util.Map; import me.devnatan.inventoryframework.RootView; import me.devnatan.inventoryframework.ViewContainer; import me.devnatan.inventoryframework.Viewer; @@ -11,40 +10,38 @@ import org.bukkit.event.inventory.InventoryType; import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.PlayerInventory; +import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -public final class SlotClickContext extends SlotContext implements IFSlotClickContext { +public class SlotClickContext extends SlotContext implements IFSlotClickContext { + private final Viewer whoClicked; + private final ViewContainer clickedContainer; + private final Component clickedComponent; private final InventoryClickEvent clickOrigin; private boolean cancelled; + @ApiStatus.Internal public SlotClickContext( - @NotNull RootView root, - @NotNull ViewContainer container, - Viewer subject, - @NotNull Map viewers, int slot, - @NotNull IFContext parent, - @Nullable Component component, + @NotNull IFRenderContext parent, + @NotNull Viewer whoClicked, + @NotNull ViewContainer clickedContainer, + @Nullable Component clickedComponent, @NotNull InventoryClickEvent clickOrigin) { - super(root, container, subject, viewers, slot, parent, component); + super(slot, parent); + this.whoClicked = whoClicked; + this.clickedContainer = clickedContainer; + this.clickedComponent = clickedComponent; this.clickOrigin = clickOrigin; } - @Override - public boolean isCancelled() { - return cancelled; - } - - @Override - public void setCancelled(boolean cancelled) { - this.cancelled = cancelled; - } - - @Override - public int getClickedSlot() { - return clickOrigin.getRawSlot(); + /** + * The player who clicked on the slot. + */ + public final @NotNull Player getPlayer() { + return (Player) clickOrigin.getWhoClicked(); } /** @@ -59,67 +56,106 @@ public InventoryClickEvent getClickOrigin() { } /** - * The player who clicked on the slot. + * The item that was clicked. */ @Override - public @NotNull Player getPlayer() { - return (Player) clickOrigin.getWhoClicked(); + public final ItemStack getItem() { + return clickOrigin.getCurrentItem(); } - /** - * The item that was clicked. - */ @Override - public ItemStack getItem() { - return clickOrigin.getCurrentItem(); + public final Component getComponent() { + return clickedComponent; + } + + @Override + public final @NotNull ViewContainer getClickedContainer() { + return clickedContainer; + } + + @Override + public final boolean isCancelled() { + return cancelled; + } + + @Override + public final void setCancelled(boolean cancelled) { + this.cancelled = cancelled; } @Override - public boolean isLeftClick() { + public final int getClickedSlot() { + return clickOrigin.getRawSlot(); + } + + @Override + public final boolean isLeftClick() { return getClickOrigin().isLeftClick(); } @Override - public boolean isRightClick() { + public final boolean isRightClick() { return getClickOrigin().isRightClick(); } @Override - public boolean isMiddleClick() { + public final boolean isMiddleClick() { return getClickOrigin().getClick() == ClickType.MIDDLE; } @Override - public boolean isShiftClick() { + public final boolean isShiftClick() { return getClickOrigin().isShiftClick(); } @Override - public boolean isKeyboardClick() { + public final boolean isKeyboardClick() { return getClickOrigin().getClick().isKeyboardClick(); } @Override - public boolean isOutsideClick() { + public final boolean isOutsideClick() { return getClickOrigin().getSlotType() == InventoryType.SlotType.OUTSIDE; } @Override @NotNull - public String getClickIdentifier() { + public final String getClickIdentifier() { return getClickOrigin().getClick().name(); } @Override - public boolean isOnEntityContainer() { + public final boolean isOnEntityContainer() { return getClickOrigin().getClickedInventory() instanceof PlayerInventory; } @Override - public String toString() { - return "SlotClickContext{" + "clickOrigin=" - + clickOrigin + ", cancelled=" - + cancelled + "} " - + super.toString(); + public final Viewer getViewer() { + return whoClicked; + } + + @Override + public final void closeForPlayer() { + getParent().closeForPlayer(); + } + + @Override + public final void openForPlayer(@NotNull Class other) { + getParent().openForPlayer(other); + } + + @Override + public final void openForPlayer(@NotNull Class other, Object initialData) { + getParent().openForPlayer(other, initialData); + } + + @Override + public final void updateTitleForPlayer(@NotNull String title) { + getParent().updateTitleForPlayer(title); + } + + @Override + public final void resetTitleForPlayer() { + getParent().resetTitleForPlayer(); } } diff --git a/inventory-framework-platform-bukkit/src/main/java/me/devnatan/inventoryframework/context/SlotContext.java b/inventory-framework-platform-bukkit/src/main/java/me/devnatan/inventoryframework/context/SlotContext.java index 56923a19..a8806a99 100644 --- a/inventory-framework-platform-bukkit/src/main/java/me/devnatan/inventoryframework/context/SlotContext.java +++ b/inventory-framework-platform-bukkit/src/main/java/me/devnatan/inventoryframework/context/SlotContext.java @@ -3,10 +3,7 @@ import java.util.List; import java.util.Map; import java.util.UUID; -import java.util.stream.Collectors; -import me.devnatan.inventoryframework.BukkitViewContainer; -import me.devnatan.inventoryframework.BukkitViewer; -import me.devnatan.inventoryframework.RootView; +import me.devnatan.inventoryframework.View; import me.devnatan.inventoryframework.ViewConfig; import me.devnatan.inventoryframework.ViewContainer; import me.devnatan.inventoryframework.Viewer; @@ -17,54 +14,26 @@ import org.bukkit.entity.Player; import org.bukkit.inventory.ItemStack; import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.UnmodifiableView; -public class SlotContext extends ConfinedContext implements IFSlotContext, Context { +public abstract class SlotContext extends PlatformContext implements IFSlotContext, Context { + // --- Inherited --- + private final IFRenderContext parent; + + // --- Properties --- private int slot; - private final Player player; - private final IFContext parent; - private final Component component; - - public SlotContext( - @NotNull RootView root, - @NotNull ViewContainer container, - Viewer subject, - @NotNull Map viewers, - int slot, - @NotNull IFContext parent, - @Nullable Component component) { - super(root, container, subject, viewers, parent.getInitialData()); + + protected SlotContext(int slot, @NotNull IFRenderContext parent) { this.slot = slot; - this.player = subject == null ? null : ((BukkitViewer) subject).getPlayer(); this.parent = parent; - this.component = component; } - @Override - public void update() { - getParent().update(); - } - - @Override - public @NotNull ViewContainer getContainer() { - return getParent().getContainer(); - } + public abstract ItemStack getItem(); @Override - public @NotNull @UnmodifiableView Map getIndexedViewers() { - return getParent().getIndexedViewers(); - } - - @Override - public final @NotNull UUID getId() { - return getParent().getId(); - } - - @Override - public final @NotNull ViewConfig getConfig() { - return getParent().getConfig(); + public final @NotNull RenderContext getParent() { + return (RenderContext) parent; } @Override @@ -73,130 +42,118 @@ public final int getSlot() { } @Override - public void setSlot(int slot) { + public final void setSlot(int slot) { this.slot = slot; } @Override - public boolean isOnEntityContainer() { - return getContainer().isEntityContainer(); - } - - @Override - public boolean hasChanged() { + public final void clear() { + // TODO do something throw new UnsupportedOperationException(); } @Override - public void setChanged(boolean changed) { - throw new UnsupportedOperationException(); + public final @NotNull Map getIndexedViewers() { + return getParent().getIndexedViewers(); } @Override - public boolean isRegistered() { - return false; + public final @NotNull String getTitle() { + return getParent().getTitle(); } @Override - public final @NotNull IFContext getParent() { - return parent; + public final @UnmodifiableView @NotNull List getComponents() { + return getParent().getComponents(); } @Override - public void clear() { - throw new UnsupportedOperationException(); + public final Component getComponent(int position) { + return getParent().getComponent(position); } - @Nullable @Override - public Component getComponent() { - return component; + public final void addComponent(@NotNull Component component) { + getParent().addComponent(component); } @Override - public @NotNull Player getPlayer() { - return player; + public final void removeComponent(@NotNull Component component) { + getParent().removeComponent(component); } @Override - public @UnmodifiableView List getAllPlayers() { - return getViewers().stream() - .map(viewer -> (BukkitViewer) viewer) - .map(BukkitViewer::getPlayer) - .collect(Collectors.toList()); + public final void renderComponent(@NotNull Component component) { + getParent().renderComponent(component); } @Override - public void updateTitleForPlayer(@NotNull String title, @NotNull Player player) { - ((BukkitViewContainer) getContainer()).changeTitle(title, player); + public final void updateComponent(@NotNull Component component) { + getParent().updateComponent(component); } @Override - public void resetTitleForPlayer(@NotNull Player player) { - ((BukkitViewContainer) getContainer()).changeTitle(null, player); + public final void update() { + getParent().update(); } - public ItemStack getItem() { - return ((BukkitViewContainer) getContainer()).getInventory().getItem(getSlot()); + @Override + public final Object getState(State state) { + return getParent().getState(state); } @Override - public final @UnmodifiableView @NotNull List getComponents() { - return getParent().getComponents(); + public final void initializeState(long id, @NotNull StateValue value) { + getParent().initializeState(id, value); } @Override - public final void addComponent(@NotNull Component component) { - throw new UnsupportedOperationException("Slot context do not have components"); + public final void updateState(long id, Object value) { + getParent().updateState(id, value); } @Override - public final void removeComponent(@NotNull Component component) { - throw new UnsupportedOperationException("Slot context do not have components"); + public final void watchState(long id, StateWatcher listener) { + getParent().watchState(id, listener); } @Override - public void renderComponent(@NotNull Component component) { - throw new UnsupportedOperationException("Slot context do not have components"); + public final @NotNull UUID getId() { + return getParent().getId(); } @Override - public void updateComponent(@NotNull Component component) { - throw new UnsupportedOperationException("Slot context do not have components"); + public final @NotNull ViewConfig getConfig() { + return getParent().getConfig(); } @Override - public boolean isMarkedForRemoval(int componentIndex) { - return getParent().isMarkedForRemoval(componentIndex); + public final ViewContainer getContainer() { + return getParent().getContainer(); } @Override - public Object getState(State state) { - return getParent().getState(state); + public final @NotNull View getRoot() { + return getParent().getRoot(); } @Override - public void initializeState(long id, @NotNull StateValue value) { - getParent().initializeState(id, value); + public final Object getInitialData() { + return getParent().getInitialData(); } @Override - public void updateState(long id, Object value) { - getParent().updateState(id, value); + public List getAllPlayers() { + return getParent().getAllPlayers(); } @Override - public void watchState(long id, StateWatcher listener) { - getParent().watchState(id, listener); + public void updateTitleForPlayer(@NotNull String title, @NotNull Player player) { + getParent().updateTitleForPlayer(title, player); } @Override - public String toString() { - return "SlotContext{" + "slot=" - + slot + ", player=" - + player + ", parent=" - + parent + ", component=" - + component + "} " - + super.toString(); + public void resetTitleForPlayer(@NotNull Player player) { + getParent().resetTitleForPlayer(player); } } 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 06a4eb38..dc58ee3a 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 @@ -1,56 +1,92 @@ package me.devnatan.inventoryframework.context; -import java.util.Map; import me.devnatan.inventoryframework.RootView; -import me.devnatan.inventoryframework.ViewContainer; import me.devnatan.inventoryframework.Viewer; -import me.devnatan.inventoryframework.component.Component; import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -public final class SlotRenderContext extends SlotContext implements IFSlotRenderContext { +public class SlotRenderContext extends SlotContext implements IFSlotRenderContext { + + private final Viewer viewer; private ItemStack item; private boolean cancelled; + private boolean changed; - public SlotRenderContext( - @NotNull RootView root, - @NotNull ViewContainer container, - Viewer subject, - @NotNull Map viewers, - int slot, - @NotNull IFContext parent, - @Nullable Component component) { - super(root, container, subject, viewers, slot, parent, component); + @ApiStatus.Internal + public SlotRenderContext(int slot, @NotNull IFRenderContext parent, @Nullable Viewer viewer) { + super(slot, parent); + this.viewer = viewer; } @Override - public Object getResult() { + public final Object getResult() { return item; } @Override - public ItemStack getItem() { + public final ItemStack getItem() { return item; } - public void setItem(ItemStack item) { + public final void setItem(ItemStack item) { this.item = item; } @Override - public boolean isCancelled() { + public final boolean isCancelled() { return cancelled; } @Override - public void setCancelled(boolean cancelled) { + public final void setCancelled(boolean cancelled) { this.cancelled = cancelled; } @Override - public String toString() { - return "SlotRenderContext{" + "item=" + item + ", cancelled=" + cancelled + "} " + super.toString(); + public final boolean hasChanged() { + return changed; + } + + @Override + public final void setChanged(boolean changed) { + this.changed = changed; + } + + @Override + public final boolean isOnEntityContainer() { + return getContainer().isEntityContainer(); + } + + @Override + public final Viewer getViewer() { + return viewer; + } + + @Override + public final void closeForPlayer() { + getParent().closeForPlayer(); + } + + @Override + public final void openForPlayer(@NotNull Class other) { + getParent().openForPlayer(other); + } + + @Override + public final void openForPlayer(@NotNull Class other, Object initialData) { + getParent().openForPlayer(other, initialData); + } + + @Override + public final void updateTitleForPlayer(@NotNull String title) { + getParent().updateTitleForPlayer(title); + } + + @Override + public final void resetTitleForPlayer() { + getParent().resetTitleForPlayer(); } } 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 ae790c80..f6e4797d 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 @@ -1,11 +1,12 @@ package me.devnatan.inventoryframework.internal; -import static java.util.Objects.requireNonNull; import static me.devnatan.inventoryframework.runtime.util.InventoryUtils.checkInventoryTypeSupport; -import static me.devnatan.inventoryframework.util.IsTypeOf.isTypeOf; +import java.util.List; import java.util.Map; import java.util.UUID; +import java.util.function.Function; +import java.util.stream.Collectors; import me.devnatan.inventoryframework.*; import me.devnatan.inventoryframework.component.BukkitItemComponentBuilder; import me.devnatan.inventoryframework.component.Component; @@ -13,8 +14,8 @@ import me.devnatan.inventoryframework.context.*; import me.devnatan.inventoryframework.logging.Logger; import me.devnatan.inventoryframework.logging.NoopLogger; -import org.bukkit.entity.Entity; import org.bukkit.entity.Player; +import org.bukkit.event.inventory.InventoryClickEvent; import org.bukkit.inventory.Inventory; import org.bukkit.inventory.InventoryHolder; import org.jetbrains.annotations.NotNull; @@ -57,63 +58,56 @@ public class BukkitElementFactory extends ElementFactory { } @Override - public @NotNull Viewer createViewer(Object... parameters) { - final Object playerObject = parameters[0]; - if (!(playerObject instanceof Player)) + public @NotNull Viewer createViewer(@NotNull Object entity, IFRenderContext context) { + if (!(entity instanceof Player)) throw new IllegalArgumentException("createViewer(...) first parameter must be a Player"); - return new BukkitViewer((Player) playerObject); + return new BukkitViewer((Player) entity, context); } @Override - public @NotNull String convertViewer(Object input) { - if (input instanceof String) return UUID.fromString((String) input).toString(); - if (input instanceof UUID) return input.toString(); - if (input instanceof Entity) return ((Entity) input).getUniqueId().toString(); - - throw new IllegalArgumentException("Inconvertible viewer id: " + input); + public IFOpenContext createOpenContext( + @NotNull RootView root, @Nullable Viewer subject, @NotNull List viewers, Object initialData) { + return new OpenContext( + (View) root, + subject, + viewers.stream().collect(Collectors.toMap(Viewer::getId, Function.identity())), + initialData); } - @SuppressWarnings("unchecked") @Override - public @NotNull T createContext( + public IFRenderContext createRenderContext( + @NotNull UUID id, @NotNull RootView root, - ViewContainer container, - Viewer subject, + @NotNull ViewConfig config, + @NotNull ViewContainer container, @NotNull Map viewers, - @NotNull Class kind, - @Nullable IFContext parent, + Viewer subject, Object initialData) { - if (isTypeOf(IFOpenContext.class, kind)) return (T) new OpenContext(root, subject, viewers, initialData); - if (isTypeOf(IFRenderContext.class, kind)) - return (T) new me.devnatan.inventoryframework.context.RenderContext( - requireNonNull(parent).getId(), - root, - container, - subject, - viewers, - requireNonNull(parent).getConfig(), - initialData); - if (isTypeOf(IFCloseContext.class, kind)) - return (T) new CloseContext(root, container, subject, viewers, requireNonNull(parent)); - - throw new UnsupportedOperationException("Unsupported context kind: " + kind); + return new RenderContext(id, (View) root, config, container, viewers, subject, initialData); } - @SuppressWarnings("unchecked") @Override - public @NotNull T createSlotContext( - int slot, - Component component, - @NotNull ViewContainer container, - Viewer subject, - @NotNull Map viewers, - @NotNull IFContext parent, - @NotNull Class kind) { - if (isTypeOf(IFSlotRenderContext.class, kind)) - return (T) new SlotRenderContext(parent.getRoot(), container, subject, viewers, slot, parent, component); + public IFSlotClickContext createSlotClickContext( + int slotClicked, + @NotNull Viewer whoClicked, + @NotNull ViewContainer interactionContainer, + @Nullable Component componentClicked, + @NotNull Object origin) { + final IFRenderContext context = whoClicked.getContext(); + return new SlotClickContext( + slotClicked, context, whoClicked, interactionContainer, componentClicked, (InventoryClickEvent) origin); + } + + @Override + public IFSlotRenderContext createSlotRenderContext( + int slot, @NotNull IFRenderContext parent, @Nullable Viewer viewer) { + return new SlotRenderContext(slot, parent, viewer); + } - return (T) new SlotContext(parent.getRoot(), container, subject, viewers, slot, parent, component); + @Override + public IFCloseContext createCloseContext(@NotNull Viewer viewer, @NotNull IFRenderContext parent) { + return new CloseContext(viewer, parent); } @Override diff --git a/inventory-framework-api/src/main/java/me/devnatan/inventoryframework/IFViewFrame.java b/inventory-framework-platform/src/main/java/me/devnatan/inventoryframework/IFViewFrame.java similarity index 59% rename from inventory-framework-api/src/main/java/me/devnatan/inventoryframework/IFViewFrame.java rename to inventory-framework-platform/src/main/java/me/devnatan/inventoryframework/IFViewFrame.java index e28099cc..129c2522 100644 --- a/inventory-framework-api/src/main/java/me/devnatan/inventoryframework/IFViewFrame.java +++ b/inventory-framework-platform/src/main/java/me/devnatan/inventoryframework/IFViewFrame.java @@ -1,13 +1,16 @@ package me.devnatan.inventoryframework; import java.util.*; +import java.util.stream.Collectors; +import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.UnmodifiableView; -public abstract class IFViewFrame> { +public abstract class IFViewFrame, V extends PlatformView> { private boolean registered; - private final Map registeredViews = new HashMap<>(); + private final Map registeredViews = new HashMap<>(); protected IFViewFrame() {} @@ -16,8 +19,8 @@ protected IFViewFrame() {} * * @return A Map containing all registered views in this view frame. */ - protected final @NotNull Map getRegisteredViews() { - return registeredViews; + protected final @NotNull @UnmodifiableView Map getRegisteredViews() { + return Collections.unmodifiableMap(registeredViews); } /** @@ -27,11 +30,12 @@ protected IFViewFrame() {} * @return An RootView instance of the given type. * @throws IllegalArgumentException If the given cannot be found. */ - protected final @NotNull RootView getRegisteredViewByType(@NotNull Class type) { + public final @NotNull V getRegisteredViewByType(@NotNull Class type) { return getRegisteredViews().values().stream() .filter(view -> view.getClass().equals(type)) .findFirst() - .orElseThrow(() -> new IllegalArgumentException(String.format("Unknown view: %s", type))); + .orElseThrow(() -> + new IllegalArgumentException(String.format("View not found or not registered: %s", type))); } /** @@ -42,9 +46,9 @@ protected IFViewFrame() {} */ @SuppressWarnings("unchecked") @Contract("_ -> this") - public final S with(RootView... views) { + public final S with(V... views) { synchronized (registeredViews) { - for (final RootView view : views) { + for (final V view : views) { if (registeredViews.containsKey(view.getUniqueId())) throw new IllegalStateException(String.format( "View %s already registered. Maybe your are using #register() before #with(...).", @@ -61,9 +65,9 @@ public final S with(RootView... views) { * * @param views The views that'll be removed. */ - public final void remove(RootView... views) { + public final void remove(V... views) { synchronized (registeredViews) { - for (final RootView view : views) { + for (final V view : views) { view.closeForEveryone(); registeredViews.remove(view.getUniqueId()); } @@ -83,17 +87,6 @@ public final void remove(RootView... views) { */ public abstract void unregister(); - /** - * Opens a view to a {@link Viewer}. - * - * @param viewClass The target view to be open. - * @param viewers The viewers that the view will be open to. They will share the same context. - * @param initialData The initial data. - * @throws IllegalStateException If this view is not registered in this view frame. - */ - public abstract void open( - @NotNull Class viewClass, @NotNull Iterable viewers, Object initialData); - /** * If this view frame is registered. * @@ -113,16 +106,25 @@ protected final void setRegistered(boolean registered) { } /** - * Throws an {@link IllegalStateException} if this framework is already registered when trying - * to install a feature. + * Opens a view to more than one player with initial data. + *

+ * These players will see the same inventory and share the same context. + * + *

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. * - * @param featureName The feature name. - * @param featureAccessName The feature installation constant name. + * @param viewClass The target view to be opened. + * @param players The players that the view will be open to. + * @param initialData The initial data. */ - public final void checkNotRegisteredForFeatureInstall(String featureName, String featureAccessName) { - if (!isRegistered()) return; - throw new IllegalStateException(String.format( - "Framework must be unregistered to install %s feature. Call #install(%s) before #register().", - featureName, featureAccessName)); + @ApiStatus.Experimental + protected final void internalOpen( + @NotNull Class viewClass, @NotNull Collection players, Object initialData) { + final V view = getRegisteredViewByType(viewClass); + final List viewers = players.stream() + .map(player -> view.getElementFactory().createViewer(player, null)) + .collect(Collectors.toList()); + + view.open(viewers, initialData); } } diff --git a/inventory-framework-platform/src/main/java/me/devnatan/inventoryframework/PaginationStateBuilder.java b/inventory-framework-platform/src/main/java/me/devnatan/inventoryframework/PaginationStateBuilder.java index 8c30648c..58064eb2 100644 --- a/inventory-framework-platform/src/main/java/me/devnatan/inventoryframework/PaginationStateBuilder.java +++ b/inventory-framework-platform/src/main/java/me/devnatan/inventoryframework/PaginationStateBuilder.java @@ -11,40 +11,24 @@ import me.devnatan.inventoryframework.state.State; import org.jetbrains.annotations.NotNull; +@SuppressWarnings("rawtypes") public final class PaginationStateBuilder< - C extends IFContext, SC extends IFSlotContext, B extends ItemComponentBuilder & ComponentFactory, V> { + Context extends IFContext, + SlotContext extends IFSlotContext, + Builder extends ItemComponentBuilder & ComponentFactory, + V> { - private final PlatformView root; + private final PlatformView root; private final Object sourceProvider; private char layoutTarget = LayoutSlot.FILLED_RESERVED_CHAR; - private PaginationElementFactory elementFactory; - private BiConsumer pageSwitchHandler; + private PaginationElementFactory elementFactory; + private BiConsumer pageSwitchHandler; - PaginationStateBuilder(PlatformView root, Object sourceProvider) { + PaginationStateBuilder(PlatformView root, Object sourceProvider) { this.root = root; this.sourceProvider = sourceProvider; } - PlatformView getRoot() { - return root; - } - - Object getSourceProvider() { - return sourceProvider; - } - - char getLayoutTarget() { - return layoutTarget; - } - - PaginationElementFactory getElementFactory() { - return elementFactory; - } - - BiConsumer getPageSwitchHandler() { - return pageSwitchHandler; - } - /** * Sets the element factory for pagination. *

@@ -57,7 +41,8 @@ BiConsumer getPageSwitchHandler() { * @param elementFactory The element factory. * @return This pagination state builder. */ - public PaginationStateBuilder elementFactory(@NotNull PaginationElementFactory elementFactory) { + public PaginationStateBuilder elementFactory( + @NotNull PaginationElementFactory elementFactory) { this.elementFactory = elementFactory; return this; } @@ -74,10 +59,11 @@ public PaginationStateBuilder elementFactory(@NotNull PaginationEle * @param itemFactory The item factory. * @return This pagination state builder. */ - public PaginationStateBuilder itemFactory(@NotNull BiConsumer itemFactory) { + public PaginationStateBuilder itemFactory( + @NotNull BiConsumer itemFactory) { return elementFactory((context, index, slot, value) -> { @SuppressWarnings("unchecked") - B builder = (B) root.getElementFactory().createComponentBuilder(context); + Builder builder = (Builder) root.getElementFactory().createComponentBuilder(context); builder.withSlot(slot).withExternallyManaged(true); itemFactory.accept(builder, value); return builder; @@ -96,7 +82,7 @@ public PaginationStateBuilder itemFactory(@NotNull BiConsumer * @param layoutTarget The target layout character. * @return This pagination state builder. */ - public PaginationStateBuilder layoutTarget(char layoutTarget) { + public PaginationStateBuilder layoutTarget(char layoutTarget) { this.layoutTarget = layoutTarget; return this; } @@ -110,7 +96,8 @@ public PaginationStateBuilder layoutTarget(char layoutTarget) { * @param pageSwitchHandler The page switch handler. * @return This pagination state builder. */ - public PaginationStateBuilder onPageSwitch(@NotNull BiConsumer pageSwitchHandler) { + public PaginationStateBuilder onPageSwitch( + @NotNull BiConsumer pageSwitchHandler) { this.pageSwitchHandler = pageSwitchHandler; return this; } @@ -129,4 +116,24 @@ public State build() { return root.buildPaginationState(this); } + + PlatformView getRoot() { + return root; + } + + Object getSourceProvider() { + return sourceProvider; + } + + char getLayoutTarget() { + return layoutTarget; + } + + PaginationElementFactory getElementFactory() { + return elementFactory; + } + + BiConsumer getPageSwitchHandler() { + return pageSwitchHandler; + } } diff --git a/inventory-framework-platform/src/main/java/me/devnatan/inventoryframework/PlatformView.java b/inventory-framework-platform/src/main/java/me/devnatan/inventoryframework/PlatformView.java index df725c1b..8fa6cf1e 100644 --- a/inventory-framework-platform/src/main/java/me/devnatan/inventoryframework/PlatformView.java +++ b/inventory-framework-platform/src/main/java/me/devnatan/inventoryframework/PlatformView.java @@ -5,7 +5,6 @@ import java.util.function.BiConsumer; import java.util.function.Function; import java.util.function.Supplier; -import java.util.stream.Collectors; import me.devnatan.inventoryframework.component.ComponentFactory; import me.devnatan.inventoryframework.component.ItemComponentBuilder; import me.devnatan.inventoryframework.component.Pagination; @@ -52,6 +51,7 @@ import org.jetbrains.annotations.NotNull; public abstract class PlatformView< + TFramework extends IFViewFrame, TItem extends ItemComponentBuilder & ComponentFactory, TContext extends IFContext, TOpenContext extends IFOpenContext, @@ -61,9 +61,66 @@ public abstract class PlatformView< TSlotClickContext extends IFSlotClickContext> extends DefaultRootView { - private IFViewFrame framework; + private TFramework framework; private boolean initialized; + /** + *

This is an internal inventory-framework API that should not be used from outside of + * this library. No compatibility guarantees are provided. + */ + @ApiStatus.Internal + public final TFramework getFramework() { + return framework; + } + + /** + * The initialization state of this view. + * + * @return If this view was initialized. + */ + final boolean isInitialized() { + return initialized; + } + + /** + * Sets the initialization state of this view. + * + * @param initialized The new initialization state. + */ + final void setInitialized(boolean initialized) { + this.initialized = initialized; + } + + /** + * Throws an exception if this view is already initialized. + * + * @throws IllegalStateException if this view is already initialized. + */ + private void requireNotInitialized() { + if (!isInitialized()) return; + throw new IllegalStateException( + "View is already initialized, please move this method call to class constructor or #onInit."); + } + + /** + * Opens this view to one or more viewers. + *

+ * This is an internal inventory-framework API that should not be used from outside of + * this library. No compatibility guarantees are provided. + * + * @param viewers The viewers that'll see this view. + * @param initialData The initial data. + */ + @ApiStatus.Internal + public final void open(@NotNull List viewers, Object initialData) { + if (!isInitialized()) throw new IllegalStateException("Cannot open a uninitialized view"); + + final Viewer subject = viewers.size() == 1 ? viewers.get(0) : null; + final IFOpenContext context = getElementFactory().createOpenContext(this, subject, viewers, initialData); + + getPipeline().execute(StandardPipelinePhases.OPEN, context); + } + /** * Creates a new configuration builder. * @@ -382,11 +439,9 @@ protected final State asyncPaginationState( * @param The pagination data type. * @return A new pagination state builder. */ - @SuppressWarnings("unchecked") protected final PaginationStateBuilder buildPaginationState( @NotNull List sourceProvider) { - return new PaginationStateBuilder<>( - (PlatformView) this, sourceProvider); + return new PaginationStateBuilder<>(this, sourceProvider); } /** @@ -396,11 +451,9 @@ protected final PaginationStateBuilder The pagination data type. * @return A new pagination state builder. */ - @SuppressWarnings("unchecked") protected final PaginationStateBuilder buildPaginationState( @NotNull Supplier> sourceProvider) { - return new PaginationStateBuilder<>( - (PlatformView) this, sourceProvider); + return new PaginationStateBuilder<>(this, sourceProvider); } /** @@ -410,11 +463,9 @@ protected final PaginationStateBuilder The pagination data type. * @return A new pagination state builder. */ - @SuppressWarnings("unchecked") protected final PaginationStateBuilder buildPaginationState( @NotNull Function> sourceProvider) { - return new PaginationStateBuilder<>( - (PlatformView) this, sourceProvider); + return new PaginationStateBuilder<>(this, sourceProvider); } /** @@ -428,11 +479,9 @@ protected final PaginationStateBuilder PaginationStateBuilder buildAsyncPaginationState( @NotNull Function>> sourceProvider) { - return new PaginationStateBuilder<>( - (PlatformView) this, sourceProvider); + return new PaginationStateBuilder<>(this, sourceProvider); } final State buildPaginationState( @@ -529,30 +578,6 @@ public void onClose(TCloseContext close) {} @ApiStatus.OverrideOnly public void onClick(TSlotClickContext click) {} - /** - * Initialization state of this view. - * - * @return If this view was initialized. - */ - final boolean isInitialized() { - return initialized; - } - - /** - * Sets the initialization state of this view. - * - * @param initialized The new initialization state. - */ - final void setInitialized(boolean initialized) { - this.initialized = initialized; - } - - private void requireNotInitialized() { - if (!isInitialized()) return; - throw new IllegalStateException( - "View is already initialized, please move this method call to class construtor or #onInit."); - } - /** * Called internally before the first initialization. *

@@ -560,11 +585,12 @@ private void requireNotInitialized() { * * @throws IllegalStateException If this platform view is already initialized. */ - final void internalInitialization(IFViewFrame framework) { + @SuppressWarnings("unchecked") + final void internalInitialization(IFViewFrame framework) { if (isInitialized()) throw new IllegalStateException("Tried to call internal initialization but view is already initialized"); - this.framework = framework; + this.framework = (TFramework) framework; final Pipeline pipeline = getPipeline(); pipeline.intercept(StandardPipelinePhases.INIT, new PlatformInitInterceptor()); @@ -589,31 +615,4 @@ final void internalInitialization(IFViewFrame framework) { public @NotNull ElementFactory getElementFactory() { return PlatformUtils.getFactory(); } - - @Override - public final void open(@NotNull List viewers, Object initialData) { - if (!isInitialized()) throw new IllegalStateException("Cannot open a uninitialized view"); - - final Viewer subject = viewers.size() == 1 ? viewers.get(0) : null; - final IFOpenContext context = getElementFactory() - .createContext( - this, - null, - subject, - viewers.stream().collect(Collectors.toMap(Viewer::getId, Function.identity())), - IFOpenContext.class, - null, - initialData); - viewers.forEach(context::addViewer); - getPipeline().execute(StandardPipelinePhases.OPEN, context); - } - - /** - *

This is an internal inventory-framework API that should not be used from outside of - * this library. No compatibility guarantees are provided. - */ - @ApiStatus.Internal - public final IFViewFrame getFramework() { - return framework; - } } diff --git a/inventory-framework-platform/src/main/java/me/devnatan/inventoryframework/context/ConfinedContext.java b/inventory-framework-platform/src/main/java/me/devnatan/inventoryframework/context/ConfinedContext.java deleted file mode 100644 index 703d6dc7..00000000 --- a/inventory-framework-platform/src/main/java/me/devnatan/inventoryframework/context/ConfinedContext.java +++ /dev/null @@ -1,74 +0,0 @@ -package me.devnatan.inventoryframework.context; - -import java.util.Collections; -import java.util.Map; -import java.util.Objects; -import me.devnatan.inventoryframework.RootView; -import me.devnatan.inventoryframework.ViewContainer; -import me.devnatan.inventoryframework.Viewer; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -class ConfinedContext extends BaseViewContext implements IFConfinedContext { - - private final Viewer viewer; - - public ConfinedContext( - @NotNull RootView root, - @Nullable ViewContainer container, - Viewer subject, - @NotNull Map viewers, - Object initialData) { - super(root, container, viewers, initialData); - this.viewer = subject; - } - - @NotNull - @Override - public Viewer getViewer() { - return viewer; - } - - @Override - public void closeForPlayer() { - getContainer().close(viewer); - } - - @Override - public void openForPlayer(@NotNull Class other) { - openForPlayer(other, null); - } - - @Override - public void openForPlayer(@NotNull Class other, Object initialData) { - getRoot().getFramework().open(other, Collections.singleton(getViewer()), initialData); - } - - @Override - public void updateTitleForPlayer(@NotNull String title) { - getContainer().changeTitle(title, getViewer()); - } - - @Override - public void resetTitleForPlayer() { - getContainer().changeTitle(null, getViewer()); - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - ConfinedContext that = (ConfinedContext) o; - return Objects.equals(getViewer(), that.getViewer()); - } - - @Override - public int hashCode() { - return Objects.hash(getViewer()); - } - - @Override - public String toString() { - return "ConfinedContext{" + "viewer=" + viewer + "} " + super.toString(); - } -} diff --git a/inventory-framework-platform/src/main/java/me/devnatan/inventoryframework/context/PlatformConfinedContext.java b/inventory-framework-platform/src/main/java/me/devnatan/inventoryframework/context/PlatformConfinedContext.java new file mode 100644 index 00000000..80dae1be --- /dev/null +++ b/inventory-framework-platform/src/main/java/me/devnatan/inventoryframework/context/PlatformConfinedContext.java @@ -0,0 +1,41 @@ +package me.devnatan.inventoryframework.context; + +import java.util.Collections; +import me.devnatan.inventoryframework.RootView; +import me.devnatan.inventoryframework.Viewer; +import org.jetbrains.annotations.NotNull; + +abstract class PlatformConfinedContext extends PlatformContext implements IFConfinedContext { + + @Override + public abstract Viewer getViewer(); + + @Override + public void closeForPlayer() { + getContainer().close(getViewer()); + } + + @Override + public void openForPlayer(@NotNull Class other) { + openForPlayer(other, null); + } + + @SuppressWarnings("unchecked") + @Override + public void openForPlayer(@NotNull Class other, Object initialData) { + getRoot() + .getFramework() + .getRegisteredViewByType(other) + .open(Collections.singletonList(getViewer()), initialData); + } + + @Override + public void updateTitleForPlayer(@NotNull String title) { + getContainer().changeTitle(title, getViewer()); + } + + @Override + public void resetTitleForPlayer() { + getContainer().changeTitle(null, getViewer()); + } +} diff --git a/inventory-framework-platform/src/main/java/me/devnatan/inventoryframework/context/PlatformContext.java b/inventory-framework-platform/src/main/java/me/devnatan/inventoryframework/context/PlatformContext.java new file mode 100644 index 00000000..8c5ac765 --- /dev/null +++ b/inventory-framework-platform/src/main/java/me/devnatan/inventoryframework/context/PlatformContext.java @@ -0,0 +1,18 @@ +package me.devnatan.inventoryframework.context; + +import me.devnatan.inventoryframework.PlatformView; +import me.devnatan.inventoryframework.RootView; +import org.jetbrains.annotations.NotNull; + +abstract class PlatformContext extends AbstractIFContext { + + @SuppressWarnings("rawtypes") + @Override + public abstract @NotNull PlatformView getRoot(); + + @SuppressWarnings("unchecked") + @Override + public final void openForEveryone(Class other, Object initialData) { + getRoot().getFramework().getRegisteredViewByType(other).open(getViewers(), initialData); + } +} diff --git a/inventory-framework-platform/src/main/java/me/devnatan/inventoryframework/context/PlatformRenderContext.java b/inventory-framework-platform/src/main/java/me/devnatan/inventoryframework/context/PlatformRenderContext.java index 19a56014..0a57b506 100644 --- a/inventory-framework-platform/src/main/java/me/devnatan/inventoryframework/context/PlatformRenderContext.java +++ b/inventory-framework-platform/src/main/java/me/devnatan/inventoryframework/context/PlatformRenderContext.java @@ -3,10 +3,15 @@ import static java.lang.String.format; import static me.devnatan.inventoryframework.utils.SlotConverter.convertSlot; -import java.util.*; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.UUID; import java.util.function.BiConsumer; import java.util.function.BiFunction; import me.devnatan.inventoryframework.InventoryFrameworkException; +import me.devnatan.inventoryframework.PlatformView; import me.devnatan.inventoryframework.RootView; import me.devnatan.inventoryframework.ViewConfig; import me.devnatan.inventoryframework.ViewContainer; @@ -18,57 +23,50 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.UnmodifiableView; -abstract class PlatformRenderContext, C extends IFContext> extends ConfinedContext - implements IFRenderContext { +@SuppressWarnings("rawtypes") +abstract class PlatformRenderContext, C extends IFContext> + extends PlatformConfinedContext implements IFRenderContext { + // --- Must inherit from parent context --- + private final UUID id; + protected final PlatformView root; + private final ViewConfig config; + private final Map viewers; + private final Object initialData; + private final Viewer subject; + + // --- Inherited --- + private final ViewContainer container; + + // --- Properties --- private final List componentBuilders = new ArrayList<>(); private final List layoutSlots = new ArrayList<>(); private BiFunction availableSlotFactory; - private final ViewConfig config; - private final UUID id; PlatformRenderContext( - UUID id, - RootView root, - ViewContainer container, + @NotNull UUID id, + @NotNull PlatformView root, + @NotNull ViewConfig config, + @NotNull ViewContainer container, + @NotNull Map viewers, Viewer subject, - Map viewers, - ViewConfig config, Object initialData) { - super(root, container, subject, viewers, initialData); this.id = id; + this.root = root; this.config = config; + this.container = container; + this.viewers = viewers; + this.subject = subject; + this.initialData = initialData; } - @Override - public @NotNull UUID getId() { - return id; - } - - @Override - public @NotNull ViewConfig getConfig() { - return config; - } - - @Override - public final @NotNull @UnmodifiableView List getComponentFactories() { - return Collections.unmodifiableList(componentBuilders); - } - - @Override - public final @NotNull List getLayoutSlots() { - return layoutSlots; - } - - @Override - public final void addLayoutSlot(@NotNull LayoutSlot layoutSlot) { - layoutSlots.add(layoutSlot); - } - - @Override - public BiFunction getAvailableSlotFactory() { - return availableSlotFactory; - } + /** + * Creates a new platform builder instance. + * + * @return A new platform builder instance. + */ + // TODO use ElementFactory's `createBuilder` instead + protected abstract T createBuilder(); /** * Creates a new item builder without a specified slot. @@ -215,14 +213,6 @@ public void layoutSlot(char character, @NotNull BiConsumer factory) })); } - /** - * Creates a new platform builder instance. - * - * @return A new platform builder instance. - */ - // TODO use ElementFactory's `createBuilder` instead - protected abstract T createBuilder(); - /** * Creates a new platform builder instance and registers it. * @@ -256,4 +246,84 @@ private void requireNonReservedLayoutCharacter(char character) { "The '%c' character cannot be used because it is only available for backwards compatibility. Please use another character.", character)); } + + @Override + public final @NotNull UUID getId() { + return id; + } + + @Override + public final @NotNull ViewContainer getContainer() { + return container; + } + + @Override + public final @NotNull ViewConfig getConfig() { + return config; + } + + @Override + public final @NotNull Map getIndexedViewers() { + return viewers; + } + + @Override + public final Object getInitialData() { + return initialData; + } + + @Override + public final Viewer getViewer() { + return subject; + } + + @Override + public final @NotNull @UnmodifiableView List getComponentFactories() { + return Collections.unmodifiableList(componentBuilders); + } + + @Override + public final @NotNull List getLayoutSlots() { + return layoutSlots; + } + + @Override + public final void addLayoutSlot(@NotNull LayoutSlot layoutSlot) { + layoutSlots.add(layoutSlot); + } + + @Override + public final BiFunction getAvailableSlotFactory() { + return availableSlotFactory; + } + + @Override + public final void closeForPlayer() { + tryThrowDoNotWorkWithSharedContext("closeForEveryone()"); + super.closeForPlayer(); + } + + @Override + public final void openForPlayer(@NotNull Class other) { + tryThrowDoNotWorkWithSharedContext("openForEveryone(Class)"); + super.openForPlayer(other); + } + + @Override + public final void openForPlayer(@NotNull Class other, Object initialData) { + tryThrowDoNotWorkWithSharedContext("openForEveryone(Class, Object)"); + super.openForPlayer(other, initialData); + } + + @Override + public final void updateTitleForPlayer(@NotNull String title) { + tryThrowDoNotWorkWithSharedContext("updateTitleForEveryone(String)"); + super.updateTitleForEveryone(title); + } + + @Override + public final void resetTitleForPlayer() { + tryThrowDoNotWorkWithSharedContext("resetTitleForEveryone()"); + super.resetTitleForPlayer(); + } } diff --git a/inventory-framework-platform/src/main/java/me/devnatan/inventoryframework/pipeline/PlatformOpenInterceptor.java b/inventory-framework-platform/src/main/java/me/devnatan/inventoryframework/pipeline/PlatformOpenInterceptor.java index 4f720b58..51ec00e3 100644 --- a/inventory-framework-platform/src/main/java/me/devnatan/inventoryframework/pipeline/PlatformOpenInterceptor.java +++ b/inventory-framework-platform/src/main/java/me/devnatan/inventoryframework/pipeline/PlatformOpenInterceptor.java @@ -49,35 +49,33 @@ private void finishOpen(@NotNull PipelineContext pipeline, @NotNull final RootView root = openContext.getRoot(); final IFRenderContext render = createRenderContext(openContext); - + root.addContext(render); root.renderContext(render); render.getViewers().forEach(render.getContainer()::open); } IFRenderContext createRenderContext(IFOpenContext openContext) { - final ElementFactory elementFactory = openContext.getRoot().getElementFactory(); + final RootView root = openContext.getRoot(); final ViewConfig contextConfig = openContext.getConfig(); final String[] layout = contextConfig.getLayout(); if (layout != null) { if (contextConfig.getSize() != 0 && contextConfig.getSize() != layout.length) { + // TODO Needs a more detailed error message throw new InvalidLayoutException("The layout length differs from the set inventory size."); } openContext.modifyConfig().size(layout.length); } - final ViewContainer container = elementFactory.createContainer(openContext); - final IFRenderContext renderCtx = elementFactory.createContext( + final ElementFactory elementFactory = root.getElementFactory(); + final ViewContainer createdContainer = elementFactory.createContainer(openContext); + return elementFactory.createRenderContext( + openContext.getId(), openContext.getRoot(), - container, - openContext.getViewer(), + openContext.getConfig(), + createdContainer, openContext.getIndexedViewers(), - IFRenderContext.class, - openContext, + openContext.getSubject(), openContext.getInitialData()); - - openContext.getViewers().forEach(renderCtx::addViewer); - openContext.getRoot().addContext(renderCtx); - return renderCtx; } } diff --git a/inventory-framework-test/src/main/java/me/devnatan/inventoryframework/TestUtils.java b/inventory-framework-test/src/main/java/me/devnatan/inventoryframework/TestUtils.java index d9248844..b67e6b38 100644 --- a/inventory-framework-test/src/main/java/me/devnatan/inventoryframework/TestUtils.java +++ b/inventory-framework-test/src/main/java/me/devnatan/inventoryframework/TestUtils.java @@ -5,13 +5,11 @@ import java.util.HashMap; import me.devnatan.inventoryframework.context.IFContext; -import me.devnatan.inventoryframework.internal.MockElementFactory; public class TestUtils { public static RootView createRootMock() { RootView root = mock(RootView.class); - when(root.getElementFactory()).thenReturn(new MockElementFactory()); return root; } 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 deleted file mode 100644 index ae0bf9ef..00000000 --- a/inventory-framework-test/src/main/java/me/devnatan/inventoryframework/internal/MockElementFactory.java +++ /dev/null @@ -1,108 +0,0 @@ -package me.devnatan.inventoryframework.internal; - -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import me.devnatan.inventoryframework.RootView; -import me.devnatan.inventoryframework.ViewContainer; -import me.devnatan.inventoryframework.Viewer; -import me.devnatan.inventoryframework.VirtualView; -import me.devnatan.inventoryframework.component.Component; -import me.devnatan.inventoryframework.component.ComponentBuilder; -import me.devnatan.inventoryframework.context.IFConfinedContext; -import me.devnatan.inventoryframework.context.IFContext; -import me.devnatan.inventoryframework.context.IFSlotContext; -import me.devnatan.inventoryframework.context.IFSlotRenderContext; -import me.devnatan.inventoryframework.logging.Logger; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -public final class MockElementFactory extends ElementFactory { - @Override - public Logger getLogger() { - return mock(Logger.class); - } - - @Override - public @NotNull RootView createUninitializedRoot() { - return mock(RootView.class); - } - - @Override - public @NotNull ViewContainer createContainer(@NotNull IFContext context) { - return mock(ViewContainer.class); - } - - @Override - public @NotNull Viewer createViewer(Object... parameters) { - return mock(Viewer.class); - } - - @Override - public @NotNull String convertViewer(Object input) { - return (String) input; - } - - @Override - public @NotNull T createContext( - @NotNull RootView root, - ViewContainer container, - Viewer subject, - @NotNull Map viewers, - @NotNull Class kind, - @Nullable IFContext parent, - Object initialData) { - T value = mock(kind); - when(value.getContainer()).thenReturn(container); - if (value instanceof IFConfinedContext) - when(((IFConfinedContext) value).getViewer()).thenReturn(subject); - if (parent != null) when(value.getConfig()).thenCallRealMethod(); - return value; - } - - @Override - public @NotNull T createSlotContext( - int slot, - Component component, - @NotNull ViewContainer container, - Viewer subject, - @NotNull Map viewers, - @NotNull IFContext parent, - @NotNull Class kind) { - @SuppressWarnings("unchecked") - T value = (T) mock(kind); - when(value.getSlot()).thenReturn(slot); - when(value.getParent()).thenReturn(parent); - when(value.getContainer()).thenReturn(container); - - final List viewerList = new ArrayList<>(); - if (subject != null) viewerList.add(subject); - if (viewers != null) viewerList.addAll(viewers.values()); - - when(value.getViewers()).thenReturn(viewerList); - - if (kind.equals(IFSlotRenderContext.class)) { - IFSlotRenderContext slotRenderContext = (IFSlotRenderContext) value; - when(slotRenderContext.isCancelled()).thenReturn(false); - } - return value; - } - - @Override - public ComponentBuilder createComponentBuilder(@NotNull VirtualView root) { - return null; - } - - @Override - public boolean worksInCurrentPlatform() { - return true; - } - - @Override - public Job scheduleJobInterval(@NotNull RootView root, long intervalInTicks, @NotNull Runnable execution) { - return null; - } -} From 693698e1a731ed0c8bdd78929f8790cee0cbce5b Mon Sep 17 00:00:00 2001 From: Natan Vieira do Nascimento Date: Wed, 30 Aug 2023 22:00:42 -0300 Subject: [PATCH 3/6] Move close, open, title and others to PlatformContext --- .../devnatan/inventoryframework/RootView.java | 5 -- .../inventoryframework/context/IFContext.java | 65 --------------- .../inventoryframework/DefaultRootView.java | 9 +- .../context/AbstractIFContext.java | 32 ------- .../runtime/InventoryFramework.java | 26 ++++++ .../inventoryframework/IFViewFrame.java | 2 +- .../inventoryframework/PlatformView.java | 34 ++++++++ .../context/PlatformConfinedContext.java | 6 +- .../context/PlatformContext.java | 83 ++++++++++++++++++- 9 files changed, 144 insertions(+), 118 deletions(-) diff --git a/inventory-framework-api/src/main/java/me/devnatan/inventoryframework/RootView.java b/inventory-framework-api/src/main/java/me/devnatan/inventoryframework/RootView.java index e91bc00d..615f84db 100644 --- a/inventory-framework-api/src/main/java/me/devnatan/inventoryframework/RootView.java +++ b/inventory-framework-api/src/main/java/me/devnatan/inventoryframework/RootView.java @@ -126,11 +126,6 @@ public interface RootView extends VirtualView, Iterable { @NotNull Pipeline getPipeline(); - /** - * Closes all contexts that are currently active in this view. - */ - void closeForEveryone(); - /** * The ElementFactory for this view. *

diff --git a/inventory-framework-api/src/main/java/me/devnatan/inventoryframework/context/IFContext.java b/inventory-framework-api/src/main/java/me/devnatan/inventoryframework/context/IFContext.java index 4ed45591..0b137401 100644 --- a/inventory-framework-api/src/main/java/me/devnatan/inventoryframework/context/IFContext.java +++ b/inventory-framework-api/src/main/java/me/devnatan/inventoryframework/context/IFContext.java @@ -12,7 +12,6 @@ import me.devnatan.inventoryframework.state.StateValueHost; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.UnmodifiableView; public interface IFContext extends VirtualView, StateValueHost { @@ -104,17 +103,6 @@ public interface IFContext extends VirtualView, StateValueHost { @NotNull RootView getRoot(); - /** - * The actual title of this context. - *

- * If the title has been dynamically changed, it will return the {@link #getUpdatedTitle() updated title}. - * - * @return The updated title, the current title of this view, if null will return - * the default title for this view type. - */ - @NotNull - String getTitle(); - /** * The initial title of this context, that is, even if it has been changed, it will return the * title that has been initially defined. @@ -124,59 +112,6 @@ public interface IFContext extends VirtualView, StateValueHost { @NotNull String getInitialTitle(); - /** - * Title that has been {@link #updateTitleForEveryone(String) dynamically changed} in this context. - * - * @return The updated title or null if it wasn't updated. - * @see #updateTitleForEveryone(String) - */ - @Nullable - String getUpdatedTitle(); - - /** - * Updates the container title for everyone that's viewing it. - * - *

This should not be used before the container is opened, if you need to set the __initial - * title__ use {@link IFOpenContext#modifyConfig()} on open handler instead. - * - *

This method is version dependant, so it may be that your server version is not yet - * supported, if you try to use this method and fail (can fail silently), report it to the - * library developers to add support to your version. - * - * @param title The new container title. - */ - void updateTitleForEveryone(@NotNull String title); - - /** - * Updates the container title to all viewers in this context, to the initially defined title. - * Must be used after {@link #updateTitleForEveryone(String)} to take effect. - */ - void resetTitleForEveryone(); - - /** - * Closes this context's container to all viewers who are viewing it. - */ - void closeForEveryone(); - - /** - * Opens a new view for all viewers in that context. - *

- * This context will be immediately invalidated if there are no viewers left after opening. - * - * @param other The view to be opened. - */ - void openForEveryone(Class other); - - /** - * Opens a new view for all viewers in that context with an initially defined data. - *

- * This context will be immediately invalidated if there are no viewers left after opening. - * - * @param other The view to be opened. - * @param initialData The initial data. - */ - void openForEveryone(Class other, Object initialData); - /** * All components in this context. * diff --git a/inventory-framework-core/src/main/java/me/devnatan/inventoryframework/DefaultRootView.java b/inventory-framework-core/src/main/java/me/devnatan/inventoryframework/DefaultRootView.java index 7150d4d5..b9eda50c 100644 --- a/inventory-framework-core/src/main/java/me/devnatan/inventoryframework/DefaultRootView.java +++ b/inventory-framework-core/src/main/java/me/devnatan/inventoryframework/DefaultRootView.java @@ -105,11 +105,6 @@ public final void setConfig(@NotNull ViewConfig config) { return pipeline; } - @Override - public final void closeForEveryone() { - getContexts().forEach(IFContext::closeForEveryone); - } - @Override public void onInit(ViewConfigBuilder config) {} @@ -140,12 +135,12 @@ public void nextTick(Runnable task) { } @Override - public Job getScheduledUpdateJob() { + public final Job getScheduledUpdateJob() { return scheduledUpdateJob; } @Override - public void setScheduledUpdateJob(@NotNull Job job) { + public final void setScheduledUpdateJob(@NotNull Job job) { this.scheduledUpdateJob = job; } 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 4c2a0b75..32d9215a 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 @@ -10,7 +10,6 @@ import java.util.Map; import java.util.Optional; import me.devnatan.inventoryframework.InventoryFrameworkException; -import me.devnatan.inventoryframework.RootView; import me.devnatan.inventoryframework.UnsupportedOperationInSharedContextException; import me.devnatan.inventoryframework.ViewConfig; import me.devnatan.inventoryframework.Viewer; @@ -18,7 +17,6 @@ import me.devnatan.inventoryframework.pipeline.StandardPipelinePhases; import me.devnatan.inventoryframework.state.DefaultStateValueHost; import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Unmodifiable; import org.jetbrains.annotations.UnmodifiableView; @@ -53,41 +51,11 @@ public final void removeViewer(@NotNull Viewer viewer) { } } - @Override - public @NotNull String getTitle() { - return getUpdatedTitle() == null ? getInitialTitle() : getUpdatedTitle(); - } - @Override public final @NotNull String getInitialTitle() { return getConfig().getTitle().toString(); } - @Override - public final @Nullable String getUpdatedTitle() { - return getContainer().getTitle(); - } - - @Override - public final void updateTitleForEveryone(@NotNull String title) { - for (final Viewer viewer : getViewers()) getContainer().changeTitle(title, viewer); - } - - @Override - public final void resetTitleForEveryone() { - for (final Viewer viewer : getViewers()) getContainer().changeTitle(null, viewer); - } - - @Override - public final void closeForEveryone() { - getContainer().close(); - } - - @Override - public final void openForEveryone(Class other) { - openForEveryone(other, null); - } - @Override public @UnmodifiableView @NotNull List getComponents() { return Collections.unmodifiableList(components); diff --git a/inventory-framework-platform-bukkit/src/main/java/me/devnatan/inventoryframework/runtime/InventoryFramework.java b/inventory-framework-platform-bukkit/src/main/java/me/devnatan/inventoryframework/runtime/InventoryFramework.java index 6cec7fe2..22d95dee 100644 --- a/inventory-framework-platform-bukkit/src/main/java/me/devnatan/inventoryframework/runtime/InventoryFramework.java +++ b/inventory-framework-platform-bukkit/src/main/java/me/devnatan/inventoryframework/runtime/InventoryFramework.java @@ -1,9 +1,35 @@ package me.devnatan.inventoryframework.runtime; +import me.devnatan.inventoryframework.View; +import me.devnatan.inventoryframework.ViewConfigBuilder; +import me.devnatan.inventoryframework.ViewFrame; +import me.devnatan.inventoryframework.context.RenderContext; +import org.bukkit.Material; +import org.bukkit.inventory.ItemStack; import org.bukkit.plugin.java.JavaPlugin; @SuppressWarnings("unused") public final class InventoryFramework extends JavaPlugin { public static final String LIBRARY_VERSION = "3.0.0-rc.2"; + + @Override + public void onEnable() { + ViewFrame vf = ViewFrame.create(this).with(new Test()).register(); + + getServer().getOnlinePlayers().forEach(player -> vf.open(Test.class, player)); + } +} + +class Test extends View { + + @Override + public void onInit(ViewConfigBuilder config) { + config.title("Textando"); + } + + @Override + public void onFirstRender(RenderContext render) { + render.slot(3, new ItemStack(Material.GOLD_INGOT)); + } } diff --git a/inventory-framework-platform/src/main/java/me/devnatan/inventoryframework/IFViewFrame.java b/inventory-framework-platform/src/main/java/me/devnatan/inventoryframework/IFViewFrame.java index 129c2522..8a0e33d5 100644 --- a/inventory-framework-platform/src/main/java/me/devnatan/inventoryframework/IFViewFrame.java +++ b/inventory-framework-platform/src/main/java/me/devnatan/inventoryframework/IFViewFrame.java @@ -7,7 +7,7 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.UnmodifiableView; -public abstract class IFViewFrame, V extends PlatformView> { +abstract class IFViewFrame, V extends PlatformView> { private boolean registered; private final Map registeredViews = new HashMap<>(); diff --git a/inventory-framework-platform/src/main/java/me/devnatan/inventoryframework/PlatformView.java b/inventory-framework-platform/src/main/java/me/devnatan/inventoryframework/PlatformView.java index 8fa6cf1e..78d08d8a 100644 --- a/inventory-framework-platform/src/main/java/me/devnatan/inventoryframework/PlatformView.java +++ b/inventory-framework-platform/src/main/java/me/devnatan/inventoryframework/PlatformView.java @@ -1,5 +1,6 @@ package me.devnatan.inventoryframework; +import java.util.Collections; import java.util.List; import java.util.concurrent.CompletableFuture; import java.util.function.BiConsumer; @@ -16,6 +17,7 @@ import me.devnatan.inventoryframework.context.IFRenderContext; import me.devnatan.inventoryframework.context.IFSlotClickContext; import me.devnatan.inventoryframework.context.IFSlotContext; +import me.devnatan.inventoryframework.context.PlatformContext; import me.devnatan.inventoryframework.internal.ElementFactory; import me.devnatan.inventoryframework.internal.PlatformUtils; import me.devnatan.inventoryframework.pipeline.AvailableSlotInterceptor; @@ -102,6 +104,13 @@ private void requireNotInitialized() { "View is already initialized, please move this method call to class constructor or #onInit."); } + /** + * Closes all contexts that are currently active in this view. + */ + public final void closeForEveryone() { + getContexts().forEach(context -> ((PlatformContext) context).closeForEveryone()); + } + /** * Opens this view to one or more viewers. *

@@ -121,6 +130,31 @@ public final void open(@NotNull List viewers, Object initialData) { getPipeline().execute(StandardPipelinePhases.OPEN, context); } + /** + *

This is an internal inventory-framework API that should not be used from outside of + * this library. No compatibility guarantees are provided. + */ + @SuppressWarnings("rawtypes") + @ApiStatus.Internal + public final void navigateTo( + @NotNull Class target, @NotNull IFContext context, Object initialData) { + getFramework().getRegisteredViewByType(target).open(context.getViewers(), initialData); + } + + /** + *

This is an internal inventory-framework API that should not be used from outside of + * this library. No compatibility guarantees are provided. + */ + @SuppressWarnings("rawtypes") + @ApiStatus.Internal + public final void navigateTo( + @NotNull Class target, + @NotNull IFContext context, + @NotNull Viewer viewer, + Object initialData) { + getFramework().getRegisteredViewByType(target).open(Collections.singletonList(viewer), initialData); + } + /** * Creates a new configuration builder. * diff --git a/inventory-framework-platform/src/main/java/me/devnatan/inventoryframework/context/PlatformConfinedContext.java b/inventory-framework-platform/src/main/java/me/devnatan/inventoryframework/context/PlatformConfinedContext.java index 80dae1be..28476049 100644 --- a/inventory-framework-platform/src/main/java/me/devnatan/inventoryframework/context/PlatformConfinedContext.java +++ b/inventory-framework-platform/src/main/java/me/devnatan/inventoryframework/context/PlatformConfinedContext.java @@ -1,6 +1,5 @@ package me.devnatan.inventoryframework.context; -import java.util.Collections; import me.devnatan.inventoryframework.RootView; import me.devnatan.inventoryframework.Viewer; import org.jetbrains.annotations.NotNull; @@ -23,10 +22,7 @@ public void openForPlayer(@NotNull Class other) { @SuppressWarnings("unchecked") @Override public void openForPlayer(@NotNull Class other, Object initialData) { - getRoot() - .getFramework() - .getRegisteredViewByType(other) - .open(Collections.singletonList(getViewer()), initialData); + getRoot().navigateTo(other, this, getViewer(), initialData); } @Override diff --git a/inventory-framework-platform/src/main/java/me/devnatan/inventoryframework/context/PlatformContext.java b/inventory-framework-platform/src/main/java/me/devnatan/inventoryframework/context/PlatformContext.java index 8c5ac765..c83e3df5 100644 --- a/inventory-framework-platform/src/main/java/me/devnatan/inventoryframework/context/PlatformContext.java +++ b/inventory-framework-platform/src/main/java/me/devnatan/inventoryframework/context/PlatformContext.java @@ -2,17 +2,94 @@ import me.devnatan.inventoryframework.PlatformView; import me.devnatan.inventoryframework.RootView; +import me.devnatan.inventoryframework.Viewer; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; -abstract class PlatformContext extends AbstractIFContext { +public abstract class PlatformContext extends AbstractIFContext { + + PlatformContext() {} @SuppressWarnings("rawtypes") @Override public abstract @NotNull PlatformView getRoot(); + /** + * The actual title of this context. + *

+ * If the title has been dynamically changed, it will return the {@link #getUpdatedTitle() updated title}. + * + * @return The updated title, the current title of this view, if null will return + * the default title for this view type. + */ + @NotNull + public String getTitle() { + return getUpdatedTitle() == null ? getInitialTitle() : getUpdatedTitle(); + } + + /** + * Title that has been {@link #updateTitleForEveryone(String) dynamically changed} in this context. + * + * @return The updated title or null if it wasn't updated. + * @see #updateTitleForEveryone(String) + */ + @Nullable + public final String getUpdatedTitle() { + return getContainer().getTitle(); + } + + /** + * Updates the container title for everyone that's viewing it. + * + *

This should not be used before the container is opened, if you need to set the __initial + * title__ use {@link IFOpenContext#modifyConfig()} on open handler instead. + * + *

This method is version dependant, so it may be that your server version is not yet + * supported, if you try to use this method and fail (can fail silently), report it to the + * library developers to add support to your version. + * + * @param title The new container title. + */ + public final void updateTitleForEveryone(@NotNull String title) { + for (final Viewer viewer : getViewers()) getContainer().changeTitle(title, viewer); + } + + /** + * Updates the container title to all viewers in this context, to the initially defined title. + * Must be used after {@link #updateTitleForEveryone(String)} to take effect. + */ + public final void resetTitleForEveryone() { + for (final Viewer viewer : getViewers()) getContainer().changeTitle(null, viewer); + } + + /** + * Closes this context's container to all viewers who are viewing it. + */ + public final void closeForEveryone() { + getContainer().close(); + } + + /** + * Opens a new view for all viewers in that context. + *

+ * This context will be immediately invalidated if there are no viewers left after opening. + * + * @param other The view to be opened. + */ + public final void openForEveryone(Class other) { + openForEveryone(other, null); + } + + /** + * Opens a new view for all viewers in that context with an initially defined data. + *

+ * This context will be immediately invalidated if there are no viewers left after opening. + * + * @param other The view to be opened. + * @param initialData The initial data. + */ @SuppressWarnings("unchecked") - @Override public final void openForEveryone(Class other, Object initialData) { - getRoot().getFramework().getRegisteredViewByType(other).open(getViewers(), initialData); + getRoot().navigateTo(other, this, initialData); } } From e838bd9eebedc9bf5abe484ccc996db17a006764 Mon Sep 17 00:00:00 2001 From: Natan Vieira do Nascimento Date: Thu, 31 Aug 2023 14:51:16 -0300 Subject: [PATCH 4/6] Register viewers globally --- .../devnatan/inventoryframework/RootView.java | 65 +--------- .../devnatan/inventoryframework/Viewer.java | 4 + .../inventoryframework/DefaultRootView.java | 64 +--------- .../ScheduledUpdateAfterCloseInterceptor.java | 3 +- ...ScheduledUpdateAfterRenderInterceptor.java | 2 +- .../inventoryframework/BukkitViewer.java | 10 ++ .../IFInventoryListener.java | 2 + .../inventoryframework/ViewFrame.java | 29 +++-- .../runtime/InventoryFramework.java | 2 +- .../inventoryframework/IFViewFrame.java | 13 ++ .../inventoryframework/PlatformView.java | 116 +++++++++++++++++- .../context/PlatformRenderContext.java | 2 +- .../pipeline/PlatformCloseInterceptor.java | 25 +--- .../pipeline/PlatformOpenInterceptor.java | 15 ++- 14 files changed, 182 insertions(+), 170 deletions(-) diff --git a/inventory-framework-api/src/main/java/me/devnatan/inventoryframework/RootView.java b/inventory-framework-api/src/main/java/me/devnatan/inventoryframework/RootView.java index 615f84db..017da5f5 100644 --- a/inventory-framework-api/src/main/java/me/devnatan/inventoryframework/RootView.java +++ b/inventory-framework-api/src/main/java/me/devnatan/inventoryframework/RootView.java @@ -9,9 +9,8 @@ import me.devnatan.inventoryframework.pipeline.Pipeline; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.UnmodifiableView; -public interface RootView extends VirtualView, Iterable { +public interface RootView extends VirtualView { /** * The unique identifier of this view. @@ -23,72 +22,14 @@ public interface RootView extends VirtualView, Iterable { /** * All contexts linked to this view. - * - * @return An unmodifiable set of all currently active contexts in this view. - */ - @NotNull - @UnmodifiableView - Set getContexts(); - - /** - * Returns the context that is linked to the specified viewer in this view. - *

- * This is an internal inventory-framework API that should not be used from outside of - * this library. No compatibility guarantees are provided. - * - * @param viewer The viewer. - * @return The context of the viewer in this context. - * @throws IllegalArgumentException If there's no context linked to the given viewer. - */ - @ApiStatus.Internal - @NotNull - IFContext getContext(@NotNull Viewer viewer); - - /** - * Returns the context that is linked to the specified viewer in this view. - *

- * This is an internal inventory-framework API that should not be used from outside of - * this library. No compatibility guarantees are provided. - * - * @param viewerId The id of the viewer. - * @return The context of the viewer in this context. - * @throws IllegalArgumentException If there's no context linked to the given viewer. - */ - @NotNull - IFContext getContext(@NotNull String viewerId); - - /** - * Adds a context to this view. - *

- * This is an internal inventory-framework API that should not be used from outside of - * this library. No compatibility guarantees are provided. - * - * @param context The context to add. - */ - @ApiStatus.Internal - void addContext(@NotNull IFContext context); - - /** - * Removes a given context from this view if that context is linked to this view. - *

- * This is an internal inventory-framework API that should not be used from outside of - * this library. No compatibility guarantees are provided. - * - * @param context The context to remove. - */ - @ApiStatus.Internal - void removeContext(@NotNull IFContext context); - - /** - * Renders a given context in this view. *

* This is an internal inventory-framework API that should not be used from outside of * this library. No compatibility guarantees are provided. * - * @param context The context to render. + * @return An unmodifiable set of all currently active contexts in this view. */ @ApiStatus.Internal - void renderContext(@NotNull IFContext context); + Set getInternalContexts(); /** * Called when the view is about to be configured, the returned object will be the view's diff --git a/inventory-framework-api/src/main/java/me/devnatan/inventoryframework/Viewer.java b/inventory-framework-api/src/main/java/me/devnatan/inventoryframework/Viewer.java index 8cd698f7..cab80117 100644 --- a/inventory-framework-api/src/main/java/me/devnatan/inventoryframework/Viewer.java +++ b/inventory-framework-api/src/main/java/me/devnatan/inventoryframework/Viewer.java @@ -2,6 +2,7 @@ import me.devnatan.inventoryframework.context.IFRenderContext; import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; @ApiStatus.Internal @@ -39,4 +40,7 @@ public interface Viewer { IFRenderContext getContext(); void setContext(IFRenderContext context); + + @Contract("_ -> this") + Viewer withContext(IFRenderContext context); } diff --git a/inventory-framework-core/src/main/java/me/devnatan/inventoryframework/DefaultRootView.java b/inventory-framework-core/src/main/java/me/devnatan/inventoryframework/DefaultRootView.java index b9eda50c..6a466091 100644 --- a/inventory-framework-core/src/main/java/me/devnatan/inventoryframework/DefaultRootView.java +++ b/inventory-framework-core/src/main/java/me/devnatan/inventoryframework/DefaultRootView.java @@ -1,6 +1,5 @@ package me.devnatan.inventoryframework; -import static java.lang.String.format; import static java.util.Collections.newSetFromMap; import static java.util.Collections.synchronizedMap; import static me.devnatan.inventoryframework.pipeline.StandardPipelinePhases.CLICK; @@ -13,7 +12,6 @@ import static me.devnatan.inventoryframework.pipeline.StandardPipelinePhases.UPDATE; import java.util.*; -import java.util.function.Consumer; import me.devnatan.inventoryframework.context.IFContext; import me.devnatan.inventoryframework.internal.ElementFactory; import me.devnatan.inventoryframework.internal.Job; @@ -21,8 +19,6 @@ import me.devnatan.inventoryframework.state.StateRegistry; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.TestOnly; -import org.jetbrains.annotations.UnmodifiableView; @ApiStatus.NonExtendable public class DefaultRootView implements RootView { @@ -41,53 +37,15 @@ public class DefaultRootView implements RootView { return id; } - @TestOnly - public final @NotNull Set getInternalContexts() { + @Override + public final Set getInternalContexts() { return contexts; } - @Override - public final @NotNull @UnmodifiableView Set getContexts() { + protected final Set getContexts() { return Collections.unmodifiableSet(getInternalContexts()); } - @Override - public final @NotNull IFContext getContext(@NotNull Viewer viewer) { - for (final IFContext context : getInternalContexts()) { - if (context.getIndexedViewers().containsKey(viewer.getId())) return context; - } - - throw new IllegalArgumentException(format("Unable to get context for %s", viewer)); - } - - @Override - public final @NotNull IFContext getContext(@NotNull String viewerId) { - for (final IFContext context : getInternalContexts()) { - if (context.getIndexedViewers().containsKey(viewerId)) return context; - } - - throw new IllegalArgumentException(format("Unable to get context for %s", viewerId)); - } - - @Override - public final void addContext(@NotNull IFContext context) { - synchronized (getInternalContexts()) { - getInternalContexts().add(context); - } - } - - @Override - public final void removeContext(@NotNull IFContext context) { - synchronized (getInternalContexts()) { - getInternalContexts().removeIf(other -> other.getId() == context.getId()); - } - } - - @Override - public final void renderContext(@NotNull IFContext context) { - getPipeline().execute(context); - } - @Override public final @NotNull ViewConfig getConfig() { return config; @@ -108,22 +66,6 @@ public final void setConfig(@NotNull ViewConfig config) { @Override public void onInit(ViewConfigBuilder config) {} - @NotNull - @Override - public final Iterator iterator() { - return getContexts().iterator(); - } - - @Override - public final void forEach(Consumer action) { - RootView.super.forEach(action); - } - - @Override - public final Spliterator spliterator() { - return RootView.super.spliterator(); - } - @ApiStatus.Internal public @NotNull ElementFactory getElementFactory() { throw new UnsupportedOperationException("Element factory not provided"); diff --git a/inventory-framework-core/src/main/java/me/devnatan/inventoryframework/pipeline/ScheduledUpdateAfterCloseInterceptor.java b/inventory-framework-core/src/main/java/me/devnatan/inventoryframework/pipeline/ScheduledUpdateAfterCloseInterceptor.java index b82f4855..7fff6432 100644 --- a/inventory-framework-core/src/main/java/me/devnatan/inventoryframework/pipeline/ScheduledUpdateAfterCloseInterceptor.java +++ b/inventory-framework-core/src/main/java/me/devnatan/inventoryframework/pipeline/ScheduledUpdateAfterCloseInterceptor.java @@ -18,7 +18,8 @@ public void intercept(PipelineContext pipeline, VirtualView subject // check possible shared context viewers count first to optimize global viewers count check if (!context.getViewers().isEmpty()) return; - if (!root.getContexts().stream().allMatch(other -> other.getViewers().isEmpty())) return; + if (!root.getInternalContexts().stream() + .allMatch(other -> other.getViewers().isEmpty())) return; root.getScheduledUpdateJob().cancel(); } diff --git a/inventory-framework-core/src/main/java/me/devnatan/inventoryframework/pipeline/ScheduledUpdateAfterRenderInterceptor.java b/inventory-framework-core/src/main/java/me/devnatan/inventoryframework/pipeline/ScheduledUpdateAfterRenderInterceptor.java index 75a3a19b..ad47bc93 100644 --- a/inventory-framework-core/src/main/java/me/devnatan/inventoryframework/pipeline/ScheduledUpdateAfterRenderInterceptor.java +++ b/inventory-framework-core/src/main/java/me/devnatan/inventoryframework/pipeline/ScheduledUpdateAfterRenderInterceptor.java @@ -19,7 +19,7 @@ public void intercept(PipelineContext pipeline, VirtualView subject if (root.getScheduledUpdateJob() != null && root.getScheduledUpdateJob().isStarted()) return; final Job updateJob = root.getElementFactory().scheduleJobInterval(root, updateIntervalInTicks, () -> { - root.getContexts().forEach(IFContext::update); + root.getInternalContexts().forEach(IFContext::update); }); updateJob.start(); root.setScheduledUpdateJob(updateJob); diff --git a/inventory-framework-platform-bukkit/src/main/java/me/devnatan/inventoryframework/BukkitViewer.java b/inventory-framework-platform-bukkit/src/main/java/me/devnatan/inventoryframework/BukkitViewer.java index 61a90b8f..5ac23279 100644 --- a/inventory-framework-platform-bukkit/src/main/java/me/devnatan/inventoryframework/BukkitViewer.java +++ b/inventory-framework-platform-bukkit/src/main/java/me/devnatan/inventoryframework/BukkitViewer.java @@ -12,7 +12,12 @@ public final class BukkitViewer implements Viewer { private IFRenderContext context; public BukkitViewer(@NotNull Player player, IFRenderContext context) { + this(player, null, context); + } + + private BukkitViewer(@NotNull Player player, @NotNull ViewContainer selfContainer, IFRenderContext context) { this.player = player; + this.selfContainer = selfContainer; this.context = context; } @@ -31,6 +36,11 @@ public void setContext(IFRenderContext context) { this.context = context; } + @Override + public Viewer withContext(IFRenderContext context) { + return new BukkitViewer(player, selfContainer, context); + } + @Override public @NotNull String getId() { return getPlayer().getUniqueId().toString(); diff --git a/inventory-framework-platform-bukkit/src/main/java/me/devnatan/inventoryframework/IFInventoryListener.java b/inventory-framework-platform-bukkit/src/main/java/me/devnatan/inventoryframework/IFInventoryListener.java index b2c4cbf8..6ae0a10e 100644 --- a/inventory-framework-platform-bukkit/src/main/java/me/devnatan/inventoryframework/IFInventoryListener.java +++ b/inventory-framework-platform-bukkit/src/main/java/me/devnatan/inventoryframework/IFInventoryListener.java @@ -31,10 +31,12 @@ public void onInventoryClick(final InventoryClickEvent event) { final Player player = (Player) event.getWhoClicked(); final Viewer viewer = viewFrame.getViewer(player); + System.out.println("viewer = " + viewer); if (viewer == null) return; final IFRenderContext context = viewer.getContext(); final Component clickedComponent = context.getComponent(event.getRawSlot()); + System.out.println("clickedComponent = " + clickedComponent); if (clickedComponent == null || !clickedComponent.isVisible()) return; final ViewContainer clickedContainer = event.getClickedInventory() instanceof PlayerInventory diff --git a/inventory-framework-platform-bukkit/src/main/java/me/devnatan/inventoryframework/ViewFrame.java b/inventory-framework-platform-bukkit/src/main/java/me/devnatan/inventoryframework/ViewFrame.java index 5e026ac5..3eb51333 100644 --- a/inventory-framework-platform-bukkit/src/main/java/me/devnatan/inventoryframework/ViewFrame.java +++ b/inventory-framework-platform-bukkit/src/main/java/me/devnatan/inventoryframework/ViewFrame.java @@ -2,7 +2,6 @@ import java.util.Collection; import java.util.Collections; -import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.UUID; @@ -33,7 +32,6 @@ public class ViewFrame extends IFViewFrame implements FeatureIn private final Plugin owner; private final FeatureInstaller featureInstaller = new DefaultFeatureInstaller<>(this); - private final Map viewerByPlayerUuid = new HashMap<>(); static { PlatformUtils.setFactory(new BukkitElementFactory()); @@ -44,7 +42,7 @@ private ViewFrame(Plugin owner) { } @NotNull - public Plugin getOwner() { + public final Plugin getOwner() { return owner; } @@ -54,7 +52,7 @@ public Plugin getOwner() { * @param viewClass The target view to be opened. * @param player The player that the view will be open to. */ - public void open(@NotNull Class viewClass, @NotNull Player player) { + public final void open(@NotNull Class viewClass, @NotNull Player player) { open(viewClass, player, null); } @@ -65,7 +63,7 @@ public void open(@NotNull Class viewClass, @NotNull Player playe * @param player The player that the view will be open to. * @param initialData The initial data. */ - public void open(@NotNull Class viewClass, @NotNull Player player, Object initialData) { + public final void open(@NotNull Class viewClass, @NotNull Player player, Object initialData) { open(viewClass, Collections.singletonList(player), initialData); } @@ -81,7 +79,7 @@ public void open(@NotNull Class viewClass, @NotNull Player playe * @param players The players that the view will be open to. */ @ApiStatus.Experimental - public void open(@NotNull Class viewClass, @NotNull Collection players) { + public final void open(@NotNull Class viewClass, @NotNull Collection players) { open(viewClass, players, null); } @@ -98,7 +96,7 @@ public void open(@NotNull Class viewClass, @NotNull Collection viewClass, @NotNull Collection players, Object initialData) { @@ -106,7 +104,7 @@ public void open( } @Override - public ViewFrame register() { + public final ViewFrame register() { if (isRegistered()) throw new IllegalStateException("This view frame is already registered"); tryEnableMetrics(); @@ -118,7 +116,7 @@ public ViewFrame register() { } @Override - public void unregister() { + public final void unregister() { if (!isRegistered()) return; // Locks new operations while unregistering @@ -202,8 +200,8 @@ private void checkRelocationIssues() { * this library. No compatibility guarantees are provided. */ @ApiStatus.Internal - public Viewer getViewer(@NotNull Player player) { - return viewerByPlayerUuid.get(player.getUniqueId().toString()); + public final Viewer getViewer(@NotNull Player player) { + return viewerById.get(player.getUniqueId().toString()); } /** @@ -217,22 +215,23 @@ public Viewer getViewer(@NotNull Player player) { } @Override - public @NotNull ViewFrame getPlatform() { + public final @NotNull ViewFrame getPlatform() { return this; } @Override - public Collection> getInstalledFeatures() { + public final Collection> getInstalledFeatures() { return featureInstaller.getInstalledFeatures(); } @Override - public @NotNull R install(@NotNull Feature feature, @NotNull UnaryOperator configure) { + public final @NotNull R install( + @NotNull Feature feature, @NotNull UnaryOperator configure) { return featureInstaller.install(feature, configure); } @Override - public void uninstall(@NotNull Feature feature) { + public final void uninstall(@NotNull Feature feature) { featureInstaller.uninstall(feature); } } diff --git a/inventory-framework-platform-bukkit/src/main/java/me/devnatan/inventoryframework/runtime/InventoryFramework.java b/inventory-framework-platform-bukkit/src/main/java/me/devnatan/inventoryframework/runtime/InventoryFramework.java index 22d95dee..228ef48c 100644 --- a/inventory-framework-platform-bukkit/src/main/java/me/devnatan/inventoryframework/runtime/InventoryFramework.java +++ b/inventory-framework-platform-bukkit/src/main/java/me/devnatan/inventoryframework/runtime/InventoryFramework.java @@ -30,6 +30,6 @@ public void onInit(ViewConfigBuilder config) { @Override public void onFirstRender(RenderContext render) { - render.slot(3, new ItemStack(Material.GOLD_INGOT)); + render.slot(3, new ItemStack(Material.GOLD_INGOT)).cancelOnClick(); } } diff --git a/inventory-framework-platform/src/main/java/me/devnatan/inventoryframework/IFViewFrame.java b/inventory-framework-platform/src/main/java/me/devnatan/inventoryframework/IFViewFrame.java index 8a0e33d5..49ef3685 100644 --- a/inventory-framework-platform/src/main/java/me/devnatan/inventoryframework/IFViewFrame.java +++ b/inventory-framework-platform/src/main/java/me/devnatan/inventoryframework/IFViewFrame.java @@ -11,6 +11,7 @@ abstract class IFViewFrame, V extends PlatformView registeredViews = new HashMap<>(); + protected final Map viewerById = new HashMap<>(); protected IFViewFrame() {} @@ -127,4 +128,16 @@ protected final void internalOpen( view.open(viewers, initialData); } + + void addViewer(@NotNull Viewer viewer) { + synchronized (viewerById) { + viewerById.put(viewer.getId(), viewer); + } + } + + void removeViewer(@NotNull Viewer viewer) { + synchronized (viewerById) { + viewerById.remove(viewer.getId()); + } + } } diff --git a/inventory-framework-platform/src/main/java/me/devnatan/inventoryframework/PlatformView.java b/inventory-framework-platform/src/main/java/me/devnatan/inventoryframework/PlatformView.java index 78d08d8a..708a9255 100644 --- a/inventory-framework-platform/src/main/java/me/devnatan/inventoryframework/PlatformView.java +++ b/inventory-framework-platform/src/main/java/me/devnatan/inventoryframework/PlatformView.java @@ -1,6 +1,9 @@ package me.devnatan.inventoryframework; +import static java.lang.String.format; + import java.util.Collections; +import java.util.Iterator; import java.util.List; import java.util.concurrent.CompletableFuture; import java.util.function.BiConsumer; @@ -61,7 +64,7 @@ public abstract class PlatformView< TRenderContext extends IFRenderContext, TSlotContext extends IFSlotContext, TSlotClickContext extends IFSlotClickContext> - extends DefaultRootView { + extends DefaultRootView implements Iterable { private TFramework framework; private boolean initialized; @@ -155,14 +158,117 @@ public final void navigateTo( getFramework().getRegisteredViewByType(target).open(Collections.singletonList(viewer), initialData); } + public final @NotNull ViewConfigBuilder createConfig() { + return new ViewConfigBuilder().type(ViewType.CHEST); + } + /** - * Creates a new configuration builder. + * Returns the context that is linked to the specified viewer in this view. + *

+ * This is an internal inventory-framework API that should not be used from outside of + * this library. No compatibility guarantees are provided. * - * @return A new {@link ViewConfigBuilder} instance. + * @param viewer The viewer. + * @return The context of the viewer in this context. + * @throws IllegalArgumentException If there's no context linked to the given viewer. */ + @ApiStatus.Internal + public final @NotNull IFContext getContext(@NotNull Viewer viewer) { + for (final IFContext context : getInternalContexts()) { + if (context.getIndexedViewers().containsKey(viewer.getId())) return context; + } + + throw new IllegalArgumentException(format("Unable to get context for %s", viewer)); + } + + /** + * Returns the context that is linked to the specified viewer in this view. + *

+ * This is an internal inventory-framework API that should not be used from outside of + * this library. No compatibility guarantees are provided. + * + * @param viewerId The id of the viewer. + * @return The context of the viewer in this context. + * @throws IllegalArgumentException If there's no context linked to the given viewer. + */ + public final @NotNull IFContext getContext(@NotNull String viewerId) { + for (final IFContext context : getInternalContexts()) { + if (context.getIndexedViewers().containsKey(viewerId)) return context; + } + + throw new IllegalArgumentException(format("Unable to get context for %s", viewerId)); + } + + /** + * Adds a context to this view. + *

+ * This is an internal inventory-framework API that should not be used from outside of + * this library. No compatibility guarantees are provided. + * + * @param context The context to add. + */ + @ApiStatus.Internal + public void addContext(@NotNull TContext context) { + synchronized (getInternalContexts()) { + getInternalContexts().add(context); + } + } + + /** + * Removes a given context from this view if that context is linked to this view. + *

+ * This is an internal inventory-framework API that should not be used from outside of + * this library. No compatibility guarantees are provided. + * + * @param context The context to remove. + */ + @ApiStatus.Internal + public void removeContext(@NotNull TContext context) { + synchronized (getInternalContexts()) { + getInternalContexts().removeIf(other -> other.getId() == context.getId()); + } + } + + /** + * Renders a given context in this view. + *

+ * This is an internal inventory-framework API that should not be used from outside of + * this library. No compatibility guarantees are provided. + * + * @param context The context to render. + */ + @ApiStatus.Internal + public void renderContext(@NotNull TContext context) { + getPipeline().execute(context); + + @SuppressWarnings("rawtypes") + final PlatformView view = (PlatformView) context.getRoot(); + context.getViewers().forEach(viewer -> { + view.getFramework().addViewer(viewer); + context.getContainer().open(viewer); + }); + } + + @ApiStatus.Internal + public void removeAndTryInvalidateContext(@NotNull Viewer viewer, @NotNull TContext context) { + context.removeViewer(viewer); + + @SuppressWarnings("rawtypes") + final PlatformView view = (PlatformView) context.getRoot(); + view.getFramework().removeViewer(viewer); + + final boolean canContextBeInvalidated = context.getViewers().isEmpty(); + if (canContextBeInvalidated) { + // TODO invalidate context + removeContext(context); + } + } + + @SuppressWarnings("unchecked") @NotNull - public final ViewConfigBuilder createConfig() { - return new ViewConfigBuilder().type(ViewType.CHEST); + @Override + public final Iterator iterator() { + return (Iterator) getContexts().iterator(); } /** diff --git a/inventory-framework-platform/src/main/java/me/devnatan/inventoryframework/context/PlatformRenderContext.java b/inventory-framework-platform/src/main/java/me/devnatan/inventoryframework/context/PlatformRenderContext.java index 0a57b506..d931ad2b 100644 --- a/inventory-framework-platform/src/main/java/me/devnatan/inventoryframework/context/PlatformRenderContext.java +++ b/inventory-framework-platform/src/main/java/me/devnatan/inventoryframework/context/PlatformRenderContext.java @@ -24,7 +24,7 @@ import org.jetbrains.annotations.UnmodifiableView; @SuppressWarnings("rawtypes") -abstract class PlatformRenderContext, C extends IFContext> +public abstract class PlatformRenderContext, C extends IFContext> extends PlatformConfinedContext implements IFRenderContext { // --- Must inherit from parent context --- diff --git a/inventory-framework-platform/src/main/java/me/devnatan/inventoryframework/pipeline/PlatformCloseInterceptor.java b/inventory-framework-platform/src/main/java/me/devnatan/inventoryframework/pipeline/PlatformCloseInterceptor.java index ff605431..5170b627 100644 --- a/inventory-framework-platform/src/main/java/me/devnatan/inventoryframework/pipeline/PlatformCloseInterceptor.java +++ b/inventory-framework-platform/src/main/java/me/devnatan/inventoryframework/pipeline/PlatformCloseInterceptor.java @@ -4,18 +4,20 @@ import me.devnatan.inventoryframework.RootView; import me.devnatan.inventoryframework.VirtualView; import me.devnatan.inventoryframework.context.IFCloseContext; -import me.devnatan.inventoryframework.context.IFContext; +import me.devnatan.inventoryframework.context.PlatformRenderContext; import org.jetbrains.annotations.NotNull; +@SuppressWarnings("unchecked") public final class PlatformCloseInterceptor implements PipelineInterceptor { + @SuppressWarnings("rawtypes") @Override public void intercept(@NotNull PipelineContext pipeline, VirtualView subject) { if (!(subject instanceof IFCloseContext)) return; final IFCloseContext context = (IFCloseContext) subject; - final IFContext parent = context.getParent(); - final RootView root = context.getRoot(); + final PlatformRenderContext parent = (PlatformRenderContext) context.getParent(); + final PlatformView root = parent.getRoot(); tryCallPlatformRootCloseHandler(root, context); if (context.isCancelled()) { @@ -23,12 +25,7 @@ public void intercept(@NotNull PipelineContext pipeline, VirtualVie return; } - parent.removeViewer(context.getViewer()); - - if (canContextBeInvalidated(parent)) { - // TODO invalidate context - root.removeContext(parent); - } + root.removeAndTryInvalidateContext(context.getViewer(), context); } /** @@ -43,14 +40,4 @@ private void tryCallPlatformRootCloseHandler(RootView root, IFCloseContext conte ((PlatformView) root).onClose(context); } } - - /** - * Checks if a context can be invalidated by checking its viewers count. - * - * @param context The subject context. - * @return If context can be invalidated. - */ - private boolean canContextBeInvalidated(IFContext context) { - return context.getViewers().isEmpty(); - } } diff --git a/inventory-framework-platform/src/main/java/me/devnatan/inventoryframework/pipeline/PlatformOpenInterceptor.java b/inventory-framework-platform/src/main/java/me/devnatan/inventoryframework/pipeline/PlatformOpenInterceptor.java index 51ec00e3..553dbeb9 100644 --- a/inventory-framework-platform/src/main/java/me/devnatan/inventoryframework/pipeline/PlatformOpenInterceptor.java +++ b/inventory-framework-platform/src/main/java/me/devnatan/inventoryframework/pipeline/PlatformOpenInterceptor.java @@ -1,5 +1,6 @@ package me.devnatan.inventoryframework.pipeline; +import java.util.HashMap; import me.devnatan.inventoryframework.*; import me.devnatan.inventoryframework.context.IFOpenContext; import me.devnatan.inventoryframework.context.IFRenderContext; @@ -39,6 +40,7 @@ public void intercept(@NotNull PipelineContext pipeline, VirtualVie }); } + @SuppressWarnings({"rawtypes", "unchecked"}) private void finishOpen(@NotNull PipelineContext pipeline, @NotNull IFOpenContext openContext) { if (openContext.isCancelled()) { pipeline.finish(); @@ -47,11 +49,10 @@ private void finishOpen(@NotNull PipelineContext pipeline, @NotNull if (skipOpen) return; - final RootView root = openContext.getRoot(); + final PlatformView root = (PlatformView) openContext.getRoot(); final IFRenderContext render = createRenderContext(openContext); root.addContext(render); root.renderContext(render); - render.getViewers().forEach(render.getContainer()::open); } IFRenderContext createRenderContext(IFOpenContext openContext) { @@ -69,13 +70,19 @@ IFRenderContext createRenderContext(IFOpenContext openContext) { final ElementFactory elementFactory = root.getElementFactory(); final ViewContainer createdContainer = elementFactory.createContainer(openContext); - return elementFactory.createRenderContext( + + final IFRenderContext context = elementFactory.createRenderContext( openContext.getId(), openContext.getRoot(), openContext.getConfig(), createdContainer, - openContext.getIndexedViewers(), + new HashMap<>(), openContext.getSubject(), openContext.getInitialData()); + + openContext.getIndexedViewers().values().forEach(viewer -> { + context.addViewer(viewer.withContext(context)); + }); + return context; } } From fb56b02efab8899ef173e96e6fde9a46ff674d2a Mon Sep 17 00:00:00 2001 From: Natan Vieira do Nascimento Date: Thu, 31 Aug 2023 19:20:52 -0300 Subject: [PATCH 5/6] CME while unregistering ViewFrame --- .../inventoryframework/IFInventoryListener.java | 11 +++++++++-- .../me/devnatan/inventoryframework/ViewFrame.java | 2 +- .../me/devnatan/inventoryframework/IFViewFrame.java | 2 +- 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/inventory-framework-platform-bukkit/src/main/java/me/devnatan/inventoryframework/IFInventoryListener.java b/inventory-framework-platform-bukkit/src/main/java/me/devnatan/inventoryframework/IFInventoryListener.java index 6ae0a10e..825156ba 100644 --- a/inventory-framework-platform-bukkit/src/main/java/me/devnatan/inventoryframework/IFInventoryListener.java +++ b/inventory-framework-platform-bukkit/src/main/java/me/devnatan/inventoryframework/IFInventoryListener.java @@ -15,6 +15,7 @@ import org.bukkit.event.inventory.InventoryDragEvent; import org.bukkit.event.player.PlayerDropItemEvent; import org.bukkit.event.player.PlayerPickupItemEvent; +import org.bukkit.event.server.PluginDisableEvent; import org.bukkit.inventory.PlayerInventory; final class IFInventoryListener implements Listener { @@ -25,18 +26,24 @@ public IFInventoryListener(ViewFrame viewFrame) { this.viewFrame = viewFrame; } + @EventHandler + public void onPluginDisable(final PluginDisableEvent event) { + if (!event.getPlugin().equals(viewFrame.getOwner())) + return; + + viewFrame.unregister(); + } + @EventHandler(priority = EventPriority.LOW, ignoreCancelled = true) public void onInventoryClick(final InventoryClickEvent event) { if (!(event.getWhoClicked() instanceof Player)) return; final Player player = (Player) event.getWhoClicked(); final Viewer viewer = viewFrame.getViewer(player); - System.out.println("viewer = " + viewer); if (viewer == null) return; final IFRenderContext context = viewer.getContext(); final Component clickedComponent = context.getComponent(event.getRawSlot()); - System.out.println("clickedComponent = " + clickedComponent); if (clickedComponent == null || !clickedComponent.isVisible()) return; final ViewContainer clickedContainer = event.getClickedInventory() instanceof PlayerInventory diff --git a/inventory-framework-platform-bukkit/src/main/java/me/devnatan/inventoryframework/ViewFrame.java b/inventory-framework-platform-bukkit/src/main/java/me/devnatan/inventoryframework/ViewFrame.java index 3eb51333..3346b76d 100644 --- a/inventory-framework-platform-bukkit/src/main/java/me/devnatan/inventoryframework/ViewFrame.java +++ b/inventory-framework-platform-bukkit/src/main/java/me/devnatan/inventoryframework/ViewFrame.java @@ -122,7 +122,7 @@ public final void unregister() { // Locks new operations while unregistering setRegistered(false); - final Iterator iterator = getRegisteredViews().values().iterator(); + final Iterator iterator = registeredViews.values().iterator(); while (iterator.hasNext()) { final View view = iterator.next(); try { diff --git a/inventory-framework-platform/src/main/java/me/devnatan/inventoryframework/IFViewFrame.java b/inventory-framework-platform/src/main/java/me/devnatan/inventoryframework/IFViewFrame.java index 49ef3685..ca24ddc8 100644 --- a/inventory-framework-platform/src/main/java/me/devnatan/inventoryframework/IFViewFrame.java +++ b/inventory-framework-platform/src/main/java/me/devnatan/inventoryframework/IFViewFrame.java @@ -10,7 +10,7 @@ abstract class IFViewFrame, V extends PlatformView> { private boolean registered; - private final Map registeredViews = new HashMap<>(); + protected final Map registeredViews = new HashMap<>(); protected final Map viewerById = new HashMap<>(); protected IFViewFrame() {} From 59e1d83ce303fa2f7e35f3b784d02fbb2568a84d Mon Sep 17 00:00:00 2001 From: Natan Vieira do Nascimento Date: Thu, 31 Aug 2023 19:35:26 -0300 Subject: [PATCH 6/6] Setup watches on component render --- .../pipeline/FirstRenderInterceptor.java | 8 +++--- .../IFInventoryListener.java | 11 ++++---- .../runtime/InventoryFramework.java | 27 ------------------- 3 files changed, 8 insertions(+), 38 deletions(-) diff --git a/inventory-framework-core/src/main/java/me/devnatan/inventoryframework/pipeline/FirstRenderInterceptor.java b/inventory-framework-core/src/main/java/me/devnatan/inventoryframework/pipeline/FirstRenderInterceptor.java index 112296b5..f7c64cb8 100644 --- a/inventory-framework-core/src/main/java/me/devnatan/inventoryframework/pipeline/FirstRenderInterceptor.java +++ b/inventory-framework-core/src/main/java/me/devnatan/inventoryframework/pipeline/FirstRenderInterceptor.java @@ -27,6 +27,8 @@ public void intercept(PipelineContext pipeline, VirtualView subject for (int i = componentList.size(); i > 0; i--) { final Component component = componentList.get(i - 1); + // TODO Setup watches on context initialization not on first render + setupWatchers(context, component); context.renderComponent(component); } } @@ -38,11 +40,7 @@ public void intercept(PipelineContext pipeline, VirtualView subject * @param context The context. */ private void registerComponents(IFRenderContext context) { - context.getComponentFactories().stream() - .map(ComponentFactory::create) - // TODO Setup watches on context initialization not on first render - .peek(component -> setupWatchers(context, component)) - .forEach(context::addComponent); + context.getComponentFactories().stream().map(ComponentFactory::create).forEach(context::addComponent); } /** diff --git a/inventory-framework-platform-bukkit/src/main/java/me/devnatan/inventoryframework/IFInventoryListener.java b/inventory-framework-platform-bukkit/src/main/java/me/devnatan/inventoryframework/IFInventoryListener.java index 825156ba..b2ad3f9a 100644 --- a/inventory-framework-platform-bukkit/src/main/java/me/devnatan/inventoryframework/IFInventoryListener.java +++ b/inventory-framework-platform-bukkit/src/main/java/me/devnatan/inventoryframework/IFInventoryListener.java @@ -26,13 +26,12 @@ public IFInventoryListener(ViewFrame viewFrame) { this.viewFrame = viewFrame; } - @EventHandler - public void onPluginDisable(final PluginDisableEvent event) { - if (!event.getPlugin().equals(viewFrame.getOwner())) - return; + @EventHandler + public void onPluginDisable(final PluginDisableEvent event) { + if (!event.getPlugin().equals(viewFrame.getOwner())) return; - viewFrame.unregister(); - } + viewFrame.unregister(); + } @EventHandler(priority = EventPriority.LOW, ignoreCancelled = true) public void onInventoryClick(final InventoryClickEvent event) { diff --git a/inventory-framework-platform-bukkit/src/main/java/me/devnatan/inventoryframework/runtime/InventoryFramework.java b/inventory-framework-platform-bukkit/src/main/java/me/devnatan/inventoryframework/runtime/InventoryFramework.java index 228ef48c..de68108a 100644 --- a/inventory-framework-platform-bukkit/src/main/java/me/devnatan/inventoryframework/runtime/InventoryFramework.java +++ b/inventory-framework-platform-bukkit/src/main/java/me/devnatan/inventoryframework/runtime/InventoryFramework.java @@ -1,35 +1,8 @@ package me.devnatan.inventoryframework.runtime; -import me.devnatan.inventoryframework.View; -import me.devnatan.inventoryframework.ViewConfigBuilder; -import me.devnatan.inventoryframework.ViewFrame; -import me.devnatan.inventoryframework.context.RenderContext; -import org.bukkit.Material; -import org.bukkit.inventory.ItemStack; import org.bukkit.plugin.java.JavaPlugin; -@SuppressWarnings("unused") public final class InventoryFramework extends JavaPlugin { public static final String LIBRARY_VERSION = "3.0.0-rc.2"; - - @Override - public void onEnable() { - ViewFrame vf = ViewFrame.create(this).with(new Test()).register(); - - getServer().getOnlinePlayers().forEach(player -> vf.open(Test.class, player)); - } -} - -class Test extends View { - - @Override - public void onInit(ViewConfigBuilder config) { - config.title("Textando"); - } - - @Override - public void onFirstRender(RenderContext render) { - render.slot(3, new ItemStack(Material.GOLD_INGOT)).cancelOnClick(); - } }