UI polishes
add compact mode for all orientations
This commit is contained in:
@@ -3,27 +3,43 @@ package com.trunksbomb.bagtabs;
|
||||
import com.trunksbomb.bagtabs.client.BagScreen;
|
||||
import com.trunksbomb.bagtabs.client.BagNamerScreen;
|
||||
import com.trunksbomb.bagtabs.client.BagTabOverlay;
|
||||
import com.mojang.blaze3d.platform.InputConstants;
|
||||
import net.minecraft.client.KeyMapping;
|
||||
import net.neoforged.bus.api.IEventBus;
|
||||
import net.minecraft.client.color.item.ItemColor;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.item.component.DyedItemColor;
|
||||
import net.neoforged.api.distmarker.Dist;
|
||||
import net.neoforged.fml.common.Mod;
|
||||
import net.neoforged.neoforge.client.event.ClientTickEvent;
|
||||
import net.neoforged.neoforge.client.event.RegisterKeyMappingsEvent;
|
||||
import net.neoforged.neoforge.client.event.RegisterColorHandlersEvent;
|
||||
import net.neoforged.neoforge.client.event.RegisterMenuScreensEvent;
|
||||
import net.neoforged.neoforge.client.event.ScreenEvent;
|
||||
import net.neoforged.neoforge.common.NeoForge;
|
||||
import org.lwjgl.glfw.GLFW;
|
||||
|
||||
@Mod(value = BagTabs.MODID, dist = Dist.CLIENT)
|
||||
public class BagTabsClient {
|
||||
private static final String KEY_CATEGORY = "key.categories.bagtabs";
|
||||
private static final KeyMapping OPEN_LAST_BAG = new KeyMapping(
|
||||
"key.bagtabs.open_last_bag",
|
||||
InputConstants.Type.KEYSYM,
|
||||
GLFW.GLFW_KEY_BACKSLASH,
|
||||
KEY_CATEGORY
|
||||
);
|
||||
|
||||
public BagTabsClient(IEventBus modEventBus) {
|
||||
modEventBus.addListener(BagTabsClient::registerScreens);
|
||||
modEventBus.addListener(BagTabsClient::registerItemColors);
|
||||
modEventBus.addListener(BagTabsClient::registerKeyMappings);
|
||||
NeoForge.EVENT_BUS.addListener(BagTabsClient::renderTabs);
|
||||
NeoForge.EVENT_BUS.addListener(BagTabsClient::clickTabs);
|
||||
NeoForge.EVENT_BUS.addListener(BagTabsClient::dragTabs);
|
||||
NeoForge.EVENT_BUS.addListener(BagTabsClient::releaseTabs);
|
||||
NeoForge.EVENT_BUS.addListener(BagTabsClient::scrollTabs);
|
||||
NeoForge.EVENT_BUS.addListener(BagTabsClient::initScreens);
|
||||
NeoForge.EVENT_BUS.addListener(BagTabsClient::clientTick);
|
||||
}
|
||||
|
||||
private static void registerScreens(RegisterMenuScreensEvent event) {
|
||||
@@ -36,6 +52,10 @@ public class BagTabsClient {
|
||||
event.register(bagColor, BagTabs.BAG.get());
|
||||
}
|
||||
|
||||
private static void registerKeyMappings(RegisterKeyMappingsEvent event) {
|
||||
event.register(OPEN_LAST_BAG);
|
||||
}
|
||||
|
||||
private static void renderTabs(ScreenEvent.Render.Post event) {
|
||||
BagTabOverlay.render(event);
|
||||
}
|
||||
@@ -48,6 +68,10 @@ public class BagTabsClient {
|
||||
BagTabOverlay.mouseReleased(event);
|
||||
}
|
||||
|
||||
private static void scrollTabs(ScreenEvent.MouseScrolled.Pre event) {
|
||||
BagTabOverlay.mouseScrolled(event);
|
||||
}
|
||||
|
||||
private static void dragTabs(ScreenEvent.MouseDragged.Pre event) {
|
||||
BagTabOverlay.mouseDragged(event);
|
||||
}
|
||||
@@ -56,6 +80,12 @@ public class BagTabsClient {
|
||||
BagTabOverlay.screenInit(event);
|
||||
}
|
||||
|
||||
private static void clientTick(ClientTickEvent.Post event) {
|
||||
while (OPEN_LAST_BAG.consumeClick()) {
|
||||
BagTabOverlay.openLastBag();
|
||||
}
|
||||
}
|
||||
|
||||
private static final class BagItemColor {
|
||||
private static final int DEFAULT_TINT = 0x9E7B4F;
|
||||
|
||||
|
||||
@@ -19,16 +19,14 @@ import net.minecraft.Util;
|
||||
import net.minecraft.ChatFormatting;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.gui.GuiGraphics;
|
||||
import net.minecraft.client.gui.navigation.ScreenRectangle;
|
||||
import net.minecraft.client.gui.screens.Screen;
|
||||
import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen;
|
||||
import net.minecraft.client.gui.screens.inventory.tooltip.ClientTooltipPositioner;
|
||||
import net.minecraft.client.gui.screens.inventory.tooltip.MenuTooltipPositioner;
|
||||
import net.minecraft.client.resources.sounds.SimpleSoundInstance;
|
||||
import net.minecraft.core.component.DataComponents;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.sounds.SoundEvents;
|
||||
import net.minecraft.util.FormattedCharSequence;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.inventory.Slot;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
@@ -40,6 +38,10 @@ import org.lwjgl.glfw.GLFW;
|
||||
public final class BagTabOverlay {
|
||||
private static final ResourceLocation TAB_BASE_TEXTURE = BagTabs.id("textures/gui/bag_tabs_base.png");
|
||||
private static final ResourceLocation TAB_OVERLAY_TEXTURE = BagTabs.id("textures/gui/bag_tabs_overlay.png");
|
||||
private static final ResourceLocation COMPACT_TAB_BASE_TEXTURE = BagTabs.id("textures/gui/bag_tabs_compact_base.png");
|
||||
private static final ResourceLocation COMPACT_TAB_OVERLAY_TEXTURE = BagTabs.id("textures/gui/bag_tabs_compact_overlay.png");
|
||||
private static final ResourceLocation COMPACT_SIDE_TAB_BASE_TEXTURE = BagTabs.id("textures/gui/bag_tabs_compact_side_base.png");
|
||||
private static final ResourceLocation COMPACT_SIDE_TAB_OVERLAY_TEXTURE = BagTabs.id("textures/gui/bag_tabs_compact_side_overlay.png");
|
||||
private static final ResourceLocation DOCK_TEXTURE = BagTabs.id("textures/gui/bag_tabs_dock.png");
|
||||
private static final ResourceLocation LOCK_ICON_TEXTURE = BagTabs.id("textures/gui/dock_lock.png");
|
||||
private static final ResourceLocation UNLOCK_ICON_TEXTURE = BagTabs.id("textures/gui/dock_unlock.png");
|
||||
@@ -48,6 +50,8 @@ public final class BagTabOverlay {
|
||||
private static final ResourceLocation NEXT_ICON_TEXTURE = BagTabs.id("textures/gui/dock_next.png");
|
||||
private static final int BASE_TAB_WIDTH = 22;
|
||||
private static final int BASE_TAB_HEIGHT = 22;
|
||||
private static final int BASE_COMPACT_TAB_WIDTH = 22;
|
||||
private static final int BASE_COMPACT_TAB_HEIGHT = 11;
|
||||
private static final int BASE_DOCK_WIDTH = 18;
|
||||
private static final int BASE_DOCK_HEIGHT = 22;
|
||||
private static final int BASE_DOCK_ICON_SIZE = 32;
|
||||
@@ -63,6 +67,8 @@ public final class BagTabOverlay {
|
||||
private static DragState dragState;
|
||||
private static PendingCursorRestore pendingCursorRestore;
|
||||
private static long nextAutoScrollAt;
|
||||
private static String lastOpenedBagKey;
|
||||
private static int lastOpenedBagSlot = -1;
|
||||
|
||||
private BagTabOverlay() {
|
||||
}
|
||||
@@ -95,7 +101,9 @@ public final class BagTabOverlay {
|
||||
continue;
|
||||
}
|
||||
renderTab(g, tab, mouseX, mouseY, carried, false);
|
||||
g.renderItem(tab.entry().stack(), tab.itemX(), tab.itemY());
|
||||
if (!tab.compact()) {
|
||||
g.renderItem(tab.entry().stack(), tab.itemX(), tab.itemY());
|
||||
}
|
||||
renderPinOverlay(g, tab);
|
||||
}
|
||||
|
||||
@@ -118,7 +126,9 @@ public final class BagTabOverlay {
|
||||
g.pose().pushPose();
|
||||
g.pose().translate(0.0F, 0.0F, 200.0F);
|
||||
renderTab(g, floating, mouseX, mouseY, carried, true);
|
||||
g.renderItem(dragged.entry().stack(), floating.itemX(), floating.itemY());
|
||||
if (!dragged.compact()) {
|
||||
g.renderItem(dragged.entry().stack(), floating.itemX(), floating.itemY());
|
||||
}
|
||||
renderPinOverlay(g, floating);
|
||||
g.pose().popPose();
|
||||
}
|
||||
@@ -129,13 +139,13 @@ public final class BagTabOverlay {
|
||||
|
||||
DockIcon hovered = strip.control().hoveredIcon(mouseX, mouseY);
|
||||
if (hovered != null) {
|
||||
renderManagedTooltip(g, screen, List.of(getDockTooltip(hovered).getVisualOrderText()), mouseX, mouseY);
|
||||
g.renderTooltip(Minecraft.getInstance().font, List.of(getDockTooltip(hovered).getVisualOrderText()), mouseX, mouseY);
|
||||
return;
|
||||
}
|
||||
|
||||
ScrollIcon hoveredScroll = hoveredScrollIcon(strip.scrollControl(), mouseX, mouseY);
|
||||
if (hoveredScroll != null) {
|
||||
renderManagedTooltip(g, screen, List.of(getScrollTooltip(hoveredScroll).getVisualOrderText()), mouseX, mouseY);
|
||||
g.renderTooltip(Minecraft.getInstance().font, List.of(getScrollTooltip(hoveredScroll).getVisualOrderText()), mouseX, mouseY);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -143,7 +153,7 @@ public final class BagTabOverlay {
|
||||
if (!tab.isHovered(mouseX, mouseY)) {
|
||||
continue;
|
||||
}
|
||||
renderManagedTooltip(g, screen, getTooltipLines(tab, strip.allEntries()).stream().map(Component::getVisualOrderText).toList(), mouseX, mouseY);
|
||||
g.renderTooltip(Minecraft.getInstance().font, getTooltipLines(tab, strip.allEntries()).stream().map(Component::getVisualOrderText).toList(), mouseX, mouseY);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -247,6 +257,32 @@ public final class BagTabOverlay {
|
||||
event.setCanceled(true);
|
||||
}
|
||||
|
||||
public static void mouseScrolled(ScreenEvent.MouseScrolled.Pre event) {
|
||||
if (!(event.getScreen() instanceof AbstractContainerScreen<?> screen) || !supportsTabs(screen)) {
|
||||
return;
|
||||
}
|
||||
Player player = Minecraft.getInstance().player;
|
||||
if (player == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
TabStrip strip = getTabStrip(screen, player);
|
||||
if (strip.scrollControl() == null || !isScrollHoverTarget(strip, event.getMouseX(), event.getMouseY())) {
|
||||
return;
|
||||
}
|
||||
|
||||
int direction = event.getScrollDeltaY() < 0 ? 1 : -1;
|
||||
if (direction == 0) {
|
||||
return;
|
||||
}
|
||||
int nextOffset = clamp(strip.scrollOffset() + direction, 0, strip.maxScrollOffset());
|
||||
if (nextOffset != strip.scrollOffset()) {
|
||||
setScrollOffset(screen, nextOffset, strip.maxScrollOffset());
|
||||
playScrollSound();
|
||||
}
|
||||
event.setCanceled(true);
|
||||
}
|
||||
|
||||
public static void mouseReleased(ScreenEvent.MouseButtonReleased.Pre event) {
|
||||
if (!(event.getScreen() instanceof AbstractContainerScreen<?> screen) || !supportsTabs(screen) || event.getButton() != 0) {
|
||||
return;
|
||||
@@ -272,6 +308,7 @@ public final class BagTabOverlay {
|
||||
for (RenderedTab tab : tabs) {
|
||||
if (tab.entry().slot() == pendingClick.slot() && tab.isHovered(event.getMouseX(), event.getMouseY())) {
|
||||
scheduleCursorRestore(event.getMouseX(), event.getMouseY());
|
||||
rememberOpenedBag(tab.entry());
|
||||
PacketDistributor.sendToServer(new OpenBagPayload(tab.entry().slot()));
|
||||
Minecraft.getInstance().getSoundManager().play(SimpleSoundInstance.forUI(SoundEvents.UI_BUTTON_CLICK, 1.0F));
|
||||
event.setCanceled(true);
|
||||
@@ -318,6 +355,33 @@ public final class BagTabOverlay {
|
||||
pendingCursorRestore = null;
|
||||
}
|
||||
|
||||
public static void openLastBag() {
|
||||
Minecraft minecraft = Minecraft.getInstance();
|
||||
if (minecraft.player == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
List<BagEntry> bags = TabPinManager.sortTabs(BagAccess.findBags(minecraft.player));
|
||||
BagEntry target = null;
|
||||
if (lastOpenedBagKey != null) {
|
||||
target = bags.stream().filter(entry -> entry.identity() != null && lastOpenedBagKey.equals(entry.identity().key())).findFirst().orElse(null);
|
||||
}
|
||||
if (target == null && lastOpenedBagSlot >= 0 && lastOpenedBagSlot < minecraft.player.getInventory().getContainerSize()) {
|
||||
final int slot = lastOpenedBagSlot;
|
||||
target = bags.stream().filter(entry -> entry.slot() == slot).findFirst().orElse(null);
|
||||
}
|
||||
if (target == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (minecraft.screen instanceof AbstractContainerScreen<?>) {
|
||||
scheduleCursorRestore(minecraft.mouseHandler.xpos(), minecraft.mouseHandler.ypos());
|
||||
}
|
||||
rememberOpenedBag(target);
|
||||
PacketDistributor.sendToServer(new OpenBagPayload(target.slot()));
|
||||
minecraft.getSoundManager().play(SimpleSoundInstance.forUI(SoundEvents.UI_BUTTON_CLICK, 1.0F));
|
||||
}
|
||||
|
||||
public static void updateInsertTargets(int requestId, List<Integer> insertableSlots) {
|
||||
if (requestId != pendingInsertRequestId) {
|
||||
return;
|
||||
@@ -348,7 +412,7 @@ public final class BagTabOverlay {
|
||||
int y = layout.firstTabY();
|
||||
int renderedCount = 0;
|
||||
for (BagEntry bag : bags.subList(scrollOffset, scrollOffset + visibleCount)) {
|
||||
rendered.add(new RenderedTab(bag, x, y, bag.slot() == activeBagSlot, TabPinManager.isPinned(bag, bags), layout.dockSide(), layout.scale(), layout.tabWidth(), layout.tabHeight()));
|
||||
rendered.add(new RenderedTab(bag, x, y, bag.slot() == activeBagSlot, TabPinManager.isPinned(bag, bags), layout.dockSide(), layout.scale(), layout.tabWidth(), layout.tabHeight(), layout.compact()));
|
||||
if (layout.vertical()) {
|
||||
y += layout.tabHeight() + TAB_GAP;
|
||||
} else {
|
||||
@@ -359,7 +423,7 @@ public final class BagTabOverlay {
|
||||
|
||||
DockControl control = getDockControl(layout);
|
||||
ScrollControl scrollControl = overflow ? getScrollControl(layout, renderedCount) : null;
|
||||
return new TabStrip(bags, rendered, layout, control, scrollControl, scrollOffset, maxScrollOffset);
|
||||
return new TabStrip(bags, rendered, layout, control, scrollControl, scrollOffset, maxScrollOffset, activeBagSlot);
|
||||
}
|
||||
|
||||
private static List<BagEntry> applyPreviewOrder(List<BagEntry> bags, List<String> previewOrder) {
|
||||
@@ -404,30 +468,40 @@ public final class BagTabOverlay {
|
||||
green = ((color >> 8) & 0xFF) / 255.0F;
|
||||
blue = (color & 0xFF) / 255.0F;
|
||||
}
|
||||
int uOffset = (hovered || selected) ? BASE_TAB_WIDTH : 0;
|
||||
int baseWidth = tab.baseTextureWidth();
|
||||
int baseHeight = tab.baseTextureHeight();
|
||||
int uOffset = (hovered || selected) ? baseWidth : 0;
|
||||
ResourceLocation baseTexture = tab.baseTexture();
|
||||
ResourceLocation overlayTexture = tab.overlayTexture();
|
||||
|
||||
RenderSystem.setShaderColor(red, green, blue, 1.0F);
|
||||
blitRotated(g, TAB_BASE_TEXTURE, tab.x(), tab.y(), tab.width(), tab.height(), uOffset, 0, BASE_TAB_WIDTH, BASE_TAB_HEIGHT, BASE_TAB_WIDTH * 2, BASE_TAB_HEIGHT, tab.rotationDegrees());
|
||||
blitRotated(g, baseTexture, tab.x(), tab.y(), tab.width(), tab.height(), uOffset, 0, baseWidth, baseHeight, baseWidth * 2, baseHeight, tab.rotationDegrees());
|
||||
RenderSystem.setShaderColor(1.0F, 1.0F, 1.0F, 1.0F);
|
||||
blitRotated(g, TAB_OVERLAY_TEXTURE, tab.x(), tab.y(), tab.width(), tab.height(), uOffset, 0, BASE_TAB_WIDTH, BASE_TAB_HEIGHT, BASE_TAB_WIDTH * 2, BASE_TAB_HEIGHT, tab.rotationDegrees());
|
||||
blitRotated(g, overlayTexture, tab.x(), tab.y(), tab.width(), tab.height(), uOffset, 0, baseWidth, baseHeight, baseWidth * 2, baseHeight, tab.rotationDegrees());
|
||||
|
||||
g.pose().pushPose();
|
||||
pushTabTransform(g, tab);
|
||||
if (dragged || tab.usesFloatingArt()) {
|
||||
g.fill(1, 0, BASE_TAB_WIDTH - 1, 1, 0xFF000000);
|
||||
g.fill(2, 1, BASE_TAB_WIDTH - 2, 2, 0x80FFFFFF);
|
||||
g.fill(1, 0, baseWidth - 1, 1, 0xFF000000);
|
||||
if (baseHeight > 2) {
|
||||
g.fill(2, 1, baseWidth - 2, 2, 0x80FFFFFF);
|
||||
}
|
||||
}
|
||||
if (selected) {
|
||||
g.fill(3, BASE_TAB_HEIGHT - 4, BASE_TAB_WIDTH - 3, BASE_TAB_HEIGHT - 2, 0xFFD94A4A);
|
||||
renderSelectedIndicator(g, tab, baseWidth, baseHeight);
|
||||
}
|
||||
if (!carried.isEmpty()) {
|
||||
if (INSERTABLE_SLOTS.contains(tab.entry().slot())) {
|
||||
renderPlusIndicator(g, BASE_TAB_WIDTH - 7, 3);
|
||||
renderPlusIndicator(g, baseWidth - 7, Math.min(3, Math.max(0, baseHeight - 6)));
|
||||
} else {
|
||||
renderXIndicator(g, BASE_TAB_WIDTH - 7, 3);
|
||||
renderXIndicator(g, baseWidth - 7, Math.min(3, Math.max(0, baseHeight - 6)));
|
||||
}
|
||||
}
|
||||
g.pose().popPose();
|
||||
|
||||
if (tab.compact()) {
|
||||
renderCompactLabel(g, tab);
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean supportsTabs(AbstractContainerScreen<?> screen) {
|
||||
@@ -513,10 +587,31 @@ public final class BagTabOverlay {
|
||||
strip.layout().dockSide(),
|
||||
strip.layout().scale(),
|
||||
strip.layout().tabWidth(),
|
||||
strip.layout().tabHeight()
|
||||
strip.layout().tabHeight(),
|
||||
strip.layout().compact()
|
||||
);
|
||||
}
|
||||
|
||||
private static boolean isScrollHoverTarget(TabStrip strip, double mouseX, double mouseY) {
|
||||
if (strip.scrollControl() != null && hoveredScrollIcon(strip.scrollControl(), mouseX, mouseY) != null) {
|
||||
return true;
|
||||
}
|
||||
if (strip.control().hoveredIcon(mouseX, mouseY) != null) {
|
||||
return true;
|
||||
}
|
||||
for (RenderedTab tab : strip.tabs()) {
|
||||
if (tab.isHovered(mouseX, mouseY)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static void rememberOpenedBag(BagEntry entry) {
|
||||
lastOpenedBagSlot = entry.slot();
|
||||
lastOpenedBagKey = entry.identity() == null ? null : entry.identity().key();
|
||||
}
|
||||
|
||||
private static void renderPinOverlay(GuiGraphics g, RenderedTab tab) {
|
||||
if (!tab.pinned()) {
|
||||
return;
|
||||
@@ -528,6 +623,65 @@ public final class BagTabOverlay {
|
||||
g.fill(x + 2, y + 4, x + 3, y + 5, 0xFFD94A4A);
|
||||
}
|
||||
|
||||
private static void renderSelectedIndicator(GuiGraphics g, RenderedTab tab, int baseWidth, int baseHeight) {
|
||||
if (!tab.compact()) {
|
||||
int highlightTop = Math.max(baseHeight - 3, 1);
|
||||
int highlightBottom = Math.max(baseHeight - 1, highlightTop + 1);
|
||||
g.fill(3, highlightTop, baseWidth - 3, highlightBottom, 0xFFD94A4A);
|
||||
return;
|
||||
}
|
||||
|
||||
Edge edge = tab.compactLocalOuterEdge();
|
||||
switch (edge) {
|
||||
case TOP -> g.fill(2, 0, baseWidth - 2, 2, 0xFFD94A4A);
|
||||
case BOTTOM -> g.fill(2, baseHeight - 2, baseWidth - 2, baseHeight, 0xFFD94A4A);
|
||||
case LEFT -> g.fill(0, 1, 2, baseHeight - 1, 0xFFD94A4A);
|
||||
case RIGHT -> g.fill(baseWidth - 2, 1, baseWidth, baseHeight - 1, 0xFFD94A4A);
|
||||
}
|
||||
}
|
||||
|
||||
private static void renderCompactLabel(GuiGraphics g, RenderedTab tab) {
|
||||
Minecraft minecraft = Minecraft.getInstance();
|
||||
int maxTextWidth = Math.max(8, tab.width() - 4);
|
||||
List<FormattedCharSequence> wrapped = minecraft.font.split(tab.entry().stack().getHoverName(), maxTextWidth);
|
||||
if (wrapped.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
List<FormattedCharSequence> lines = wrapped.size() > 2 ? wrapped.subList(0, 2) : wrapped;
|
||||
int widestLine = lines.stream().mapToInt(minecraft.font::width).max().orElse(1);
|
||||
int lineCount = lines.size();
|
||||
int totalHeight = lineCount * minecraft.font.lineHeight;
|
||||
float scale = Math.min(1.0F, Math.min(maxTextWidth / (float) Math.max(widestLine, 1), (tab.height() - 2) / (float) Math.max(totalHeight, 1)));
|
||||
float scaledWidth = widestLine * scale;
|
||||
float scaledHeight = totalHeight * scale;
|
||||
float startX = tab.x() + ((tab.width() - scaledWidth) / 2.0F);
|
||||
float startY = tab.y() + ((tab.height() - scaledHeight) / 2.0F);
|
||||
int textColor = getCompactTextColor(tab);
|
||||
|
||||
g.pose().pushPose();
|
||||
g.pose().translate(startX, startY, 0.0F);
|
||||
g.pose().scale(scale, scale, 1.0F);
|
||||
int y = 0;
|
||||
for (FormattedCharSequence line : lines) {
|
||||
int lineX = (int) ((widestLine - minecraft.font.width(line)) / 2.0F);
|
||||
g.drawString(minecraft.font, line, lineX, y, textColor, false);
|
||||
y += minecraft.font.lineHeight;
|
||||
}
|
||||
g.pose().popPose();
|
||||
}
|
||||
|
||||
private static int getCompactTextColor(RenderedTab tab) {
|
||||
if (!tab.entry().stack().has(DataComponents.DYED_COLOR)) {
|
||||
return 0xFF2B2B2B;
|
||||
}
|
||||
int color = DyedItemColor.getOrDefault(tab.entry().stack(), BagItem.DEFAULT_COLOR);
|
||||
int red = (color >> 16) & 0xFF;
|
||||
int green = (color >> 8) & 0xFF;
|
||||
int blue = color & 0xFF;
|
||||
double luminance = (0.2126D * red) + (0.7152D * green) + (0.0722D * blue);
|
||||
return luminance < 140.0D ? 0xFFF4F0E6 : 0xFF2B2B2B;
|
||||
}
|
||||
|
||||
private static List<Component> getTooltipLines(RenderedTab tab, List<BagEntry> tabs) {
|
||||
List<Component> lines = new ArrayList<>();
|
||||
lines.add(tab.entry().stack().getHoverName());
|
||||
@@ -632,8 +786,8 @@ public final class BagTabOverlay {
|
||||
private static DockLayout getDockLayout(AbstractContainerScreen<?> screen, Player player, int bagCount) {
|
||||
DockConfigManager.DockSettings settings = DockConfigManager.getEffectiveSettings(getScreenKey(screen));
|
||||
float scale = 1.0F;
|
||||
int tabWidth = BASE_TAB_WIDTH;
|
||||
int tabHeight = BASE_TAB_HEIGHT;
|
||||
int tabWidth = settings.compact() ? BASE_COMPACT_TAB_WIDTH : BASE_TAB_WIDTH;
|
||||
int tabHeight = settings.compact() ? BASE_COMPACT_TAB_HEIGHT : BASE_TAB_HEIGHT;
|
||||
int baseControlWidth = BASE_DOCK_WIDTH;
|
||||
int baseControlHeight = BASE_DOCK_HEIGHT;
|
||||
int inventoryLeftBound = getInventoryLeftBound(screen, player);
|
||||
@@ -659,7 +813,7 @@ public final class BagTabOverlay {
|
||||
firstTabX = inventoryLeftBound + TAB_X_OFFSET + settings.xOffset() + controlWidth;
|
||||
firstTabY = guiTopBound - tabHeight + 1 + settings.yOffset();
|
||||
controlX = firstTabX - controlWidth - 2;
|
||||
controlY = firstTabY + Math.max(0, (tabHeight - controlHeight) / 2);
|
||||
controlY = getHorizontalControlY(firstTabY, tabHeight, controlHeight, settings.dockSide(), settings.compact());
|
||||
vertical = false;
|
||||
}
|
||||
case LEFT -> {
|
||||
@@ -686,7 +840,7 @@ public final class BagTabOverlay {
|
||||
firstTabX = controlWidth + 2 + settings.xOffset();
|
||||
firstTabY = screen.height - tabHeight + settings.yOffset();
|
||||
controlX = settings.xOffset();
|
||||
controlY = firstTabY + Math.max(0, (tabHeight - controlHeight) / 2);
|
||||
controlY = getHorizontalControlY(firstTabY, tabHeight, controlHeight, settings.dockSide(), settings.compact());
|
||||
vertical = false;
|
||||
}
|
||||
case SCREEN_TOP -> {
|
||||
@@ -695,7 +849,7 @@ public final class BagTabOverlay {
|
||||
firstTabX = controlWidth + 2 + settings.xOffset();
|
||||
firstTabY = settings.yOffset();
|
||||
controlX = settings.xOffset();
|
||||
controlY = firstTabY + Math.max(0, (tabHeight - controlHeight) / 2);
|
||||
controlY = getHorizontalControlY(firstTabY, tabHeight, controlHeight, settings.dockSide(), settings.compact());
|
||||
vertical = false;
|
||||
}
|
||||
case SCREEN_LEFT -> {
|
||||
@@ -723,7 +877,7 @@ public final class BagTabOverlay {
|
||||
controlX = (screen.width / 2) - (totalWidth / 2) + settings.xOffset();
|
||||
firstTabX = controlX + controlWidth + 2;
|
||||
firstTabY = (screen.height / 2) + settings.yOffset();
|
||||
controlY = firstTabY + Math.max(0, (tabHeight - controlHeight) / 2);
|
||||
controlY = getHorizontalControlY(firstTabY, tabHeight, controlHeight, settings.dockSide(), settings.compact());
|
||||
vertical = false;
|
||||
}
|
||||
case FLOATING_VERTICAL -> {
|
||||
@@ -742,7 +896,7 @@ public final class BagTabOverlay {
|
||||
firstTabX = inventoryLeftBound + TAB_X_OFFSET + settings.xOffset() + controlWidth;
|
||||
firstTabY = bottomBound + TAB_Y_OFFSET + settings.yOffset() + 8;
|
||||
controlX = firstTabX - controlWidth - 2;
|
||||
controlY = firstTabY + Math.max(0, (tabHeight - controlHeight) / 2);
|
||||
controlY = getHorizontalControlY(firstTabY, tabHeight, controlHeight, settings.dockSide(), settings.compact());
|
||||
vertical = false;
|
||||
}
|
||||
default -> throw new IllegalStateException("Unexpected dock side");
|
||||
@@ -784,7 +938,7 @@ public final class BagTabOverlay {
|
||||
}
|
||||
}
|
||||
|
||||
return new DockLayout(firstTabX, firstTabY, tabWidth, tabHeight, controlX, controlY, controlWidth, controlHeight, scale, settings.dockSide(), vertical, settings.maxTabs(), settings.dockSide() == DockConfigManager.DockSide.FLOATING_HORIZONTAL || settings.dockSide() == DockConfigManager.DockSide.FLOATING_VERTICAL);
|
||||
return new DockLayout(firstTabX, firstTabY, tabWidth, tabHeight, controlX, controlY, controlWidth, controlHeight, scale, settings.dockSide(), vertical, settings.maxTabs(), settings.dockSide() == DockConfigManager.DockSide.FLOATING_HORIZONTAL || settings.dockSide() == DockConfigManager.DockSide.FLOATING_VERTICAL, settings.compact());
|
||||
}
|
||||
|
||||
private static DockControl getDockControl(DockLayout layout) {
|
||||
@@ -802,13 +956,20 @@ public final class BagTabOverlay {
|
||||
layout.dockSide());
|
||||
}
|
||||
return new ScrollControl(layout.firstTabX() + primarySpan + 2,
|
||||
layout.firstTabY() + Math.max(0, (layout.tabHeight() - layout.controlHeight()) / 2),
|
||||
getHorizontalControlY(layout.firstTabY(), layout.tabHeight(), layout.controlHeight(), layout.dockSide(), layout.compact()),
|
||||
layout.controlWidth(),
|
||||
layout.controlHeight(),
|
||||
layout.scale(),
|
||||
layout.dockSide());
|
||||
}
|
||||
|
||||
private static int getHorizontalControlY(int firstTabY, int tabHeight, int controlHeight, DockConfigManager.DockSide dockSide, boolean compact) {
|
||||
if (compact && (dockSide == DockConfigManager.DockSide.TOP || dockSide == DockConfigManager.DockSide.SCREEN_BOTTOM)) {
|
||||
return firstTabY + tabHeight - controlHeight;
|
||||
}
|
||||
return firstTabY + Math.max(0, (tabHeight - controlHeight) / 2);
|
||||
}
|
||||
|
||||
private static int getVisibleCapacity(AbstractContainerScreen<?> screen, Player player, DockLayout layout, boolean reserveScrollControl) {
|
||||
if (layout.floating()) {
|
||||
return Math.max(1, layout.maxTabs());
|
||||
@@ -920,16 +1081,6 @@ public final class BagTabOverlay {
|
||||
return control == null ? null : control.hoveredIcon(mouseX, mouseY);
|
||||
}
|
||||
|
||||
private static void renderManagedTooltip(GuiGraphics g, AbstractContainerScreen<?> screen, List<?> lines, int mouseX, int mouseY) {
|
||||
@SuppressWarnings("unchecked")
|
||||
List<net.minecraft.util.FormattedCharSequence> formatted = (List<net.minecraft.util.FormattedCharSequence>) lines;
|
||||
if (usesDefaultTooltipPlacement(screen)) {
|
||||
g.renderTooltip(Minecraft.getInstance().font, formatted, mouseX, mouseY);
|
||||
return;
|
||||
}
|
||||
g.renderTooltip(Minecraft.getInstance().font, formatted, createTooltipPositioner(screen), mouseX, mouseY);
|
||||
}
|
||||
|
||||
private static void blitScaled(GuiGraphics g, ResourceLocation texture, int x, int y, int width, int height, int u, int v, int regionWidth, int regionHeight, int textureWidth, int textureHeight) {
|
||||
g.pose().pushPose();
|
||||
g.pose().translate(x, y, 0.0F);
|
||||
@@ -962,8 +1113,8 @@ public final class BagTabOverlay {
|
||||
private static void pushTabTransform(GuiGraphics g, RenderedTab tab) {
|
||||
g.pose().translate(tab.x() + (tab.width() / 2.0F), tab.y() + (tab.height() / 2.0F), 0.0F);
|
||||
g.pose().mulPose(Axis.ZP.rotationDegrees(tab.rotationDegrees()));
|
||||
g.pose().scale(tab.width() / (float) BASE_TAB_WIDTH, tab.height() / (float) BASE_TAB_HEIGHT, 1.0F);
|
||||
g.pose().translate(-(BASE_TAB_WIDTH / 2.0F), -(BASE_TAB_HEIGHT / 2.0F), 0.0F);
|
||||
g.pose().scale(tab.width() / (float) tab.baseTextureWidth(), tab.height() / (float) tab.baseTextureHeight(), 1.0F);
|
||||
g.pose().translate(-(tab.baseTextureWidth() / 2.0F), -(tab.baseTextureHeight() / 2.0F), 0.0F);
|
||||
}
|
||||
|
||||
private static int getInventoryTopBound(AbstractContainerScreen<?> screen, Player player) {
|
||||
@@ -995,22 +1146,11 @@ public final class BagTabOverlay {
|
||||
return screen.getClass().getName();
|
||||
}
|
||||
|
||||
private static ClientTooltipPositioner createTooltipPositioner(AbstractContainerScreen<?> screen) {
|
||||
return new MenuTooltipPositioner(new ScreenRectangle(screen.getGuiLeft(), screen.getGuiTop(), screen.getXSize(), screen.getYSize()));
|
||||
}
|
||||
|
||||
private static boolean usesDefaultTooltipPlacement(AbstractContainerScreen<?> screen) {
|
||||
DockConfigManager.DockSide dockSide = DockConfigManager.getEffectiveSettings(getScreenKey(screen)).dockSide();
|
||||
return dockSide == DockConfigManager.DockSide.BOTTOM
|
||||
|| dockSide == DockConfigManager.DockSide.SCREEN_BOTTOM
|
||||
|| dockSide == DockConfigManager.DockSide.FLOATING_HORIZONTAL;
|
||||
}
|
||||
|
||||
private static int clamp(int value, int min, int max) {
|
||||
return Math.max(min, Math.min(max, value));
|
||||
}
|
||||
|
||||
private record RenderedTab(BagEntry entry, int x, int y, boolean selected, boolean pinned, DockConfigManager.DockSide dockSide, float scale, int width, int height) {
|
||||
private record RenderedTab(BagEntry entry, int x, int y, boolean selected, boolean pinned, DockConfigManager.DockSide dockSide, float scale, int width, int height, boolean compact) {
|
||||
private boolean isHovered(double mouseX, double mouseY) {
|
||||
return mouseX >= this.x && mouseX < this.x + this.width && mouseY >= this.y && mouseY < this.y + this.height;
|
||||
}
|
||||
@@ -1018,7 +1158,7 @@ public final class BagTabOverlay {
|
||||
return this.isVertical() ? this.y + (this.height / 2.0D) : this.x + (this.width / 2.0D);
|
||||
}
|
||||
private RenderedTab withPosition(int newX, int newY) {
|
||||
return new RenderedTab(this.entry, newX, newY, this.selected, this.pinned, this.dockSide, this.scale, this.width, this.height);
|
||||
return new RenderedTab(this.entry, newX, newY, this.selected, this.pinned, this.dockSide, this.scale, this.width, this.height, this.compact);
|
||||
}
|
||||
private boolean isVertical() {
|
||||
return this.dockSide == DockConfigManager.DockSide.LEFT
|
||||
@@ -1028,6 +1168,13 @@ public final class BagTabOverlay {
|
||||
|| this.dockSide == DockConfigManager.DockSide.FLOATING_VERTICAL;
|
||||
}
|
||||
private float rotationDegrees() {
|
||||
if (this.compact) {
|
||||
return switch (this.dockSide) {
|
||||
case TOP, SCREEN_BOTTOM -> 180.0F;
|
||||
case RIGHT, SCREEN_LEFT -> 180.0F;
|
||||
default -> 0.0F;
|
||||
};
|
||||
}
|
||||
return switch (this.dockSide) {
|
||||
case TOP -> 180.0F;
|
||||
case LEFT -> 90.0F;
|
||||
@@ -1044,6 +1191,40 @@ public final class BagTabOverlay {
|
||||
return this.dockSide == DockConfigManager.DockSide.FLOATING_HORIZONTAL
|
||||
|| this.dockSide == DockConfigManager.DockSide.FLOATING_VERTICAL;
|
||||
}
|
||||
private boolean usesCompactSideArt() {
|
||||
return this.compact && this.isVertical();
|
||||
}
|
||||
private int baseTextureWidth() {
|
||||
return this.compact ? BASE_COMPACT_TAB_WIDTH : BASE_TAB_WIDTH;
|
||||
}
|
||||
private int baseTextureHeight() {
|
||||
return this.compact ? BASE_COMPACT_TAB_HEIGHT : BASE_TAB_HEIGHT;
|
||||
}
|
||||
private ResourceLocation baseTexture() {
|
||||
if (!this.compact) {
|
||||
return TAB_BASE_TEXTURE;
|
||||
}
|
||||
return this.usesCompactSideArt() ? COMPACT_SIDE_TAB_BASE_TEXTURE : COMPACT_TAB_BASE_TEXTURE;
|
||||
}
|
||||
private ResourceLocation overlayTexture() {
|
||||
if (!this.compact) {
|
||||
return TAB_OVERLAY_TEXTURE;
|
||||
}
|
||||
return this.usesCompactSideArt() ? COMPACT_SIDE_TAB_OVERLAY_TEXTURE : COMPACT_TAB_OVERLAY_TEXTURE;
|
||||
}
|
||||
private Edge compactScreenOuterEdge() {
|
||||
return switch (this.dockSide) {
|
||||
case TOP, SCREEN_TOP -> Edge.TOP;
|
||||
case BOTTOM, SCREEN_BOTTOM -> Edge.BOTTOM;
|
||||
case LEFT, SCREEN_LEFT -> Edge.LEFT;
|
||||
case RIGHT, SCREEN_RIGHT -> Edge.RIGHT;
|
||||
case FLOATING_VERTICAL -> Edge.RIGHT;
|
||||
default -> Edge.BOTTOM;
|
||||
};
|
||||
}
|
||||
private Edge compactLocalOuterEdge() {
|
||||
return this.rotationDegrees() == 180.0F ? this.compactScreenOuterEdge().opposite() : this.compactScreenOuterEdge();
|
||||
}
|
||||
private ScreenPoint transformedPoint(int localX, int localY) {
|
||||
double radians = Math.toRadians(this.rotationDegrees());
|
||||
double centerX = this.x + (this.width / 2.0D);
|
||||
@@ -1070,10 +1251,10 @@ public final class BagTabOverlay {
|
||||
private record DragState(int draggedSlot, double grabOffsetX, double grabOffsetY, List<String> previewOrder) {
|
||||
}
|
||||
|
||||
private record TabStrip(List<BagEntry> allEntries, List<RenderedTab> tabs, DockLayout layout, DockControl control, ScrollControl scrollControl, int scrollOffset, int maxScrollOffset) {
|
||||
private record TabStrip(List<BagEntry> allEntries, List<RenderedTab> tabs, DockLayout layout, DockControl control, ScrollControl scrollControl, int scrollOffset, int maxScrollOffset, int activeBagSlot) {
|
||||
}
|
||||
|
||||
private record DockLayout(int firstTabX, int firstTabY, int tabWidth, int tabHeight, int controlX, int controlY, int controlWidth, int controlHeight, float scale, DockConfigManager.DockSide dockSide, boolean vertical, int maxTabs, boolean floating) {
|
||||
private record DockLayout(int firstTabX, int firstTabY, int tabWidth, int tabHeight, int controlX, int controlY, int controlWidth, int controlHeight, float scale, DockConfigManager.DockSide dockSide, boolean vertical, int maxTabs, boolean floating, boolean compact) {
|
||||
}
|
||||
|
||||
private record PendingCursorRestore(double guiX, double guiY, long expiresAt) {
|
||||
@@ -1105,6 +1286,7 @@ public final class BagTabOverlay {
|
||||
case SCREEN_TOP -> 0.0F;
|
||||
case SCREEN_LEFT -> -90.0F;
|
||||
case SCREEN_RIGHT -> 90.0F;
|
||||
case FLOATING_VERTICAL -> 90.0F;
|
||||
default -> 0.0F;
|
||||
};
|
||||
}
|
||||
@@ -1138,6 +1320,7 @@ public final class BagTabOverlay {
|
||||
case SCREEN_TOP -> 0.0F;
|
||||
case SCREEN_LEFT -> -90.0F;
|
||||
case SCREEN_RIGHT -> 90.0F;
|
||||
case FLOATING_VERTICAL -> 90.0F;
|
||||
default -> 0.0F;
|
||||
};
|
||||
}
|
||||
@@ -1146,6 +1329,22 @@ public final class BagTabOverlay {
|
||||
private record ScreenPoint(int x, int y) {
|
||||
}
|
||||
|
||||
private enum Edge {
|
||||
TOP,
|
||||
BOTTOM,
|
||||
LEFT,
|
||||
RIGHT;
|
||||
|
||||
private Edge opposite() {
|
||||
return switch (this) {
|
||||
case TOP -> BOTTOM;
|
||||
case BOTTOM -> TOP;
|
||||
case LEFT -> RIGHT;
|
||||
case RIGHT -> LEFT;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
private enum DockIcon {
|
||||
CONFIG,
|
||||
LOCK
|
||||
|
||||
@@ -183,6 +183,7 @@ public final class DockConfigManager {
|
||||
private int xOffset = 0;
|
||||
private int yOffset = 0;
|
||||
private int maxTabs = 8;
|
||||
private boolean compact = false;
|
||||
|
||||
public DockSide dockSide() {
|
||||
return dockSide;
|
||||
@@ -200,6 +201,10 @@ public final class DockConfigManager {
|
||||
return maxTabs;
|
||||
}
|
||||
|
||||
public boolean compact() {
|
||||
return compact;
|
||||
}
|
||||
|
||||
public DockSettings withDockSide(DockSide nextDockSide) {
|
||||
DockSettings copy = copy();
|
||||
copy.dockSide = nextDockSide;
|
||||
@@ -224,12 +229,19 @@ public final class DockConfigManager {
|
||||
return copy;
|
||||
}
|
||||
|
||||
public DockSettings withCompact(boolean nextCompact) {
|
||||
DockSettings copy = copy();
|
||||
copy.compact = nextCompact;
|
||||
return copy;
|
||||
}
|
||||
|
||||
public DockSettings copy() {
|
||||
DockSettings copy = new DockSettings();
|
||||
copy.dockSide = this.dockSide;
|
||||
copy.xOffset = this.xOffset;
|
||||
copy.yOffset = this.yOffset;
|
||||
copy.maxTabs = this.maxTabs;
|
||||
copy.compact = this.compact;
|
||||
return copy;
|
||||
}
|
||||
|
||||
|
||||
@@ -22,6 +22,7 @@ public class DockConfigScreen extends Screen {
|
||||
private Button yPlusButton;
|
||||
private Button maxTabsMinusButton;
|
||||
private Button maxTabsPlusButton;
|
||||
private Button compactButton;
|
||||
private Button resetButton;
|
||||
|
||||
public DockConfigScreen(Screen parent) {
|
||||
@@ -65,15 +66,21 @@ public class DockConfigScreen extends Screen {
|
||||
this.maxTabsPlusButton = this.addRenderableWidget(Button.builder(Component.literal("+"), button -> adjustMaxTabs(1))
|
||||
.bounds(centerX + 70, top + 108, 20, 20).build());
|
||||
|
||||
this.compactButton = this.addRenderableWidget(Button.builder(Component.empty(), button -> {
|
||||
this.dockSettings = this.dockSettings.withCompact(!this.dockSettings.compact());
|
||||
saveCurrent();
|
||||
syncLabels();
|
||||
}).bounds(centerX - 90, top + 134, 180, 20).build());
|
||||
|
||||
this.resetButton = this.addRenderableWidget(Button.builder(BagTabs.translation("dock.reset"), button -> {
|
||||
DockConfigManager.clearOverride(this.screenKey);
|
||||
this.editOverride = false;
|
||||
this.dockSettings = DockConfigManager.getEditableSettings(this.screenKey, false).copy();
|
||||
syncLabels();
|
||||
}).bounds(centerX - 90, top + 138, 180, 20).build());
|
||||
}).bounds(centerX - 90, top + 160, 180, 20).build());
|
||||
|
||||
this.addRenderableWidget(Button.builder(BagTabs.translation("dock.done"), button -> onClose())
|
||||
.bounds(centerX - 90, top + 164, 180, 20).build());
|
||||
.bounds(centerX - 90, top + 186, 180, 20).build());
|
||||
|
||||
syncLabels();
|
||||
Tooltip offsetTooltip = Tooltip.create(BagTabs.translation("dock.offset_steps"));
|
||||
@@ -151,6 +158,7 @@ public class DockConfigScreen extends Screen {
|
||||
? BagTabs.translation("dock.target.override")
|
||||
: BagTabs.translation("dock.target.global"));
|
||||
this.sideButton.setMessage(BagTabs.translation("dock.side." + this.dockSettings.dockSide().name().toLowerCase()));
|
||||
this.compactButton.setMessage(BagTabs.translation(this.dockSettings.compact() ? "dock.compact.on" : "dock.compact.off"));
|
||||
this.resetButton.active = this.editOverride || DockConfigManager.hasOverride(this.screenKey);
|
||||
}
|
||||
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
"bagtabs.dock.x_offset": "X Offset",
|
||||
"bagtabs.dock.y_offset": "Y Offset",
|
||||
"bagtabs.dock.max_tabs": "Max Tabs",
|
||||
"bagtabs.dock.compact.on": "Layout: Compact",
|
||||
"bagtabs.dock.compact.off": "Layout: Normal",
|
||||
"bagtabs.dock.open": "Open dock settings",
|
||||
"bagtabs.dock.lock": "Lock tab interactions",
|
||||
"bagtabs.dock.unlock": "Unlock tab interactions",
|
||||
@@ -31,5 +33,7 @@
|
||||
"bagtabs.dock.side.screen_left": "Dock: Screen Left",
|
||||
"bagtabs.dock.side.screen_right": "Dock: Screen Right",
|
||||
"bagtabs.dock.side.floating_horizontal": "Dock: Floating Horizontal",
|
||||
"bagtabs.dock.side.floating_vertical": "Dock: Floating Vertical"
|
||||
"bagtabs.dock.side.floating_vertical": "Dock: Floating Vertical",
|
||||
"key.categories.bagtabs": "Bag Tabs",
|
||||
"key.bagtabs.open_last_bag": "Open Last Bag"
|
||||
}
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 399 B |
Binary file not shown.
|
After Width: | Height: | Size: 401 B |
Binary file not shown.
|
After Width: | Height: | Size: 408 B |
Binary file not shown.
|
After Width: | Height: | Size: 423 B |
Reference in New Issue
Block a user