Skip to content
This repository was archived by the owner on Nov 28, 2025. It is now read-only.

Commit 67aad89

Browse files
committed
improve skin management ux
1 parent 628ea5f commit 67aad89

File tree

10 files changed

+341
-69
lines changed

10 files changed

+341
-69
lines changed
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
/*
2+
* Copyright © 2025 moehreag <[email protected]> & Contributors
3+
*
4+
* This file is part of AxolotlClient.
5+
*
6+
* This program is free software; you can redistribute it and/or
7+
* modify it under the terms of the GNU Lesser General Public
8+
* License as published by the Free Software Foundation; either
9+
* version 3 of the License, or (at your option) any later version.
10+
*
11+
* This program is distributed in the hope that it will be useful,
12+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14+
* Lesser General Public License for more details.
15+
*
16+
* You should have received a copy of the GNU Lesser General Public License
17+
* along with this program; if not, write to the Free Software Foundation,
18+
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19+
*
20+
* For more information, see the LICENSE file.
21+
*/
22+
23+
package io.github.axolotlclient.modules.auth.skin;
24+
25+
import net.minecraft.client.gui.screen.Screen;
26+
import net.minecraft.client.util.math.MatrixStack;
27+
import net.minecraft.text.Text;
28+
import net.minecraft.util.Util;
29+
30+
public class LoadingScreen extends Screen {
31+
private final Text description;
32+
33+
public LoadingScreen(Text title, Text description) {
34+
super(title);
35+
this.description = description;
36+
}
37+
38+
@Override
39+
public void render(MatrixStack graphics, int mouseX, int mouseY, float delta) {
40+
renderBackground(graphics);
41+
drawCenteredText(graphics, textRenderer, getTitle(), width / 2, 33 / 2 - textRenderer.fontHeight / 2, -1);
42+
int centerX = width / 2;
43+
int centerY = height / 2;
44+
var text = description;
45+
textRenderer.draw(graphics, text, centerX - textRenderer.getWidth(text) / 2f, centerY - 9, -1);
46+
String string = switch ((int) (Util.getMeasuringTimeMs() / 300L % 4L)) {
47+
case 1, 3 -> "o O o";
48+
case 2 -> "o o O";
49+
default -> "O o o";
50+
};
51+
textRenderer.draw(graphics, string, centerX - textRenderer.getWidth(string) / 2f, centerY + 9, 0xFF808080);
52+
}
53+
}

1.16_combat-6/src/main/java/io/github/axolotlclient/modules/auth/skin/SkinManagementScreen.java

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ public class SkinManagementScreen extends Screen {
8585
private SkinWidget current;
8686
private final Watcher skinDirWatcher;
8787
private final List<Drawable> drawables = new ArrayList<>();
88-
private final CompletableFuture<?> refreshFuture;
88+
private final CompletableFuture<MSApi.MCProfile> loadingFuture;
8989
private Text tooltip;
9090

9191
public SkinManagementScreen(Screen parent, Account account) {
@@ -96,11 +96,9 @@ public SkinManagementScreen(Screen parent, Account account) {
9696
AxolotlClientCommon.getInstance().getLogger().info("Reloading screen as local files changed!");
9797
loadSkinsList();
9898
});
99-
if (account.needsRefresh()) {
100-
refreshFuture = account.refresh(Auth.getInstance().getMsApi());
101-
} else {
102-
refreshFuture = CompletableFuture.completedFuture(null);
103-
}
99+
loadingFuture = (account.needsRefresh() ? account.refresh(Auth.getInstance().getMsApi())
100+
: CompletableFuture.completedFuture(null))
101+
.thenComposeAsync(unused -> Auth.getInstance().getMsApi().getProfile(account));
104102
}
105103

106104
@Override
@@ -208,8 +206,7 @@ protected MutableText getNarrationMessage() {
208206
addWidgets.run();
209207
return;
210208
}
211-
refreshFuture.thenComposeAsync(unused -> Auth.getInstance().getMsApi().getProfile(account))
212-
.thenAcceptAsync(profile -> {
209+
loadingFuture.thenAcceptAsync(profile -> {
213210
cachedProfile = profile;
214211
initDisplay();
215212
addWidgets.run();
@@ -624,6 +621,7 @@ public Entry(int height, SkinWidget widget, @Nullable Text label) {
624621
this.actionButtons.add(new SpriteButton(new TranslatableText("skins.manage.delete"), btn -> {
625622
btn.active = false;
626623
client.openScreen(new ConfirmScreen(confirmed -> {
624+
client.openScreen(new LoadingScreen(getTitle(), new TranslatableText("menu.working")));
627625
if (confirmed) {
628626
try {
629627
Files.delete(local.file());
@@ -673,7 +671,7 @@ public void renderButton(MatrixStack guiGraphics, int mouseX, int mouseY, float
673671
if (client.currentScreen == SkinManagementScreen.this) {
674672
refreshCurrentList();
675673
} else {
676-
client.openScreen(SkinManagementScreen.this);
674+
client.execute(() -> client.openScreen(SkinManagementScreen.this));
677675
}
678676
}).exceptionally(t -> {
679677
AxolotlClientCommon.getInstance().getLogger().warn("Failed to equip asset!", t);
@@ -682,6 +680,7 @@ public void renderButton(MatrixStack guiGraphics, int mouseX, int mouseY, float
682680
});
683681
if (asset instanceof Skin && !(current.getSkin() instanceof Skin.Local)) {
684682
client.openScreen(new ConfirmScreen(confirmed -> {
683+
client.openScreen(new LoadingScreen(getTitle(), TEXT_EQUIPPING));
685684
if (confirmed) {
686685
consumer.accept(download(current.getSkin()).thenCompose(a -> widget.equip()));
687686
} else {
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
/*
2+
* Copyright © 2025 moehreag <[email protected]> & Contributors
3+
*
4+
* This file is part of AxolotlClient.
5+
*
6+
* This program is free software; you can redistribute it and/or
7+
* modify it under the terms of the GNU Lesser General Public
8+
* License as published by the Free Software Foundation; either
9+
* version 3 of the License, or (at your option) any later version.
10+
*
11+
* This program is distributed in the hope that it will be useful,
12+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14+
* Lesser General Public License for more details.
15+
*
16+
* You should have received a copy of the GNU Lesser General Public License
17+
* along with this program; if not, write to the Free Software Foundation,
18+
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19+
*
20+
* For more information, see the LICENSE file.
21+
*/
22+
23+
package io.github.axolotlclient.modules.auth.skin;
24+
25+
import net.minecraft.client.gui.GuiGraphics;
26+
import net.minecraft.client.gui.screen.LoadingDisplay;
27+
import net.minecraft.client.gui.screen.Screen;
28+
import net.minecraft.client.gui.screen.narration.NarrationMessageBuilder;
29+
import net.minecraft.client.gui.widget.ClickableWidget;
30+
import net.minecraft.client.gui.widget.TextWidget;
31+
import net.minecraft.text.Text;
32+
import net.minecraft.util.Util;
33+
34+
public class LoadingScreen extends Screen {
35+
private final Text description;
36+
37+
public LoadingScreen(Text title, Text description) {
38+
super(title);
39+
this.description = description;
40+
}
41+
42+
@Override
43+
protected void init() {
44+
int headerHeight = 33;
45+
int contentHeight = height - headerHeight * 2;
46+
var titleWidget = new TextWidget(width / 2 - textRenderer.getWidth(getTitle()) / 2, headerHeight / 2 - textRenderer.fontHeight / 2, textRenderer.getWidth(getTitle()), textRenderer.fontHeight, getTitle(), textRenderer);
47+
addDrawableChild(titleWidget);
48+
49+
var loadingPlaceholder = new ClickableWidget(0, headerHeight, width, contentHeight, description) {
50+
@Override
51+
protected void drawWidget(GuiGraphics graphics, int mouseX, int mouseY, float delta) {
52+
int centerX = this.getX() + this.getWidth() / 2;
53+
int centerY = this.getY() + this.getHeight() / 2;
54+
Text text = this.getMessage();
55+
graphics.drawText(textRenderer, text, centerX - textRenderer.getWidth(text) / 2, centerY - 9, -1, false);
56+
String string = LoadingDisplay.get(Util.getMeasuringTimeMs());
57+
graphics.drawText(textRenderer, string, centerX - textRenderer.getWidth(string) / 2, centerY + 9, 0xFF808080, false);
58+
}
59+
60+
@Override
61+
protected void updateNarration(NarrationMessageBuilder builder) {
62+
63+
}
64+
};
65+
addDrawableChild(loadingPlaceholder);
66+
}
67+
}

1.20/src/main/java/io/github/axolotlclient/modules/auth/skin/SkinManagementScreen.java

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ public class SkinManagementScreen extends Screen {
7676
private boolean capesTab;
7777
private SkinWidget current;
7878
private final Watcher skinDirWatcher;
79-
private final CompletableFuture<?> refreshFuture;
79+
private final CompletableFuture<MSApi.MCProfile> loadingFuture;
8080

8181
public SkinManagementScreen(Screen parent, Account account) {
8282
super(Text.translatable("skins.manage"));
@@ -86,11 +86,9 @@ public SkinManagementScreen(Screen parent, Account account) {
8686
AxolotlClientCommon.getInstance().getLogger().info("Reloading screen as local files changed!");
8787
loadSkinsList();
8888
});
89-
if (account.needsRefresh()) {
90-
refreshFuture = account.refresh(Auth.getInstance().getMsApi());
91-
} else {
92-
refreshFuture = CompletableFuture.completedFuture(null);
93-
}
89+
loadingFuture = (account.needsRefresh() ? account.refresh(Auth.getInstance().getMsApi())
90+
: CompletableFuture.completedFuture(null))
91+
.thenComposeAsync(unused -> Auth.getInstance().getMsApi().getProfile(account));
9492
}
9593

9694
@Override
@@ -197,8 +195,7 @@ protected void updateNarration(NarrationMessageBuilder builder) {
197195
addWidgets.run();
198196
return;
199197
}
200-
refreshFuture.thenComposeAsync(unused -> Auth.getInstance().getMsApi().getProfile(account))
201-
.thenAcceptAsync(profile -> {
198+
loadingFuture.thenAcceptAsync(profile -> {
202199
cachedProfile = profile;
203200
initDisplay();
204201
addWidgets.run();
@@ -592,6 +589,7 @@ public Entry(int height, SkinWidget widget, @Nullable Text label) {
592589
this.actionButtons.add(new SpriteButton(Text.translatable("skins.manage.delete"), btn -> {
593590
btn.active = false;
594591
client.setScreen(new ConfirmScreen(confirmed -> {
592+
client.setScreen(new LoadingScreen(getTitle(), Text.translatable("menu.working")));
595593
if (confirmed) {
596594
try {
597595
Files.delete(local.file());
@@ -641,7 +639,7 @@ protected void drawWidget(GuiGraphics guiGraphics, int mouseX, int mouseY, float
641639
if (client.currentScreen == SkinManagementScreen.this) {
642640
refreshCurrentList();
643641
} else {
644-
client.setScreen(SkinManagementScreen.this);
642+
client.execute(() -> client.setScreen(SkinManagementScreen.this));
645643
}
646644
}).exceptionally(t -> {
647645
AxolotlClientCommon.getInstance().getLogger().warn("Failed to equip asset!", t);
@@ -650,6 +648,7 @@ protected void drawWidget(GuiGraphics guiGraphics, int mouseX, int mouseY, float
650648
});
651649
if (asset instanceof Skin && !(current.getSkin() instanceof Skin.Local)) {
652650
client.setScreen(new ConfirmScreen(confirmed -> {
651+
client.setScreen(new LoadingScreen(getTitle(), TEXT_EQUIPPING));
653652
if (confirmed) {
654653
consumer.accept(download(current.getSkin()).thenCompose(a -> widget.equip()));
655654
} else {
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
/*
2+
* Copyright © 2025 moehreag <[email protected]> & Contributors
3+
*
4+
* This file is part of AxolotlClient.
5+
*
6+
* This program is free software; you can redistribute it and/or
7+
* modify it under the terms of the GNU Lesser General Public
8+
* License as published by the Free Software Foundation; either
9+
* version 3 of the License, or (at your option) any later version.
10+
*
11+
* This program is distributed in the hope that it will be useful,
12+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14+
* Lesser General Public License for more details.
15+
*
16+
* You should have received a copy of the GNU Lesser General Public License
17+
* along with this program; if not, write to the Free Software Foundation,
18+
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19+
*
20+
* For more information, see the LICENSE file.
21+
*/
22+
23+
package io.github.axolotlclient.modules.auth.skin;
24+
25+
import net.minecraft.client.gui.screen.Screen;
26+
import net.minecraft.client.gui.widget.LoadingTextWidget;
27+
import net.minecraft.client.gui.widget.text.TextWidget;
28+
import net.minecraft.text.Text;
29+
30+
public class LoadingScreen extends Screen {
31+
private final Text description;
32+
33+
public LoadingScreen(Text title, Text description) {
34+
super(title);
35+
this.description = description;
36+
}
37+
38+
@Override
39+
protected void init() {
40+
int headerHeight = 33;
41+
int contentHeight = height - headerHeight * 2;
42+
var titleWidget = new TextWidget(width / 2 - textRenderer.getWidth(getTitle()) / 2, headerHeight / 2 - textRenderer.fontHeight / 2, textRenderer.getWidth(getTitle()), textRenderer.fontHeight, getTitle(), textRenderer);
43+
addDrawableSelectableElement(titleWidget);
44+
45+
var loadingPlaceholder = new LoadingTextWidget(textRenderer, description);
46+
loadingPlaceholder.setDimensionsAndPosition(width, contentHeight, 0,
47+
headerHeight);
48+
addDrawableSelectableElement(loadingPlaceholder);
49+
}
50+
}

1.21/src/main/java/io/github/axolotlclient/modules/auth/skin/SkinManagementScreen.java

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ public class SkinManagementScreen extends Screen {
8585
private boolean capesTab;
8686
private SkinWidget current;
8787
private final Watcher skinDirWatcher;
88-
private final CompletableFuture<?> refreshFuture;
88+
private final CompletableFuture<MSApi.MCProfile> loadingFuture;
8989

9090
public SkinManagementScreen(Screen parent, Account account) {
9191
super(Text.translatable("skins.manage"));
@@ -95,11 +95,9 @@ public SkinManagementScreen(Screen parent, Account account) {
9595
AxolotlClientCommon.getInstance().getLogger().info("Reloading screen as local files changed!");
9696
loadSkinsList();
9797
});
98-
if (account.needsRefresh()) {
99-
refreshFuture = account.refresh(Auth.getInstance().getMsApi());
100-
} else {
101-
refreshFuture = CompletableFuture.completedFuture(null);
102-
}
98+
loadingFuture = (account.needsRefresh() ? account.refresh(Auth.getInstance().getMsApi())
99+
: CompletableFuture.completedFuture(null))
100+
.thenComposeAsync(unused -> Auth.getInstance().getMsApi().getProfile(account));
103101
}
104102

105103
@Override
@@ -193,8 +191,7 @@ protected void init() {
193191
addWidgets.run();
194192
return;
195193
}
196-
refreshFuture.thenComposeAsync(unused -> Auth.getInstance().getMsApi().getProfile(account))
197-
.thenAcceptAsync(profile -> {
194+
loadingFuture.thenAcceptAsync(profile -> {
198195
cachedProfile = profile;
199196
initDisplay();
200197
addWidgets.run();
@@ -597,6 +594,7 @@ public void drawScrollableText(GuiGraphics graphics, TextRenderer renderer, int
597594
this.actionButtons.add(new SpriteButton(Text.translatable("skins.manage.delete"), btn -> {
598595
btn.active = false;
599596
client.setScreen(new ConfirmScreen(confirmed -> {
597+
client.setScreen(new LoadingScreen(getTitle(), Text.translatable("menu.working")));
600598
if (confirmed) {
601599
try {
602600
Files.delete(local.file());
@@ -646,7 +644,7 @@ protected void drawWidget(GuiGraphics guiGraphics, int mouseX, int mouseY, float
646644
if (client.currentScreen == SkinManagementScreen.this) {
647645
refreshCurrentList();
648646
} else {
649-
client.setScreen(SkinManagementScreen.this);
647+
client.execute(() -> client.setScreen(SkinManagementScreen.this));
650648
}
651649
}).exceptionally(t -> {
652650
AxolotlClientCommon.getInstance().getLogger().warn("Failed to equip asset!", t);
@@ -655,6 +653,7 @@ protected void drawWidget(GuiGraphics guiGraphics, int mouseX, int mouseY, float
655653
});
656654
if (asset instanceof Skin && !(current.getSkin() instanceof Skin.Local)) {
657655
client.setScreen(new ConfirmScreen(confirmed -> {
656+
client.setScreen(new LoadingScreen(getTitle(), TEXT_EQUIPPING));
658657
if (confirmed) {
659658
consumer.accept(download(current.getSkin()).thenCompose(a -> widget.equip()));
660659
} else {

0 commit comments

Comments
 (0)