Skip to content

Commit dab7577

Browse files
committed
ClickGUI improvements
1 parent 19d527b commit dab7577

File tree

10 files changed

+327
-4
lines changed

10 files changed

+327
-4
lines changed

README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,10 @@ Examples:
9393
- Highlight skulls → Item ID: `minecraft:player_head`, special color: magenta, outline-only ON.
9494
- Highlight talismans (non-standard item) → Query: `talisman`, special color: rainbow, highlight frames ON, lines-only-for-special ON.
9595

96+
### ClickGUI improvements
97+
- Accidently typing in ClickGUI just continues what you typed in the Navigator
98+
- Favourites category, middle click a hack for it to be added to Favourites. Middle click when within Favourites to remove it.
99+
96100
### Search improvements
97101
- Keyword queries supported; falls back to picker when empty.
98102
- List mode with visual item list.

src/main/java/net/wurstclient/Category.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99

1010
public enum Category
1111
{
12+
FAVORITES("Favorites"),
1213
BLOCKS("Blocks"),
1314
MOVEMENT("Movement"),
1415
COMBAT("Combat"),

src/main/java/net/wurstclient/WurstClient.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,8 @@ public void initialize()
9191
eventManager = new EventManager(this);
9292

9393
Path enabledHacksFile = wurstFolder.resolve("enabled-hacks.json");
94-
hax = new HackList(enabledHacksFile);
94+
Path favoritesHacksFile = wurstFolder.resolve("favourites.json");
95+
hax = new HackList(enabledHacksFile, favoritesHacksFile);
9596

9697
cmds = new CmdList();
9798

src/main/java/net/wurstclient/clickgui/ClickGui.java

Lines changed: 98 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,18 @@ public void init()
8080
for(Feature f : features)
8181
if(f.getCategory() != null)
8282
windowMap.get(f.getCategory()).add(new FeatureButton(f));
83-
83+
// add favorites window entries (show favorites in the Favorites
84+
// category)
85+
for(Feature f : features)
86+
if(f instanceof net.wurstclient.hack.Hack
87+
&& ((net.wurstclient.hack.Hack)f).isFavorite())
88+
windowMap.get(net.wurstclient.Category.FAVORITES)
89+
.add(new FeatureButton(f));
90+
// ensure favourites window is sorted alphabetically
91+
Window favWindow = windowMap.get(net.wurstclient.Category.FAVORITES);
92+
if(favWindow != null)
93+
sortFavoritesWindow(favWindow);
94+
8495
windows.addAll(windowMap.values());
8596

8697
Window uiSettings = new Window("UI Settings");
@@ -836,8 +847,94 @@ public void addPopup(Popup popup)
836847
popups.add(popup);
837848
}
838849

850+
/**
851+
* Add a feature to the Favorites window if not already present.
852+
*/
853+
public void addFavoriteFeature(Feature feature)
854+
{
855+
String favTitle = net.wurstclient.Category.FAVORITES.getName();
856+
for(Window window : windows)
857+
{
858+
if(window.getTitle().equals(favTitle))
859+
{
860+
// check existing
861+
for(int i = 0; i < window.countChildren(); i++)
862+
{
863+
Component c = window.getChild(i);
864+
if(c instanceof net.wurstclient.clickgui.components.FeatureButton)
865+
{
866+
net.wurstclient.clickgui.components.FeatureButton fb =
867+
(net.wurstclient.clickgui.components.FeatureButton)c;
868+
if(fb.getFeature().getName().equals(feature.getName()))
869+
return;
870+
}
871+
}
872+
window
873+
.add(new net.wurstclient.clickgui.components.FeatureButton(
874+
feature));
875+
sortFavoritesWindow(window);
876+
return;
877+
}
878+
}
879+
}
880+
881+
public void removeFavoriteFeature(Feature feature)
882+
{
883+
String favTitle = net.wurstclient.Category.FAVORITES.getName();
884+
for(Window window : windows)
885+
{
886+
if(!window.getTitle().equals(favTitle))
887+
continue;
888+
for(int i = window.countChildren() - 1; i >= 0; i--)
889+
{
890+
Component c = window.getChild(i);
891+
if(c instanceof net.wurstclient.clickgui.components.FeatureButton)
892+
{
893+
net.wurstclient.clickgui.components.FeatureButton fb =
894+
(net.wurstclient.clickgui.components.FeatureButton)c;
895+
if(fb.getFeature().getName().equals(feature.getName()))
896+
{
897+
window.remove(i);
898+
window.pack();
899+
return;
900+
}
901+
}
902+
}
903+
}
904+
}
905+
839906
public boolean isLeftMouseButtonPressed()
840907
{
841908
return leftMouseButtonPressed;
842909
}
910+
911+
/**
912+
* Sort the given favourites window's children alphabetically by feature
913+
* name.
914+
*/
915+
private void sortFavoritesWindow(Window window)
916+
{
917+
if(window == null)
918+
return;
919+
// collect children
920+
ArrayList<Component> all = new ArrayList<>();
921+
for(int i = 0; i < window.countChildren(); i++)
922+
all.add(window.getChild(i));
923+
// sort by feature name when possible
924+
all.sort((c1, c2) -> {
925+
String n1 = c1 instanceof FeatureButton
926+
? ((FeatureButton)c1).getFeature().getName()
927+
: c1.getClass().getName();
928+
String n2 = c2 instanceof FeatureButton
929+
? ((FeatureButton)c2).getFeature().getName()
930+
: c2.getClass().getName();
931+
return n1.compareToIgnoreCase(n2);
932+
});
933+
// remove all children and re-add in sorted order
934+
for(int i = window.countChildren() - 1; i >= 0; i--)
935+
window.remove(i);
936+
for(Component c : all)
937+
window.add(c);
938+
window.pack();
939+
}
843940
}

src/main/java/net/wurstclient/clickgui/components/FeatureButton.java

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import net.wurstclient.hacks.TooManyHaxHack;
2121
import net.wurstclient.util.ChatUtils;
2222
import net.wurstclient.util.RenderUtils;
23+
import org.lwjgl.glfw.GLFW;
2324

2425
public final class FeatureButton extends Component
2526
{
@@ -39,12 +40,42 @@ public FeatureButton(Feature feature)
3940
hasSettings = !feature.getSettings().isEmpty();
4041
}
4142

43+
public Feature getFeature()
44+
{
45+
return feature;
46+
}
47+
4248
@Override
4349
public void handleMouseClick(double mouseX, double mouseY, int mouseButton)
4450
{
45-
if(mouseButton != 0)
51+
if(mouseButton != 0 && mouseButton != GLFW.GLFW_MOUSE_BUTTON_MIDDLE)
4652
return;
4753

54+
// middle click anywhere on the feature toggles favourite
55+
if(mouseButton == GLFW.GLFW_MOUSE_BUTTON_MIDDLE
56+
&& feature instanceof net.wurstclient.hack.Hack)
57+
{
58+
net.wurstclient.hack.Hack h = (net.wurstclient.hack.Hack)feature;
59+
boolean inFavouritesWindow = false;
60+
if(getParent() != null && getParent().getTitle()
61+
.equals(net.wurstclient.Category.FAVORITES.getName()))
62+
inFavouritesWindow = true;
63+
64+
if(inFavouritesWindow)
65+
{
66+
h.setFavorite(false);
67+
net.wurstclient.util.ChatUtils
68+
.message(h.getName() + " removed from favourites.");
69+
return;
70+
}else
71+
{
72+
h.setFavorite(true);
73+
net.wurstclient.util.ChatUtils
74+
.message(h.getName() + " added to favourites.");
75+
return;
76+
}
77+
}
78+
4879
if(hasSettings && (mouseX > getX() + getWidth() - 12
4980
|| feature.getPrimaryAction().isEmpty()))
5081
{
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
/*
2+
* Copyright (c) 2014-2025 Wurst-Imperium and contributors.
3+
*
4+
* This source code is subject to the terms of the GNU General Public
5+
* License, version 3. If a copy of the GPL was not distributed with this
6+
* file, You can obtain one at: https://www.gnu.org/licenses/gpl-3.0.txt
7+
*/
8+
package net.wurstclient.hack;
9+
10+
import java.io.IOException;
11+
import java.nio.file.NoSuchFileException;
12+
import java.nio.file.Path;
13+
14+
import com.google.gson.JsonArray;
15+
16+
import net.wurstclient.util.json.JsonException;
17+
import net.wurstclient.util.json.JsonUtils;
18+
import net.wurstclient.util.json.WsonArray;
19+
20+
public final class FavoriteHacksFile
21+
{
22+
private final Path path;
23+
private boolean disableSaving;
24+
25+
public FavoriteHacksFile(Path path)
26+
{
27+
this.path = path;
28+
}
29+
30+
public void load(HackList hackList)
31+
{
32+
try
33+
{
34+
WsonArray wson = JsonUtils.parseFileToArray(path);
35+
applyFavorites(hackList, wson);
36+
37+
}catch(NoSuchFileException e)
38+
{
39+
// file doesn't exist yet
40+
}catch(IOException | JsonException e)
41+
{
42+
System.out.println("Couldn't load " + path.getFileName());
43+
e.printStackTrace();
44+
}
45+
46+
save(hackList);
47+
}
48+
49+
private void applyFavorites(HackList hax, WsonArray wson)
50+
{
51+
try
52+
{
53+
disableSaving = true;
54+
for(Hack hack : hax.getAllHax())
55+
hack.setFavorite(false);
56+
57+
for(String name : wson.getAllStrings())
58+
{
59+
Hack hack = hax.getHackByName(name);
60+
if(hack == null)
61+
continue;
62+
hack.setFavorite(true);
63+
}
64+
65+
}finally
66+
{
67+
disableSaving = false;
68+
}
69+
}
70+
71+
public void save(HackList hax)
72+
{
73+
if(disableSaving)
74+
return;
75+
76+
JsonArray json = createJson(hax);
77+
78+
try
79+
{
80+
JsonUtils.toJson(json, path);
81+
}catch(IOException | JsonException e)
82+
{
83+
System.out.println("Couldn't save " + path.getFileName());
84+
e.printStackTrace();
85+
}
86+
}
87+
88+
private JsonArray createJson(HackList hax)
89+
{
90+
JsonArray json = new JsonArray();
91+
hax.getAllHax().stream().filter(Hack::isFavorite).map(Hack::getName)
92+
.forEach(name -> json.add(name));
93+
return json;
94+
}
95+
}

src/main/java/net/wurstclient/hack/Hack.java

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,9 @@ public abstract class Hack extends Feature
2525
private final boolean stateSaved =
2626
!getClass().isAnnotationPresent(DontSaveState.class);
2727

28+
// favorites
29+
private boolean favorite = false;
30+
2831
public Hack(String name)
2932
{
3033
this.name = Objects.requireNonNull(name);
@@ -111,6 +114,37 @@ public final boolean isStateSaved()
111114
return stateSaved;
112115
}
113116

117+
// favorites
118+
public final boolean isFavorite()
119+
{
120+
return favorite;
121+
}
122+
123+
public final void setFavorite(boolean fav)
124+
{
125+
if(this.favorite == fav)
126+
return;
127+
this.favorite = fav;
128+
// allow HackList to persist favorites when needed
129+
if(WURST != null && WURST.getHax() != null)
130+
WURST.getHax().saveFavoriteHax();
131+
132+
// update ClickGui immediately if present
133+
if(WURST != null && WURST.getGui() != null)
134+
{
135+
try
136+
{
137+
if(fav)
138+
WURST.getGui().addFavoriteFeature(this);
139+
else
140+
WURST.getGui().removeFavoriteFeature(this);
141+
}catch(Exception e)
142+
{
143+
// ignore GUI update failures
144+
}
145+
}
146+
}
147+
114148
protected void onEnable()
115149
{
116150

src/main/java/net/wurstclient/hack/HackList.java

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -204,15 +204,17 @@ public final class HackList implements UpdateListener
204204
new TreeMap<>(String::compareToIgnoreCase);
205205

206206
private final EnabledHacksFile enabledHacksFile;
207+
private final FavoriteHacksFile favoriteHacksFile;
207208
private final Path profilesFolder =
208209
WurstClient.INSTANCE.getWurstFolder().resolve("enabled hacks");
209210

210211
private final EventManager eventManager =
211212
WurstClient.INSTANCE.getEventManager();
212213

213-
public HackList(Path enabledHacksFile)
214+
public HackList(Path enabledHacksFile, Path favoriteHacksFile)
214215
{
215216
this.enabledHacksFile = new EnabledHacksFile(enabledHacksFile);
217+
this.favoriteHacksFile = new FavoriteHacksFile(favoriteHacksFile);
216218

217219
try
218220
{
@@ -239,6 +241,7 @@ public HackList(Path enabledHacksFile)
239241
public void onUpdate()
240242
{
241243
enabledHacksFile.load(this);
244+
favoriteHacksFile.load(this);
242245
eventManager.remove(UpdateListener.class, this);
243246
}
244247

@@ -247,6 +250,11 @@ public void saveEnabledHax()
247250
enabledHacksFile.save(this);
248251
}
249252

253+
public void saveFavoriteHax()
254+
{
255+
favoriteHacksFile.save(this);
256+
}
257+
250258
public Hack getHackByName(String name)
251259
{
252260
return hax.get(name);

0 commit comments

Comments
 (0)