diff --git a/SpigotCore/SpigotCore_Main/src/de/steamwar/inventory/ui/StatefulUIInventory.java b/SpigotCore/SpigotCore_Main/src/de/steamwar/inventory/ui/StatefulUIInventory.java new file mode 100644 index 00000000..4dbb2fa8 --- /dev/null +++ b/SpigotCore/SpigotCore_Main/src/de/steamwar/inventory/ui/StatefulUIInventory.java @@ -0,0 +1,70 @@ +/* + * This file is a part of the SteamWar software. + * + * Copyright (C) 2025 SteamWar.de-Serverteam + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package de.steamwar.inventory.ui; + +import de.steamwar.inventory.ui.util.UpdateStream; +import org.bukkit.entity.Player; + +public abstract class StatefulUIInventory extends UIInventory implements UpdateStream.Subscriber { + + private T state; + private final UpdateStream stream; + private UpdateStream.Unsubscriber unsubscriber; + + protected abstract UIComponent render(T state); + protected abstract void update(T state); + + public StatefulUIInventory(UpdateStream stateObservable) { + this.stream = stateObservable; + } + + public void dispose() { + unsubscriber.unsubscribe(); + this.unsubscriber = null; + } + + @Override + public UIComponent render() { + return render(state); + } + + @Override + public void onUpdate(T current) { + state = current; + update(current); + updateUI(); + } + + @Override + void onClose() { + if (viewers.isEmpty()) { + dispose(); + } + } + + @Override + public void addViewer(Player... players) { + if (viewers.isEmpty()) { + unsubscriber = stream.subscribe(this); + } + + super.addViewer(players); + } +} diff --git a/SpigotCore/SpigotCore_Main/src/de/steamwar/inventory/ui/UIComponent.java b/SpigotCore/SpigotCore_Main/src/de/steamwar/inventory/ui/UIComponent.java new file mode 100644 index 00000000..d0206018 --- /dev/null +++ b/SpigotCore/SpigotCore_Main/src/de/steamwar/inventory/ui/UIComponent.java @@ -0,0 +1,82 @@ +/* + * This file is a part of the SteamWar software. + * + * Copyright (C) 2025 SteamWar.de-Serverteam + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package de.steamwar.inventory.ui; + +import de.steamwar.inventory.ui.item.Enchanted; +import de.steamwar.inventory.ui.item.OnClick; +import de.steamwar.inventory.ui.layout.FixedSizeContainer; +import de.steamwar.inventory.ui.util.*; +import org.bukkit.event.inventory.InventoryClickEvent; + +import java.util.function.Consumer; +import java.util.function.Function; + +public interface UIComponent { + ComponentRender render(RenderContext context); + + default UIComponent onClick(Consumer event) { + return onClick(false, event); + } + + default UIComponent onClick(boolean allowDoubleClick, Consumer event) { + return new OnClick(this, allowDoubleClick, inventoryClickEvent -> { + event.accept(inventoryClickEvent); + return true; + }); + } + + default UIComponent onClickWithUpdate(Function event) { + return onClickWithUpdate(false, event); + } + + default UIComponent onClickWithUpdate(boolean allowDoubleClick, Function event) { + return new OnClick(this, allowDoubleClick, event); + } + + default UIComponent onClickNoUpdate(Consumer event) { + return onClickNoUpdate(false, event); + } + + default UIComponent onClickNoUpdate(boolean allowDoubleClick, Consumer event) { + return new OnClick(this, allowDoubleClick, inventoryClickEvent -> { + event.accept(inventoryClickEvent); + return false; + }); + } + + default UIComponent cancelOnClick() { + return new OnClick(this, true, inventoryClickEvent -> false); + } + + default UIComponent enchanted() { + return new Enchanted(this); + } + + default UIComponent enchanted(boolean enchanted) { + if (enchanted) + return enchanted(); + else + return this; + } + + default UIComponent size(Size size) { + return new FixedSizeContainer(size, this); + } +} diff --git a/SpigotCore/SpigotCore_Main/src/de/steamwar/inventory/ui/UIFragment.java b/SpigotCore/SpigotCore_Main/src/de/steamwar/inventory/ui/UIFragment.java new file mode 100644 index 00000000..3e5a6583 --- /dev/null +++ b/SpigotCore/SpigotCore_Main/src/de/steamwar/inventory/ui/UIFragment.java @@ -0,0 +1,33 @@ +/* + * This file is a part of the SteamWar software. + * + * Copyright (C) 2025 SteamWar.de-Serverteam + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package de.steamwar.inventory.ui; + +import de.steamwar.inventory.ui.util.ComponentRender; +import de.steamwar.inventory.ui.util.RenderContext; + +public abstract class UIFragment implements UIComponent { + + abstract public UIComponent build(RenderContext context); + + @Override + public ComponentRender render(RenderContext context) { + return build(context).render(context); + } +} diff --git a/SpigotCore/SpigotCore_Main/src/de/steamwar/inventory/ui/UIInventory.java b/SpigotCore/SpigotCore_Main/src/de/steamwar/inventory/ui/UIInventory.java new file mode 100644 index 00000000..1a828cab --- /dev/null +++ b/SpigotCore/SpigotCore_Main/src/de/steamwar/inventory/ui/UIInventory.java @@ -0,0 +1,166 @@ +/* + * This file is a part of the SteamWar software. + * + * Copyright (C) 2025 SteamWar.de-Serverteam + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package de.steamwar.inventory.ui; + +import de.steamwar.core.Core; +import de.steamwar.inventory.ui.util.ComponentRender; +import de.steamwar.inventory.ui.util.RenderContext; +import de.steamwar.inventory.ui.util.RenderItem; +import de.steamwar.inventory.ui.util.Size; +import de.steamwar.message.Message; +import lombok.AllArgsConstructor; +import lombok.Setter; +import org.bukkit.entity.Item; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.inventory.InventoryClickEvent; +import org.bukkit.event.inventory.InventoryCloseEvent; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.InventoryView; +import org.bukkit.inventory.ItemStack; + +import java.util.*; +import java.util.function.Consumer; +import java.util.function.Function; +import java.util.function.Supplier; +import java.util.stream.Collectors; + +public abstract class UIInventory { + protected final List viewers = new ArrayList<>(); + private final Map callbacks = new HashMap<>(); + + private UIInventory parent; + private Player currentRender; + + public abstract UIComponent render(); + public abstract Inventory createInventory(Player player); + public abstract Message getMessages(); + + void onClose() { } + + public void addViewer(Player... players) { + viewers.addAll(Arrays.asList(players)); + + for (Player player : players) { + openInventory(player); + } + } + + private void openInventory(Player player) { + Inventory inventory = createInventory(player); + renderPlayer(player, inventory); + InventoryView view = player.openInventory(inventory); + Core.getInstance().getLogger().info("[UIINV] Opened " + view.getTitle() + " for " + player.getName()); + } + + private void renderPlayer(Player player, Inventory inventory) { + int width = Math.min(inventory.getSize(), 9); + int height = Math.max(inventory.getSize() / 9, 1); + + currentRender = player; + ComponentRender render = render().render(new RenderContext(new Size(width, height), new Size(width, height), this)); + currentRender = null; + + if (render.getSize().getWidth() > width || render.getSize().getHeight() > height) { + throw new IllegalArgumentException("The UIComponent does not fit in the inventory"); + } + + Message message = getMessages(); + + ItemStack[] itemStacks = new ItemStack[inventory.getSize()]; + + render.getItems().forEach(item -> itemStacks[item.getX() + item.getY() * 9] = item.getItemStack().message(message).build(player)); + + inventory.setContents(itemStacks); + + Map> clickEventMap = render.getItems().stream() + .filter(item -> item.getClickEvent() != null) + .collect(Collectors.toMap(renderItem -> renderItem.getX() + renderItem.getY() * 9, RenderItem::getClickEvent)); + + callbacks.computeIfAbsent(player, p -> new OpenPlayerInventory(p, clickEventMap).register()).setCallbacks(clickEventMap); + } + + protected void updateTitle(String title, Object... params) { + if (currentRender != null) { + currentRender.getOpenInventory().setTitle(getMessages().parse(title, currentRender, params)); + } + } + + protected void updateUI() { + viewers.forEach(player -> renderPlayer(player, player.getOpenInventory().getTopInventory())); + } + + protected void push(Player player, UIInventory inventory) { + inventory.parent = this; + inventory.addViewer(player); + viewers.remove(player); + } + + protected void pop(Player player) { + if(parent == null) + return; + + parent.addViewer(player); + } + + @AllArgsConstructor + @Setter + private class OpenPlayerInventory implements Listener { + private final Player player; + private Map> callbacks; + + OpenPlayerInventory register() { + Core.getInstance().getServer().getPluginManager().registerEvents(this, Core.getInstance()); + return this; + } + + @EventHandler + public void onInventoryClick(InventoryClickEvent e) { + if (!player.equals(e.getWhoClicked())) + return; + + if (callbacks.containsKey(e.getRawSlot()) && callbacks.get(e.getRawSlot()) != null) { + e.setCancelled(true); + Core.getInstance().getLogger().info("[UIINV] " + e.getWhoClicked().getName() + " " + e.getClick().name() + " clicked " + e.getRawSlot() + " on " + (e.getCurrentItem() != null ? e.getCurrentItem().getItemMeta().getDisplayName() : "[EMPTY]") + " in " + e.getView().getTitle()); + if (callbacks.get(e.getRawSlot()).apply(e)) { + updateUI(); + } + } + } + + @EventHandler + public void onInventoryClose(InventoryCloseEvent e){ + if(!player.equals(e.getPlayer())) + return; + + InventoryClickEvent.getHandlerList().unregister(this); + InventoryCloseEvent.getHandlerList().unregister(this); + Core.getInstance().getLogger().info("[UIINV] " + player.getName() + " closed " + e.getView().getTitle()); + if(callbacks.containsKey(-1)) + callbacks.get(-1).apply(null); + + viewers.remove(player); + UIInventory.this.callbacks.remove(player); + + onClose(); + } + } +} diff --git a/SpigotCore/SpigotCore_Main/src/de/steamwar/inventory/ui/fragments/ActionBar.java b/SpigotCore/SpigotCore_Main/src/de/steamwar/inventory/ui/fragments/ActionBar.java new file mode 100644 index 00000000..e4404c85 --- /dev/null +++ b/SpigotCore/SpigotCore_Main/src/de/steamwar/inventory/ui/fragments/ActionBar.java @@ -0,0 +1,62 @@ +/* + * This file is a part of the SteamWar software. + * + * Copyright (C) 2025 SteamWar.de-Serverteam + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package de.steamwar.inventory.ui.fragments; + +import de.steamwar.inventory.ui.UIComponent; +import de.steamwar.inventory.ui.UIFragment; +import de.steamwar.inventory.ui.item.ItemBuilder; +import de.steamwar.inventory.ui.item.UIItem; +import de.steamwar.inventory.ui.layout.Row; +import de.steamwar.inventory.ui.util.RenderContext; +import de.steamwar.inventory.ui.util.Size; +import lombok.Getter; +import lombok.Setter; +import org.bukkit.Material; + +import java.util.Arrays; +import java.util.Map; + +@Getter +@Setter +public class ActionBar extends UIFragment { + private static final UIComponent DEFAULT_FILLER = new UIItem(ItemBuilder.of(Material.GRAY_STAINED_GLASS_PANE).displayName("§e")).cancelOnClick(); + + private final Map actionBar; + private UIComponent filler = DEFAULT_FILLER; + + public ActionBar(Map actionBar) { + this.actionBar = actionBar; + } + + public ActionBar filler(UIComponent filler) { + this.filler = filler; + return this; + } + + @Override + public UIComponent build(RenderContext context) { + UIComponent[] components = new UIComponent[context.getContainerSize().getWidth()]; + for (int i = 0; i < components.length; i++) { + components[i] = actionBar.getOrDefault(i, filler); + } + + return new Row(Arrays.asList(components)).size(new Size(context.getContainerSize().getWidth(), 1)); + } +} diff --git a/SpigotCore/SpigotCore_Main/src/de/steamwar/inventory/ui/fragments/HorizontalScrollLayout.java b/SpigotCore/SpigotCore_Main/src/de/steamwar/inventory/ui/fragments/HorizontalScrollLayout.java new file mode 100644 index 00000000..456bf46c --- /dev/null +++ b/SpigotCore/SpigotCore_Main/src/de/steamwar/inventory/ui/fragments/HorizontalScrollLayout.java @@ -0,0 +1,93 @@ +/* + * This file is a part of the SteamWar software. + * + * Copyright (C) 2025 SteamWar.de-Serverteam + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package de.steamwar.inventory.ui.fragments; + +import de.steamwar.core.Core; +import de.steamwar.inventory.ui.UIComponent; +import de.steamwar.inventory.ui.UIFragment; +import de.steamwar.inventory.ui.item.ItemBuilder; +import de.steamwar.inventory.ui.item.UIItem; +import de.steamwar.inventory.ui.layout.Column; +import de.steamwar.inventory.ui.layout.FixedSizeContainer; +import de.steamwar.inventory.ui.layout.Row; +import de.steamwar.inventory.ui.util.RenderContext; +import lombok.Setter; +import org.bukkit.DyeColor; + +import java.util.*; + +@Setter +public class HorizontalScrollLayout extends UIFragment { + private List data; + private int scroll = 0; + private final Map actionBar = new HashMap<>(); + + public HorizontalScrollLayout(List data) { + this.data = data; + } + + private int maxScroll() { + return Math.max(0, Math.min(scroll, data.size() - 9 + 1)); + } + + public HorizontalScrollLayout actionBar(Map actionBar) { + this.actionBar.clear(); + this.actionBar.putAll(actionBar); + return this; + } + + public HorizontalScrollLayout data(List data) { + this.data = data; + return this; + } + + @Override + public UIComponent build(RenderContext context) { + scroll = maxScroll(); + boolean hasPrev = scroll > 0; + boolean hasNext = data.size() - scroll > 9; + + Map actionBar = new HashMap<>(this.actionBar); + + actionBar.put(0, new UIItem(ItemBuilder.color(hasPrev ? DyeColor.LIME : DyeColor.GRAY).message(Core.MESSAGE).translateName(hasPrev ? "SWLISINV_PREVIOUS_PAGE_ACTIVE" : "SWLISINV_PREVIOUS_PAGE_INACTIVE")).onClickWithUpdate(inventoryClickEvent -> { + if (scroll > 0) { + scroll = Math.max(0, scroll - 9); + return true; + } else { + return false; + } + })); + actionBar.put(8, new UIItem(ItemBuilder.color(hasNext ? DyeColor.LIME : DyeColor.GRAY).message(Core.MESSAGE).translateName(hasNext ? "SWLISINV_NEXT_PAGE_ACTIVE" : "SWLISINV_NEXT_PAGE_INACTIVE")).onClickWithUpdate(inventoryClickEvent -> { + if (hasNext) { + scroll = Math.min(scroll + 9, data.size() - 9); + return true; + } else { + return false; + } + })); + + return new Column( + new FixedSizeContainer(context.getContainerSize().shrink(0, 1), + new Row(data.subList(scroll, Math.min(data.size(), scroll + 9))) + ), + new ActionBar(actionBar) + ); + } +} diff --git a/SpigotCore/SpigotCore_Main/src/de/steamwar/inventory/ui/fragments/PageLayout.java b/SpigotCore/SpigotCore_Main/src/de/steamwar/inventory/ui/fragments/PageLayout.java new file mode 100644 index 00000000..d17a83ba --- /dev/null +++ b/SpigotCore/SpigotCore_Main/src/de/steamwar/inventory/ui/fragments/PageLayout.java @@ -0,0 +1,105 @@ +/* + * This file is a part of the SteamWar software. + * + * Copyright (C) 2025 SteamWar.de-Serverteam + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package de.steamwar.inventory.ui.fragments; + +import de.steamwar.core.Core; +import de.steamwar.inventory.ui.UIComponent; +import de.steamwar.inventory.ui.UIFragment; +import de.steamwar.inventory.ui.item.ItemBuilder; +import de.steamwar.inventory.ui.item.UIItem; +import de.steamwar.inventory.ui.layout.Column; +import de.steamwar.inventory.ui.layout.FixedSizeContainer; +import de.steamwar.inventory.ui.layout.Grid; +import de.steamwar.inventory.ui.util.RenderContext; +import lombok.Data; +import lombok.Getter; +import lombok.Setter; +import org.bukkit.DyeColor; +import org.bukkit.event.inventory.ClickType; + +import java.util.*; +import java.util.function.Consumer; + +@Getter +@Setter +public class PageLayout extends UIFragment { + + private final List> items; + private final UIListCallback callback; + private int page = 0; + private Consumer onPageChange = null; + private Map itemsOnBar = Collections.emptyMap(); + + public PageLayout(List> items, UIListCallback callback) { + System.out.println(items.size()); + this.items = items; + this.callback = callback; + } + + @Override + public UIComponent build(RenderContext context) { + List uiItems = new ArrayList<>(); + + int itemsOnPage = context.getContainerSize().getWidth() * (context.getContainerSize().getHeight() - 1); + + int ipageLimit = items.size() - page * itemsOnPage; + if (ipageLimit > itemsOnPage) { + ipageLimit = itemsOnPage; + } + + int i = page * itemsOnPage; + for (int ipage = 0; ipage < ipageLimit; ipage++) { + UIListEntry e = items.get(i); + + uiItems.add(new UIItem(e.item).onClickNoUpdate(inventoryClickEvent -> callback.clicked(inventoryClickEvent.getClick(), e.object))); + i++; + } + + Map uiBar = new HashMap<>(itemsOnBar); + + if (page != 0) { + uiBar.put(0, new UIItem(ItemBuilder.color(DyeColor.LIME).message(Core.MESSAGE).translateName("SWLISINV_PREVIOUS_PAGE_ACTIVE")).onClick(inventoryClickEvent -> page--)); + } else { + uiBar.put(0, new UIItem(ItemBuilder.color(DyeColor.GRAY).message(Core.MESSAGE).translateName("SWLISINV_PREVIOUS_PAGE_INACTIVE")).cancelOnClick()); + } + if (page < items.size() / itemsOnPage - (items.size() % itemsOnPage == 0 ? 1 : 0)) { + uiBar.put(8, new UIItem(ItemBuilder.color(DyeColor.LIME).message(Core.MESSAGE).translateName("SWLISINV_NEXT_PAGE_ACTIVE")).onClick(inventoryClickEvent -> page++)); + } else { + uiBar.put(8, new UIItem(ItemBuilder.color(DyeColor.GRAY).message(Core.MESSAGE).translateName("SWLISINV_NEXT_PAGE_INACTIVE")).cancelOnClick()); + } + + return new Column( + new FixedSizeContainer(context.getContainerSize().shrink(0, 1), + new Grid(uiItems)), + new ActionBar(uiBar) + ); + } + + + public interface UIListCallback{ + void clicked(ClickType click, T element); + } + + @Data + public static class UIListEntry{ + final ItemBuilder item; + final T object; + } +} diff --git a/SpigotCore/SpigotCore_Main/src/de/steamwar/inventory/ui/item/Enchanted.java b/SpigotCore/SpigotCore_Main/src/de/steamwar/inventory/ui/item/Enchanted.java new file mode 100644 index 00000000..69f12695 --- /dev/null +++ b/SpigotCore/SpigotCore_Main/src/de/steamwar/inventory/ui/item/Enchanted.java @@ -0,0 +1,44 @@ +/* + * This file is a part of the SteamWar software. + * + * Copyright (C) 2025 SteamWar.de-Serverteam + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package de.steamwar.inventory.ui.item; + +import de.steamwar.inventory.ui.UIComponent; +import de.steamwar.inventory.ui.util.ComponentRender; +import de.steamwar.inventory.ui.util.RenderContext; +import de.steamwar.inventory.ui.util.RenderItem; + +public class Enchanted implements UIComponent { + private final UIComponent children; + + public Enchanted(UIComponent children) { + this.children = children; + } + + @Override + public ComponentRender render(RenderContext context) { + ComponentRender render = children.render(context); + + for (RenderItem item : render.getItems()) { + item.getItemStack().enchanted(); + } + + return render; + } +} diff --git a/SpigotCore/SpigotCore_Main/src/de/steamwar/inventory/ui/item/ItemBuilder.java b/SpigotCore/SpigotCore_Main/src/de/steamwar/inventory/ui/item/ItemBuilder.java new file mode 100644 index 00000000..351b4fe6 --- /dev/null +++ b/SpigotCore/SpigotCore_Main/src/de/steamwar/inventory/ui/item/ItemBuilder.java @@ -0,0 +1,186 @@ +/* + * This file is a part of the SteamWar software. + * + * Copyright (C) 2025 SteamWar.de-Serverteam + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package de.steamwar.inventory.ui.item; + +import de.steamwar.core.FlatteningWrapper; +import de.steamwar.core.TrickyTrialsWrapper; +import de.steamwar.inventory.SWItem; +import de.steamwar.message.Message; +import de.steamwar.message.SubMessage; +import org.bukkit.DyeColor; +import org.bukkit.Material; +import org.bukkit.OfflinePlayer; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemFlag; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; + +import java.util.*; +import java.util.stream.Collectors; + +public class ItemBuilder { + private static final Map DYE_COLOR_INTEGER_MAP = new EnumMap<>(DyeColor.class); + + static { + DYE_COLOR_INTEGER_MAP.put(DyeColor.BLACK, 16); + DYE_COLOR_INTEGER_MAP.put(DyeColor.RED, 1); + DYE_COLOR_INTEGER_MAP.put(DyeColor.GREEN, 2); + DYE_COLOR_INTEGER_MAP.put(DyeColor.BROWN, 3); + DYE_COLOR_INTEGER_MAP.put(DyeColor.BLUE, 4); + DYE_COLOR_INTEGER_MAP.put(DyeColor.PURPLE, 5); + DYE_COLOR_INTEGER_MAP.put(DyeColor.CYAN, 6); + DYE_COLOR_INTEGER_MAP.put(DyeColor.LIGHT_GRAY, 7); + DYE_COLOR_INTEGER_MAP.put(DyeColor.GRAY, 8); + DYE_COLOR_INTEGER_MAP.put(DyeColor.PINK, 9); + DYE_COLOR_INTEGER_MAP.put(DyeColor.LIME, 10); + DYE_COLOR_INTEGER_MAP.put(DyeColor.YELLOW, 11); + DYE_COLOR_INTEGER_MAP.put(DyeColor.LIGHT_BLUE, 12); + DYE_COLOR_INTEGER_MAP.put(DyeColor.MAGENTA, 13); + DYE_COLOR_INTEGER_MAP.put(DyeColor.ORANGE, 14); + DYE_COLOR_INTEGER_MAP.put(DyeColor.WHITE, 15); + } + + private final ItemStack itemStack; + private final ItemMeta itemMeta; + private Message message = null; + private SubMessage translateName = null; + private List translateLore = null; + + private ItemBuilder(Material material) { + this(new ItemStack(material)); + } + private ItemBuilder(ItemStack item) { + this.itemStack = item; + this.itemMeta = itemStack.getItemMeta(); + } + + public static ItemBuilder of(Material material) { + return new ItemBuilder(material); + } + + public static ItemBuilder of(ItemStack itemStack) { + return new ItemBuilder(itemStack.getType()).amount(itemStack.getAmount()); + } + + private static ItemBuilder of(Material material, Byte meta) { + ItemStack itemStack; + try { + itemStack = new ItemStack(material, 1, (short)0, meta); + } catch (IllegalArgumentException e) { + itemStack = new ItemStack(material, 1); + } + + return new ItemBuilder(itemStack); + } + + public static ItemBuilder color(DyeColor color) { + return ItemBuilder.of(SWItem.getDye(DYE_COLOR_INTEGER_MAP.get(color)), DYE_COLOR_INTEGER_MAP.get(color).byteValue()); + } + + public static ItemBuilder skull(OfflinePlayer player) { + return skull(player.getName()); + } + + public static ItemBuilder skull(String name) { + return ItemBuilder.of(FlatteningWrapper.impl.setSkullOwner(name)); + } + + public ItemBuilder message(Message message) { + if (this.message == null) { + this.message = message; + } + return this; + } + + public ItemStack build(Player player) { + if (message != null) { + if (translateName != null) { + itemMeta.setDisplayName(message.parse(translateName.getMessage(), player, translateName.getParams())); + } + if (translateLore != null) { + itemMeta.setLore(translateLore.stream().map(subMessage -> message.parse(subMessage.getMessage(), player, subMessage.getParams())).collect(Collectors.toList())); + } + } + itemStack.setItemMeta(itemMeta); + return itemStack; + } + + public ItemBuilder amount(int amount) { + itemStack.setAmount(amount); + return this; + } + + public ItemBuilder displayName(String name) { + itemMeta.setDisplayName(name); + return this; + } + + public ItemBuilder lore(String... lore) { + if (itemMeta.hasLore()) { + List loreList = itemMeta.getLore(); + loreList.addAll(Arrays.asList(lore)); + itemMeta.setLore(loreList); + } else { + itemMeta.setLore(Arrays.asList(lore)); + } + + return this; + } + + public ItemBuilder translateName(String name, Object... params) { + this.translateName = new SubMessage(name, params); + return this; + } + + public ItemBuilder addTranslateLore(SubMessage... lore) { + if (this.translateLore == null) { + this.translateLore = Arrays.asList(lore); + } else { + this.translateLore.addAll(Arrays.asList(lore)); + } + + return this; + } + + public ItemBuilder enchanted() { + itemMeta.addEnchant(TrickyTrialsWrapper.impl.getUnbreakingEnchantment(), 10, true); + return this; + } + + public ItemBuilder enchanted(boolean enchanted) { + if(enchanted) { + return enchanted(); + } else { + return this; + } + } + + public ItemBuilder hideFlags() { + for (ItemFlag flag : EnumSet.allOf(ItemFlag.class)) { + itemMeta.addItemFlags(flag); + } + return this; + } + + public ItemBuilder customModel(int modelData) { + itemMeta.setCustomModelData(modelData); + return this; + } +} diff --git a/SpigotCore/SpigotCore_Main/src/de/steamwar/inventory/ui/item/OnClick.java b/SpigotCore/SpigotCore_Main/src/de/steamwar/inventory/ui/item/OnClick.java new file mode 100644 index 00000000..f6d3e203 --- /dev/null +++ b/SpigotCore/SpigotCore_Main/src/de/steamwar/inventory/ui/item/OnClick.java @@ -0,0 +1,59 @@ +/* + * This file is a part of the SteamWar software. + * + * Copyright (C) 2025 SteamWar.de-Serverteam + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package de.steamwar.inventory.ui.item; + +import de.steamwar.inventory.ui.UIComponent; +import de.steamwar.inventory.ui.util.ComponentRender; +import de.steamwar.inventory.ui.util.RenderContext; +import de.steamwar.inventory.ui.util.RenderItem; +import org.bukkit.event.inventory.ClickType; +import org.bukkit.event.inventory.InventoryClickEvent; + +import java.util.function.Consumer; +import java.util.function.Function; + +public class OnClick implements UIComponent { + private final UIComponent children; + private final boolean allowDoubleClick; + private final Function event; + + public OnClick(UIComponent children, boolean allowDoubleClick, Function event) { + this.children = children; + this.allowDoubleClick = allowDoubleClick; + this.event = event; + } + + @Override + public ComponentRender render(RenderContext context) { + ComponentRender render = children.render(context); + + for (RenderItem item : render.getItems()) { + item.setClickEvent(ev -> { + if (ev.getClick() != ClickType.DOUBLE_CLICK || allowDoubleClick) { + return event.apply(ev); + } + + return false; + }); + } + + return render; + } +} diff --git a/SpigotCore/SpigotCore_Main/src/de/steamwar/inventory/ui/item/Space.java b/SpigotCore/SpigotCore_Main/src/de/steamwar/inventory/ui/item/Space.java new file mode 100644 index 00000000..d947966c --- /dev/null +++ b/SpigotCore/SpigotCore_Main/src/de/steamwar/inventory/ui/item/Space.java @@ -0,0 +1,37 @@ +/* + * This file is a part of the SteamWar software. + * + * Copyright (C) 2025 SteamWar.de-Serverteam + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package de.steamwar.inventory.ui.item; + +import de.steamwar.inventory.ui.UIComponent; +import de.steamwar.inventory.ui.util.ComponentRender; +import de.steamwar.inventory.ui.util.RenderContext; +import de.steamwar.inventory.ui.util.Size; + +import java.util.ArrayList; + +public class Space implements UIComponent { + @Override + public ComponentRender render(RenderContext context) { + return new ComponentRender( + new ArrayList<>(), + new Size(1, 1) + ); + } +} diff --git a/SpigotCore/SpigotCore_Main/src/de/steamwar/inventory/ui/item/UIItem.java b/SpigotCore/SpigotCore_Main/src/de/steamwar/inventory/ui/item/UIItem.java new file mode 100644 index 00000000..cdc7de01 --- /dev/null +++ b/SpigotCore/SpigotCore_Main/src/de/steamwar/inventory/ui/item/UIItem.java @@ -0,0 +1,47 @@ +/* + * This file is a part of the SteamWar software. + * + * Copyright (C) 2025 SteamWar.de-Serverteam + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package de.steamwar.inventory.ui.item; + +import de.steamwar.inventory.ui.UIComponent; +import de.steamwar.inventory.ui.util.*; +import org.bukkit.inventory.ItemStack; + +import java.util.Arrays; + +public class UIItem implements UIComponent { + + private final ItemBuilder itemStack; + + public UIItem(ItemStack itemStack) { + this(ItemBuilder.of(itemStack)); + } + + public UIItem(ItemBuilder itemStack) { + this.itemStack = itemStack; + } + + @Override + public ComponentRender render(RenderContext context) { + return new ComponentRender( + Arrays.asList(new RenderItem(0, 0, itemStack, null)), + new Size(1, 1) + ); + } +} diff --git a/SpigotCore/SpigotCore_Main/src/de/steamwar/inventory/ui/layout/Alignment.java b/SpigotCore/SpigotCore_Main/src/de/steamwar/inventory/ui/layout/Alignment.java new file mode 100644 index 00000000..3c3bf755 --- /dev/null +++ b/SpigotCore/SpigotCore_Main/src/de/steamwar/inventory/ui/layout/Alignment.java @@ -0,0 +1,27 @@ +/* + * This file is a part of the SteamWar software. + * + * Copyright (C) 2025 SteamWar.de-Serverteam + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package de.steamwar.inventory.ui.layout; + +public enum Alignment { + START, + CENTER, + END, + BETWEEN +} diff --git a/SpigotCore/SpigotCore_Main/src/de/steamwar/inventory/ui/layout/Column.java b/SpigotCore/SpigotCore_Main/src/de/steamwar/inventory/ui/layout/Column.java new file mode 100644 index 00000000..a17abaf7 --- /dev/null +++ b/SpigotCore/SpigotCore_Main/src/de/steamwar/inventory/ui/layout/Column.java @@ -0,0 +1,93 @@ +/* + * This file is a part of the SteamWar software. + * + * Copyright (C) 2025 SteamWar.de-Serverteam + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package de.steamwar.inventory.ui.layout; + +import de.steamwar.inventory.ui.UIComponent; +import de.steamwar.inventory.ui.util.*; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class Column implements UIComponent { + + private final List components; + + private Alignment alignment; + private boolean shrinkWrap = false; + + public Column(UIComponent... components) { + this(Arrays.asList(components)); + } + + public Column(List components) { + this.components = components; + } + + public Column centered() { + this.alignment = Alignment.CENTER; + return this; + } + + public Column setShrinkWrap(boolean shrinkWrap) { + this.shrinkWrap = shrinkWrap; + return this; + } + + @Override + public ComponentRender render(RenderContext context) { + List renderItems = new ArrayList<>(); + + int height = 0; + int maxWidth = 0; + + for (UIComponent component : components) { + ComponentRender render = component.render(context.withContainerSize(context.getContainerSize().withHeight(context.getContainerSize().getHeight() - height))); + for(RenderItem item : render.getItems()){ + renderItems.add(item.shift(0, height)); + } + maxWidth = Math.max(maxWidth, render.getSize().getWidth()); + height += render.getSize().getHeight(); + } + + if (alignment == Alignment.CENTER) { + int shiftAmount = (context.getContainerSize().getHeight() - height) / 2; + for (RenderItem item : renderItems) { + item.shift(0, shiftAmount); + } + } else if (alignment == Alignment.BETWEEN) { + int shiftAmount = (context.getContainerSize().getHeight() - height) / (components.size() - 1); + for (int i = 0; i < components.size() - 1; i++) { + RenderItem item = renderItems.get(i); + item.shift(0, shiftAmount * i); + } + } else if (alignment == Alignment.END) { + int shiftAmount = (context.getContainerSize().getHeight() - height); + for (RenderItem item : renderItems) { + item.shift(0, shiftAmount); + } + } + + return new ComponentRender( + renderItems, + new Size(maxWidth, shrinkWrap ? height : context.getContainerSize().getHeight()) + ); + } +} diff --git a/SpigotCore/SpigotCore_Main/src/de/steamwar/inventory/ui/layout/FixedSizeContainer.java b/SpigotCore/SpigotCore_Main/src/de/steamwar/inventory/ui/layout/FixedSizeContainer.java new file mode 100644 index 00000000..75520a27 --- /dev/null +++ b/SpigotCore/SpigotCore_Main/src/de/steamwar/inventory/ui/layout/FixedSizeContainer.java @@ -0,0 +1,40 @@ +/* + * This file is a part of the SteamWar software. + * + * Copyright (C) 2025 SteamWar.de-Serverteam + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package de.steamwar.inventory.ui.layout; + +import de.steamwar.inventory.ui.UIComponent; +import de.steamwar.inventory.ui.util.ComponentRender; +import de.steamwar.inventory.ui.util.RenderContext; +import de.steamwar.inventory.ui.util.Size; + +public class FixedSizeContainer implements UIComponent { + private final UIComponent children; + private final Size size; + + public FixedSizeContainer(Size size, UIComponent children) { + this.children = children; + this.size = size; + } + + @Override + public ComponentRender render(RenderContext context) { + return children.render(context.withContainerSize(size)).withSize(size); + } +} diff --git a/SpigotCore/SpigotCore_Main/src/de/steamwar/inventory/ui/layout/Grid.java b/SpigotCore/SpigotCore_Main/src/de/steamwar/inventory/ui/layout/Grid.java new file mode 100644 index 00000000..8feac6c4 --- /dev/null +++ b/SpigotCore/SpigotCore_Main/src/de/steamwar/inventory/ui/layout/Grid.java @@ -0,0 +1,68 @@ +/* + * This file is a part of the SteamWar software. + * + * Copyright (C) 2025 SteamWar.de-Serverteam + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package de.steamwar.inventory.ui.layout; + +import de.steamwar.inventory.ui.UIComponent; +import de.steamwar.inventory.ui.util.ComponentRender; +import de.steamwar.inventory.ui.util.RenderContext; +import de.steamwar.inventory.ui.util.RenderItem; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class Grid implements UIComponent { + private final List children; + + public Grid(UIComponent... children) { + this(Arrays.asList(children)); + } + + public Grid(List children) { + this.children = children; + } + + @Override + public ComponentRender render(RenderContext context) { + List renderItems = new ArrayList<>(); + int x = 0; + int y = 0; + int currentRowMaxHeight = 0; + + for (UIComponent child : children) { + ComponentRender render = child.render(context.withContainerSize( + context.getContainerSize() + .withWidth(context.getContainerSize().getWidth() - x) + .withHeight(context.getContainerSize().getHeight() - y))); + currentRowMaxHeight = Math.max(currentRowMaxHeight, render.getSize().getHeight()); + for (RenderItem item : render.getItems()) { + renderItems.add(item.shift(x, y)); + } + x += render.getSize().getWidth(); + if(x >= context.getContainerSize().getWidth()){ + x = 0; + y += currentRowMaxHeight; + currentRowMaxHeight = 0; + } + } + + return new ComponentRender(renderItems, context.getContainerSize()); + } +} diff --git a/SpigotCore/SpigotCore_Main/src/de/steamwar/inventory/ui/layout/Row.java b/SpigotCore/SpigotCore_Main/src/de/steamwar/inventory/ui/layout/Row.java new file mode 100644 index 00000000..8a856cd4 --- /dev/null +++ b/SpigotCore/SpigotCore_Main/src/de/steamwar/inventory/ui/layout/Row.java @@ -0,0 +1,98 @@ +/* + * This file is a part of the SteamWar software. + * + * Copyright (C) 2025 SteamWar.de-Serverteam + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package de.steamwar.inventory.ui.layout; + +import de.steamwar.inventory.ui.UIComponent; +import de.steamwar.inventory.ui.util.*; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class Row implements UIComponent { + + private final List components; + + private Alignment alignment; + private boolean shrinkWrap = false; + + public Row(UIComponent... components) { + this(Arrays.asList(components)); + } + + public Row(List components) { + this.components = components; + } + + public Row centered() { + this.alignment = Alignment.CENTER; + return this; + } + + public Row alignment(Alignment alignment) { + this.alignment = alignment; + return this; + } + + public Row setShrinkWrap(boolean shrinkWrap) { + this.shrinkWrap = shrinkWrap; + return this; + } + + @Override + public ComponentRender render(RenderContext context) { + List renderItems = new ArrayList<>(); + + int width = 0; + int maxHeight = 0; + + for (UIComponent component : components) { + ComponentRender render = component.render(context.withContainerSize(context.getContainerSize().withWidth(context.getContainerSize().getWidth() - width))); + for(RenderItem item : render.getItems()){ + renderItems.add(item.shift(width, 0)); + } + maxHeight = Math.max(maxHeight, render.getSize().getHeight()); + width += render.getSize().getWidth(); + } + + if (alignment == Alignment.CENTER) { + int shiftAmount = (context.getContainerSize().getWidth() - width) / 2; + for (RenderItem item : renderItems) { + item.shift(shiftAmount, 0); + } + } else if (alignment == Alignment.BETWEEN) { + int shiftAmount = (context.getContainerSize().getWidth() - width) / (components.size() - 1); + for (int i = 0; i < components.size() - 1; i++) { + RenderItem item = renderItems.get(i); + item.shift(shiftAmount * i, 0); + } + } else if (alignment == Alignment.END) { + int shiftAmount = (context.getContainerSize().getWidth() - width); + for (RenderItem item : renderItems) { + item.shift(shiftAmount, 0); + } + } + + return new ComponentRender( + renderItems, + new Size(shrinkWrap ? width : context.getContainerSize().getWidth(), maxHeight) + ); + } +} diff --git a/SpigotCore/SpigotCore_Main/src/de/steamwar/inventory/ui/test/TestInvCommand.java b/SpigotCore/SpigotCore_Main/src/de/steamwar/inventory/ui/test/TestInvCommand.java new file mode 100644 index 00000000..53e6132f --- /dev/null +++ b/SpigotCore/SpigotCore_Main/src/de/steamwar/inventory/ui/test/TestInvCommand.java @@ -0,0 +1,41 @@ +/* + * This file is a part of the SteamWar software. + * + * Copyright (C) 2025 SteamWar.de-Serverteam + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package de.steamwar.inventory.ui.test; + +import de.steamwar.command.SWCommand; +import de.steamwar.inventory.ui.util.UpdateStream; +import org.bukkit.entity.Player; + +import java.util.ArrayList; +import java.util.List; + +public class TestInvCommand extends SWCommand { + + public static final UpdateStream> STREAM = new UpdateStream<>(new ArrayList<>()); + + public TestInvCommand() { + super("testinv"); + } + + @Register + public void run(Player player) { + new TestInventory(STREAM).addViewer(player); + } +} diff --git a/SpigotCore/SpigotCore_Main/src/de/steamwar/inventory/ui/test/TestInventory.java b/SpigotCore/SpigotCore_Main/src/de/steamwar/inventory/ui/test/TestInventory.java new file mode 100644 index 00000000..1896cefa --- /dev/null +++ b/SpigotCore/SpigotCore_Main/src/de/steamwar/inventory/ui/test/TestInventory.java @@ -0,0 +1,79 @@ +/* + * This file is a part of the SteamWar software. + * + * Copyright (C) 2025 SteamWar.de-Serverteam + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package de.steamwar.inventory.ui.test; + +import de.steamwar.core.Core; +import de.steamwar.inventory.ui.StatefulUIInventory; +import de.steamwar.inventory.ui.UIComponent; +import de.steamwar.inventory.ui.fragments.HorizontalScrollLayout; +import de.steamwar.inventory.ui.item.ItemBuilder; +import de.steamwar.inventory.ui.item.UIItem; +import de.steamwar.inventory.ui.util.UpdateStream; +import de.steamwar.message.Message; +import org.bukkit.Bukkit; +import org.bukkit.Material; +import org.bukkit.entity.Player; +import org.bukkit.inventory.Inventory; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +import static de.steamwar.inventory.ui.test.TestInvCommand.STREAM; + +public class TestInventory extends StatefulUIInventory> { + + private HorizontalScrollLayout scrollLayout; + private static int counter = 1; + + public TestInventory(UpdateStream> stateObservable) { + super(stateObservable); + scrollLayout = new HorizontalScrollLayout(new ArrayList<>()); + } + + @Override + protected UIComponent render(List state) { + return scrollLayout; + } + + @Override + protected void update(List state) { + System.out.println("Update"); + List items = state.stream().map(integer -> new UIItem(ItemBuilder.of(Material.ACACIA_PLANKS).displayName(String.valueOf(integer))).onClickNoUpdate(inventoryClickEvent -> STREAM.update(current -> { + current.remove(integer); + return current; + }))).collect(Collectors.toList()); + items.add(new UIItem(ItemBuilder.of(Material.BUCKET).displayName("Add")).onClickNoUpdate(inventoryClickEvent -> STREAM.update(current -> { + current.add(counter++); + return current; + }))); + scrollLayout = scrollLayout.data(items); + } + + @Override + public Inventory createInventory(Player player) { + return Bukkit.createInventory(null, 9 * 6, "Test Inventory"); + } + + @Override + public Message getMessages() { + return Core.MESSAGE; + } +} diff --git a/SpigotCore/SpigotCore_Main/src/de/steamwar/inventory/ui/util/ComponentRender.java b/SpigotCore/SpigotCore_Main/src/de/steamwar/inventory/ui/util/ComponentRender.java new file mode 100644 index 00000000..0236e364 --- /dev/null +++ b/SpigotCore/SpigotCore_Main/src/de/steamwar/inventory/ui/util/ComponentRender.java @@ -0,0 +1,32 @@ +/* + * This file is a part of the SteamWar software. + * + * Copyright (C) 2025 SteamWar.de-Serverteam + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package de.steamwar.inventory.ui.util; + +import lombok.Data; +import lombok.With; + +import java.util.List; + +@Data +@With +public class ComponentRender { + private final List items; + private final Size size; +} diff --git a/SpigotCore/SpigotCore_Main/src/de/steamwar/inventory/ui/util/RenderContext.java b/SpigotCore/SpigotCore_Main/src/de/steamwar/inventory/ui/util/RenderContext.java new file mode 100644 index 00000000..910415f0 --- /dev/null +++ b/SpigotCore/SpigotCore_Main/src/de/steamwar/inventory/ui/util/RenderContext.java @@ -0,0 +1,32 @@ +/* + * This file is a part of the SteamWar software. + * + * Copyright (C) 2025 SteamWar.de-Serverteam + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package de.steamwar.inventory.ui.util; + +import de.steamwar.inventory.ui.UIInventory; +import lombok.Data; +import lombok.With; + +@Data +@With +public class RenderContext { + private final Size inventorySize; + private final Size containerSize; + private final UIInventory inventory; +} diff --git a/SpigotCore/SpigotCore_Main/src/de/steamwar/inventory/ui/util/RenderItem.java b/SpigotCore/SpigotCore_Main/src/de/steamwar/inventory/ui/util/RenderItem.java new file mode 100644 index 00000000..e78578d3 --- /dev/null +++ b/SpigotCore/SpigotCore_Main/src/de/steamwar/inventory/ui/util/RenderItem.java @@ -0,0 +1,42 @@ +/* + * This file is a part of the SteamWar software. + * + * Copyright (C) 2025 SteamWar.de-Serverteam + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package de.steamwar.inventory.ui.util; + +import de.steamwar.inventory.ui.item.ItemBuilder; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.With; +import org.bukkit.event.inventory.InventoryClickEvent; + +import java.util.function.Function; + +@Data +@With +@AllArgsConstructor +public class RenderItem { + private int x; + private int y; + private ItemBuilder itemStack; + private Function clickEvent; + + public RenderItem shift(int deltaX, int deltaY) { + return withX(x + deltaX).withY(y + deltaY); + } +} diff --git a/SpigotCore/SpigotCore_Main/src/de/steamwar/inventory/ui/util/Size.java b/SpigotCore/SpigotCore_Main/src/de/steamwar/inventory/ui/util/Size.java new file mode 100644 index 00000000..6bcb342f --- /dev/null +++ b/SpigotCore/SpigotCore_Main/src/de/steamwar/inventory/ui/util/Size.java @@ -0,0 +1,34 @@ +/* + * This file is a part of the SteamWar software. + * + * Copyright (C) 2025 SteamWar.de-Serverteam + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package de.steamwar.inventory.ui.util; + +import lombok.Data; +import lombok.With; + +@Data +@With +public class Size { + private final int width; + private final int height; + + public Size shrink(int width, int height) { + return new Size(this.width - width, this.height - height); + } +} diff --git a/SpigotCore/SpigotCore_Main/src/de/steamwar/inventory/ui/util/UpdateStream.java b/SpigotCore/SpigotCore_Main/src/de/steamwar/inventory/ui/util/UpdateStream.java new file mode 100644 index 00000000..2d5c4dba --- /dev/null +++ b/SpigotCore/SpigotCore_Main/src/de/steamwar/inventory/ui/util/UpdateStream.java @@ -0,0 +1,66 @@ +/* + * This file is a part of the SteamWar software. + * + * Copyright (C) 2025 SteamWar.de-Serverteam + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package de.steamwar.inventory.ui.util; + +import java.util.ArrayList; +import java.util.List; + +public class UpdateStream { + + private T current; + + private final List> subscribers = new ArrayList<>(); + + public UpdateStream(T current) { + this.current = current; + } + + public Unsubscriber subscribe(Subscriber subscriber) { + subscribers.add(subscriber); + subscriber.onUpdate(current); + return () -> subscribers.remove(subscriber); + } + + public T get() { + return current; + } + + public void emit(T newCurrent) { + current = newCurrent; + subscribers.forEach(subscriber -> subscriber.onUpdate(newCurrent)); + } + + public void update(Updater updater) { + current = updater.update(current); + subscribers.forEach(subscriber -> subscriber.onUpdate(current)); + } + + public interface Subscriber { + void onUpdate(T current); + } + + public interface Unsubscriber { + void unsubscribe(); + } + + public interface Updater { + T update(T current); + } +}