From b251d1f444859f960fddf8a2c04ae965874c93d8 Mon Sep 17 00:00:00 2001 From: NotNekodev <97458908+NotNekodev@users.noreply.github.com> Date: Sun, 30 Nov 2025 14:20:47 +0100 Subject: [PATCH 01/21] Add undo button in VOptionScreen.java --- .../vulkanmod/config/gui/VOptionScreen.java | 37 +++++++++++++++++-- .../net/vulkanmod/config/option/Option.java | 16 +++++++- .../vulkanmod/config/option/OptionPage.java | 18 ++++++++- .../assets/vulkanmod/lang/en_us.json | 1 + 4 files changed, 66 insertions(+), 6 deletions(-) diff --git a/src/main/java/net/vulkanmod/config/gui/VOptionScreen.java b/src/main/java/net/vulkanmod/config/gui/VOptionScreen.java index fcf879d71..b6d2f1101 100644 --- a/src/main/java/net/vulkanmod/config/gui/VOptionScreen.java +++ b/src/main/java/net/vulkanmod/config/gui/VOptionScreen.java @@ -41,10 +41,12 @@ public class VOptionScreen extends Screen { private VButtonWidget doneButton; private VButtonWidget applyButton; + private VButtonWidget undoButton; private final List pageButtons = Lists.newArrayList(); private final List buttons = Lists.newArrayList(); + public VOptionScreen(Component title, Screen parent) { super(title); this.parent = parent; @@ -83,20 +85,19 @@ private void addPages() { @Override protected void init() { this.addPages(); + this.captureOriginalState(); int top = 40; int bottom = 60; int itemHeight = 20; int leftMargin = 100; -// int listWidth = (int) (this.width * 0.65f); int listWidth = Math.min((int) (this.width * 0.65f), 420); int listHeight = this.height - top - bottom; this.buildLists(leftMargin, top, listWidth, listHeight, itemHeight); int x = leftMargin + listWidth + 10; -// int width = Math.min(this.width - this.tooltipX - 10, 200); int width = this.width - x - 10; int y = 50; @@ -113,6 +114,21 @@ protected void init() { buildPage(); this.applyButton.active = false; + this.undoButton.active = false; + } + + private void captureOriginalState() { + for (OptionPage page : this.optionPages) { + page.captureOriginalState(); + } + } + + private void undo() { + for (OptionPage page : this.optionPages) { + page.resetToOriginalState(); + } + + buildPage(); } private void buildLists(int left, int top, int listWidth, int listHeight, int itemHeight) { @@ -146,7 +162,6 @@ private void buildPage() { this.pageButtons.clear(); this.clearWidgets(); -// this.addPageButtons(20, 6, 60, 20, false); this.addPageButtons(10, 40, 80, 22, true); VOptionList currentList = this.optionPages.get(this.currentListIdx).getOptionList(); @@ -180,6 +195,17 @@ private void addButtons() { button -> this.applyOptions() ); + buttonWidth = minecraft.font.width(Component.translatable("vulkanmod.options.buttons.undo")) + 2 * padding; + x0 -= (buttonWidth + buttonMargin); + this.undoButton = new VButtonWidget( + x0, y0, + buttonWidth, buttonHeight, + Component.translatable("vulkanmod.options.buttons.undo"), + button -> { + undo(); + } + ); + buttonWidth = minecraft.font.width(Component.translatable("vulkanmod.options.buttons.kofi")) + 10; x0 = (this.width - buttonWidth - rightMargin); this.supportButton = new VButtonWidget( @@ -192,10 +218,12 @@ private void addButtons() { this.buttons.add(this.applyButton); this.buttons.add(this.doneButton); this.buttons.add(this.supportButton); + this.buttons.add(this.undoButton); this.addWidget(this.applyButton); this.addWidget(this.doneButton); this.addWidget(this.supportButton); + this.addWidget(this.undoButton); } @Override @@ -292,6 +320,7 @@ private void updateState() { } this.applyButton.active = modified; + this.undoButton.active = modified; } private void setOptionList(int i) { @@ -310,4 +339,4 @@ private void applyOptions() { Initializer.CONFIG.write(); } -} +} \ No newline at end of file diff --git a/src/main/java/net/vulkanmod/config/option/Option.java b/src/main/java/net/vulkanmod/config/option/Option.java index a1425ccf8..274c4cac3 100644 --- a/src/main/java/net/vulkanmod/config/option/Option.java +++ b/src/main/java/net/vulkanmod/config/option/Option.java @@ -16,6 +16,7 @@ public abstract class Option { protected T value; protected T newValue; + protected T originalValue; protected Function translator; @@ -91,6 +92,19 @@ public void apply() { this.value = this.newValue; } + public void captureOriginalState() { + this.originalValue = this.value; + } + + public void resetToOriginalState() { + if (this.originalValue != null) { + this.newValue = this.originalValue; + + if (onChange != null) + onChange.run(); + } + } + public T getNewValue() { return this.newValue; } @@ -107,4 +121,4 @@ public Option setTooltip(Component text) { public Component getTooltip() { return this.tooltip; } -} +} \ No newline at end of file diff --git a/src/main/java/net/vulkanmod/config/option/OptionPage.java b/src/main/java/net/vulkanmod/config/option/OptionPage.java index 8d783fe4e..069ebef85 100644 --- a/src/main/java/net/vulkanmod/config/option/OptionPage.java +++ b/src/main/java/net/vulkanmod/config/option/OptionPage.java @@ -41,4 +41,20 @@ public void applyOptionChanges() { } } } -} + + public void captureOriginalState() { + for (var block : this.optionBlocks) { + for (var option : block.options()) { + option.captureOriginalState(); + } + } + } + + public void resetToOriginalState() { + for (var block : this.optionBlocks) { + for (var option : block.options()) { + option.resetToOriginalState(); + } + } + } +} \ No newline at end of file diff --git a/src/main/resources/assets/vulkanmod/lang/en_us.json b/src/main/resources/assets/vulkanmod/lang/en_us.json index 09be191c7..167bcd7c0 100644 --- a/src/main/resources/assets/vulkanmod/lang/en_us.json +++ b/src/main/resources/assets/vulkanmod/lang/en_us.json @@ -7,6 +7,7 @@ "vulkanmod.options.pages.other": "Other", "vulkanmod.options.buttons.apply": "Apply", + "vulkanmod.options.buttons.undo": "Undo", "vulkanmod.options.buttons.kofi": "Support me", "vulkanmod.options.advCulling": "Advanced Chunk Culling", From b2e7a4af4e3a4e286f0679512c5fdd219bd9f72a Mon Sep 17 00:00:00 2001 From: NotNekodev <97458908+NotNekodev@users.noreply.github.com> Date: Sun, 30 Nov 2025 14:41:31 +0100 Subject: [PATCH 02/21] Change position of tooltips to be slightly below the option and aligned to the right edge of the option --- .../vulkanmod/config/gui/VOptionScreen.java | 34 ++++++++----------- 1 file changed, 15 insertions(+), 19 deletions(-) diff --git a/src/main/java/net/vulkanmod/config/gui/VOptionScreen.java b/src/main/java/net/vulkanmod/config/gui/VOptionScreen.java index b6d2f1101..453fb1b81 100644 --- a/src/main/java/net/vulkanmod/config/gui/VOptionScreen.java +++ b/src/main/java/net/vulkanmod/config/gui/VOptionScreen.java @@ -33,8 +33,6 @@ public class VOptionScreen extends Screen { private int currentListIdx = 0; - private int tooltipX; - private int tooltipY; private int tooltipWidth; private VButtonWidget supportButton; @@ -99,16 +97,11 @@ protected void init() { int x = leftMargin + listWidth + 10; int width = this.width - x - 10; - int y = 50; if (width < 200) { - x = 100; width = listWidth; - y = this.height - bottom + 10; } - this.tooltipX = x; - this.tooltipY = y; this.tooltipWidth = width; buildPage(); @@ -271,9 +264,16 @@ public void render(GuiGraphics guiGraphics, int mouseX, int mouseY, float delta) currentList.renderWidget(mouseX, mouseY); renderButtons(mouseX, mouseY); - List list = getHoveredButtonTooltip(currentList, mouseX, mouseY); - if (list != null) { - this.renderTooltip(list, this.tooltipX, this.tooltipY); + VAbstractWidget hoveredWidget = currentList.getHoveredWidget(mouseX, mouseY); + if (hoveredWidget != null) { + List tooltip = getWidgetTooltip(hoveredWidget); + if (tooltip != null) { + int padding = 3; + int tooltipWidth = GuiRenderer.getMaxTextWidth(this.font, tooltip); + int tooltipX = hoveredWidget.getX() + hoveredWidget.getWidth() - tooltipWidth - padding; + int tooltipY = hoveredWidget.getY() + hoveredWidget.getHeight() + 3 + 1; // 3 is the padding inside of renderTooltip and 1 is the custom padding + this.renderTooltip(tooltip, tooltipX, tooltipY); + } } } @@ -301,16 +301,12 @@ private void renderTooltip(List list, int x, int y) { } } - private List getHoveredButtonTooltip(VOptionList buttonList, int mouseX, int mouseY) { - VAbstractWidget widget = buttonList.getHoveredWidget(mouseX, mouseY); - if (widget != null) { - var tooltip = widget.getTooltip(); - if (tooltip == null) - return null; + private List getWidgetTooltip(VAbstractWidget widget) { + var tooltip = widget.getTooltip(); + if (tooltip == null) + return null; - return this.font.split(tooltip, this.tooltipWidth); - } - return null; + return this.font.split(tooltip, this.tooltipWidth); } private void updateState() { From 2b37f17dc765702c027a659456b24329629f2891 Mon Sep 17 00:00:00 2001 From: NotNekodev <97458908+NotNekodev@users.noreply.github.com> Date: Sun, 30 Nov 2025 16:39:20 +0100 Subject: [PATCH 03/21] make colors more welcoming, add VGuiConstants.java add hovering colors --- .../vulkanmod/config/gui/VGuiConstants.java | 14 ++++ .../vulkanmod/config/gui/VOptionScreen.java | 49 ++++++----- .../config/gui/widget/VAbstractWidget.java | 25 ++++-- .../config/gui/widget/VButtonWidget.java | 83 ++++++++++++++----- 4 files changed, 122 insertions(+), 49 deletions(-) create mode 100644 src/main/java/net/vulkanmod/config/gui/VGuiConstants.java diff --git a/src/main/java/net/vulkanmod/config/gui/VGuiConstants.java b/src/main/java/net/vulkanmod/config/gui/VGuiConstants.java new file mode 100644 index 000000000..fa34faf60 --- /dev/null +++ b/src/main/java/net/vulkanmod/config/gui/VGuiConstants.java @@ -0,0 +1,14 @@ +package net.vulkanmod.config.gui; + +import net.vulkanmod.vulkan.util.ColorUtil; + +public class VGuiConstants { + public static final int COLOR_WHITE = ColorUtil.ARGB.pack(1f, 1f, 1f, 1f); + public static final int COLOR_BLACK = ColorUtil.ARGB.pack(0f, 0f, 0f, 1f); + public static final int COLOR_GRAY = ColorUtil.ARGB.pack(0.6f, 0.6f, 0.6f, 1f); + public static final int COLOR_RED = ColorUtil.ARGB.pack(0.59f, 0.18f, 0.17f, 1f); + + public static final int WIDGET_HEIGHT = 20; + public static final int WIDGET_MARGIN = 5; + +} diff --git a/src/main/java/net/vulkanmod/config/gui/VOptionScreen.java b/src/main/java/net/vulkanmod/config/gui/VOptionScreen.java index 453fb1b81..b515f15c7 100644 --- a/src/main/java/net/vulkanmod/config/gui/VOptionScreen.java +++ b/src/main/java/net/vulkanmod/config/gui/VOptionScreen.java @@ -24,7 +24,6 @@ import java.util.List; public class VOptionScreen extends Screen { - public final static int RED = ColorUtil.ARGB.pack(0.3f, 0.0f, 0.0f, 0.8f); final ResourceLocation ICON = ResourceLocation.fromNamespaceAndPath("vulkanmod", "vlogo_transparent.png"); private final Screen parent; @@ -90,7 +89,8 @@ protected void init() { int itemHeight = 20; int leftMargin = 100; - int listWidth = Math.min((int) (this.width * 0.65f), 420); + int rightMargin = 20; + int listWidth = this.width - rightMargin - leftMargin; int listHeight = this.height - top - bottom; this.buildLists(leftMargin, top, listWidth, listHeight, itemHeight); @@ -107,7 +107,7 @@ protected void init() { buildPage(); this.applyButton.active = false; - this.undoButton.active = false; + this.undoButton.visible = false; } private void captureOriginalState() { @@ -155,7 +155,7 @@ private void buildPage() { this.pageButtons.clear(); this.clearWidgets(); - this.addPageButtons(10, 40, 80, 22, true); + this.addPageButtons(10, 40, 80, VGuiConstants.WIDGET_HEIGHT, true); VOptionList currentList = this.optionPages.get(this.currentListIdx).getOptionList(); this.addWidget(currentList); @@ -165,34 +165,32 @@ private void buildPage() { private void addButtons() { int rightMargin = 20; - int buttonHeight = 20; int padding = 10; - int buttonMargin = 5; int buttonWidth = minecraft.font.width(CommonComponents.GUI_DONE) + 2 * padding; int x0 = (this.width - buttonWidth - rightMargin); - int y0 = this.height - buttonHeight - 7; + int y0 = this.height - VGuiConstants.WIDGET_HEIGHT - 7; this.doneButton = new VButtonWidget( x0, y0, - buttonWidth, buttonHeight, + buttonWidth, VGuiConstants.WIDGET_HEIGHT, CommonComponents.GUI_DONE, button -> this.minecraft.setScreen(this.parent) ); buttonWidth = minecraft.font.width(Component.translatable("vulkanmod.options.buttons.apply")) + 2 * padding; - x0 -= (buttonWidth + buttonMargin); + x0 -= (buttonWidth + VGuiConstants.WIDGET_MARGIN); this.applyButton = new VButtonWidget( x0, y0, - buttonWidth, buttonHeight, + buttonWidth, VGuiConstants.WIDGET_HEIGHT, Component.translatable("vulkanmod.options.buttons.apply"), button -> this.applyOptions() ); buttonWidth = minecraft.font.width(Component.translatable("vulkanmod.options.buttons.undo")) + 2 * padding; - x0 -= (buttonWidth + buttonMargin); + x0 -= (buttonWidth + VGuiConstants.WIDGET_MARGIN); this.undoButton = new VButtonWidget( x0, y0, - buttonWidth, buttonHeight, + buttonWidth, VGuiConstants.WIDGET_HEIGHT, Component.translatable("vulkanmod.options.buttons.undo"), button -> { undo(); @@ -203,7 +201,7 @@ private void addButtons() { x0 = (this.width - buttonWidth - rightMargin); this.supportButton = new VButtonWidget( x0, 6, - buttonWidth, buttonHeight, + buttonWidth, VGuiConstants.WIDGET_HEIGHT, Component.translatable("vulkanmod.options.buttons.kofi"), button -> Util.getPlatform().openUri("https://ko-fi.com/xcollateral") ); @@ -264,7 +262,19 @@ public void render(GuiGraphics guiGraphics, int mouseX, int mouseY, float delta) currentList.renderWidget(mouseX, mouseY); renderButtons(mouseX, mouseY); - VAbstractWidget hoveredWidget = currentList.getHoveredWidget(mouseX, mouseY); + VAbstractWidget hoveredWidget = null; + + for (var b : buttons) { + if (b.isMouseOver(mouseX, mouseY)) { + hoveredWidget = b; + break; + } + } + + if (hoveredWidget == null) { + hoveredWidget = currentList.getHoveredWidget(mouseX, mouseY); + } + if (hoveredWidget != null) { List tooltip = getWidgetTooltip(hoveredWidget); if (tooltip != null) { @@ -279,6 +289,7 @@ public void render(GuiGraphics guiGraphics, int mouseX, int mouseY, float delta) public void renderButtons(int mouseX, int mouseY) { for (VButtonWidget button : buttons) { + button.updateState(mouseX, mouseY); button.render(mouseX, mouseY); } } @@ -287,12 +298,10 @@ private void renderTooltip(List list, int x, int y) { int padding = 3; int width = GuiRenderer.getMaxTextWidth(this.font, list); int height = list.size() * 10; - float intensity = 0.05f; - int color = ColorUtil.ARGB.pack(intensity, intensity, intensity, 0.6f); - GuiRenderer.fill(x - padding, y - padding, x + width + padding, y + height + padding, color); + GuiRenderer.fill(x - padding, y - padding, x + width + padding, y + height + padding, + ColorUtil.ARGB.pack(0.05f, 0.05f, 0.05f, 0.6f)); - color = RED; - GuiRenderer.renderBorder(x - padding, y - padding, x + width + padding, y + height + padding, 1, color); + GuiRenderer.renderBorder(x - padding, y - padding, x + width + padding, y + height + padding, 1, VGuiConstants.COLOR_RED); int yOffset = 0; for (var text : list) { @@ -316,7 +325,7 @@ private void updateState() { } this.applyButton.active = modified; - this.undoButton.active = modified; + this.undoButton.visible = modified; } private void setOptionList(int i) { diff --git a/src/main/java/net/vulkanmod/config/gui/widget/VAbstractWidget.java b/src/main/java/net/vulkanmod/config/gui/widget/VAbstractWidget.java index 0a76bd6ff..2c8690645 100644 --- a/src/main/java/net/vulkanmod/config/gui/widget/VAbstractWidget.java +++ b/src/main/java/net/vulkanmod/config/gui/widget/VAbstractWidget.java @@ -7,6 +7,7 @@ import net.minecraft.network.chat.Component; import net.minecraft.sounds.SoundEvents; import net.vulkanmod.config.gui.GuiElement; +import net.vulkanmod.config.gui.VGuiConstants; import net.vulkanmod.config.gui.render.GuiRenderer; import net.vulkanmod.vulkan.util.ColorUtil; @@ -20,6 +21,7 @@ public abstract class VAbstractWidget extends GuiElement { public void render(double mX, double mY) { this.updateState(mX, mY); this.renderWidget(mX, mY); + this.renderHovering(0, 0); } public void renderWidget(double mX, double mY) { @@ -35,16 +37,17 @@ protected void onDrag(double mX, double mY, double f, double g) { } protected void renderHovering(int xPadding, int yPadding) { + if (this.isFocused() || !this.isActive()) + return; + float hoverMultiplier = this.getHoverMultiplier(200); + int borderColor = ColorUtil.ARGB.multiplyAlpha(VGuiConstants.COLOR_RED, hoverMultiplier); + int backgroundColor = ColorUtil.ARGB.multiplyAlpha(VGuiConstants.COLOR_RED, 0.3f * hoverMultiplier); if (hoverMultiplier > 0.0f) { -// int color = ColorUtil.ARGB.pack(0.5f, 0.5f, 0.5f, hoverMultiplier * 0.2f); - int color = ColorUtil.ARGB.pack(0.3f, 0.0f, 0.0f, hoverMultiplier * 0.2f); -// int color = ColorUtil.ARGB.multiplyAlpha(VOptionScreen.RED, hoverMultiplier); - GuiRenderer.fill(this.x - xPadding, this.y - yPadding, this.x + this.width + xPadding, this.y + this.height + yPadding, color); - -// color = ColorUtil.ARGB.pack(1.0f, 1.0f, 1.0f, hoverMultiplier * 0.8f); - color = ColorUtil.ARGB.pack(0.3f, 0.0f, 0.0f, hoverMultiplier * 0.8f); + GuiRenderer.fill(this.x - xPadding, this.y - yPadding, + this.x + this.width + xPadding, this.y + this.height + yPadding, + backgroundColor); int x0 = this.x - xPadding; int x1 = this.x + this.width + xPadding; @@ -52,7 +55,7 @@ protected void renderHovering(int xPadding, int yPadding) { int y1 = this.y + height + yPadding; int border = 1; - GuiRenderer.renderBorder(x0, y0, x1, y1, border, color); + GuiRenderer.renderBorder(x0, y0, x1, y1, border, borderColor); } } @@ -105,6 +108,12 @@ public boolean mouseDragged(MouseButtonEvent event, double d, double e) { } } + @Override + public void updateState(double mX, double mY) { + super.updateState(mX, mY); + + } + public void playDownSound(SoundManager soundManager) { soundManager.play(SimpleSoundInstance.forUI(SoundEvents.UI_BUTTON_CLICK, 1.0F)); } diff --git a/src/main/java/net/vulkanmod/config/gui/widget/VButtonWidget.java b/src/main/java/net/vulkanmod/config/gui/widget/VButtonWidget.java index 3055259d6..18dbf5bf2 100644 --- a/src/main/java/net/vulkanmod/config/gui/widget/VButtonWidget.java +++ b/src/main/java/net/vulkanmod/config/gui/widget/VButtonWidget.java @@ -1,12 +1,16 @@ package net.vulkanmod.config.gui.widget; import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.ComponentPath; import net.minecraft.client.gui.Font; +import net.minecraft.client.gui.navigation.FocusNavigationEvent; import net.minecraft.network.chat.Component; import net.minecraft.util.Mth; +import net.vulkanmod.config.gui.VGuiConstants; import net.vulkanmod.config.gui.render.GuiRenderer; import net.vulkanmod.vulkan.VRenderSystem; import net.vulkanmod.vulkan.util.ColorUtil; +import org.jetbrains.annotations.Nullable; import java.util.function.Consumer; @@ -24,38 +28,75 @@ public VButtonWidget(int x, int y, int width, int height, Component message, Con } public void renderWidget(double mouseX, double mouseY) { - Minecraft minecraftClient = Minecraft.getInstance(); - Font textRenderer = minecraftClient.font; + if (!this.isVisible()) return; + + int backgroundColor = this.isActive() + ? ColorUtil.ARGB.multiplyAlpha(VGuiConstants.COLOR_BLACK, 0.45f) + : ColorUtil.ARGB.multiplyAlpha(VGuiConstants.COLOR_BLACK, 0.3f); + int textColor = this.isActive() + ? VGuiConstants.COLOR_WHITE + : VGuiConstants.COLOR_GRAY; + int selectionOutlineColor = ColorUtil.ARGB.multiplyAlpha(VGuiConstants.COLOR_RED, 0.8f); + int selectionFillColor = ColorUtil.ARGB.multiplyAlpha(VGuiConstants.COLOR_RED, 0.2f); + + GuiRenderer.fill(this.x, this.y, this.x + this.width, this.y + this.height, backgroundColor); + + if (this.selected) { + GuiRenderer.fill(this.x, this.y, this.x + 2, this.y + this.height, selectionOutlineColor); + GuiRenderer.fill(this.x, this.y, this.x + this.width, this.y + this.height, selectionFillColor); + } - int xPadding = 0; - int yPadding = 0; + // this is down here because of layering + GuiRenderer.drawCenteredString( + Minecraft.getInstance().font, + this.message, + this.x + this.width / 2, (this.y + this.height / 2) - 4, + textColor | (Mth.ceil(this.alpha * 255.0f) << 24)); + } - int color = ColorUtil.ARGB.pack(0.0f, 0.0f, 0.0f, this.active ? 0.45f : 0.3f); - GuiRenderer.fill(this.x - xPadding, this.y - yPadding, this.x + this.width + xPadding, this.y + this.height + yPadding, color); + public void onClick(double mX, double mY) { + this.onPress.accept(this); + } - if (this.active) { - this.renderHovering(0, 0); - } + private void doAction() { + this.onPress.accept(this); + this.playDownSound(Minecraft.getInstance().getSoundManager()); + } - int j = this.active ? 0xFFFFFF : 0xA0A0A0; - GuiRenderer.drawCenteredString(textRenderer, this.message, this.x + this.width / 2, this.y + (this.height - 8) / 2, j | Mth.ceil(this.alpha * 255.0f) << 24); + public void setSelected(boolean selected) { + this.selected = selected; + } + public Component getMessage() { + return message; + } - if(this.selected) { - color = ColorUtil.ARGB.pack(0.3f, 0.0f, 0.0f, 1.0f); - GuiRenderer.fillBox(this.x, this.y, (int) 1.5f, this.height, color); + public void setMessage(Component message) { + this.message = message; + } - color = ColorUtil.ARGB.pack(0.3f, 0.0f, 0.0f, 0.2f); - GuiRenderer.fillBox(this.x, this.y, this.width, this.height, color); - } + public boolean isVisible() { + return visible; } - public void setSelected(boolean selected) { - this.selected = selected; + public void setVisible(boolean visible) { + this.visible = visible; } - public void onClick(double mX, double mY) { - this.onPress.accept(this); + @Override + public boolean isActive() { + return active; + } + + public void setActive(boolean active) { + this.active = active; + } + + @Override + public @Nullable ComponentPath nextFocusPath(FocusNavigationEvent event) { + if (!this.active || !this.visible) + return null; + return super.nextFocusPath(event); } } From 54caebf85b6c73c7986c162dfed8e56e7c217a64 Mon Sep 17 00:00:00 2001 From: NotNekodev <97458908+NotNekodev@users.noreply.github.com> Date: Sun, 30 Nov 2025 18:57:51 +0100 Subject: [PATCH 04/21] rewrite tooltip system, to support different translations based on different values --- .../vulkanmod/config/gui/VOptionScreen.java | 1 + .../net/vulkanmod/config/option/Option.java | 29 +++++++++++++++---- .../net/vulkanmod/config/option/Options.java | 25 +++++++++++----- .../assets/vulkanmod/lang/en_us.json | 4 +-- 4 files changed, 43 insertions(+), 16 deletions(-) diff --git a/src/main/java/net/vulkanmod/config/gui/VOptionScreen.java b/src/main/java/net/vulkanmod/config/gui/VOptionScreen.java index b515f15c7..42fda32aa 100644 --- a/src/main/java/net/vulkanmod/config/gui/VOptionScreen.java +++ b/src/main/java/net/vulkanmod/config/gui/VOptionScreen.java @@ -295,6 +295,7 @@ public void renderButtons(int mouseX, int mouseY) { } private void renderTooltip(List list, int x, int y) { + if (list.isEmpty()) return; int padding = 3; int width = GuiRenderer.getMaxTextWidth(this.font, list); int height = list.size() * 10; diff --git a/src/main/java/net/vulkanmod/config/option/Option.java b/src/main/java/net/vulkanmod/config/option/Option.java index 274c4cac3..be35f5c9c 100644 --- a/src/main/java/net/vulkanmod/config/option/Option.java +++ b/src/main/java/net/vulkanmod/config/option/Option.java @@ -19,10 +19,23 @@ public abstract class Option { protected T originalValue; protected Function translator; + protected Function tooltipTranslator; protected boolean active; protected Runnable onChange; + public Option(Component name, Consumer setter, Supplier getter, Function translator, Function tooltip) { + this.name = name; + + this.onApply = setter; + this.valueSupplier = getter; + + this.translator = translator; + this.tooltipTranslator = tooltip; + + this.newValue = this.value = this.valueSupplier.get(); + } + public Option(Component name, Consumer setter, Supplier getter, Function translator) { this.name = name; @@ -58,6 +71,11 @@ public Option setTranslator(Function translator) { return this; } + public Option setTooltip(Function tooltipTranslator) { + this.tooltipTranslator = tooltipTranslator; + return this; + } + public Option setActive(boolean active) { this.active = active; return this; @@ -113,12 +131,11 @@ public Component getDisplayedValue() { return this.translator.apply(this.newValue); } - public Option setTooltip(Component text) { - this.tooltip = text; - return this; - } - public Component getTooltip() { - return this.tooltip; + if (this.tooltipTranslator != null) { + return this.tooltipTranslator.apply(this.newValue); + } else { + return Component.empty(); + } } } \ No newline at end of file diff --git a/src/main/java/net/vulkanmod/config/option/Options.java b/src/main/java/net/vulkanmod/config/option/Options.java index 560e6d2c9..55761e8f2 100644 --- a/src/main/java/net/vulkanmod/config/option/Options.java +++ b/src/main/java/net/vulkanmod/config/option/Options.java @@ -210,7 +210,13 @@ public static OptionBlock[] getGraphicsOpts() { case LightMode.SUB_BLOCK -> "vulkanmod.options.ao.subBlock"; default -> "vulkanmod.options.unknown"; })) - .setTooltip(Component.translatable("vulkanmod.options.ao.subBlock.tooltip")), + .setTooltip(value -> switch (value) { + case LightMode.FLAT -> Component.empty(); + case LightMode.SMOOTH -> Component.empty(); + case LightMode.SUB_BLOCK -> Component.translatable("vulkanmod.options.ao.subBlock.tooltip"); + default -> Component.empty(); + } + ), new RangeOption(Component.translatable("options.biomeBlendRadius"), 0, 7, 1, value -> { @@ -258,11 +264,14 @@ public static OptionBlock[] getOptimizationOpts() { case 10 -> "options.off"; default -> "vulkanmod.options.unknown"; })) - .setTooltip(Component.translatable("vulkanmod.options.advCulling.tooltip")), + .setTooltip(value -> Component.translatable(switch (value) { + case 1, 2, 3 -> "vulkanmod.options.advCulling.tooltip"; + default -> ""; + })), new SwitchOption(Component.translatable("vulkanmod.options.entityCulling"), value -> config.entityCulling = value, () -> config.entityCulling) - .setTooltip(Component.translatable("vulkanmod.options.entityCulling.tooltip")), + .setTooltip(value -> Component.translatable("vulkanmod.options.entityCulling.tooltip")), new SwitchOption(Component.translatable("vulkanmod.options.uniqueOpaqueLayer"), value -> { config.uniqueOpaqueLayer = value; @@ -270,18 +279,18 @@ public static OptionBlock[] getOptimizationOpts() { minecraft.levelRenderer.allChanged(); }, () -> config.uniqueOpaqueLayer) - .setTooltip(Component.translatable("vulkanmod.options.uniqueOpaqueLayer.tooltip")), + .setTooltip(value -> Component.translatable("vulkanmod.options.uniqueOpaqueLayer.tooltip")), new SwitchOption(Component.translatable("vulkanmod.options.backfaceCulling"), value -> { config.backFaceCulling = value; Minecraft.getInstance().levelRenderer.allChanged(); }, () -> config.backFaceCulling) - .setTooltip(Component.translatable("vulkanmod.options.backfaceCulling.tooltip")), + .setTooltip(value -> Component.translatable("vulkanmod.options.backfaceCulling.tooltip")), new SwitchOption(Component.translatable("vulkanmod.options.indirectDraw"), value -> config.indirectDraw = value, () -> config.indirectDraw) - .setTooltip(Component.translatable("vulkanmod.options.indirectDraw.tooltip")), + .setTooltip(value -> Component.translatable("vulkanmod.options.indirectDraw.tooltip")), }) }; @@ -309,7 +318,7 @@ public static OptionBlock[] getOtherOpts() { config.frameQueueSize = value; Renderer.scheduleSwapChainUpdate(); }, () -> config.frameQueueSize) - .setTooltip(Component.translatable("vulkanmod.options.frameQueue.tooltip")), + .setTooltip(value -> Component.translatable("vulkanmod.options.frameQueue.tooltip")), new SwitchOption(Component.translatable("vulkanmod.options.textureAnimations"), value -> { config.textureAnimations = value; @@ -327,7 +336,7 @@ public static OptionBlock[] getOtherOpts() { : DeviceManager.suitableDevices.get( value).deviceName) ) - .setTooltip(Component.nullToEmpty("%s: %s".formatted( + .setTooltip(value -> Component.nullToEmpty("%s: %s".formatted( Component.translatable("vulkanmod.options.deviceSelector.tooltip").getString(), DeviceManager.device.deviceName))) }) diff --git a/src/main/resources/assets/vulkanmod/lang/en_us.json b/src/main/resources/assets/vulkanmod/lang/en_us.json index 167bcd7c0..0421bef4a 100644 --- a/src/main/resources/assets/vulkanmod/lang/en_us.json +++ b/src/main/resources/assets/vulkanmod/lang/en_us.json @@ -16,8 +16,8 @@ "vulkanmod.options.advCulling.normal": "Normal", "vulkanmod.options.advCulling.tooltip": "Use a culling algorithm that might improve performance by reducing the number of non visible chunk sections rendered.", - "vulkanmod.options.ao.subBlock": "ON (Sub-block)", - "vulkanmod.options.ao.subBlock.tooltip": "ON (Sub-block): Enables smooth lighting for non full block (experimental).", + "vulkanmod.options.ao.subBlock": "Sub Block", + "vulkanmod.options.ao.subBlock.tooltip": "Enables smooth lighting for non full block (experimental).", "vulkanmod.options.deviceSelector": "Device selector", "vulkanmod.options.deviceSelector.auto": "Auto", From 2aad69dcdd0e153dec97b2bc9caa207703e356be Mon Sep 17 00:00:00 2001 From: NotNekodev <97458908+NotNekodev@users.noreply.github.com> Date: Sun, 30 Nov 2025 21:18:49 +0100 Subject: [PATCH 05/21] more compact page button layout --- .../java/net/vulkanmod/config/gui/VOptionScreen.java | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/main/java/net/vulkanmod/config/gui/VOptionScreen.java b/src/main/java/net/vulkanmod/config/gui/VOptionScreen.java index 42fda32aa..383ab9166 100644 --- a/src/main/java/net/vulkanmod/config/gui/VOptionScreen.java +++ b/src/main/java/net/vulkanmod/config/gui/VOptionScreen.java @@ -130,7 +130,7 @@ private void buildLists(int left, int top, int listWidth, int listHeight, int it } } - private void addPageButtons(int x0, int y0, int width, int height, boolean verticalLayout) { + private void addPageButtons(int x0, int y0, int width, int height) { int x = x0; int y = y0; for (int i = 0; i < this.optionPages.size(); ++i) { @@ -141,10 +141,7 @@ private void addPageButtons(int x0, int y0, int width, int height, boolean verti this.pageButtons.add(widget); this.addWidget(widget); - if (verticalLayout) - y += height + 1; - else - x += width + 1; + y += height; } this.pageButtons.get(this.currentListIdx).setSelected(true); @@ -155,7 +152,7 @@ private void buildPage() { this.pageButtons.clear(); this.clearWidgets(); - this.addPageButtons(10, 40, 80, VGuiConstants.WIDGET_HEIGHT, true); + this.addPageButtons(10, 40, 80, VGuiConstants.WIDGET_HEIGHT); VOptionList currentList = this.optionPages.get(this.currentListIdx).getOptionList(); this.addWidget(currentList); From 8efec30d2da1b29a19696e02c601abde01107a20 Mon Sep 17 00:00:00 2001 From: NotNekodev <97458908+NotNekodev@users.noreply.github.com> Date: Sun, 30 Nov 2025 21:36:43 +0100 Subject: [PATCH 06/21] fix hovering of the undo button even when its not visible --- .../java/net/vulkanmod/config/gui/widget/VAbstractWidget.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/net/vulkanmod/config/gui/widget/VAbstractWidget.java b/src/main/java/net/vulkanmod/config/gui/widget/VAbstractWidget.java index 2c8690645..87545531a 100644 --- a/src/main/java/net/vulkanmod/config/gui/widget/VAbstractWidget.java +++ b/src/main/java/net/vulkanmod/config/gui/widget/VAbstractWidget.java @@ -37,7 +37,7 @@ protected void onDrag(double mX, double mY, double f, double g) { } protected void renderHovering(int xPadding, int yPadding) { - if (this.isFocused() || !this.isActive()) + if (this.isFocused() || !this.isActive() || !this.visible || this.focused) return; float hoverMultiplier = this.getHoverMultiplier(200); From 9da51eca3815c368547f7e7c84074b62c9d5b4bb Mon Sep 17 00:00:00 2001 From: NotNekodev <97458908+NotNekodev@users.noreply.github.com> Date: Sun, 30 Nov 2025 22:04:09 +0100 Subject: [PATCH 07/21] make scrolling strings for options that are too long --- .../vulkanmod/config/gui/VOptionScreen.java | 1 + .../config/gui/render/GuiRenderer.java | 78 ++++++++++++++++++- .../config/gui/{ => util}/VGuiConstants.java | 2 +- .../gui/widget/CyclingOptionWidget.java | 2 +- .../config/gui/widget/VAbstractWidget.java | 2 +- .../config/gui/widget/VButtonWidget.java | 4 +- 6 files changed, 82 insertions(+), 7 deletions(-) rename src/main/java/net/vulkanmod/config/gui/{ => util}/VGuiConstants.java (92%) diff --git a/src/main/java/net/vulkanmod/config/gui/VOptionScreen.java b/src/main/java/net/vulkanmod/config/gui/VOptionScreen.java index 383ab9166..5eacf9a21 100644 --- a/src/main/java/net/vulkanmod/config/gui/VOptionScreen.java +++ b/src/main/java/net/vulkanmod/config/gui/VOptionScreen.java @@ -13,6 +13,7 @@ import net.minecraft.util.FormattedCharSequence; import net.vulkanmod.Initializer; import net.vulkanmod.config.gui.render.GuiRenderer; +import net.vulkanmod.config.gui.util.VGuiConstants; import net.vulkanmod.config.gui.widget.VAbstractWidget; import net.vulkanmod.config.gui.widget.VButtonWidget; import net.vulkanmod.config.option.OptionPage; diff --git a/src/main/java/net/vulkanmod/config/gui/render/GuiRenderer.java b/src/main/java/net/vulkanmod/config/gui/render/GuiRenderer.java index e268dab92..3ab7171b6 100644 --- a/src/main/java/net/vulkanmod/config/gui/render/GuiRenderer.java +++ b/src/main/java/net/vulkanmod/config/gui/render/GuiRenderer.java @@ -2,15 +2,19 @@ import com.mojang.blaze3d.pipeline.RenderPipeline; import com.mojang.blaze3d.vertex.*; +import net.minecraft.Util; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.Font; import net.minecraft.client.gui.GuiGraphics; import net.minecraft.client.gui.render.TextureSetup; import net.minecraft.network.chat.Component; import net.minecraft.util.FormattedCharSequence; +import net.minecraft.util.Mth; import org.joml.Matrix3x2f; +import java.util.HashMap; import java.util.List; +import java.util.Map; public abstract class GuiRenderer { @@ -19,6 +23,60 @@ public abstract class GuiRenderer { public static PoseStack pose; public static BufferBuilder bufferBuilder; + // Scrolling text state management + private static final Map scrollingTextStates = new HashMap<>(); + private static final float SCROLL_SPEED = 30.0f; // pixels per second + private static final float SCROLL_PAUSE_DURATION = 1.0f; // seconds to pause at ends + + private static class ScrollingTextState { + float scrollOffset = 0.0f; + long lastUpdateTime = System.currentTimeMillis(); + boolean scrollingForward = true; + float pauseTimer = 0.0f; + + void update(float textWidth, float maxWidth) { + long currentTime = System.currentTimeMillis(); + float deltaTime = (currentTime - lastUpdateTime) / 1000.0f; + lastUpdateTime = currentTime; + + if (textWidth <= maxWidth) { + scrollOffset = 0.0f; + return; + } + + // Handle pause at ends + if (pauseTimer > 0) { + pauseTimer -= deltaTime; + return; + } + + float maxScroll = textWidth - maxWidth; + + if (scrollingForward) { + scrollOffset += SCROLL_SPEED * deltaTime; + if (scrollOffset >= maxScroll) { + scrollOffset = maxScroll; + scrollingForward = false; + pauseTimer = SCROLL_PAUSE_DURATION; + } + } else { + scrollOffset -= SCROLL_SPEED * deltaTime; + if (scrollOffset <= 0) { + scrollOffset = 0; + scrollingForward = true; + pauseTimer = SCROLL_PAUSE_DURATION; + } + } + } + + void reset() { + scrollOffset = 0.0f; + scrollingForward = true; + pauseTimer = 0.0f; + lastUpdateTime = System.currentTimeMillis(); + } + } + public static void enableScissor(int i, int j, int k, int l) { guiGraphics.enableScissor(i, j, k, l); } @@ -80,6 +138,24 @@ public static void drawCenteredString(Font font, Component component, int x, int guiGraphics.drawString(font, formattedCharSequence, x - font.width(formattedCharSequence) / 2, y, color); } + public static void drawScrollingString(Font font, Component component, int x, int y, int maxWidth, int color) { + int textWidth = font.width(component); + if (textWidth <= maxWidth) { + drawCenteredString(font, component, x, y, color); + } else { + int x0 = x - maxWidth / 2, x1 = x + maxWidth / 2; + int scrollAmount = textWidth - maxWidth; + double currentTimeInSeconds = (double) Util.getMillis() / 1000.0; + double scrollSpeed = Math.max(scrollAmount * 0.5, 3.0); + double scrollingOffset = Math.sin((Math.PI / 2) * Math.cos((Math.PI * 2) * currentTimeInSeconds / scrollSpeed)) / 2.0 + 0.5; + double horizontalScroll = Mth.lerp(scrollingOffset, 0.0, scrollAmount); + + enableScissor(x0 - 1, 0, x1, Minecraft.getInstance().getWindow().getScreenHeight()); + drawString(font, component, (int) (x0 - horizontalScroll), y, color); + disableScissor(); + } + } + public static int getMaxTextWidth(Font font, List list) { int maxWidth = 0; for (var text : list) { @@ -98,4 +174,4 @@ renderPipeline, textureSetup, new Matrix3x2f(), vertices, color, guiGraphics.sci ) ); } -} +} \ No newline at end of file diff --git a/src/main/java/net/vulkanmod/config/gui/VGuiConstants.java b/src/main/java/net/vulkanmod/config/gui/util/VGuiConstants.java similarity index 92% rename from src/main/java/net/vulkanmod/config/gui/VGuiConstants.java rename to src/main/java/net/vulkanmod/config/gui/util/VGuiConstants.java index fa34faf60..419cde2f0 100644 --- a/src/main/java/net/vulkanmod/config/gui/VGuiConstants.java +++ b/src/main/java/net/vulkanmod/config/gui/util/VGuiConstants.java @@ -1,4 +1,4 @@ -package net.vulkanmod.config.gui; +package net.vulkanmod.config.gui.util; import net.vulkanmod.vulkan.util.ColorUtil; diff --git a/src/main/java/net/vulkanmod/config/gui/widget/CyclingOptionWidget.java b/src/main/java/net/vulkanmod/config/gui/widget/CyclingOptionWidget.java index 6ec5157d2..f1401e809 100644 --- a/src/main/java/net/vulkanmod/config/gui/widget/CyclingOptionWidget.java +++ b/src/main/java/net/vulkanmod/config/gui/widget/CyclingOptionWidget.java @@ -39,7 +39,7 @@ public void renderControls(double mouseX, double mouseY) { Font textRenderer = Minecraft.getInstance().font; int x = this.controlX + this.controlWidth / 2; int y = this.y + (this.height - 9) / 2; - GuiRenderer.drawCenteredString(textRenderer, this.getDisplayedValue(), x, y, color); + GuiRenderer.drawScrollingString(textRenderer, this.getDisplayedValue(), x, y, (int) (rightButton.x - (leftButton.x + leftButton.width) - 12), color); this.leftButton.renderButton(mouseX, mouseY); this.rightButton.renderButton(mouseX, mouseY); diff --git a/src/main/java/net/vulkanmod/config/gui/widget/VAbstractWidget.java b/src/main/java/net/vulkanmod/config/gui/widget/VAbstractWidget.java index 87545531a..5597d90dd 100644 --- a/src/main/java/net/vulkanmod/config/gui/widget/VAbstractWidget.java +++ b/src/main/java/net/vulkanmod/config/gui/widget/VAbstractWidget.java @@ -7,7 +7,7 @@ import net.minecraft.network.chat.Component; import net.minecraft.sounds.SoundEvents; import net.vulkanmod.config.gui.GuiElement; -import net.vulkanmod.config.gui.VGuiConstants; +import net.vulkanmod.config.gui.util.VGuiConstants; import net.vulkanmod.config.gui.render.GuiRenderer; import net.vulkanmod.vulkan.util.ColorUtil; diff --git a/src/main/java/net/vulkanmod/config/gui/widget/VButtonWidget.java b/src/main/java/net/vulkanmod/config/gui/widget/VButtonWidget.java index 18dbf5bf2..291a88648 100644 --- a/src/main/java/net/vulkanmod/config/gui/widget/VButtonWidget.java +++ b/src/main/java/net/vulkanmod/config/gui/widget/VButtonWidget.java @@ -2,13 +2,11 @@ import net.minecraft.client.Minecraft; import net.minecraft.client.gui.ComponentPath; -import net.minecraft.client.gui.Font; import net.minecraft.client.gui.navigation.FocusNavigationEvent; import net.minecraft.network.chat.Component; import net.minecraft.util.Mth; -import net.vulkanmod.config.gui.VGuiConstants; +import net.vulkanmod.config.gui.util.VGuiConstants; import net.vulkanmod.config.gui.render.GuiRenderer; -import net.vulkanmod.vulkan.VRenderSystem; import net.vulkanmod.vulkan.util.ColorUtil; import org.jetbrains.annotations.Nullable; From 701cf4c77077ed164dc8669731df7d2dbd4ce363 Mon Sep 17 00:00:00 2001 From: NotNekodev <97458908+NotNekodev@users.noreply.github.com> Date: Mon, 1 Dec 2025 17:02:53 +0100 Subject: [PATCH 08/21] Apply italicised option name for options, where the changes havent been applied yet --- .../net/vulkanmod/config/gui/VOptionScreen.java | 2 ++ .../vulkanmod/config/gui/widget/OptionWidget.java | 15 ++++++++++++++- .../java/net/vulkanmod/config/option/Option.java | 1 + 3 files changed, 17 insertions(+), 1 deletion(-) diff --git a/src/main/java/net/vulkanmod/config/gui/VOptionScreen.java b/src/main/java/net/vulkanmod/config/gui/VOptionScreen.java index 5eacf9a21..02dd966aa 100644 --- a/src/main/java/net/vulkanmod/config/gui/VOptionScreen.java +++ b/src/main/java/net/vulkanmod/config/gui/VOptionScreen.java @@ -341,6 +341,8 @@ private void applyOptions() { page.applyOptionChanges(); } + this.captureOriginalState(); + Initializer.CONFIG.write(); } } \ No newline at end of file diff --git a/src/main/java/net/vulkanmod/config/gui/widget/OptionWidget.java b/src/main/java/net/vulkanmod/config/gui/widget/OptionWidget.java index b3ee79345..80f9bde4a 100644 --- a/src/main/java/net/vulkanmod/config/gui/widget/OptionWidget.java +++ b/src/main/java/net/vulkanmod/config/gui/widget/OptionWidget.java @@ -72,7 +72,20 @@ public void renderWidget(double mouseX, double mouseY) { color = this.active ? 0xFFFFFFFF : 0xFFA0A0A0; Font textRenderer = minecraftClient.font; - GuiRenderer.drawString(textRenderer, this.getName().getVisualOrderText(), this.x + 8, this.y + (this.height - 8) / 2, color); + Component nameComp = this.getName(); + + if (this.option.isChanged()) { + nameComp = nameComp.copy().withStyle(style -> style.withItalic(true)); + } + + GuiRenderer.drawString( + textRenderer, + nameComp.getVisualOrderText(), + this.x + 8, + this.y + (this.height - 8) / 2, + color + ); + this.renderControls(mouseX, mouseY); } diff --git a/src/main/java/net/vulkanmod/config/option/Option.java b/src/main/java/net/vulkanmod/config/option/Option.java index be35f5c9c..4a441806f 100644 --- a/src/main/java/net/vulkanmod/config/option/Option.java +++ b/src/main/java/net/vulkanmod/config/option/Option.java @@ -3,6 +3,7 @@ import net.minecraft.network.chat.Component; import net.vulkanmod.config.gui.widget.OptionWidget; +import java.util.Objects; import java.util.function.Consumer; import java.util.function.Function; import java.util.function.Supplier; From d797d1b5949aad10efce74b8a9f48087699c4467 Mon Sep 17 00:00:00 2001 From: NotNekodev <97458908+NotNekodev@users.noreply.github.com> Date: Mon, 1 Dec 2025 17:28:30 +0100 Subject: [PATCH 09/21] Make the icon and the pageButtons more compact --- .../java/net/vulkanmod/config/gui/VOptionScreen.java | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/main/java/net/vulkanmod/config/gui/VOptionScreen.java b/src/main/java/net/vulkanmod/config/gui/VOptionScreen.java index 02dd966aa..244fcb019 100644 --- a/src/main/java/net/vulkanmod/config/gui/VOptionScreen.java +++ b/src/main/java/net/vulkanmod/config/gui/VOptionScreen.java @@ -85,11 +85,11 @@ protected void init() { this.addPages(); this.captureOriginalState(); - int top = 40; + int top = 36; int bottom = 60; int itemHeight = 20; - int leftMargin = 100; + int leftMargin = 95; int rightMargin = 20; int listWidth = this.width - rightMargin - leftMargin; int listHeight = this.height - top - bottom; @@ -153,7 +153,7 @@ private void buildPage() { this.pageButtons.clear(); this.clearWidgets(); - this.addPageButtons(10, 40, 80, VGuiConstants.WIDGET_HEIGHT); + this.addPageButtons(10, 36, 80, VGuiConstants.WIDGET_HEIGHT); VOptionList currentList = this.optionPages.get(this.currentListIdx).getOptionList(); this.addWidget(currentList); @@ -251,8 +251,12 @@ public void render(GuiGraphics guiGraphics, int mouseX, int mouseY, float delta) GuiRenderer.guiGraphics = guiGraphics; VRenderSystem.enableBlend(); - int size = minecraft.font.lineHeight * 4; + int iconBackgroundColor = ColorUtil.ARGB.multiplyAlpha(VGuiConstants.COLOR_BLACK, 0.45f); + int iconBackgroundWidth = 90; + int iconBackgroundHeight = (minecraft.font.lineHeight * 4); + guiGraphics.fill(10, 4, iconBackgroundWidth, iconBackgroundHeight, iconBackgroundColor); + int size = minecraft.font.lineHeight * 4; guiGraphics.blit(RenderPipelines.GUI_TEXTURED, ICON, 30, 4, 0f, 0f, size, size, size, size); VOptionList currentList = this.optionPages.get(this.currentListIdx).getOptionList(); From a5ace88d053605f3baa7e73f1eac5b078eb1ca1b Mon Sep 17 00:00:00 2001 From: NotNekodev <97458908+NotNekodev@users.noreply.github.com> Date: Mon, 1 Dec 2025 20:14:27 +0100 Subject: [PATCH 10/21] initial revision for the search area + some ui positioning improvements and some scaling improvements --- .../net/vulkanmod/config/gui/VOptionList.java | 4 +- .../vulkanmod/config/gui/VOptionScreen.java | 48 ++++-- .../config/gui/widget/VAbstractWidget.java | 2 + .../config/gui/widget/VTextInputWidget.java | 140 ++++++++++++++++++ .../assets/vulkanmod/lang/en_us.json | 4 +- 5 files changed, 179 insertions(+), 19 deletions(-) create mode 100644 src/main/java/net/vulkanmod/config/gui/widget/VTextInputWidget.java diff --git a/src/main/java/net/vulkanmod/config/gui/VOptionList.java b/src/main/java/net/vulkanmod/config/gui/VOptionList.java index 6e353f2f4..619238c4d 100644 --- a/src/main/java/net/vulkanmod/config/gui/VOptionList.java +++ b/src/main/java/net/vulkanmod/config/gui/VOptionList.java @@ -31,7 +31,7 @@ public VOptionList(int x, int y, int width, int height, int itemHeight) { this.width = width; this.height = height; - this.itemWidth = (int) (0.95f * this.width); + this.itemWidth = this.width - 7; this.itemHeight = itemHeight; this.itemMargin = 3; this.totalItemHeight = this.itemHeight + this.itemMargin; @@ -238,7 +238,7 @@ public void renderWidget(int mouseX, int mouseY) { } protected int getScrollbarPosition() { - return this.x + this.itemWidth + 5; + return this.x + this.width; } public VAbstractWidget getHoveredWidget(double mouseX, double mouseY) { diff --git a/src/main/java/net/vulkanmod/config/gui/VOptionScreen.java b/src/main/java/net/vulkanmod/config/gui/VOptionScreen.java index 244fcb019..3c63fab9a 100644 --- a/src/main/java/net/vulkanmod/config/gui/VOptionScreen.java +++ b/src/main/java/net/vulkanmod/config/gui/VOptionScreen.java @@ -16,6 +16,7 @@ import net.vulkanmod.config.gui.util.VGuiConstants; import net.vulkanmod.config.gui.widget.VAbstractWidget; import net.vulkanmod.config.gui.widget.VButtonWidget; +import net.vulkanmod.config.gui.widget.VTextInputWidget; import net.vulkanmod.config.option.OptionPage; import net.vulkanmod.config.option.Options; import net.vulkanmod.vulkan.VRenderSystem; @@ -41,6 +42,8 @@ public class VOptionScreen extends Screen { private VButtonWidget applyButton; private VButtonWidget undoButton; + private VTextInputWidget searchField; + private final List pageButtons = Lists.newArrayList(); private final List buttons = Lists.newArrayList(); @@ -85,12 +88,12 @@ protected void init() { this.addPages(); this.captureOriginalState(); - int top = 36; + int top = 29; int bottom = 60; int itemHeight = 20; - int leftMargin = 95; - int rightMargin = 20; + int leftMargin = 94; + int rightMargin = 3; int listWidth = this.width - rightMargin - leftMargin; int listHeight = this.height - top - bottom; @@ -162,7 +165,7 @@ private void buildPage() { } private void addButtons() { - int rightMargin = 20; + int rightMargin = 10; int padding = 10; int buttonWidth = minecraft.font.width(CommonComponents.GUI_DONE) + 2 * padding; int x0 = (this.width - buttonWidth - rightMargin); @@ -194,25 +197,38 @@ private void addButtons() { undo(); } ); + this.searchField = new VTextInputWidget( + 94, 4, + x0 - 19, VGuiConstants.WIDGET_HEIGHT, + Component.translatable("vulkanmod.options.searchFieldPlaceholder"), + widget -> { + System.out.println("Searched in graphic settings: " + widget.getInput()); + } + ); - buttonWidth = minecraft.font.width(Component.translatable("vulkanmod.options.buttons.kofi")) + 10; + + buttonWidth = minecraft.font.width(Component.translatable("vulkanmod.options.buttons.kofi")) + padding; x0 = (this.width - buttonWidth - rightMargin); this.supportButton = new VButtonWidget( - x0, 6, + x0, 4, buttonWidth, VGuiConstants.WIDGET_HEIGHT, Component.translatable("vulkanmod.options.buttons.kofi"), button -> Util.getPlatform().openUri("https://ko-fi.com/xcollateral") ); + this.buttons.add(this.applyButton); this.buttons.add(this.doneButton); this.buttons.add(this.supportButton); this.buttons.add(this.undoButton); + this.addWidget(this.applyButton); this.addWidget(this.doneButton); this.addWidget(this.supportButton); this.addWidget(this.undoButton); + + this.addWidget(this.searchField); } @Override @@ -257,12 +273,19 @@ public void render(GuiGraphics guiGraphics, int mouseX, int mouseY, float delta) guiGraphics.fill(10, 4, iconBackgroundWidth, iconBackgroundHeight, iconBackgroundColor); int size = minecraft.font.lineHeight * 4; - guiGraphics.blit(RenderPipelines.GUI_TEXTURED, ICON, 30, 4, 0f, 0f, size, size, size, size); + int iconX = 10 + (iconBackgroundWidth - 10 - size) / 2; + int iconY = 4 + (iconBackgroundHeight - 4 - size) / 2; + guiGraphics.blit(RenderPipelines.GUI_TEXTURED, ICON, iconX, iconY, 0f, 0f, size, size, size, size); VOptionList currentList = this.optionPages.get(this.currentListIdx).getOptionList(); currentList.updateState(mouseX, mouseY); currentList.renderWidget(mouseX, mouseY); - renderButtons(mouseX, mouseY); + for (VButtonWidget button : buttons) { + button.updateState(mouseX, mouseY); + button.render(mouseX, mouseY); + } + searchField.updateState(mouseX, mouseY); + searchField.render(mouseX, mouseY); VAbstractWidget hoveredWidget = null; @@ -283,19 +306,12 @@ public void render(GuiGraphics guiGraphics, int mouseX, int mouseY, float delta) int padding = 3; int tooltipWidth = GuiRenderer.getMaxTextWidth(this.font, tooltip); int tooltipX = hoveredWidget.getX() + hoveredWidget.getWidth() - tooltipWidth - padding; - int tooltipY = hoveredWidget.getY() + hoveredWidget.getHeight() + 3 + 1; // 3 is the padding inside of renderTooltip and 1 is the custom padding + int tooltipY = hoveredWidget.getY() + hoveredWidget.getHeight() + 3 + 1; this.renderTooltip(tooltip, tooltipX, tooltipY); } } } - public void renderButtons(int mouseX, int mouseY) { - for (VButtonWidget button : buttons) { - button.updateState(mouseX, mouseY); - button.render(mouseX, mouseY); - } - } - private void renderTooltip(List list, int x, int y) { if (list.isEmpty()) return; int padding = 3; diff --git a/src/main/java/net/vulkanmod/config/gui/widget/VAbstractWidget.java b/src/main/java/net/vulkanmod/config/gui/widget/VAbstractWidget.java index 5597d90dd..6745f4810 100644 --- a/src/main/java/net/vulkanmod/config/gui/widget/VAbstractWidget.java +++ b/src/main/java/net/vulkanmod/config/gui/widget/VAbstractWidget.java @@ -1,6 +1,7 @@ package net.vulkanmod.config.gui.widget; import net.minecraft.client.Minecraft; +import net.minecraft.client.input.KeyEvent; import net.minecraft.client.input.MouseButtonEvent; import net.minecraft.client.resources.sounds.SimpleSoundInstance; import net.minecraft.client.sounds.SoundManager; @@ -10,6 +11,7 @@ import net.vulkanmod.config.gui.util.VGuiConstants; import net.vulkanmod.config.gui.render.GuiRenderer; import net.vulkanmod.vulkan.util.ColorUtil; +import org.lwjgl.glfw.GLFW; public abstract class VAbstractWidget extends GuiElement { public boolean active = true; diff --git a/src/main/java/net/vulkanmod/config/gui/widget/VTextInputWidget.java b/src/main/java/net/vulkanmod/config/gui/widget/VTextInputWidget.java new file mode 100644 index 000000000..fcfc18f16 --- /dev/null +++ b/src/main/java/net/vulkanmod/config/gui/widget/VTextInputWidget.java @@ -0,0 +1,140 @@ +package net.vulkanmod.config.gui.widget; + +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.ComponentPath; +import net.minecraft.client.gui.navigation.FocusNavigationEvent; +import net.minecraft.client.input.KeyEvent; +import net.minecraft.network.chat.Component; +import net.minecraft.util.Mth; +import net.vulkanmod.config.gui.render.GuiRenderer; +import net.vulkanmod.config.gui.util.VGuiConstants; +import net.vulkanmod.vulkan.util.ColorUtil; +import org.jetbrains.annotations.Nullable; +import org.lwjgl.glfw.GLFW; + +import java.util.Objects; +import java.util.function.Consumer; + +public class VTextInputWidget extends VAbstractWidget { + boolean selected = false; + Consumer onSearch; // when the search is "activated", like pressing enter + String searchTerm = ""; + Component message; + Component placeholder; + + public VTextInputWidget(int x, int y, int width, int height, Component placeholder, Consumer onSearch) { + this.setPosition(x, y, width, height); + + this.message = placeholder; + this.placeholder = placeholder; + this.onSearch = onSearch; + } + + public void renderWidget(double mouseX, double mouseY) { + if (!this.isVisible()) return; + int backgroundColor = this.isActive() + ? ColorUtil.ARGB.multiplyAlpha(VGuiConstants.COLOR_BLACK, 0.45f) + : ColorUtil.ARGB.multiplyAlpha(VGuiConstants.COLOR_BLACK, 0.3f); + + int textColor = VGuiConstants.COLOR_WHITE; + + if (this.message.getString().equals(this.placeholder.getString())) { + textColor = VGuiConstants.COLOR_GRAY; + } + + int selectionOutlineColor = ColorUtil.ARGB.multiplyAlpha(VGuiConstants.COLOR_RED, 0.8f); + int selectionFillColor = ColorUtil.ARGB.multiplyAlpha(VGuiConstants.COLOR_RED, 0.2f); + + GuiRenderer.fill(this.x, this.y, this.x + this.width, this.y + this.height, backgroundColor); + + if (this.selected) { + GuiRenderer.fill(this.x, this.y, this.x + 2, this.y + this.height, selectionOutlineColor); + GuiRenderer.fill(this.x, this.y, this.x + this.width, this.y + this.height, selectionFillColor); + } + + // this is down here because of layering + GuiRenderer.drawString( + Minecraft.getInstance().font, + this.message, + this.x + 8, (this.y + this.height / 2) - 4, + textColor | (Mth.ceil(255.0f) << 24)); + + } + + @Override + public boolean keyPressed(KeyEvent keyEvent) { + if (keyEvent.key() == GLFW.GLFW_KEY_ENTER || keyEvent.key() == GLFW.GLFW_KEY_KP_ENTER) { + this.onSearch.accept(this); + return true; + } else { + if (this.message.getString().equals(this.placeholder.getString())) { + this.message = Component.empty(); + } + + if (keyEvent.key() == GLFW.GLFW_KEY_BACKSPACE) { + String string = this.message.getString(); + if (!string.isEmpty()) { + string = string.substring(0, string.length() - 1); + } + this.message = Component.literal(string); + } else { + if (!keyEvent.hasShiftDown()) { + String string = this.message.getString(); + String name = GLFW.glfwGetKeyName(keyEvent.key(), keyEvent.scancode()); + if (name == null) { + return false; + } + string += name; + this.message = Component.literal(string); + } else { + String string = this.message.getString(); + String name = Objects.requireNonNull(GLFW.glfwGetKeyName(keyEvent.key(), keyEvent.scancode())).toUpperCase(); + string += name; + this.message = Component.literal(string); + } + } + + return true; + } + } + + public String getInput() { + return this.message.getString(); + } + + public void setSelected(boolean selected) { + this.selected = selected; + } + + public Component getMessage() { + return message; + } + + public void setMessage(Component message) { + this.message = message; + } + + public boolean isVisible() { + return visible; + } + + public void setVisible(boolean visible) { + this.visible = visible; + } + + @Override + public boolean isActive() { + return active; + } + + public void setActive(boolean active) { + this.active = active; + } + + @Override + public @Nullable ComponentPath nextFocusPath(FocusNavigationEvent event) { + if (!this.active || !this.visible) + return null; + return super.nextFocusPath(event); + } +} diff --git a/src/main/resources/assets/vulkanmod/lang/en_us.json b/src/main/resources/assets/vulkanmod/lang/en_us.json index 0421bef4a..310967ad4 100644 --- a/src/main/resources/assets/vulkanmod/lang/en_us.json +++ b/src/main/resources/assets/vulkanmod/lang/en_us.json @@ -47,5 +47,7 @@ "vulkanmod.options.builderThreads": "Chunk Builder Threads", "vulkanmod.options.builderThreads.auto": "Auto", - "vulkanmod.options.textureAnimations": "Texture Animations" + "vulkanmod.options.textureAnimations": "Texture Animations", + + "vulkanmod.options.searchFieldPlaceholder": "Search Graphics Settings" } \ No newline at end of file From 59d5413d645290babe30fbb289f6b6cf1d38ddde Mon Sep 17 00:00:00 2001 From: NotNekodev <97458908+NotNekodev@users.noreply.github.com> Date: Mon, 1 Dec 2025 20:32:49 +0100 Subject: [PATCH 11/21] fix warnings in the java code --- .../net/vulkanmod/config/gui/GuiElement.java | 8 ++- .../net/vulkanmod/config/gui/VOptionList.java | 13 +---- .../vulkanmod/config/gui/VOptionScreen.java | 57 ++++++++----------- .../gui/widget/CyclingOptionWidget.java | 32 +++++------ .../config/gui/widget/OptionWidget.java | 34 +---------- .../config/gui/widget/RangeOptionWidget.java | 6 -- .../config/gui/widget/VAbstractWidget.java | 3 +- .../config/gui/widget/VButtonWidget.java | 18 +----- .../config/gui/widget/VTextInputWidget.java | 18 +----- 9 files changed, 55 insertions(+), 134 deletions(-) diff --git a/src/main/java/net/vulkanmod/config/gui/GuiElement.java b/src/main/java/net/vulkanmod/config/gui/GuiElement.java index 04b85d2a1..a50316cab 100644 --- a/src/main/java/net/vulkanmod/config/gui/GuiElement.java +++ b/src/main/java/net/vulkanmod/config/gui/GuiElement.java @@ -7,7 +7,7 @@ import net.minecraft.client.gui.narration.NarrationElementOutput; import net.minecraft.client.gui.navigation.FocusNavigationEvent; import net.minecraft.client.gui.navigation.ScreenRectangle; -import net.minecraft.client.input.MouseButtonEvent; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; public abstract class GuiElement implements GuiEventListener, NarratableEntry { @@ -22,6 +22,7 @@ public abstract class GuiElement implements GuiEventListener, NarratableEntry { protected int hoverTime; protected long hoverStopTime; + @SuppressWarnings("unused") // this will surely be used some day public void setPosition(int x, int y) { this.x = x; this.y = y; @@ -34,6 +35,7 @@ public void setPosition(int x, int y, int width, int height) { this.height = height; } + @SuppressWarnings("unused") // this will surely be used someday public void resize(int width, int height) { this.width = width; this.height = height; @@ -102,7 +104,7 @@ public ComponentPath getCurrentFocusPath() { } @Override - public ScreenRectangle getRectangle() { + public @NotNull ScreenRectangle getRectangle() { return GuiEventListener.super.getRectangle(); } @@ -117,7 +119,7 @@ public boolean isFocused() { } @Override - public NarrationPriority narrationPriority() { + public @NotNull NarrationPriority narrationPriority() { return NarrationPriority.NONE; } diff --git a/src/main/java/net/vulkanmod/config/gui/VOptionList.java b/src/main/java/net/vulkanmod/config/gui/VOptionList.java index 619238c4d..4f7e18123 100644 --- a/src/main/java/net/vulkanmod/config/gui/VOptionList.java +++ b/src/main/java/net/vulkanmod/config/gui/VOptionList.java @@ -37,6 +37,7 @@ public VOptionList(int x, int y, int width, int height, int itemHeight) { this.totalItemHeight = this.itemHeight + this.itemMargin; } + @SuppressWarnings("unused") public void addButton(OptionWidget widget) { this.addEntry(new Entry(widget, this.itemMargin)); } @@ -59,23 +60,13 @@ public void addAll(OptionBlock[] blocks) { } } - public void addAll(Option[] options) { - for (Option option : options) { - int x0 = this.x; - int width = this.itemWidth; - int height = this.itemHeight; - - this.addEntry(new Entry(option.createOptionWidget(x0, 0, width, height), this.itemMargin)); -// this.addEntry(new Entry(options[i].createOptionWidget(width / 2 - 155, 0, 200, 20))); - } - } - private void addEntry(Entry entry) { this.children.add(entry); this.listLength += entry.getTotalHeight(); } + @SuppressWarnings("unused") public void clearEntries() { this.listLength = 0; this.children.clear(); diff --git a/src/main/java/net/vulkanmod/config/gui/VOptionScreen.java b/src/main/java/net/vulkanmod/config/gui/VOptionScreen.java index 3c63fab9a..9c3247d5d 100644 --- a/src/main/java/net/vulkanmod/config/gui/VOptionScreen.java +++ b/src/main/java/net/vulkanmod/config/gui/VOptionScreen.java @@ -2,6 +2,7 @@ import com.google.common.collect.Lists; import net.minecraft.Util; +import net.minecraft.client.Minecraft; import net.minecraft.client.gui.GuiGraphics; import net.minecraft.client.gui.components.events.GuiEventListener; import net.minecraft.client.gui.screens.Screen; @@ -36,9 +37,6 @@ public class VOptionScreen extends Screen { private int tooltipWidth; - private VButtonWidget supportButton; - - private VButtonWidget doneButton; private VButtonWidget applyButton; private VButtonWidget undoButton; @@ -134,29 +132,25 @@ private void buildLists(int left, int top, int listWidth, int listHeight, int it } } - private void addPageButtons(int x0, int y0, int width, int height) { - int x = x0; - int y = y0; + private void buildPage() { + this.buttons.clear(); + this.pageButtons.clear(); + this.clearWidgets(); + + int x = 10; + int y = 36; for (int i = 0; i < this.optionPages.size(); ++i) { var page = this.optionPages.get(i); final int finalIdx = i; - VButtonWidget widget = new VButtonWidget(x, y, width, height, Component.nullToEmpty(page.name), button -> this.setOptionList(finalIdx)); + VButtonWidget widget = new VButtonWidget(x, y, 80, VGuiConstants.WIDGET_HEIGHT, Component.nullToEmpty(page.name), button -> this.setOptionList(finalIdx)); this.buttons.add(widget); this.pageButtons.add(widget); this.addWidget(widget); - y += height; + y += VGuiConstants.WIDGET_HEIGHT; } this.pageButtons.get(this.currentListIdx).setSelected(true); - } - - private void buildPage() { - this.buttons.clear(); - this.pageButtons.clear(); - this.clearWidgets(); - - this.addPageButtons(10, 36, 80, VGuiConstants.WIDGET_HEIGHT); VOptionList currentList = this.optionPages.get(this.currentListIdx).getOptionList(); this.addWidget(currentList); @@ -167,18 +161,18 @@ private void buildPage() { private void addButtons() { int rightMargin = 10; int padding = 10; - int buttonWidth = minecraft.font.width(CommonComponents.GUI_DONE) + 2 * padding; + int buttonWidth = Minecraft.getInstance().font.width(CommonComponents.GUI_DONE) + 2 * padding; int x0 = (this.width - buttonWidth - rightMargin); int y0 = this.height - VGuiConstants.WIDGET_HEIGHT - 7; - this.doneButton = new VButtonWidget( + VButtonWidget doneButton = new VButtonWidget( x0, y0, buttonWidth, VGuiConstants.WIDGET_HEIGHT, CommonComponents.GUI_DONE, - button -> this.minecraft.setScreen(this.parent) + button -> Minecraft.getInstance().setScreen(this.parent) ); - buttonWidth = minecraft.font.width(Component.translatable("vulkanmod.options.buttons.apply")) + 2 * padding; + buttonWidth = Minecraft.getInstance().font.width(Component.translatable("vulkanmod.options.buttons.apply")) + 2 * padding; x0 -= (buttonWidth + VGuiConstants.WIDGET_MARGIN); this.applyButton = new VButtonWidget( x0, y0, @@ -187,29 +181,28 @@ private void addButtons() { button -> this.applyOptions() ); - buttonWidth = minecraft.font.width(Component.translatable("vulkanmod.options.buttons.undo")) + 2 * padding; + buttonWidth = Minecraft.getInstance().font.width(Component.translatable("vulkanmod.options.buttons.undo")) + 2 * padding; x0 -= (buttonWidth + VGuiConstants.WIDGET_MARGIN); this.undoButton = new VButtonWidget( x0, y0, buttonWidth, VGuiConstants.WIDGET_HEIGHT, Component.translatable("vulkanmod.options.buttons.undo"), - button -> { - undo(); - } + button -> undo() + ); this.searchField = new VTextInputWidget( 94, 4, x0 - 19, VGuiConstants.WIDGET_HEIGHT, Component.translatable("vulkanmod.options.searchFieldPlaceholder"), widget -> { - System.out.println("Searched in graphic settings: " + widget.getInput()); + } ); - buttonWidth = minecraft.font.width(Component.translatable("vulkanmod.options.buttons.kofi")) + padding; + buttonWidth = Minecraft.getInstance().font.width(Component.translatable("vulkanmod.options.buttons.kofi")) + padding; x0 = (this.width - buttonWidth - rightMargin); - this.supportButton = new VButtonWidget( + VButtonWidget supportButton = new VButtonWidget( x0, 4, buttonWidth, VGuiConstants.WIDGET_HEIGHT, Component.translatable("vulkanmod.options.buttons.kofi"), @@ -218,14 +211,14 @@ private void addButtons() { this.buttons.add(this.applyButton); - this.buttons.add(this.doneButton); - this.buttons.add(this.supportButton); + this.buttons.add(doneButton); + this.buttons.add(supportButton); this.buttons.add(this.undoButton); this.addWidget(this.applyButton); - this.addWidget(this.doneButton); - this.addWidget(this.supportButton); + this.addWidget(doneButton); + this.addWidget(supportButton); this.addWidget(this.undoButton); this.addWidget(this.searchField); @@ -259,7 +252,7 @@ public boolean mouseReleased(MouseButtonEvent event) { @Override public void onClose() { - this.minecraft.setScreen(this.parent); + Minecraft.getInstance().setScreen(this.parent); } @Override diff --git a/src/main/java/net/vulkanmod/config/gui/widget/CyclingOptionWidget.java b/src/main/java/net/vulkanmod/config/gui/widget/CyclingOptionWidget.java index f1401e809..b9fe2a69b 100644 --- a/src/main/java/net/vulkanmod/config/gui/widget/CyclingOptionWidget.java +++ b/src/main/java/net/vulkanmod/config/gui/widget/CyclingOptionWidget.java @@ -8,10 +8,11 @@ import net.vulkanmod.config.option.CyclingOption; import net.vulkanmod.render.shader.CustomRenderPipelines; import net.vulkanmod.vulkan.util.ColorUtil; +import org.jetbrains.annotations.NotNull; public class CyclingOptionWidget extends OptionWidget> { - private Button leftButton; - private Button rightButton; + private final Button leftButton; + private final Button rightButton; private boolean focused; @@ -24,11 +25,6 @@ public CyclingOptionWidget(CyclingOption option, int x, int y, int width, int // updateDisplayedValue(option.getValueText()); } - @Override - protected int getYImage(boolean hovered) { - return 0; - } - public void renderControls(double mouseX, double mouseY) { this.renderBars(); @@ -39,7 +35,7 @@ public void renderControls(double mouseX, double mouseY) { Font textRenderer = Minecraft.getInstance().font; int x = this.controlX + this.controlWidth / 2; int y = this.y + (this.height - 9) / 2; - GuiRenderer.drawScrollingString(textRenderer, this.getDisplayedValue(), x, y, (int) (rightButton.x - (leftButton.x + leftButton.width) - 12), color); + GuiRenderer.drawScrollingString(textRenderer, this.getDisplayedValue(), x, y, (rightButton.x - (leftButton.x + leftButton.width) - 12), color); this.leftButton.renderButton(mouseX, mouseY); this.rightButton.renderButton(mouseX, mouseY); @@ -137,7 +133,13 @@ else if (this.active) { color = INACTIVE_COLOR; } - float h = f; + float[][] vertices = getVertices(f); + + + GuiRenderer.submitPolygon(CustomRenderPipelines.GUI_TRIANGLES, TextureSetup.noTexture(), vertices, color); + } + + private float[] @NotNull [] getVertices(float f) { float w = f - 1.0f; float yC = y + height * 0.5f; float xC = x + width * 0.5f; @@ -146,20 +148,18 @@ else if (this.active) { if (this.direction == Direction.LEFT) { vertices = new float[][]{ {xC - w, yC}, - {xC + w, yC + h}, - {xC + w, yC - h}, + {xC + w, yC + f}, + {xC + w, yC - f}, }; } else { vertices = new float[][]{ {xC + w, yC}, - {xC - w, yC - h}, - {xC - w, yC + h}, + {xC - w, yC - f}, + {xC - w, yC + f}, }; } - - - GuiRenderer.submitPolygon(CustomRenderPipelines.GUI_TRIANGLES, TextureSetup.noTexture(), vertices, color); + return vertices; } enum Direction { diff --git a/src/main/java/net/vulkanmod/config/gui/widget/OptionWidget.java b/src/main/java/net/vulkanmod/config/gui/widget/OptionWidget.java index 80f9bde4a..bd93245b9 100644 --- a/src/main/java/net/vulkanmod/config/gui/widget/OptionWidget.java +++ b/src/main/java/net/vulkanmod/config/gui/widget/OptionWidget.java @@ -5,13 +5,11 @@ import net.minecraft.client.gui.narration.NarratableEntry; import net.minecraft.client.gui.narration.NarrationElementOutput; import net.minecraft.client.input.MouseButtonEvent; -import net.minecraft.client.resources.sounds.SimpleSoundInstance; -import net.minecraft.client.sounds.SoundManager; import net.minecraft.network.chat.Component; -import net.minecraft.sounds.SoundEvents; import net.vulkanmod.config.gui.render.GuiRenderer; import net.vulkanmod.config.option.Option; import net.vulkanmod.vulkan.util.ColorUtil; +import org.jetbrains.annotations.NotNull; public abstract class OptionWidget> extends VAbstractWidget implements NarratableEntry { @@ -52,15 +50,9 @@ public void render(double mouseX, double mouseY) { this.renderWidget(mouseX, mouseY); } - public void updateState() { - - } - public void renderWidget(double mouseX, double mouseY) { Minecraft minecraftClient = Minecraft.getInstance(); - int i = this.getYImage(this.isHovered()); - int xPadding = 0; int yPadding = 0; @@ -90,20 +82,6 @@ public void renderWidget(double mouseX, double mouseY) { this.renderControls(mouseX, mouseY); } - protected int getYImage(boolean hovered) { - int i = 1; - if (!this.active) { - i = 0; - } else if (hovered) { - i = 2; - } - return i; - } - - public boolean isHovered() { - return this.hovered || this.focused; - } - protected abstract void renderControls(double mouseX, double mouseY); public abstract void onClick(double mouseX, double mouseY); @@ -112,10 +90,6 @@ public boolean isHovered() { protected abstract void onDrag(double mouseX, double mouseY, double deltaX, double deltaY); - protected boolean isValidClickButton(int button) { - return button == 0; - } - @Override public boolean mouseDragged(MouseButtonEvent event, double deltaX, double deltaY) { if (this.isValidClickButton(event.button())) { @@ -184,7 +158,7 @@ public Component getTooltip() { } @Override - public NarrationPriority narrationPriority() { + public @NotNull NarrationPriority narrationPriority() { if (this.focused) { return NarrationPriority.FOCUSED; } @@ -198,8 +172,4 @@ public NarrationPriority narrationPriority() { public final void updateNarration(NarrationElementOutput narrationElementOutput) { } - public void playDownSound(SoundManager soundManager) { - soundManager.play(SimpleSoundInstance.forUI(SoundEvents.UI_BUTTON_CLICK, 1.0f)); - } - } diff --git a/src/main/java/net/vulkanmod/config/gui/widget/RangeOptionWidget.java b/src/main/java/net/vulkanmod/config/gui/widget/RangeOptionWidget.java index 277386177..a9459be2a 100644 --- a/src/main/java/net/vulkanmod/config/gui/widget/RangeOptionWidget.java +++ b/src/main/java/net/vulkanmod/config/gui/widget/RangeOptionWidget.java @@ -8,7 +8,6 @@ import net.minecraft.util.Mth; import net.vulkanmod.config.gui.render.GuiRenderer; import net.vulkanmod.config.option.RangeOption; -import net.vulkanmod.vulkan.VRenderSystem; import net.vulkanmod.vulkan.util.ColorUtil; import org.lwjgl.glfw.GLFW; @@ -24,11 +23,6 @@ public RangeOptionWidget(RangeOption option, int x, int y, int width, int height } - @Override - protected int getYImage(boolean hovered) { - return 0; - } - @Override protected void renderControls(double mouseX, double mouseY) { int valueX = this.controlX + (int) (this.value * (this.controlWidth)); diff --git a/src/main/java/net/vulkanmod/config/gui/widget/VAbstractWidget.java b/src/main/java/net/vulkanmod/config/gui/widget/VAbstractWidget.java index 6745f4810..d84c53006 100644 --- a/src/main/java/net/vulkanmod/config/gui/widget/VAbstractWidget.java +++ b/src/main/java/net/vulkanmod/config/gui/widget/VAbstractWidget.java @@ -1,7 +1,6 @@ package net.vulkanmod.config.gui.widget; import net.minecraft.client.Minecraft; -import net.minecraft.client.input.KeyEvent; import net.minecraft.client.input.MouseButtonEvent; import net.minecraft.client.resources.sounds.SimpleSoundInstance; import net.minecraft.client.sounds.SoundManager; @@ -11,7 +10,6 @@ import net.vulkanmod.config.gui.util.VGuiConstants; import net.vulkanmod.config.gui.render.GuiRenderer; import net.vulkanmod.vulkan.util.ColorUtil; -import org.lwjgl.glfw.GLFW; public abstract class VAbstractWidget extends GuiElement { public boolean active = true; @@ -38,6 +36,7 @@ public void onRelease(double mX, double mY) { protected void onDrag(double mX, double mY, double f, double g) { } + @SuppressWarnings("SameParameterValue") // I just want code without warnings :^ protected void renderHovering(int xPadding, int yPadding) { if (this.isFocused() || !this.isActive() || !this.visible || this.focused) return; diff --git a/src/main/java/net/vulkanmod/config/gui/widget/VButtonWidget.java b/src/main/java/net/vulkanmod/config/gui/widget/VButtonWidget.java index 291a88648..50745bcd8 100644 --- a/src/main/java/net/vulkanmod/config/gui/widget/VButtonWidget.java +++ b/src/main/java/net/vulkanmod/config/gui/widget/VButtonWidget.java @@ -34,6 +34,7 @@ public void renderWidget(double mouseX, double mouseY) { int textColor = this.isActive() ? VGuiConstants.COLOR_WHITE : VGuiConstants.COLOR_GRAY; + //noinspection DuplicatedCode int selectionOutlineColor = ColorUtil.ARGB.multiplyAlpha(VGuiConstants.COLOR_RED, 0.8f); int selectionFillColor = ColorUtil.ARGB.multiplyAlpha(VGuiConstants.COLOR_RED, 0.2f); @@ -56,31 +57,14 @@ public void onClick(double mX, double mY) { this.onPress.accept(this); } - private void doAction() { - this.onPress.accept(this); - this.playDownSound(Minecraft.getInstance().getSoundManager()); - } - public void setSelected(boolean selected) { this.selected = selected; } - public Component getMessage() { - return message; - } - - public void setMessage(Component message) { - this.message = message; - } - public boolean isVisible() { return visible; } - public void setVisible(boolean visible) { - this.visible = visible; - } - @Override public boolean isActive() { return active; diff --git a/src/main/java/net/vulkanmod/config/gui/widget/VTextInputWidget.java b/src/main/java/net/vulkanmod/config/gui/widget/VTextInputWidget.java index fcfc18f16..3d18a7a66 100644 --- a/src/main/java/net/vulkanmod/config/gui/widget/VTextInputWidget.java +++ b/src/main/java/net/vulkanmod/config/gui/widget/VTextInputWidget.java @@ -18,7 +18,6 @@ public class VTextInputWidget extends VAbstractWidget { boolean selected = false; Consumer onSearch; // when the search is "activated", like pressing enter - String searchTerm = ""; Component message; Component placeholder; @@ -42,6 +41,7 @@ public void renderWidget(double mouseX, double mouseY) { textColor = VGuiConstants.COLOR_GRAY; } + //noinspection DuplicatedCode int selectionOutlineColor = ColorUtil.ARGB.multiplyAlpha(VGuiConstants.COLOR_RED, 0.8f); int selectionFillColor = ColorUtil.ARGB.multiplyAlpha(VGuiConstants.COLOR_RED, 0.2f); @@ -65,7 +65,6 @@ public void renderWidget(double mouseX, double mouseY) { public boolean keyPressed(KeyEvent keyEvent) { if (keyEvent.key() == GLFW.GLFW_KEY_ENTER || keyEvent.key() == GLFW.GLFW_KEY_KP_ENTER) { this.onSearch.accept(this); - return true; } else { if (this.message.getString().equals(this.placeholder.getString())) { this.message = Component.empty(); @@ -94,34 +93,23 @@ public boolean keyPressed(KeyEvent keyEvent) { } } - return true; } + return true; } public String getInput() { return this.message.getString(); } + @SuppressWarnings("unused") public void setSelected(boolean selected) { this.selected = selected; } - public Component getMessage() { - return message; - } - - public void setMessage(Component message) { - this.message = message; - } - public boolean isVisible() { return visible; } - public void setVisible(boolean visible) { - this.visible = visible; - } - @Override public boolean isActive() { return active; From 51de9bc0c53abadad3b3da4a3e49049cc268769c Mon Sep 17 00:00:00 2001 From: NotNekodev <97458908+NotNekodev@users.noreply.github.com> Date: Mon, 1 Dec 2025 21:21:14 +0100 Subject: [PATCH 12/21] first real search version --- .../net/vulkanmod/config/gui/VOptionList.java | 55 ++++++++--- .../vulkanmod/config/gui/VOptionScreen.java | 99 +++++++++++++++++-- .../config/gui/util/SearchHelper.java | 19 ++++ .../config/gui/widget/VTextInputWidget.java | 4 + .../net/vulkanmod/config/option/Option.java | 4 + .../vulkanmod/config/option/OptionPage.java | 2 +- 6 files changed, 159 insertions(+), 24 deletions(-) create mode 100644 src/main/java/net/vulkanmod/config/gui/util/SearchHelper.java diff --git a/src/main/java/net/vulkanmod/config/gui/VOptionList.java b/src/main/java/net/vulkanmod/config/gui/VOptionList.java index 4f7e18123..69f665046 100644 --- a/src/main/java/net/vulkanmod/config/gui/VOptionList.java +++ b/src/main/java/net/vulkanmod/config/gui/VOptionList.java @@ -2,8 +2,10 @@ import com.mojang.blaze3d.opengl.GlStateManager; import it.unimi.dsi.fastutil.objects.ObjectArrayList; +import net.minecraft.client.Minecraft; import net.minecraft.client.gui.components.events.GuiEventListener; import net.minecraft.client.input.MouseButtonEvent; +import net.minecraft.network.chat.Component; import net.minecraft.util.Mth; import net.vulkanmod.config.gui.render.GuiRenderer; import net.vulkanmod.config.gui.widget.OptionWidget; @@ -39,7 +41,7 @@ public VOptionList(int x, int y, int width, int height, int itemHeight) { @SuppressWarnings("unused") public void addButton(OptionWidget widget) { - this.addEntry(new Entry(widget, this.itemMargin)); + this.addEntry(new Entry(widget, this.itemMargin, null)); } public void addAll(OptionBlock[] blocks) { @@ -48,21 +50,24 @@ public void addAll(OptionBlock[] blocks) { int width = this.itemWidth; int height = this.itemHeight; + // add a header (this is MOSTLY for the search) + String title = block.title(); + if (title != null && !title.isEmpty()) { + this.addEntry(new Entry(null, 8, title)); + } + var options = block.options(); for (Option option : options) { - int margin = this.itemMargin; - - this.addEntry(new Entry(option.createOptionWidget(x0, 0, width, height), margin)); + this.addEntry(new Entry(option.createOptionWidget(x0, 0, width, height), margin, null)); } - this.addEntry(new Entry(null, 12)); + this.addEntry(new Entry(null, 12, null)); } } private void addEntry(Entry entry) { this.children.add(entry); - this.listLength += entry.getTotalHeight(); } @@ -254,13 +259,11 @@ protected void renderList(int mouseX, int mouseY) { int rowTop = this.y - (int) this.getScrollAmount(); for (int j = 0; j < itemCount; ++j) { - int rowBottom = rowTop + this.itemHeight; - VOptionList.Entry entry = this.getEntry(j); - if (rowBottom >= this.y && rowTop <= (this.y + this.height)) { - boolean updateState = this.focused == null; - entry.render(rowTop, mouseX, mouseY, updateState); + if (rowTop + entry.getTotalHeight() >= this.y && rowTop <= (this.y + this.height)) { + boolean updateState = this.focused == null; + entry.render(rowTop, mouseX, mouseY, updateState, this.x); } rowTop += entry.getTotalHeight(); @@ -278,13 +281,28 @@ protected boolean isValidClickButton(int i) { protected static class Entry implements GuiEventListener { final VAbstractWidget widget; final int margin; + final String headerTitle; - private Entry(OptionWidget widget, int margin) { + private Entry(OptionWidget widget, int margin, String headerTitle) { this.widget = widget; this.margin = margin; + this.headerTitle = headerTitle; } - public void render(int y, int mouseX, int mouseY, boolean updateState) { + public void render(int y, int mouseX, int mouseY, boolean updateState, int listX) { + // if there is a title, RENDER IT!!! + if (headerTitle != null && !headerTitle.isEmpty()) { + int headerY = y + 4; + GuiRenderer.drawString( + Minecraft.getInstance().font, + Component.literal(headerTitle), + listX + 8, + headerY, + 0xFFFFFFFF + ); + return; + } + if (widget == null) return; @@ -297,6 +315,9 @@ public void render(int y, int mouseX, int mouseY, boolean updateState) { } public int getTotalHeight() { + if (headerTitle != null && !headerTitle.isEmpty()) { + return Minecraft.getInstance().font.lineHeight + margin; + } if (widget != null) return widget.height + margin; else @@ -305,16 +326,19 @@ public int getTotalHeight() { @Override public boolean mouseClicked(MouseButtonEvent event, boolean bl) { + if (widget == null) return false; return widget.mouseClicked(event, bl); } @Override public boolean mouseReleased(MouseButtonEvent event) { + if (widget == null) return false; return widget.mouseReleased(event); } @Override public boolean mouseDragged(MouseButtonEvent event, double deltaX, double deltaY) { + if (widget == null) return false; return widget.mouseDragged(event, deltaX, deltaY); } @@ -325,7 +349,8 @@ public boolean isFocused() { @Override public void setFocused(boolean bl) { - widget.setFocused(bl); + if (widget != null) + widget.setFocused(bl); } } -} +} \ No newline at end of file diff --git a/src/main/java/net/vulkanmod/config/gui/VOptionScreen.java b/src/main/java/net/vulkanmod/config/gui/VOptionScreen.java index 9c3247d5d..1ebe0e337 100644 --- a/src/main/java/net/vulkanmod/config/gui/VOptionScreen.java +++ b/src/main/java/net/vulkanmod/config/gui/VOptionScreen.java @@ -14,12 +14,15 @@ import net.minecraft.util.FormattedCharSequence; import net.vulkanmod.Initializer; import net.vulkanmod.config.gui.render.GuiRenderer; +import net.vulkanmod.config.gui.util.SearchHelper; import net.vulkanmod.config.gui.util.VGuiConstants; import net.vulkanmod.config.gui.widget.VAbstractWidget; import net.vulkanmod.config.gui.widget.VButtonWidget; import net.vulkanmod.config.gui.widget.VTextInputWidget; +import net.vulkanmod.config.option.CyclingOption; import net.vulkanmod.config.option.OptionPage; import net.vulkanmod.config.option.Options; +import net.vulkanmod.config.option.Option; import net.vulkanmod.vulkan.VRenderSystem; import net.vulkanmod.vulkan.util.ColorUtil; @@ -32,8 +35,10 @@ public class VOptionScreen extends Screen { private final Screen parent; private final List optionPages; + private OptionPage searchResultsPage; private int currentListIdx = 0; + private boolean isSearchActive = false; private int tooltipWidth; @@ -132,6 +137,72 @@ private void buildLists(int left, int top, int listWidth, int listHeight, int it } } + private void performSearch(String query) { + if (query == null || query.trim().isEmpty()) { + isSearchActive = false; + this.currentListIdx = 0; + buildPage(); + return; + } + + String searchTerm = query.toLowerCase().trim(); + List searchResults = new ArrayList<>(); + + for (OptionPage page : this.optionPages) { + List> matchingOptions = new ArrayList<>(); + + for (OptionBlock block : page.optionBlocks) { + for (Option option : block.options()) { + boolean matches = false; + + String optionName = option.getName().getString().toLowerCase(); + String optionTooltip = option.getTooltip() != null ? + option.getTooltip().getString().toLowerCase() : ""; + String displayedValue = option.getDisplayedValue().getString().toLowerCase(); + + if (optionName.contains(searchTerm) || + optionTooltip.contains(searchTerm) || + displayedValue.contains(searchTerm)) { + matches = true; + } + + else if (option instanceof CyclingOption cycling) { + if (SearchHelper.matchesAnyValue(cycling, searchTerm)) { + matches = true; + } + } + + if (matches) { + matchingOptions.add(option); + } + } + } + + if (!matchingOptions.isEmpty()) { + searchResults.add(new OptionBlock("§l" + page.name, + matchingOptions.toArray(new Option[0]))); + searchResults.add(new OptionBlock("", new Option[0])); + } + } + + searchResultsPage = new OptionPage( + "Search Results", + searchResults.toArray(new OptionBlock[0]) + ); + + int top = 29; + int itemHeight = 20; + int leftMargin = 94; + int rightMargin = 3; + int listWidth = this.width - rightMargin - leftMargin; + int listHeight = this.height - top - 60; + + searchResultsPage.createList(leftMargin, top, listWidth, listHeight, itemHeight); + + isSearchActive = true; + buildPage(); + } + private void buildPage() { this.buttons.clear(); this.pageButtons.clear(); @@ -150,10 +221,16 @@ private void buildPage() { y += VGuiConstants.WIDGET_HEIGHT; } - this.pageButtons.get(this.currentListIdx).setSelected(true); - - VOptionList currentList = this.optionPages.get(this.currentListIdx).getOptionList(); - this.addWidget(currentList); + if (!isSearchActive) { + this.pageButtons.get(this.currentListIdx).setSelected(true); + VOptionList currentList = this.optionPages.get(this.currentListIdx).getOptionList(); + this.addWidget(currentList); + } else { + if (searchResultsPage != null) { + VOptionList searchList = searchResultsPage.getOptionList(); + this.addWidget(searchList); + } + } this.addButtons(); } @@ -194,9 +271,7 @@ private void addButtons() { 94, 4, x0 - 19, VGuiConstants.WIDGET_HEIGHT, Component.translatable("vulkanmod.options.searchFieldPlaceholder"), - widget -> { - - } + widget -> performSearch(widget.getInput()) ); @@ -270,9 +345,16 @@ public void render(GuiGraphics guiGraphics, int mouseX, int mouseY, float delta) int iconY = 4 + (iconBackgroundHeight - 4 - size) / 2; guiGraphics.blit(RenderPipelines.GUI_TEXTURED, ICON, iconX, iconY, 0f, 0f, size, size, size, size); - VOptionList currentList = this.optionPages.get(this.currentListIdx).getOptionList(); + VOptionList currentList; + if (isSearchActive && searchResultsPage != null) { + currentList = searchResultsPage.getOptionList(); + } else { + currentList = this.optionPages.get(this.currentListIdx).getOptionList(); + } + currentList.updateState(mouseX, mouseY); currentList.renderWidget(mouseX, mouseY); + for (VButtonWidget button : buttons) { button.updateState(mouseX, mouseY); button.render(mouseX, mouseY); @@ -342,6 +424,7 @@ private void updateState() { private void setOptionList(int i) { this.currentListIdx = i; + this.isSearchActive = false; this.buildPage(); diff --git a/src/main/java/net/vulkanmod/config/gui/util/SearchHelper.java b/src/main/java/net/vulkanmod/config/gui/util/SearchHelper.java new file mode 100644 index 000000000..4ec106685 --- /dev/null +++ b/src/main/java/net/vulkanmod/config/gui/util/SearchHelper.java @@ -0,0 +1,19 @@ +package net.vulkanmod.config.gui.util; + +import net.minecraft.network.chat.Component; +import net.vulkanmod.config.option.CyclingOption; + +import java.util.function.Function; + +public class SearchHelper { + public static boolean matchesAnyValue(CyclingOption cycling, String searchTerm) { + Function translator = cycling.getTranslator(); + for (T value : cycling.getValues()) { + String translated = translator.apply(value).getString().toLowerCase(); + if (translated.contains(searchTerm)) { + return true; + } + } + return false; + } +} diff --git a/src/main/java/net/vulkanmod/config/gui/widget/VTextInputWidget.java b/src/main/java/net/vulkanmod/config/gui/widget/VTextInputWidget.java index 3d18a7a66..75eb9f99c 100644 --- a/src/main/java/net/vulkanmod/config/gui/widget/VTextInputWidget.java +++ b/src/main/java/net/vulkanmod/config/gui/widget/VTextInputWidget.java @@ -101,6 +101,10 @@ public String getInput() { return this.message.getString(); } + public void setInput(String input) { + this.message = Component.literal(input); + } + @SuppressWarnings("unused") public void setSelected(boolean selected) { this.selected = selected; diff --git a/src/main/java/net/vulkanmod/config/option/Option.java b/src/main/java/net/vulkanmod/config/option/Option.java index 4a441806f..42b211b9d 100644 --- a/src/main/java/net/vulkanmod/config/option/Option.java +++ b/src/main/java/net/vulkanmod/config/option/Option.java @@ -72,6 +72,10 @@ public Option setTranslator(Function translator) { return this; } + public Function getTranslator() { + return translator; + } + public Option setTooltip(Function tooltipTranslator) { this.tooltipTranslator = tooltipTranslator; return this; diff --git a/src/main/java/net/vulkanmod/config/option/OptionPage.java b/src/main/java/net/vulkanmod/config/option/OptionPage.java index 069ebef85..bb9e1f0d9 100644 --- a/src/main/java/net/vulkanmod/config/option/OptionPage.java +++ b/src/main/java/net/vulkanmod/config/option/OptionPage.java @@ -5,7 +5,7 @@ public class OptionPage { public final String name; - OptionBlock[] optionBlocks; + public OptionBlock[] optionBlocks; private VOptionList optionList; public OptionPage(String name, OptionBlock[] optionBlocks) { From cf86e8f0fe542f9f23acc4f5e69c7e43252f0861 Mon Sep 17 00:00:00 2001 From: NotNekodev <97458908+NotNekodev@users.noreply.github.com> Date: Mon, 1 Dec 2025 21:47:54 +0100 Subject: [PATCH 13/21] add some styling for selection --- .../vulkanmod/config/gui/VOptionScreen.java | 79 ++++++++++++ .../config/gui/widget/VTextInputWidget.java | 113 ++++++++++-------- 2 files changed, 144 insertions(+), 48 deletions(-) diff --git a/src/main/java/net/vulkanmod/config/gui/VOptionScreen.java b/src/main/java/net/vulkanmod/config/gui/VOptionScreen.java index 1ebe0e337..cd8ec75c3 100644 --- a/src/main/java/net/vulkanmod/config/gui/VOptionScreen.java +++ b/src/main/java/net/vulkanmod/config/gui/VOptionScreen.java @@ -6,6 +6,7 @@ import net.minecraft.client.gui.GuiGraphics; import net.minecraft.client.gui.components.events.GuiEventListener; import net.minecraft.client.gui.screens.Screen; +import net.minecraft.client.input.KeyEvent; import net.minecraft.client.input.MouseButtonEvent; import net.minecraft.client.renderer.RenderPipelines; import net.minecraft.network.chat.CommonComponents; @@ -25,6 +26,7 @@ import net.vulkanmod.config.option.Option; import net.vulkanmod.vulkan.VRenderSystem; import net.vulkanmod.vulkan.util.ColorUtil; +import org.lwjgl.glfw.GLFW; import java.util.ArrayList; import java.util.List; @@ -111,6 +113,8 @@ protected void init() { this.tooltipWidth = width; + addButtonsWithSearchBar(); + buildPage(); this.applyButton.active = false; @@ -267,6 +271,64 @@ private void addButtons() { button -> undo() ); + + buttonWidth = Minecraft.getInstance().font.width(Component.translatable("vulkanmod.options.buttons.kofi")) + padding; + x0 = (this.width - buttonWidth - rightMargin); + VButtonWidget supportButton = new VButtonWidget( + x0, 4, + buttonWidth, VGuiConstants.WIDGET_HEIGHT, + Component.translatable("vulkanmod.options.buttons.kofi"), + button -> Util.getPlatform().openUri("https://ko-fi.com/xcollateral") + ); + + + this.buttons.add(this.applyButton); + this.buttons.add(doneButton); + this.buttons.add(supportButton); + this.buttons.add(this.undoButton); + + + this.addWidget(this.applyButton); + this.addWidget(doneButton); + this.addWidget(supportButton); + this.addWidget(this.undoButton); + + this.addWidget(this.searchField); + } + + private void addButtonsWithSearchBar() { + int rightMargin = 10; + int padding = 10; + int buttonWidth = Minecraft.getInstance().font.width(CommonComponents.GUI_DONE) + 2 * padding; + int x0 = (this.width - buttonWidth - rightMargin); + int y0 = this.height - VGuiConstants.WIDGET_HEIGHT - 7; + + VButtonWidget doneButton = new VButtonWidget( + x0, y0, + buttonWidth, VGuiConstants.WIDGET_HEIGHT, + CommonComponents.GUI_DONE, + button -> Minecraft.getInstance().setScreen(this.parent) + ); + + buttonWidth = Minecraft.getInstance().font.width(Component.translatable("vulkanmod.options.buttons.apply")) + 2 * padding; + x0 -= (buttonWidth + VGuiConstants.WIDGET_MARGIN); + this.applyButton = new VButtonWidget( + x0, y0, + buttonWidth, VGuiConstants.WIDGET_HEIGHT, + Component.translatable("vulkanmod.options.buttons.apply"), + button -> this.applyOptions() + ); + + buttonWidth = Minecraft.getInstance().font.width(Component.translatable("vulkanmod.options.buttons.undo")) + 2 * padding; + x0 -= (buttonWidth + VGuiConstants.WIDGET_MARGIN); + this.undoButton = new VButtonWidget( + x0, y0, + buttonWidth, VGuiConstants.WIDGET_HEIGHT, + Component.translatable("vulkanmod.options.buttons.undo"), + button -> undo() + + ); + this.searchField = new VTextInputWidget( 94, 4, x0 - 19, VGuiConstants.WIDGET_HEIGHT, @@ -426,6 +488,9 @@ private void setOptionList(int i) { this.currentListIdx = i; this.isSearchActive = false; + this.searchField.setInput(""); + this.searchField.setFocused(false); + this.buildPage(); this.pageButtons.get(i).setSelected(true); @@ -441,4 +506,18 @@ private void applyOptions() { Initializer.CONFIG.write(); } + + @Override + public boolean keyPressed(KeyEvent keyEvent) { + if (keyEvent.key() == GLFW.GLFW_KEY_ESCAPE && this.isSearchActive) { + this.isSearchActive = false; + this.searchField.setInput(""); + this.searchField.setFocused(false); + this.buildPage(); + this.pageButtons.get(this.currentListIdx).setSelected(true); + return true; + } + + return super.keyPressed(keyEvent); + } } \ No newline at end of file diff --git a/src/main/java/net/vulkanmod/config/gui/widget/VTextInputWidget.java b/src/main/java/net/vulkanmod/config/gui/widget/VTextInputWidget.java index 75eb9f99c..47745d05d 100644 --- a/src/main/java/net/vulkanmod/config/gui/widget/VTextInputWidget.java +++ b/src/main/java/net/vulkanmod/config/gui/widget/VTextInputWidget.java @@ -4,42 +4,41 @@ import net.minecraft.client.gui.ComponentPath; import net.minecraft.client.gui.navigation.FocusNavigationEvent; import net.minecraft.client.input.KeyEvent; +import net.minecraft.client.input.MouseButtonEvent; import net.minecraft.network.chat.Component; -import net.minecraft.util.Mth; import net.vulkanmod.config.gui.render.GuiRenderer; import net.vulkanmod.config.gui.util.VGuiConstants; import net.vulkanmod.vulkan.util.ColorUtil; import org.jetbrains.annotations.Nullable; import org.lwjgl.glfw.GLFW; -import java.util.Objects; import java.util.function.Consumer; public class VTextInputWidget extends VAbstractWidget { boolean selected = false; Consumer onSearch; // when the search is "activated", like pressing enter - Component message; - Component placeholder; + private String text; + private final Component placeholder; public VTextInputWidget(int x, int y, int width, int height, Component placeholder, Consumer onSearch) { this.setPosition(x, y, width, height); - this.message = placeholder; this.placeholder = placeholder; this.onSearch = onSearch; + this.text = ""; } + @Override public void renderWidget(double mouseX, double mouseY) { if (!this.isVisible()) return; - int backgroundColor = this.isActive() - ? ColorUtil.ARGB.multiplyAlpha(VGuiConstants.COLOR_BLACK, 0.45f) - : ColorUtil.ARGB.multiplyAlpha(VGuiConstants.COLOR_BLACK, 0.3f); - int textColor = VGuiConstants.COLOR_WHITE; + boolean hasText = !this.text.isEmpty(); - if (this.message.getString().equals(this.placeholder.getString())) { - textColor = VGuiConstants.COLOR_GRAY; - } + int backgroundColor = this.focused || this.selected + ? ColorUtil.ARGB.multiplyAlpha(VGuiConstants.COLOR_BLACK, 0.3f) + : ColorUtil.ARGB.multiplyAlpha(VGuiConstants.COLOR_BLACK, 0.45f); + + int textColor = hasText ? VGuiConstants.COLOR_WHITE : VGuiConstants.COLOR_GRAY; //noinspection DuplicatedCode int selectionOutlineColor = ColorUtil.ARGB.multiplyAlpha(VGuiConstants.COLOR_RED, 0.8f); @@ -47,62 +46,56 @@ public void renderWidget(double mouseX, double mouseY) { GuiRenderer.fill(this.x, this.y, this.x + this.width, this.y + this.height, backgroundColor); - if (this.selected) { - GuiRenderer.fill(this.x, this.y, this.x + 2, this.y + this.height, selectionOutlineColor); + if (this.selected || this.focused) { + GuiRenderer.renderBorder(x, y, x + width, y + height, 1, selectionOutlineColor); GuiRenderer.fill(this.x, this.y, this.x + this.width, this.y + this.height, selectionFillColor); } - // this is down here because of layering + Component displayText = hasText ? Component.literal(this.text) : this.placeholder; + GuiRenderer.drawString( Minecraft.getInstance().font, - this.message, - this.x + 8, (this.y + this.height / 2) - 4, - textColor | (Mth.ceil(255.0f) << 24)); - + displayText, + this.x + 8, + this.y + (this.height - 8) / 2, + textColor | 0xFF000000 + ); } @Override public boolean keyPressed(KeyEvent keyEvent) { - if (keyEvent.key() == GLFW.GLFW_KEY_ENTER || keyEvent.key() == GLFW.GLFW_KEY_KP_ENTER) { + if (!this.focused && !this.selected) return false; + + if (keyEvent.key() == GLFW.GLFW_KEY_ENTER || keyEvent.key() == GLFW.GLFW_KEY_KP_ENTER) { this.onSearch.accept(this); - } else { - if (this.message.getString().equals(this.placeholder.getString())) { - this.message = Component.empty(); - } + return true; + } - if (keyEvent.key() == GLFW.GLFW_KEY_BACKSPACE) { - String string = this.message.getString(); - if (!string.isEmpty()) { - string = string.substring(0, string.length() - 1); - } - this.message = Component.literal(string); - } else { - if (!keyEvent.hasShiftDown()) { - String string = this.message.getString(); - String name = GLFW.glfwGetKeyName(keyEvent.key(), keyEvent.scancode()); - if (name == null) { - return false; - } - string += name; - this.message = Component.literal(string); - } else { - String string = this.message.getString(); - String name = Objects.requireNonNull(GLFW.glfwGetKeyName(keyEvent.key(), keyEvent.scancode())).toUpperCase(); - string += name; - this.message = Component.literal(string); - } + if (keyEvent.key() == GLFW.GLFW_KEY_BACKSPACE) { + if (!this.text.isEmpty()) { + this.text = this.text.substring(0, this.text.length() - 1); + this.onSearch.accept(this); // live search } + return true; + } + String keyName = GLFW.glfwGetKeyName(keyEvent.key(), keyEvent.scancode()); + if (keyName != null && keyName.length() == 1) { + if (keyEvent.hasShiftDown()) keyName = keyName.toUpperCase(); + this.text += keyName; + this.onSearch.accept(this); + return true; } - return true; + + return false; } public String getInput() { - return this.message.getString(); + return this.text; } public void setInput(String input) { - this.message = Component.literal(input); + this.text = input != null ? input : ""; } @SuppressWarnings("unused") @@ -129,4 +122,28 @@ public void setActive(boolean active) { return null; return super.nextFocusPath(event); } + + @Override + public boolean mouseClicked(MouseButtonEvent event, boolean bl) { + if (!this.active || !this.visible) return false; + + boolean clicked = this.clicked(event.x(), event.y()); + if (clicked) { + this.setFocused(true); + this.selected = true; + return true; + } else { + this.setFocused(false); + this.selected = false; + return false; + } + } + + @Override + public void setFocused(boolean focused) { + super.setFocused(focused); + if (!focused) { + this.selected = false; + } + } } From 0c353b4bdc643b1010b5ee518aad0281e961096b Mon Sep 17 00:00:00 2001 From: NotNekodev <97458908+NotNekodev@users.noreply.github.com> Date: Mon, 1 Dec 2025 22:05:21 +0100 Subject: [PATCH 14/21] add selection and such UX + a blinking cursor --- .../config/gui/widget/VTextInputWidget.java | 135 +++++++++++++++--- 1 file changed, 113 insertions(+), 22 deletions(-) diff --git a/src/main/java/net/vulkanmod/config/gui/widget/VTextInputWidget.java b/src/main/java/net/vulkanmod/config/gui/widget/VTextInputWidget.java index 47745d05d..7d6b85aa9 100644 --- a/src/main/java/net/vulkanmod/config/gui/widget/VTextInputWidget.java +++ b/src/main/java/net/vulkanmod/config/gui/widget/VTextInputWidget.java @@ -1,5 +1,6 @@ package net.vulkanmod.config.gui.widget; +import net.minecraft.Util; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.ComponentPath; import net.minecraft.client.gui.navigation.FocusNavigationEvent; @@ -20,6 +21,13 @@ public class VTextInputWidget extends VAbstractWidget { private String text; private final Component placeholder; + private int cursorPos = 0; + private int selectionEnd = 0; + private long lastBlinkTime = 0; + private boolean showCursor = true; + + private static final int CURSOR_BLINK_INTERVAL = 500; // ms + public VTextInputWidget(int x, int y, int width, int height, Component placeholder, Consumer onSearch) { this.setPosition(x, y, width, height); @@ -33,56 +41,129 @@ public void renderWidget(double mouseX, double mouseY) { if (!this.isVisible()) return; boolean hasText = !this.text.isEmpty(); + boolean isFocused = this.focused || this.selected; - int backgroundColor = this.focused || this.selected - ? ColorUtil.ARGB.multiplyAlpha(VGuiConstants.COLOR_BLACK, 0.3f) - : ColorUtil.ARGB.multiplyAlpha(VGuiConstants.COLOR_BLACK, 0.45f); + int backgroundColor = ColorUtil.ARGB.multiplyAlpha(VGuiConstants.COLOR_BLACK, 0.45f); int textColor = hasText ? VGuiConstants.COLOR_WHITE : VGuiConstants.COLOR_GRAY; - //noinspection DuplicatedCode - int selectionOutlineColor = ColorUtil.ARGB.multiplyAlpha(VGuiConstants.COLOR_RED, 0.8f); - int selectionFillColor = ColorUtil.ARGB.multiplyAlpha(VGuiConstants.COLOR_RED, 0.2f); - GuiRenderer.fill(this.x, this.y, this.x + this.width, this.y + this.height, backgroundColor); - if (this.selected || this.focused) { - GuiRenderer.renderBorder(x, y, x + width, y + height, 1, selectionOutlineColor); - GuiRenderer.fill(this.x, this.y, this.x + this.width, this.y + this.height, selectionFillColor); + if (isFocused && cursorPos != selectionEnd) { + int start = Math.min(cursorPos, selectionEnd); + int end = Math.max(cursorPos, selectionEnd); + String before = text.substring(0, start); + String selected = text.substring(start, end); + + int xBefore = this.x + 8 + Minecraft.getInstance().font.width(before); + int xSelected = Minecraft.getInstance().font.width(selected); + + int selColor = ColorUtil.ARGB.multiplyAlpha(VGuiConstants.COLOR_RED, 0.55f); + GuiRenderer.fill(xBefore, this.y + 4, xBefore + xSelected, this.y + this.height - 4, selColor); } Component displayText = hasText ? Component.literal(this.text) : this.placeholder; + GuiRenderer.drawString(Minecraft.getInstance().font, displayText, + this.x + 8, this.y + (this.height - 8) / 2, textColor | 0xFF000000); - GuiRenderer.drawString( - Minecraft.getInstance().font, - displayText, - this.x + 8, - this.y + (this.height - 8) / 2, - textColor | 0xFF000000 - ); + if (isFocused && showCursor) { + String beforeCursor = text.substring(0, cursorPos); + int cursorX = this.x + 8 + Minecraft.getInstance().font.width(beforeCursor); + + GuiRenderer.fill(cursorX, this.y + 6, cursorX + 1, this.y + this.height - 6, + VGuiConstants.COLOR_WHITE); + } + + if (isFocused) { + int borderColor = ColorUtil.ARGB.multiplyAlpha(VGuiConstants.COLOR_RED, 0.8f); + GuiRenderer.renderBorder(this.x, this.y, this.x + this.width, this.y + this.height, 1, borderColor); + } + + if (isFocused) { + long time = Util.getMillis(); + if (time - lastBlinkTime > CURSOR_BLINK_INTERVAL) { + showCursor = !showCursor; + lastBlinkTime = time; + } + } else { + showCursor = true; + } } @Override public boolean keyPressed(KeyEvent keyEvent) { if (!this.focused && !this.selected) return false; + boolean shift = keyEvent.hasShiftDown(); + boolean ctrl = keyEvent.hasControlDown(); + if (keyEvent.key() == GLFW.GLFW_KEY_ENTER || keyEvent.key() == GLFW.GLFW_KEY_KP_ENTER) { this.onSearch.accept(this); return true; } + if (cursorPos != selectionEnd) { + int start = Math.min(cursorPos, selectionEnd); + int end = Math.max(cursorPos, selectionEnd); + + if (keyEvent.key() == GLFW.GLFW_KEY_BACKSPACE || keyEvent.key() == GLFW.GLFW_KEY_DELETE) { + this.text = text.substring(0, start) + text.substring(end); + cursorPos = start; + selectionEnd = start; + this.onSearch.accept(this); + return true; + } + } + if (keyEvent.key() == GLFW.GLFW_KEY_BACKSPACE) { - if (!this.text.isEmpty()) { - this.text = this.text.substring(0, this.text.length() - 1); - this.onSearch.accept(this); // live search + if (cursorPos > 0) { + this.text = text.substring(0, cursorPos - 1) + text.substring(cursorPos); + cursorPos--; + selectionEnd = cursorPos; + this.onSearch.accept(this); + } + return true; + } + + if (keyEvent.key() == GLFW.GLFW_KEY_DELETE) { + if (cursorPos < text.length()) { + this.text = text.substring(0, cursorPos) + text.substring(cursorPos + 1); + this.onSearch.accept(this); } return true; } + if (ctrl && keyEvent.key() == GLFW.GLFW_KEY_A) { + cursorPos = text.length(); + selectionEnd = 0; + return true; + } + + if (keyEvent.key() == GLFW.GLFW_KEY_LEFT) { + if (cursorPos > 0) cursorPos--; + if (!shift) selectionEnd = cursorPos; + return true; + } + if (keyEvent.key() == GLFW.GLFW_KEY_RIGHT) { + if (cursorPos < text.length()) cursorPos++; + if (!shift) selectionEnd = cursorPos; + return true; + } + String keyName = GLFW.glfwGetKeyName(keyEvent.key(), keyEvent.scancode()); if (keyName != null && keyName.length() == 1) { - if (keyEvent.hasShiftDown()) keyName = keyName.toUpperCase(); - this.text += keyName; + char c = keyEvent.hasShiftDown() ? keyName.toUpperCase().charAt(0) : keyName.charAt(0); + + if (cursorPos != selectionEnd) { + int start = Math.min(cursorPos, selectionEnd); + int end = Math.max(cursorPos, selectionEnd); + this.text = text.substring(0, start) + c + text.substring(end); + cursorPos = start + 1; + } else { + this.text = text.substring(0, cursorPos) + c + text.substring(cursorPos); + cursorPos++; + } + selectionEnd = cursorPos; this.onSearch.accept(this); return true; } @@ -131,6 +212,16 @@ public boolean mouseClicked(MouseButtonEvent event, boolean bl) { if (clicked) { this.setFocused(true); this.selected = true; + + int relX = (int) event.x() - (this.x + 8); + int pos = 0; + for (int i = 0; i < text.length(); i++) { + if (Minecraft.getInstance().font.width(text.substring(0, i + 1)) > relX) break; + pos = i + 1; + } + cursorPos = pos; + selectionEnd = pos; + return true; } else { this.setFocused(false); From 68c0198b379d6ae17af5803fda0972be4631284b Mon Sep 17 00:00:00 2001 From: NotNekodev <97458908+NotNekodev@users.noreply.github.com> Date: Mon, 1 Dec 2025 22:20:31 +0100 Subject: [PATCH 15/21] add a keybind for returning to the vanilla settings (Shift + P) and a keybind for focusing the search field (Ctrl + L) --- .../vulkanmod/config/gui/VOptionScreen.java | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/main/java/net/vulkanmod/config/gui/VOptionScreen.java b/src/main/java/net/vulkanmod/config/gui/VOptionScreen.java index cd8ec75c3..7481c2c57 100644 --- a/src/main/java/net/vulkanmod/config/gui/VOptionScreen.java +++ b/src/main/java/net/vulkanmod/config/gui/VOptionScreen.java @@ -6,6 +6,7 @@ import net.minecraft.client.gui.GuiGraphics; import net.minecraft.client.gui.components.events.GuiEventListener; import net.minecraft.client.gui.screens.Screen; +import net.minecraft.client.gui.screens.options.VideoSettingsScreen; import net.minecraft.client.input.KeyEvent; import net.minecraft.client.input.MouseButtonEvent; import net.minecraft.client.renderer.RenderPipelines; @@ -509,6 +510,14 @@ private void applyOptions() { @Override public boolean keyPressed(KeyEvent keyEvent) { + if (keyEvent.hasControlDown() && keyEvent.key() == GLFW.GLFW_KEY_L) { + this.setFocused(searchField); + searchField.setFocused(true); + searchField.setSelected(true); + + return true; + } + if (keyEvent.key() == GLFW.GLFW_KEY_ESCAPE && this.isSearchActive) { this.isSearchActive = false; this.searchField.setInput(""); @@ -518,6 +527,15 @@ public boolean keyPressed(KeyEvent keyEvent) { return true; } + + if (!this.isSearchActive + && keyEvent.key() == GLFW.GLFW_KEY_P + && keyEvent.hasShiftDown()) { + Minecraft.getInstance().setScreen(new VideoSettingsScreen(this, Minecraft.getInstance(), Minecraft.getInstance().options)); + + return true; + } + return super.keyPressed(keyEvent); } } \ No newline at end of file From a50e6c76cd07657d864452edc84ab18e622e9b35 Mon Sep 17 00:00:00 2001 From: NotNekodev <97458908+NotNekodev@users.noreply.github.com> Date: Tue, 2 Dec 2025 18:34:29 +0100 Subject: [PATCH 16/21] fix a bug, where you cant change to the vanilla screen when there is something in the text box --- src/main/java/net/vulkanmod/config/gui/VOptionScreen.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/net/vulkanmod/config/gui/VOptionScreen.java b/src/main/java/net/vulkanmod/config/gui/VOptionScreen.java index 7481c2c57..1968b9c67 100644 --- a/src/main/java/net/vulkanmod/config/gui/VOptionScreen.java +++ b/src/main/java/net/vulkanmod/config/gui/VOptionScreen.java @@ -528,12 +528,12 @@ public boolean keyPressed(KeyEvent keyEvent) { } - if (!this.isSearchActive + if (!this.searchField.focused && keyEvent.key() == GLFW.GLFW_KEY_P && keyEvent.hasShiftDown()) { Minecraft.getInstance().setScreen(new VideoSettingsScreen(this, Minecraft.getInstance(), Minecraft.getInstance().options)); - return true; + return false; } return super.keyPressed(keyEvent); From b7436efd4614a172d66940764cdcd57ad78845cc Mon Sep 17 00:00:00 2001 From: NotNekodev <97458908+NotNekodev@users.noreply.github.com> Date: Tue, 2 Dec 2025 18:46:20 +0100 Subject: [PATCH 17/21] add fovEffectScale option --- src/main/java/net/vulkanmod/config/option/Options.java | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/main/java/net/vulkanmod/config/option/Options.java b/src/main/java/net/vulkanmod/config/option/Options.java index 55761e8f2..37b0d155b 100644 --- a/src/main/java/net/vulkanmod/config/option/Options.java +++ b/src/main/java/net/vulkanmod/config/option/Options.java @@ -2,6 +2,7 @@ import com.mojang.blaze3d.platform.Window; import net.minecraft.client.*; +import net.minecraft.network.chat.CommonComponents; import net.minecraft.network.chat.Component; import net.minecraft.server.level.ParticleStatus; import net.vulkanmod.Initializer; @@ -142,6 +143,11 @@ public static OptionBlock[] getVideoOpts() { new SwitchOption(Component.translatable("options.viewBobbing"), (value) -> minecraftOptions.bobView().set(value), () -> minecraftOptions.bobView().get()), + new RangeOption(Component.translatable("options.fovEffectScale"), + 0, 100, 1, + (value) -> minecraftOptions.fovEffectScale().set(value / 100d), + () -> (int) (minecraftOptions.fovEffectScale().get() * 100)) + .setTooltip(value -> Component.translatable("options.fovEffectScale.tooltip")), new CyclingOption<>(Component.translatable("options.attackIndicator"), AttackIndicatorStatus.values(), value -> minecraftOptions.attackIndicator().set(value), @@ -246,7 +252,9 @@ public static OptionBlock[] getGraphicsOpts() { }, () -> minecraftOptions.mipmapLevels().get()) .setTranslator(value -> Component.nullToEmpty(value.toString())) - }) + })/*, + + */ }; } From cb4e1df40727e59811648bd3760e2125eb493775 Mon Sep 17 00:00:00 2001 From: NotNekodev <97458908+NotNekodev@users.noreply.github.com> Date: Tue, 2 Dec 2025 18:53:08 +0100 Subject: [PATCH 18/21] add glint options (glintSpeed and glintStrength) + change vulkanmod.options.refreshRate to "Fullscreen Refresh Rate", because it only applies in fullscreen --- src/main/java/net/vulkanmod/config/option/Options.java | 10 ++++++++++ src/main/resources/assets/vulkanmod/lang/en_us.json | 2 +- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/main/java/net/vulkanmod/config/option/Options.java b/src/main/java/net/vulkanmod/config/option/Options.java index 37b0d155b..0ec1951f5 100644 --- a/src/main/java/net/vulkanmod/config/option/Options.java +++ b/src/main/java/net/vulkanmod/config/option/Options.java @@ -148,6 +148,16 @@ public static OptionBlock[] getVideoOpts() { (value) -> minecraftOptions.fovEffectScale().set(value / 100d), () -> (int) (minecraftOptions.fovEffectScale().get() * 100)) .setTooltip(value -> Component.translatable("options.fovEffectScale.tooltip")), + new RangeOption(Component.translatable("options.glintSpeed"), + 0, 100, 1, + (value) -> minecraftOptions.glintSpeed().set(value / 100d), + () -> (int) (minecraftOptions.glintSpeed().get() * 100)) + .setTooltip(value -> Component.translatable("options.glintSpeed.tooltip")), + new RangeOption(Component.translatable("options.glintStrength"), + 0, 100, 1, + (value) -> minecraftOptions.glintStrength().set(value / 100d), + () -> (int) (minecraftOptions.glintStrength().get() * 100)) + .setTooltip(value -> Component.translatable("options.glintStrength.tooltip")), new CyclingOption<>(Component.translatable("options.attackIndicator"), AttackIndicatorStatus.values(), value -> minecraftOptions.attackIndicator().set(value), diff --git a/src/main/resources/assets/vulkanmod/lang/en_us.json b/src/main/resources/assets/vulkanmod/lang/en_us.json index 310967ad4..241746fcf 100644 --- a/src/main/resources/assets/vulkanmod/lang/en_us.json +++ b/src/main/resources/assets/vulkanmod/lang/en_us.json @@ -35,7 +35,7 @@ "vulkanmod.options.indirectDraw": "Indirect Draw", "vulkanmod.options.indirectDraw.tooltip": "Reduces CPU overhead but might increases GPU overhead.", - "vulkanmod.options.refreshRate": "Refresh Rate", + "vulkanmod.options.refreshRate": "Fullscreen Refresh Rate", "vulkanmod.options.uniqueOpaqueLayer": "Unique opaque layer", "vulkanmod.options.uniqueOpaqueLayer.tooltip": "Use a unique render layer for opaque terrain to improve performance.", From f4fc07c49dd3d845d132424cced419788f3fd09a Mon Sep 17 00:00:00 2001 From: NotNekodev <97458908+NotNekodev@users.noreply.github.com> Date: Tue, 2 Dec 2025 19:27:07 +0100 Subject: [PATCH 19/21] WindowMode, VideoMode and all that has been rewritten and Config has also changed --- src/main/java/net/vulkanmod/Initializer.java | 9 +- .../java/net/vulkanmod/config/Config.java | 138 +++-- .../vulkanmod/config/gui/VOptionScreen.java | 2 +- .../net/vulkanmod/config/option/Options.java | 472 +++++++++--------- .../net/vulkanmod/config/video/VideoMode.java | 15 + .../config/video/VideoModeManager.java | 123 +++-- .../vulkanmod/config/video/VideoModeSet.java | 96 +--- .../vulkanmod/config/video/WindowMode.java | 48 +- .../vulkanmod/mixin/window/WindowMixin.java | 23 +- 9 files changed, 459 insertions(+), 467 deletions(-) create mode 100644 src/main/java/net/vulkanmod/config/video/VideoMode.java diff --git a/src/main/java/net/vulkanmod/Initializer.java b/src/main/java/net/vulkanmod/Initializer.java index 198d7153a..6a116e5a3 100644 --- a/src/main/java/net/vulkanmod/Initializer.java +++ b/src/main/java/net/vulkanmod/Initializer.java @@ -42,14 +42,7 @@ public void onInitializeClient() { } private static Config loadConfig(Path path) { - Config config = Config.load(path); - - if(config == null) { - config = new Config(); - config.write(); - } - - return config; + return Config.load(path); } public static String getVersion() { diff --git a/src/main/java/net/vulkanmod/config/Config.java b/src/main/java/net/vulkanmod/config/Config.java index e2bebdb7f..821266000 100644 --- a/src/main/java/net/vulkanmod/config/Config.java +++ b/src/main/java/net/vulkanmod/config/Config.java @@ -1,75 +1,143 @@ package net.vulkanmod.config; -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; +import com.google.gson.*; +import com.google.gson.annotations.JsonAdapter; +import net.vulkanmod.config.video.VideoMode; import net.vulkanmod.config.video.VideoModeManager; -import net.vulkanmod.config.video.VideoModeSet; -import java.io.FileReader; import java.io.IOException; import java.lang.reflect.Modifier; import java.nio.file.Files; import java.nio.file.Path; -import java.util.Collections; +import java.util.Objects; +@JsonAdapter(Config.GsonAdapter.class) public class Config { - public VideoModeSet.VideoMode videoMode = VideoModeManager.getFirstAvailable().getVideoMode(); + + public VideoMode videoMode; public int windowMode = 0; public int advCulling = 2; public boolean indirectDraw = true; - public boolean uniqueOpaqueLayer = true; public boolean entityCulling = true; - public int device = -1; public int ambientOcclusion = 1; public int frameQueueSize = 2; public int builderThreads = 0; - public boolean backFaceCulling = true; public boolean textureAnimations = true; - public void write() { + public int device = -1; - if(!Files.exists(CONFIG_PATH.getParent())) { - try { - Files.createDirectories(CONFIG_PATH); - } catch (IOException e) { - e.printStackTrace(); - } - } + private static Path CONFIG_PATH; + private static final Gson GSON = new GsonBuilder() + .setPrettyPrinting() + .registerTypeAdapter(Config.class, new GsonAdapter()) + .create(); + public void save() { try { - Files.write(CONFIG_PATH, Collections.singleton(GSON.toJson(this))); + Files.createDirectories(CONFIG_PATH.getParent()); + Files.writeString(CONFIG_PATH, GSON.toJson(this)); } catch (IOException e) { e.printStackTrace(); } } - private static Path CONFIG_PATH; - - private static final Gson GSON = new GsonBuilder() - .setPrettyPrinting() - .excludeFieldsWithModifiers(Modifier.PRIVATE) - .create(); - public static Config load(Path path) { - Config config; - Config.CONFIG_PATH = path; + CONFIG_PATH = path; if (Files.exists(path)) { - try (FileReader fileReader = new FileReader(path.toFile())) { - config = GSON.fromJson(fileReader, Config.class); + try { + String content = Files.readString(path); + Config config = GSON.fromJson(content, Config.class); + + if (config.videoMode == null || + VideoModeManager.findSetFor(config.videoMode) == null) { + config.videoMode = VideoModeManager.currentOsMode(); + } + + return config; + } catch (IOException | JsonSyntaxException e) { + System.err.println("Failed to load config, using defaults: " + e.getMessage()); } - catch (IOException exception) { - throw new RuntimeException(exception.getMessage()); + } + + Config config = new Config(); + config.videoMode = VideoModeManager.currentOsMode(); + return config; + } + + public static class GsonAdapter implements JsonSerializer, JsonDeserializer { + + @Override + public JsonElement serialize(Config src, java.lang.reflect.Type typeOfSrc, JsonSerializationContext context) { + JsonObject obj = new JsonObject(); + + if (src.videoMode != null) { + JsonObject vm = new JsonObject(); + vm.addProperty("width", src.videoMode.width()); + vm.addProperty("height", src.videoMode.height()); + vm.addProperty("bitDepth", src.videoMode.bitDepth()); + vm.addProperty("refreshRate", src.videoMode.refreshRate()); + obj.add("videoMode", vm); } + + obj.addProperty("windowMode", src.windowMode); + obj.addProperty("advCulling", src.advCulling); + obj.addProperty("indirectDraw", src.indirectDraw); + obj.addProperty("uniqueOpaqueLayer", src.uniqueOpaqueLayer); + obj.addProperty("entityCulling", src.entityCulling); + obj.addProperty("ambientOcclusion", src.ambientOcclusion); + obj.addProperty("frameQueueSize", src.frameQueueSize); + obj.addProperty("builderThreads", src.builderThreads); + obj.addProperty("backFaceCulling", src.backFaceCulling); + obj.addProperty("textureAnimations", src.textureAnimations); + obj.addProperty("device", src.device); + + return obj; } - else { - config = null; + + @Override + public Config deserialize(JsonElement json, java.lang.reflect.Type typeOfT, JsonDeserializationContext context) throws JsonParseException { + Config config = new Config(); + JsonObject obj = json.getAsJsonObject(); + + if (obj.has("videoMode")) { + JsonObject vm = obj.getAsJsonObject("videoMode"); + int w = getInt(vm, "width", 1920); + int h = getInt(vm, "height", 1080); + int bd = getInt(vm, "bitDepth", 8); + int rr = getInt(vm, "refreshRate", 60); + config.videoMode = new VideoMode(w, h, bd, rr); + } else { + config.videoMode = VideoModeManager.currentOsMode(); + } + + config.windowMode = getInt(obj, "windowMode", 0); + config.advCulling = getInt(obj, "advCulling", 2); + config.indirectDraw = getBoolean(obj, "indirectDraw", true); + config.uniqueOpaqueLayer = getBoolean(obj, "uniqueOpaqueLayer", true); + config.entityCulling = getBoolean(obj, "entityCulling", true); + config.ambientOcclusion = getInt(obj, "ambientOcclusion", 1); + config.frameQueueSize = getInt(obj, "frameQueueSize", 2); + config.builderThreads = getInt(obj, "builderThreads", 0); + config.backFaceCulling = getBoolean(obj, "backFaceCulling", true); + config.textureAnimations = getBoolean(obj, "textureAnimations", true); + config.device = getInt(obj, "device", -1); + + return config; } - return config; + private int getInt(JsonObject obj, String key, int def) { + JsonElement el = obj.get(key); + return el != null && el.isJsonPrimitive() ? el.getAsInt() : def; + } + + private boolean getBoolean(JsonObject obj, String key, boolean def) { + JsonElement el = obj.get(key); + return el != null && el.isJsonPrimitive() ? el.getAsBoolean() : def; + } } -} +} \ No newline at end of file diff --git a/src/main/java/net/vulkanmod/config/gui/VOptionScreen.java b/src/main/java/net/vulkanmod/config/gui/VOptionScreen.java index 1968b9c67..ea307103c 100644 --- a/src/main/java/net/vulkanmod/config/gui/VOptionScreen.java +++ b/src/main/java/net/vulkanmod/config/gui/VOptionScreen.java @@ -505,7 +505,7 @@ private void applyOptions() { this.captureOriginalState(); - Initializer.CONFIG.write(); + Initializer.CONFIG.save(); } @Override diff --git a/src/main/java/net/vulkanmod/config/option/Options.java b/src/main/java/net/vulkanmod/config/option/Options.java index 0ec1951f5..7749f775e 100644 --- a/src/main/java/net/vulkanmod/config/option/Options.java +++ b/src/main/java/net/vulkanmod/config/option/Options.java @@ -2,15 +2,12 @@ import com.mojang.blaze3d.platform.Window; import net.minecraft.client.*; -import net.minecraft.network.chat.CommonComponents; import net.minecraft.network.chat.Component; import net.minecraft.server.level.ParticleStatus; import net.vulkanmod.Initializer; import net.vulkanmod.config.Config; -import net.vulkanmod.config.gui.OptionBlock; -import net.vulkanmod.config.video.VideoModeManager; -import net.vulkanmod.config.video.VideoModeSet; -import net.vulkanmod.config.video.WindowMode; +import net.vulkanmod.config.gui.*; +import net.vulkanmod.config.video.*; import net.vulkanmod.render.chunk.WorldRenderer; import net.vulkanmod.render.chunk.build.light.LightMode; import net.vulkanmod.render.vertex.TerrainRenderType; @@ -20,152 +17,155 @@ import java.util.stream.IntStream; public abstract class Options { + public static boolean fullscreenDirty = false; - static Config config = Initializer.CONFIG; - static Minecraft minecraft = Minecraft.getInstance(); - static Window window = minecraft.getWindow(); - static net.minecraft.client.Options minecraftOptions = minecraft.options; - public static OptionBlock[] getVideoOpts() { - var videoMode = config.videoMode; - var videoModeSet = VideoModeManager.getFromVideoMode(videoMode); + private static final Config config = Initializer.CONFIG; + private static final Minecraft minecraft = Minecraft.getInstance(); + private static final Window window = minecraft.getWindow(); + private static final net.minecraft.client.Options mcOptions = minecraft.options; - if (videoModeSet == null) { - videoModeSet = VideoModeSet.getDummy(); - videoMode = videoModeSet.getVideoMode(-1); - } + public static OptionBlock[] getVideoOpts() { + VideoMode currentMode = config.videoMode; + VideoModeSet currentSet = VideoModeManager.findSetFor(currentMode); + VideoModeSet[] resolutions = VideoModeManager.availableSets().toArray(VideoModeSet[]::new); - VideoModeManager.selectedVideoMode = videoMode; - var refreshRates = videoModeSet.getRefreshRates(); + CyclingOption resolutionOption = (CyclingOption) new CyclingOption<>( + Component.translatable("options.fullscreen.resolution"), + resolutions, + set -> { + int targetRate = currentSet.supportsRate(currentMode.refreshRate()) + ? currentMode.refreshRate() + : set.refreshRates().last(); - CyclingOption RefreshRate = (CyclingOption) new CyclingOption<>( - Component.translatable("vulkanmod.options.refreshRate"), - refreshRates.toArray(new Integer[0]), - (value) -> { - VideoModeManager.selectedVideoMode.refreshRate = value; - VideoModeManager.applySelectedVideoMode(); + VideoMode newMode = set.modeAtRate(targetRate); + config.videoMode = newMode; + VideoModeManager.selectMode(newMode); - if (minecraftOptions.fullscreen().get()) + if (mcOptions.fullscreen().get()) { fullscreenDirty = true; + } }, - () -> VideoModeManager.selectedVideoMode.refreshRate) - .setTranslator(refreshRate -> Component.nullToEmpty(refreshRate.toString())); + () -> currentSet + ).setTranslator(set -> Component.literal(set.toString())); - Option resolutionOption = new CyclingOption<>( - Component.translatable("options.fullscreen.resolution"), - VideoModeManager.getVideoResolutions(), - (value) -> { - VideoModeManager.selectedVideoMode = value.getVideoMode(RefreshRate.getNewValue()); - VideoModeManager.applySelectedVideoMode(); + CyclingOption refreshRateOption = (CyclingOption) new CyclingOption<>( + Component.translatable("vulkanmod.options.refreshRate"), + currentSet.refreshRates().toArray(Integer[]::new), + rate -> { + VideoMode newMode = currentMode.withRefreshRate(rate); + config.videoMode = newMode; + VideoModeManager.selectMode(newMode); - if (minecraftOptions.fullscreen().get()) + if (mcOptions.fullscreen().get()) { fullscreenDirty = true; + } }, - () -> { - var selectedVideoMode = VideoModeManager.selectedVideoMode; - var selectedVideoModeSet = VideoModeManager.getFromVideoMode(selectedVideoMode); - - return selectedVideoModeSet != null ? selectedVideoModeSet : VideoModeSet.getDummy(); - }) - .setTranslator(resolution -> Component.nullToEmpty(resolution.toString())); + currentMode::refreshRate + ).setTranslator(rate -> Component.literal(rate + " Hz")); resolutionOption.setOnChange(() -> { - var newVideoMode = resolutionOption.getNewValue(); - var newRefreshRates = newVideoMode.getRefreshRates().toArray(new Integer[0]); - - RefreshRate.setValues(newRefreshRates); - RefreshRate.setNewValue(newRefreshRates[newRefreshRates.length - 1]); + VideoModeSet newSet = resolutionOption.getNewValue(); + Integer[] rates = newSet.refreshRates().toArray(new Integer[0]); + refreshRateOption.setValues(rates); + refreshRateOption.setNewValue(rates[rates.length - 1]); }); + CyclingOption windowModeOption = (CyclingOption) new CyclingOption( + Component.translatable("vulkanmod.options.windowMode"), + WindowMode.VALUES, + mode -> { + config.windowMode = switch (mode) { + case WindowMode.Windowed() -> 0; + case WindowMode.WindowedFullscreen() -> 1; + case WindowMode.ExclusiveFullscreen() -> 2; + }; + + boolean exclusiveFullscreen = mode instanceof WindowMode.ExclusiveFullscreen; + mcOptions.fullscreen().set(exclusiveFullscreen); + fullscreenDirty = true; + }, + () -> switch (config.windowMode) { + case 1 -> new WindowMode.WindowedFullscreen(); + case 2 -> new WindowMode.ExclusiveFullscreen(); + default -> new WindowMode.Windowed(); + } + ).setTranslator(WindowMode::nameOf); + return new OptionBlock[]{ new OptionBlock("", new Option[]{ resolutionOption, - RefreshRate, - new CyclingOption<>(Component.translatable("vulkanmod.options.windowMode"), - WindowMode.values(), + refreshRateOption, + windowModeOption, + new RangeOption(Component.translatable("options.framerateLimit"), + 10, 260, 10, + value -> Component.nullToEmpty(value == 260 + ? Component.translatable("options.framerateLimit.max").getString() + : String.valueOf(value)), value -> { - boolean exclusiveFullscreen = value == WindowMode.EXCLUSIVE_FULLSCREEN; - minecraftOptions.fullscreen() - .set(exclusiveFullscreen); - - config.windowMode = value.mode; - fullscreenDirty = true; + mcOptions.framerateLimit().set(value); + minecraft.getFramerateLimitTracker().setFramerateLimit(value); }, - () -> WindowMode.fromValue(config.windowMode)) - .setTranslator(value -> Component.translatable(WindowMode.getComponentName(value))), - new RangeOption(Component.translatable("options.framerateLimit"), - 10, 260, 10, - value -> Component.nullToEmpty(value == 260 ? - Component.translatable( - "options.framerateLimit.max") - .getString() : - String.valueOf(value)), - value -> { - minecraftOptions.framerateLimit().set(value); - minecraft.getFramerateLimitTracker().setFramerateLimit(value); - }, - () -> minecraftOptions.framerateLimit().get()), + () -> mcOptions.framerateLimit().get()), new SwitchOption(Component.translatable("options.vsync"), - value -> { - minecraftOptions.enableVsync().set(value); - window.updateVsync(value); - }, - () -> minecraftOptions.enableVsync().get()), + value -> { + mcOptions.enableVsync().set(value); + window.updateVsync(value); + }, + () -> mcOptions.enableVsync().get()), new CyclingOption<>(Component.translatable("options.inactivityFpsLimit"), - InactivityFpsLimit.values(), - value -> minecraftOptions.inactivityFpsLimit().set(value), - () -> minecraftOptions.inactivityFpsLimit().get()) - .setTranslator(inactivityFpsLimit -> Component.translatable(inactivityFpsLimit.getKey())) + InactivityFpsLimit.values(), + value -> mcOptions.inactivityFpsLimit().set(value), + () -> mcOptions.inactivityFpsLimit().get()) + .setTranslator(v -> Component.translatable(v.getKey())) }), new OptionBlock("", new Option[]{ new RangeOption(Component.translatable("options.guiScale"), - 0, window.calculateScale(0, minecraft.isEnforceUnicode()), 1, - value -> Component.translatable((value == 0) - ? "options.guiScale.auto" - : String.valueOf(value)), - value -> { - minecraftOptions.guiScale().set(value); - minecraft.resizeDisplay(); - }, - () -> (minecraftOptions.guiScale().get())), + 0, window.calculateScale(0, minecraft.isEnforceUnicode()), 1, + value -> Component.translatable(value == 0 ? "options.guiScale.auto" : String.valueOf(value)), + value -> { + mcOptions.guiScale().set(value); + minecraft.resizeDisplay(); + }, + () -> mcOptions.guiScale().get()), new RangeOption(Component.translatable("options.gamma"), - 0, 100, 1, - value -> Component.translatable(switch (value) { - case 0 -> "options.gamma.min"; - case 50 -> "options.gamma.default"; - case 100 -> "options.gamma.max"; - default -> String.valueOf(value); - }), - value -> minecraftOptions.gamma().set(value * 0.01), - () -> (int) (minecraftOptions.gamma().get() * 100.0)), + 0, 100, 1, + value -> Component.translatable(switch (value) { + case 0 -> "options.gamma.min"; + case 50 -> "options.gamma.default"; + case 100 -> "options.gamma.max"; + default -> String.valueOf(value); + }), + value -> mcOptions.gamma().set(value * 0.01), + () -> (int) (mcOptions.gamma().get() * 100.0)) }), new OptionBlock("", new Option[]{ new SwitchOption(Component.translatable("options.viewBobbing"), - (value) -> minecraftOptions.bobView().set(value), - () -> minecraftOptions.bobView().get()), + value -> mcOptions.bobView().set(value), + () -> mcOptions.bobView().get()), new RangeOption(Component.translatable("options.fovEffectScale"), - 0, 100, 1, - (value) -> minecraftOptions.fovEffectScale().set(value / 100d), - () -> (int) (minecraftOptions.fovEffectScale().get() * 100)) + 0, 100, 1, + value -> mcOptions.fovEffectScale().set(value / 100.0), + () -> (int) (mcOptions.fovEffectScale().get() * 100)) .setTooltip(value -> Component.translatable("options.fovEffectScale.tooltip")), new RangeOption(Component.translatable("options.glintSpeed"), 0, 100, 1, - (value) -> minecraftOptions.glintSpeed().set(value / 100d), - () -> (int) (minecraftOptions.glintSpeed().get() * 100)) + value -> mcOptions.glintSpeed().set(value / 100.0), + () -> (int) (mcOptions.glintSpeed().get() * 100)) .setTooltip(value -> Component.translatable("options.glintSpeed.tooltip")), new RangeOption(Component.translatable("options.glintStrength"), 0, 100, 1, - (value) -> minecraftOptions.glintStrength().set(value / 100d), - () -> (int) (minecraftOptions.glintStrength().get() * 100)) + value -> mcOptions.glintStrength().set(value / 100.0), + () -> (int) (mcOptions.glintStrength().get() * 100)) .setTooltip(value -> Component.translatable("options.glintStrength.tooltip")), new CyclingOption<>(Component.translatable("options.attackIndicator"), - AttackIndicatorStatus.values(), - value -> minecraftOptions.attackIndicator().set(value), - () -> minecraftOptions.attackIndicator().get()) - .setTranslator(value -> Component.translatable(value.getKey())), + AttackIndicatorStatus.values(), + value -> mcOptions.attackIndicator().set(value), + () -> mcOptions.attackIndicator().get()) + .setTranslator(v -> Component.translatable(v.getKey())), new SwitchOption(Component.translatable("options.autosaveIndicator"), - value -> minecraftOptions.showAutosaveIndicator().set(value), - () -> minecraftOptions.showAutosaveIndicator().get()), + value -> mcOptions.showAutosaveIndicator().set(value), + () -> mcOptions.showAutosaveIndicator().get()) }) }; } @@ -174,191 +174,167 @@ public static OptionBlock[] getGraphicsOpts() { return new OptionBlock[]{ new OptionBlock("", new Option[]{ new RangeOption(Component.translatable("options.renderDistance"), - 2, 32, 1, - (value) -> minecraftOptions.renderDistance().set(value), - () -> minecraftOptions.renderDistance().get()), + 2, 32, 1, + value -> mcOptions.renderDistance().set(value), + () -> mcOptions.renderDistance().get()), new RangeOption(Component.translatable("options.simulationDistance"), - 5, 32, 1, - (value) -> minecraftOptions.simulationDistance().set(value), - () -> minecraftOptions.simulationDistance().get()), + 5, 32, 1, + value -> mcOptions.simulationDistance().set(value), + () -> mcOptions.simulationDistance().get()), new CyclingOption<>(Component.translatable("options.prioritizeChunkUpdates"), - PrioritizeChunkUpdates.values(), - value -> minecraftOptions.prioritizeChunkUpdates().set(value), - () -> minecraftOptions.prioritizeChunkUpdates().get()) - .setTranslator(value -> Component.translatable(value.getKey())), + PrioritizeChunkUpdates.values(), + value -> mcOptions.prioritizeChunkUpdates().set(value), + () -> mcOptions.prioritizeChunkUpdates().get()) + .setTranslator(v -> Component.translatable(v.getKey())) }), new OptionBlock("", new Option[]{ new CyclingOption<>(Component.translatable("options.graphics"), - new GraphicsStatus[]{GraphicsStatus.FAST, GraphicsStatus.FANCY}, - value -> minecraftOptions.graphicsMode().set(value), - () -> minecraftOptions.graphicsMode().get()) - .setTranslator(graphicsMode -> Component.translatable(graphicsMode.getKey())), + new GraphicsStatus[]{GraphicsStatus.FAST, GraphicsStatus.FANCY}, + value -> mcOptions.graphicsMode().set(value), + () -> mcOptions.graphicsMode().get()) + .setTranslator(g -> Component.translatable(g.getKey())), new CyclingOption<>(Component.translatable("options.particles"), - new ParticleStatus[]{ParticleStatus.MINIMAL, ParticleStatus.DECREASED, ParticleStatus.ALL}, - value -> minecraftOptions.particles().set(value), - () -> minecraftOptions.particles().get()) - .setTranslator(particlesMode -> Component.translatable(particlesMode.getKey())), + new ParticleStatus[]{ParticleStatus.MINIMAL, ParticleStatus.DECREASED, ParticleStatus.ALL}, + value -> mcOptions.particles().set(value), + () -> mcOptions.particles().get()) + .setTranslator(p -> Component.translatable(p.getKey())), new CyclingOption<>(Component.translatable("options.renderClouds"), - CloudStatus.values(), - value -> minecraftOptions.cloudStatus().set(value), - () -> minecraftOptions.cloudStatus().get()) - .setTranslator(value -> Component.translatable(value.getKey())), + CloudStatus.values(), + value -> mcOptions.cloudStatus().set(value), + () -> mcOptions.cloudStatus().get()) + .setTranslator(c -> Component.translatable(c.getKey())), new RangeOption(Component.translatable("options.renderCloudsDistance"), - 2, 128, 1, - (value) -> minecraftOptions.cloudRange().set(value), - () -> minecraftOptions.cloudRange().get()), + 2, 128, 1, + value -> mcOptions.cloudRange().set(value), + () -> mcOptions.cloudRange().get()), new CyclingOption<>(Component.translatable("options.ao"), - new Integer[]{LightMode.FLAT, LightMode.SMOOTH, LightMode.SUB_BLOCK}, - (value) -> { - if (value > LightMode.FLAT) - minecraftOptions.ambientOcclusion().set(true); - else - minecraftOptions.ambientOcclusion().set(false); - - config.ambientOcclusion = value; - - minecraft.levelRenderer.allChanged(); - }, - () -> config.ambientOcclusion) + new Integer[]{LightMode.FLAT, LightMode.SMOOTH, LightMode.SUB_BLOCK}, + value -> { + mcOptions.ambientOcclusion().set(value > LightMode.FLAT); + config.ambientOcclusion = value; + minecraft.levelRenderer.allChanged(); + }, + () -> config.ambientOcclusion) .setTranslator(value -> Component.translatable(switch (value) { case LightMode.FLAT -> "options.off"; case LightMode.SMOOTH -> "options.on"; case LightMode.SUB_BLOCK -> "vulkanmod.options.ao.subBlock"; default -> "vulkanmod.options.unknown"; })) - .setTooltip(value -> switch (value) { - case LightMode.FLAT -> Component.empty(); - case LightMode.SMOOTH -> Component.empty(); - case LightMode.SUB_BLOCK -> Component.translatable("vulkanmod.options.ao.subBlock.tooltip"); - default -> Component.empty(); - } - ), + .setTooltip(value -> value == LightMode.SUB_BLOCK + ? Component.translatable("vulkanmod.options.ao.subBlock.tooltip") + : Component.empty()), new RangeOption(Component.translatable("options.biomeBlendRadius"), - 0, 7, 1, - value -> { - int v = value * 2 + 1; - return Component.nullToEmpty("%d x %d".formatted(v, v)); - }, - (value) -> { - minecraftOptions.biomeBlendRadius().set(value); - minecraft.levelRenderer.allChanged(); - }, - () -> minecraftOptions.biomeBlendRadius().get()), + 0, 7, 1, + value -> Component.nullToEmpty("%d x %d".formatted(value * 2 + 1, value * 2 + 1)), + value -> { + mcOptions.biomeBlendRadius().set(value); + minecraft.levelRenderer.allChanged(); + }, + () -> mcOptions.biomeBlendRadius().get()) }), new OptionBlock("", new Option[]{ new SwitchOption(Component.translatable("options.entityShadows"), - value -> minecraftOptions.entityShadows().set(value), - () -> minecraftOptions.entityShadows().get()), + value -> mcOptions.entityShadows().set(value), + () -> mcOptions.entityShadows().get()), new RangeOption(Component.translatable("options.entityDistanceScaling"), - 50, 500, 25, - value -> minecraftOptions.entityDistanceScaling().set(value * 0.01), - () -> minecraftOptions.entityDistanceScaling().get().intValue() * 100), + 50, 500, 25, + value -> mcOptions.entityDistanceScaling().set(value * 0.01), + () -> (int)(mcOptions.entityDistanceScaling().get() * 100)), new CyclingOption<>(Component.translatable("options.mipmapLevels"), - new Integer[]{0, 1, 2, 3, 4}, - value -> { - minecraftOptions.mipmapLevels().set(value); - minecraft.updateMaxMipLevel(value); - minecraft.delayTextureReload(); - }, - () -> minecraftOptions.mipmapLevels().get()) - .setTranslator(value -> Component.nullToEmpty(value.toString())) - })/*, - - */ + new Integer[]{0,1,2,3,4}, + value -> { + mcOptions.mipmapLevels().set(value); + minecraft.updateMaxMipLevel(value); + minecraft.delayTextureReload(); + }, + () -> mcOptions.mipmapLevels().get()) + .setTranslator(v -> Component.literal(String.valueOf(v))) + }) }; } public static OptionBlock[] getOptimizationOpts() { return new OptionBlock[]{ - new OptionBlock("", new Option[]{ + new OptionBlock("", new Option[]{ new CyclingOption<>(Component.translatable("vulkanmod.options.advCulling"), - new Integer[]{1, 2, 3, 10}, - value -> config.advCulling = value, - () -> config.advCulling) - .setTranslator(value -> Component.translatable(switch (value) { + new Integer[]{1, 2, 3, 10}, + value -> config.advCulling = value, + () -> config.advCulling) + .setTranslator(v -> Component.translatable(switch (v) { case 1 -> "vulkanmod.options.advCulling.aggressive"; case 2 -> "vulkanmod.options.advCulling.normal"; case 3 -> "vulkanmod.options.advCulling.conservative"; case 10 -> "options.off"; default -> "vulkanmod.options.unknown"; })) - .setTooltip(value -> Component.translatable(switch (value) { - case 1, 2, 3 -> "vulkanmod.options.advCulling.tooltip"; - default -> ""; - })), + .setTooltip(v -> v <= 3 ? Component.translatable("vulkanmod.options.advCulling.tooltip") : Component.empty()), new SwitchOption(Component.translatable("vulkanmod.options.entityCulling"), - value -> config.entityCulling = value, - () -> config.entityCulling) - .setTooltip(value -> Component.translatable("vulkanmod.options.entityCulling.tooltip")), + v -> config.entityCulling = v, + () -> config.entityCulling) + .setTooltip(v -> Component.translatable("vulkanmod.options.entityCulling.tooltip")), new SwitchOption(Component.translatable("vulkanmod.options.uniqueOpaqueLayer"), - value -> { - config.uniqueOpaqueLayer = value; - TerrainRenderType.updateMapping(); - minecraft.levelRenderer.allChanged(); - }, - () -> config.uniqueOpaqueLayer) - .setTooltip(value -> Component.translatable("vulkanmod.options.uniqueOpaqueLayer.tooltip")), + v -> { + config.uniqueOpaqueLayer = v; + TerrainRenderType.updateMapping(); + minecraft.levelRenderer.allChanged(); + }, + () -> config.uniqueOpaqueLayer) + .setTooltip(v -> Component.translatable("vulkanmod.options.uniqueOpaqueLayer.tooltip")), new SwitchOption(Component.translatable("vulkanmod.options.backfaceCulling"), - value -> { - config.backFaceCulling = value; - Minecraft.getInstance().levelRenderer.allChanged(); - }, - () -> config.backFaceCulling) - .setTooltip(value -> Component.translatable("vulkanmod.options.backfaceCulling.tooltip")), + v -> { + config.backFaceCulling = v; + minecraft.levelRenderer.allChanged(); + }, + () -> config.backFaceCulling) + .setTooltip(v -> Component.translatable("vulkanmod.options.backfaceCulling.tooltip")), new SwitchOption(Component.translatable("vulkanmod.options.indirectDraw"), - value -> config.indirectDraw = value, - () -> config.indirectDraw) - .setTooltip(value -> Component.translatable("vulkanmod.options.indirectDraw.tooltip")), + v -> config.indirectDraw = v, + () -> config.indirectDraw) + .setTooltip(v -> Component.translatable("vulkanmod.options.indirectDraw.tooltip")) }) }; - } public static OptionBlock[] getOtherOpts() { return new OptionBlock[]{ - new OptionBlock("", new Option[]{ + new OptionBlock("", new Option[]{ new RangeOption(Component.translatable("vulkanmod.options.builderThreads"), - 0, (Runtime.getRuntime().availableProcessors() - 1), 1, - value -> { - config.builderThreads = value; - WorldRenderer.getInstance().getTaskDispatcher().createThreads(value); - }, - () -> config.builderThreads) - .setTranslator(value -> { - if (value == 0) - return Component.translatable("vulkanmod.options.builderThreads.auto"); - else - return Component.nullToEmpty(String.valueOf(value)); - }), + 0, Runtime.getRuntime().availableProcessors() - 1, 1, + value -> { + config.builderThreads = value; + WorldRenderer.getInstance().getTaskDispatcher().createThreads(value); + }, + () -> config.builderThreads) + .setTranslator(v -> v == 0 + ? Component.translatable("vulkanmod.options.builderThreads.auto") + : Component.literal(String.valueOf(v))), new RangeOption(Component.translatable("vulkanmod.options.frameQueue"), - 2, 5, 1, - value -> { - config.frameQueueSize = value; - Renderer.scheduleSwapChainUpdate(); - }, () -> config.frameQueueSize) - .setTooltip(value -> Component.translatable("vulkanmod.options.frameQueue.tooltip")), + 2, 5, 1, + value -> { + config.frameQueueSize = value; + Renderer.scheduleSwapChainUpdate(); + }, + () -> config.frameQueueSize) + .setTooltip(v -> Component.translatable("vulkanmod.options.frameQueue.tooltip")), new SwitchOption(Component.translatable("vulkanmod.options.textureAnimations"), - value -> { - config.textureAnimations = value; - }, - () -> config.textureAnimations), + v -> config.textureAnimations = v, + () -> config.textureAnimations) }), - new OptionBlock("", new Option[]{ + new OptionBlock("", new Option[]{ new CyclingOption<>(Component.translatable("vulkanmod.options.deviceSelector"), - IntStream.range(-1, DeviceManager.suitableDevices.size()).boxed() - .toArray(Integer[]::new), - value -> config.device = value, - () -> config.device) - .setTranslator(value -> Component.translatable((value == -1) - ? "vulkanmod.options.deviceSelector.auto" - : DeviceManager.suitableDevices.get( - value).deviceName) - ) - .setTooltip(value -> Component.nullToEmpty("%s: %s".formatted( - Component.translatable("vulkanmod.options.deviceSelector.tooltip").getString(), - DeviceManager.device.deviceName))) + IntStream.range(-1, DeviceManager.suitableDevices.size()) + .boxed() + .toArray(Integer[]::new), + value -> config.device = value, + () -> config.device) + .setTranslator(v -> Component.translatable( + v == -1 ? "vulkanmod.options.deviceSelector.auto" + : DeviceManager.suitableDevices.get(v).deviceName)) + .setTooltip(v -> Component.literal( + Component.translatable("vulkanmod.options.deviceSelector.tooltip").getString() + ": " + + DeviceManager.device.deviceName)) }) }; - } -} +} \ No newline at end of file diff --git a/src/main/java/net/vulkanmod/config/video/VideoMode.java b/src/main/java/net/vulkanmod/config/video/VideoMode.java new file mode 100644 index 000000000..f31f38398 --- /dev/null +++ b/src/main/java/net/vulkanmod/config/video/VideoMode.java @@ -0,0 +1,15 @@ +package net.vulkanmod.config.video; + +import org.jetbrains.annotations.NotNull; + +public record VideoMode(int width, int height, int bitDepth, int refreshRate) { + + @Override + public @NotNull String toString() { + return width + "×" + height + (refreshRate > 0 ? " @ " + refreshRate + "Hz" : ""); + } + + public VideoMode withRefreshRate(int newRate) { + return new VideoMode(width, height, bitDepth, newRate); + } +} \ No newline at end of file diff --git a/src/main/java/net/vulkanmod/config/video/VideoModeManager.java b/src/main/java/net/vulkanmod/config/video/VideoModeManager.java index 985455a63..a96ccdf11 100644 --- a/src/main/java/net/vulkanmod/config/video/VideoModeManager.java +++ b/src/main/java/net/vulkanmod/config/video/VideoModeManager.java @@ -1,96 +1,91 @@ package net.vulkanmod.config.video; -import net.vulkanmod.Initializer; import org.lwjgl.glfw.GLFW; import org.lwjgl.glfw.GLFWVidMode; -import java.util.ArrayList; -import java.util.List; +import java.util.*; -import static org.lwjgl.glfw.GLFW.*; +public final class VideoModeManager { -public abstract class VideoModeManager { - private static VideoModeSet.VideoMode osVideoMode; - private static VideoModeSet[] videoModeSets; + private static List availableSets = List.of(); + private static VideoMode currentOsMode = new VideoMode(800, 600, 8, 60); + private static VideoMode selectedMode = currentOsMode; - public static VideoModeSet.VideoMode selectedVideoMode; + private VideoModeManager() {} public static void init() { - long monitor = glfwGetPrimaryMonitor(); - osVideoMode = getCurrentVideoMode(monitor); - videoModeSets = populateVideoResolutions(GLFW.glfwGetPrimaryMonitor()); + long monitor = GLFW.glfwGetPrimaryMonitor(); + currentOsMode = getCurrentVideoMode(monitor); + availableSets = List.copyOf(loadVideoModeSets(monitor)); + selectedMode = findClosestMatch(currentOsMode).bestMode(); } - public static void applySelectedVideoMode() { - Initializer.CONFIG.videoMode = selectedVideoMode; - } - - public static VideoModeSet[] getVideoResolutions() { - return videoModeSets; - } - - public static VideoModeSet getFirstAvailable() { - if(videoModeSets != null) - return videoModeSets[videoModeSets.length - 1]; - else - return VideoModeSet.getDummy(); - } + public static VideoMode selectedMode() { return selectedMode; } + public static void selectMode(VideoMode mode) { selectedMode = mode; } - public static VideoModeSet.VideoMode getOsVideoMode() { - return osVideoMode; - } + public static List availableSets() { return availableSets; } + public static VideoMode currentOsMode() { return currentOsMode; } - public static VideoModeSet.VideoMode getCurrentVideoMode(long monitor){ + private static VideoMode getCurrentVideoMode(long monitor) { GLFWVidMode vidMode = GLFW.glfwGetVideoMode(monitor); - - if (vidMode == null) - throw new NullPointerException("Unable to get current video mode"); - - return new VideoModeSet.VideoMode(vidMode.width(), vidMode.height(), vidMode.redBits(), vidMode.refreshRate()); + if (vidMode == null) return new VideoMode(1920, 1080, 8, 60); + return new VideoMode(vidMode.width(), vidMode.height(), vidMode.redBits(), vidMode.refreshRate()); } - public static VideoModeSet[] populateVideoResolutions(long monitor) { + private static List loadVideoModeSets(long monitor) { GLFWVidMode.Buffer buffer = GLFW.glfwGetVideoModes(monitor); + if (buffer == null) return List.of(); - List videoModeSets = new ArrayList<>(); - - int currWidth = 0, currHeight = 0, currBitDepth = 0; - VideoModeSet videoModeSet = null; + Map> map = new LinkedHashMap<>(); for (int i = 0; i < buffer.limit(); i++) { buffer.position(i); - int bitDepth = buffer.redBits(); - if (buffer.redBits() < 8 || buffer.greenBits() != bitDepth || buffer.blueBits() != bitDepth) - continue; - - int width = buffer.width(); - int height = buffer.height(); - int refreshRate = buffer.refreshRate(); + int r = buffer.redBits(); + if (r < 8 || buffer.greenBits() != r || buffer.blueBits() != r) continue; - if (currWidth != width || currHeight != height || currBitDepth != bitDepth) { - currWidth = width; - currHeight = height; - currBitDepth = bitDepth; - - videoModeSet = new VideoModeSet(currWidth, currHeight, currBitDepth); - videoModeSets.add(videoModeSet); - } + String key = buffer.width() + "x" + buffer.height() + "@" + r; + map.computeIfAbsent(key, k -> new TreeSet<>()).add(buffer.refreshRate()); + } - videoModeSet.addRefreshRate(refreshRate); + List sets = new ArrayList<>(); + for (var entry : map.entrySet()) { + String[] parts = entry.getKey().split("@"); + String[] res = parts[0].split("x"); + int bitDepth = Integer.parseInt(parts[1]); + sets.add(new VideoModeSet( + Integer.parseInt(res[0]), + Integer.parseInt(res[1]), + bitDepth, + entry.getValue() + )); } - VideoModeSet[] arr = new VideoModeSet[videoModeSets.size()]; - videoModeSets.toArray(arr); + sets.sort(Comparator + .comparingInt(VideoModeSet::width) + .thenComparingInt(VideoModeSet::height) + .thenComparingInt(VideoModeSet::bitDepth) + .reversed()); - return arr; + return sets; } - public static VideoModeSet getFromVideoMode(VideoModeSet.VideoMode videoMode) { - for (var set : videoModeSets) { - if (set.width == videoMode.width && set.height == videoMode.height) - return set; - } + public static VideoModeSet findSetFor(VideoMode mode) { + return availableSets.stream() + .filter(s -> s.width() == mode.width() && s.height() == mode.height()) + .findFirst() + .orElseGet(() -> new VideoModeSet(mode.width(), mode.height(), mode.bitDepth(), Set.of(mode.refreshRate()))); + } + + private static VideoModeSet findClosestMatch(VideoMode mode) { + return availableSets.stream() + .min(Comparator.comparingInt((VideoModeSet s) -> + Math.abs(s.width() - mode.width()) * 10000 + + Math.abs(s.height() - mode.height()) * 100 + + Math.abs(s.bitDepth() - mode.bitDepth()))) + .orElseGet(() -> new VideoModeSet(mode.width(), mode.height(), 8, Set.of(60))); + } - return null; + public static VideoModeSet getDummy() { + return new VideoModeSet(-1, -1, -1, Set.of(-1)); } } diff --git a/src/main/java/net/vulkanmod/config/video/VideoModeSet.java b/src/main/java/net/vulkanmod/config/video/VideoModeSet.java index e4fb34fac..0962b2aab 100644 --- a/src/main/java/net/vulkanmod/config/video/VideoModeSet.java +++ b/src/main/java/net/vulkanmod/config/video/VideoModeSet.java @@ -1,95 +1,31 @@ package net.vulkanmod.config.video; -import it.unimi.dsi.fastutil.objects.ObjectArrayList; +import org.jetbrains.annotations.NotNull; -import java.util.List; +import java.util.*; -public class VideoModeSet { - public final int width; - public final int height; - public final int bitDepth; - List refreshRates = new ObjectArrayList<>(); +public record VideoModeSet(int width, int height, int bitDepth, NavigableSet refreshRates) { - public static VideoModeSet getDummy() { - var set = new VideoModeSet(-1, -1, -1); - set.addRefreshRate(-1); - return set; + public VideoModeSet(int width, int height, int bitDepth, Collection refreshRates) { + this(width, height, bitDepth, new TreeSet<>(refreshRates)); } - public VideoModeSet(int width, int height, int bitDepth) { - this.width = width; - this.height = height; - this.bitDepth = bitDepth; + public VideoMode bestMode() { + return new VideoMode(width, height, bitDepth, refreshRates.last()); } - public int getRefreshRate() { - return this.refreshRates.get(0); + public VideoMode modeAtRate(int rate) { + Integer closest = refreshRates.floor(rate); + if (closest == null) closest = refreshRates.first(); + return new VideoMode(width, height, bitDepth, closest); } - public boolean hasRefreshRate(int r) { - return this.refreshRates.contains(r); - } - - public List getRefreshRates() { - return this.refreshRates; - } - - void addRefreshRate(int rr) { - this.refreshRates.add(rr); - } - - public String toString() { - return this.width + " x " + this.height; + public boolean supportsRate(int rate) { + return refreshRates.contains(rate); } @Override - public boolean equals(Object o) { - if (this == o) - return true; - if (o == null || getClass() != o.getClass()) - return false; - - VideoModeSet that = (VideoModeSet) o; - return width == that.width && height == that.height && bitDepth == that.bitDepth && refreshRates.equals(that.refreshRates); - } - - public VideoMode getVideoMode(int refresh) { - int idx = refreshRates.indexOf(refresh); - - if (idx == -1) { - idx = 0; - } - - return new VideoMode(this.width, this.height, this.bitDepth, this.refreshRates.get(idx)); + public @NotNull String toString() { + return width + "×" + height; } - - public VideoMode getVideoMode() { - int refreshRate = this.refreshRates.get(this.refreshRates.size() - 1); - return new VideoMode(this.width, this.height, this.bitDepth, refreshRate); - } - - public static final class VideoMode { - public int width; - public int height; - public int bitDepth; - public int refreshRate; - - public VideoMode(int width, int height, int bitDepth, int refreshRate) { - this.width = width; - this.height = height; - this.bitDepth = bitDepth; - this.refreshRate = refreshRate; - } - - @Override - public String toString() { - return "VideoMode[" + - "width=" + width + ", " + - "height=" + height + ", " + - "bitDepth=" + bitDepth + ", " + - "refreshRate=" + refreshRate + ']'; - } - - } - -} +} \ No newline at end of file diff --git a/src/main/java/net/vulkanmod/config/video/WindowMode.java b/src/main/java/net/vulkanmod/config/video/WindowMode.java index 621f06dea..044ffb9ac 100644 --- a/src/main/java/net/vulkanmod/config/video/WindowMode.java +++ b/src/main/java/net/vulkanmod/config/video/WindowMode.java @@ -1,31 +1,39 @@ package net.vulkanmod.config.video; -public enum WindowMode { - WINDOWED(0), - WINDOWED_FULLSCREEN(1), - EXCLUSIVE_FULLSCREEN(2); +import net.minecraft.network.chat.Component; - public final int mode; +public sealed interface WindowMode permits WindowMode.Windowed, WindowMode.WindowedFullscreen, WindowMode.ExclusiveFullscreen { - WindowMode(int mode) { - this.mode = mode; + String translationKey(); + + boolean isFullscreen(); + + record Windowed() implements WindowMode { + public String translationKey() { return "vulkanmod.options.windowMode.windowed"; } + public boolean isFullscreen() { return false; } + } + + record WindowedFullscreen() implements WindowMode { + public String translationKey() { return "vulkanmod.options.windowMode.windowedFullscreen"; } + public boolean isFullscreen() { return true; } } - public static WindowMode fromValue(int value) { - return switch (value) { - case 0 -> WINDOWED; - case 1 -> WINDOWED_FULLSCREEN; - case 2 -> EXCLUSIVE_FULLSCREEN; + record ExclusiveFullscreen() implements WindowMode { + public String translationKey() { return "options.fullscreen"; } + public boolean isFullscreen() { return true; } + } + + WindowMode[] VALUES = { new Windowed(), new WindowedFullscreen(), new ExclusiveFullscreen() }; + + static WindowMode fromIndex(int index) { + return VALUES[index % VALUES.length]; + } - default -> throw new IllegalStateException("Unexpected value: " + value); - }; + static WindowMode fromMinecraftFullscreen(boolean mcFullscreen) { + return mcFullscreen ? new ExclusiveFullscreen() : new Windowed(); } - public static String getComponentName(WindowMode windowMode) { - return switch (windowMode) { - case WINDOWED -> "vulkanmod.options.windowMode.windowed"; - case WINDOWED_FULLSCREEN -> "vulkanmod.options.windowMode.windowedFullscreen"; - case EXCLUSIVE_FULLSCREEN -> "options.fullscreen"; - }; + static Component nameOf(WindowMode mode) { + return Component.translatable(mode.translationKey()); } } diff --git a/src/main/java/net/vulkanmod/mixin/window/WindowMixin.java b/src/main/java/net/vulkanmod/mixin/window/WindowMixin.java index 4827f9043..02fabcad4 100644 --- a/src/main/java/net/vulkanmod/mixin/window/WindowMixin.java +++ b/src/main/java/net/vulkanmod/mixin/window/WindowMixin.java @@ -6,6 +6,7 @@ import net.vulkanmod.Initializer; import net.vulkanmod.config.Config; import net.vulkanmod.config.Platform; +import net.vulkanmod.config.video.VideoMode; import net.vulkanmod.config.video.VideoModeManager; import net.vulkanmod.config.option.Options; import net.vulkanmod.config.video.VideoModeSet; @@ -114,13 +115,13 @@ private void setMode() { long monitor = GLFW.glfwGetPrimaryMonitor(); if (this.fullscreen) { { - VideoModeSet.VideoMode videoMode = config.videoMode; + VideoMode videoMode = config.videoMode; boolean supported; - VideoModeSet set = VideoModeManager.getFromVideoMode(videoMode); + VideoModeSet set = VideoModeManager.findSetFor(videoMode); if (set != null) { - supported = set.hasRefreshRate(videoMode.refreshRate); + supported = set.supportsRate(videoMode.refreshRate()); } else { supported = false; @@ -128,7 +129,7 @@ private void setMode() { if(!supported) { LOGGER.error("Resolution not supported, using first available as fallback"); - videoMode = VideoModeManager.getFirstAvailable().getVideoMode(); + videoMode = VideoModeManager.currentOsMode(); } if (!this.wasOnFullscreen) { @@ -140,15 +141,15 @@ private void setMode() { this.x = 0; this.y = 0; - this.width = videoMode.width; - this.height = videoMode.height; - GLFW.glfwSetWindowMonitor(this.handle, monitor, this.x, this.y, this.width, this.height, videoMode.refreshRate); + this.width = videoMode.width(); + this.height = videoMode.height(); + GLFW.glfwSetWindowMonitor(this.handle, monitor, this.x, this.y, this.width, this.height, videoMode.refreshRate()); this.wasOnFullscreen = true; } } - else if (config.windowMode == WindowMode.WINDOWED_FULLSCREEN.mode) { - VideoModeSet.VideoMode videoMode = VideoModeManager.getOsVideoMode(); + else if (config.windowMode == 0) { // 0 is windowed + VideoMode videoMode = VideoModeManager.currentOsMode(); if (!this.wasOnFullscreen) { this.windowedX = this.x; @@ -157,8 +158,8 @@ else if (config.windowMode == WindowMode.WINDOWED_FULLSCREEN.mode) { this.windowedHeight = this.height; } - int width = videoMode.width; - int height = videoMode.height; + int width = videoMode.width(); + int height = videoMode.height(); GLFW.glfwSetWindowAttrib(this.handle, GLFW_DECORATED, GLFW_FALSE); GLFW.glfwSetWindowMonitor(this.handle, 0L, 0, 0, width, height, -1); From f9f3845c0354e74e970d86d56fc5ccc960e1c25f Mon Sep 17 00:00:00 2001 From: NotNekodev <97458908+NotNekodev@users.noreply.github.com> Date: Tue, 2 Dec 2025 21:01:41 +0100 Subject: [PATCH 20/21] make error logging in Config.java more robust --- src/main/java/net/vulkanmod/config/Config.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/main/java/net/vulkanmod/config/Config.java b/src/main/java/net/vulkanmod/config/Config.java index 821266000..c3358814f 100644 --- a/src/main/java/net/vulkanmod/config/Config.java +++ b/src/main/java/net/vulkanmod/config/Config.java @@ -2,14 +2,13 @@ import com.google.gson.*; import com.google.gson.annotations.JsonAdapter; +import net.vulkanmod.Initializer; import net.vulkanmod.config.video.VideoMode; import net.vulkanmod.config.video.VideoModeManager; import java.io.IOException; -import java.lang.reflect.Modifier; import java.nio.file.Files; import java.nio.file.Path; -import java.util.Objects; @JsonAdapter(Config.GsonAdapter.class) public class Config { @@ -41,7 +40,7 @@ public void save() { Files.createDirectories(CONFIG_PATH.getParent()); Files.writeString(CONFIG_PATH, GSON.toJson(this)); } catch (IOException e) { - e.printStackTrace(); + Initializer.LOGGER.error("Error saving config file!", e); } } From 85ddb21193314d17ea76dee231228ffd617c899d Mon Sep 17 00:00:00 2001 From: NotNekodev <97458908+NotNekodev@users.noreply.github.com> Date: Tue, 2 Dec 2025 21:10:19 +0100 Subject: [PATCH 21/21] fix some small warnings --- .../java/net/vulkanmod/config/Config.java | 14 ++--- .../java/net/vulkanmod/config/Platform.java | 8 +-- .../vulkanmod/config/gui/VOptionScreen.java | 2 + .../config/gui/render/GuiRenderer.java | 61 +------------------ .../config/option/CyclingOption.java | 1 + .../net/vulkanmod/config/option/Option.java | 5 +- .../config/video/VideoModeManager.java | 2 + .../vulkanmod/config/video/WindowMode.java | 3 + 8 files changed, 26 insertions(+), 70 deletions(-) diff --git a/src/main/java/net/vulkanmod/config/Config.java b/src/main/java/net/vulkanmod/config/Config.java index c3358814f..4e65dca0d 100644 --- a/src/main/java/net/vulkanmod/config/Config.java +++ b/src/main/java/net/vulkanmod/config/Config.java @@ -116,14 +116,14 @@ public Config deserialize(JsonElement json, java.lang.reflect.Type typeOfT, Json config.windowMode = getInt(obj, "windowMode", 0); config.advCulling = getInt(obj, "advCulling", 2); - config.indirectDraw = getBoolean(obj, "indirectDraw", true); - config.uniqueOpaqueLayer = getBoolean(obj, "uniqueOpaqueLayer", true); - config.entityCulling = getBoolean(obj, "entityCulling", true); + config.indirectDraw = getBoolean(obj, "indirectDraw"); + config.uniqueOpaqueLayer = getBoolean(obj, "uniqueOpaqueLayer"); + config.entityCulling = getBoolean(obj, "entityCulling"); config.ambientOcclusion = getInt(obj, "ambientOcclusion", 1); config.frameQueueSize = getInt(obj, "frameQueueSize", 2); config.builderThreads = getInt(obj, "builderThreads", 0); - config.backFaceCulling = getBoolean(obj, "backFaceCulling", true); - config.textureAnimations = getBoolean(obj, "textureAnimations", true); + config.backFaceCulling = getBoolean(obj, "backFaceCulling"); + config.textureAnimations = getBoolean(obj, "textureAnimations"); config.device = getInt(obj, "device", -1); return config; @@ -134,9 +134,9 @@ private int getInt(JsonObject obj, String key, int def) { return el != null && el.isJsonPrimitive() ? el.getAsInt() : def; } - private boolean getBoolean(JsonObject obj, String key, boolean def) { + private boolean getBoolean(JsonObject obj, String key) { JsonElement el = obj.get(key); - return el != null && el.isJsonPrimitive() ? el.getAsBoolean() : def; + return el == null || !el.isJsonPrimitive() || el.getAsBoolean(); } } } \ No newline at end of file diff --git a/src/main/java/net/vulkanmod/config/Platform.java b/src/main/java/net/vulkanmod/config/Platform.java index 38914708d..8bc63c0ee 100644 --- a/src/main/java/net/vulkanmod/config/Platform.java +++ b/src/main/java/net/vulkanmod/config/Platform.java @@ -16,7 +16,7 @@ public static void init() { Configuration.STACK_SIZE.set(256); GLFW.glfwInitHint(GLFW_PLATFORM, activePlat); - LOGGER.info("Selecting Platform: {}", getStringFromPlat(activePlat)); + LOGGER.info("Selecting Platform: {}", getStringFromPlat()); LOGGER.info("GLFW: {}", GLFW.glfwGetVersionString()); GLFW.glfwInit(); } @@ -43,14 +43,14 @@ private static int getSupportedPlat() { return GLFW_ANY_PLATFORM; //Unknown platform } - private static String getStringFromPlat(int plat) { - return switch (plat) { + private static String getStringFromPlat() { + return switch (Platform.activePlat) { case GLFW_PLATFORM_WIN32 -> "WIN32"; case GLFW_PLATFORM_WAYLAND -> "WAYLAND"; case GLFW_PLATFORM_X11 -> "X11"; case GLFW_PLATFORM_COCOA -> "MACOS"; case GLFW_ANY_PLATFORM -> "ANDROID"; - default -> throw new IllegalStateException("Unexpected value: " + plat); + default -> throw new IllegalStateException("Unexpected value: " + Platform.activePlat); }; } diff --git a/src/main/java/net/vulkanmod/config/gui/VOptionScreen.java b/src/main/java/net/vulkanmod/config/gui/VOptionScreen.java index ea307103c..569b51d4f 100644 --- a/src/main/java/net/vulkanmod/config/gui/VOptionScreen.java +++ b/src/main/java/net/vulkanmod/config/gui/VOptionScreen.java @@ -240,6 +240,7 @@ private void buildPage() { this.addButtons(); } + @SuppressWarnings("DuplicatedCode") private void addButtons() { int rightMargin = 10; int padding = 10; @@ -297,6 +298,7 @@ private void addButtons() { this.addWidget(this.searchField); } + @SuppressWarnings("DuplicatedCode") private void addButtonsWithSearchBar() { int rightMargin = 10; int padding = 10; diff --git a/src/main/java/net/vulkanmod/config/gui/render/GuiRenderer.java b/src/main/java/net/vulkanmod/config/gui/render/GuiRenderer.java index 3ab7171b6..3a502e3e1 100644 --- a/src/main/java/net/vulkanmod/config/gui/render/GuiRenderer.java +++ b/src/main/java/net/vulkanmod/config/gui/render/GuiRenderer.java @@ -12,9 +12,7 @@ import net.minecraft.util.Mth; import org.joml.Matrix3x2f; -import java.util.HashMap; import java.util.List; -import java.util.Map; public abstract class GuiRenderer { @@ -23,60 +21,6 @@ public abstract class GuiRenderer { public static PoseStack pose; public static BufferBuilder bufferBuilder; - // Scrolling text state management - private static final Map scrollingTextStates = new HashMap<>(); - private static final float SCROLL_SPEED = 30.0f; // pixels per second - private static final float SCROLL_PAUSE_DURATION = 1.0f; // seconds to pause at ends - - private static class ScrollingTextState { - float scrollOffset = 0.0f; - long lastUpdateTime = System.currentTimeMillis(); - boolean scrollingForward = true; - float pauseTimer = 0.0f; - - void update(float textWidth, float maxWidth) { - long currentTime = System.currentTimeMillis(); - float deltaTime = (currentTime - lastUpdateTime) / 1000.0f; - lastUpdateTime = currentTime; - - if (textWidth <= maxWidth) { - scrollOffset = 0.0f; - return; - } - - // Handle pause at ends - if (pauseTimer > 0) { - pauseTimer -= deltaTime; - return; - } - - float maxScroll = textWidth - maxWidth; - - if (scrollingForward) { - scrollOffset += SCROLL_SPEED * deltaTime; - if (scrollOffset >= maxScroll) { - scrollOffset = maxScroll; - scrollingForward = false; - pauseTimer = SCROLL_PAUSE_DURATION; - } - } else { - scrollOffset -= SCROLL_SPEED * deltaTime; - if (scrollOffset <= 0) { - scrollOffset = 0; - scrollingForward = true; - pauseTimer = SCROLL_PAUSE_DURATION; - } - } - } - - void reset() { - scrollOffset = 0.0f; - scrollingForward = true; - pauseTimer = 0.0f; - lastUpdateTime = System.currentTimeMillis(); - } - } - public static void enableScissor(int i, int j, int k, int l) { guiGraphics.enableScissor(i, j, k, l); } @@ -93,15 +37,16 @@ public static void fill(int x0, int y0, int x1, int y1, int color) { fill(x0, y0, x1, y1, 0, color); } - public static void fill(int x0, int y0, int x1, int y1, int z, int color) { + public static void fill(int x0, int y0, int x1, int y1, @SuppressWarnings("unused") int z, int color) { guiGraphics.fill(x0, y0, x1, y1, color); } + @SuppressWarnings("unused") public static void fillGradient(int x0, int y0, int x1, int y1, int color1, int color2) { fillGradient(x0, y0, x1, y1, 0, color1, color2); } - public static void fillGradient(int x0, int y0, int x1, int y1, int z, int color1, int color2) { + public static void fillGradient(int x0, int y0, int x1, int y1, @SuppressWarnings("unused") int z, int color1, int color2) { guiGraphics.fillGradient(x0, y0, x1, y1, color1, color2); } diff --git a/src/main/java/net/vulkanmod/config/option/CyclingOption.java b/src/main/java/net/vulkanmod/config/option/CyclingOption.java index d6080f7fd..b070543f5 100644 --- a/src/main/java/net/vulkanmod/config/option/CyclingOption.java +++ b/src/main/java/net/vulkanmod/config/option/CyclingOption.java @@ -24,6 +24,7 @@ public OptionWidget createOptionWidget(int x, int y, int width, int height) { return new CyclingOptionWidget(this, x, y, width, height, this.name); } + @SuppressWarnings("unused") public void updateOption(E[] values, Consumer setter, Supplier getter) { this.onApply = setter; this.valueSupplier = getter; diff --git a/src/main/java/net/vulkanmod/config/option/Option.java b/src/main/java/net/vulkanmod/config/option/Option.java index 42b211b9d..4afbc3a0b 100644 --- a/src/main/java/net/vulkanmod/config/option/Option.java +++ b/src/main/java/net/vulkanmod/config/option/Option.java @@ -3,13 +3,13 @@ import net.minecraft.network.chat.Component; import net.vulkanmod.config.gui.widget.OptionWidget; -import java.util.Objects; import java.util.function.Consumer; import java.util.function.Function; import java.util.function.Supplier; public abstract class Option { protected final Component name; + @SuppressWarnings("unused") protected Component tooltip; protected Consumer onApply; @@ -25,6 +25,7 @@ public abstract class Option { protected boolean active; protected Runnable onChange; + @SuppressWarnings("unused") public Option(Component name, Consumer setter, Supplier getter, Function translator, Function tooltip) { this.name = name; @@ -57,11 +58,13 @@ public Option(Component name, Consumer setter, Supplier getter) { this.newValue = this.value = this.valueSupplier.get(); } + @SuppressWarnings("unused") public Option setOnApply(Consumer onApply) { this.onApply = onApply; return this; } + @SuppressWarnings("unused") public Option setValueSupplier(Supplier supplier) { this.valueSupplier = supplier; return this; diff --git a/src/main/java/net/vulkanmod/config/video/VideoModeManager.java b/src/main/java/net/vulkanmod/config/video/VideoModeManager.java index a96ccdf11..39c0615fe 100644 --- a/src/main/java/net/vulkanmod/config/video/VideoModeManager.java +++ b/src/main/java/net/vulkanmod/config/video/VideoModeManager.java @@ -20,6 +20,7 @@ public static void init() { selectedMode = findClosestMatch(currentOsMode).bestMode(); } + @SuppressWarnings("unused") public static VideoMode selectedMode() { return selectedMode; } public static void selectMode(VideoMode mode) { selectedMode = mode; } @@ -85,6 +86,7 @@ private static VideoModeSet findClosestMatch(VideoMode mode) { .orElseGet(() -> new VideoModeSet(mode.width(), mode.height(), 8, Set.of(60))); } + @SuppressWarnings("unused") public static VideoModeSet getDummy() { return new VideoModeSet(-1, -1, -1, Set.of(-1)); } diff --git a/src/main/java/net/vulkanmod/config/video/WindowMode.java b/src/main/java/net/vulkanmod/config/video/WindowMode.java index 044ffb9ac..975f17724 100644 --- a/src/main/java/net/vulkanmod/config/video/WindowMode.java +++ b/src/main/java/net/vulkanmod/config/video/WindowMode.java @@ -6,6 +6,7 @@ public sealed interface WindowMode permits WindowMode.Windowed, WindowMode.Windo String translationKey(); + @SuppressWarnings("unused") boolean isFullscreen(); record Windowed() implements WindowMode { @@ -25,10 +26,12 @@ record ExclusiveFullscreen() implements WindowMode { WindowMode[] VALUES = { new Windowed(), new WindowedFullscreen(), new ExclusiveFullscreen() }; + @SuppressWarnings("unused") static WindowMode fromIndex(int index) { return VALUES[index % VALUES.length]; } + @SuppressWarnings("unused") static WindowMode fromMinecraftFullscreen(boolean mcFullscreen) { return mcFullscreen ? new ExclusiveFullscreen() : new Windowed(); }