diff --git a/common/src/main/java/dev/ftb/mods/ftblibrary/FTBLibraryClient.java b/common/src/main/java/dev/ftb/mods/ftblibrary/FTBLibraryClient.java index 3dabcb60..da578483 100644 --- a/common/src/main/java/dev/ftb/mods/ftblibrary/FTBLibraryClient.java +++ b/common/src/main/java/dev/ftb/mods/ftblibrary/FTBLibraryClient.java @@ -3,7 +3,6 @@ import dev.architectury.event.events.client.ClientGuiEvent; import dev.architectury.event.events.client.ClientTickEvent; import dev.architectury.hooks.client.screen.ScreenAccess; -import dev.architectury.platform.Platform; import dev.architectury.registry.ReloadListenerRegistry; import dev.ftb.mods.ftblibrary.config.FTBLibraryClientConfig; import dev.ftb.mods.ftblibrary.config.ui.EditConfigScreen; @@ -13,33 +12,22 @@ import dev.ftb.mods.ftblibrary.ui.CursorType; import dev.ftb.mods.ftblibrary.ui.IScreenWrapper; import dev.ftb.mods.ftblibrary.util.client.ClientUtils; -import me.shedaniel.rei.api.client.config.ConfigObject; import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.components.events.GuiEventListener; import net.minecraft.client.gui.screens.Screen; import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen; -import net.minecraft.client.gui.screens.inventory.EffectRenderingInventoryScreen; import net.minecraft.server.packs.PackType; import org.jetbrains.annotations.Nullable; import java.util.ArrayList; public class FTBLibraryClient { - /** - * Meaning of the different values: 0 = No, 1 = Yes, 2 = Only in inventory, 3 = Managed by integration - * (should this be an enum instead at this point?) - */ - public static int showButtons = 1; + public static CursorType lastCursorType = null; public static void init() { FTBLibraryClientConfig.load(); - // when using REI >= 6, disable the regular sidebar buttons, - // we'll be using REI's system favourites instead. - if (Platform.isModLoaded("roughlyenoughitems")) { - showButtons = 3; - } - // Datagens hahayes if (Minecraft.getInstance() == null) { return; @@ -73,6 +61,7 @@ private static void clientTick(Minecraft client) { ClientUtils.RUN_LATER.clear(); } + } public static boolean areButtonsVisible(@Nullable Screen gui) { @@ -80,17 +69,11 @@ public static boolean areButtonsVisible(@Nullable Screen gui) { return false; } - if (showButtons == 0 || showButtons == 2 && !(gui instanceof EffectRenderingInventoryScreen)) { + if (!FTBLibraryClientConfig.SIDEBAR_ENABLED.get()) { return false; } - if(showButtons == 3 && Platform.isModLoaded("roughlyenoughitems")) { - if (ConfigObject.getInstance().isFavoritesEnabled()) { - return false; - } - } - - return gui instanceof AbstractContainerScreen && !SidebarButtonManager.INSTANCE.getGroups().isEmpty(); + return gui instanceof AbstractContainerScreen && !SidebarButtonManager.INSTANCE.getButtons().isEmpty(); } public static void editConfig(boolean isClientConfig) { diff --git a/common/src/main/java/dev/ftb/mods/ftblibrary/api/sidebar/ButtonOverlayRender.java b/common/src/main/java/dev/ftb/mods/ftblibrary/api/sidebar/ButtonOverlayRender.java new file mode 100644 index 00000000..0e81774b --- /dev/null +++ b/common/src/main/java/dev/ftb/mods/ftblibrary/api/sidebar/ButtonOverlayRender.java @@ -0,0 +1,32 @@ +package dev.ftb.mods.ftblibrary.api.sidebar; + +import com.mojang.blaze3d.systems.RenderSystem; +import dev.ftb.mods.ftblibrary.icon.Color4I; +import net.minecraft.client.gui.Font; +import net.minecraft.client.gui.GuiGraphics; + +import java.util.function.Supplier; + +public interface ButtonOverlayRender { + + /** + * Called when the button is rendering + * graphics is aligned so that 0, 0 is the top left corner of the button + * @param graphics The graphics object + * @param font The font object + * @param buttonSize The size of the button + */ + void render(GuiGraphics graphics, Font font, int buttonSize); + + static ButtonOverlayRender ofSimpleString(Supplier customTextHandler) { + return (graphics, font, buttonSize) -> { + String text = customTextHandler.get(); + if (!text.isEmpty()) { + var nw = font.width(text); + Color4I.LIGHT_RED.draw(graphics, buttonSize - nw, -1, nw + 1, 9); + graphics.drawString(font, text, buttonSize - nw + 1, 0, 0xFFFFFFFF); + RenderSystem.setShaderColor(1F, 1F, 1F, 1F); + } + }; + } +} diff --git a/common/src/main/java/dev/ftb/mods/ftblibrary/api/sidebar/SidebarButton.java b/common/src/main/java/dev/ftb/mods/ftblibrary/api/sidebar/SidebarButton.java new file mode 100644 index 00000000..eefe69bf --- /dev/null +++ b/common/src/main/java/dev/ftb/mods/ftblibrary/api/sidebar/SidebarButton.java @@ -0,0 +1,35 @@ +package dev.ftb.mods.ftblibrary.api.sidebar; + +import net.minecraft.network.chat.Component; +import net.minecraft.resources.ResourceLocation; + +import java.util.List; +import java.util.function.BooleanSupplier; +import java.util.function.Supplier; + +public interface SidebarButton { + + /** + * @return the id of the button used for saving config data created from the location button in resource path + */ + ResourceLocation getId(); + + /** + * Register a condition that must be met for the button to be visible + * @param condition a condition that must be met for the button to be visible + */ + void addVisibilityCondition(BooleanSupplier condition); + + /** + * Register a custom overlay renderer to render on top of the button icon + * @param renderer the renderer to render on top of the button icon + */ + void addOverlayRender(ButtonOverlayRender renderer); + + /** + * Override the default tooltip displayed when hovering over the button + * @param tooltipOverride a supplier that returns the tooltip to be displayed when hovering over the button + */ + void setTooltipOverride(Supplier> tooltipOverride); + +} diff --git a/common/src/main/java/dev/ftb/mods/ftblibrary/api/sidebar/SidebarButtonCreatedEvent.java b/common/src/main/java/dev/ftb/mods/ftblibrary/api/sidebar/SidebarButtonCreatedEvent.java new file mode 100644 index 00000000..502a3850 --- /dev/null +++ b/common/src/main/java/dev/ftb/mods/ftblibrary/api/sidebar/SidebarButtonCreatedEvent.java @@ -0,0 +1,21 @@ +package dev.ftb.mods.ftblibrary.api.sidebar; + +import dev.architectury.event.Event; +import dev.architectury.event.EventFactory; +import dev.ftb.mods.ftblibrary.sidebar.RegisteredSidebarButton; + +import java.util.function.Consumer; + +public class SidebarButtonCreatedEvent { + public static final Event> EVENT = EventFactory.createConsumerLoop(SidebarButtonCreatedEvent.class); + + private final RegisteredSidebarButton button; + + public SidebarButtonCreatedEvent(RegisteredSidebarButton button) { + this.button = button; + } + + public RegisteredSidebarButton getButton() { + return button; + } +} diff --git a/common/src/main/java/dev/ftb/mods/ftblibrary/config/FTBLibraryClientConfig.java b/common/src/main/java/dev/ftb/mods/ftblibrary/config/FTBLibraryClientConfig.java index 56eec8d4..63bc54df 100644 --- a/common/src/main/java/dev/ftb/mods/ftblibrary/config/FTBLibraryClientConfig.java +++ b/common/src/main/java/dev/ftb/mods/ftblibrary/config/FTBLibraryClientConfig.java @@ -1,8 +1,8 @@ package dev.ftb.mods.ftblibrary.config; -import dev.ftb.mods.ftblibrary.snbt.config.BooleanValue; -import dev.ftb.mods.ftblibrary.snbt.config.IntArrayValue; -import dev.ftb.mods.ftblibrary.snbt.config.SNBTConfig; +import dev.ftb.mods.ftblibrary.snbt.config.*; + +import java.util.HashMap; import static dev.ftb.mods.ftblibrary.FTBLibrary.MOD_ID; import static dev.ftb.mods.ftblibrary.snbt.config.ConfigUtil.LOCAL_DIR; @@ -26,6 +26,14 @@ public interface FTBLibraryClientConfig { IntArrayValue RECENT = COLOR.addIntArray("recents", new int[0]) .comment("Colors recently selected in the color selector"); + SNBTConfig SIDEBAR = CONFIG.addGroup("sidebar"); + BooleanValue SIDEBAR_ENABLED = SIDEBAR.addBoolean("enabled", true) + .comment("Enable the sidebar"); + EnumValue SIDEBAR_POSITION = SIDEBAR.addEnum("position", SidebarPosition.NAME_MAP, SidebarPosition.TOP_LEFT) + .comment("Position of the sidebar"); + + StringSidebarMapValue SIDEBAR_BUTTONS = SIDEBAR.add(new StringSidebarMapValue(SIDEBAR, "buttons", new HashMap<>())); + static void load() { loadDefaulted(CONFIG, LOCAL_DIR, MOD_ID); } @@ -44,4 +52,29 @@ static ConfigGroup getConfigGroup() { return group; } + + public enum SidebarPosition { + TOP_LEFT(false, false), + TOP_RIGHT(false, true), + BOTTOM_LEFT(true, false), + BOTTOM_RIGHT(true, true); + + private final boolean isBottom; + private final boolean isRight; + + SidebarPosition(boolean isBottom, boolean isRight) { + this.isBottom = isBottom; + this.isRight = isRight; + } + + public boolean isBottom() { + return isBottom; + } + + public boolean isRight() { + return isRight; + } + + public static final NameMap NAME_MAP = NameMap.of(TOP_LEFT, SidebarPosition.values()).baseNameKey("ftblibrary.panel.position").create(); + } } diff --git a/common/src/main/java/dev/ftb/mods/ftblibrary/icon/Icon.java b/common/src/main/java/dev/ftb/mods/ftblibrary/icon/Icon.java index 55eba3a4..080c1fe3 100644 --- a/common/src/main/java/dev/ftb/mods/ftblibrary/icon/Icon.java +++ b/common/src/main/java/dev/ftb/mods/ftblibrary/icon/Icon.java @@ -2,11 +2,20 @@ import com.google.gson.JsonElement; import com.google.gson.JsonPrimitive; +import com.mojang.datafixers.FunctionType; +import com.mojang.datafixers.util.Pair; +import com.mojang.serialization.Codec; +import com.mojang.serialization.DataResult; +import com.mojang.serialization.Decoder; +import com.mojang.serialization.DynamicOps; +import com.mojang.serialization.Encoder; import dev.ftb.mods.ftblibrary.config.ImageResourceConfig; import dev.ftb.mods.ftblibrary.math.PixelBuffer; import net.minecraft.network.FriendlyByteBuf; import net.minecraft.network.codec.StreamCodec; import net.minecraft.resources.ResourceLocation; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.util.ExtraCodecs; import org.jetbrains.annotations.Nullable; import java.net.URI; @@ -20,6 +29,8 @@ public static Color4I empty() { return Color4I.EMPTY_ICON; } + public static final Codec CODEC = ExtraCodecs.JSON.xmap(Icon::getIcon, Icon::getJson); + public static final StreamCodec STREAM_CODEC = new StreamCodec<>() { @Override public Icon decode(FriendlyByteBuf buf) { diff --git a/common/src/main/java/dev/ftb/mods/ftblibrary/integration/REIIntegration.java b/common/src/main/java/dev/ftb/mods/ftblibrary/integration/REIIntegration.java index f49413d9..0e68b1c7 100644 --- a/common/src/main/java/dev/ftb/mods/ftblibrary/integration/REIIntegration.java +++ b/common/src/main/java/dev/ftb/mods/ftblibrary/integration/REIIntegration.java @@ -1,51 +1,28 @@ package dev.ftb.mods.ftblibrary.integration; -import com.google.gson.Gson; -import com.google.gson.JsonObject; -import com.google.gson.JsonParser; -import com.mojang.serialization.DataResult; -import com.mojang.serialization.Lifecycle; -import dev.ftb.mods.ftblibrary.FTBLibrary; import dev.ftb.mods.ftblibrary.config.ui.SelectItemStackScreen; import dev.ftb.mods.ftblibrary.config.ui.ResourceSearchMode; import dev.ftb.mods.ftblibrary.config.ui.SelectableResource; -import dev.ftb.mods.ftblibrary.icon.Color4I; import dev.ftb.mods.ftblibrary.icon.Icon; import dev.ftb.mods.ftblibrary.icon.ItemIcon; -import dev.ftb.mods.ftblibrary.sidebar.SidebarButton; -import dev.ftb.mods.ftblibrary.sidebar.SidebarButtonCreatedEvent; -import dev.ftb.mods.ftblibrary.sidebar.SidebarButtonGroup; -import dev.ftb.mods.ftblibrary.sidebar.SidebarButtonManager; -import dev.ftb.mods.ftblibrary.ui.GuiHelper; +import dev.ftb.mods.ftblibrary.sidebar.SidebarGroupGuiButton; import me.shedaniel.math.Rectangle; -import me.shedaniel.rei.api.client.favorites.FavoriteEntry; -import me.shedaniel.rei.api.client.favorites.FavoriteEntryType; -import me.shedaniel.rei.api.client.gui.Renderer; -import me.shedaniel.rei.api.client.gui.widgets.Tooltip; -import me.shedaniel.rei.api.client.gui.widgets.TooltipContext; import me.shedaniel.rei.api.client.plugins.REIClientPlugin; import me.shedaniel.rei.api.client.registry.entry.EntryRegistry; +import me.shedaniel.rei.api.client.registry.screen.ExclusionZones; import me.shedaniel.rei.api.common.entry.type.VanillaEntryTypes; import me.shedaniel.rei.api.common.util.CollectionUtils; -import net.minecraft.client.Minecraft; -import net.minecraft.client.gui.Font; -import net.minecraft.client.gui.GuiGraphics; -import net.minecraft.client.gui.screens.Screen; -import net.minecraft.client.resources.language.I18n; -import net.minecraft.nbt.CompoundTag; +import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen; +import net.minecraft.client.renderer.Rect2i; import net.minecraft.network.chat.Component; import net.minecraft.network.chat.MutableComponent; -import net.minecraft.resources.ResourceLocation; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.Items; -import org.jetbrains.annotations.Nullable; -import java.util.ArrayList; import java.util.Collection; import java.util.List; public class REIIntegration implements REIClientPlugin { - public static final ResourceLocation ID = FTBLibrary.rl("sidebar_button"); private static final ResourceSearchMode REI_ITEMS = new ResourceSearchMode<>() { @Override @@ -73,134 +50,12 @@ public Collection> getAllResources() { } @Override - public void registerFavorites(FavoriteEntryType.Registry registry) { - registry.register(ID, SidebarButtonType.INSTANCE); - for (var group : SidebarButtonManager.INSTANCE.getGroups()) { - List buttons = CollectionUtils.map(group.getButtons(), SidebarButtonEntry::new); - if (!buttons.isEmpty()) { - registry.getOrCrateSection(Component.translatable(group.getLangKey())) - .add(group.isPinned(), buttons.toArray(new SidebarButtonEntry[0])); - } - } - } - - private static SidebarButton createSidebarButton(ResourceLocation id, SidebarButtonGroup g, JsonObject json) { - SidebarButton b = new SidebarButton(id, g, json); - SidebarButtonCreatedEvent.EVENT.invoker().accept(new SidebarButtonCreatedEvent(b)); - return b; + public void registerExclusionZones(ExclusionZones zones) { + zones.register(AbstractContainerScreen.class, screen -> { + Rect2i lastDrawnArea = SidebarGroupGuiButton.lastDrawnArea; + Rectangle sidebar = new Rectangle(lastDrawnArea.getX(), lastDrawnArea.getY(), lastDrawnArea.getWidth(), lastDrawnArea.getHeight()); + return List.of(sidebar); + }); } - private enum SidebarButtonType implements FavoriteEntryType { - INSTANCE; - - @Override - public CompoundTag save(SidebarButtonEntry entry, CompoundTag tag) { - tag.putString("id", entry.button.getId().toString()); - tag.putString("json", new Gson().toJson(entry.button.getJson())); - return tag; - } - - @Override - public DataResult read(CompoundTag object) { - var id = ResourceLocation.parse(object.getString("id")); - var json = (JsonObject) JsonParser.parseString(object.getString("json")); - return DataResult.success(new SidebarButtonEntry(createSidebarButton(id, null, json)), Lifecycle.stable()); - } - - @Override - public DataResult fromArgs(Object... args) { - if (args.length == 0) { - return DataResult.error(() -> "Cannot create SidebarButtonEntry from empty args!"); - } - if (!(args[0] instanceof ResourceLocation id)) { - return DataResult.error(() -> "Creation of SidebarButtonEntry from args expected ResourceLocation as the first argument!"); - } - if (!(args[1] instanceof SidebarButton) && !(args[1] instanceof JsonObject)) { - return DataResult.error(() -> "Creation of SidebarButtonEntry from args expected SidebarButton or JsonObject as the second argument!"); - } - return DataResult.success(new SidebarButtonEntry(args[1] instanceof SidebarButton button ? button : createSidebarButton(id, null, (JsonObject) args[1])), Lifecycle.stable()); - } - } - - private static class SidebarButtonEntry extends FavoriteEntry { - private final SidebarButton button; - - public SidebarButtonEntry(SidebarButton button) { - this.button = button; - } - - @Override - public boolean isInvalid() { - for (var group : SidebarButtonManager.INSTANCE.getGroups()) { - for (var groupButton : group.getButtons()) { - if (groupButton.getId().equals(button.getId()) && groupButton.isActuallyVisible()) { - return false; - } - } - } - return true; - } - - @Override - public Renderer getRenderer(boolean showcase) { - return new Renderer() { - @Override - public void render(GuiGraphics graphics, Rectangle bounds, int mouseX, int mouseY, float delta) { - GuiHelper.setupDrawing(); - button.getIcon().draw(graphics, bounds.getX(), bounds.getY(), bounds.getWidth(), bounds.getHeight()); - if (button.getCustomTextHandler() != null) { - String text = button.getCustomTextHandler().get(); - Font font = Minecraft.getInstance().font; - if (!text.isEmpty()) { - var width = font.width(text); - Color4I.LIGHT_RED.draw(graphics, bounds.getX() + bounds.getWidth() - width, bounds.getY() - 1, width + 1, font.lineHeight); - graphics.drawString(font, text, bounds.getX() + bounds.getWidth() - width + 1, bounds.getY(), 0xFFFFFFFF); - } - } - } - - @Override - @Nullable - public Tooltip getTooltip(TooltipContext context) { - List list = new ArrayList<>(); - list.add(I18n.get(button.getLangKey())); - - if (button.getTooltipHandler() != null) { - button.getTooltipHandler().accept(list); - } - - return Tooltip.create(context.getPoint(), CollectionUtils.map(list, Component::literal)); - } - }; - } - - @Override - public boolean doAction(int button) { - this.button.onClicked(Screen.hasShiftDown()); - return true; - } - - @Override - public long hashIgnoreAmount() { - return this.button.getId().hashCode(); - } - - @Override - public FavoriteEntry copy() { - return new SidebarButtonEntry(createSidebarButton(button.getId(), null, button.getJson())); - } - - @Override - public ResourceLocation getType() { - return ID; - } - - @Override - public boolean isSame(FavoriteEntry other) { - if (other instanceof SidebarButtonEntry entry) { - return entry.button.getId().equals(button.getId()); - } - return false; - } - } } diff --git a/common/src/main/java/dev/ftb/mods/ftblibrary/sidebar/GridLocation.java b/common/src/main/java/dev/ftb/mods/ftblibrary/sidebar/GridLocation.java new file mode 100644 index 00000000..04d553be --- /dev/null +++ b/common/src/main/java/dev/ftb/mods/ftblibrary/sidebar/GridLocation.java @@ -0,0 +1,18 @@ +package dev.ftb.mods.ftblibrary.sidebar; + +public record GridLocation(int x, int y) { + + public static final GridLocation OUT_OF_BOUNDS = new GridLocation(-1, -1); + + public boolean isOutOfBounds() { + return x < 0 || y < 0; + } + + public boolean isLaterInRow(GridLocation other) { + return x == other.x && y <= other.y; + } + + public boolean isLaterInColumn(GridLocation other) { + return x <= other.x && y == other.y; + } +} \ No newline at end of file diff --git a/common/src/main/java/dev/ftb/mods/ftblibrary/sidebar/RegisteredSidebarButton.java b/common/src/main/java/dev/ftb/mods/ftblibrary/sidebar/RegisteredSidebarButton.java new file mode 100644 index 00000000..613027d8 --- /dev/null +++ b/common/src/main/java/dev/ftb/mods/ftblibrary/sidebar/RegisteredSidebarButton.java @@ -0,0 +1,105 @@ +package dev.ftb.mods.ftblibrary.sidebar; + +import dev.architectury.platform.Platform; +import dev.ftb.mods.ftblibrary.api.sidebar.ButtonOverlayRender; +import dev.ftb.mods.ftblibrary.ui.GuiHelper; +import dev.ftb.mods.ftblibrary.ui.misc.LoadingScreen; +import dev.ftb.mods.ftblibrary.util.ChainedBooleanSupplier; +import dev.ftb.mods.ftblibrary.util.client.ClientUtils; +import net.minecraft.Util; +import net.minecraft.network.chat.Component; +import net.minecraft.resources.ResourceLocation; + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import java.util.function.BooleanSupplier; +import java.util.function.Supplier; + +public class RegisteredSidebarButton implements dev.ftb.mods.ftblibrary.api.sidebar.SidebarButton { + + private final SidebarButtonData data; + private final ResourceLocation id; + private final String langKey; + private final Component tooltip; + private final List extraRenderers; + private Supplier> tooltipOverride; + private ChainedBooleanSupplier visible = ChainedBooleanSupplier.TRUE; + + public RegisteredSidebarButton(ResourceLocation id, SidebarButtonData data) { + this.id = id; + this.data = data; + this.langKey = Util.makeDescriptionId("sidebar_button", id); + tooltip = Component.translatable(langKey + ".tooltip"); + if (data.requiresOp()) { + addVisibilityCondition(ClientUtils.IS_CLIENT_OP); + } + data.requiredMods().ifPresent(mods -> addVisibilityCondition(() -> mods.stream().allMatch(Platform::isModLoaded))); + extraRenderers = new ArrayList<>(); + } + + + public SidebarButtonData getData() { + return data; + } + + @Override + public ResourceLocation getId() { + return id; + } + + public String getLangKey() { + return langKey; + } + + public List getTooltip(boolean shift) { + if (tooltipOverride != null) { + return tooltipOverride.get(); + } else { + List tooltips = new ArrayList<>(); + tooltips.add(Component.translatable(langKey)); + if (shift) { + tooltips.add(tooltip); + } + Optional> components = shift ? data.shiftTooltip() : data.tooltip(); + components.ifPresent(tooltips::addAll); + return tooltips; + } + } + + public void clickButton(boolean shift) { + if (data.loadingScreen()) { + new LoadingScreen(Component.translatable(getLangKey())).openGui(); + } + + boolean canShift = shift && data.shiftClickEvent().isPresent(); + List clickEvents = canShift ? data.shiftClickEvent().get() : data.clickEvents(); + for (String event : clickEvents) { + GuiHelper.BLANK_GUI.handleClick(event); + } + } + + public boolean canSee() { + return visible.getAsBoolean(); + } + + @Override + public void addVisibilityCondition(BooleanSupplier condition) { + visible = visible.and(condition); + } + + @Override + public void addOverlayRender(ButtonOverlayRender renderer) { + extraRenderers.add(renderer); + } + + @Override + public void setTooltipOverride(Supplier> tooltipOverride) { + this.tooltipOverride = tooltipOverride; + } + + public List getExtraRenderers() { + return extraRenderers; + } +} + diff --git a/common/src/main/java/dev/ftb/mods/ftblibrary/sidebar/SidebarButton.java b/common/src/main/java/dev/ftb/mods/ftblibrary/sidebar/SidebarButton.java deleted file mode 100644 index 8dd8ca96..00000000 --- a/common/src/main/java/dev/ftb/mods/ftblibrary/sidebar/SidebarButton.java +++ /dev/null @@ -1,213 +0,0 @@ -package dev.ftb.mods.ftblibrary.sidebar; - -import com.google.gson.JsonObject; -import dev.architectury.platform.Platform; -import dev.ftb.mods.ftblibrary.FTBLibraryClient; -import dev.ftb.mods.ftblibrary.icon.Icon; -import dev.ftb.mods.ftblibrary.icon.Icons; -import dev.ftb.mods.ftblibrary.ui.GuiHelper; -import dev.ftb.mods.ftblibrary.ui.misc.LoadingScreen; -import dev.ftb.mods.ftblibrary.util.ChainedBooleanSupplier; -import dev.ftb.mods.ftblibrary.util.client.ClientUtils; -import net.minecraft.Util; -import net.minecraft.network.chat.Component; -import net.minecraft.resources.ResourceLocation; -import org.jetbrains.annotations.Nullable; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.function.BooleanSupplier; -import java.util.function.Consumer; -import java.util.function.Supplier; - - -public class SidebarButton implements Comparable { - private static final BooleanSupplier NEI_NOT_LOADED = () -> !Platform.isModLoaded("notenoughitems"); - - private final ResourceLocation id; - private final JsonObject json; - private final SidebarButtonGroup group; - - private Icon icon = Icon.empty(); - private int x = 0; - private boolean defaultConfig = true; - private boolean configValue = true; - private final List clickEvents = new ArrayList<>(); - private final List shiftClickEvents = new ArrayList<>(); - private final boolean loadingScreen; - private ChainedBooleanSupplier visible = ChainedBooleanSupplier.TRUE; - private Supplier customTextHandler = null; - private Consumer> tooltipHandler = null; - - public SidebarButton(ResourceLocation id, SidebarButtonGroup group, JsonObject json) { - this.group = group; - this.id = id; - this.json = json; - - if (json.has("icon")) { - icon = Icon.getIcon(json.get("icon")); - } - - if (icon.isEmpty()) { - icon = Icons.ACCEPT_GRAY; - } - - if (json.has("click")) { - var j = json.get("click"); - for (var e : j.isJsonArray() ? j.getAsJsonArray() : Collections.singleton(j)) { - if (e.isJsonPrimitive()) { - clickEvents.add(e.getAsString()); - } - } - } - if (json.has("shift_click")) { - var j = json.get("shift_click"); - for (var e : j.isJsonArray() ? j.getAsJsonArray() : Collections.singleton(j)) { - if (e.isJsonPrimitive()) { - shiftClickEvents.add(e.getAsString()); - } - } - } - if (json.has("config")) { - defaultConfig = configValue = json.get("config").getAsBoolean(); - } - - if (json.has("x")) { - x = json.get("x").getAsInt(); - } - - if (json.has("requires_op") && json.get("requires_op").getAsBoolean()) { - addVisibilityCondition(ClientUtils.IS_CLIENT_OP); - } - - if (json.has("hide_with_nei") && json.get("hide_with_nei").getAsBoolean()) { - addVisibilityCondition(NEI_NOT_LOADED); - } - - if (json.has("required_mods")) { - var requiredServerMods = new LinkedHashSet(); - - for (var e : json.get("required_mods").getAsJsonArray()) { - requiredServerMods.add(e.getAsString()); - } - - addVisibilityCondition(() -> { - for (var s : requiredServerMods) { - if (!Platform.isModLoaded(s)) { - return false; - } - } - - return true; - }); - } - - loadingScreen = json.has("loading_screen") && json.get("loading_screen").getAsBoolean(); - } - - public static SidebarButton copyWithoutGroup(SidebarButton toCopy) { - return new SidebarButton(toCopy.id, null, toCopy.json); - } - - public ResourceLocation getId() { - return id; - } - - public SidebarButtonGroup getGroup() { - return group; - } - - public JsonObject getJson() { - return json; - } - - public void addVisibilityCondition(BooleanSupplier supplier) { - visible = visible.and(supplier); - } - - public String getLangKey() { - return Util.makeDescriptionId("sidebar_button", id); - } - - public String getTooltipLangKey() { - return getLangKey() + ".tooltip"; - } - - @Override - public String toString() { - return id.toString(); - } - - @Override - public final int hashCode() { - return id.hashCode(); - } - - @Override - public final boolean equals(Object o) { - return o == this || o instanceof SidebarButton && id.equals(((SidebarButton) o).id); - } - - public Icon getIcon() { - return icon; - } - - public int getX() { - return x; - } - - public boolean getDefaultConfig() { - return defaultConfig; - } - - public void onClicked(boolean shift) { - if (loadingScreen) { - new LoadingScreen(Component.translatable(getLangKey())).openGui(); - } - - for (var event : (shift && !shiftClickEvents.isEmpty() ? shiftClickEvents : clickEvents)) { - GuiHelper.BLANK_GUI.handleClick(event); - } - } - - public boolean isActuallyVisible() { - return configValue && FTBLibraryClient.showButtons != 0 && isVisible(); - } - - public boolean isVisible() { - return visible.getAsBoolean(); - } - - public boolean getConfig() { - return configValue; - } - - public void setConfig(boolean value) { - configValue = value; - } - - @Nullable - public Supplier getCustomTextHandler() { - return customTextHandler; - } - - public void setCustomTextHandler(Supplier text) { - customTextHandler = text; - } - - @Nullable - public Consumer> getTooltipHandler() { - return tooltipHandler; - } - - public void setTooltipHandler(Consumer> text) { - tooltipHandler = text; - } - - @Override - public int compareTo(SidebarButton button) { - return getX() - button.getX(); - } -} diff --git a/common/src/main/java/dev/ftb/mods/ftblibrary/sidebar/SidebarButtonCreatedEvent.java b/common/src/main/java/dev/ftb/mods/ftblibrary/sidebar/SidebarButtonCreatedEvent.java deleted file mode 100644 index 83875f4e..00000000 --- a/common/src/main/java/dev/ftb/mods/ftblibrary/sidebar/SidebarButtonCreatedEvent.java +++ /dev/null @@ -1,23 +0,0 @@ -package dev.ftb.mods.ftblibrary.sidebar; - -import dev.architectury.annotations.ForgeEvent; -import dev.architectury.event.Event; -import dev.architectury.event.EventFactory; - -import java.util.function.Consumer; - -// TODO currently broken for neoforge, uncomment when there's a fix in architectury -//@ForgeEvent -public class SidebarButtonCreatedEvent { - public static final Event> EVENT = EventFactory.createConsumerLoop(SidebarButtonCreatedEvent.class); - - private final SidebarButton button; - - public SidebarButtonCreatedEvent(SidebarButton b) { - button = b; - } - - public SidebarButton getButton() { - return button; - } -} diff --git a/common/src/main/java/dev/ftb/mods/ftblibrary/sidebar/SidebarButtonData.java b/common/src/main/java/dev/ftb/mods/ftblibrary/sidebar/SidebarButtonData.java new file mode 100644 index 00000000..ee28db26 --- /dev/null +++ b/common/src/main/java/dev/ftb/mods/ftblibrary/sidebar/SidebarButtonData.java @@ -0,0 +1,43 @@ +package dev.ftb.mods.ftblibrary.sidebar; + +import com.mojang.serialization.Codec; +import com.mojang.serialization.codecs.RecordCodecBuilder; +import dev.ftb.mods.ftblibrary.icon.Icon; +import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.ComponentSerialization; +import org.jetbrains.annotations.NotNull; + +import java.util.List; +import java.util.Optional; + + +public record SidebarButtonData( + Icon icon, + boolean defaultEnabled, + List clickEvents, + Optional> shiftClickEvent, + boolean loadingScreen, + Optional> tooltip, + Optional> shiftTooltip, + boolean requiresOp, + Optional> requiredMods, + int sortIndex) implements Comparable { + + public static final Codec CODEC = RecordCodecBuilder.create(builder -> builder.group( + Icon.CODEC.fieldOf("icon").forGetter(SidebarButtonData::icon), + Codec.BOOL.fieldOf("default_enabled").orElse(true).forGetter(SidebarButtonData::defaultEnabled), + Codec.STRING.listOf(1, Integer.MAX_VALUE).fieldOf("click").forGetter(SidebarButtonData::clickEvents), + Codec.STRING.listOf(1, Integer.MAX_VALUE).optionalFieldOf("shift_click").forGetter(SidebarButtonData::shiftClickEvent), + Codec.BOOL.fieldOf("loading_screen").orElse(false).forGetter(SidebarButtonData::loadingScreen), + ComponentSerialization.CODEC.listOf().optionalFieldOf("tooltip").forGetter(SidebarButtonData::tooltip), + ComponentSerialization.CODEC.listOf().optionalFieldOf("shift_tooltip").forGetter(SidebarButtonData::shiftTooltip), + Codec.BOOL.fieldOf("requires_op").orElse(false).forGetter(SidebarButtonData::requiresOp), + Codec.STRING.listOf(1, Integer.MAX_VALUE).optionalFieldOf("required_mods").forGetter(SidebarButtonData::requiredMods), + Codec.INT.fieldOf("sort_index").orElse(0).forGetter(SidebarButtonData::sortIndex) + ).apply(builder, SidebarButtonData::new)); + + @Override + public int compareTo(@NotNull SidebarButtonData o) { + return Integer.compare(sortIndex, o.sortIndex); + } +} diff --git a/common/src/main/java/dev/ftb/mods/ftblibrary/sidebar/SidebarButtonGroup.java b/common/src/main/java/dev/ftb/mods/ftblibrary/sidebar/SidebarButtonGroup.java deleted file mode 100644 index 9160533b..00000000 --- a/common/src/main/java/dev/ftb/mods/ftblibrary/sidebar/SidebarButtonGroup.java +++ /dev/null @@ -1,47 +0,0 @@ -package dev.ftb.mods.ftblibrary.sidebar; - -import net.minecraft.Util; -import net.minecraft.resources.ResourceLocation; - -import java.util.ArrayList; -import java.util.List; - - -public class SidebarButtonGroup implements Comparable { - private final ResourceLocation id; - private final int y; - private final boolean isPinned; - private final List buttons; - - public SidebarButtonGroup(ResourceLocation id, int y, boolean isPinned) { - this.id = id; - this.y = y; - this.isPinned = isPinned; - buttons = new ArrayList<>(); - } - - public ResourceLocation getId() { - return id; - } - - public String getLangKey() { - return Util.makeDescriptionId("sidebar_group", id); - } - - public boolean isPinned() { - return isPinned; - } - - public int getY() { - return y; - } - - public List getButtons() { - return buttons; - } - - @Override - public int compareTo(SidebarButtonGroup group) { - return getY() - group.getY(); - } -} diff --git a/common/src/main/java/dev/ftb/mods/ftblibrary/sidebar/SidebarButtonManager.java b/common/src/main/java/dev/ftb/mods/ftblibrary/sidebar/SidebarButtonManager.java index b885ea84..948825bd 100644 --- a/common/src/main/java/dev/ftb/mods/ftblibrary/sidebar/SidebarButtonManager.java +++ b/common/src/main/java/dev/ftb/mods/ftblibrary/sidebar/SidebarButtonManager.java @@ -1,187 +1,166 @@ package dev.ftb.mods.ftblibrary.sidebar; -import com.google.gson.*; -import com.google.gson.stream.JsonWriter; -import dev.architectury.platform.Platform; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.JsonElement; +import com.google.gson.JsonNull; +import com.google.gson.JsonParseException; +import com.google.gson.JsonParser; +import com.mojang.logging.LogUtils; +import com.mojang.serialization.Codec; +import com.mojang.serialization.DataResult; +import com.mojang.serialization.JsonOps; import dev.ftb.mods.ftblibrary.FTBLibrary; +import dev.ftb.mods.ftblibrary.api.sidebar.SidebarButtonCreatedEvent; +import dev.ftb.mods.ftblibrary.config.FTBLibraryClientConfig; +import dev.ftb.mods.ftblibrary.snbt.config.StringSidebarMapValue; +import dev.ftb.mods.ftblibrary.util.MapUtils; import net.minecraft.resources.ResourceLocation; import net.minecraft.server.packs.resources.Resource; import net.minecraft.server.packs.resources.ResourceManager; import net.minecraft.server.packs.resources.ResourceManagerReloadListener; +import net.minecraft.server.packs.resources.SimpleJsonResourceReloadListener; +import net.minecraft.util.profiling.ProfilerFiller; +import org.slf4j.Logger; -import java.io.File; -import java.io.FileReader; -import java.io.FileWriter; +import java.io.BufferedReader; import java.io.IOException; import java.util.ArrayList; +import java.util.Collection; +import java.util.Comparator; import java.util.HashMap; +import java.util.LinkedList; import java.util.List; import java.util.Map; - - -public enum SidebarButtonManager implements ResourceManagerReloadListener { - INSTANCE; - - private final List groups = new ArrayList<>(); - - public List getGroups() { - return groups; - } - - private JsonElement readJson(Resource resource) { - try (var reader = resource.openAsReader()) { - return JsonParser.parseReader(reader); - } catch (JsonParseException | IOException e) { - FTBLibrary.LOGGER.warn("can't read {}: {}", resource.sourcePackId(), e.getMessage()); - } - - return JsonNull.INSTANCE; - } - - private JsonElement readJson(File file) { - try (var reader = new FileReader(file)) { - return JsonParser.parseReader(reader); - } catch (JsonParseException | IOException e) { - FTBLibrary.LOGGER.warn("can't read {}: {}", file.getAbsolutePath(), e.getMessage()); - } - - return JsonNull.INSTANCE; - } - - @Override - public void onResourceManagerReload(ResourceManager manager) { - groups.clear(); - - var element = readJson(Platform.getConfigFolder().resolve("sidebar_buttons.json").toFile()); - JsonObject sidebarButtonConfig; - - if (element.isJsonObject()) { - sidebarButtonConfig = element.getAsJsonObject(); - } else { - sidebarButtonConfig = new JsonObject(); - } - - Map groupMap = new HashMap<>(); - - for (var domain : manager.getNamespaces()) { - try { - // TODO: Use an alternative way to register sidebar groups because jsons are a bit messy - for (var resource : manager.getResourceStack(ResourceLocation.fromNamespaceAndPath(domain, "sidebar_button_groups.json"))) { - var json = readJson(resource); - - for (var entry : json.getAsJsonObject().entrySet()) { - if (entry.getValue().isJsonObject()) { - var groupJson = entry.getValue().getAsJsonObject(); - var y = 0; - var pinned = true; - - if (groupJson.has("y")) { - y = groupJson.get("y").getAsInt(); - } - - if(groupJson.has("pinned")) { - pinned = groupJson.get("pinned").getAsBoolean(); - } - - var group = new SidebarButtonGroup(ResourceLocation.fromNamespaceAndPath(domain, entry.getKey()), y, pinned); - groupMap.put(group.getId(), group); - } - } - } - } catch (Exception ex) { - ex.printStackTrace(); - } - } - - for (var domain : manager.getNamespaces()) { - try { - for (var resource : manager.getResourceStack(ResourceLocation.fromNamespaceAndPath(domain, "sidebar_buttons.json"))) { - var json = readJson(resource); - - if (json.isJsonObject()) { - for (var entry : json.getAsJsonObject().entrySet()) { - if (entry.getValue().isJsonObject()) { - var buttonJson = entry.getValue().getAsJsonObject(); - - if (!buttonJson.has("group")) { - continue; - } - - if (/*!FTBLibConfig.debugging.dev_sidebar_buttons && */buttonJson.has("dev_only") && buttonJson.get("dev_only").getAsBoolean()) { - continue; - } - - var group = groupMap.get(ResourceLocation.parse(buttonJson.get("group").getAsString())); - - if (group == null) { - continue; - } - - var button = new SidebarButton(ResourceLocation.fromNamespaceAndPath(domain, entry.getKey()), group, buttonJson); - - group.getButtons().add(button); - - if (sidebarButtonConfig.has(button.getId().getNamespace())) { - var e = sidebarButtonConfig.get(button.getId().getNamespace()); - - if (e.isJsonObject() && e.getAsJsonObject().has(button.getId().getPath())) { - button.setConfig(e.getAsJsonObject().get(button.getId().getPath()).getAsBoolean()); - } - } else if (sidebarButtonConfig.has(button.getId().toString())) { - button.setConfig(sidebarButtonConfig.get(button.getId().toString()).getAsBoolean()); - } - } - } - } - } - } catch (Exception ex) { - ex.printStackTrace(); - } - } - - for (var group : groupMap.values()) { - if (!group.getButtons().isEmpty()) { - group.getButtons().sort(null); - groups.add(group); - } - } - - groups.sort(null); - - for (var group : groups) { - for (var button : group.getButtons()) { - SidebarButtonCreatedEvent.EVENT.invoker().accept(new SidebarButtonCreatedEvent(button)); - } - } - - saveConfig(); - } - - public void saveConfig() { - var o = new JsonObject(); - - for (var group : groups) { - for (var button : group.getButtons()) { - var o1 = o.getAsJsonObject(button.getId().getNamespace()); - - if (o1 == null) { - o1 = new JsonObject(); - o.add(button.getId().getNamespace(), o1); - } - - o1.addProperty(button.getId().getPath(), button.getConfig()); - } - } - - var file = Platform.getConfigFolder().resolve("sidebar_buttons.json").toFile(); - - try (var writer = new FileWriter(file)) { - var gson = new GsonBuilder().disableHtmlEscaping().setPrettyPrinting().create(); - var jsonWriter = new JsonWriter(writer); - jsonWriter.setIndent("\t"); - gson.toJson(o, jsonWriter); - } catch (Exception ex) { - ex.printStackTrace(); - } - } +import java.util.function.BiConsumer; +import java.util.stream.Collectors; + + +public class SidebarButtonManager extends SimpleJsonResourceReloadListener { + + private static final Gson GSON = new GsonBuilder().setPrettyPrinting().disableHtmlEscaping().create(); + + public static final SidebarButtonManager INSTANCE = new SidebarButtonManager(); + private final Map buttons = new HashMap<>(); + private final List buttonList = new ArrayList<>(); + + public SidebarButtonManager() { + super(GSON, "sidebar_buttons"); + } + + @Override + protected void apply(Map object, ResourceManager resourceManager, ProfilerFiller profilerFiller) { + buttons.clear(); + + // Read the button and group json files and register them to their 'registry' map + loadResources(object, SidebarButtonData.CODEC, (id, buttonData) -> buttons.put(id, new RegisteredSidebarButton(id, buttonData))); + + buttonList.clear(); + List sortedButtons = buttons.values().stream().sorted(Comparator.comparingInt(value -> value.getData().sortIndex())).toList(); + + int y = 0; + int x = 0; + + for (RegisteredSidebarButton buttonEntry : sortedButtons) { + StringSidebarMapValue.SideButtonInfo buttonSettings = FTBLibraryClientConfig.SIDEBAR_BUTTONS.get().get(buttonEntry.getId().toString()); + if (buttonSettings == null) { + buttonSettings = new StringSidebarMapValue.SideButtonInfo(true, x, y); + FTBLibraryClientConfig.SIDEBAR_BUTTONS.get().put(buttonEntry.getId().toString(), buttonSettings); + FTBLibraryClientConfig.save(); + } + buttonList.add(new SidebarGuiButton(new GridLocation(buttonSettings.xPos(), buttonSettings.yPos()), buttonSettings.enabled(), buttonEntry)); + + x++; + if (x >= 4) { + x = 0; + y++; + } + } + + for (RegisteredSidebarButton value : buttons.values()) { + SidebarButtonCreatedEvent.EVENT.invoker().accept(new SidebarButtonCreatedEvent(value)); + } + FTBLibraryClientConfig.save(); + } + + private void loadResources(Map objects, Codec codec, BiConsumer consumer) { + for (Map.Entry resource : objects.entrySet()) { + JsonElement jsonElement = resource.getValue(); + DataResult parse = codec.parse(JsonOps.INSTANCE, jsonElement); + + if (parse.error().isPresent()) { + FTBLibrary.LOGGER.error("Failed to parse json: {}", parse.error().get().message()); + } else { + T result = parse.result().get(); + ResourceLocation key = resource.getKey(); + String path1 = key.getPath(); + ResourceLocation fixed = ResourceLocation.fromNamespaceAndPath(key.getNamespace(), key.getPath()); + consumer.accept(fixed, result); + } + } + } + + public void saveConfigFromButtonList() { + Map> buttonMap = new HashMap<>(); + for (SidebarGuiButton button : getButtonList()) { + int y = button.isEnabled() ? button.getGirdLocation().y() : -1; + buttonMap.computeIfAbsent(y, k -> new LinkedList<>()).add(button); + } + + int y = 0; + for (Map.Entry> integerListEntry : MapUtils.sortMapByKey(buttonMap).entrySet()) { + if (integerListEntry.getKey() == -1) { + for (SidebarGuiButton button : integerListEntry.getValue()) { + button.setGridLocation(-1, -1); + FTBLibraryClientConfig.SIDEBAR_BUTTONS.get().put(button.getSidebarButton().getId().toString(), new StringSidebarMapValue.SideButtonInfo(false, -1, -1)); + } + } + + int x = 0; + integerListEntry.getValue() + .sort(Comparator.comparingInt((SidebarGuiButton button) -> button.getGirdLocation().x())); + List value = integerListEntry.getValue(); + + for (SidebarGuiButton sidebarButton : value) { + if (sidebarButton.isEnabled()) { + sidebarButton.setGridLocation(x, y); + FTBLibraryClientConfig.SIDEBAR_BUTTONS.get().put(sidebarButton.getSidebarButton().getId().toString(), new StringSidebarMapValue.SideButtonInfo(sidebarButton.isEnabled(), x, y)); + x++; + } + } + if (x != 0) { + y++; + } + } + + for (SidebarGuiButton button : buttonList) { + StringSidebarMapValue.SideButtonInfo buttonSettings = FTBLibraryClientConfig.SIDEBAR_BUTTONS.get().get(button.getSidebarButton().getId().toString()); + if (buttonSettings != null) { + FTBLibraryClientConfig.SIDEBAR_BUTTONS.get().put(button.getSidebarButton().getId().toString(), new StringSidebarMapValue.SideButtonInfo(button.isEnabled(), button.getGirdLocation().x(), button.getGirdLocation().y())); + } + } + FTBLibraryClientConfig.save(); + } + + public List getButtonList() { + return buttonList; + } + + public List getEnabledButtonList(boolean all) { + return buttonList.stream() + .filter(SidebarGuiButton::isEnabled) + .filter(button -> all || button.getSidebarButton().canSee()) + .toList(); + } + + public List getDisabledButtonList(boolean all) { + return buttonList.stream() + .filter(button -> !button.isEnabled()) + .filter(button -> all || button.getSidebarButton().canSee()) + .collect(Collectors.toList()); + } + + public Collection getButtons() { + return buttons.values(); + } } diff --git a/common/src/main/java/dev/ftb/mods/ftblibrary/sidebar/SidebarGroupGuiButton.java b/common/src/main/java/dev/ftb/mods/ftblibrary/sidebar/SidebarGroupGuiButton.java index d01a602a..0266390b 100644 --- a/common/src/main/java/dev/ftb/mods/ftblibrary/sidebar/SidebarGroupGuiButton.java +++ b/common/src/main/java/dev/ftb/mods/ftblibrary/sidebar/SidebarGroupGuiButton.java @@ -1,7 +1,10 @@ package dev.ftb.mods.ftblibrary.sidebar; -import com.mojang.blaze3d.systems.RenderSystem; +import dev.ftb.mods.ftblibrary.FTBLibraryClient; +import dev.ftb.mods.ftblibrary.api.sidebar.ButtonOverlayRender; +import dev.ftb.mods.ftblibrary.config.FTBLibraryClientConfig; import dev.ftb.mods.ftblibrary.icon.Color4I; +import dev.ftb.mods.ftblibrary.icon.Icons; import dev.ftb.mods.ftblibrary.ui.GuiHelper; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.GuiGraphics; @@ -12,151 +15,509 @@ import net.minecraft.client.resources.language.I18n; import net.minecraft.network.chat.Component; -import java.util.ArrayList; +import java.util.Comparator; +import java.util.HashMap; +import java.util.LinkedList; import java.util.List; - +import java.util.Map; +import java.util.Optional; +import java.util.TreeMap; +import java.util.stream.Collectors; public class SidebarGroupGuiButton extends AbstractButton { - public static Rect2i lastDrawnArea = new Rect2i(0, 0, 0, 0); - - private final List buttons; - private SidebarGuiButton mouseOver; - - public SidebarGroupGuiButton() { - super(0, 0, 0, 0, Component.empty()); - buttons = new ArrayList<>(); - } - - @Override - public void renderWidget(GuiGraphics graphics, int mx, int my, float partialTicks) { - buttons.clear(); - mouseOver = null; - int rx, ry = 0; - boolean addedAny; - - for (var group : SidebarButtonManager.INSTANCE.getGroups()) { - rx = 0; - addedAny = false; - - for (var button : group.getButtons()) { - if (button.isActuallyVisible()) { - buttons.add(new SidebarGuiButton(rx, ry, button)); - rx++; - addedAny = true; - } - } - - if (addedAny) { - ry++; - } - } - - for (var button : buttons) { - button.x = 1 + button.buttonX * 17; - button.y = 1 + button.buttonY * 17; - } - - setX(Integer.MAX_VALUE); - setY(Integer.MAX_VALUE); - var maxX = Integer.MIN_VALUE; - var maxY = Integer.MIN_VALUE; - - for (var b : buttons) { - if (b.x >= 0 && b.y >= 0) { - setX(Math.min(getX(), b.x)); - setY(Math.min(getY(), b.y)); - maxX = Math.max(maxX, b.x + 16); - maxY = Math.max(maxY, b.y + 16); - } - - if (mx >= b.x && my >= b.y && mx < b.x + 16 && my < b.y + 16) { - mouseOver = b; - } - } - - // Important: JEI doesn't like negative X/Y values and will silently clamp them, - // leading it to think the values have changed every frame, and do unnecessary updating - // of its GUI areas, including resetting the filter textfield's selection - // https://github.com/FTBTeam/FTB-Mods-Issues/issues/262 - // https://github.com/mezz/JustEnoughItems/issues/2938 - setX(Math.max(0, getX() - 2)); - setY(Math.max(0, getY() - 2)); - maxX += 2; - maxY += 2; - - width = maxX - getX(); - height = maxY - getY(); - //zLevel = 0F; - - graphics.pose().pushPose(); - graphics.pose().translate(0, 0, 500); - - var font = Minecraft.getInstance().font; - - for (var b : buttons) { - GuiHelper.setupDrawing(); - b.button.getIcon().draw(graphics, b.x, b.y, 16, 16); - - if (b == mouseOver) { - Color4I.WHITE.withAlpha(33).draw(graphics, b.x, b.y, 16, 16); - } - - if (b.button.getCustomTextHandler() != null) { - var text = b.button.getCustomTextHandler().get(); - - if (!text.isEmpty()) { - var nw = font.width(text); - var width = 16; - Color4I.LIGHT_RED.draw(graphics, b.x + width - nw, b.y - 1, nw + 1, 9); - graphics.drawString(font, text, b.x + width - nw + 1, b.y, 0xFFFFFFFF); - RenderSystem.setShaderColor(1F, 1F, 1F, 1F); - } - } - } - - if (mouseOver != null) { - GuiHelper.setupDrawing(); - var mx1 = mx + 10; - var my1 = Math.max(3, my - 9); - - List list = new ArrayList<>(); - list.add(I18n.get(mouseOver.button.getLangKey())); - - if (mouseOver.button.getTooltipHandler() != null) { - mouseOver.button.getTooltipHandler().accept(list); - } - - var tw = 0; - - for (var s : list) { - tw = Math.max(tw, font.width(s)); - } - - graphics.pose().translate(0, 0, 500); - - Color4I.DARK_GRAY.draw(graphics, mx1 - 3, my1 - 2, tw + 6, 2 + list.size() * 10); - - for (var i = 0; i < list.size(); i++) { - graphics.drawString(font, list.get(i), mx1, my1 + i * 10, 0xFFFFFFFF); - } - } - - GuiHelper.setupDrawing(); - //zLevel = 0F; - - lastDrawnArea = new Rect2i(getX(), getY(), width, height); - graphics.pose().popPose(); - } - - @Override - public void onPress() { - if (mouseOver != null) { - mouseOver.button.onClicked(Screen.hasShiftDown()); - } - } - - @Override - public void updateWidgetNarration(NarrationElementOutput narrationElementOutput) { - defaultButtonNarrationText(narrationElementOutput); - } + + public static Rect2i lastDrawnArea = new Rect2i(0, 0, 0, 0); + private static final int BUTTON_SPACING = 17; + + private static final List noButtonComponents = List.of( + Component.translatable("sidebar_button.ftblibrary.config"), + Component.translatable("sidebar_button.ftblibrary.config.enter_edit_mode") + ); + + private SidebarGuiButton mouseOver; + private SidebarGuiButton selectedButton; + private GridLocation selectedLocation; + private int lastMouseClickButton = 0; + private boolean isEditMode; + + private int currentMouseX; + private int currentMouseY; + + private int mouseOffsetX; + private int mouseOffsetY; + + private int currentGirdWidth = 1; + private int currentGridHeight = 1; + + private boolean addBoxOpen; + + boolean gridStartBottom = false; + boolean gridStartRight = false; + + int yRenderStart; + int xRenderStart; + + private boolean isMouseOverAdd; + private boolean mouseOverSettingsIcon; + + private final Map realLocationMap = new HashMap<>(); + + public SidebarGroupGuiButton() { + super(0, 0, 0, 0, Component.empty()); + ensureGridAlignment(); + } + + @Override + public void renderWidget(GuiGraphics graphics, int mx, int my, float partialTicks) { + graphics.pose().pushPose(); + graphics.pose().translate(0, 0, 5000); + + currentMouseX = mx; + currentMouseY = my; + mouseOver = null; + isMouseOverAdd = false; + mouseOverSettingsIcon = false; + + GridLocation gridLocation = getGridLocation(); + + for (Map.Entry entry : realLocationMap.entrySet()) { + SidebarGuiButton button = entry.getKey(); + if (entry.getValue().equals(gridLocation)) { + mouseOver = button; + } + } + + if (isEditMode) { + renderEditMode(graphics, mx, my); + } + + renderSidebarButtons(graphics, mx, my); + + graphics.pose().popPose(); + } + + private void renderSidebarButtons(GuiGraphics graphics, int mx, int my) { + var font = Minecraft.getInstance().font; + + graphics.pose().translate(0, 0, 50); + + //If there are no sidebar buttons enabled render "fake" button + if (!isEditMode && SidebarButtonManager.INSTANCE.getEnabledButtonList(false).isEmpty()) { + if (mx >= xRenderStart + 2 && my >= yRenderStart + 2 && mx < xRenderStart + 18 && my < yRenderStart + 18) { + graphics.renderTooltip(font, noButtonComponents, Optional.empty(), mx, my + 5); + Color4I.WHITE.withAlpha(33).draw(graphics, xRenderStart + 1, yRenderStart + 1, 16, 16); + mouseOverSettingsIcon = true; + } + Icons.SETTINGS.draw(graphics, xRenderStart + 1, yRenderStart + 1, 16, 16); + + } else { + for (SidebarGuiButton button : SidebarButtonManager.INSTANCE.getButtonList()) { + graphics.pose().pushPose(); + GridLocation realGridLocation = realLocationMap.get(button); + if (isEditMode || (button.equals(selectedButton) || button.isEnabled())) { + if (isEditMode && button == selectedButton) { + graphics.pose().translate(0, 0, 1000); + button.x = mx - mouseOffsetX; + button.y = my - mouseOffsetY; + } else { + if (realGridLocation == null) { + continue; + } + + int adjustedX = gridStartRight ? xRenderStart + (currentGirdWidth - realGridLocation.x() - 1) * BUTTON_SPACING : xRenderStart + realGridLocation.x() * BUTTON_SPACING; + int adjustedY = gridStartBottom ? yRenderStart + (currentGridHeight - realGridLocation.y() - 1) * BUTTON_SPACING : yRenderStart + realGridLocation.y() * BUTTON_SPACING; + button.x = adjustedX + 1; + button.y = adjustedY + 1; + } + GuiHelper.setupDrawing(); + button.getSidebarButton().getData().icon().draw(graphics, button.x, button.y, 16, 16); + + if (isEditMode) { + if (mx >= button.x + 12 && my <= button.y + 4 && mx < button.x + 16 && my >= button.y) { + Icons.CANCEL.draw(graphics, button.x + 11, button.y - 1, 6, 6); + } else { + Icons.CANCEL.draw(graphics, button.x + 12, button.y, 4, 4); + } + } + + if (button == mouseOver) { + Color4I.WHITE.withAlpha(33).draw(graphics, button.x, button.y, 16, 16); + } + + graphics.pose().pushPose(); + graphics.pose().translate(button.x, button.y, 0); + for (ButtonOverlayRender buttonOverlayRender : button.getSidebarButton().getExtraRenderers()) { + buttonOverlayRender.render(graphics, font, 16); + } + graphics.pose().popPose(); + + } + if (!isEditMode && mouseOver == button) { + graphics.renderTooltip(font, button.getSidebarButton().getTooltip(Screen.hasShiftDown()), Optional.empty(), mx, Math.max(7, my - 9) + 10); + } + graphics.pose().popPose(); + } + } + } + + private void renderEditMode(GuiGraphics graphics, int mx, int my) { + drawHoveredGrid(graphics, xRenderStart, yRenderStart, currentGirdWidth, currentGridHeight, BUTTON_SPACING, Color4I.GRAY.withAlpha(70), Color4I.BLACK.withAlpha(90), mx, my, gridStartBottom, gridStartRight, getX(), getY()); + + List disabledButtonList = SidebarButtonManager.INSTANCE.getDisabledButtonList(isEditMode); + if (!disabledButtonList.isEmpty()) { + int addIconY = gridStartBottom ? yRenderStart + ((currentGridHeight - 1) * BUTTON_SPACING) : 0; + int addIconX = gridStartRight ? xRenderStart - (2 * BUTTON_SPACING) : (currentGirdWidth + 1) * BUTTON_SPACING; + drawGrid(graphics, addIconX, addIconY, 1, 1, BUTTON_SPACING, BUTTON_SPACING, Color4I.GRAY, Color4I.BLACK); + Icons.ADD.draw(graphics, addIconX + 1, addIconY + 1, 16, 16); + + if (mx >= addIconX && my >= addIconY && mx < addIconX + 16 && my < addIconY + 16) { + isMouseOverAdd = true; + Color4I.WHITE.withAlpha(137).draw(graphics, addIconX + 1, addIconY + 1, 16, 16); + } + + if (addBoxOpen) { + int maxWidth = 0; + for (SidebarGuiButton button : disabledButtonList) { + String s = I18n.get(button.getSidebarButton().getLangKey()); + int width = Minecraft.getInstance().font.width(s); + maxWidth = Math.max(maxWidth, width); + } + + int gridY = gridStartBottom ? addIconY - disabledButtonList.size() * BUTTON_SPACING : BUTTON_SPACING; + int gridX = gridStartRight ? addIconX - maxWidth - 6 : (currentGirdWidth + 1) * BUTTON_SPACING; + + graphics.pose().pushPose(); + graphics.pose().translate(0, 0, 1000); + + if (gridStartRight) { + drawHoveredGrid(graphics, addIconX, gridY, 1, disabledButtonList.size(), BUTTON_SPACING, Color4I.GRAY, Color4I.BLACK, mx, my, gridStartBottom, gridStartRight, gridX, gridY); + drawGrid(graphics, addIconX - maxWidth - 6, gridY, 1, disabledButtonList.size(), maxWidth + 6, BUTTON_SPACING, Color4I.GRAY, Color4I.BLACK); + } else { + drawHoveredGrid(graphics, gridX, gridY, 1, disabledButtonList.size(), BUTTON_SPACING, Color4I.GRAY, Color4I.BLACK, mx, my, gridStartBottom, gridStartRight, gridX, gridY); + drawGrid(graphics, gridX + BUTTON_SPACING, gridY, 1, disabledButtonList.size(), maxWidth + 6, BUTTON_SPACING, Color4I.GRAY, Color4I.BLACK); + } + + for (int i = 0; i < disabledButtonList.size(); i++) { + SidebarGuiButton button = disabledButtonList.get(i); + if (selectedButton != null && selectedButton == button) { + continue; + } + + int buttonY = gridY + BUTTON_SPACING * i; + button.x = gridStartRight ? addIconX : gridX; + button.y = buttonY; + GuiHelper.setupDrawing(); + + if (mx >= button.x && my >= button.y && mx < button.x + 16 && my < button.y + 16) { + Color4I.WHITE.withAlpha(137).draw(graphics, button.x + 1, button.y + 1, 16, 16); + mouseOver = button; + } + + button.getSidebarButton().getData().icon().draw(graphics, button.x + 1, button.y + 1, 16, 16); + + String langText = I18n.get(button.getSidebarButton().getLangKey()); + int textXPos = gridStartRight ? addIconX - Minecraft.getInstance().font.width(langText) - 2 : gridX + BUTTON_SPACING + 3; + graphics.drawString(Minecraft.getInstance().font, langText, textXPos, buttonY + 5, 0xFFFFFFFF); + } + graphics.pose().popPose(); + + } + } + } + + @Override + public void onRelease(double d, double e) { + if (lastMouseClickButton == 1) { + return; + } + super.onRelease(d, e); + //Normal click action + if (!isEditMode && mouseOver != null) { + mouseOver.getSidebarButton().clickButton(Screen.hasShiftDown()); + } else if (selectedButton != null) { + GridLocation gLocation = getGridLocation(); + + // Make sure the placement is in grid and that is not in the same location that it was picked up from + if (!gLocation.isOutOfBounds() && !gLocation.equals(selectedLocation)) { + updateButtonLocations(gLocation); + } + selectedButton = null; + ensureGridAlignment(); + } + } + + private void updateButtonLocations(GridLocation gLocation) { + // Checks if moved from the first spot, so we can move other icons over but only as the same row + + boolean isFrom0XTo1X = selectedLocation.y() == gLocation.y() && selectedLocation.x() == 0 && gLocation.x() == 1; + selectedButton.setGridLocation(gLocation.x(), gLocation.y()); + + // Checks for icon that needs to be moved over + List buttonList = SidebarButtonManager.INSTANCE.getButtonList(); + for (SidebarGuiButton button : buttonList) { + GridLocation realGridLocation = realLocationMap.get(button); + if (realGridLocation == null || selectedButton.getSidebarButton().getId().equals(button.getSidebarButton().getId())) { + continue; + } + + if (!gLocation.isLaterInColumn(realGridLocation)) { + continue; + } + + int moveAmount = isFrom0XTo1X && realGridLocation.x() == 1 ? -1 : 1; + button.setGridLocation(realGridLocation.x() + moveAmount, realGridLocation.y()); + } + + // If the icon was disabled enable it + if (!selectedButton.isEnabled()) { + selectedButton.setEnabled(true); + if (SidebarButtonManager.INSTANCE.getDisabledButtonList(isEditMode).isEmpty()) { + addBoxOpen = false; + } + } + } + + // Aligns icons to a 'realLocationMap' this uses the button config as base, but checks if button is "visible" then aligns it to the grid + private void ensureGridAlignment() { + List enabledButtonList = SidebarButtonManager.INSTANCE.getEnabledButtonList(isEditMode); + Map> buttonMap = enabledButtonList + .stream() + .filter(SidebarGuiButton::isEnabled) + .collect(Collectors.groupingBy(button -> button.getGirdLocation().y(), TreeMap::new, Collectors.toCollection(LinkedList::new))); + + realLocationMap.clear(); + + int y = 0; + for (Map.Entry> entry : buttonMap.entrySet()) { + entry.getValue().sort(Comparator.comparingInt(b -> b.getGirdLocation().x())); + + int x = 0; + for (SidebarGuiButton button : entry.getValue()) { + realLocationMap.put(button, new GridLocation(x, y)); + x++; + } + if (x != 0) { + y++; + } + } + + SidebarButtonManager.INSTANCE.saveConfigFromButtonList(); + updateWidgetSize(); + } + + private void updateWidgetSize() { + // Important: JEI doesn't like negative X/Y values and will silently clamp them, + // leading it to think the values have changed every frame, and do unnecessary updating + // of its GUI areas, including resetting the filter textfield's selection + // https://github.com/FTBTeam/FTB-Mods-Issues/issues/262 + // https://github.com/mezz/JustEnoughItems/issues/2938 + // This calculates the grid size + int girdAmountX = 1; + int girdAmountY = 1; + for (SidebarGuiButton b : SidebarButtonManager.INSTANCE.getEnabledButtonList(isEditMode)) { + girdAmountX = Math.max(girdAmountX, b.getGirdLocation().x() + 1); + girdAmountY = Math.max(girdAmountY, b.getGirdLocation().y() + 1); + } + + if (isEditMode && addBoxOpen) { + int disabledList = SidebarButtonManager.INSTANCE.getDisabledButtonList(isEditMode).size(); + girdAmountX += 4; + girdAmountY = Math.max(girdAmountY, disabledList); + } + if (isEditMode) { + // Add 3 extra to the x so that add button is clickable + girdAmountX += 3; + // Add one extra y, so you can place widgets at the bottom + girdAmountY += 1; + } + + // ensure the grid size is not bigger than the screen + int screenWidth = Minecraft.getInstance().getWindow().getGuiScaledWidth(); + int screenHeight = Minecraft.getInstance().getWindow().getGuiScaledHeight(); + + int maxGirdAmountX = screenWidth / BUTTON_SPACING; + int maxGirdAmountY = screenHeight / BUTTON_SPACING; + girdAmountX = Math.min(girdAmountX, maxGirdAmountX); + girdAmountY = Math.min(girdAmountY, maxGirdAmountY); + + + width = (girdAmountX) * BUTTON_SPACING; + height = (girdAmountY) * BUTTON_SPACING; + + FTBLibraryClientConfig.SidebarPosition sidebarPosition = FTBLibraryClientConfig.SIDEBAR_POSITION.get(); + + if (sidebarPosition.isBottom()) { + setY(screenHeight - height - 2); + gridStartBottom = true; + } else { + setY(0); + gridStartBottom = false; + } + + if (sidebarPosition.isRight()) { + setX(screenWidth - width - 2); + gridStartRight = true; + } else { + setX(0); + gridStartRight = false; + } + + currentGirdWidth = 1; + currentGridHeight = 1; + + for (Map.Entry value : realLocationMap.entrySet()) { + GridLocation location = value.getValue(); + currentGirdWidth = Math.max(currentGirdWidth, location.x() + 1); + currentGridHeight = Math.max(currentGridHeight, location.y() + 1); + } + + if (isEditMode) { + currentGirdWidth += 1; + currentGridHeight += 1; + } + + xRenderStart = (gridStartRight ? maxGirdAmountX - currentGirdWidth : 0) * BUTTON_SPACING; + yRenderStart = (gridStartBottom ? maxGirdAmountY - currentGridHeight + 1 : 0) * BUTTON_SPACING; + + if (gridStartBottom) { + yRenderStart -= 4; + } + if (gridStartRight) { + xRenderStart += 3; + } + + // Set the last drawn area so recipe viewer knows where we are and we can move it out the way + lastDrawnArea = new Rect2i(getX(), getY(), width, height); + } + + private GridLocation getGridLocation() { + int gridX = (currentMouseX - xRenderStart - 1) / BUTTON_SPACING; + int gridY = (currentMouseY - yRenderStart - 1) / BUTTON_SPACING; + + if (gridStartRight) { + gridX = currentGirdWidth - gridX - 1; + } + + if (gridStartBottom) { + gridY = currentGridHeight - gridY - 1; + } + + if (gridX >= currentGirdWidth || gridY >= currentGridHeight || gridX < 0 || gridY < 0) { + return GridLocation.OUT_OF_BOUNDS; + } + + return new GridLocation(gridX, gridY); + } + + + @Override + public void onPress() { + if (lastMouseClickButton == 1) { + isEditMode = !isEditMode; + ensureGridAlignment(); + return; + } + + if (mouseOverSettingsIcon) { + FTBLibraryClient.editConfig(true); + return; + } + + if (isEditMode && isMouseOverAdd) { + addBoxOpen = !addBoxOpen; + updateWidgetSize(); + return; + } + + if (mouseOver == null) { + return; + } + + mouseOffsetX = currentMouseX - mouseOver.x; + mouseOffsetY = currentMouseY - mouseOver.y; + + if (!isEditMode) { + return; + } + + // if the mouse is over the cancel icon + if (currentMouseX >= mouseOver.x + 12 && currentMouseY <= mouseOver.y + 4 && currentMouseX < mouseOver.x + 16 && currentMouseY >= mouseOver.y) { + mouseOver.setEnabled(false); + mouseOver = null; + ensureGridAlignment(); + return; + } + + selectedButton = mouseOver; + GridLocation realGridLocation = realLocationMap.get(selectedButton); + selectedLocation = realGridLocation == null ? selectedButton.getGirdLocation() : realGridLocation; + } + + @Override + public void updateWidgetNarration(NarrationElementOutput narrationElementOutput) { + defaultButtonNarrationText(narrationElementOutput); + } + + // Custom handling so our button click locations are where a buttons are not just a box + @Override + protected boolean isValidClickButton(int i) { + boolean inBounds = clicked(currentMouseX, currentMouseY); + if (!inBounds && isEditMode) { + isEditMode = false; + return false; + } + + lastMouseClickButton = i; + if (i == 1) { + return inBounds; + } + + if (super.isValidClickButton(i)) { + if (isEditMode) { + return isMouseOverAdd || selectedButton != null || mouseOver != null; + } else { + GridLocation gridLocation = getGridLocation(); + return (gridLocation.y() == 0 && gridLocation.x() == 0) || mouseOver != null; + } + } + return false; + } + + private static void drawGrid(GuiGraphics graphics, int x, int y, int width, int height, int spacingWidth, int spacingHeight, Color4I backgroundColor, Color4I gridColor) { + backgroundColor.draw(graphics, x, y, width * spacingWidth, height * spacingHeight); + + for (var i = 0; i < width + 1; i++) { + gridColor.draw(graphics, x + i * spacingWidth, y, 1, height * spacingHeight + 1); + } + + for (var i = 0; i < height + 1; i++) { + gridColor.draw(graphics, x, y + i * spacingHeight, width * spacingWidth, 1); + } + } + + public static void drawGrid(GuiGraphics graphics, int x, int y, int width, int height, int spacing, Color4I backgroundColor, Color4I gridColor) { + drawGrid(graphics, x, y, width, height, spacing, spacing, backgroundColor, gridColor); + } + + private static void drawHoveredGrid(GuiGraphics graphics, int x, int y, int width, int height, int spacing, Color4I backgroundColor, Color4I gridColor, int mx, int my, boolean gridStartBottom, boolean gridStartRight, int posX, int posY) { + drawGrid(graphics, x, y, width, height, spacing, backgroundColor, gridColor); + + int adjustedMx = mx; + int adjustedMy = my; + + if (gridStartRight) { + adjustedMx = x + width * spacing - (mx - x); + } + + if (gridStartBottom) { + adjustedMy = y + height * spacing - (my - y); + } + + if (adjustedMx >= x + posX && adjustedMy >= y + posY && adjustedMx < x + posX + width * spacing && adjustedMy < y + posY + height * spacing) { + int gridX = (adjustedMx - x - posX) / spacing; + int gridY = (adjustedMy - y - posY) / spacing; + Color4I.WHITE.withAlpha(127).draw(graphics, gridX * BUTTON_SPACING + 1, gridY * BUTTON_SPACING + 1, spacing - 1, spacing - 1); + } + } + } diff --git a/common/src/main/java/dev/ftb/mods/ftblibrary/sidebar/SidebarGuiButton.java b/common/src/main/java/dev/ftb/mods/ftblibrary/sidebar/SidebarGuiButton.java index 67234e37..4108aa28 100644 --- a/common/src/main/java/dev/ftb/mods/ftblibrary/sidebar/SidebarGuiButton.java +++ b/common/src/main/java/dev/ftb/mods/ftblibrary/sidebar/SidebarGuiButton.java @@ -1,14 +1,41 @@ package dev.ftb.mods.ftblibrary.sidebar; - public class SidebarGuiButton { - public final int buttonX, buttonY; - public final SidebarButton button; - public int x, y; - - public SidebarGuiButton(int x, int y, SidebarButton b) { - buttonX = x; - buttonY = y; - button = b; - } + + private final RegisteredSidebarButton sidebarButton; + public int x, y; + private GridLocation gridLocation; + private boolean enabled; + + public SidebarGuiButton(GridLocation girdLocation, boolean enabled, RegisteredSidebarButton sidebarButton) { + x = 0; + y = 0; + this.gridLocation = girdLocation; + this.sidebarButton = sidebarButton; + this.enabled = enabled; + } + + public boolean isEnabled() { + return enabled; + } + + public void setEnabled(boolean enabled) { + this.enabled = enabled; + } + + public RegisteredSidebarButton getSidebarButton() { + return sidebarButton; + } + + public GridLocation getGirdLocation() { + return gridLocation; + } + + public void setGridLocation(GridLocation gridLocation) { + this.gridLocation = gridLocation; + } + + public void setGridLocation(int x, int y) { + this.gridLocation = new GridLocation(x, y); + } } diff --git a/common/src/main/java/dev/ftb/mods/ftblibrary/snbt/config/StringSidebarMapValue.java b/common/src/main/java/dev/ftb/mods/ftblibrary/snbt/config/StringSidebarMapValue.java new file mode 100644 index 00000000..e7147608 --- /dev/null +++ b/common/src/main/java/dev/ftb/mods/ftblibrary/snbt/config/StringSidebarMapValue.java @@ -0,0 +1,51 @@ +package dev.ftb.mods.ftblibrary.snbt.config; + +import dev.ftb.mods.ftblibrary.snbt.SNBTCompoundTag; +import net.minecraft.resources.ResourceLocation; +import org.jetbrains.annotations.Nullable; + +import java.util.HashMap; +import java.util.Map; + +public class StringSidebarMapValue extends BaseValue> { + public StringSidebarMapValue(@Nullable SNBTConfig c, String n, Map def) { + super(c, n, def); + super.set(new HashMap<>(def)); + } + + @Override + public void write(SNBTCompoundTag tag) { + Map map = get(); + SNBTCompoundTag mapTag = new SNBTCompoundTag(); + + for (Map.Entry entry : map.entrySet()) { + SNBTCompoundTag buttonTag = new SNBTCompoundTag(); + buttonTag.putBoolean("enabled", entry.getValue().enabled()); + buttonTag.putInt("x", entry.getValue().xPos()); + buttonTag.putInt("y", entry.getValue().yPos()); + mapTag.put(entry.getKey(), buttonTag); + } + + tag.put(key, mapTag); + } + + @Override + public void read(SNBTCompoundTag tag) { + Map map = new HashMap<>(); + + SNBTCompoundTag compound = tag.getCompound(key); + for (String key : compound.getAllKeys()) { + SNBTCompoundTag buttonTag = compound.getCompound(key); + map.put(key, new SideButtonInfo( + buttonTag.getBoolean("enabled"), + buttonTag.getInt("x"), + buttonTag.getInt("y") + )); + } + + set(map); + } + + + public record SideButtonInfo(boolean enabled, int xPos, int yPos) {} +} diff --git a/common/src/main/java/dev/ftb/mods/ftblibrary/util/MapUtils.java b/common/src/main/java/dev/ftb/mods/ftblibrary/util/MapUtils.java new file mode 100644 index 00000000..7421189c --- /dev/null +++ b/common/src/main/java/dev/ftb/mods/ftblibrary/util/MapUtils.java @@ -0,0 +1,31 @@ +package dev.ftb.mods.ftblibrary.util; + +import java.util.Comparator; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.stream.Collectors; + +public class MapUtils { + + public static Map sortMapByKey(Map map, Comparator comparator) { + return map.entrySet().stream() + .sorted(Map.Entry.comparingByKey(comparator)) + .collect(Collectors.toMap( + Map.Entry::getKey, + Map.Entry::getValue, + (a, b) -> a, + LinkedHashMap::new + )); + } + + public static , V> Map sortMapByKey(Map map) { + return map.entrySet().stream() + .sorted(Map.Entry.comparingByKey()) + .collect(Collectors.toMap( + Map.Entry::getKey, + Map.Entry::getValue, + (a, b) -> a, + LinkedHashMap::new + )); + } +} \ No newline at end of file diff --git a/common/src/main/resources/assets/ftblibrary/lang/en_us.json b/common/src/main/resources/assets/ftblibrary/lang/en_us.json index c539bb2c..f98cfee0 100644 --- a/common/src/main/resources/assets/ftblibrary/lang/en_us.json +++ b/common/src/main/resources/assets/ftblibrary/lang/en_us.json @@ -60,9 +60,9 @@ "sidebar_button.ftblibrary.toggle.day.tooltip": "Sets daytime", "sidebar_button.ftblibrary.toggle.night": "Set time to Night", "sidebar_button.ftblibrary.toggle.night.tooltip": "Sets nighttime", - "sidebar_group.ftblibrary.info": "FTB Library — Info", - "sidebar_group.ftblibrary.cheat": "FTB Library — Cheats", - "sidebar_group.ftblibrary.util": "FTB Library — Utilities", + "sidebar_button.ftblibrary.config": "Open Client Config", + "sidebar_button.ftblibrary.config.tooltip": "Open FTB Client Library config", + "sidebar_button.ftblibrary.config.enter_edit_mode": "Right-click to enter edit mode", "item.ftblibrary.fluid_container": "Fluid Container", "item.ftblibrary.fluid_container.use": "Right-click on a tank to empty the container", "ftblibrary.mb": "%d mB of %s", @@ -76,9 +76,13 @@ "ftblibrary.gui.no_selection": "Nothing Selected", "ftblibrary.gui.key_reference": "Key Reference", "ftblibrary.client_settings": "Client Config", + "ftblibrary.client_settings.tooltips": "Tooltips", "ftblibrary.client_settings.tooltips.item_modname": "Show Mod Name in Item Select GUI", "ftblibrary.client_settings.tooltips.fluid_modname": "Show Mod Name in Fluid Select GUI", "ftblibrary.client_settings.tooltips.image_modname": "Show Mod Name in Image Select GUI", + "ftblibrary.client_settings.sidebar": "Sidebar Buttons", + "ftblibrary.client_settings.sidebar.enabled": "Enable Sidebar Buttons", + "ftblibrary.client_settings.sidebar.position": "Position of Sidebar Buttons", "ftblibrary.palette.chat": "Chat Colors", "ftblibrary.palette.dye": "Dye Colors", "ftblibrary.palette.nord": "Nord Theme", diff --git a/common/src/main/resources/assets/ftblibrary/sidebar_button_groups.json b/common/src/main/resources/assets/ftblibrary/sidebar_button_groups.json deleted file mode 100644 index 0128c381..00000000 --- a/common/src/main/resources/assets/ftblibrary/sidebar_button_groups.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "info": { - "y": 0 - }, - "cheat": { - "y": 100, - "pinned": false - }, - "util": { - "y": 200 - } -} \ No newline at end of file diff --git a/common/src/main/resources/assets/ftblibrary/sidebar_buttons.json b/common/src/main/resources/assets/ftblibrary/sidebar_buttons.json deleted file mode 100644 index aa1d8da1..00000000 --- a/common/src/main/resources/assets/ftblibrary/sidebar_buttons.json +++ /dev/null @@ -1,53 +0,0 @@ -{ - "toggle.gamemode": { - "group": "ftblibrary:cheat", - "icon": [ - "ftblibrary:icons/blue_button", - "ftblibrary:textures/icons/toggle_gamemode.png" - ], - "x": 100, - "click": "command:/ftblibrary gamemode", - "hide_with_nei": true, - "requires_op": true - }, - "toggle.rain": { - "group": "ftblibrary:cheat", - "icon": [ - "ftblibrary:icons/blue_button", - "ftblibrary:textures/icons/toggle_rain.png" - ], - "x": 110, - "click": "command:/ftblibrary rain", - "config": true, - "hide_with_nei": true, - "requires_op": true - }, - "toggle.day": { - "group": "ftblibrary:cheat", - "icon": [ - "ftblibrary:icons/blue_button", - "ftblibrary:textures/icons/toggle_day.png" - ], - "x": 120, - "click": "command:/ftblibrary day", - "required_server_mods": [ - "ftblibrary" - ], - "hide_with_nei": true, - "requires_op": true - }, - "toggle.night": { - "group": "ftblibrary:cheat", - "icon": [ - "ftblibrary:icons/blue_button", - "ftblibrary:textures/icons/toggle_night.png" - ], - "x": 130, - "click": "command:/ftblibrary night", - "required_server_mods": [ - "ftblibrary" - ], - "hide_with_nei": true, - "requires_op": true - } -} \ No newline at end of file diff --git a/common/src/main/resources/assets/ftblibrary/sidebar_buttons/config.json b/common/src/main/resources/assets/ftblibrary/sidebar_buttons/config.json new file mode 100644 index 00000000..9dc92cc2 --- /dev/null +++ b/common/src/main/resources/assets/ftblibrary/sidebar_buttons/config.json @@ -0,0 +1,13 @@ +{ + "icon": [ + "ftblibrary:icons/settings" + ], + "sort_index": 500, + "click": ["command:/ftblibrary clientconfig"], + "required_mods": [ + "ftblibrary" + ], + "tooltip": [ + {"translate": "sidebar_button.ftblibrary.config.enter_edit_mode" } + ] +} \ No newline at end of file diff --git a/common/src/main/resources/assets/ftblibrary/sidebar_buttons/toggle/day.json b/common/src/main/resources/assets/ftblibrary/sidebar_buttons/toggle/day.json new file mode 100644 index 00000000..5ba64be8 --- /dev/null +++ b/common/src/main/resources/assets/ftblibrary/sidebar_buttons/toggle/day.json @@ -0,0 +1,12 @@ +{ + "icon": [ + "ftblibrary:icons/blue_button", + "ftblibrary:textures/icons/toggle_day.png" + ], + "sort_index": 200, + "click": ["command:/ftblibrary day"], + "required_mods": [ + "ftblibrary" + ], + "requires_op": true +} \ No newline at end of file diff --git a/common/src/main/resources/assets/ftblibrary/sidebar_buttons/toggle/gamemode.json b/common/src/main/resources/assets/ftblibrary/sidebar_buttons/toggle/gamemode.json new file mode 100644 index 00000000..176508d3 --- /dev/null +++ b/common/src/main/resources/assets/ftblibrary/sidebar_buttons/toggle/gamemode.json @@ -0,0 +1,9 @@ +{ + "icon": [ + "ftblibrary:icons/blue_button", + "ftblibrary:textures/icons/toggle_gamemode.png" + ], + "sort_index": 100, + "click": ["command:/ftblibrary gamemode"], + "requires_op": true +} \ No newline at end of file diff --git a/common/src/main/resources/assets/ftblibrary/sidebar_buttons/toggle/night.json b/common/src/main/resources/assets/ftblibrary/sidebar_buttons/toggle/night.json new file mode 100644 index 00000000..742a753c --- /dev/null +++ b/common/src/main/resources/assets/ftblibrary/sidebar_buttons/toggle/night.json @@ -0,0 +1,12 @@ +{ + "icon": [ + "ftblibrary:icons/blue_button", + "ftblibrary:textures/icons/toggle_night.png" + ], + "sort_index": 300, + "click": ["command:/ftblibrary night"], + "required_mods": [ + "ftblibrary" + ], + "requires_op": true +} \ No newline at end of file diff --git a/common/src/main/resources/assets/ftblibrary/sidebar_buttons/toggle/rain.json b/common/src/main/resources/assets/ftblibrary/sidebar_buttons/toggle/rain.json new file mode 100644 index 00000000..7a7d4506 --- /dev/null +++ b/common/src/main/resources/assets/ftblibrary/sidebar_buttons/toggle/rain.json @@ -0,0 +1,9 @@ +{ + "icon": [ + "ftblibrary:icons/blue_button", + "ftblibrary:textures/icons/toggle_rain.png" + ], + "sort_index": 400, + "click": ["command:/ftblibrary rain"], + "requires_op": true +} \ No newline at end of file diff --git a/fabric/src/main/java/dev/ftb/mods/ftblibrary/core/mixin/fabric/AbstractContainerScreenMixin.java b/fabric/src/main/java/dev/ftb/mods/ftblibrary/core/mixin/fabric/AbstractContainerScreenMixin.java new file mode 100644 index 00000000..1bf21844 --- /dev/null +++ b/fabric/src/main/java/dev/ftb/mods/ftblibrary/core/mixin/fabric/AbstractContainerScreenMixin.java @@ -0,0 +1,26 @@ +package dev.ftb.mods.ftblibrary.core.mixin.fabric; + +import net.minecraft.client.gui.screens.Screen; +import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen; +import net.minecraft.network.chat.Component; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +// Need to make sure mouseReleased is called for widgets +// See https://github.com/FabricMC/fabric/pull/4010 +@Mixin(AbstractContainerScreen.class) +public abstract class AbstractContainerScreenMixin extends Screen { + + protected AbstractContainerScreenMixin(Component component) { + super(component); + } + + @Inject(at = @At("HEAD"), method = "mouseReleased", cancellable = true) + public void onMouseReleased(double mouseX, double mouseY, int button, CallbackInfoReturnable info) { + if(super.mouseReleased(mouseX, mouseY, button)) { + info.setReturnValue(true); + } + } +} diff --git a/fabric/src/main/resources/ftblibrary-fabric.mixins.json b/fabric/src/main/resources/ftblibrary-fabric.mixins.json index 71041dcc..a961dbb9 100644 --- a/fabric/src/main/resources/ftblibrary-fabric.mixins.json +++ b/fabric/src/main/resources/ftblibrary-fabric.mixins.json @@ -6,6 +6,7 @@ "PlayerMixin" ], "client": [ + "AbstractContainerScreenMixin", "KeyMappingAccessor" ], "injectors": { diff --git a/gradle.properties b/gradle.properties index e225b1f8..9dc4a8f0 100644 --- a/gradle.properties +++ b/gradle.properties @@ -4,7 +4,7 @@ org.gradle.daemon=false # Mod mod_id=ftblibrary readable_name=FTB Library -mod_version=2100.1.4 +mod_version=2100.1.5 mod_author=FTB Team # Maven @@ -20,7 +20,7 @@ neoforge_version=21.0.163 neoforge_version_range=[21.0.143,) neoforge_loader_version=4 fabric_loader_version=0.15.11 -fabric_api_version=0.100.8+1.21 +fabric_api_version=0.101.2+1.21 fabric_api_version_range=>=0.100.1+1.21 architectury_version=13.0.6