diff --git a/src/main/java/cc/fros/features/gui/FrosGui.java b/src/main/java/cc/fros/features/gui/FrosGui.java index 3ebce1c..3157ff0 100644 --- a/src/main/java/cc/fros/features/gui/FrosGui.java +++ b/src/main/java/cc/fros/features/gui/FrosGui.java @@ -4,6 +4,7 @@ import cc.fros.Fros; import cc.fros.features.Feature; import cc.fros.features.gui.items.buttons.ModuleButton; import cc.fros.features.modules.Module; +import cc.fros.features.modules.client.ClickGuiModule; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.GuiGraphics; import net.minecraft.client.gui.screens.Screen; @@ -84,27 +85,39 @@ public class FrosGui extends Screen { } context.fill(0, 0, context.guiWidth(), context.guiHeight(), new Color(0, 0, 0, 120).hashCode()); - this.widgets.forEach(components -> components.drawScreen(context, mouseX, mouseY, delta)); + + // scale the whole clickgui; widgets work in layout units so mouse + // coordinates are divided back down before they reach them + float scale = ClickGuiModule.scale(); + int layoutX = (int) (mouseX / scale); + int layoutY = (int) (mouseY / scale); + context.pose().pushMatrix(); + context.pose().scale(scale, scale); + this.widgets.forEach(components -> components.drawScreen(context, layoutX, layoutY, delta)); + context.pose().popMatrix(); } @Override public boolean mouseClicked(MouseButtonEvent click, boolean doubled) { - this.widgets.forEach(components -> components.mouseClicked((int) click.x(), (int) click.y(), click.button())); + float scale = ClickGuiModule.scale(); + this.widgets.forEach(components -> components.mouseClicked((int) (click.x() / scale), (int) (click.y() / scale), click.button())); return super.mouseClicked(click, doubled); } @Override public boolean mouseReleased(MouseButtonEvent click) { - this.widgets.forEach(components -> components.mouseReleased((int) click.x(), (int) click.y(), click.button())); + float scale = ClickGuiModule.scale(); + this.widgets.forEach(components -> components.mouseReleased((int) (click.x() / scale), (int) (click.y() / scale), click.button())); return super.mouseReleased(click); } @Override public boolean mouseScrolled(double mouseX, double mouseY, double horizontalAmount, double verticalAmount) { + int step = Math.max(1, Math.round((float) (10 / ClickGuiModule.scale()))); if (verticalAmount < 0) { - this.widgets.forEach(component -> component.setY(component.getY() - 10)); + this.widgets.forEach(component -> component.setY(component.getY() - step)); } else if (verticalAmount > 0) { - this.widgets.forEach(component -> component.setY(component.getY() + 10)); + this.widgets.forEach(component -> component.setY(component.getY() + step)); } return super.mouseScrolled(mouseX, mouseY, horizontalAmount, verticalAmount); } diff --git a/src/main/java/cc/fros/features/gui/HudEditorScreen.java b/src/main/java/cc/fros/features/gui/HudEditorScreen.java index 80aee19..6ca65bd 100644 --- a/src/main/java/cc/fros/features/gui/HudEditorScreen.java +++ b/src/main/java/cc/fros/features/gui/HudEditorScreen.java @@ -4,6 +4,7 @@ import cc.fros.Fros; import cc.fros.features.Feature; import cc.fros.features.gui.items.buttons.ModuleButton; import cc.fros.features.modules.Module; +import cc.fros.features.modules.client.ClickGuiModule; import cc.fros.features.modules.client.HudModule; import net.minecraft.client.gui.GuiGraphics; import net.minecraft.client.gui.screens.Screen; @@ -40,27 +41,37 @@ public class HudEditorScreen extends Screen { @Override public void render(GuiGraphics context, int mouseX, int mouseY, float delta) { anyHover = false; - this.components.forEach(component -> component.drawScreen(context, mouseX, mouseY, delta)); + // same ui scale treatment as the clickgui + float scale = ClickGuiModule.scale(); + int layoutX = (int) (mouseX / scale); + int layoutY = (int) (mouseY / scale); + context.pose().pushMatrix(); + context.pose().scale(scale, scale); + this.components.forEach(component -> component.drawScreen(context, layoutX, layoutY, delta)); + context.pose().popMatrix(); } @Override public boolean mouseClicked(MouseButtonEvent click, boolean doubled) { - this.components.forEach(component -> component.mouseClicked((int) click.x(), (int) click.y(), click.button())); + float scale = ClickGuiModule.scale(); + this.components.forEach(component -> component.mouseClicked((int) (click.x() / scale), (int) (click.y() / scale), click.button())); return super.mouseClicked(click, doubled); } @Override public boolean mouseReleased(MouseButtonEvent click) { - this.components.forEach(component -> component.mouseReleased((int) click.x(), (int) click.y(), click.button())); + float scale = ClickGuiModule.scale(); + this.components.forEach(component -> component.mouseReleased((int) (click.x() / scale), (int) (click.y() / scale), click.button())); return super.mouseReleased(click); } @Override public boolean mouseScrolled(double mouseX, double mouseY, double horizontalAmount, double verticalAmount) { + int step = Math.max(1, Math.round((float) (10 / ClickGuiModule.scale()))); if (verticalAmount < 0) { - this.components.forEach(component -> component.setY(component.getY() - 10)); + this.components.forEach(component -> component.setY(component.getY() - step)); } else if (verticalAmount > 0) { - this.components.forEach(component -> component.setY(component.getY() + 10)); + this.components.forEach(component -> component.setY(component.getY() + step)); } return super.mouseScrolled(mouseX, mouseY, horizontalAmount, verticalAmount); } diff --git a/src/main/java/cc/fros/features/modules/client/ClickGuiModule.java b/src/main/java/cc/fros/features/modules/client/ClickGuiModule.java index d798871..75cb509 100644 --- a/src/main/java/cc/fros/features/modules/client/ClickGuiModule.java +++ b/src/main/java/cc/fros/features/modules/client/ClickGuiModule.java @@ -19,6 +19,7 @@ public class ClickGuiModule extends Module { public final Setting minecraftFont = bool("MinecraftFont", false); public final Setting font = str("Font", "SansSerif"); public final Setting fontSize = num("FontSize", 10, 6, 24); + public final Setting guiScale = num("GUIScale", 1.0f, 0.5f, 2.0f); public final Setting prefix = str("Prefix", "."); public final Setting color = color("Color", 0, 0, 255, 180); public final Setting topColor = color("TopColor", 0, 0, 150, 240); @@ -75,4 +76,9 @@ public class ClickGuiModule extends Module { public static ClickGuiModule getInstance() { return INSTANCE; } + + /** ui scale factor applied to the client's own hud and clickgui */ + public static float scale() { + return INSTANCE == null ? 1f : Math.max(0.5f, INSTANCE.guiScale.getValue()); + } } \ No newline at end of file diff --git a/src/main/java/cc/fros/features/modules/client/HudModule.java b/src/main/java/cc/fros/features/modules/client/HudModule.java index 28a533c..e29f5d2 100644 --- a/src/main/java/cc/fros/features/modules/client/HudModule.java +++ b/src/main/java/cc/fros/features/modules/client/HudModule.java @@ -30,19 +30,36 @@ public abstract class HudModule extends Module { } public float getY() { + float scaledHeight = getHeight() * ClickGuiModule.scale(); float heightWithChat = mc.getWindow().getGuiScaledHeight() - 14; float baseY = mc.getWindow().getGuiScaledHeight() * pos.getValue().y(); - float combined = baseY + getHeight(); + float combined = baseY + scaledHeight; if (mc.screen instanceof ChatScreen) { - baseY = Math.min(combined, heightWithChat) - getHeight(); + baseY = Math.min(combined, heightWithChat) - scaledHeight; } return baseY; } @Subscribe public void onRender2DHud(Render2DEvent e) { - render(e); + float scale = ClickGuiModule.scale(); + if (scale == 1f) { + render(e); + return; + } + // scale the element about its own position anchor so content, + // editor box and hover label all grow together + float anchorX = getX(); + float anchorY = getY(); + e.getContext().pose().pushMatrix(); + e.getContext().pose().translate(anchorX * (1f - scale), anchorY * (1f - scale)); + e.getContext().pose().scale(scale, scale); + try { + render(e); + } finally { + e.getContext().pose().popMatrix(); + } } @Subscribe @@ -75,10 +92,11 @@ public abstract class HudModule extends Module { } if (dragging) { + float scale = ClickGuiModule.scale(); float finalX = Math.min(Math.max(getMouseX() - dragX, 0), - mc.getWindow().getGuiScaledWidth() - width); + mc.getWindow().getGuiScaledWidth() - width * scale); float finalY = Math.min(Math.max(getMouseY() - dragY, 0), - mc.getWindow().getGuiScaledHeight() - height); + mc.getWindow().getGuiScaledHeight() - height * scale); pos.setValue(new Vector2f(finalX, finalY) .div(mc.getWindow().getGuiScaledWidth(), mc.getWindow().getGuiScaledHeight())); @@ -95,8 +113,10 @@ public abstract class HudModule extends Module { if (shouldDrawDescription) { int textWidth = FrosFont.width(getName()); int textHeight = FrosFont.lineHeight(); + float scale = ClickGuiModule.scale(); float textX = x + width + 5; - if (textX + textWidth > mc.getWindow().getGuiScaledWidth()) { + // the label is drawn inside the element's scale transform + if (x + (textX + textWidth - x) * scale > mc.getWindow().getGuiScaledWidth()) { textX = x - 5 - textWidth; } FrosFont.draw(e.getContext(), getName(), @@ -129,9 +149,10 @@ public abstract class HudModule extends Module { float y = getY(); int mouseX = getMouseX(); int mouseY = getMouseY(); + float scale = ClickGuiModule.scale(); - return mouseX >= x - 1 && mouseX <= x + width + 1 && - mouseY >= y - 1 && mouseY <= y + height + 1; + return mouseX >= x - 1 && mouseX <= x + width * scale + 1 && + mouseY >= y - 1 && mouseY <= y + height * scale + 1; } public float getWidth() { diff --git a/src/main/java/cc/fros/util/render/FrosFont.java b/src/main/java/cc/fros/util/render/FrosFont.java index 8ca728d..dcf3be5 100644 --- a/src/main/java/cc/fros/util/render/FrosFont.java +++ b/src/main/java/cc/fros/util/render/FrosFont.java @@ -82,7 +82,7 @@ public final class FrosFont { private static FontMetrics metrics; private static String loadedName; private static float loadedSize = Float.NaN; - private static int loadedScale = 1; + private static float loadedScale = 1; private static long lastAttempt; private static int glyphCounter; @@ -110,14 +110,16 @@ public final class FrosFont { /** converts a raster pixel measurement to gui units */ private static int toGui(int pixels) { - return (pixels + loadedScale - 1) / loadedScale; + return (int) Math.ceil(pixels / (double) loadedScale); } - private static int currentScale() { + private static float currentScale() { try { - return Math.max(1, (int) Math.round(mc.getWindow().getGuiScale())); + // one raster pixel per screen pixel, following both mc's gui scale + // and the client's own ui scale slider + return Math.max(0.5f, (float) (mc.getWindow().getGuiScale() * ClickGuiModule.scale())); } catch (Exception e) { - return 1; + return 1f; } } @@ -154,7 +156,7 @@ public final class FrosFont { String name = gui == null || vanilla ? "" : gui.font.getValue(); int size = gui == null ? 10 : gui.fontSize.getValue(); if (name == null) name = ""; - int scale = currentScale(); + float scale = currentScale(); boolean changed = !Objects.equals(name, loadedName) || size != loadedSize || scale != loadedScale; if (!changed) { @@ -166,7 +168,7 @@ public final class FrosFont { reset(name, size, scale, changed); } - private static void reset(String name, float size, int scale, boolean verbose) { + private static void reset(String name, float size, float scale, boolean verbose) { loadedName = name; loadedSize = size; loadedScale = scale; @@ -312,7 +314,7 @@ public final class FrosFont { String plain = stripCodes(text); if (plain.isEmpty()) return null; - int pad = loadedScale; + int pad = Math.max(1, Math.round(loadedScale)); int width = Math.max(1, metrics.stringWidth(plain)); int height = metrics.getHeight(); int imageWidth = width + 2 * pad; @@ -345,7 +347,7 @@ public final class FrosFont { } private static void drawSegments(Graphics2D graphics, String text, int baseColor) { - int baseline = metrics.getAscent() + loadedScale; + int baseline = metrics.getAscent() + Math.round(loadedScale); int cursor = 1; int current = baseColor; int segmentStart = 0;