center clickgui text by the font's cap band instead of vanilla offsets

button labels, panel titles and the color picker's copy/paste text used
y offsets hand-tuned for vanilla's 9px line box, so the custom font's
14px line box rendered text ~4px too low, poking out below 15px buttons
and over the header accent line. FrosFont now measures the distance from
line top to the middle of the cap band (glyph 'A' visual bounds) and all
labels center on that via FrosFont.visualCenter()/Item.textY(). the
vanilla fallback lands on the exact old offsets, so nothing moves when
fonts are disabled, and the legacy getTextOffset() hack is gone
This commit is contained in:
froslanz
2026-10-22 12:54:00 -04:00
parent ebd0a76f0c
commit 3568996138
11 changed files with 43 additions and 20 deletions
@@ -146,10 +146,6 @@ public class FrosGui extends Screen {
return this.widgets;
}
public int getTextOffset() {
return -6;
}
public static Color getColorClipboard() {
return colorClipboard;
}
@@ -77,7 +77,8 @@ public class Widget
RenderUtil.rect(context, this.x, headerTop, this.x + this.width, panelBottom, 0x99000000, 1.0f);
context.fill(this.x, headerBottom - 1, this.x + this.width, headerBottom, accent);
drawString(this.getName(), (float) this.x + 3.0f, (float) this.y - 4.0f - (float) FrosGui.getClickGui().getTextOffset(), -1);
drawString(this.getName(), (float) this.x + 3.0f,
(headerTop + headerBottom) / 2f - FrosFont.visualCenter(), -1);
ScissorUtil.enable(context, x, 0, x + width, mc.getWindow().getGuiScaledHeight());
if (this.open) {
@@ -83,6 +83,11 @@ public class Item
FrosFont.draw(context, text, (int) x, (int) y, color);
}
/** y coordinate that visually centers text inside this item */
protected float textY() {
return this.y + this.height / 2f - FrosFont.visualCenter();
}
public boolean isHovering(int mouseX, int mouseY) {
return false;
}
@@ -1,7 +1,6 @@
package cc.fros.features.gui.items.buttons;
import cc.fros.Fros;
import cc.fros.features.gui.FrosGui;
import cc.fros.features.modules.client.ClickGuiModule;
import cc.fros.features.settings.Bind;
import cc.fros.features.settings.Setting;
@@ -29,10 +28,10 @@ public class BindButton
int color = ClickGuiModule.getInstance().color.getValue().getRGB();
RenderUtil.rect(context, this.x, this.y, this.x + (float) this.width + 7.4f, this.y + (float) this.height - 0.5f, this.getState() ? (!this.isHovering(mouseX, mouseY) ? 0x11555555 : -2007673515) : (!this.isHovering(mouseX, mouseY) ? Fros.colorManager.getColorWithAlpha(y, ClickGuiModule.getInstance().color.getValue().getAlpha()) : Fros.colorManager.getColorWithAlpha(y, ClickGuiModule.getInstance().topColor.getValue().getAlpha())));
if (this.isListening) {
drawString(context, "Press a Key...", this.x + 2.3f, this.y - 1.7f - (float) FrosGui.getClickGui().getTextOffset(), -1);
drawString(context, "Press a Key...", this.x + 2.3f, textY(), -1);
} else {
String str = KeyboardUtil.getKeyName(setting.getValue());
drawString(context, this.setting.getName() + " " + ChatFormatting.GRAY + str, this.x + 2.3f, this.y - 1.7f - (float) FrosGui.getClickGui().getTextOffset(), this.getState() ? -1 : -5592406);
drawString(context, this.setting.getName() + " " + ChatFormatting.GRAY + str, this.x + 2.3f, textY(), this.getState() ? -1 : -5592406);
}
}
@@ -2,7 +2,6 @@ package cc.fros.features.gui.items.buttons;
import cc.fros.Fros;
import cc.fros.features.gui.FrosGui;
import cc.fros.features.modules.client.ClickGuiModule;
import cc.fros.features.settings.Setting;
import cc.fros.util.render.RenderUtil;
@@ -23,7 +22,7 @@ public class BooleanButton
@Override
public void drawScreen(GuiGraphics context, int mouseX, int mouseY, float partialTicks) {
RenderUtil.rect(context, this.x, this.y, this.x + (float) this.width + 7.4f, this.y + (float) this.height - 0.5f, this.getState() ? (!this.isHovering(mouseX, mouseY) ? Fros.colorManager.getColorWithAlpha(y, ClickGuiModule.getInstance().color.getValue().getAlpha()) : Fros.colorManager.getColorWithAlpha(y, ClickGuiModule.getInstance().topColor.getValue().getAlpha())) : (!this.isHovering(mouseX, mouseY) ? 0x11555555 : -2007673515));
drawString(context, this.getName(), this.x + 2.3f, this.y - 1.7f - (float) FrosGui.getClickGui().getTextOffset(), this.getState() ? -1 : -5592406);
drawString(context, this.getName(), this.x + 2.3f, textY(), this.getState() ? -1 : -5592406);
}
@Override
@@ -22,7 +22,7 @@ public class Button
@Override
public void drawScreen(GuiGraphics context, int mouseX, int mouseY, float partialTicks) {
RenderUtil.rect(context, this.x, this.y, this.x + (float) this.width, this.y + (float) this.height - 0.5f, this.getState() ? (!this.isHovering(mouseX, mouseY) ? Fros.colorManager.getColorWithAlpha(y, ClickGuiModule.getInstance().color.getValue().getAlpha()) : Fros.colorManager.getColorWithAlpha(y, ClickGuiModule.getInstance().topColor.getValue().getAlpha())) : (!this.isHovering(mouseX, mouseY) ? 0x11555555 : -2007673515));
drawString(context, this.getName(), this.x + 2.3f, this.y - 2.0f - (float) FrosGui.getClickGui().getTextOffset(), this.getState() ? -1 : -5592406);
drawString(context, this.getName(), this.x + 2.3f, textY(), this.getState() ? -1 : -5592406);
}
@Override
@@ -44,7 +44,7 @@ public class ColorButton extends Button {
this.x + (float) this.width + 5.0f, this.y + (float) this.height - 2.5f,
new Color(currentColor.getRGB(), false).getRGB());
drawString(context, this.getName(), this.x + 2.3f, this.y - 1.7f - (float) FrosGui.getClickGui().getTextOffset(), -1);
drawString(context, this.getName(), this.x + 2.3f, textY(), -1);
if (open) {
float yOffset = this.height + 1;
@@ -121,12 +121,12 @@ public class ColorButton extends Button {
int buttonWidth = availableWidth / 2;
RenderUtil.rect(context, pickerX, this.y + yOffset, pickerX + buttonWidth, this.y + yOffset + 14,
hoveringCopy ? Fros.colorManager.getColorWithAlpha(y, ClickGuiModule.getInstance().topColor.getValue().getAlpha()) : 0x11555555);
drawString(context, "Copy", pickerX + buttonWidth / 2.0 - FrosFont.width("Copy") / 2.0, this.y + yOffset + 3, -1);
drawString(context, "Copy", pickerX + buttonWidth / 2.0 - FrosFont.width("Copy") / 2.0, this.y + yOffset + 7f - FrosFont.visualCenter(), -1);
hoveringCopy = isHoveringArea(mouseX, mouseY, pickerX, this.y + yOffset, pickerX + buttonWidth, this.y + yOffset + 14);
RenderUtil.rect(context, pickerX + buttonWidth + 1, this.y + yOffset, pickerX + buttonWidth * 2 + 1, this.y + yOffset + 14,
hoveringPaste ? Fros.colorManager.getColorWithAlpha(y, ClickGuiModule.getInstance().topColor.getValue().getAlpha()) : 0x11555555);
drawString(context, "Paste", pickerX + buttonWidth + buttonWidth / 2.0 - FrosFont.width("Paste") / 2.0 + 1, this.y + yOffset + 3, -1);
drawString(context, "Paste", pickerX + buttonWidth + buttonWidth / 2.0 - FrosFont.width("Paste") / 2.0 + 1, this.y + yOffset + 7f - FrosFont.visualCenter(), -1);
hoveringPaste = isHoveringArea(mouseX, mouseY, pickerX + buttonWidth + 1, this.y + yOffset, pickerX + buttonWidth * 2 + 1, this.y + yOffset + 14);
}
}
@@ -2,7 +2,6 @@ package cc.fros.features.gui.items.buttons;
import cc.fros.Fros;
import cc.fros.features.gui.FrosGui;
import cc.fros.features.modules.client.ClickGuiModule;
import cc.fros.features.settings.Setting;
import cc.fros.util.render.RenderUtil;
@@ -24,7 +23,7 @@ public class EnumButton
@Override
public void drawScreen(GuiGraphics context, int mouseX, int mouseY, float partialTicks) {
RenderUtil.rect(context, this.x, this.y, this.x + (float) this.width + 7.4f, this.y + (float) this.height - 0.5f, this.getState() ? (!this.isHovering(mouseX, mouseY) ? Fros.colorManager.getColorWithAlpha(y, ClickGuiModule.getInstance().color.getValue().getAlpha()) : Fros.colorManager.getColorWithAlpha(y, ClickGuiModule.getInstance().topColor.getValue().getAlpha())) : (!this.isHovering(mouseX, mouseY) ? 0x11555555 : -2007673515));
drawString(context, this.setting.getName() + " " + ChatFormatting.GRAY + (this.setting.currentEnumName().equalsIgnoreCase("ABC") ? "ABC" : this.setting.currentEnumName()), this.x + 2.3f, this.y - 1.7f - (float) FrosGui.getClickGui().getTextOffset(), this.getState() ? -1 : -5592406);
drawString(context, this.setting.getName() + " " + ChatFormatting.GRAY + (this.setting.currentEnumName().equalsIgnoreCase("ABC") ? "ABC" : this.setting.currentEnumName()), this.x + 2.3f, textY(), this.getState() ? -1 : -5592406);
}
@Override
@@ -32,7 +32,7 @@ public class Slider
this.dragSetting(mouseX, mouseY);
RenderUtil.rect(context, this.x, this.y, this.x + (float) this.width + 7.4f, this.y + (float) this.height - 0.5f, !(this.dragging || this.isHovering(mouseX, mouseY)) ? 0x11555555 : -2007673515);
RenderUtil.rect(context, this.x, this.y, (this.setting.getValue()).floatValue() <= this.min.floatValue() ? this.x : this.x + ((float) this.width + 7.4f) * this.partialMultiplier(), this.y + (float) this.height - 0.5f, !(this.dragging || this.isHovering(mouseX, mouseY)) ? Fros.colorManager.getColorWithAlpha(y, ClickGuiModule.getInstance().color.getValue().getAlpha()) : Fros.colorManager.getColorWithAlpha(y, ClickGuiModule.getInstance().topColor.getValue().getAlpha()));
drawString(context, this.getName() + " " + ChatFormatting.GRAY + (this.setting.getValue() instanceof Float ? this.setting.getValue() : Double.valueOf((this.setting.getValue()).doubleValue())), this.x + 2.3f, this.y - 1.7f - (float) FrosGui.getClickGui().getTextOffset(), -1);
drawString(context, this.getName() + " " + ChatFormatting.GRAY + (this.setting.getValue() instanceof Float ? this.setting.getValue() : Double.valueOf((this.setting.getValue()).doubleValue())), this.x + 2.3f, textY(), -1);
}
@Override
@@ -1,7 +1,6 @@
package cc.fros.features.gui.items.buttons;
import cc.fros.Fros;
import cc.fros.features.gui.FrosGui;
import cc.fros.features.modules.client.ClickGuiModule;
import cc.fros.features.settings.Setting;
import cc.fros.util.models.Timer;
@@ -38,9 +37,9 @@ public class StringButton
public void drawScreen(GuiGraphics context, int mouseX, int mouseY, float partialTicks) {
RenderUtil.rect(context, this.x, this.y, this.x + (float) this.width + 7.4f, this.y + (float) this.height - 0.5f, this.getState() ? (!this.isHovering(mouseX, mouseY) ? Fros.colorManager.getColorWithAlpha(y, ClickGuiModule.getInstance().color.getValue().getAlpha()) : Fros.colorManager.getColorWithAlpha(y, ClickGuiModule.getInstance().topColor.getValue().getAlpha())) : (!this.isHovering(mouseX, mouseY) ? 0x11555555 : -2007673515));
if (this.isListening) {
drawString(context, this.currentString.string() + getIdleSign(), this.x + 2.3f, this.y - 1.7f - (float) FrosGui.getClickGui().getTextOffset(), this.getState() ? -1 : -5592406);
drawString(context, this.currentString.string() + getIdleSign(), this.x + 2.3f, textY(), this.getState() ? -1 : -5592406);
} else {
drawString(context, (this.setting.getName().equals("Buttons") ? "Buttons " : (this.setting.getName().equals("Prefix") ? "Prefix " + ChatFormatting.GRAY : "")) + this.setting.getValue(), this.x + 2.3f, this.y - 1.7f - (float) FrosGui.getClickGui().getTextOffset(), this.getState() ? -1 : -5592406);
drawString(context, (this.setting.getName().equals("Buttons") ? "Buttons " : (this.setting.getName().equals("Prefix") ? "Prefix " + ChatFormatting.GRAY : "")) + this.setting.getValue(), this.x + 2.3f, textY(), this.getState() ? -1 : -5592406);
}
}
@@ -83,6 +83,8 @@ public final class FrosFont {
private static String loadedName;
private static float loadedSize = Float.NaN;
private static float loadedScale = 1;
/** raster pixels from the top of a line box to the middle of the cap band */
private static float visualCenterRaster = 4f;
private static long lastAttempt;
private static int glyphCounter;
@@ -218,10 +220,33 @@ public final class FrosFont {
Graphics2D graphics = probe.createGraphics();
graphics.setFont(awtFont);
metrics = graphics.getFontMetrics();
// distance from the top of the line box to the middle of the cap band:
// drawString puts the baseline at metrics.getAscent(), and "A" spans
// from the baseline up to the cap line
java.awt.geom.Rectangle2D capBounds = awtFont
.createGlyphVector(graphics.getFontRenderContext(), "A")
.getVisualBounds();
float capHeight = (float) capBounds.getHeight();
if (capHeight <= 0f || capHeight > metrics.getAscent()) {
capHeight = metrics.getAscent();
}
visualCenterRaster = metrics.getAscent() - capHeight / 2f;
graphics.dispose();
if (verbose) LOGGER.info("{} ({}pt)", source, size);
}
/**
* Distance from the top of a line box down to the visual center of its
* text (middle of the cap band), in gui units. lets callers center labels
* by what the eye sees instead of the line box, and behaves the same for
* the custom font and the vanilla fallback.
*/
public static float visualCenter() {
sync();
if (awtFont == null) return mc.font.lineHeight / 2f - 1f;
return visualCenterRaster / loadedScale;
}
/**
* Resolves java's logical fonts (guaranteed on every os) and any font
* family already installed on the system, without needing a file.