tidying up UI and UX

Add dock tab for locking the dock and opening settings, where menu dock location and offset can be moved.
This commit is contained in:
trunksbomb
2026-03-22 21:44:01 -04:00
parent 85a7e1a48b
commit b51b41206c
10 changed files with 640 additions and 31 deletions

View File

@@ -16,7 +16,9 @@ import java.util.List;
import java.util.Set;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.GuiGraphics;
import net.minecraft.client.gui.screens.Screen;
import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen;
import net.minecraft.core.component.DataComponents;
import net.minecraft.client.resources.sounds.SimpleSoundInstance;
import net.minecraft.ChatFormatting;
import net.minecraft.network.chat.Component;
@@ -32,8 +34,15 @@ import net.neoforged.neoforge.network.PacketDistributor;
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 int TAB_WIDTH = 22;
private static final int TAB_HEIGHT = 22;
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");
private static final ResourceLocation GEAR_ICON_TEXTURE = BagTabs.id("textures/gui/dock_gear.png");
private static final int BASE_TAB_WIDTH = 22;
private static final int BASE_TAB_HEIGHT = 22;
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;
private static final int TAB_GAP = 0;
private static final int TAB_Y_OFFSET = -3;
private static final int TAB_X_OFFSET = -6;
@@ -67,9 +76,15 @@ public final class BagTabOverlay {
int mouseX = event.getMouseX();
int mouseY = event.getMouseY();
ItemStack carriedStack = screen.getMenu().getCarried();
DockLayout layout = getDockLayout(screen, player, tabs.size());
DockControl control = getDockControl(layout);
refreshInsertTargets(screen, carriedStack);
if (control != null) {
renderDockControl(guiGraphics, control, mouseX, mouseY);
}
RenderedTab draggedTab = null;
for (RenderedTab tab : tabs) {
if (dragState != null && tab.entry().slot() == dragState.draggedSlot()) {
@@ -77,7 +92,7 @@ public final class BagTabOverlay {
continue;
}
renderTab(guiGraphics, tab, mouseX, mouseY, carriedStack, false);
guiGraphics.renderItem(tab.entry().stack(), tab.x() + 3, tab.y() + 2);
guiGraphics.renderItem(tab.entry().stack(), tab.itemX(), tab.itemY());
}
if (draggedTab != null) {
@@ -86,11 +101,24 @@ public final class BagTabOverlay {
guiGraphics.pose().pushPose();
guiGraphics.pose().translate(0.0F, 0.0F, 200.0F);
renderTab(guiGraphics, floatingTab, mouseX, mouseY, carriedStack, true);
guiGraphics.renderItem(draggedTab.entry().stack(), dragX + 3, draggedTab.y() + 2);
guiGraphics.renderItem(draggedTab.entry().stack(), floatingTab.itemX(), floatingTab.itemY());
guiGraphics.pose().popPose();
}
if (dragState == null) {
if (control != null) {
DockIcon hoveredIcon = control.hoveredIcon(mouseX, mouseY);
if (hoveredIcon != null) {
guiGraphics.renderTooltip(
Minecraft.getInstance().font,
List.of(getDockTooltip(hoveredIcon).getVisualOrderText()),
mouseX,
mouseY
);
return;
}
}
for (RenderedTab tab : tabs) {
if (!tab.isHovered(mouseX, mouseY)) {
continue;
@@ -118,8 +146,32 @@ public final class BagTabOverlay {
}
List<RenderedTab> tabs = getRenderedTabs(screen, player);
DockControl control = getDockControl(getDockLayout(screen, player, tabs.size()));
if (event.getButton() == 0) {
if (control != null) {
DockIcon clickedIcon = control.hoveredIcon(event.getMouseX(), event.getMouseY());
if (clickedIcon == DockIcon.LOCK) {
DockConfigManager.toggleInteractionsLocked();
dragState = null;
pendingClick = null;
Minecraft.getInstance().getSoundManager().play(SimpleSoundInstance.forUI(SoundEvents.UI_BUTTON_CLICK, 1.0F));
event.setCanceled(true);
return;
}
if (clickedIcon == DockIcon.CONFIG) {
Minecraft.getInstance().setScreen(new DockConfigScreen(screen));
Minecraft.getInstance().getSoundManager().play(SimpleSoundInstance.forUI(SoundEvents.UI_BUTTON_CLICK, 1.0F));
event.setCanceled(true);
return;
}
}
}
if (event.getButton() == 1) {
if (DockConfigManager.isInteractionsLocked()) {
return;
}
for (RenderedTab tab : tabs) {
if (!tab.isHovered(event.getMouseX(), event.getMouseY())) {
continue;
@@ -143,7 +195,7 @@ public final class BagTabOverlay {
for (RenderedTab tab : tabs) {
if (tab.isHovered(event.getMouseX(), event.getMouseY())) {
pendingClick = new PendingClick(tab.entry().slot(), event.getMouseX() - tab.x(), event.getMouseY() - tab.y(), tab.pinned());
pendingClick = new PendingClick(tab.entry().slot(), event.getMouseX() - tab.x(), event.getMouseY() - tab.y(), tab.pinned() && !DockConfigManager.isInteractionsLocked());
event.setCanceled(true);
return;
}
@@ -257,11 +309,10 @@ public final class BagTabOverlay {
}
List<RenderedTab> renderedTabs = new ArrayList<>();
int activeBagSlot = getActiveBagSlot(screen);
int leftBound = getInventoryLeftBound(screen, player);
int rightBound = getInventoryRightBound(screen, player);
int x = leftBound + TAB_X_OFFSET;
int y = screen.getGuiTop() + screen.getYSize() + TAB_Y_OFFSET;
int maxX = rightBound - TAB_WIDTH;
DockLayout layout = getDockLayout(screen, player, bags.size());
int x = layout.firstTabX();
int y = layout.tabY();
int maxX = getMaxTabX(screen, player, layout);
for (BagEntry bag : bags) {
if (x > maxX) {
@@ -269,8 +320,18 @@ public final class BagTabOverlay {
}
boolean pinned = TabPinManager.isPinned(bag, bags);
renderedTabs.add(new RenderedTab(bag, x, y, bag.slot() == activeBagSlot, pinned));
x += TAB_WIDTH + TAB_GAP;
renderedTabs.add(new RenderedTab(
bag,
x,
y,
bag.slot() == activeBagSlot,
pinned,
x + Math.max(2, Math.round(layout.scale() * 3.0F)),
y + Math.max(1, Math.round(layout.scale() * 2.0F)),
layout.tabWidth(),
layout.tabHeight()
));
x += layout.tabWidth() + TAB_GAP;
}
return renderedTabs;
@@ -321,24 +382,29 @@ public final class BagTabOverlay {
private static void renderTab(GuiGraphics guiGraphics, RenderedTab tab, int mouseX, int mouseY, ItemStack carriedStack, boolean dragged) {
boolean hovered = tab.isHovered(mouseX, mouseY);
boolean selected = tab.selected();
int color = DyedItemColor.getOrDefault(tab.entry().stack(), BagItem.DEFAULT_COLOR);
float red = ((color >> 16) & 0xFF) / 255.0F;
float green = ((color >> 8) & 0xFF) / 255.0F;
float blue = (color & 0xFF) / 255.0F;
int uOffset = (hovered || selected) ? TAB_WIDTH : 0;
float red = 1.0F;
float green = 1.0F;
float blue = 1.0F;
if (tab.entry().stack().has(DataComponents.DYED_COLOR)) {
int color = DyedItemColor.getOrDefault(tab.entry().stack(), BagItem.DEFAULT_COLOR);
red = ((color >> 16) & 0xFF) / 255.0F;
green = ((color >> 8) & 0xFF) / 255.0F;
blue = (color & 0xFF) / 255.0F;
}
int uOffset = (hovered || selected) ? BASE_TAB_WIDTH : 0;
RenderSystem.setShaderColor(red, green, blue, 1.0F);
guiGraphics.blit(TAB_BASE_TEXTURE, tab.x(), tab.y(), uOffset, 0, TAB_WIDTH, TAB_HEIGHT, TAB_WIDTH * 2, TAB_HEIGHT);
blitScaled(guiGraphics, 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);
RenderSystem.setShaderColor(1.0F, 1.0F, 1.0F, 1.0F);
guiGraphics.blit(TAB_OVERLAY_TEXTURE, tab.x(), tab.y(), uOffset, 0, TAB_WIDTH, TAB_HEIGHT, TAB_WIDTH * 2, TAB_HEIGHT);
blitScaled(guiGraphics, 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);
if (dragged) {
guiGraphics.fill(tab.x() + 1, tab.y(), tab.x() + TAB_WIDTH - 1, tab.y() + 1, 0xFF000000);
guiGraphics.fill(tab.x() + 2, tab.y() + 1, tab.x() + TAB_WIDTH - 2, tab.y() + 2, 0x80FFFFFF);
guiGraphics.fill(tab.x() + 1, tab.y(), tab.x() + tab.width() - 1, tab.y() + 1, 0xFF000000);
guiGraphics.fill(tab.x() + 2, tab.y() + 1, tab.x() + tab.width() - 2, tab.y() + 2, 0x80FFFFFF);
}
if (selected) {
guiGraphics.fill(tab.x() + 2, tab.y() + 2, tab.x() + TAB_WIDTH - 2, tab.y() + 4, 0x90FFFFFF);
guiGraphics.fill(tab.x() + 3, tab.y() + tab.height() - 4, tab.x() + tab.width() - 3, tab.y() + tab.height() - 2, 0xFFD94A4A);
}
if (tab.pinned()) {
@@ -349,9 +415,9 @@ public final class BagTabOverlay {
if (!carriedStack.isEmpty()) {
if (INSERTABLE_SLOTS.contains(tab.entry().slot())) {
renderPlusIndicator(guiGraphics, tab.x() + 15, tab.y() + 3);
renderPlusIndicator(guiGraphics, tab.x() + tab.width() - 7, tab.y() + 3);
} else {
renderXIndicator(guiGraphics, tab.x() + 15, tab.y() + 3);
renderXIndicator(guiGraphics, tab.x() + tab.width() - 7, tab.y() + 3);
}
}
}
@@ -410,10 +476,16 @@ public final class BagTabOverlay {
lines.add(tab.entry().stack().getHoverName());
lines.add(Component.literal("Left-click: open"));
if (tab.pinned()) {
lines.add(Component.literal("Drag: reorder pinned tabs"));
lines.add(Component.literal("Right-click: unpin"));
if (!DockConfigManager.isInteractionsLocked()) {
lines.add(Component.literal("Drag: reorder pinned tabs"));
lines.add(Component.literal("Right-click: unpin"));
}
} else {
String failureReason = TabPinManager.getPinFailureReason(tab.entry(), tabs.stream().map(RenderedTab::entry).toList());
if (DockConfigManager.isInteractionsLocked()) {
return lines;
}
if (failureReason == null) {
lines.add(Component.literal("Right-click: pin"));
} else {
@@ -484,17 +556,157 @@ public final class BagTabOverlay {
}
}
private record RenderedTab(BagEntry entry, int x, int y, boolean selected, boolean pinned) {
private static DockLayout getDockLayout(AbstractContainerScreen<?> screen, Player player, int bagCount) {
DockConfigManager.DockSettings dockSettings = DockConfigManager.getEffectiveSettings(getScreenKey(screen));
float scale = dockSettings.scalePercent() / 100.0F;
int tabWidth = Math.max(16, Math.round(BASE_TAB_WIDTH * scale));
int tabHeight = Math.max(16, Math.round(BASE_TAB_HEIGHT * scale));
int controlWidth = Math.max(14, Math.round(BASE_DOCK_WIDTH * scale));
int controlHeight = Math.max(16, Math.round(BASE_DOCK_HEIGHT * scale));
int leftBound = getInventoryLeftBound(screen, player);
int rightBound = getInventoryRightBound(screen, player);
int topBound = getInventoryTopBound(screen, player);
int bottomBound = getInventoryBottomBound(screen, player);
int controlStripWidth = controlWidth;
int firstTabX;
int tabY;
switch (dockSettings.dockSide()) {
case TOP -> {
firstTabX = leftBound + TAB_X_OFFSET + dockSettings.xOffset() + controlStripWidth;
tabY = topBound - tabHeight + dockSettings.yOffset();
}
case LEFT -> {
firstTabX = leftBound - (bagCount * tabWidth) + dockSettings.xOffset() + controlStripWidth - controlStripWidth;
tabY = bottomBound - tabHeight + dockSettings.yOffset();
}
case RIGHT -> {
firstTabX = rightBound + dockSettings.xOffset() + controlStripWidth;
tabY = bottomBound - tabHeight + dockSettings.yOffset();
}
case FLOATING -> {
int totalWidth = (bagCount * tabWidth) + controlStripWidth;
int controlX = (screen.width / 2) - (totalWidth / 2) + dockSettings.xOffset();
firstTabX = controlX + controlStripWidth;
tabY = (screen.height / 2) + dockSettings.yOffset();
}
case BOTTOM -> {
firstTabX = leftBound + TAB_X_OFFSET + dockSettings.xOffset() + controlStripWidth;
tabY = bottomBound + TAB_Y_OFFSET + dockSettings.yOffset() + 8;
}
default -> {
firstTabX = leftBound + TAB_X_OFFSET + dockSettings.xOffset() + controlStripWidth;
tabY = bottomBound + TAB_Y_OFFSET + dockSettings.yOffset() + 8;
}
}
return new DockLayout(firstTabX, tabY, tabWidth, tabHeight, controlWidth, controlHeight, scale);
}
private static DockControl getDockControl(DockLayout layout) {
int y = layout.tabY() + Math.max(0, (layout.tabHeight() - layout.controlHeight()) / 2);
int x = layout.firstTabX() - layout.controlWidth() - 2;
return new DockControl(x, y, layout.controlWidth(), layout.controlHeight(), layout.scale());
}
private static void renderDockControl(GuiGraphics guiGraphics, DockControl control, int mouseX, int mouseY) {
DockIcon hoveredIcon = control.hoveredIcon(mouseX, mouseY);
blitScaled(
guiGraphics,
DOCK_TEXTURE,
control.x(),
control.y(),
control.width(),
control.height(),
0,
0,
BASE_DOCK_WIDTH,
BASE_DOCK_HEIGHT,
BASE_DOCK_WIDTH,
BASE_DOCK_HEIGHT
);
if (hoveredIcon != null) {
if (hoveredIcon == DockIcon.CONFIG) {
guiGraphics.fill(control.x() + 2, control.y() + 1, control.x() + control.width() - 2, control.y() + (control.height() / 2), 0x22FFFFFF);
} else {
guiGraphics.fill(control.x() + 2, control.y() + (control.height() / 2), control.x() + control.width() - 2, control.y() + control.height() - 3, 0x22FFFFFF);
}
}
int iconSize = Math.max(6, Math.min(Math.round(8 * control.scale()), control.width() - 4));
int iconX = control.x() + (control.width() - iconSize) / 2;
int topSectionHeight = control.height() / 2;
int bottomSectionHeight = control.height() - topSectionHeight;
int gearY = control.y() + Math.max(1, (topSectionHeight - iconSize) / 2);
int lockY = control.y() + topSectionHeight + Math.max(1, (bottomSectionHeight - iconSize) / 2);
blitScaled(guiGraphics, GEAR_ICON_TEXTURE, iconX, gearY, iconSize, iconSize, 0, 0, BASE_DOCK_ICON_SIZE, BASE_DOCK_ICON_SIZE, BASE_DOCK_ICON_SIZE, BASE_DOCK_ICON_SIZE);
blitScaled(guiGraphics, DockConfigManager.isInteractionsLocked() ? UNLOCK_ICON_TEXTURE : LOCK_ICON_TEXTURE, iconX, lockY, iconSize, iconSize, 0, 0, BASE_DOCK_ICON_SIZE, BASE_DOCK_ICON_SIZE, BASE_DOCK_ICON_SIZE, BASE_DOCK_ICON_SIZE);
}
private static Component getDockTooltip(DockIcon icon) {
return switch (icon) {
case CONFIG -> BagTabs.translation("dock.open");
case LOCK -> DockConfigManager.isInteractionsLocked() ? BagTabs.translation("dock.unlock") : BagTabs.translation("dock.lock");
};
}
private static void blitScaled(
GuiGraphics guiGraphics,
ResourceLocation texture,
int x,
int y,
int width,
int height,
int u,
int v,
int regionWidth,
int regionHeight,
int textureWidth,
int textureHeight
) {
guiGraphics.pose().pushPose();
guiGraphics.pose().translate(x, y, 0.0F);
guiGraphics.pose().scale(width / (float) regionWidth, height / (float) regionHeight, 1.0F);
guiGraphics.blit(texture, 0, 0, u, v, regionWidth, regionHeight, textureWidth, textureHeight);
guiGraphics.pose().popPose();
}
private static int getInventoryTopBound(AbstractContainerScreen<?> screen, Player player) {
return screen.getGuiTop() + getPlayerInventorySlots(screen, player).stream()
.mapToInt(slot -> slot.y)
.min()
.orElse(84);
}
private static int getInventoryBottomBound(AbstractContainerScreen<?> screen, Player player) {
return screen.getGuiTop() + getPlayerInventorySlots(screen, player).stream()
.mapToInt(slot -> slot.y + 16)
.max()
.orElse(screen.getYSize());
}
private static int getMaxTabX(AbstractContainerScreen<?> screen, Player player, DockLayout layout) {
int guiRight = screen.getGuiLeft() + screen.getXSize();
int inventoryRight = getInventoryRightBound(screen, player);
return Math.min(guiRight, inventoryRight) - layout.tabWidth();
}
private static String getScreenKey(Screen screen) {
return screen.getClass().getName();
}
private record RenderedTab(BagEntry entry, int x, int y, boolean selected, boolean pinned, int itemX, int itemY, int width, int height) {
private boolean isHovered(double mouseX, double mouseY) {
return mouseX >= this.x && mouseX < this.x + TAB_WIDTH && mouseY >= this.y && mouseY < this.y + TAB_HEIGHT;
return mouseX >= this.x && mouseX < this.x + this.width && mouseY >= this.y && mouseY < this.y + this.height;
}
private double centerX() {
return this.x + (TAB_WIDTH / 2.0D);
return this.x + (this.width / 2.0D);
}
private RenderedTab withPosition(int newX, int newY) {
return new RenderedTab(this.entry, newX, newY, this.selected, this.pinned);
return new RenderedTab(this.entry, newX, newY, this.selected, this.pinned, newX + (this.itemX - this.x), newY + (this.itemY - this.y), this.width, this.height);
}
}
@@ -503,4 +715,22 @@ public final class BagTabOverlay {
private record DragState(int draggedSlot, double grabOffsetX, List<String> previewOrder) {
}
private record DockLayout(int firstTabX, int tabY, int tabWidth, int tabHeight, int controlWidth, int controlHeight, float scale) {
}
private record DockControl(int x, int y, int width, int height, float scale) {
private DockIcon hoveredIcon(double mouseX, double mouseY) {
if (mouseX < this.x || mouseX >= this.x + this.width || mouseY < this.y || mouseY >= this.y + this.height) {
return null;
}
int split = this.y + (this.height / 2);
return mouseY < split ? DockIcon.CONFIG : DockIcon.LOCK;
}
}
private enum DockIcon {
CONFIG,
LOCK,
}
}

View File

@@ -0,0 +1,235 @@
package com.trunksbomb.bagtabs.client;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.trunksbomb.bagtabs.BagTabs;
import java.io.IOException;
import java.io.Reader;
import java.io.Writer;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import net.minecraft.client.Minecraft;
import net.minecraft.client.multiplayer.ServerData;
public final class DockConfigManager {
private static final Gson GSON = new GsonBuilder().setPrettyPrinting().create();
private static SessionSettings cachedSettings = new SessionSettings();
private static Path cachedPath;
private DockConfigManager() {
}
public static DockSettings getEffectiveSettings(String screenKey) {
SessionSettings settings = getSessionSettings();
DockSettings override = settings.screenOverrides.get(screenKey);
return override == null ? settings.globalDefault : override;
}
public static DockSettings getEditableSettings(String screenKey, boolean editOverride) {
SessionSettings settings = getSessionSettings();
if (!editOverride) {
return settings.globalDefault;
}
return settings.screenOverrides.getOrDefault(screenKey, settings.globalDefault.copy());
}
public static boolean hasOverride(String screenKey) {
return getSessionSettings().screenOverrides.containsKey(screenKey);
}
public static void setEditableSettings(String screenKey, boolean editOverride, DockSettings dockSettings) {
SessionSettings settings = getSessionSettings();
if (editOverride) {
settings.screenOverrides.put(screenKey, dockSettings.copy());
} else {
settings.globalDefault = dockSettings.copy();
}
save();
}
public static void clearOverride(String screenKey) {
SessionSettings settings = getSessionSettings();
settings.screenOverrides.remove(screenKey);
save();
}
public static boolean isInteractionsLocked() {
return getSessionSettings().interactionsLocked;
}
public static void setInteractionsLocked(boolean locked) {
SessionSettings settings = getSessionSettings();
settings.interactionsLocked = locked;
save();
}
public static void toggleInteractionsLocked() {
setInteractionsLocked(!isInteractionsLocked());
}
public static int getRememberedPage(String screenKey) {
return getSessionSettings().rememberedPages.getOrDefault(screenKey, 0);
}
public static void setRememberedPage(String screenKey, int page) {
SessionSettings settings = getSessionSettings();
settings.rememberedPages.put(screenKey, Math.max(0, page));
save();
}
private static SessionSettings getSessionSettings() {
Path path = getConfigPath();
if (!Objects.equals(path, cachedPath)) {
cachedPath = path;
cachedSettings = load(path);
}
return cachedSettings;
}
private static SessionSettings load(Path path) {
if (!Files.exists(path)) {
return new SessionSettings();
}
try (Reader reader = Files.newBufferedReader(path)) {
SessionSettings settings = GSON.fromJson(reader, SessionSettings.class);
return settings == null ? new SessionSettings() : settings.normalize();
} catch (IOException exception) {
BagTabs.LOGGER.warn("Failed to load dock config", exception);
return new SessionSettings();
}
}
private static void save() {
if (cachedPath == null) {
cachedPath = getConfigPath();
}
try {
Files.createDirectories(cachedPath.getParent());
try (Writer writer = Files.newBufferedWriter(cachedPath)) {
GSON.toJson(cachedSettings, writer);
}
} catch (IOException exception) {
BagTabs.LOGGER.warn("Failed to save dock config", exception);
}
}
private static Path getConfigPath() {
Minecraft minecraft = Minecraft.getInstance();
String playerKey = minecraft.player != null ? minecraft.player.getUUID().toString() : "unknown-player";
String worldKey = "default";
if (minecraft.hasSingleplayerServer() && minecraft.getSingleplayerServer() != null) {
worldKey = "singleplayer-" + sanitize(minecraft.getSingleplayerServer().getWorldData().getLevelName());
} else {
ServerData currentServer = minecraft.getCurrentServer();
if (currentServer != null) {
worldKey = "server-" + sanitize(currentServer.ip);
}
}
return minecraft.gameDirectory.toPath()
.resolve("config")
.resolve("bagtabs")
.resolve(playerKey)
.resolve(worldKey + ".json");
}
private static String sanitize(String value) {
return value == null ? "unknown" : value.replaceAll("[^a-zA-Z0-9._-]", "_");
}
public enum DockSide {
BOTTOM,
TOP,
LEFT,
RIGHT,
FLOATING;
public DockSide next() {
DockSide[] values = values();
return values[(ordinal() + 1) % values.length];
}
}
public static final class DockSettings {
private DockSide dockSide = DockSide.BOTTOM;
private int xOffset = 0;
private int yOffset = 0;
private int scalePercent = 100;
public DockSide dockSide() {
return dockSide;
}
public int xOffset() {
return xOffset;
}
public int yOffset() {
return yOffset;
}
public int scalePercent() {
return scalePercent;
}
public DockSettings withDockSide(DockSide nextDockSide) {
DockSettings copy = copy();
copy.dockSide = nextDockSide;
return copy;
}
public DockSettings withXOffset(int nextXOffset) {
DockSettings copy = copy();
copy.xOffset = nextXOffset;
return copy;
}
public DockSettings withYOffset(int nextYOffset) {
DockSettings copy = copy();
copy.yOffset = nextYOffset;
return copy;
}
public DockSettings withScalePercent(int nextScalePercent) {
DockSettings copy = copy();
copy.scalePercent = Math.max(75, Math.min(150, nextScalePercent));
return copy;
}
public DockSettings copy() {
DockSettings copy = new DockSettings();
copy.dockSide = this.dockSide;
copy.xOffset = this.xOffset;
copy.yOffset = this.yOffset;
copy.scalePercent = this.scalePercent;
return copy;
}
}
private static final class SessionSettings {
private boolean interactionsLocked = false;
private DockSettings globalDefault = new DockSettings();
private Map<String, DockSettings> screenOverrides = new HashMap<>();
private Map<String, Integer> rememberedPages = new HashMap<>();
private SessionSettings normalize() {
if (globalDefault == null) {
globalDefault = new DockSettings();
}
if (screenOverrides == null) {
screenOverrides = new HashMap<>();
}
if (rememberedPages == null) {
rememberedPages = new HashMap<>();
}
return this;
}
}
}

View File

@@ -0,0 +1,128 @@
package com.trunksbomb.bagtabs.client;
import com.trunksbomb.bagtabs.BagTabs;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.GuiGraphics;
import net.minecraft.client.gui.components.Button;
import net.minecraft.client.gui.screens.Screen;
import net.minecraft.network.chat.Component;
public class DockConfigScreen extends Screen {
private final Screen parent;
private final String screenKey;
private boolean editOverride;
private DockConfigManager.DockSettings dockSettings;
private Button targetButton;
private Button sideButton;
private Button xMinusButton;
private Button xPlusButton;
private Button yMinusButton;
private Button yPlusButton;
private Button scaleMinusButton;
private Button scalePlusButton;
private Button resetButton;
public DockConfigScreen(Screen parent) {
super(BagTabs.translation("dock.title"));
this.parent = parent;
this.screenKey = parent.getClass().getName();
this.editOverride = DockConfigManager.hasOverride(this.screenKey);
this.dockSettings = DockConfigManager.getEditableSettings(this.screenKey, this.editOverride).copy();
}
@Override
protected void init() {
int centerX = this.width / 2;
int top = this.height / 2 - 70;
this.targetButton = this.addRenderableWidget(Button.builder(Component.empty(), button -> {
saveCurrent();
this.editOverride = !this.editOverride;
this.dockSettings = DockConfigManager.getEditableSettings(this.screenKey, this.editOverride).copy();
syncLabels();
}).bounds(centerX - 90, top, 180, 20).build());
this.sideButton = this.addRenderableWidget(Button.builder(Component.empty(), button -> {
this.dockSettings = this.dockSettings.withDockSide(this.dockSettings.dockSide().next());
saveCurrent();
syncLabels();
}).bounds(centerX - 90, top + 26, 180, 20).build());
this.xMinusButton = this.addRenderableWidget(Button.builder(Component.literal("-"), button -> adjustX(-4))
.bounds(centerX - 90, top + 56, 20, 20).build());
this.xPlusButton = this.addRenderableWidget(Button.builder(Component.literal("+"), button -> adjustX(4))
.bounds(centerX + 70, top + 56, 20, 20).build());
this.yMinusButton = this.addRenderableWidget(Button.builder(Component.literal("-"), button -> adjustY(-4))
.bounds(centerX - 90, top + 82, 20, 20).build());
this.yPlusButton = this.addRenderableWidget(Button.builder(Component.literal("+"), button -> adjustY(4))
.bounds(centerX + 70, top + 82, 20, 20).build());
this.scaleMinusButton = this.addRenderableWidget(Button.builder(Component.literal("-"), button -> adjustScale(-5))
.bounds(centerX - 90, top + 108, 20, 20).build());
this.scalePlusButton = this.addRenderableWidget(Button.builder(Component.literal("+"), button -> adjustScale(5))
.bounds(centerX + 70, top + 108, 20, 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());
this.addRenderableWidget(Button.builder(BagTabs.translation("dock.done"), button -> onClose())
.bounds(centerX - 90, top + 164, 180, 20).build());
syncLabels();
}
@Override
public void render(GuiGraphics guiGraphics, int mouseX, int mouseY, float partialTick) {
this.renderBackground(guiGraphics, mouseX, mouseY, partialTick);
super.render(guiGraphics, mouseX, mouseY, partialTick);
int centerX = this.width / 2;
int top = this.height / 2 - 70;
guiGraphics.drawCenteredString(this.font, this.title, centerX, top - 18, 0xFFFFFF);
guiGraphics.drawCenteredString(this.font, Component.literal("X Offset: " + this.dockSettings.xOffset()), centerX, top + 62, 0xFFFFFF);
guiGraphics.drawCenteredString(this.font, Component.literal("Y Offset: " + this.dockSettings.yOffset()), centerX, top + 88, 0xFFFFFF);
guiGraphics.drawCenteredString(this.font, Component.literal("Tab Size: " + this.dockSettings.scalePercent() + "%"), centerX, top + 114, 0xFFFFFF);
}
@Override
public void onClose() {
saveCurrent();
Minecraft.getInstance().setScreen(this.parent);
}
private void adjustX(int delta) {
this.dockSettings = this.dockSettings.withXOffset(this.dockSettings.xOffset() + delta);
saveCurrent();
syncLabels();
}
private void adjustY(int delta) {
this.dockSettings = this.dockSettings.withYOffset(this.dockSettings.yOffset() + delta);
saveCurrent();
syncLabels();
}
private void adjustScale(int delta) {
this.dockSettings = this.dockSettings.withScalePercent(this.dockSettings.scalePercent() + delta);
saveCurrent();
syncLabels();
}
private void syncLabels() {
this.targetButton.setMessage(this.editOverride
? BagTabs.translation("dock.target.override")
: BagTabs.translation("dock.target.global"));
this.sideButton.setMessage(BagTabs.translation("dock.side." + this.dockSettings.dockSide().name().toLowerCase()));
this.resetButton.active = this.editOverride || DockConfigManager.hasOverride(this.screenKey);
}
private void saveCurrent() {
DockConfigManager.setEditableSettings(this.screenKey, this.editOverride, this.dockSettings);
}
}

View File

@@ -7,5 +7,21 @@
"bagtabs.tooltip.click_to_open": "Open from your inventory tabs",
"bagtabs.gui.bag_namer.name": "New Name",
"bagtabs.gui.bag_namer.placeholder": "Leave blank to clear",
"bagtabs.gui.bag_namer.rename": "Rename"
"bagtabs.gui.bag_namer.rename": "Rename",
"bagtabs.dock.title": "Tab Dock Settings",
"bagtabs.dock.done": "Done",
"bagtabs.dock.reset": "Reset Screen Override",
"bagtabs.dock.target.global": "Editing: Global Default",
"bagtabs.dock.target.override": "Editing: This Screen Override",
"bagtabs.dock.x_offset": "X Offset",
"bagtabs.dock.y_offset": "Y Offset",
"bagtabs.dock.scale": "Tab Size",
"bagtabs.dock.open": "Open dock settings",
"bagtabs.dock.lock": "Lock tab interactions",
"bagtabs.dock.unlock": "Unlock tab interactions",
"bagtabs.dock.side.bottom": "Dock: Bottom",
"bagtabs.dock.side.top": "Dock: Top",
"bagtabs.dock.side.left": "Dock: Left",
"bagtabs.dock.side.right": "Dock: Right",
"bagtabs.dock.side.floating": "Dock: Floating"
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 225 B

After

Width:  |  Height:  |  Size: 426 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 429 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 227 B

After

Width:  |  Height:  |  Size: 452 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 592 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 470 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 477 B