From 19c8c4bc0aec4b9d0067185418e84e68da492856 Mon Sep 17 00:00:00 2001 From: Serendisand <2742754621@qq.com> Date: Mon, 3 Aug 2026 21:21:46 +0800 Subject: [PATCH 1/6] Update tasks.md --- docs/tasks.md | 1 - 1 file changed, 1 deletion(-) diff --git a/docs/tasks.md b/docs/tasks.md index 864d071f..50a52202 100644 --- a/docs/tasks.md +++ b/docs/tasks.md @@ -40,7 +40,6 @@ FPSMaster v4将会在此列表任务大部分完成后发布 - [ ] 添加异步光照性能优化 - [x] 添加自定义迷雾(颜色,距离等) - [ ] 按[性能优化路线图](performance-roadmap.md)推进区块、实体、光照、纹理和画质裁剪工作 -- [ ] 添加自定义迷雾(颜色,距离等) - [ ] 为客户端的组件颜色选择添加彩色选项 - [x] 在聊天栏显示头像 - [ ] HypixelMod(合并AutoGG,AutoPlay以及聊天栏实用功能等) From adcff28a8f0a05b6b26623fe9356736890be855e Mon Sep 17 00:00:00 2001 From: Ukiyograin Date: Mon, 3 Aug 2026 22:02:58 +0800 Subject: [PATCH 2/6] =?UTF-8?q?feat:=20ClickGUI=20=E6=96=B0=E5=A2=9E?= =?UTF-8?q?=E5=85=A8=E5=B1=80=E6=90=9C=E7=B4=A2=EF=BC=8C=E6=94=AF=E6=8C=81?= =?UTF-8?q?=E6=8C=89=E5=8A=9F=E8=83=BD=E5=90=8D=E4=B8=8E=E6=8F=8F=E8=BF=B0?= =?UTF-8?q?=E6=90=9C=E7=B4=A2=E5=B9=B6=E9=AB=98=E4=BA=AE=E5=91=BD=E4=B8=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../top/fpsmaster/ui/click/MainPanel.java | 75 ++++++++++++- .../top/fpsmaster/ui/click/SearchBar.java | 105 ++++++++++++++++++ .../ui/click/modules/ModuleRenderer.java | 58 ++++++++-- .../top/fpsmaster/ui/common/TextField.java | 3 +- .../utils/render/gui/GuiInputState.java | 6 + .../utils/render/gui/ScaledGuiScreen.java | 10 ++ .../assets/minecraft/client/lang/en_us.lang | 2 + .../assets/minecraft/client/lang/zh_cn.lang | 2 + 8 files changed, 247 insertions(+), 14 deletions(-) create mode 100644 src/main/java/top/fpsmaster/ui/click/SearchBar.java diff --git a/src/main/java/top/fpsmaster/ui/click/MainPanel.java b/src/main/java/top/fpsmaster/ui/click/MainPanel.java index b2f38792..45eb0f98 100644 --- a/src/main/java/top/fpsmaster/ui/click/MainPanel.java +++ b/src/main/java/top/fpsmaster/ui/click/MainPanel.java @@ -33,6 +33,7 @@ import java.awt.*; import java.io.IOException; import java.util.LinkedList; +import java.util.Locale; public class MainPanel extends ScaledGuiScreen { boolean drag = false; @@ -51,6 +52,7 @@ public class MainPanel extends ScaledGuiScreen { private final ColorAnimator themeBtnAnim = new ColorAnimator(ClickGuiTheme.themeBtnBg()); private final ColorAnimator configBtnAnim = new ColorAnimator(ClickGuiTheme.themeBtnBg()); private final ColorAnimator musicBtnAnim = new ColorAnimator(ClickGuiTheme.themeBtnBg()); + private final SearchBar searchBar = new SearchBar(); private final AnimClock animClock = new AnimClock(); private static final BezierEasing CLICKGUI_EASE = BezierEasing.of(0.25, 0.1, 0.25, 1.0); private static final int MASK_MAX_ALPHA = 110; @@ -99,6 +101,41 @@ private float getCategoryStartY() { return getCategoryBgY() + 10f; } + // author:Serendisand + // reason:全局搜索 + private float getSearchBarX() { + return x + leftWidth + 10; + } + + private float getSearchBarY() { + return y + 6; + } + + private float getSearchBarWidth() { + return width - leftWidth - 20; + } + + private float getSearchBarHeight() { + return 16f; + } + + private boolean isSearchActive() { + return !searchBar.getQuery().trim().isEmpty(); + } + + private boolean matchesSearch(Module module, String rawQuery) { + String query = rawQuery.trim().toLowerCase(Locale.getDefault()); + if (query.isEmpty()) { + return false; + } + String key = module.name.toLowerCase(Locale.getDefault()); + String name = FPSMaster.i18n.get(key); + String desc = FPSMaster.i18n.get(key + ".desc"); + return name.toLowerCase(Locale.getDefault()).contains(query) + || module.name.toLowerCase(Locale.getDefault()).contains(query) + || desc.toLowerCase(Locale.getDefault()).contains(query); + } + @Override public void render(int mouseX, int mouseY, float partialTicks) { updateZoomModifierState(); @@ -153,6 +190,8 @@ public void render(int mouseX, int mouseY, float partialTicks) { 8, ClickGuiTheme.panelBg()); } + searchBar.draw(this, getSearchBarX(), getSearchBarY(), getSearchBarWidth(), getSearchBarHeight(), mouseX, mouseY); + moduleListAlpha = (float) AnimMath.base(moduleListAlpha, 255.0, 0.1f); float scale = (float) scaleAnimation.get(); @@ -171,10 +210,17 @@ public void render(int mouseX, int mouseY, float partialTicks) { modHeight = 20f; float containerWidth = width - leftWidth - 10; int finalMouseY = mouseY; + boolean searching = isSearchActive(); + String query = searchBar.getQuery(); modsContainer.draw(this, x + leftWidth, y + 25f, containerWidth, height - 20f, mouseX, mouseY, () -> { float modsY = y + 22f; + boolean anyMatch = false; for (ModuleRenderer m : mods) { - if (m.mod.category == curType) { + m.highlight = searching ? query.trim() : null; + m.searchMode = searching; + boolean show = searching ? matchesSearch(m.mod, query) : m.mod.category == curType; + if (show) { + anyMatch = true; float moduleY = modsY + modsContainer.getScroll(); if (moduleY + 40 + m.height > y && moduleY < y + height) { m.render( @@ -192,6 +238,14 @@ public void render(int mouseX, int mouseY, float partialTicks) { modHeight += 45 + m.height; } } + if (searching && !anyMatch) { + FPSMaster.fontManager.s16.drawCenteredString( + FPSMaster.i18n.get("clickgui.search.noresults"), + x + leftWidth + containerWidth / 2f, + y + 25f + (height - 20f) / 2f, + ClickGuiTheme.textDisabled().getRGB() + ); + } modsContainer.setHeight(modHeight); }); GL11.glEnable(GL11.GL_BLEND); @@ -360,6 +414,8 @@ public void initGui() { maskAlpha.start(0.0, MASK_MAX_ALPHA, 0.2f, CLICKGUI_EASE); close = false; configSavedOnClose = false; + searchBar.clear(); + searchBar.setFocused(false); // if (width == 0f || height == 0f) { // width = scaledWidth / 2f; @@ -390,6 +446,11 @@ public void onGuiClosed() { public void keyTyped(char typedChar, int keyCode) throws IOException { // aiChatPanel.keyTyped(typedChar, keyCode); + if (searchBar.isFocused()) { + searchBar.keyTyped(typedChar, keyCode); + return; + } + if (keyCode == 1) { if (scaleAnimation.isRunning() || scaleAnimation.get() != 0.7) { requestClose(); @@ -414,6 +475,15 @@ protected float[] getOccludingBounds() { } private void handlePointerPress() { + // author:Serendisand + // reason:全局搜索 + if (searchBar.isFocused()) { + ScaledGuiScreen.PointerEvent rawPress = peekRawPress(); + if (rawPress != null && !Hover.is(getSearchBarX(), getSearchBarY(), getSearchBarWidth(), getSearchBarHeight(), rawPress.x, rawPress.y)) { + searchBar.setFocused(false); + } + } + ScaledGuiScreen.PointerEvent press = peekAnyPress(); if (press == null) { return; @@ -435,6 +505,9 @@ private void handlePointerPress() { float my = getCategoryStartY(); for (Category c : Category.values()) { if (Hover.is(x, my - 8, leftWidth, 24f, mouseX, mouseY)) { + if (isSearchActive()) { + searchBar.clear(); + } wheelTemp = 0f; modsWheel = 0f; if (curType != c) { diff --git a/src/main/java/top/fpsmaster/ui/click/SearchBar.java b/src/main/java/top/fpsmaster/ui/click/SearchBar.java new file mode 100644 index 00000000..96fc1b4a --- /dev/null +++ b/src/main/java/top/fpsmaster/ui/click/SearchBar.java @@ -0,0 +1,105 @@ +package top.fpsmaster.ui.click; + +import top.fpsmaster.FPSMaster; +import top.fpsmaster.ui.common.TextField; +import top.fpsmaster.utils.math.anim.ColorAnimator; +import top.fpsmaster.utils.math.anim.Easings; +import top.fpsmaster.utils.render.draw.Hover; +import top.fpsmaster.utils.render.draw.Icons; +import top.fpsmaster.utils.render.draw.Rects; +import top.fpsmaster.utils.render.gui.ScaledGuiScreen; + +import java.awt.*; + +// author:Serendisand +// reason:全局搜索 +public class SearchBar { + private static final int TRANSPARENT = new Color(0, 0, 0, 0).getRGB(); + private static final float ICON_SIZE = 10f; + private static final float CLEAR_SIZE = 10f; + + private TextField field; + private final ColorAnimator bgAnim = new ColorAnimator(ClickGuiTheme.inputBg()); + + private void ensureField() { + if (field == null) { + field = new TextField( + FPSMaster.fontManager.s16, + false, + FPSMaster.i18n.get("clickgui.search.placeholder"), + TRANSPARENT, + ClickGuiTheme.textFieldText().getRGB(), + 64 + ); + field.setCanLoseFocus(false); + } + } + + public void draw(ScaledGuiScreen screen, float x, float y, float width, float height, int mouseX, int mouseY) { + ensureField(); + field.backGroundColor = TRANSPARENT; + field.fontColor = ClickGuiTheme.textFieldText().getRGB(); + field.placeHolder = FPSMaster.i18n.get("clickgui.search.placeholder"); + + boolean hovered = Hover.is(x, y, width, height, mouseX, mouseY); + bgAnim.animateTo(hovered ? ClickGuiTheme.sideBtnHoverBg() : ClickGuiTheme.inputBg(), 0.15, Easings.QUAD_OUT); + bgAnim.update(); + Rects.rounded(Math.round(x), Math.round(y), Math.round(width), Math.round(height), 4, bgAnim.get().getRGB()); + + Icons.draw("search", x + 6, y + (height - ICON_SIZE) / 2f, ICON_SIZE, ClickGuiTheme.textSecondary().getRGB()); + + boolean hasText = !field.getText().isEmpty(); + float clearZone = hasText ? 22f : 6f; + field.drawTextBox(x + 16, y + 1, width - 16 - clearZone, height - 2); + + if (hasText) { + float clearX = x + width - 18; + float clearY = y + (height - CLEAR_SIZE) / 2f; + boolean clearHover = Hover.is(clearX, clearY, 12, 12, mouseX, mouseY); + Icons.draw("close", clearX, clearY, CLEAR_SIZE, + clearHover ? ClickGuiTheme.textPrimary().getRGB() : ClickGuiTheme.textSecondary().getRGB()); + if (screen.consumePressInBounds(clearX, clearY, 12, 12) != null) { + field.setText(""); + setFocused(true); + } + } + + ScaledGuiScreen.PointerEvent press = screen.consumePressInBounds(x, y, width, height); + if (press != null) { + setFocused(true); + field.mouseClicked(press.x, press.y, 0); + } + } + + public boolean keyTyped(char typedChar, int keyCode) { + if (!isFocused()) { + return false; + } + if (keyCode == 1) { + setFocused(false); + return true; + } + field.textboxKeyTyped(typedChar, keyCode); + return true; + } + + public String getQuery() { + ensureField(); + return field.getText(); + } + + public boolean isFocused() { + ensureField(); + return field.isFocused(); + } + + public void setFocused(boolean focused) { + ensureField(); + field.setFocused(focused); + } + + public void clear() { + ensureField(); + field.setText(""); + } +} diff --git a/src/main/java/top/fpsmaster/ui/click/modules/ModuleRenderer.java b/src/main/java/top/fpsmaster/ui/click/modules/ModuleRenderer.java index c5fdd520..bd680a61 100644 --- a/src/main/java/top/fpsmaster/ui/click/modules/ModuleRenderer.java +++ b/src/main/java/top/fpsmaster/ui/click/modules/ModuleRenderer.java @@ -13,6 +13,7 @@ import java.io.IOException; import net.minecraft.client.Minecraft; import top.fpsmaster.FPSMaster; +import top.fpsmaster.font.impl.UFontRenderer; import top.fpsmaster.modules.logger.ClientLogger; import top.fpsmaster.features.manager.Category; import top.fpsmaster.features.manager.Module; @@ -79,6 +80,11 @@ public static void clearIconCache() { ColorAnimator option = new ColorAnimator(); float optionX = 0; + // author:Serendisand + // reason:全局搜索 + public String highlight = null; + public boolean searchMode = false; + public ModuleRenderer(Module module) { this.mod = module; content = new ColorAnimator(module.isEnabled() ? ClickGuiTheme.moduleContentEnabled() : ClickGuiTheme.moduleContentDisabled()); @@ -165,18 +171,20 @@ public void render(ScaledGuiScreen screen, float x, float y, float width, float Images.draw(icon, x + 14, y + 10, 14f, 14f, content.getColor().getRGB()); } - FPSMaster.fontManager.s18.drawString( - FPSMaster.i18n.get(mod.name.toLowerCase(Locale.getDefault())), - x + 40, - y + 9, - content.getColor().getRGB() - ); - FPSMaster.fontManager.s16.drawString( - FPSMaster.i18n.get(mod.name.toLowerCase(Locale.getDefault()) + ".desc"), - x + 40, - y + 20, - ClickGuiTheme.textDescription().getRGB() - ); + String name = FPSMaster.i18n.get(mod.name.toLowerCase(Locale.getDefault())); + String desc = FPSMaster.i18n.get(mod.name.toLowerCase(Locale.getDefault()) + ".desc"); + drawHighlighted(name, FPSMaster.fontManager.s18, x + 40, y + 9, content.getColor().getRGB()); + drawHighlighted(desc, FPSMaster.fontManager.s16, x + 40, y + 20, ClickGuiTheme.textDescription().getRGB()); + + if (searchMode) { + String categoryName = FPSMaster.i18n.get("category." + mod.category.name().toLowerCase(Locale.getDefault())); + float tagWidth = FPSMaster.fontManager.s14.getStringWidth(categoryName); + float nameWidth = FPSMaster.fontManager.s18.getStringWidth(name); + float tagX = x + width - 52 - tagWidth; + if (tagX >= x + 40 + nameWidth + 10) { + FPSMaster.fontManager.s14.drawString(categoryName, tagX, y + 9, ClickGuiTheme.accent().getRGB()); + } + } float settingsHeight = 0f; if (expand) { @@ -213,6 +221,32 @@ public void render(ScaledGuiScreen screen, float x, float y, float width, float } } + // author:Serendisand + // reason:全局搜索 + private void drawHighlighted(String text, UFontRenderer font, float x, float y, int color) { + if (highlight == null || highlight.isEmpty()) { + font.drawString(text, x, y, color); + return; + } + int index = text.toLowerCase(Locale.getDefault()).indexOf(highlight.toLowerCase(Locale.getDefault())); + if (index < 0) { + font.drawString(text, x, y, color); + return; + } + String before = text.substring(0, index); + String match = text.substring(index, index + highlight.length()); + String after = text.substring(index + highlight.length()); + float cursor = x; + if (!before.isEmpty()) { + cursor += font.drawString(before, cursor, y, color); + } + font.drawString(match, cursor, y, ClickGuiTheme.accent().getRGB()); + cursor += font.getStringWidth(match); + if (!after.isEmpty()) { + font.drawString(after, cursor, y, color); + } + } + @Override public void keyTyped(char typedChar, int keyCode) { if (expand) { diff --git a/src/main/java/top/fpsmaster/ui/common/TextField.java b/src/main/java/top/fpsmaster/ui/common/TextField.java index 8677b5f7..cad97c03 100644 --- a/src/main/java/top/fpsmaster/ui/common/TextField.java +++ b/src/main/java/top/fpsmaster/ui/common/TextField.java @@ -11,6 +11,7 @@ import net.minecraft.util.ChatAllowedCharacters; import top.fpsmaster.font.impl.UFontRenderer; import top.fpsmaster.utils.math.MathUtils; +import top.fpsmaster.utils.render.draw.Colors; import java.awt.*; import java.util.Arrays; @@ -475,7 +476,7 @@ public void drawTextBox(float x, float y, float width, float height) { } if (s.isEmpty()){ - this.fontRendererInstance.drawString(placeHolder, j1, i1, new Color(200,200,200).getRGB()); + this.fontRendererInstance.drawString(placeHolder, j1, i1, Colors.alpha(new Color(fontColor), 120).getRGB()); } if (!s.isEmpty() && flag && j < s.length()) { diff --git a/src/main/java/top/fpsmaster/utils/render/gui/GuiInputState.java b/src/main/java/top/fpsmaster/utils/render/gui/GuiInputState.java index eed85ed0..d4d7a680 100644 --- a/src/main/java/top/fpsmaster/utils/render/gui/GuiInputState.java +++ b/src/main/java/top/fpsmaster/utils/render/gui/GuiInputState.java @@ -101,6 +101,12 @@ public MouseButtonEvent getLatestPress() { return latestPress != null && latestPress.isConsumed() ? null : latestPress; } + // author:Serendisand + // reason:全局搜索 + public MouseButtonEvent peekRawLatestPress() { + return latestPress; + } + /** * Records that the cursor is inside this widget, overwriting whatever was recorded earlier in the * frame. diff --git a/src/main/java/top/fpsmaster/utils/render/gui/ScaledGuiScreen.java b/src/main/java/top/fpsmaster/utils/render/gui/ScaledGuiScreen.java index 85ee33a8..bbd0fa89 100644 --- a/src/main/java/top/fpsmaster/utils/render/gui/ScaledGuiScreen.java +++ b/src/main/java/top/fpsmaster/utils/render/gui/ScaledGuiScreen.java @@ -286,6 +286,16 @@ public PointerEvent peekAnyPress() { return new PointerEvent(latestPress.getX(), latestPress.getY(), latestPress.getButton()); } + // author:Serendisand + // reason:全局搜索 + public PointerEvent peekRawPress() { + GuiInputState.MouseButtonEvent latestPress = inputState.peekRawLatestPress(); + if (latestPress == null) { + return null; + } + return new PointerEvent(latestPress.getX(), latestPress.getY(), latestPress.getButton()); + } + public PointerEvent consumePressInBounds(float x, float y, float width, float height) { return consumePressInBounds(x, y, width, height, -1); } diff --git a/src/main/resources/assets/minecraft/client/lang/en_us.lang b/src/main/resources/assets/minecraft/client/lang/en_us.lang index ecf85af9..2c7581af 100644 --- a/src/main/resources/assets/minecraft/client/lang/en_us.lang +++ b/src/main/resources/assets/minecraft/client/lang/en_us.lang @@ -960,3 +960,5 @@ chat.irc=Client chat.mc=Vanilla replay=Replay replay.desc=Record a session and play it back +clickgui.search.placeholder=Search modules... +clickgui.search.noresults=No matching modules diff --git a/src/main/resources/assets/minecraft/client/lang/zh_cn.lang b/src/main/resources/assets/minecraft/client/lang/zh_cn.lang index 9e149223..e5a79fba 100644 --- a/src/main/resources/assets/minecraft/client/lang/zh_cn.lang +++ b/src/main/resources/assets/minecraft/client/lang/zh_cn.lang @@ -960,3 +960,5 @@ chat.irc=客户端 chat.mc=原版 replay=录像 replay.desc=录制并回放对局 +clickgui.search.placeholder=搜索功能 +clickgui.search.noresults=未找到匹配的功能 From 6ebfb983ff2ea5b9865c81e14a5a090976614ff0 Mon Sep 17 00:00:00 2001 From: Serendisand <2742754621@qq.com> Date: Mon, 3 Aug 2026 22:08:41 +0800 Subject: [PATCH 3/6] Update tasks.md --- docs/tasks.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/tasks.md b/docs/tasks.md index 50a52202..582e51d0 100644 --- a/docs/tasks.md +++ b/docs/tasks.md @@ -28,7 +28,7 @@ FPSMaster v4将会在此列表任务大部分完成后发布 - [ ] 跨版本框架 - [ ] 自定义客户端字体 - [ ] 自定义动画速率、曲线等 -- [ ] 全局搜索 +- [x] 全局搜索 - [x] 新的OOBE - [ ] 功能完整的饰品界面 - [x] AutoText From 607b7227986fdf9a90ce4c07be429b69bf11c0dd Mon Sep 17 00:00:00 2001 From: Ukiyograin Date: Mon, 3 Aug 2026 22:26:08 +0800 Subject: [PATCH 4/6] =?UTF-8?q?style(clickgui):=20=E6=90=9C=E7=B4=A2?= =?UTF-8?q?=E6=A1=86=E6=94=B9=E4=B8=BA=E8=83=B6=E5=9B=8A=E7=8A=B6=E5=B9=B6?= =?UTF-8?q?=E6=82=AC=E6=B5=AE=E4=BA=8E=20GUI=20=E5=8F=B3=E4=B8=8A=E6=96=B9?= =?UTF-8?q?=E9=9D=A0=E5=8F=B3=E5=AF=B9=E9=BD=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/top/fpsmaster/ui/click/MainPanel.java | 8 ++++---- src/main/java/top/fpsmaster/ui/click/SearchBar.java | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/main/java/top/fpsmaster/ui/click/MainPanel.java b/src/main/java/top/fpsmaster/ui/click/MainPanel.java index 45eb0f98..10c2cae3 100644 --- a/src/main/java/top/fpsmaster/ui/click/MainPanel.java +++ b/src/main/java/top/fpsmaster/ui/click/MainPanel.java @@ -104,19 +104,19 @@ private float getCategoryStartY() { // author:Serendisand // reason:全局搜索 private float getSearchBarX() { - return x + leftWidth + 10; + return x + width + 8 - getSearchBarWidth(); } private float getSearchBarY() { - return y + 6; + return y - 3 - getSearchBarHeight(); } private float getSearchBarWidth() { - return width - leftWidth - 20; + return 130f; } private float getSearchBarHeight() { - return 16f; + return 18f; } private boolean isSearchActive() { diff --git a/src/main/java/top/fpsmaster/ui/click/SearchBar.java b/src/main/java/top/fpsmaster/ui/click/SearchBar.java index 96fc1b4a..75199cb5 100644 --- a/src/main/java/top/fpsmaster/ui/click/SearchBar.java +++ b/src/main/java/top/fpsmaster/ui/click/SearchBar.java @@ -44,7 +44,7 @@ public void draw(ScaledGuiScreen screen, float x, float y, float width, float he boolean hovered = Hover.is(x, y, width, height, mouseX, mouseY); bgAnim.animateTo(hovered ? ClickGuiTheme.sideBtnHoverBg() : ClickGuiTheme.inputBg(), 0.15, Easings.QUAD_OUT); bgAnim.update(); - Rects.rounded(Math.round(x), Math.round(y), Math.round(width), Math.round(height), 4, bgAnim.get().getRGB()); + Rects.rounded(Math.round(x), Math.round(y), Math.round(width), Math.round(height), Math.round(height / 2f), bgAnim.get().getRGB()); Icons.draw("search", x + 6, y + (height - ICON_SIZE) / 2f, ICON_SIZE, ClickGuiTheme.textSecondary().getRGB()); From c867db1462554badb0b6b0398a66f7e674c6e7d1 Mon Sep 17 00:00:00 2001 From: Ukiyograin Date: Tue, 4 Aug 2026 10:24:55 +0800 Subject: [PATCH 5/6] =?UTF-8?q?fix(clickgui):=20=E6=90=9C=E7=B4=A2?= =?UTF-8?q?=E6=A1=86=E5=8F=B3=E8=BE=B9=E7=BC=98=E4=B8=8E=E4=B8=BB=20GUI=20?= =?UTF-8?q?=E5=AF=B9=E9=BD=90=EF=BC=8C=E4=B8=AD=E6=96=87=E6=A8=A1=E5=BC=8F?= =?UTF-8?q?=E4=B8=8B=E4=BB=85=E6=A3=80=E7=B4=A2=E4=B8=AD=E6=96=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/top/fpsmaster/ui/click/MainPanel.java | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/src/main/java/top/fpsmaster/ui/click/MainPanel.java b/src/main/java/top/fpsmaster/ui/click/MainPanel.java index 10c2cae3..83a84f92 100644 --- a/src/main/java/top/fpsmaster/ui/click/MainPanel.java +++ b/src/main/java/top/fpsmaster/ui/click/MainPanel.java @@ -104,7 +104,7 @@ private float getCategoryStartY() { // author:Serendisand // reason:全局搜索 private float getSearchBarX() { - return x + width + 8 - getSearchBarWidth(); + return x + width - getSearchBarWidth(); } private float getSearchBarY() { @@ -131,9 +131,16 @@ private boolean matchesSearch(Module module, String rawQuery) { String key = module.name.toLowerCase(Locale.getDefault()); String name = FPSMaster.i18n.get(key); String desc = FPSMaster.i18n.get(key + ".desc"); - return name.toLowerCase(Locale.getDefault()).contains(query) - || module.name.toLowerCase(Locale.getDefault()).contains(query) - || desc.toLowerCase(Locale.getDefault()).contains(query); + if (name.toLowerCase(Locale.getDefault()).contains(query) + || desc.toLowerCase(Locale.getDefault()).contains(query)) { + return true; + } + // author:Serendisand + // reason:全局搜索 - 中文模式下只检索中文,不匹配英文原始名 + if (ClientSettings.language.getValue() == 1) { + return false; + } + return module.name.toLowerCase(Locale.getDefault()).contains(query); } @Override From 667bf51a04c6bc7d0a841540acb8f4992f7213af Mon Sep 17 00:00:00 2001 From: gaoyu06 Date: Tue, 4 Aug 2026 12:30:20 +0800 Subject: [PATCH 6/6] =?UTF-8?q?fix(clickgui):=20=E6=90=9C=E7=B4=A2?= =?UTF-8?q?=E9=AB=98=E4=BA=AE=E8=B6=8A=E7=95=8C=E4=BF=9D=E6=8A=A4,?= =?UTF-8?q?=E6=90=9C=E7=B4=A2=E6=BF=80=E6=B4=BB/=E9=80=80=E5=87=BA?= =?UTF-8?q?=E6=97=B6=E9=87=8D=E7=BD=AE=E6=BB=9A=E5=8A=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - drawHighlighted 大小写折叠改长度时截断匹配段,避免 StringIndexOutOfBounds - ScrollContainer 新增 resetScroll,搜索状态切换时回到列表顶部 --- src/main/java/top/fpsmaster/ui/click/MainPanel.java | 7 +++++++ .../fpsmaster/ui/click/component/ScrollContainer.java | 9 +++++++++ .../top/fpsmaster/ui/click/modules/ModuleRenderer.java | 9 ++++++--- 3 files changed, 22 insertions(+), 3 deletions(-) diff --git a/src/main/java/top/fpsmaster/ui/click/MainPanel.java b/src/main/java/top/fpsmaster/ui/click/MainPanel.java index 83a84f92..e2bdc326 100644 --- a/src/main/java/top/fpsmaster/ui/click/MainPanel.java +++ b/src/main/java/top/fpsmaster/ui/click/MainPanel.java @@ -53,6 +53,7 @@ public class MainPanel extends ScaledGuiScreen { private final ColorAnimator configBtnAnim = new ColorAnimator(ClickGuiTheme.themeBtnBg()); private final ColorAnimator musicBtnAnim = new ColorAnimator(ClickGuiTheme.themeBtnBg()); private final SearchBar searchBar = new SearchBar(); + private boolean searchWasActive = false; private final AnimClock animClock = new AnimClock(); private static final BezierEasing CLICKGUI_EASE = BezierEasing.of(0.25, 0.1, 0.25, 1.0); private static final int MASK_MAX_ALPHA = 110; @@ -219,6 +220,12 @@ public void render(int mouseX, int mouseY, float partialTicks) { int finalMouseY = mouseY; boolean searching = isSearchActive(); String query = searchBar.getQuery(); + // author:Serendisand + // reason:全局搜索 - 搜索激活/退出时回到列表顶部,避免结果在视口外 + if (searching != searchWasActive) { + modsContainer.resetScroll(); + } + searchWasActive = searching; modsContainer.draw(this, x + leftWidth, y + 25f, containerWidth, height - 20f, mouseX, mouseY, () -> { float modsY = y + 22f; boolean anyMatch = false; diff --git a/src/main/java/top/fpsmaster/ui/click/component/ScrollContainer.java b/src/main/java/top/fpsmaster/ui/click/component/ScrollContainer.java index 16a9512a..bf559670 100644 --- a/src/main/java/top/fpsmaster/ui/click/component/ScrollContainer.java +++ b/src/main/java/top/fpsmaster/ui/click/component/ScrollContainer.java @@ -124,6 +124,15 @@ public int getScroll() { return (int) wheel; } + // author:Serendisand + // reason:全局搜索 - 搜索激活/退出时回到列表顶部 + public void resetScroll() { + wheelAnim = 0f; + lastTarget = Float.NaN; + wheelDelay = System.nanoTime(); + isScrolling = false; + } + public float getRealScroll() { return wheelAnim; } diff --git a/src/main/java/top/fpsmaster/ui/click/modules/ModuleRenderer.java b/src/main/java/top/fpsmaster/ui/click/modules/ModuleRenderer.java index bd680a61..706adc33 100644 --- a/src/main/java/top/fpsmaster/ui/click/modules/ModuleRenderer.java +++ b/src/main/java/top/fpsmaster/ui/click/modules/ModuleRenderer.java @@ -228,14 +228,17 @@ private void drawHighlighted(String text, UFontRenderer font, float x, float y, font.drawString(text, x, y, color); return; } - int index = text.toLowerCase(Locale.getDefault()).indexOf(highlight.toLowerCase(Locale.getDefault())); + String lowerText = text.toLowerCase(Locale.getDefault()); + int index = lowerText.indexOf(highlight.toLowerCase(Locale.getDefault())); if (index < 0) { font.drawString(text, x, y, color); return; } + // 大小写折叠可能改变长度(如 ß→ss),匹配段按原文截断并钳制,避免越界 + int matchEnd = Math.min(index + highlight.length(), text.length()); String before = text.substring(0, index); - String match = text.substring(index, index + highlight.length()); - String after = text.substring(index + highlight.length()); + String match = text.substring(index, matchEnd); + String after = text.substring(matchEnd); float cursor = x; if (!before.isEmpty()) { cursor += font.drawString(before, cursor, y, color);