Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions docs/tasks.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ FPSMaster v4将会在此列表任务大部分完成后发布
- [ ] 跨版本框架
- [ ] 自定义客户端字体
- [ ] 自定义动画速率、曲线等
- [ ] 全局搜索
- [x] 全局搜索
- [x] 新的OOBE
- [ ] 功能完整的饰品界面
- [x] AutoText
Expand All @@ -40,7 +40,6 @@ FPSMaster v4将会在此列表任务大部分完成后发布
- [ ] 添加异步光照性能优化
- [x] 添加自定义迷雾(颜色,距离等)
- [ ] 按[性能优化路线图](performance-roadmap.md)推进区块、实体、光照、纹理和画质裁剪工作
- [ ] 添加自定义迷雾(颜色,距离等)
- [ ] 为客户端的组件颜色选择添加彩色选项
- [x] 在聊天栏显示头像
- [ ] HypixelMod(合并AutoGG,AutoPlay以及聊天栏实用功能等)
Expand Down
89 changes: 88 additions & 1 deletion src/main/java/top/fpsmaster/ui/click/MainPanel.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -51,6 +52,8 @@ 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 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;
Expand Down Expand Up @@ -99,6 +102,48 @@ private float getCategoryStartY() {
return getCategoryBgY() + 10f;
}

// author:Serendisand
// reason:全局搜索
private float getSearchBarX() {
return x + width - getSearchBarWidth();
}

private float getSearchBarY() {
return y - 3 - getSearchBarHeight();
}

private float getSearchBarWidth() {
return 130f;
}

private float getSearchBarHeight() {
return 18f;
}

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");
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
public void render(int mouseX, int mouseY, float partialTicks) {
updateZoomModifierState();
Expand Down Expand Up @@ -153,6 +198,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();
Expand All @@ -171,10 +218,23 @@ 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();
// 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;
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(
Expand All @@ -192,6 +252,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);
Expand Down Expand Up @@ -360,6 +428,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;
Expand Down Expand Up @@ -390,6 +460,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();
Expand All @@ -414,6 +489,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;
Expand All @@ -435,6 +519,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) {
Expand Down
105 changes: 105 additions & 0 deletions src/main/java/top/fpsmaster/ui/click/SearchBar.java
Original file line number Diff line number Diff line change
@@ -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), Math.round(height / 2f), 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("");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down
61 changes: 49 additions & 12 deletions src/main/java/top/fpsmaster/ui/click/modules/ModuleRenderer.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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());
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -213,6 +221,35 @@ 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;
}
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, matchEnd);
String after = text.substring(matchEnd);
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) {
Expand Down
Loading
Loading