From 434f707a4b56f98aa8ad76c42d2cdff9b548ca0d Mon Sep 17 00:00:00 2001 From: Ishaan Kothari Date: Sat, 5 Sep 2026 20:14:37 -0700 Subject: [PATCH 1/2] feat(ui): add a World Backups icon to the title and pause screen icon rows --- CHANGELOG.md | 8 + .../worldarchive/WorldArchiveClient.java | 2 + .../ui/IconRowBackupIntegration.java | 140 ++++++++++++++++++ .../ui/WorldArchiveIconButton.java | 31 ++++ .../assets/worldarchive/lang/en_us.json | 1 + .../textures/gui/sprites/world_backups.png | Bin 0 -> 697 bytes 6 files changed, 182 insertions(+) create mode 100644 src/client/java/dev/ishaanko/worldarchive/ui/IconRowBackupIntegration.java create mode 100644 src/client/java/dev/ishaanko/worldarchive/ui/WorldArchiveIconButton.java create mode 100644 src/main/resources/assets/worldarchive/textures/gui/sprites/world_backups.png diff --git a/CHANGELOG.md b/CHANGELOG.md index 1898542..f19c6aa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## Unreleased + +### Added + +- A World Backups icon sits at the far right of the square icons on the title + screen and on the pause screen. On the title screen it opens the list of + backed-up worlds; in a world it opens that world's backups. + ## 0.3.5.1 (2026-08-24) ### Fixed diff --git a/src/client/java/dev/ishaanko/worldarchive/WorldArchiveClient.java b/src/client/java/dev/ishaanko/worldarchive/WorldArchiveClient.java index 6c8c65e..c4d8874 100644 --- a/src/client/java/dev/ishaanko/worldarchive/WorldArchiveClient.java +++ b/src/client/java/dev/ishaanko/worldarchive/WorldArchiveClient.java @@ -3,6 +3,7 @@ import dev.ishaanko.worldarchive.runtime.WorldArchiveRuntime; import dev.ishaanko.worldarchive.settings.ClientSettingsAccess; import dev.ishaanko.worldarchive.ui.EditWorldBackupIntegration; +import dev.ishaanko.worldarchive.ui.IconRowBackupIntegration; import dev.ishaanko.worldarchive.ui.SelectWorldBackupIntegration; import net.fabricmc.api.ClientModInitializer; import org.slf4j.Logger; @@ -17,6 +18,7 @@ public void onInitializeClient() { WorldArchiveRuntime runtime = WorldArchiveRuntime.initialize(); SelectWorldBackupIntegration.register(() -> runtime); EditWorldBackupIntegration.register(() -> runtime); + IconRowBackupIntegration.register(() -> runtime, runtime::openBrowser); LOGGER.info("{} initialized.", WorldArchiveMetadata.MOD_NAME); } } diff --git a/src/client/java/dev/ishaanko/worldarchive/ui/IconRowBackupIntegration.java b/src/client/java/dev/ishaanko/worldarchive/ui/IconRowBackupIntegration.java new file mode 100644 index 0000000..eb7ae71 --- /dev/null +++ b/src/client/java/dev/ishaanko/worldarchive/ui/IconRowBackupIntegration.java @@ -0,0 +1,140 @@ +package dev.ishaanko.worldarchive.ui; + +import java.util.ArrayList; +import java.util.Comparator; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.function.Supplier; +import java.util.stream.Collectors; +import net.fabricmc.fabric.api.client.screen.v1.ScreenEvents; +import net.fabricmc.fabric.api.client.screen.v1.Screens; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.components.AbstractWidget; +import net.minecraft.client.gui.components.Button; +import net.minecraft.client.gui.components.FriendsButton; +import net.minecraft.client.gui.screens.PauseScreen; +import net.minecraft.client.gui.screens.Screen; +import net.minecraft.client.gui.screens.TitleScreen; + +/** + * Adds the WorldArchive shortcut at the far right of the square-icon row on the title screen + * and the pause screen. On the title screen it opens the world list; in a world it opens + * that world's backups. + */ +public final class IconRowBackupIntegration { + private static final int GAP = 4; + + private static final AtomicBoolean REGISTERED = new AtomicBoolean(); + + private static volatile Supplier facadeSupplier; + + private static volatile Runnable openLiveWorldBackups; + + private IconRowBackupIntegration() { + } + + /** + * Registers the global Fabric screen hook. Repeated calls update the actions. + * + * @param facade supplies the facade the title-screen world list needs + * @param openLiveWorld opens the backup browser for the world that is currently loaded + */ + public static void register( + Supplier facade, + Runnable openLiveWorld) { + facadeSupplier = Objects.requireNonNull(facade, "facade"); + openLiveWorldBackups = Objects.requireNonNull(openLiveWorld, "openLiveWorld"); + if (REGISTERED.compareAndSet(false, true)) { + ScreenEvents.AFTER_INIT.register(IconRowBackupIntegration::afterInit); + } + } + + private static void afterInit(Minecraft minecraft, Screen screen, int width, int height) { + Button.OnPress action; + if (screen instanceof TitleScreen) { + action = ignored -> minecraft.setScreenAndShow( + new BackupWorldsScreen(screen, currentFacade())); + } else if (screen instanceof PauseScreen && minecraft.hasSingleplayerServer()) { + action = ignored -> currentLiveWorldAction().run(); + } else { + return; + } + List