fix(panel): unstick PanelClickGui layout & icons

修复 PanelClickGui 错位:
- 按 recaf 完全复刻 drawSearchBar (变量顺序/分支/常量回到原版)
- 同步精简 PanelClickGui 主体: 去掉死的 static block、Renderer.render lambda 包装、drawPanelGlow/drawSearchBar 多余的 RenderUtil.drawBlurredRect、render() 里的重复变量赋值
- ModuleListPanel 第一行起点偏移 +2*scale -> +8*scale, 解决反混淆后的 GlyphMetrics 让 GlHelper.drawText 视觉顶部高于 drawY 导致第一个 module 名字被 clip 上沿裁掉的问题; onMouseClick 命中盒同步下移
- NeverloseWatermark: 补齐 6 个 Material Icons 字形 (\ue8a6 / \ue8b8 / \uebca / \ueb66 / \ue0c8 / \ue192)
- FastUse.getTriple: keyName 由 "" 改为 null, KeyBindsHud 才会画默认 \ue9f6 key 图标

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
Shirona1337
2026-05-23 07:36:32 +08:00
parent fe1fdea994
commit f9068db44e
5 changed files with 80 additions and 96 deletions
-2
View File
@@ -15,8 +15,6 @@
![Open Zen ClickGUI](img/screenshot.png)
## TODO
- 客户端的Shader渲染明显还有很多问题。
- 部分UI错位。
- 直接构建为热注入DLL。
- 你来提出Issue,我来解决。
+60 -71
View File
@@ -4,6 +4,8 @@ import java.awt.Color;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import javax.annotation.Nonnull;
import lombok.Getter;
import net.minecraft.client.gui.GuiGraphics;
import net.minecraft.client.gui.screens.Screen;
import net.minecraft.network.chat.Component;
@@ -63,8 +65,11 @@ extends Screen {
private float openProgress = 0.0f;
private float currentScale = 1.0f;
private final ProfileWidget profileWidget;
@Getter
private final CategoryBar categoryBar;
@Getter
private final ModuleListPanel moduleListPanel;
@Getter
private final SettingsPanel settingsPanel;
public final KeybindOverlay keybindOverlay = new KeybindOverlay();
private final ScaleSwitchOverlay scaleSwitchOverlay = new ScaleSwitchOverlay();
@@ -121,15 +126,14 @@ extends Screen {
}
public void render(@Nonnull GuiGraphics guiGraphics, int mouseX, int mouseY, float partialTicks) {
float eased;
LerpUtil.update();
this.updateOpenState();
if (this.currentScaleSwitchState == PanelClickGui.OpenState.CLOSED && this.openProgress <= 0.0f) {
return;
}
float dup = eased = this.easeOutCubic(this.openProgress);
float eased = this.easeOutCubic(this.openProgress);
float scaleFactor = 0.98f + 0.02f * eased;
guiGraphics.fill(0, 0, this.width, this.height, new Color(0, 0, 0, (int)(80.0f * dup)).getRGB());
guiGraphics.fill(0, 0, this.width, this.height, new Color(0, 0, 0, (int)(80.0f * eased)).getRGB());
this.updateScaleSwitchState();
int centerX = this.width / 2;
int centerY = this.height / 2;
@@ -137,25 +141,22 @@ extends Screen {
guiGraphics.pose().translate((float)centerX, (float)centerY, 0.0f);
guiGraphics.pose().scale(scaleFactor, scaleFactor, 1.0f);
guiGraphics.pose().translate((float)(-centerX), (float)(-centerY), 0.0f);
float overlayScale = this.currentOpenState == PanelClickGui.ScaleSwitchState.FADING_OUT || this.currentOpenState == PanelClickGui.ScaleSwitchState.WAITING ? this.targetScale : this.currentScale;
float overlayScale = this.currentOpenState == PanelClickGui.ScaleSwitchState.FADING_OUT
|| this.currentOpenState == PanelClickGui.ScaleSwitchState.WAITING
? this.targetScale : this.currentScale;
int panelWidth = (int)(600.0f * this.currentScale);
int panelHeight = (int)(400.0f * this.currentScale);
int panelX = centerX - panelWidth / 2;
int panelY = centerY - panelHeight / 2;
float effectiveAlpha = this.panelAlpha * dup;
float effectiveAlpha = this.panelAlpha * eased;
if (effectiveAlpha > 0.005f) {
final float finalAlpha = effectiveAlpha;
final int fPanelX = panelX;
final int fPanelY = panelY;
Renderer.render(guiGraphics, drawContext -> {
this.drawPanelGlow(guiGraphics, fPanelX, fPanelY, partialTicks, finalAlpha);
this.categoryBar.render(guiGraphics, fPanelX, fPanelY, mouseX, mouseY, this.currentScale, finalAlpha);
this.moduleListPanel.render(guiGraphics, fPanelX, fPanelY, mouseX, mouseY, this.getSelectedCategory(), this.currentScale, finalAlpha);
this.settingsPanel.render(guiGraphics, fPanelX, fPanelY, mouseX, mouseY, this.moduleListPanel.getHoveredModule(), this.currentScale, finalAlpha);
this.profileWidget.render(guiGraphics, fPanelX, fPanelY, mouseX, mouseY, this.currentScale, finalAlpha);
this.drawSearchBar(guiGraphics, fPanelX, fPanelY, mouseX, mouseY, finalAlpha);
this.drawToasts(guiGraphics, fPanelX, fPanelY, finalAlpha);
});
this.drawPanelGlow(guiGraphics, panelX, panelY, partialTicks, effectiveAlpha);
this.categoryBar.render(guiGraphics, panelX, panelY, mouseX, mouseY, this.currentScale, effectiveAlpha);
this.moduleListPanel.render(guiGraphics, panelX, panelY, mouseX, mouseY, this.getSelectedCategory(), this.currentScale, effectiveAlpha);
this.settingsPanel.render(guiGraphics, panelX, panelY, mouseX, mouseY, this.moduleListPanel.getHoveredModule(), this.currentScale, effectiveAlpha);
this.profileWidget.render(guiGraphics, panelX, panelY, mouseX, mouseY, this.currentScale, effectiveAlpha);
this.drawSearchBar(guiGraphics, panelX, panelY, mouseX, mouseY, effectiveAlpha);
this.drawToasts(guiGraphics, panelX, panelY, effectiveAlpha);
}
super.render(guiGraphics, mouseX, mouseY, partialTicks);
guiGraphics.pose().popPose();
@@ -167,22 +168,25 @@ extends Screen {
switch (this.currentOpenState) {
case FADING_OUT: {
this.panelAlpha = LerpUtil.lerp(this.panelAlpha, 0.0f, 0.08f);
if (!(this.panelAlpha <= 0.0f)) break;
this.currentOpenState = PanelClickGui.ScaleSwitchState.WAITING;
this.scaleWaitStart = System.currentTimeMillis();
if (this.panelAlpha <= 0.0f) {
this.currentOpenState = PanelClickGui.ScaleSwitchState.WAITING;
this.scaleWaitStart = System.currentTimeMillis();
}
break;
}
case WAITING: {
if (System.currentTimeMillis() - this.scaleWaitStart <= 3000L) break;
this.currentScale = this.targetScale;
this.currentOpenState = PanelClickGui.ScaleSwitchState.FADING_IN;
this.scaleSwitchOverlay.hide();
if (System.currentTimeMillis() - this.scaleWaitStart > 3000L) {
this.currentScale = this.targetScale;
this.currentOpenState = PanelClickGui.ScaleSwitchState.FADING_IN;
this.scaleSwitchOverlay.hide();
}
break;
}
case FADING_IN: {
this.panelAlpha = LerpUtil.lerp(this.panelAlpha, 1.0f, 0.08f);
if (!(this.panelAlpha >= 1.0f) || !this.scaleSwitchOverlay.isFullyHidden()) break;
this.currentOpenState = PanelClickGui.ScaleSwitchState.IDLE;
if (this.panelAlpha >= 1.0f && this.scaleSwitchOverlay.isFullyHidden()) {
this.currentOpenState = PanelClickGui.ScaleSwitchState.IDLE;
}
}
}
}
@@ -190,13 +194,10 @@ extends Screen {
private void drawPanelGlow(GuiGraphics guiGraphics, int panelX, int panelY, float partialTicks, float alpha) {
int panelWidth = (int)(600.0f * this.currentScale);
int panelHeight = (int)(400.0f * this.currentScale);
float radius = 12.0f * this.currentScale;
RenderUtil.drawBlurredRect(guiGraphics.pose(), panelX, panelY, panelWidth, panelHeight, radius, 8.0f, alpha, 0);
TextGlow.drawBackground(guiGraphics.pose(), panelX, panelY, panelWidth, panelHeight, radius, alpha);
TextGlow.drawBackground(guiGraphics.pose(), panelX, panelY, panelWidth, panelHeight, 12.0f * this.currentScale, alpha);
}
public boolean mouseClicked(double mouseX, double mouseY, int button) {
boolean overSearch;
if (this.openProgress < 1.0f) {
return true;
}
@@ -219,7 +220,8 @@ extends Screen {
int searchHeight = (int)(20.0f * this.currentScale);
int searchX = panelX + (panelWidth - searchWidth) / 2;
int searchY = panelY + panelHeight + (int)(15.0f * this.currentScale);
boolean dup = overSearch = mouseX >= (double)searchX && mouseX <= (double)(searchX + searchWidth) && mouseY >= (double)searchY && mouseY <= (double)(searchY + searchHeight);
boolean overSearch = mouseX >= (double)searchX && mouseX <= (double)(searchX + searchWidth)
&& mouseY >= (double)searchY && mouseY <= (double)(searchY + searchHeight);
if (overSearch) {
if (!this.searchActive) {
this.searchActive = true;
@@ -233,7 +235,7 @@ extends Screen {
this.searchFocused = false;
boolean handled = false;
if (button == 0 || button == 1 || button == 2) {
if (!handled && button == 0 && this.categoryBar.onMouseClick(panelX, panelY, (int)mouseX, (int)mouseY, this.currentScale)) {
if (button == 0 && this.categoryBar.onMouseClick(panelX, panelY, (int)mouseX, (int)mouseY, this.currentScale)) {
if (this.searchActive) {
this.searchActive = false;
this.searchFocused = false;
@@ -273,8 +275,8 @@ extends Screen {
return true;
}
int centerX = this.width / 2;
int panelX = centerX - (int)(600.0f * this.currentScale) / 2;
int centerY = this.height / 2;
int panelX = centerX - (int)(600.0f * this.currentScale) / 2;
int panelY = centerY - (int)(400.0f * this.currentScale) / 2;
if (this.moduleListPanel.isMouseOverPanel(panelX, panelY, (int)mouseX, (int)mouseY, this.currentScale)) {
this.moduleListPanel.onScroll(scrollDelta, this.currentScale);
@@ -357,18 +359,6 @@ extends Screen {
return this.categoryBar.getSelectedCategory();
}
public CategoryBar getCategoryBar() {
return this.categoryBar;
}
public ModuleListPanel getModuleListPanel() {
return this.moduleListPanel;
}
public SettingsPanel getSettingsPanel() {
return this.settingsPanel;
}
private void drawSearchBar(GuiGraphics guiGraphics, int panelX, int panelY, int mouseX, int mouseY, float alpha) {
try {
int panelWidth = (int)(600.0f * this.currentScale);
@@ -377,39 +367,42 @@ extends Screen {
int searchHeight = (int)(20.0f * this.currentScale);
int searchX = panelX + (panelWidth - searchWidth) / 2;
int searchY = panelY + panelHeight + (int)(15.0f * this.currentScale);
float searchRadius = 8.0f * this.currentScale;
RenderUtil.drawBlurredRect(guiGraphics.pose(), searchX, searchY, searchWidth, searchHeight, searchRadius, 8.0f, alpha, 0);
TextGlow.drawBackground(guiGraphics.pose(), searchX, searchY, searchWidth, searchHeight, searchRadius, alpha);
Renderer.renderConsumer((drawContext -> {
TextGlow.drawBackground(guiGraphics.pose(), searchX, searchY, searchWidth, searchHeight, 8.0f * this.currentScale, alpha);
Renderer.renderConsumer(drawContext -> {
FontRenderer iconFont = FontPresets.materialIcons(20.0f * this.currentScale);
String iconText = "\uE8B6";
float iconX = (float)searchX + 12.0f * this.currentScale;
float iconY = (float)searchY + (float)searchHeight / 2.0f + iconFont.getMetrics().capHeight() / 2.0f - 12.0f * this.currentScale;
float iconX = searchX + 12.0f * this.currentScale;
float iconY = searchY + searchHeight / 2.0f + iconFont.getMetrics().capHeight() / 2.0f - 12.0f * this.currentScale + 10F;
int iconColor = new Color(255, 255, 255, (int)(180.0f * alpha)).getRGB();
TextGlow.drawGlowText(iconText, iconX, iconY, iconFont, -2236963, iconColor, 12.0f * this.currentScale);
TextGlow.drawGlowText(iconText, iconX, iconY, iconFont, 0xffdddddd, iconColor, 12.0f * this.currentScale);
FontRenderer queryFont = FontPresets.axiformaRegular(16.0f * this.currentScale);
if (this.searchActive || !this.searchQuery.isEmpty()) {
float queryX = (float)searchX + 35.0f * this.currentScale;
float queryY = (float)searchY + (float)searchHeight / 2.0f + queryFont.getMetrics().capHeight() / 2.0f - 9.0f * this.currentScale;
if (!this.searchActive && this.searchQuery.isEmpty()) {
FontRenderer placeholderFont = FontPresets.axiformaBold(14.0f * this.currentScale);
String placeholder = "search";
float placeholderWidth = GlHelper.getStringWidth(placeholder, placeholderFont);
float placeholderX = searchX + (searchWidth - placeholderWidth) / 2.0f;
float placeholderY = searchY + searchHeight / 2.0f + placeholderFont.getMetrics().capHeight() / 2.0f - 8.0f * this.currentScale + 5F;
TextGlow.drawGlowText(placeholder, placeholderX, placeholderY, placeholderFont, 0xffcccccc,
new Color(255, 255, 255, (int)(130.0f * alpha)).getRGB(), 10.0f * this.currentScale);
} else {
float queryX = searchX + 35.0f * this.currentScale;
float queryY = searchY + searchHeight / 2.0f + queryFont.getMetrics().capHeight() / 2.0f - 9.0f * this.currentScale + 6F;
int queryColor = new Color(255, 255, 255, (int)(120.0f * alpha)).getRGB();
TextGlow.drawGlowText(this.searchQuery, queryX, queryY, queryFont, -1, queryColor, 8.0f * this.currentScale);
if (this.searchFocused) {
long sinceCursor = System.currentTimeMillis() - this.searchCursorTime;
float blinkAmount = (float)(Math.sin((double)sinceCursor / 200.0) * 0.5 + 0.5);
float blinkAmount = (float)(Math.sin(sinceCursor / 200.0) * 0.5 + 0.5);
int cursorColor = (int)(blinkAmount * alpha * 255.0f) << 24 | 0xFFFFFF;
float cursorX = queryX + GlHelper.getStringWidth(this.searchQuery, queryFont) + 2.0f * this.currentScale;
float cursorHeight = queryFont.getMetrics().capHeight();
RenderUtil.drawFilledRect(guiGraphics.pose(), cursorX, queryY - cursorHeight + 11.0f * this.currentScale, this.currentScale, cursorHeight - 3.0f * this.currentScale, cursorColor);
RenderUtil.drawFilledRect(guiGraphics.pose(), cursorX,
queryY - cursorHeight + 3.0f * this.currentScale,
this.currentScale,
cursorHeight + 2 * this.currentScale,
cursorColor);
}
} else {
FontRenderer placeholderFont = FontPresets.axiformaBold(14.0f * this.currentScale);
String placeholder = "search";
float placeholderWidth = GlHelper.getStringWidth(placeholder, placeholderFont);
float placeholderX = (float)searchX + ((float)searchWidth - placeholderWidth) / 2.0f;
float placeholderY = (float)searchY + (float)searchHeight / 2.0f + placeholderFont.getMetrics().capHeight() / 2.0f - 8.0f * this.currentScale;
TextGlow.drawGlowText(placeholder, placeholderX, placeholderY, placeholderFont, -3355444, new Color(255, 255, 255, (int)(130.0f * alpha)).getRGB(), 10.0f * this.currentScale);
}
}));
});
} catch (Exception exception) {
// empty catch block
}
@@ -431,7 +424,7 @@ extends Screen {
return;
}
try {
Renderer.renderConsumer((drawContext -> {
Renderer.renderConsumer(drawContext -> {
FontRenderer toastFont = FontPresets.axiformaBold(18.0f * this.currentScale);
for (PanelClickGui.ToastEntry toast : this.toasts) {
long elapsed = System.currentTimeMillis() - toast.createdAt;
@@ -458,13 +451,9 @@ extends Screen {
int glowColor = glowAlpha << 24 | 0xFFFFFF;
TextGlow.drawGlowText(toast.message, toastX, toastY + 6.0f * this.currentScale, toastFont, textColor, glowColor, 8.0f * this.currentScale);
}
}));
});
} catch (Exception exception) {
// empty catch block
}
}
static {
PanelClickGui panelClickGui = new PanelClickGui();
}
}
@@ -227,7 +227,10 @@ extends ClientBase {
DrawContext drawContext = GlHelper.getCanvas();
drawContext.save();
drawContext.clip(Rectangle.ofXYWH(panelX, (float)panelY + headerHeight, panelWidth, (float)panelHeight - headerHeight));
int rowY = panelY + (int)headerHeight + (int)(2.0f * scale);
// Recaf used +2*scale here, but the deobfuscated GlyphMetrics flips the ascent sign so
// GlHelper.drawText draws ~7*scale ABOVE drawY — at +2 the first row's text top slips out
// of the clip. +8 leaves ~1*scale headroom under the clip top.
int rowY = panelY + (int)headerHeight + (int)(8.0f * scale);
if (modules != null) {
this.totalContentHeight = modules.size() * Math.round(18.0f * scale);
for (Module module : modules) {
@@ -329,7 +332,9 @@ extends ClientBase {
if (!this.isMouseOverPanel(originX, originY, mouseX, mouseY, scale)) {
return false;
}
int rowY = panelY + headerHeight + Math.round(2.0f * scale);
// Keep in sync with renderModuleList's rowY offset (+8*scale) so click hit-boxes match
// the visible row positions.
int rowY = panelY + headerHeight + Math.round(8.0f * scale);
for (Module module : modules) {
int adjustedRowY;
if (this.isMouseOverModule(module, panelX, adjustedRowY = (int)((float)rowY - this.scrollOffset), mouseX, mouseY, scale)) {
@@ -38,12 +38,12 @@ public class NeverloseWatermark {
float blurRadius = 15.0f;
float zenWidth = this.measureText("ZEN", this.boldFont);
currentX += zenWidth + gap;
currentX = this.renderSectionLegacy(render2DEvent.poseStack(), currentX, y, this.getServerName(), this.smallFont, "", cornerRadius, blurRadius, gap);
currentX = this.renderSectionLegacy(render2DEvent.poseStack(), currentX, y, this.getPingText(), this.regularFont, "", cornerRadius, blurRadius, gap);
currentX = this.renderSectionLegacy(render2DEvent.poseStack(), currentX, y, this.getFpsText(), this.regularFont, "", cornerRadius, blurRadius, gap);
currentX = this.renderSectionLegacy(render2DEvent.poseStack(), currentX, y, this.getTimeText(), this.regularFont, "", cornerRadius, blurRadius, gap);
currentX = this.renderSectionLegacy(render2DEvent.poseStack(), currentX, y, this.getCpsText(), this.regularFont, "", cornerRadius, blurRadius, gap);
this.renderSectionLegacy(render2DEvent.poseStack(), currentX, y, this.getCoordText(), this.regularFont, "", cornerRadius, blurRadius, gap);
currentX = this.renderSectionLegacy(render2DEvent.poseStack(), currentX, y, this.getServerName(), this.smallFont, "\uE8A6", cornerRadius, blurRadius, gap);
currentX = this.renderSectionLegacy(render2DEvent.poseStack(), currentX, y, this.getPingText(), this.regularFont, "\uE8B8", cornerRadius, blurRadius, gap);
currentX = this.renderSectionLegacy(render2DEvent.poseStack(), currentX, y, this.getFpsText(), this.regularFont, "\uEBCA", cornerRadius, blurRadius, gap);
currentX = this.renderSectionLegacy(render2DEvent.poseStack(), currentX, y, this.getTimeText(), this.regularFont, "\uEB66", cornerRadius, blurRadius, gap);
currentX = this.renderSectionLegacy(render2DEvent.poseStack(), currentX, y, this.getCpsText(), this.regularFont, "\uE0C8", cornerRadius, blurRadius, gap);
this.renderSectionLegacy(render2DEvent.poseStack(), currentX, y, this.getCoordText(), this.regularFont, "\uE192", cornerRadius, blurRadius, gap);
}
private float renderSectionLegacy(PoseStack poseStack, float x, float y, String text, FontRenderer fontRenderer, String icon, float cornerRadius, float blurRadius, float gap) {
@@ -61,12 +61,12 @@ public class NeverloseWatermark {
float gap = 6.0f;
float cornerRadius = 4.5f;
currentX = this.renderSection(glRenderEvent.drawContext(), currentX, y, "ZEN", this.boldFont, cornerRadius, gap);
currentX = this.renderSectionWithSub(glRenderEvent.drawContext(), currentX, y, this.getServerName(), this.smallFont, "", cornerRadius, gap);
currentX = this.renderSectionWithSub(glRenderEvent.drawContext(), currentX, y, this.getPingText(), this.regularFont, "", cornerRadius, gap);
currentX = this.renderSectionWithSub(glRenderEvent.drawContext(), currentX, y, this.getFpsText(), this.regularFont, "", cornerRadius, gap);
currentX = this.renderSectionWithSub(glRenderEvent.drawContext(), currentX, y, this.getTimeText(), this.regularFont, "", cornerRadius, gap);
currentX = this.renderSectionWithSub(glRenderEvent.drawContext(), currentX, y, this.getCpsText(), this.regularFont, "", cornerRadius, gap);
this.renderSectionWithSub(glRenderEvent.drawContext(), currentX, y, this.getCoordText(), this.regularFont, "", cornerRadius, gap);
currentX = this.renderSectionWithSub(glRenderEvent.drawContext(), currentX, y, this.getServerName(), this.smallFont, "", cornerRadius, gap);
currentX = this.renderSectionWithSub(glRenderEvent.drawContext(), currentX, y, this.getPingText(), this.regularFont, "", cornerRadius, gap);
currentX = this.renderSectionWithSub(glRenderEvent.drawContext(), currentX, y, this.getFpsText(), this.regularFont, "", cornerRadius, gap);
currentX = this.renderSectionWithSub(glRenderEvent.drawContext(), currentX, y, this.getTimeText(), this.regularFont, "", cornerRadius, gap);
currentX = this.renderSectionWithSub(glRenderEvent.drawContext(), currentX, y, this.getCpsText(), this.regularFont, "", cornerRadius, gap);
this.renderSectionWithSub(glRenderEvent.drawContext(), currentX, y, this.getCoordText(), this.regularFont, "", cornerRadius, gap);
}
private float renderSection(DrawContext drawContext, float x, float y, String text, FontRenderer fontRenderer, float cornerRadius, float gap) {
@@ -49,7 +49,7 @@ import shit.zen.utils.misc.Triple;
import shit.zen.utils.misc.TripleProvider;
import shit.zen.event.EventTarget;
public class FastUse extends Module implements TripleProvider {
public class FastUse extends Module {
public static FastUse INSTANCE;
public static boolean releaseItemSent;
@@ -272,14 +272,6 @@ public class FastUse extends Module implements TripleProvider {
}
}
@Override
public Triple<String, String, Boolean> getTriple() {
if (this.isEnabled()) {
return new Triple<>(this.getName(), "", true);
}
return null;
}
private void handleGrimSlowdown(SlowdownEvent event, ItemStack stack) {
Item item = stack.getItem();
boolean isBow = item instanceof BowItem;