Skip to content

Commit 4d3abfc

Browse files
committed
feat: Complete GUI redesign with enhanced icon system
Major UI/UX overhaul of the addon browser with improved layouts, multi-size icon support, and better information hierarchy. Changes: - Redesigned all three screens (Browse, Installed, Detail) with cleaner layouts and better visual hierarchy - Added IconPreloadSystem for efficient background icon loading - Implemented multi-size icon support (48x48, 64x64, 128x128) - Added TimeUtil for human-readable relative timestamps - Removed WAddonListItem widget - list view now inline - Removed AddonIconTexture - icon management in IconCache - Enhanced AddonDetailScreen with compact header and features section - Improved BrowseAddonsScreen with better grid/list view layouts - Updated InstalledAddonsScreen with consistent styling - Better color coding using theme.textSecondaryColor()
1 parent 95b6963 commit 4d3abfc

13 files changed

Lines changed: 687 additions & 526 deletions

.claude/settings.local.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,10 @@
66
"mcp__code-search-mcp__search_files",
77
"mcp__code-search-mcp__add_workspace",
88
"mcp__gradle-mcp-server__gradle_execute",
9-
"mcp__gradle-mcp-server__gradle_build"
9+
"mcp__gradle-mcp-server__gradle_build",
10+
"WebFetch(domain:maven.fabricmc.net)",
11+
"mcp__code-search-mcp__search_text",
12+
"Bash(grep:*)"
1013
],
1114
"deny": [],
1215
"ask": []

src/main/java/com/cope/meteoraddons/MeteorAddonsAddon.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import com.cope.meteoraddons.gui.tabs.AddonsTab;
44
import com.cope.meteoraddons.systems.AddonManager;
5+
import com.cope.meteoraddons.systems.IconPreloadSystem;
56
import meteordevelopment.meteorclient.addons.GithubRepo;
67
import meteordevelopment.meteorclient.addons.MeteorAddon;
78
import meteordevelopment.meteorclient.gui.tabs.Tabs;
@@ -24,6 +25,11 @@ public class MeteorAddonsAddon extends MeteorAddon {
2425
public void onInitialize() {
2526
LOG.info("Initializing Meteor Addons Addon");
2627

28+
// Initialize IconPreloadSystem (handles icon lifecycle)
29+
IconPreloadSystem iconPreloadSystem = new IconPreloadSystem();
30+
Systems.add(iconPreloadSystem);
31+
LOG.info("IconPreloadSystem registered");
32+
2733
// Initialize AddonManager system (manages addon state, downloads, updates)
2834
Systems.add(new AddonManager());
2935
LOG.info("AddonManager system initialized");

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

Lines changed: 114 additions & 108 deletions
Original file line numberDiff line numberDiff line change
@@ -7,17 +7,21 @@
77
import com.cope.meteoraddons.util.IconCache;
88
import meteordevelopment.meteorclient.gui.GuiTheme;
99
import meteordevelopment.meteorclient.gui.WindowScreen;
10-
import meteordevelopment.meteorclient.gui.widgets.containers.WTable;
10+
import meteordevelopment.meteorclient.gui.widgets.containers.WHorizontalList;
11+
import meteordevelopment.meteorclient.gui.widgets.containers.WSection;
1112
import meteordevelopment.meteorclient.gui.widgets.containers.WVerticalList;
1213
import meteordevelopment.meteorclient.gui.widgets.pressable.WButton;
1314
import meteordevelopment.meteorclient.renderer.Texture;
1415
import meteordevelopment.meteorclient.utils.network.MeteorExecutor;
1516
import net.minecraft.client.gui.screen.Screen;
1617
import net.minecraft.util.Util;
1718

19+
import com.cope.meteoraddons.util.TimeUtil;
20+
1821
import java.util.List;
1922

2023
import static meteordevelopment.meteorclient.MeteorClient.mc;
24+
import static meteordevelopment.meteorclient.utils.Utils.getWindowWidth;
2125

2226
/**
2327
* Detail screen for viewing and downloading an addon.
@@ -34,132 +38,116 @@ public AddonDetailScreen(GuiTheme theme, Addon addon, Screen parent) {
3438

3539
@Override
3640
public void initWidgets() {
41+
// Header Section (Icon + Details)
42+
WHorizontalList header = add(theme.horizontalList()).expandX().widget();
43+
3744
// Icon
3845
Texture iconTexture = IconCache.get(addon);
39-
add(theme.texture(96, 96, 0, iconTexture)).centerX();
40-
41-
// Name
42-
WTable header = add(theme.table()).expandX().widget();
43-
header.add(theme.label(addon.getName(), true)).expandCellX();
46+
header.add(theme.texture(64, 64, 0, iconTexture)).widget();
47+
48+
// Details (Name, Authors, Version, Verified)
49+
WVerticalList details = header.add(theme.verticalList()).expandX().widget();
50+
51+
WHorizontalList titleRow = details.add(theme.horizontalList()).widget();
52+
titleRow.add(theme.label(addon.getName(), true)); // Title
53+
54+
if (addon.isInstalled()) {
55+
Texture installedIcon = IconCache.getInstalledIndicator();
56+
if (installedIcon != null) {
57+
double size = theme.textHeight(true);
58+
titleRow.add(theme.texture(size, size, 0, installedIcon)).padLeft(4);
59+
}
60+
titleRow.add(theme.label("(Installed)", true).color(theme.textSecondaryColor())).padLeft(4);
61+
}
4462

45-
// Show verified badge for online addons
4663
if (addon instanceof OnlineAddon) {
4764
AddonMetadata metadata = ((OnlineAddon) addon).getMetadata();
4865
if (metadata.verified) {
49-
header.add(theme.label("Verified"));
66+
details.add(theme.label("Verified").color(theme.textSecondaryColor()));
5067
}
5168
}
69+
70+
String version = addon.getVersion();
71+
if (version != null && !version.isEmpty()) {
72+
details.add(theme.label("Version: " + version).color(theme.textSecondaryColor()));
73+
}
5274

53-
// Description
54-
addon.getDescription().ifPresent(desc -> {
55-
add(theme.label(desc)).expandX();
56-
});
57-
58-
// Authors
5975
List<String> authors = addon.getAuthors();
6076
if (!authors.isEmpty()) {
61-
WVerticalList authorsList = add(theme.verticalList()).expandX().widget();
62-
authorsList.add(theme.label("Authors:"));
63-
for (String author : authors) {
64-
authorsList.add(theme.label(" • " + author));
65-
}
66-
}
67-
68-
// Version
69-
String version = addon.getVersion();
70-
if (version != null && !version.isEmpty()) {
71-
add(theme.label("Version: " + version)).expandX();
77+
details.add(theme.label("By: " + String.join(", ", authors)).color(theme.textSecondaryColor()));
7278
}
7379

7480
add(theme.horizontalSeparator()).expandX();
7581

76-
// Stats (only for online addons)
82+
// Description
83+
addon.getDescription().ifPresent(desc -> {
84+
add(theme.label(desc, getWindowWidth() / 2.0));
85+
});
86+
87+
// Stats (Online Addons only)
7788
if (addon instanceof OnlineAddon) {
78-
AddonMetadata metadata = ((OnlineAddon) addon).getMetadata();
79-
if (metadata.repo != null) {
80-
WTable stats = add(theme.table()).expandX().widget();
81-
stats.add(theme.label("Stars: "));
82-
stats.add(theme.label(String.valueOf(metadata.repo.stars)));
83-
stats.row();
84-
stats.add(theme.label("Downloads: "));
85-
stats.add(theme.label(String.valueOf(metadata.repo.downloads)));
86-
stats.row();
87-
stats.add(theme.label("Last Update: "));
88-
stats.add(theme.label(metadata.repo.last_update));
89-
90-
add(theme.horizontalSeparator()).expandX();
91-
}
92-
93-
// Features
94-
if (metadata.features != null) {
95-
WVerticalList featuresList = add(theme.verticalList()).expandX().widget();
96-
97-
if (metadata.features.modules != null && !metadata.features.modules.isEmpty()) {
98-
featuresList.add(theme.label("Modules (" + metadata.features.modules.size() + ")"));
99-
int count = 0;
100-
for (String module : metadata.features.modules) {
101-
if (count++ >= 10) {
102-
featuresList.add(theme.label(" ... and " + (metadata.features.modules.size() - 10) + " more"));
103-
break;
104-
}
105-
featuresList.add(theme.label(" • " + module));
106-
}
107-
}
108-
109-
if (metadata.features.commands != null && !metadata.features.commands.isEmpty()) {
110-
featuresList.add(theme.label("Commands (" + metadata.features.commands.size() + ")"));
111-
for (String command : metadata.features.commands) {
112-
featuresList.add(theme.label(" • " + command));
113-
}
114-
}
115-
116-
if (metadata.features.hud_elements != null && !metadata.features.hud_elements.isEmpty()) {
117-
featuresList.add(theme.label("HUD Elements (" + metadata.features.hud_elements.size() + ")"));
118-
for (String hud : metadata.features.hud_elements) {
119-
featuresList.add(theme.label(" • " + hud));
120-
}
121-
}
122-
123-
add(theme.horizontalSeparator()).expandX();
124-
}
125-
}
126-
127-
// Links
128-
WTable links = add(theme.table()).expandX().widget();
129-
boolean hasLinks = false;
130-
131-
if (addon.getGithubUrl().isPresent()) {
132-
WButton githubButton = links.add(theme.button("GitHub")).expandCellX().widget();
133-
final String githubUrl = addon.getGithubUrl().get();
134-
githubButton.action = () -> Util.getOperatingSystem().open(githubUrl);
135-
hasLinks = true;
136-
}
137-
138-
if (addon.getDiscordUrl().isPresent()) {
139-
WButton discordButton = links.add(theme.button("Discord")).expandCellX().widget();
140-
final String discordUrl = addon.getDiscordUrl().get();
141-
discordButton.action = () -> Util.getOperatingSystem().open(discordUrl);
142-
hasLinks = true;
143-
}
144-
145-
if (addon.getHomepageUrl().isPresent()) {
146-
WButton homepageButton = links.add(theme.button("Homepage")).expandCellX().widget();
147-
final String homepageUrl = addon.getHomepageUrl().get();
148-
homepageButton.action = () -> Util.getOperatingSystem().open(homepageUrl);
149-
hasLinks = true;
89+
AddonMetadata metadata = ((OnlineAddon) addon).getMetadata();
90+
if (metadata.repo != null) {
91+
WHorizontalList stats = add(theme.horizontalList()).centerX().widget();
92+
stats.add(theme.label("Stars: " + metadata.repo.stars));
93+
stats.add(theme.label("Downloads: " + metadata.repo.downloads)).padHorizontal(10);
94+
stats.add(theme.label("Updated: " + TimeUtil.getRelativeTime(metadata.repo.last_update)));
95+
}
96+
97+
// Features Section
98+
if (metadata.features != null) {
99+
boolean hasFeatures = (metadata.features.modules != null && !metadata.features.modules.isEmpty()) ||
100+
(metadata.features.commands != null && !metadata.features.commands.isEmpty()) ||
101+
(metadata.features.hud_elements != null && !metadata.features.hud_elements.isEmpty()) ||
102+
(metadata.features.custom_screens != null && !metadata.features.custom_screens.isEmpty());
103+
104+
if (hasFeatures) {
105+
WSection featuresSection = add(theme.section("Features", true)).expandX().widget();
106+
107+
boolean first = true;
108+
109+
if (metadata.features.modules != null && !metadata.features.modules.isEmpty()) {
110+
featuresSection.add(theme.label("Modules (" + metadata.features.modules.size() + "):"));
111+
String modulesStr = String.join(", ", metadata.features.modules);
112+
featuresSection.add(theme.label(modulesStr, getWindowWidth() / 2.0).color(theme.textSecondaryColor()));
113+
first = false;
114+
}
115+
116+
if (metadata.features.commands != null && !metadata.features.commands.isEmpty()) {
117+
if (!first) featuresSection.add(theme.horizontalSeparator()).expandX();
118+
featuresSection.add(theme.label("Commands (" + metadata.features.commands.size() + "):"));
119+
String cmdStr = String.join(", ", metadata.features.commands);
120+
featuresSection.add(theme.label(cmdStr, getWindowWidth() / 2.0).color(theme.textSecondaryColor()));
121+
first = false;
122+
}
123+
124+
if (metadata.features.hud_elements != null && !metadata.features.hud_elements.isEmpty()) {
125+
if (!first) featuresSection.add(theme.horizontalSeparator()).expandX();
126+
featuresSection.add(theme.label("HUD (" + metadata.features.hud_elements.size() + "):"));
127+
String hudStr = String.join(", ", metadata.features.hud_elements);
128+
featuresSection.add(theme.label(hudStr, getWindowWidth() / 2.0).color(theme.textSecondaryColor()));
129+
first = false;
130+
}
131+
132+
if (metadata.features.custom_screens != null && !metadata.features.custom_screens.isEmpty()) {
133+
if (!first) featuresSection.add(theme.horizontalSeparator()).expandX();
134+
featuresSection.add(theme.label("Screens (" + metadata.features.custom_screens.size() + "):"));
135+
String screensStr = String.join(", ", metadata.features.custom_screens);
136+
featuresSection.add(theme.label(screensStr, getWindowWidth() / 2.0).color(theme.textSecondaryColor()));
137+
first = false;
138+
}
139+
}
140+
}
150141
}
151142

152-
if (hasLinks) {
153-
add(theme.horizontalSeparator()).expandX();
154-
}
143+
add(theme.horizontalSeparator()).expandX();
155144

156-
// Download button (only for online addons)
157-
WTable actions = add(theme.table()).expandX().widget();
145+
// Actions (Buttons)
146+
WHorizontalList actions = add(theme.horizontalList()).right().widget();
158147

159-
if (addon.isInstalled()) {
160-
actions.add(theme.label("✓ Installed")).expandCellX();
161-
} else if (addon instanceof OnlineAddon) {
162-
WButton downloadButton = actions.add(theme.button("Download")).expandCellX().widget();
148+
// Download/Install Button
149+
if (!addon.isInstalled() && addon instanceof OnlineAddon) {
150+
WButton downloadButton = actions.add(theme.button("Download")).widget();
163151
downloadButton.action = () -> {
164152
downloadButton.set("Downloading...");
165153
MeteorExecutor.execute(() -> {
@@ -175,7 +163,25 @@ public void initWidgets() {
175163
};
176164
}
177165

178-
WButton backButton = actions.add(theme.button("Back")).expandCellX().widget();
166+
// Link Buttons
167+
if (addon.getGithubUrl().isPresent()) {
168+
WButton btn = actions.add(theme.button("GitHub")).widget();
169+
final String url = addon.getGithubUrl().get();
170+
btn.action = () -> Util.getOperatingSystem().open(url);
171+
}
172+
if (addon.getDiscordUrl().isPresent()) {
173+
WButton btn = actions.add(theme.button("Discord")).widget();
174+
final String url = addon.getDiscordUrl().get();
175+
btn.action = () -> Util.getOperatingSystem().open(url);
176+
}
177+
if (addon.getHomepageUrl().isPresent()) {
178+
WButton btn = actions.add(theme.button("Homepage")).widget();
179+
final String url = addon.getHomepageUrl().get();
180+
btn.action = () -> Util.getOperatingSystem().open(url);
181+
}
182+
183+
// Back Button
184+
WButton backButton = actions.add(theme.button("Back")).widget();
179185
backButton.action = () -> mc.setScreen(parent);
180186
}
181187
}

0 commit comments

Comments
 (0)