UI polishes

add compact mode for all orientations
This commit is contained in:
trunksbomb
2026-03-23 01:55:51 -04:00
parent 56fcb49f4d
commit 966e199a23
9 changed files with 311 additions and 58 deletions

View File

@@ -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;

View File

@@ -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

View File

@@ -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;
}

View File

@@ -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);
}

View File

@@ -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