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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions API/src/main/java/fr/maxlego08/menu/api/InventoryManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import com.tcoded.folialib.impl.PlatformScheduler;
import fr.maxlego08.menu.api.button.Button;
import fr.maxlego08.menu.api.button.ButtonOption;
import fr.maxlego08.menu.api.button.GenericPaginationButton;
import fr.maxlego08.menu.api.checker.InventoryRequirementType;
import fr.maxlego08.menu.api.enchantment.Enchantments;
import fr.maxlego08.menu.api.engine.InventoryEngine;
Expand All @@ -12,6 +13,7 @@
import fr.maxlego08.menu.api.font.FontImage;
import fr.maxlego08.menu.api.itemstack.ItemStackSimilar;
import fr.maxlego08.menu.api.loader.MaterialLoader;
import fr.maxlego08.menu.api.pagination.PaginationManager;
import fr.maxlego08.menu.api.utils.Message;
import fr.maxlego08.menu.api.utils.MetaUpdater;
import fr.maxlego08.menu.api.utils.Placeholders;
Expand Down Expand Up @@ -595,4 +597,28 @@ public interface InventoryManager extends Listener {
* <p>
*/
MenuItemStack loadItemStack(File file, String path, Map<String, Object> map);

/**
* Provides access to the pagination manager for handling paginated content in inventories.
*
* <p>The PaginationManager is responsible for managing multi-page inventory displays,
* allowing buttons to paginate through large collections of items or data. It tracks
* the current page for each player and manages navigation between pages.</p>
*
* <p>This is typically used in conjunction with {@link GenericPaginationButton} or
* other paginated button implementations to display collections that exceed a single
* inventory page's capacity.</p>
*
* <p>Example usage:</p>
* <pre>{@code
* PaginationManager manager = inventoryManager.getPaginationManager();
* // Use manager to control pagination state
* }</pre>
*
* @return An instance of {@link PaginationManager} for managing pagination state in inventories.
* @see GenericPaginationButton
* @see PaginationManager
*/
PaginationManager getPaginationManager();

}
3 changes: 3 additions & 0 deletions API/src/main/java/fr/maxlego08/menu/api/MenuPlugin.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import java.io.File;
import java.util.List;
import java.util.Map;
import java.util.Optional;

public interface MenuPlugin extends Plugin {

Expand Down Expand Up @@ -134,6 +135,8 @@ public interface MenuPlugin extends Plugin {
*/
FontImage getFontImage();

Optional<PacketManager> getPacketManager();

/**
* Returns the data manager.
* This method returns the data manager, which is used for managing data related to players.
Expand Down
18 changes: 18 additions & 0 deletions API/src/main/java/fr/maxlego08/menu/api/PacketManager.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package fr.maxlego08.menu.api;

import net.kyori.adventure.text.Component;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;

public interface PacketManager {

void onLoad();

void onEnable();

void onDisable();

void editInventoryTitleName(@NotNull Player player, @NotNull Component title);

void editInventoryTitleName(@NotNull Player player, @NotNull String title);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
package fr.maxlego08.menu.api.button;

import fr.maxlego08.menu.api.pagination.PaginationManager;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;

public abstract class GenericPaginateButton extends PaginateButton {

@NotNull
public abstract String getContextId(@NotNull Player player);


@NotNull
public abstract PaginationManager getPaginationManager();

/**
* Gets the current page for this button (0-based index).
*
* @param player the player
* @return the current page
*/
public final int getCurrentPage(@NotNull Player player) {
return getPaginationManager().getPage(player.getUniqueId(), getContextId(player));
}

/**
* Gets the current page (1-based index) for UI purposes.
*
* @param player the player
* @return the current page (1-based)
*/
public final int getCurrentPageOneIndexed(@NotNull Player player) {
return getCurrentPage(player) + 1;
}

/**
* Sets the current page for this button.
*
* @param player the player
* @param page the page to set (0-based index)
*/
public final void setCurrentPage(@NotNull Player player, int page) {
getPaginationManager().setPage(player.getUniqueId(), getContextId(player), page);
}

/**
* Advances to the next page if available.
*
* @param player the player
* @return true if advanced, false if already at the last page
*/
public final boolean nextPage(@NotNull Player player) {
int currentPage = getCurrentPage(player);
int maxPage = getMaxPage(player);
if (currentPage < maxPage) {
getPaginationManager().nextPage(player.getUniqueId(), getContextId(player));
return true;
}
return false;
}

/**
* Goes to the previous page if available.
*
* @param player the player
* @return true if went back, false if already at the first page
*/
public final boolean previousPage(@NotNull Player player) {
int currentPage = getCurrentPage(player);
if (currentPage > 0) {
getPaginationManager().previousPage(player.getUniqueId(), getContextId(player));
return true;
}
return false;
}

/**
* Resets pagination to the first page.
*
* @param player the player
*/
public final void resetPagination(@NotNull Player player) {
getPaginationManager().reset(player.getUniqueId(), getContextId(player));
}

/**
* Calculates the maximum page number (0-based index).
* This caches the page size to avoid multiple getSlots() calls.
*
* @param player the player
* @return the maximum page
*/
public final int getMaxPage(@NotNull Player player) {
int totalSize = getPaginationSize(player);
int pageSize = getSlots().size();
if (pageSize <= 0) return 0;
return Math.max(0, (totalSize - 1) / pageSize);
}

/**
* Calculates the maximum page number with pre-calculated page size (0-based index).
* Use this when page size is already available to avoid redundant getSlots() calls.
*
* @param player the player
* @param pageSize the page size
* @return the maximum page
*/
public final int getMaxPage(@NotNull Player player, int pageSize) {
if (pageSize <= 0) return 0;
int totalSize = getPaginationSize(player);
return Math.max(0, (totalSize - 1) / pageSize);
}

/**
* Checks if there's a next page available.
*
* @param player the player
* @return true if there's a next page
*/
public final boolean hasNextPage(@NotNull Player player) {
int currentPage = getCurrentPage(player);
return currentPage < getMaxPage(player);
}

/**
* Checks if there's a previous page available.
*
* @param player the player
* @return true if there's a previous page
*/
public final boolean hasPreviousPage(@NotNull Player player) {
return getCurrentPage(player) > 0;
}

}

Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package fr.maxlego08.menu.api.button;

import fr.maxlego08.menu.api.engine.InventoryEngine;
import fr.maxlego08.menu.api.engine.Pagination;
import fr.maxlego08.menu.api.utils.Placeholders;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;

import java.util.Collection;
import java.util.List;

public abstract class GenericPaginationButton <T> extends GenericPaginateButton {

/**
* Gets the list of elements to paginate.
*
* @param player the player
* @return the list of elements
*/
@NotNull
protected abstract List<T> getElements(@NotNull Player player);

/**
* Renders a single element at the given slot.
*
* @param player the player
* @param inventory the inventory engine
* @param slot the inventory slot
* @param element the element to render
* @param placeholders the placeholders
*/
protected abstract void renderElement(
@NotNull Player player,
@NotNull InventoryEngine inventory,
int slot,
@NotNull T element,
@NotNull Placeholders placeholders);

@Override
public final void onRender(@NotNull Player player, @NotNull InventoryEngine inventory) {
List<T> elements = getElements(player);
Collection<Integer> slots = getSlots();
int pageSize = slots.size();
int currentPage = getCurrentPageOneIndexed(player);

int maxPage = getMaxPage(player, pageSize);

getPaginationManager().setMaxPage(player.getUniqueId(), getContextId(player), maxPage);

Pagination<T> pagination = new Pagination<>();
List<T> paginatedElements = pagination.paginate(elements, pageSize, currentPage);

int slotIndex = 0;
for (Integer slot : slots) {
if (slotIndex >= paginatedElements.size()) break;

T element = paginatedElements.get(slotIndex);
Placeholders placeholders = new Placeholders();
placeholders.register("page", String.valueOf(currentPage));
placeholders.register("max_page", String.valueOf(maxPage + 1));

renderElement(player, inventory, slot, element, placeholders);
slotIndex++;
}
}
}


Original file line number Diff line number Diff line change
Expand Up @@ -320,6 +320,9 @@ public class Configuration {
label = "Enable performance debug"
)
public static boolean enablePerformanceDebug = false;

public static List<String> skipCloseActionsOnInventorySwitch = Arrays.asList("inventory", "inv", "back");

public static PerformanceFilterMode performanceFilterMode = PerformanceFilterMode.DISABLED;
public static List<String> performanceFilterOperations = new ArrayList<>();
public static long performanceThresholdMs = 10;
Expand Down Expand Up @@ -416,6 +419,7 @@ public void load(@NotNull FileConfiguration fileConfiguration) {
enablePerformanceDebug = fileConfiguration.getBoolean(ConfigPath.ENABLE_PERFORMANCE_DEBUG.getPath(), false);
performanceThresholdMs = fileConfiguration.getLong(ConfigPath.PERFORMANCE_DEBUG_THRESHOLD_MS.getPath(), 10L);
performanceFilterOperations = fileConfiguration.getStringList(ConfigPath.PERFORMANCE_DEBUG_FILTER_OPERATIONS.getPath());
skipCloseActionsOnInventorySwitch = fileConfiguration.getStringList(ConfigPath.SKIP_CLOSE_ACTIONS_ON_INVENTORY_SWITCH.getPath());
try {
performanceFilterMode = PerformanceFilterMode.valueOf(fileConfiguration.getString(ConfigPath.PERFORMANCE_DEBUG_FILTER_MODE.getPath(), PerformanceFilterMode.DISABLED.name()).toUpperCase());
} catch (IllegalArgumentException e) {
Expand Down Expand Up @@ -470,6 +474,7 @@ public void save(@NotNull FileConfiguration fileConfiguration,@NotNull File file
fileConfiguration.set(ConfigPath.ENABLE_PLAYER_COMMANDS_AS_OP_ACTION.getPath(), enablePlayerCommandsAsOPAction);
fileConfiguration.set(ConfigPath.OP_GRANT_METHOD.getPath(), opGrantMethod.name());
fileConfiguration.set(ConfigPath.ENABLE_TOAST.getPath(), enableToast);
fileConfiguration.set(ConfigPath.SKIP_CLOSE_ACTIONS_ON_INVENTORY_SWITCH.getPath(), skipCloseActionsOnInventorySwitch);
fileConfiguration.set(ConfigPath.ENABLE_PACKET_EVENT_CLICK_LIMITER.getPath(), enablePacketEventClickLimiter);
fileConfiguration.set(ConfigPath.PACKET_EVENT_CLICK_LIMITER_MILLISECONDS.getPath(), packetEventClickLimiterMilliseconds);
fileConfiguration.set(ConfigPath.ENABLE_PERFORMANCE_DEBUG.getPath(), enablePerformanceDebug);
Expand Down Expand Up @@ -526,6 +531,7 @@ private enum ConfigPath {
ENABLE_PLAYER_COMMANDS_AS_OP_ACTION("enable-player-commands-as-op-action"),
OP_GRANT_METHOD("op-grant-method"),
ENABLE_TOAST("enable-toast"),
SKIP_CLOSE_ACTIONS_ON_INVENTORY_SWITCH("skip-close-actions-on-inventory-switch"),

ENABLE_PACKET_EVENT_CLICK_LIMITER("enable-packet-event-click-limiter"),
PACKET_EVENT_CLICK_LIMITER_MILLISECONDS("packet-event-click-limiter-milliseconds"),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,35 +10,34 @@
import org.jetbrains.annotations.Nullable;

import java.util.List;
import java.util.Optional;

public class CustomModelDataComponent extends ItemComponent {
private final @NotNull Optional<@NotNull List<@NotNull Color>> colors;
private final @NotNull Optional<@NotNull List<@NotNull Boolean>> flags;
private final @NotNull Optional<@NotNull List<@NotNull Float>> floats;
private final @NotNull Optional<@NotNull List<@NotNull String>> string;
private final @NotNull List<@NotNull Color> colors;
private final @NotNull List<@NotNull Boolean> flags;
private final @NotNull List<@NotNull Float> floats;
private final @NotNull List<@NotNull String> strings;

public CustomModelDataComponent(@NotNull Optional<@NotNull List<@NotNull Color>> colors, @NotNull Optional<@NotNull List<@NotNull Boolean>> flags, @NotNull Optional<@NotNull List<@NotNull Float>> floats, @NotNull Optional<@NotNull List<@NotNull String>> string) {
public CustomModelDataComponent(@NotNull List<@NotNull Color> colors, @NotNull List<@NotNull Boolean> flags, @NotNull List<@NotNull Float> floats, @NotNull List<@NotNull String> strings) {
this.colors = colors;
this.flags = flags;
this.floats = floats;
this.string = string;
this.strings = strings;
}

public @NotNull Optional<@NotNull List<@NotNull Color>> getColors() {
public @NotNull List<@NotNull Color> getColors() {
return colors;
}

public @NotNull Optional<@NotNull List<@NotNull Boolean>> getFlags() {
return flags;
public @NotNull List<@NotNull Boolean> getFlags() {
return this.flags;
}

public @NotNull Optional<@NotNull List<@NotNull Float>> getFloats() {
return floats;
public @NotNull List<@NotNull Float> getFloats() {
return this.floats;
}

public @NotNull Optional<@NotNull List<@NotNull String>> getString() {
return string;
public @NotNull List<@NotNull String> getStrings() {
return this.strings;
}

@Override
Expand All @@ -47,10 +46,18 @@ public void apply(@NotNull BuildContext context, @NotNull ItemStack itemStack, @
if (itemMeta != null) {
org.bukkit.inventory.meta.components.CustomModelDataComponent customModelDataComponent = itemMeta.getCustomModelDataComponent();

this.colors.ifPresent(customModelDataComponent::setColors);
this.flags.ifPresent(customModelDataComponent::setFlags);
this.floats.ifPresent(customModelDataComponent::setFloats);
this.string.ifPresent(customModelDataComponent::setStrings);
if (!this.colors.isEmpty()) {
customModelDataComponent.setColors(this.colors);
}
if (!this.flags.isEmpty()) {
customModelDataComponent.setFlags(this.flags);
}
if (!this.floats.isEmpty()) {
customModelDataComponent.setFloats(this.floats);
}
if (!this.strings.isEmpty()) {
customModelDataComponent.setStrings(this.strings);
}

itemStack.setItemMeta(itemMeta);
}
Expand Down
Loading
Loading