Skip to content

Commit 92e3a85

Browse files
authored
Merge pull request #7 from cqb13/main
updated to support new addon list data structure
2 parents 53096b7 + ea44aab commit 92e3a85

8 files changed

Lines changed: 214 additions & 170 deletions

File tree

src/main/java/com/cope/meteoraddons/gui/screens/AddonDetailScreen.java

Lines changed: 87 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import com.cope.meteoraddons.config.IconSizeConfig;
88
import com.cope.meteoraddons.models.AddonMetadata;
99
import com.cope.meteoraddons.models.UpdateInfo;
10+
import com.cope.meteoraddons.models.AddonMetadata.Feature;
1011
import com.cope.meteoraddons.systems.AddonManager;
1112
import com.cope.meteoraddons.util.GitHubReleaseAPI;
1213
import com.cope.meteoraddons.util.HashUtil;
@@ -29,6 +30,7 @@
2930
import java.util.Collections;
3031
import java.util.List;
3132
import java.util.Optional;
33+
import java.util.stream.Collectors;
3234

3335
import static meteordevelopment.meteorclient.MeteorClient.mc;
3436
import static meteordevelopment.meteorclient.utils.Utils.getWindowWidth;
@@ -50,17 +52,18 @@ public AddonDetailScreen(GuiTheme theme, Addon addon, Screen parent) {
5052
public void initWidgets() {
5153
// Header Section (Icon + Details)
5254
WHorizontalList header = add(theme.horizontalList()).centerX().widget();
53-
55+
5456
// Icon
5557
Texture iconTexture = IconCache.get(addon);
56-
header.add(theme.texture(IconSizeConfig.ADDON_ICON_SIZE, IconSizeConfig.ADDON_ICON_SIZE, 0, iconTexture)).widget();
58+
header.add(theme.texture(IconSizeConfig.ADDON_ICON_SIZE, IconSizeConfig.ADDON_ICON_SIZE, 0, iconTexture))
59+
.widget();
5760

5861
// Details (Name, Authors, Version, Verified)
5962
WVerticalList details = header.add(theme.verticalList()).expandX().widget();
60-
63+
6164
WHorizontalList titleRow = details.add(theme.horizontalList()).widget();
6265
titleRow.add(theme.label(addon.getName(), true)); // Title
63-
66+
6467
if (addon.isInstalled()) {
6568
Texture installedIcon = IconCache.getInstalledIndicator();
6669
if (installedIcon != null) {
@@ -70,8 +73,6 @@ public void initWidgets() {
7073
titleRow.add(theme.label("(Installed)", true).color(theme.textSecondaryColor())).padLeft(4);
7174
}
7275

73-
74-
7576
List<String> authors = addon.getAuthors();
7677
if (!authors.isEmpty()) {
7778
details.add(theme.label("By: " + String.join(", ", authors)).color(theme.textSecondaryColor()));
@@ -96,27 +97,30 @@ public void initWidgets() {
9697
}
9798
}
9899
}
99-
100+
100101
// Stats (Online Addons only)
101102
if (addon instanceof OnlineAddon) {
102-
AddonMetadata metadata = ((OnlineAddon) addon).getMetadata();
103-
if (metadata.repo != null) {
104-
WHorizontalList stats = add(theme.horizontalList()).centerX().widget();
105-
stats.add(theme.label("Stars: " + metadata.repo.stars));
106-
stats.add(theme.label("Downloads: " + metadata.repo.downloads)).padHorizontal(10);
107-
stats.add(theme.label("Updated: " + TimeUtil.getRelativeTime(metadata.repo.last_update)));
108-
}
109-
110-
// Features Section
111-
if (metadata.features != null && hasAnyFeatures(metadata.features)) {
112-
WSection featuresSection = add(theme.section("Features", true)).expandX().widget();
113-
114-
boolean needsSeparator = false;
115-
needsSeparator = addFeatureList(featuresSection, "Modules", metadata.features.modules, needsSeparator) || needsSeparator;
116-
needsSeparator = addFeatureList(featuresSection, "Commands", metadata.features.commands, needsSeparator) || needsSeparator;
117-
needsSeparator = addFeatureList(featuresSection, "HUD", metadata.features.hud_elements, needsSeparator) || needsSeparator;
118-
addFeatureList(featuresSection, "Screens", metadata.features.custom_screens, needsSeparator);
119-
}
103+
AddonMetadata metadata = ((OnlineAddon) addon).getMetadata();
104+
if (metadata.repo != null) {
105+
WHorizontalList stats = add(theme.horizontalList()).centerX().widget();
106+
stats.add(theme.label("Stars: " + metadata.repo.stars));
107+
stats.add(theme.label("Downloads: " + metadata.repo.downloads)).padHorizontal(10);
108+
stats.add(theme.label("Updated: " + TimeUtil.getRelativeTime(metadata.repo.last_update)));
109+
}
110+
111+
// Features Section
112+
if (metadata.features != null && hasAnyFeatures(metadata.features)) {
113+
WSection featuresSection = add(theme.section("Features", true)).expandX().widget();
114+
115+
boolean needsSeparator = false;
116+
needsSeparator = addFeatureList(featuresSection, "Modules", metadata.features.modules, needsSeparator)
117+
|| needsSeparator;
118+
needsSeparator = addFeatureList(featuresSection, "Commands", metadata.features.commands, needsSeparator)
119+
|| needsSeparator;
120+
needsSeparator = addFeatureList(featuresSection, "HUD", metadata.features.hud_elements, needsSeparator)
121+
|| needsSeparator;
122+
addStringFeatureList(featuresSection, "Screens", metadata.features.custom_screens, needsSeparator);
123+
}
120124
}
121125

122126
add(theme.horizontalSeparator()).expandX();
@@ -153,7 +157,8 @@ public void initWidgets() {
153157
if (update.isPresent()) {
154158
checkUpdateBtn.set("Update Found!");
155159
// Show updates screen with this single update
156-
mc.setScreen(new UpdatesAvailableScreen(GuiThemes.get(), Collections.singletonList(update.get())));
160+
mc.setScreen(new UpdatesAvailableScreen(GuiThemes.get(),
161+
Collections.singletonList(update.get())));
157162
} else {
158163
checkUpdateBtn.set("Up to date");
159164
}
@@ -164,19 +169,19 @@ public void initWidgets() {
164169

165170
// Link Buttons
166171
if (addon.getGithubUrl().isPresent()) {
167-
WButton btn = actions.add(theme.button("GitHub")).widget();
168-
final String url = addon.getGithubUrl().get();
169-
btn.action = () -> Util.getOperatingSystem().open(url);
172+
WButton btn = actions.add(theme.button("GitHub")).widget();
173+
final String url = addon.getGithubUrl().get();
174+
btn.action = () -> Util.getOperatingSystem().open(url);
170175
}
171176
if (addon.getDiscordUrl().isPresent()) {
172-
WButton btn = actions.add(theme.button("Discord")).widget();
173-
final String url = addon.getDiscordUrl().get();
174-
btn.action = () -> Util.getOperatingSystem().open(url);
177+
WButton btn = actions.add(theme.button("Discord")).widget();
178+
final String url = addon.getDiscordUrl().get();
179+
btn.action = () -> Util.getOperatingSystem().open(url);
175180
}
176181
if (addon.getHomepageUrl().isPresent()) {
177-
WButton btn = actions.add(theme.button("Homepage")).widget();
178-
final String url = addon.getHomepageUrl().get();
179-
btn.action = () -> Util.getOperatingSystem().open(url);
182+
WButton btn = actions.add(theme.button("Homepage")).widget();
183+
final String url = addon.getHomepageUrl().get();
184+
btn.action = () -> Util.getOperatingSystem().open(url);
180185
}
181186

182187
// Back Button
@@ -189,21 +194,48 @@ public void initWidgets() {
189194
*/
190195
private boolean hasAnyFeatures(AddonMetadata.Features features) {
191196
return (features.modules != null && !features.modules.isEmpty()) ||
192-
(features.commands != null && !features.commands.isEmpty()) ||
193-
(features.hud_elements != null && !features.hud_elements.isEmpty()) ||
194-
(features.custom_screens != null && !features.custom_screens.isEmpty());
197+
(features.commands != null && !features.commands.isEmpty()) ||
198+
(features.hud_elements != null && !features.hud_elements.isEmpty()) ||
199+
(features.custom_screens != null && !features.custom_screens.isEmpty());
195200
}
196201

197202
/**
198203
* Add a feature list to the section if items are present.
199204
*
200-
* @param section The section to add to
201-
* @param label The feature type label (e.g., "Modules", "Commands")
202-
* @param items The list of feature names
205+
* @param section The section to add to
206+
* @param label The feature type label (e.g., "Modules", "Commands")
207+
* @param items The list of feature names
208+
* @param addSeparator Whether to add a separator before this feature group
209+
* @return true if items were added, false otherwise
210+
*/
211+
private boolean addFeatureList(WSection section, String label, List<Feature> items, boolean addSeparator) {
212+
if (items == null || items.isEmpty()) {
213+
return false;
214+
}
215+
216+
if (addSeparator) {
217+
section.add(theme.horizontalSeparator()).expandX();
218+
}
219+
220+
section.add(theme.label(label + " (" + items.size() + "):"));
221+
String itemsStr = items.stream()
222+
.map(item -> item.name)
223+
.collect(Collectors.joining(", "));
224+
section.add(theme.label(itemsStr, getWindowWidth() / 2.0).color(theme.textSecondaryColor()));
225+
226+
return true;
227+
}
228+
229+
/**
230+
* Add a string feature list to the section if items are present.
231+
*
232+
* @param section The section to add to
233+
* @param label The feature type label (e.g., "Modules", "Commands")
234+
* @param items The list of feature names
203235
* @param addSeparator Whether to add a separator before this feature group
204236
* @return true if items were added, false otherwise
205237
*/
206-
private boolean addFeatureList(WSection section, String label, List<String> items, boolean addSeparator) {
238+
private boolean addStringFeatureList(WSection section, String label, List<String> items, boolean addSeparator) {
207239
if (items == null || items.isEmpty()) {
208240
return false;
209241
}
@@ -262,7 +294,8 @@ private Optional<UpdateInfo> checkForUpdate(InstalledAddon installed) {
262294
MeteorAddonsAddon.LOG.info("Local SHA256: {}", localHash);
263295

264296
// Fetch release info from GitHub
265-
Optional<GitHubReleaseAPI.ReleaseInfo> releaseOpt = GitHubReleaseAPI.getLatestRelease(ownerRepo[0], ownerRepo[1]);
297+
Optional<GitHubReleaseAPI.ReleaseInfo> releaseOpt = GitHubReleaseAPI.getLatestRelease(ownerRepo[0],
298+
ownerRepo[1]);
266299
if (releaseOpt.isEmpty()) {
267300
MeteorAddonsAddon.LOG.warn("No release found for {}/{}", ownerRepo[0], ownerRepo[1]);
268301
return Optional.empty();
@@ -284,7 +317,8 @@ private Optional<UpdateInfo> checkForUpdate(InstalledAddon installed) {
284317
MeteorAddonsAddon.LOG.info("Remote SHA256: {}", remoteHash);
285318

286319
if (remoteHash == null || remoteHash.isEmpty()) {
287-
MeteorAddonsAddon.LOG.warn("No SHA256 digest available for {} (GitHub may not have computed it yet)", installed.getName());
320+
MeteorAddonsAddon.LOG.warn("No SHA256 digest available for {} (GitHub may not have computed it yet)",
321+
installed.getName());
288322
return Optional.empty();
289323
}
290324

@@ -293,16 +327,15 @@ private Optional<UpdateInfo> checkForUpdate(InstalledAddon installed) {
293327
MeteorAddonsAddon.LOG.info("UPDATE AVAILABLE for {}: hashes differ", installed.getName());
294328

295329
return Optional.of(new UpdateInfo(
296-
installed,
297-
installed.getName(),
298-
installed.getVersion(),
299-
release.getVersion(),
300-
release.getChangelog(),
301-
asset.getDownloadUrl(),
302-
remoteHash,
303-
localHash,
304-
localJarPath
305-
));
330+
installed,
331+
installed.getName(),
332+
installed.getVersion(),
333+
release.getVersion(),
334+
release.getChangelog(),
335+
asset.getDownloadUrl(),
336+
remoteHash,
337+
localHash,
338+
localJarPath));
306339
} else {
307340
MeteorAddonsAddon.LOG.info("{} is up to date (hashes match)", installed.getName());
308341
return Optional.empty();

src/main/java/com/cope/meteoraddons/gui/screens/BrowseAddonsScreen.java

Lines changed: 27 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -2,31 +2,23 @@
22

33
import com.cope.meteoraddons.addons.Addon;
44
import com.cope.meteoraddons.addons.OnlineAddon;
5-
import com.cope.meteoraddons.config.IconSizeConfig;
65
import com.cope.meteoraddons.gui.widgets.WAddonCard;
76
import com.cope.meteoraddons.gui.widgets.WAddonList;
8-
import com.cope.meteoraddons.models.AddonMetadata;
97
import com.cope.meteoraddons.systems.AddonManager;
108
import com.cope.meteoraddons.util.AddonSearchUtil;
11-
import com.cope.meteoraddons.util.IconCache;
129
import com.cope.meteoraddons.util.VersionUtil;
1310
import meteordevelopment.meteorclient.gui.GuiTheme;
1411
import meteordevelopment.meteorclient.gui.WindowScreen;
1512
import meteordevelopment.meteorclient.gui.widgets.containers.WContainer;
1613
import meteordevelopment.meteorclient.gui.widgets.containers.WHorizontalList;
1714
import meteordevelopment.meteorclient.gui.widgets.containers.WTable;
18-
import meteordevelopment.meteorclient.gui.widgets.containers.WVerticalList;
1915
import meteordevelopment.meteorclient.gui.widgets.input.WTextBox;
2016
import meteordevelopment.meteorclient.gui.widgets.pressable.WButton;
21-
import meteordevelopment.meteorclient.renderer.Texture;
22-
import net.minecraft.util.Util;
2317

2418
import java.util.List;
25-
import java.util.Locale;
2619
import java.util.stream.Collectors;
2720

2821
import static meteordevelopment.meteorclient.MeteorClient.mc;
29-
import static meteordevelopment.meteorclient.utils.Utils.getWindowWidth;
3022

3123
public class BrowseAddonsScreen extends WindowScreen {
3224
private static final int CARDS_PER_ROW = 4;
@@ -72,7 +64,7 @@ public void initWidgets() {
7264

7365
// Toolbar: Search + View Toggle
7466
WHorizontalList toolbar = add(theme.horizontalList()).expandX().widget();
75-
67+
7668
// Search Bar
7769
searchField = toolbar.add(theme.textBox(currentSearch)).minWidth(200).expandX().widget();
7870
searchField.setFocused(true);
@@ -83,17 +75,17 @@ public void initWidgets() {
8375

8476
// View Toggles
8577
toolbar.add(theme.horizontalList()).expandX(); // Spacer
86-
78+
8779
WButton listBtn = toolbar.add(theme.button(isGridView ? "List" : "[List]")).widget();
88-
listBtn.action = () -> {
89-
isGridView = false;
90-
reload();
80+
listBtn.action = () -> {
81+
isGridView = false;
82+
reload();
9183
};
9284

9385
WButton gridBtn = toolbar.add(theme.button(isGridView ? "[Grid]" : "Grid")).widget();
94-
gridBtn.action = () -> {
95-
isGridView = true;
96-
reload();
86+
gridBtn.action = () -> {
87+
isGridView = true;
88+
reload();
9789
};
9890

9991
add(theme.horizontalSeparator()).expandX();
@@ -107,8 +99,8 @@ private void updateContent(List<Addon> allAddons) {
10799
contentContainer.clear();
108100

109101
List<Addon> filtered = allAddons.stream()
110-
.filter(addon -> AddonSearchUtil.matches(addon, currentSearch))
111-
.collect(Collectors.toList());
102+
.filter(addon -> AddonSearchUtil.matches(addon, currentSearch))
103+
.collect(Collectors.toList());
112104

113105
if (filtered.isEmpty()) {
114106
contentContainer.add(theme.label("No addons match your search.")).centerX();
@@ -137,23 +129,22 @@ private void initGridView(WContainer parent, List<Addon> addons) {
137129

138130
private void initListView(WContainer parent, List<Addon> addons) {
139131
parent.add(new WAddonList(
140-
addons,
141-
addon -> () -> mc.setScreen(new AddonDetailScreen(theme, addon, this)),
142-
addon -> button -> {
143-
if (addon instanceof OnlineAddon) {
144-
button.set("Downloading...");
145-
meteordevelopment.meteorclient.utils.network.MeteorExecutor.execute(() -> {
146-
boolean success = AddonManager.get().downloadAddon((OnlineAddon) addon);
147-
mc.execute(() -> {
148-
if (success) {
149-
button.set("Downloaded!");
150-
} else {
151-
button.set("Failed");
152-
}
132+
addons,
133+
addon -> () -> mc.setScreen(new AddonDetailScreen(theme, addon, this)),
134+
addon -> button -> {
135+
if (addon instanceof OnlineAddon) {
136+
button.set("Downloading...");
137+
meteordevelopment.meteorclient.utils.network.MeteorExecutor.execute(() -> {
138+
boolean success = AddonManager.get().downloadAddon((OnlineAddon) addon);
139+
mc.execute(() -> {
140+
if (success) {
141+
button.set("Downloaded!");
142+
} else {
143+
button.set("Failed");
144+
}
145+
});
153146
});
154-
});
155-
}
156-
}
157-
)).expandX();
147+
}
148+
})).expandX();
158149
}
159-
}
150+
}

0 commit comments

Comments
 (0)