From e2d40032c1f17f70d23a788eeb401d1b882264ee Mon Sep 17 00:00:00 2001 From: Surya Date: Sun, 30 May 2021 16:20:52 +0530 Subject: [PATCH] Fix modularity --- build.gradle | 6 + gradle.properties | 4 +- .../mods/SaveCoordinatesClient.java | 62 ------ .../me/bionicbeanie/mods/api/IFileStore.java | 19 -- .../java/me/bionicbeanie/mods/api/IGui.java | 18 -- .../bionicbeanie/mods/api/IPlayerLocator.java | 9 - .../bionicbeanie/mods/api/IRootGridPanel.java | 13 -- .../mods/api/IScreenController.java | 10 - .../bionicbeanie/mods/api/IViewHandler.java | 9 - .../bionicbeanie/mods/api/IViewOperation.java | 5 - .../mods/gui/SaveCoordinatesGui.java | 66 ------ .../mods/gui/view/DefaultViewHandler.java | 149 -------------- .../mods/gui/view/DeleteOperation.java | 18 -- .../mods/gui/view/PingOperation.java | 23 --- .../mods/gui/view/SaveOperation.java | 54 ----- .../mods/gui/view/ShowDetailOperation.java | 19 -- .../mods/gui/view/ViewHandlerBase.java | 41 ---- .../mods/gui/view/ViewOperationBase.java | 26 --- .../gui/view/ViewPositionOperationBase.java | 22 -- .../mods/savecoords/IFileStore.java | 18 ++ .../mods/savecoords/IPlayerLocator.java | 7 + .../savecoords/SaveCoordinatesClient.java | 35 ++++ .../mods/savecoords/gui/IGuiController.java | 9 + .../mods/savecoords/gui/IRootPanel.java | 11 + .../mods/savecoords/gui/IViewHandler.java | 9 + .../gui/SaveCoordinatesScreen.java | 3 +- .../gui/impl/DefaultViewHandler.java | 193 ++++++++++++++++++ .../savecoords/gui/impl/DeleteOperation.java | 17 ++ .../gui/impl/GuiController.java} | 8 +- .../gui/impl}/ListViewHandler.java | 98 ++++++--- .../mods/savecoords/gui/impl/ModGui.java | 18 ++ .../savecoords/gui/impl/PingOperation.java | 20 ++ .../gui/impl}/RootGridPanel.java | 16 +- .../gui/impl/SaveCoordinatesGui.java | 94 +++++++++ .../savecoords/gui/impl/SaveOperation.java | 19 ++ .../savecoords/gui/impl/ViewHandlerBase.java | 42 ++++ .../gui/impl/ViewOperationBase.java | 27 +++ .../mods/savecoords/impl/Factory.java | 16 ++ .../mods/{ => savecoords}/impl/FileStore.java | 10 +- .../{ => savecoords}/impl/PlayerLocator.java | 16 +- .../mods/{ => savecoords}/model/ModData.java | 2 +- .../model/PlayerPosition.java | 2 +- .../model/PlayerRawPosition.java | 2 +- .../model/PositionMetadata.java | 2 +- .../mods/savecoords/util/ResourceUtils.java | 45 ++++ .../mods/util/DimensionSpriteUtil.java | 27 --- .../assets/savecoords/textures/gui/close.png | Bin 0 -> 183 bytes .../assets/savecoords/textures/gui/end.png | Bin 0 -> 5976 bytes .../assets/savecoords/textures/gui/more.png | Bin 0 -> 195 bytes .../assets/savecoords/textures/gui/nether.png | Bin 0 -> 6504 bytes .../savecoords/textures/gui/overworld.png | Bin 0 -> 4844 bytes .../assets/savecoords/textures/gui/ping.png | Bin 0 -> 221 bytes src/main/resources/data/savecoords/end.png | Bin 0 -> 29194 bytes 53 files changed, 691 insertions(+), 648 deletions(-) delete mode 100644 src/main/java/me/bionicbeanie/mods/SaveCoordinatesClient.java delete mode 100644 src/main/java/me/bionicbeanie/mods/api/IFileStore.java delete mode 100644 src/main/java/me/bionicbeanie/mods/api/IGui.java delete mode 100644 src/main/java/me/bionicbeanie/mods/api/IPlayerLocator.java delete mode 100644 src/main/java/me/bionicbeanie/mods/api/IRootGridPanel.java delete mode 100644 src/main/java/me/bionicbeanie/mods/api/IScreenController.java delete mode 100644 src/main/java/me/bionicbeanie/mods/api/IViewHandler.java delete mode 100644 src/main/java/me/bionicbeanie/mods/api/IViewOperation.java delete mode 100644 src/main/java/me/bionicbeanie/mods/gui/SaveCoordinatesGui.java delete mode 100644 src/main/java/me/bionicbeanie/mods/gui/view/DefaultViewHandler.java delete mode 100644 src/main/java/me/bionicbeanie/mods/gui/view/DeleteOperation.java delete mode 100644 src/main/java/me/bionicbeanie/mods/gui/view/PingOperation.java delete mode 100644 src/main/java/me/bionicbeanie/mods/gui/view/SaveOperation.java delete mode 100644 src/main/java/me/bionicbeanie/mods/gui/view/ShowDetailOperation.java delete mode 100644 src/main/java/me/bionicbeanie/mods/gui/view/ViewHandlerBase.java delete mode 100644 src/main/java/me/bionicbeanie/mods/gui/view/ViewOperationBase.java delete mode 100644 src/main/java/me/bionicbeanie/mods/gui/view/ViewPositionOperationBase.java create mode 100644 src/main/java/me/bionicbeanie/mods/savecoords/IFileStore.java create mode 100644 src/main/java/me/bionicbeanie/mods/savecoords/IPlayerLocator.java create mode 100644 src/main/java/me/bionicbeanie/mods/savecoords/SaveCoordinatesClient.java create mode 100644 src/main/java/me/bionicbeanie/mods/savecoords/gui/IGuiController.java create mode 100644 src/main/java/me/bionicbeanie/mods/savecoords/gui/IRootPanel.java create mode 100644 src/main/java/me/bionicbeanie/mods/savecoords/gui/IViewHandler.java rename src/main/java/me/bionicbeanie/mods/{ => savecoords}/gui/SaveCoordinatesScreen.java (86%) create mode 100644 src/main/java/me/bionicbeanie/mods/savecoords/gui/impl/DefaultViewHandler.java create mode 100644 src/main/java/me/bionicbeanie/mods/savecoords/gui/impl/DeleteOperation.java rename src/main/java/me/bionicbeanie/mods/{gui/ScreenController.java => savecoords/gui/impl/GuiController.java} (63%) rename src/main/java/me/bionicbeanie/mods/{gui/view => savecoords/gui/impl}/ListViewHandler.java (51%) create mode 100644 src/main/java/me/bionicbeanie/mods/savecoords/gui/impl/ModGui.java create mode 100644 src/main/java/me/bionicbeanie/mods/savecoords/gui/impl/PingOperation.java rename src/main/java/me/bionicbeanie/mods/{gui => savecoords/gui/impl}/RootGridPanel.java (57%) create mode 100644 src/main/java/me/bionicbeanie/mods/savecoords/gui/impl/SaveCoordinatesGui.java create mode 100644 src/main/java/me/bionicbeanie/mods/savecoords/gui/impl/SaveOperation.java create mode 100644 src/main/java/me/bionicbeanie/mods/savecoords/gui/impl/ViewHandlerBase.java create mode 100644 src/main/java/me/bionicbeanie/mods/savecoords/gui/impl/ViewOperationBase.java create mode 100644 src/main/java/me/bionicbeanie/mods/savecoords/impl/Factory.java rename src/main/java/me/bionicbeanie/mods/{ => savecoords}/impl/FileStore.java (92%) rename src/main/java/me/bionicbeanie/mods/{ => savecoords}/impl/PlayerLocator.java (52%) rename src/main/java/me/bionicbeanie/mods/{ => savecoords}/model/ModData.java (69%) rename src/main/java/me/bionicbeanie/mods/{ => savecoords}/model/PlayerPosition.java (95%) rename src/main/java/me/bionicbeanie/mods/{ => savecoords}/model/PlayerRawPosition.java (93%) rename src/main/java/me/bionicbeanie/mods/{ => savecoords}/model/PositionMetadata.java (95%) create mode 100644 src/main/java/me/bionicbeanie/mods/savecoords/util/ResourceUtils.java delete mode 100644 src/main/java/me/bionicbeanie/mods/util/DimensionSpriteUtil.java create mode 100644 src/main/resources/assets/savecoords/textures/gui/close.png create mode 100644 src/main/resources/assets/savecoords/textures/gui/end.png create mode 100644 src/main/resources/assets/savecoords/textures/gui/more.png create mode 100644 src/main/resources/assets/savecoords/textures/gui/nether.png create mode 100644 src/main/resources/assets/savecoords/textures/gui/overworld.png create mode 100644 src/main/resources/assets/savecoords/textures/gui/ping.png create mode 100644 src/main/resources/data/savecoords/end.png diff --git a/build.gradle b/build.gradle index 15b89b9..2fd94a5 100644 --- a/build.gradle +++ b/build.gradle @@ -15,15 +15,21 @@ repositories { name = "CottonMC" url = "https://server.bbkr.space/artifactory/libs-release" } + maven { + name = "ClothConfig" + url = "https://maven.shedaniel.me/" + } } dependencies { minecraft "com.mojang:minecraft:${project.minecraft_version}" mappings "net.fabricmc:yarn:${project.yarn_mappings}:v2" + modImplementation "net.fabricmc:fabric-loader:${project.loader_version}" modImplementation "net.fabricmc.fabric-api:fabric-api:${project.fabric_version}" modImplementation include("io.github.cottonmc:LibGui:${project.libgui_version}") + modImplementation include("me.shedaniel.cloth:cloth-config-fabric:${project.clothconfig_version}") } processResources { diff --git a/gradle.properties b/gradle.properties index cc65ca9..f514cbc 100644 --- a/gradle.properties +++ b/gradle.properties @@ -9,7 +9,7 @@ org.gradle.jvmargs=-Xmx1G # Mod Properties mod_version = 1.16.5-0.0.3 - maven_group = me.bionicbeanie.mods + maven_group = me.bionicbeanie.mods.savecoords archives_base_name = save-coordinates # Dependencies @@ -18,3 +18,5 @@ org.gradle.jvmargs=-Xmx1G # https://github.com/CottonMC/LibGui/wiki/Setup libgui_version=3.4.0+1.16.5 + + clothconfig_version=4.11.26 diff --git a/src/main/java/me/bionicbeanie/mods/SaveCoordinatesClient.java b/src/main/java/me/bionicbeanie/mods/SaveCoordinatesClient.java deleted file mode 100644 index 6bb780f..0000000 --- a/src/main/java/me/bionicbeanie/mods/SaveCoordinatesClient.java +++ /dev/null @@ -1,62 +0,0 @@ -package me.bionicbeanie.mods; - -import org.lwjgl.glfw.GLFW; - -import me.bionicbeanie.mods.api.IFileStore; -import me.bionicbeanie.mods.api.IGui; -import me.bionicbeanie.mods.api.IViewHandler; -import me.bionicbeanie.mods.api.IPlayerLocator; -import me.bionicbeanie.mods.api.IScreenController; -import me.bionicbeanie.mods.gui.SaveCoordinatesGui; -import me.bionicbeanie.mods.gui.ScreenController; -import me.bionicbeanie.mods.gui.view.DefaultViewHandler; -import me.bionicbeanie.mods.gui.view.ListViewHandler; -import me.bionicbeanie.mods.impl.FileStore; -import me.bionicbeanie.mods.impl.PlayerLocator; -import me.bionicbeanie.mods.model.PlayerPosition; -import net.fabricmc.api.ClientModInitializer; -import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents; -import net.fabricmc.fabric.api.client.keybinding.v1.KeyBindingHelper; -import net.minecraft.client.MinecraftClient; -import net.minecraft.client.options.KeyBinding; -import net.minecraft.client.util.InputUtil; - -public class SaveCoordinatesClient implements ClientModInitializer { - - private static IPlayerLocator locator = new PlayerLocator(); - - @Override - public void onInitializeClient() { - - KeyBinding keyBinding = registerKeyBinding(); - - ClientTickEvents.END_CLIENT_TICK.register(client -> { - while (keyBinding.wasPressed()) { - IGui gui = CreateModGui(client); - gui.showDefaultView(); - } - }); - } - - private KeyBinding registerKeyBinding() { - String translationKey = "key.savecoords.coords"; - String category = "category.savecoords.generic"; - int keyBind = GLFW.GLFW_KEY_H; - - KeyBinding keyBinding = new KeyBinding(translationKey, InputUtil.Type.KEYSYM, keyBind, category); - - return KeyBindingHelper.registerKeyBinding(keyBinding); - } - - private IGui CreateModGui(MinecraftClient client) { - IGui gui = new SaveCoordinatesGui(); - IFileStore fileStore = new FileStore(client.runDirectory.getAbsolutePath()); - IViewHandler defaultHandler = new DefaultViewHandler(fileStore, locator, client, gui); - IViewHandler listHandler = new ListViewHandler(fileStore, gui, client); - IScreenController screenController = new ScreenController(client); - - gui.init(defaultHandler, listHandler, screenController); - - return gui; - } -} diff --git a/src/main/java/me/bionicbeanie/mods/api/IFileStore.java b/src/main/java/me/bionicbeanie/mods/api/IFileStore.java deleted file mode 100644 index c22d8b7..0000000 --- a/src/main/java/me/bionicbeanie/mods/api/IFileStore.java +++ /dev/null @@ -1,19 +0,0 @@ -package me.bionicbeanie.mods.api; - -import java.io.IOException; -import java.util.List; - -import me.bionicbeanie.mods.model.PlayerPosition; - -public interface IFileStore { - - public String getDefaultWorld() throws IOException; - - public void setDefaultWorld(String defaultWorldName) throws IOException; - - public void save(PlayerPosition position) throws IOException; - - public void delete(String id) throws IOException; - - public List list() throws IOException; -} diff --git a/src/main/java/me/bionicbeanie/mods/api/IGui.java b/src/main/java/me/bionicbeanie/mods/api/IGui.java deleted file mode 100644 index 0f8241a..0000000 --- a/src/main/java/me/bionicbeanie/mods/api/IGui.java +++ /dev/null @@ -1,18 +0,0 @@ -package me.bionicbeanie.mods.api; - -import me.bionicbeanie.mods.model.PlayerPosition; - -public interface IGui { - - public void init(IViewHandler saveHandler, IViewHandler listHandler, - IScreenController screenController); - - public void showDefaultView(); - - public void setDefaultViewState(PlayerPosition position); - - public void showListView(); - - public void close(); - -} diff --git a/src/main/java/me/bionicbeanie/mods/api/IPlayerLocator.java b/src/main/java/me/bionicbeanie/mods/api/IPlayerLocator.java deleted file mode 100644 index 7adec47..0000000 --- a/src/main/java/me/bionicbeanie/mods/api/IPlayerLocator.java +++ /dev/null @@ -1,9 +0,0 @@ -package me.bionicbeanie.mods.api; - -import me.bionicbeanie.mods.model.PlayerRawPosition; -import net.minecraft.client.MinecraftClient; - -public interface IPlayerLocator { - - public PlayerRawPosition locate(MinecraftClient client); -} diff --git a/src/main/java/me/bionicbeanie/mods/api/IRootGridPanel.java b/src/main/java/me/bionicbeanie/mods/api/IRootGridPanel.java deleted file mode 100644 index a23bb5d..0000000 --- a/src/main/java/me/bionicbeanie/mods/api/IRootGridPanel.java +++ /dev/null @@ -1,13 +0,0 @@ -package me.bionicbeanie.mods.api; - -import io.github.cottonmc.cotton.gui.GuiDescription; -import io.github.cottonmc.cotton.gui.widget.WWidget; - -public interface IRootGridPanel { - - public void add(WWidget widget, int x, int y, int w, int h); - - public void validate(GuiDescription gui); - - public void reset(); -} diff --git a/src/main/java/me/bionicbeanie/mods/api/IScreenController.java b/src/main/java/me/bionicbeanie/mods/api/IScreenController.java deleted file mode 100644 index 5da4cbc..0000000 --- a/src/main/java/me/bionicbeanie/mods/api/IScreenController.java +++ /dev/null @@ -1,10 +0,0 @@ -package me.bionicbeanie.mods.api; - -import net.minecraft.client.gui.screen.Screen; - -public interface IScreenController { - - public void openScreen(Screen screen); - - public void closeScreen(); -} diff --git a/src/main/java/me/bionicbeanie/mods/api/IViewHandler.java b/src/main/java/me/bionicbeanie/mods/api/IViewHandler.java deleted file mode 100644 index 96ebf14..0000000 --- a/src/main/java/me/bionicbeanie/mods/api/IViewHandler.java +++ /dev/null @@ -1,9 +0,0 @@ -package me.bionicbeanie.mods.api; - -public interface IViewHandler { - - void setState(T state); - void clearState(); - void placeWidgets(IRootGridPanel rootPanel); - -} diff --git a/src/main/java/me/bionicbeanie/mods/api/IViewOperation.java b/src/main/java/me/bionicbeanie/mods/api/IViewOperation.java deleted file mode 100644 index 0429878..0000000 --- a/src/main/java/me/bionicbeanie/mods/api/IViewOperation.java +++ /dev/null @@ -1,5 +0,0 @@ -package me.bionicbeanie.mods.api; - -public interface IViewOperation extends Runnable { - -} diff --git a/src/main/java/me/bionicbeanie/mods/gui/SaveCoordinatesGui.java b/src/main/java/me/bionicbeanie/mods/gui/SaveCoordinatesGui.java deleted file mode 100644 index 04d9f22..0000000 --- a/src/main/java/me/bionicbeanie/mods/gui/SaveCoordinatesGui.java +++ /dev/null @@ -1,66 +0,0 @@ -package me.bionicbeanie.mods.gui; - -import io.github.cottonmc.cotton.gui.client.LightweightGuiDescription; -import me.bionicbeanie.mods.api.IGui; -import me.bionicbeanie.mods.api.IViewHandler; -import me.bionicbeanie.mods.model.PlayerPosition; -import me.bionicbeanie.mods.api.IRootGridPanel; -import me.bionicbeanie.mods.api.IScreenController; - -public class SaveCoordinatesGui extends LightweightGuiDescription implements IGui { - - private IRootGridPanel rootGridPanel; - private IViewHandler defaultHandler; - private IViewHandler listHandler; - private IScreenController screenController; - - @Override - public void init(IViewHandler saveHandler, IViewHandler listHandler, - IScreenController screenController) { - this.rootGridPanel = createRootPanel(); - this.defaultHandler = saveHandler; - this.listHandler = listHandler; - this.screenController = screenController; - - openScreen(); - } - - @Override - public void showDefaultView() { - showView(defaultHandler); - } - - @Override - public void setDefaultViewState(PlayerPosition position) { - defaultHandler.setState(position); - } - - @Override - public void showListView() { - showView(listHandler); - } - - @Override - public void close() { - screenController.closeScreen(); - } - - private void openScreen() { - screenController.openScreen(new SaveCoordinatesScreen(this)); - } - - private IRootGridPanel createRootPanel() { - RootGridPanel panel = new RootGridPanel(18); - panel.setSize(15 * 18, 10 * 18); - - setRootPanel(panel); - return panel; - } - - private void showView(IViewHandler handler) { - rootGridPanel.reset(); - handler.placeWidgets(rootGridPanel); - rootGridPanel.validate(this); - } - -} diff --git a/src/main/java/me/bionicbeanie/mods/gui/view/DefaultViewHandler.java b/src/main/java/me/bionicbeanie/mods/gui/view/DefaultViewHandler.java deleted file mode 100644 index 82ce041..0000000 --- a/src/main/java/me/bionicbeanie/mods/gui/view/DefaultViewHandler.java +++ /dev/null @@ -1,149 +0,0 @@ -package me.bionicbeanie.mods.gui.view; - -import io.github.cottonmc.cotton.gui.widget.WButton; -import io.github.cottonmc.cotton.gui.widget.WLabel; -import io.github.cottonmc.cotton.gui.widget.WText; -import io.github.cottonmc.cotton.gui.widget.WTextField; -import io.github.cottonmc.cotton.gui.widget.WWidget; -import me.bionicbeanie.mods.api.IFileStore; -import me.bionicbeanie.mods.api.IGui; -import me.bionicbeanie.mods.api.IPlayerLocator; -import me.bionicbeanie.mods.api.IRootGridPanel; -import me.bionicbeanie.mods.model.PlayerPosition; -import me.bionicbeanie.mods.model.PlayerRawPosition; -import me.bionicbeanie.mods.util.DimensionSpriteUtil; -import net.minecraft.client.MinecraftClient; -import net.minecraft.text.LiteralText; - -public class DefaultViewHandler extends ViewHandlerBase { - - private IPlayerLocator locator; - - public DefaultViewHandler(IFileStore fileStore, IPlayerLocator locator, MinecraftClient client, IGui gui) { - super(fileStore, gui, client); - this.locator = locator; - } - - @Override - public void placeWidgets(IRootGridPanel root, PlayerPosition existingPosition) { - - PlayerRawPosition rawPosition = existingPosition == null ? locator.locate(client) : existingPosition; - - WWidget xLabel = CreateLabelForCoorindate("X"); - WWidget yLabel = CreateLabelForCoorindate("Y"); - WWidget zLabel = CreateLabelForCoorindate("Z"); - - WWidget xText = CreateWidgetForCoordinate(rawPosition.getX()); - WWidget yText = CreateWidgetForCoordinate(rawPosition.getY()); - WWidget zText = CreateWidgetForCoordinate(rawPosition.getZ()); - - root.add(xLabel, 2, 1, 2, 1); - root.add(yLabel, 2, 2, 2, 1); - root.add(zLabel, 2, 3, 2, 1); - - root.add(xText, 3, 1, 2, 1); - root.add(yText, 3, 2, 2, 1); - root.add(zText, 3, 3, 2, 1); - - WWidget icon = DimensionSpriteUtil.CreateWorldIcon(rawPosition.getWorldDimension()); - root.add(icon, 8, 1, 2, 2); - - String defaultWorld = getDefaultWorld(existingPosition); - WTextField world = CreateWorldField(defaultWorld); - root.add(world, 0, 4, 4, 1); - - WTextField location = CreateLocationField(existingPosition); - root.add(location, 5, 4, 7, 1); - - WTextField notes = CreateNotesField(existingPosition); - root.add(notes, 0, 6, 12, 1); - - WWidget save = CreateSaveButton(rawPosition, location, notes, world, existingPosition); - root.add(save, 13, 6, 2, 1); - - WWidget list = CreateListButton(); - root.add(list, 13, 9, 2, 1); - - WWidget ping = CreatePingButton(rawPosition); - root.add(ping, 13, 1, 2, 1); - - WWidget close = CreateCloseButton(); - root.add(close, 0, 9, 2, 1); - } - - private String getDefaultWorld(PlayerPosition existingPosition) { - if (existingPosition != null && existingPosition.getPositionMetadata().getWorldName() != null) { - return existingPosition.getPositionMetadata().getWorldName(); - } - - try { - return fileStore.getDefaultWorld(); - } catch (Exception e) { - e.printStackTrace(); - } - - return ""; - } - - private WWidget CreateLabelForCoorindate(String label) { - return new WLabel(label, 0xb80000); - } - - private WWidget CreateWidgetForCoordinate(long l) { - return new WText(new LiteralText(String.valueOf(l)), 0x3939ac); - } - - private WTextField CreateLocationField(PlayerPosition existingPosition) { - WTextField location = new WTextField(new LiteralText("location name")); - - if (existingPosition != null) { - location.setText(existingPosition.getLocationName()); - } - - location.setMaxLength(20); - - return location; - } - - private WTextField CreateNotesField(PlayerPosition existingPosition) { - WTextField notes = new WTextField(new LiteralText("additional notes")); - - if (existingPosition != null && existingPosition.getPositionMetadata() != null) { - notes.setText(existingPosition.getPositionMetadata().getNotes()); - } - - return notes; - } - - private WTextField CreateWorldField(String defaultWorld) { - WTextField world = new WTextField(new LiteralText("world name")); - world.setMaxLength(7); - world.setText(defaultWorld); - return world; - } - - private WWidget CreateSaveButton(PlayerRawPosition rawPosition, WTextField location, WTextField notes, - WTextField world, PlayerPosition existingPosition) { - WButton button = new WButton(new LiteralText("SAVE")); - button.setOnClick(new SaveOperation(fileStore, gui, rawPosition, world, location, notes, existingPosition)); - return button; - } - - private WWidget CreateListButton() { - WButton button = new WButton(new LiteralText("LIST")); - button.setOnClick(gui::showListView); - return button; - } - - private WWidget CreatePingButton(PlayerRawPosition rawPosition) { - WButton button = new WButton(new LiteralText("PING")); - button.setOnClick(new PingOperation(fileStore, gui, rawPosition)); - return button; - } - - private WWidget CreateCloseButton() { - WButton button = new WButton(new LiteralText("CLOSE")); - button.setOnClick(gui::close); - return button; - } -} diff --git a/src/main/java/me/bionicbeanie/mods/gui/view/DeleteOperation.java b/src/main/java/me/bionicbeanie/mods/gui/view/DeleteOperation.java deleted file mode 100644 index 063e87d..0000000 --- a/src/main/java/me/bionicbeanie/mods/gui/view/DeleteOperation.java +++ /dev/null @@ -1,18 +0,0 @@ -package me.bionicbeanie.mods.gui.view; - -import me.bionicbeanie.mods.api.IFileStore; -import me.bionicbeanie.mods.api.IGui; -import me.bionicbeanie.mods.model.PlayerPosition; - -public class DeleteOperation extends ViewPositionOperationBase { - - public DeleteOperation(IFileStore fileStore, IGui gui, PlayerPosition position) { - super(fileStore, gui, position); - } - - @Override - protected void executeOperation(IFileStore fileStore, IGui gui, PlayerPosition position) throws Exception { - fileStore.delete(position.getId()); - gui.showListView(); - } -} diff --git a/src/main/java/me/bionicbeanie/mods/gui/view/PingOperation.java b/src/main/java/me/bionicbeanie/mods/gui/view/PingOperation.java deleted file mode 100644 index 0c69121..0000000 --- a/src/main/java/me/bionicbeanie/mods/gui/view/PingOperation.java +++ /dev/null @@ -1,23 +0,0 @@ -package me.bionicbeanie.mods.gui.view; - -import me.bionicbeanie.mods.api.IFileStore; -import me.bionicbeanie.mods.api.IGui; -import me.bionicbeanie.mods.model.PlayerRawPosition; -import net.minecraft.client.MinecraftClient; - -public class PingOperation extends ViewOperationBase{ - - private PlayerRawPosition position; - - public PingOperation(IFileStore fileStore, IGui gui, PlayerRawPosition position) { - super(fileStore, gui); - - this.position = position; - } - - @SuppressWarnings("resource") - @Override - protected void executeOperation(IFileStore fileStore, IGui gui) throws Exception { - MinecraftClient.getInstance().player.sendChatMessage(position.toString()); - } -} diff --git a/src/main/java/me/bionicbeanie/mods/gui/view/SaveOperation.java b/src/main/java/me/bionicbeanie/mods/gui/view/SaveOperation.java deleted file mode 100644 index 961b35d..0000000 --- a/src/main/java/me/bionicbeanie/mods/gui/view/SaveOperation.java +++ /dev/null @@ -1,54 +0,0 @@ -package me.bionicbeanie.mods.gui.view; - -import java.util.UUID; - -import io.github.cottonmc.cotton.gui.widget.WTextField; -import me.bionicbeanie.mods.api.IFileStore; -import me.bionicbeanie.mods.api.IGui; -import me.bionicbeanie.mods.model.PlayerPosition; -import me.bionicbeanie.mods.model.PlayerRawPosition; -import me.bionicbeanie.mods.model.PositionMetadata; - -public class SaveOperation extends ViewOperationBase { - - private PlayerRawPosition rawPosition; - private WTextField location, notes; - private WTextField world; - private PlayerPosition existingPosition; - - public SaveOperation(IFileStore fileStore, IGui gui, PlayerRawPosition rawPosition, WTextField world, - WTextField location, WTextField notes, PlayerPosition existingPosition) { - super(fileStore, gui); - this.rawPosition = rawPosition; - this.existingPosition = existingPosition; - this.world = world; - this.location = location; - this.notes = notes; - } - - @Override - protected void executeOperation(IFileStore fileStore, IGui gui) throws Exception { - - String id = existingPosition == null ? UUID.randomUUID().toString() : existingPosition.getId(); - PositionMetadata metadata = CreateMetadata(existingPosition); - - PlayerPosition position = new PlayerPosition(id, rawPosition, this.location.getText(), metadata); - - fileStore.save(position); - fileStore.setDefaultWorld(position.getPositionMetadata().getWorldName()); - gui.showListView(); - gui.setDefaultViewState(null); - } - - private PositionMetadata CreateMetadata(PlayerPosition existingPosition) { - if(existingPosition == null) { - return new PositionMetadata(this.world.getText(), this.notes.getText()); - } - - existingPosition.getPositionMetadata().setWorldName(this.world.getText()); - existingPosition.getPositionMetadata().setNotes(this.notes.getText()); - existingPosition.getPositionMetadata().updateLastModified(); - - return existingPosition.getPositionMetadata(); - } -} diff --git a/src/main/java/me/bionicbeanie/mods/gui/view/ShowDetailOperation.java b/src/main/java/me/bionicbeanie/mods/gui/view/ShowDetailOperation.java deleted file mode 100644 index eb62b8e..0000000 --- a/src/main/java/me/bionicbeanie/mods/gui/view/ShowDetailOperation.java +++ /dev/null @@ -1,19 +0,0 @@ -package me.bionicbeanie.mods.gui.view; - -import me.bionicbeanie.mods.api.IFileStore; -import me.bionicbeanie.mods.api.IGui; -import me.bionicbeanie.mods.model.PlayerPosition; - -public class ShowDetailOperation extends ViewPositionOperationBase { - - public ShowDetailOperation(IFileStore fileStore, IGui gui, PlayerPosition position) { - super(fileStore, gui, position); - } - - @Override - protected void executeOperation(IFileStore fileStore, IGui gui, PlayerPosition position) throws Exception { - gui.setDefaultViewState(position); - gui.showDefaultView(); - } - -} diff --git a/src/main/java/me/bionicbeanie/mods/gui/view/ViewHandlerBase.java b/src/main/java/me/bionicbeanie/mods/gui/view/ViewHandlerBase.java deleted file mode 100644 index 89542fd..0000000 --- a/src/main/java/me/bionicbeanie/mods/gui/view/ViewHandlerBase.java +++ /dev/null @@ -1,41 +0,0 @@ -package me.bionicbeanie.mods.gui.view; - -import io.github.cottonmc.cotton.gui.widget.WWidget; -import me.bionicbeanie.mods.api.IFileStore; -import me.bionicbeanie.mods.api.IGui; -import me.bionicbeanie.mods.api.IRootGridPanel; -import me.bionicbeanie.mods.api.IViewHandler; -import net.minecraft.client.MinecraftClient; - -public abstract class ViewHandlerBase implements IViewHandler { - - protected IFileStore fileStore; - protected IGui gui; - protected MinecraftClient client; - protected WWidget panel; - private T state; - - protected ViewHandlerBase(IFileStore fileStore, IGui gui, MinecraftClient client) { - this.fileStore = fileStore; - this.gui = gui; - this.client = client; - } - - protected abstract void placeWidgets(IRootGridPanel rootPanel, T state); - - @Override - public void clearState() { - state = null; - } - - @Override - public void setState(T state) { - this.state = state; - } - - @Override - public void placeWidgets(IRootGridPanel rootPanel) { - placeWidgets(rootPanel, state); - } - -} diff --git a/src/main/java/me/bionicbeanie/mods/gui/view/ViewOperationBase.java b/src/main/java/me/bionicbeanie/mods/gui/view/ViewOperationBase.java deleted file mode 100644 index 676645e..0000000 --- a/src/main/java/me/bionicbeanie/mods/gui/view/ViewOperationBase.java +++ /dev/null @@ -1,26 +0,0 @@ -package me.bionicbeanie.mods.gui.view; - -import me.bionicbeanie.mods.api.IFileStore; -import me.bionicbeanie.mods.api.IGui; - -public abstract class ViewOperationBase implements Runnable { - - private IFileStore fileStore; - private IGui gui; - - public ViewOperationBase(IFileStore fileStore, IGui gui) { - this.fileStore = fileStore; - this.gui = gui; - } - - @Override - public void run() { - try { - executeOperation(fileStore, gui); - } catch (Exception e) { - e.printStackTrace(); - } - } - - protected abstract void executeOperation(IFileStore fileStore, IGui gui) throws Exception; -} diff --git a/src/main/java/me/bionicbeanie/mods/gui/view/ViewPositionOperationBase.java b/src/main/java/me/bionicbeanie/mods/gui/view/ViewPositionOperationBase.java deleted file mode 100644 index 23c64d5..0000000 --- a/src/main/java/me/bionicbeanie/mods/gui/view/ViewPositionOperationBase.java +++ /dev/null @@ -1,22 +0,0 @@ -package me.bionicbeanie.mods.gui.view; - -import me.bionicbeanie.mods.api.IFileStore; -import me.bionicbeanie.mods.api.IGui; -import me.bionicbeanie.mods.model.PlayerPosition; - -public abstract class ViewPositionOperationBase extends ViewOperationBase{ - - private PlayerPosition position; - - public ViewPositionOperationBase(IFileStore fileStore, IGui gui, PlayerPosition position) { - super(fileStore, gui); - this.position = position; - } - - @Override - protected void executeOperation(IFileStore fileStore, IGui gui) throws Exception { - executeOperation(fileStore, gui, position); - } - - protected abstract void executeOperation(IFileStore fileStore, IGui gui, PlayerPosition position) throws Exception; -} diff --git a/src/main/java/me/bionicbeanie/mods/savecoords/IFileStore.java b/src/main/java/me/bionicbeanie/mods/savecoords/IFileStore.java new file mode 100644 index 0000000..5266416 --- /dev/null +++ b/src/main/java/me/bionicbeanie/mods/savecoords/IFileStore.java @@ -0,0 +1,18 @@ +package me.bionicbeanie.mods.savecoords; + +import java.io.IOException; +import java.util.List; + +import me.bionicbeanie.mods.savecoords.model.PlayerPosition; + +public interface IFileStore { + String getDefaultWorld() throws IOException; + + void setDefaultWorld(String defaultWorldName) throws IOException; + + void save(PlayerPosition position) throws IOException; + + void delete(String id) throws IOException; + + List list() throws IOException; +} diff --git a/src/main/java/me/bionicbeanie/mods/savecoords/IPlayerLocator.java b/src/main/java/me/bionicbeanie/mods/savecoords/IPlayerLocator.java new file mode 100644 index 0000000..31f8198 --- /dev/null +++ b/src/main/java/me/bionicbeanie/mods/savecoords/IPlayerLocator.java @@ -0,0 +1,7 @@ +package me.bionicbeanie.mods.savecoords; + +import me.bionicbeanie.mods.savecoords.model.PlayerRawPosition; + +public interface IPlayerLocator { + PlayerRawPosition locate(); +} diff --git a/src/main/java/me/bionicbeanie/mods/savecoords/SaveCoordinatesClient.java b/src/main/java/me/bionicbeanie/mods/savecoords/SaveCoordinatesClient.java new file mode 100644 index 0000000..a4a93d4 --- /dev/null +++ b/src/main/java/me/bionicbeanie/mods/savecoords/SaveCoordinatesClient.java @@ -0,0 +1,35 @@ +package me.bionicbeanie.mods.savecoords; + +import org.lwjgl.glfw.GLFW; + +import me.bionicbeanie.mods.savecoords.gui.impl.ModGui; +import net.fabricmc.api.ClientModInitializer; +import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents; +import net.fabricmc.fabric.api.client.keybinding.v1.KeyBindingHelper; +import net.minecraft.client.options.KeyBinding; +import net.minecraft.client.util.InputUtil; + +public class SaveCoordinatesClient implements ClientModInitializer { + + @Override + public void onInitializeClient() { + + KeyBinding keyBinding = registerKeyBinding(); + + ClientTickEvents.END_CLIENT_TICK.register(client -> { + while (keyBinding.wasPressed()) { + ModGui.start(client); + } + }); + } + + private KeyBinding registerKeyBinding() { + String translationKey = "key.savecoords.coords"; + String category = "category.savecoords.generic"; + int keyBind = GLFW.GLFW_KEY_H; + + KeyBinding keyBinding = new KeyBinding(translationKey, InputUtil.Type.KEYSYM, keyBind, category); + + return KeyBindingHelper.registerKeyBinding(keyBinding); + } +} diff --git a/src/main/java/me/bionicbeanie/mods/savecoords/gui/IGuiController.java b/src/main/java/me/bionicbeanie/mods/savecoords/gui/IGuiController.java new file mode 100644 index 0000000..01a3cf0 --- /dev/null +++ b/src/main/java/me/bionicbeanie/mods/savecoords/gui/IGuiController.java @@ -0,0 +1,9 @@ +package me.bionicbeanie.mods.savecoords.gui; + +import net.minecraft.client.gui.screen.Screen; + +public interface IGuiController { + void openScreen(Screen screen); + + void closeScreen(); +} diff --git a/src/main/java/me/bionicbeanie/mods/savecoords/gui/IRootPanel.java b/src/main/java/me/bionicbeanie/mods/savecoords/gui/IRootPanel.java new file mode 100644 index 0000000..943a114 --- /dev/null +++ b/src/main/java/me/bionicbeanie/mods/savecoords/gui/IRootPanel.java @@ -0,0 +1,11 @@ +package me.bionicbeanie.mods.savecoords.gui; + +import io.github.cottonmc.cotton.gui.widget.WWidget; + +public interface IRootPanel { + void add(WWidget widget, int x, int y, int w, int h); + + void validate(); + + void reset(); +} diff --git a/src/main/java/me/bionicbeanie/mods/savecoords/gui/IViewHandler.java b/src/main/java/me/bionicbeanie/mods/savecoords/gui/IViewHandler.java new file mode 100644 index 0000000..971e928 --- /dev/null +++ b/src/main/java/me/bionicbeanie/mods/savecoords/gui/IViewHandler.java @@ -0,0 +1,9 @@ +package me.bionicbeanie.mods.savecoords.gui; + +import net.minecraft.client.gui.screen.Screen; + +public interface IViewHandler { + Screen createView(T state); + + T getState(); +} diff --git a/src/main/java/me/bionicbeanie/mods/gui/SaveCoordinatesScreen.java b/src/main/java/me/bionicbeanie/mods/savecoords/gui/SaveCoordinatesScreen.java similarity index 86% rename from src/main/java/me/bionicbeanie/mods/gui/SaveCoordinatesScreen.java rename to src/main/java/me/bionicbeanie/mods/savecoords/gui/SaveCoordinatesScreen.java index d009cd3..fd3f457 100644 --- a/src/main/java/me/bionicbeanie/mods/gui/SaveCoordinatesScreen.java +++ b/src/main/java/me/bionicbeanie/mods/savecoords/gui/SaveCoordinatesScreen.java @@ -1,4 +1,4 @@ -package me.bionicbeanie.mods.gui; +package me.bionicbeanie.mods.savecoords.gui; import io.github.cottonmc.cotton.gui.GuiDescription; import io.github.cottonmc.cotton.gui.client.CottonClientScreen; @@ -8,5 +8,4 @@ public class SaveCoordinatesScreen extends CottonClientScreen { public SaveCoordinatesScreen(GuiDescription description) { super(description); } - } diff --git a/src/main/java/me/bionicbeanie/mods/savecoords/gui/impl/DefaultViewHandler.java b/src/main/java/me/bionicbeanie/mods/savecoords/gui/impl/DefaultViewHandler.java new file mode 100644 index 0000000..484edc6 --- /dev/null +++ b/src/main/java/me/bionicbeanie/mods/savecoords/gui/impl/DefaultViewHandler.java @@ -0,0 +1,193 @@ +package me.bionicbeanie.mods.savecoords.gui.impl; + +import java.util.UUID; +import java.util.function.Supplier; + +import io.github.cottonmc.cotton.gui.widget.WButton; +import io.github.cottonmc.cotton.gui.widget.WLabel; +import io.github.cottonmc.cotton.gui.widget.WText; +import io.github.cottonmc.cotton.gui.widget.WTextField; +import io.github.cottonmc.cotton.gui.widget.WWidget; +import me.bionicbeanie.mods.savecoords.IFileStore; +import me.bionicbeanie.mods.savecoords.IPlayerLocator; +import me.bionicbeanie.mods.savecoords.gui.IRootPanel; +import me.bionicbeanie.mods.savecoords.model.PlayerPosition; +import me.bionicbeanie.mods.savecoords.model.PlayerRawPosition; +import me.bionicbeanie.mods.savecoords.model.PositionMetadata; +import me.bionicbeanie.mods.savecoords.util.ResourceUtils; +import net.minecraft.text.LiteralText; + +class DefaultViewHandler extends ViewHandlerBase { + + private IPlayerLocator locator; + private IFileStore fileStore; + private WButton saveButton; + private WButton listButton; + private WButton pingButton; + private WButton closeButton; + private WButton configButton; + + public DefaultViewHandler(IFileStore fileStore, IPlayerLocator locator) { + this.fileStore = fileStore; + this.locator = locator; + + this.listButton = CreateButton("LIST"); + this.saveButton = CreateButton("SAVE"); + this.pingButton = CreatePingButton(); + this.closeButton = CreateButton("CLOSE"); + this.configButton = CreateButton("CONF"); + } + + @Override + public Supplier placeWidgets(IRootPanel root, PlayerPosition existingPosition) { + + PlayerRawPosition rawPosition = existingPosition == null ? locator.locate() : existingPosition; + + WWidget xLabel = CreateLabelForCoorindate("X"); + WWidget yLabel = CreateLabelForCoorindate("Y"); + WWidget zLabel = CreateLabelForCoorindate("Z"); + + WWidget xText = CreateWidgetForCoordinate(rawPosition.getX()); + WWidget yText = CreateWidgetForCoordinate(rawPosition.getY()); + WWidget zText = CreateWidgetForCoordinate(rawPosition.getZ()); + + root.add(xLabel, 2, 1, 2, 1); + root.add(yLabel, 2, 2, 2, 1); + root.add(zLabel, 2, 3, 2, 1); + + root.add(xText, 3, 1, 2, 1); + root.add(yText, 3, 2, 2, 1); + root.add(zText, 3, 3, 2, 1); + + WWidget icon = ResourceUtils.CreateWorldIcon(rawPosition.getWorldDimension()); + root.add(icon, 8, 1, 2, 2); + + String defaultWorldName = getDefaultWorldName(existingPosition); + WTextField world = CreateWorldField(defaultWorldName); + root.add(world, 0, 4, 4, 1); + + WTextField location = CreateLocationField(existingPosition); + root.add(location, 5, 4, 7, 1); + + WTextField notes = CreateNotesField(existingPosition); + root.add(notes, 0, 6, 12, 1); + + root.add(saveButton, 13, 6, 2, 1); + root.add(listButton, 13, 9, 2, 1); + root.add(pingButton, 13, 1, 1, 1); + root.add(closeButton, 0, 9, 2, 1); + root.add(configButton, 10, 9, 2, 1); + + return createPlayerPositionSupplier(existingPosition, rawPosition, world, location, notes); + } + + public void onSave(Runnable runnable) { + this.saveButton.setOnClick(runnable); + } + + public void onClose(Runnable runnable) { + this.closeButton.setOnClick(runnable); + } + + public void onList(Runnable runnable) { + this.listButton.setOnClick(runnable); + } + + public void onPing(Runnable runnable) { + this.pingButton.setOnClick(runnable); + } + + public void onConfig(Runnable runnable) { + this.configButton.setOnClick(runnable); + } + + private Supplier createPlayerPositionSupplier(PlayerPosition existingPosition, + PlayerRawPosition rawPosition, WTextField world, WTextField location, WTextField notes) { + + return () -> { + String id = CreateId(existingPosition); + PositionMetadata metadata = CreateMetadata(existingPosition, world, notes); + + return new PlayerPosition(id, rawPosition, location.getText(), metadata); + }; + } + + private String CreateId(PlayerPosition existingPosition) { + return existingPosition == null ? UUID.randomUUID().toString() : existingPosition.getId(); + } + + private PositionMetadata CreateMetadata(PlayerPosition existingPosition, WTextField world, WTextField notes) { + if (existingPosition == null) { + return new PositionMetadata(world.getText(), notes.getText()); + } + + existingPosition.getPositionMetadata().setWorldName(world.getText()); + existingPosition.getPositionMetadata().setNotes(notes.getText()); + existingPosition.getPositionMetadata().updateLastModified(); + + return existingPosition.getPositionMetadata(); + } + + private String getDefaultWorldName(PlayerPosition existingPosition) { + if (existingPosition != null && existingPosition.getPositionMetadata().getWorldName() != null) { + return existingPosition.getPositionMetadata().getWorldName(); + } + + try { + return fileStore.getDefaultWorld(); + } catch (Exception e) { + e.printStackTrace(); + } + + return ""; + } + + private WWidget CreateLabelForCoorindate(String label) { + return new WLabel(label, 0xb80000); + } + + private WWidget CreateWidgetForCoordinate(long l) { + return new WText(new LiteralText(String.valueOf(l)), 0x3939ac); + } + + private WTextField CreateLocationField(PlayerPosition existingPosition) { + WTextField location = new WTextField(new LiteralText("location name")); + + if (existingPosition != null) { + location.setText(existingPosition.getLocationName()); + } + + location.setMaxLength(20); + + return location; + } + + private WTextField CreateNotesField(PlayerPosition existingPosition) { + WTextField notes = new WTextField(new LiteralText("additional notes")); + + if (existingPosition != null && existingPosition.getPositionMetadata() != null) { + notes.setText(existingPosition.getPositionMetadata().getNotes()); + } + + return notes; + } + + private WTextField CreateWorldField(String defaultWorld) { + WTextField world = new WTextField(new LiteralText("world name")); + world.setMaxLength(7); + world.setText(defaultWorld); + return world; + } + + private WButton CreatePingButton() { + WButton button = CreateButton(""); + button.setIcon(ResourceUtils.CreatePingIcon()); + return button; + } + + private WButton CreateButton(String name) { + WButton button = new WButton(new LiteralText(name)); + + return button; + } +} diff --git a/src/main/java/me/bionicbeanie/mods/savecoords/gui/impl/DeleteOperation.java b/src/main/java/me/bionicbeanie/mods/savecoords/gui/impl/DeleteOperation.java new file mode 100644 index 0000000..4f6d5df --- /dev/null +++ b/src/main/java/me/bionicbeanie/mods/savecoords/gui/impl/DeleteOperation.java @@ -0,0 +1,17 @@ +package me.bionicbeanie.mods.savecoords.gui.impl; + +import java.util.function.Supplier; + +import me.bionicbeanie.mods.savecoords.IFileStore; + +class DeleteOperation extends ViewOperationBase { + + public DeleteOperation(IFileStore fileStore, Supplier stateSupplier) { + super(fileStore, stateSupplier); + } + + @Override + protected void executeOperation(IFileStore fileStore, String positionId) throws Exception { + fileStore.delete(positionId); + } +} diff --git a/src/main/java/me/bionicbeanie/mods/gui/ScreenController.java b/src/main/java/me/bionicbeanie/mods/savecoords/gui/impl/GuiController.java similarity index 63% rename from src/main/java/me/bionicbeanie/mods/gui/ScreenController.java rename to src/main/java/me/bionicbeanie/mods/savecoords/gui/impl/GuiController.java index 3997293..f43bf6d 100644 --- a/src/main/java/me/bionicbeanie/mods/gui/ScreenController.java +++ b/src/main/java/me/bionicbeanie/mods/savecoords/gui/impl/GuiController.java @@ -1,14 +1,14 @@ -package me.bionicbeanie.mods.gui; +package me.bionicbeanie.mods.savecoords.gui.impl; -import me.bionicbeanie.mods.api.IScreenController; +import me.bionicbeanie.mods.savecoords.gui.IGuiController; import net.minecraft.client.MinecraftClient; import net.minecraft.client.gui.screen.Screen; -public class ScreenController implements IScreenController { +class GuiController implements IGuiController { private MinecraftClient client; - public ScreenController(MinecraftClient client) { + public GuiController(MinecraftClient client) { this.client = client; } diff --git a/src/main/java/me/bionicbeanie/mods/gui/view/ListViewHandler.java b/src/main/java/me/bionicbeanie/mods/savecoords/gui/impl/ListViewHandler.java similarity index 51% rename from src/main/java/me/bionicbeanie/mods/gui/view/ListViewHandler.java rename to src/main/java/me/bionicbeanie/mods/savecoords/gui/impl/ListViewHandler.java index 36d8887..b550e77 100644 --- a/src/main/java/me/bionicbeanie/mods/gui/view/ListViewHandler.java +++ b/src/main/java/me/bionicbeanie/mods/savecoords/gui/impl/ListViewHandler.java @@ -1,61 +1,80 @@ -package me.bionicbeanie.mods.gui.view; +package me.bionicbeanie.mods.savecoords.gui.impl; import java.io.IOException; import java.util.Collections; import java.util.List; import java.util.function.BiConsumer; +import java.util.function.Consumer; +import java.util.function.Supplier; import io.github.cottonmc.cotton.gui.widget.WButton; import io.github.cottonmc.cotton.gui.widget.WLabel; import io.github.cottonmc.cotton.gui.widget.WListPanel; import io.github.cottonmc.cotton.gui.widget.WPlainPanel; import io.github.cottonmc.cotton.gui.widget.WSprite; -import me.bionicbeanie.mods.api.IFileStore; -import me.bionicbeanie.mods.api.IGui; -import me.bionicbeanie.mods.api.IRootGridPanel; -import me.bionicbeanie.mods.model.PlayerPosition; -import me.bionicbeanie.mods.util.DimensionSpriteUtil; -import net.minecraft.client.MinecraftClient; +import me.bionicbeanie.mods.savecoords.IFileStore; +import me.bionicbeanie.mods.savecoords.gui.IRootPanel; +import me.bionicbeanie.mods.savecoords.model.PlayerPosition; +import me.bionicbeanie.mods.savecoords.model.PlayerRawPosition; +import me.bionicbeanie.mods.savecoords.util.ResourceUtils; import net.minecraft.text.LiteralText; import net.minecraft.util.Identifier; -public class ListViewHandler extends ViewHandlerBase { +class ListViewHandler extends ViewHandlerBase { - public ListViewHandler(IFileStore fileStore, IGui gui, MinecraftClient client) { - super(fileStore, gui, client); + private IFileStore fileStore; + private WButton backButton; + private Consumer onDelete; + private Consumer onEdit; + private Consumer onPing; + + public ListViewHandler(IFileStore fileStore, Consumer onDelete, Consumer onEdit, + Consumer onPing) { + this.fileStore = fileStore; + this.backButton = createBackButton(); + this.onDelete = onDelete; + this.onEdit = onEdit; + this.onPing = onPing; } @Override - public void placeWidgets(IRootGridPanel root, Void nullState) { + public Supplier placeWidgets(IRootPanel root, Void nullState) { List positions = getPositions(fileStore); WListPanel listPanel = createListPane(positions); - WButton backButton = createBackButton(); - root.add(listPanel, 0, 0, 15, 9); root.add(backButton, 0, 9, 2, 1); + + return () -> (Void) null; + } + + public void onBack(Runnable runnable) { + this.backButton.setOnClick(runnable); } private WButton createBackButton() { - WButton backButton = new WButton(new LiteralText("BACK")); - backButton.setOnClick(gui::showDefaultView); - return backButton; + return new WButton(new LiteralText("BACK")); } private WListPanel createListPane(List positions) { - BiConsumer configurator = (position, panel) -> { - panel.setPosition(position, fileStore, gui); - }; - - WListPanel panel = new WListPanel<>(positions, - CoordinatesListItemPanel::new, configurator); + BiConsumer configurator = (pos, p) -> p.setPosition(pos, fileStore); + WListPanel panel = CreateListPanel(positions, configurator); panel.setListItemHeight(2 * 18); return panel; } + private WListPanel CreateListPanel(List positions, + BiConsumer config) { + return new WListPanel<>(positions, this::createListPanel, config); + } + + private CoordinatesListItemPanel createListPanel() { + return new CoordinatesListItemPanel(onDelete, onEdit, onPing); + } + private List getPositions(IFileStore fileStore) { try { List positions = fileStore.list(); @@ -68,7 +87,7 @@ public class ListViewHandler extends ViewHandlerBase { } } - public static class CoordinatesListItemPanel extends WPlainPanel { + private static class CoordinatesListItemPanel extends WPlainPanel { private WLabel coordinates; private WLabel location; @@ -77,22 +96,35 @@ public class ListViewHandler extends ViewHandlerBase { private WButton pingButton; private WButton detailButton; private WLabel world; + private Consumer onPing; + private Consumer onEdit; + private Consumer onDelete; + + CoordinatesListItemPanel(Consumer onDelete, Consumer onEdit, + Consumer onPing) { + + this.onDelete = onDelete; + this.onEdit = onEdit; + this.onPing = onPing; - public CoordinatesListItemPanel() { this.coordinates = new WLabel("Foo"); this.location = new WLabel("Foo"); this.world = new WLabel("Foo"); this.icon = new WSprite(new Identifier("minecraft:textures/item/ender_eye.png")); this.deleteButton = new WButton(new LiteralText("x")); - this.pingButton = new WButton(new LiteralText("!")); - this.detailButton = new WButton(new LiteralText("+")); + this.pingButton = new WButton(new LiteralText("")); + this.detailButton = new WButton(new LiteralText("")); + + this.deleteButton.setIcon(ResourceUtils.CreateCloseIcon()); + this.pingButton.setIcon(ResourceUtils.CreatePingIcon()); + this.detailButton.setIcon(ResourceUtils.CreateDetailsIcon()); this.add(icon, 0, 0, 1 * 9, 1 * 9); this.add(world, 1 * 18, 0, 3 * 18, 1 * 18); this.add(location, 4 * 18, 0, 4 * 18, 1 * 18); - this.add(coordinates, 1 * 18, 1 * 18, 9 * 18, 1 * 18); + this.add(coordinates, 3 * 18, 1 * 18, 9 * 18, 1 * 18); this.add(deleteButton, 13 * 18, 0, 1 * 18, 1 * 18); - this.add(pingButton, 12 * 18, 0, 1 * 18, 1 * 18); + this.add(pingButton, 1 * 18, 1 * 18, 1 * 18, 1 * 18); this.add(detailButton, 11 * 18, 0, 1 * 18, 1 * 18); this.icon.setSize(1 * 15, 1 * 15); @@ -106,17 +138,17 @@ public class ListViewHandler extends ViewHandlerBase { this.setSize(15 * 18, 2 * 18); } - public void setPosition(PlayerPosition position, IFileStore fileStore, IGui gui) { - this.icon.setImage(DimensionSpriteUtil.CreateWorldIconIdentifier(position.getWorldDimension())); + void setPosition(PlayerPosition position, IFileStore fileStore) { + this.icon.setImage(ResourceUtils.CreateWorldIconIdentifier(position.getWorldDimension())); this.location.setText(new LiteralText(position.getLocationName())); this.location.setColor(0x3939ac); this.world.setText(new LiteralText("[" + position.getPositionMetadata().getWorldName() + "]")); this.world.setColor(0xb80000); this.coordinates .setText(new LiteralText(position.getX() + ", " + position.getY() + ", " + position.getZ())); - this.deleteButton.setOnClick(new DeleteOperation(fileStore, gui, position)); - this.pingButton.setOnClick(new PingOperation(fileStore, gui, position)); - this.detailButton.setOnClick(new ShowDetailOperation(fileStore, gui, position)); + this.deleteButton.setOnClick(() -> onDelete.accept(position)); + this.pingButton.setOnClick(() -> onPing.accept(position)); + this.detailButton.setOnClick(() -> onEdit.accept(position)); } } } diff --git a/src/main/java/me/bionicbeanie/mods/savecoords/gui/impl/ModGui.java b/src/main/java/me/bionicbeanie/mods/savecoords/gui/impl/ModGui.java new file mode 100644 index 0000000..57c2e9b --- /dev/null +++ b/src/main/java/me/bionicbeanie/mods/savecoords/gui/impl/ModGui.java @@ -0,0 +1,18 @@ +package me.bionicbeanie.mods.savecoords.gui.impl; + +import me.bionicbeanie.mods.savecoords.IFileStore; +import me.bionicbeanie.mods.savecoords.IPlayerLocator; +import me.bionicbeanie.mods.savecoords.gui.IGuiController; +import me.bionicbeanie.mods.savecoords.impl.Factory; +import net.minecraft.client.MinecraftClient; + +public class ModGui { + + public static void start(MinecraftClient client) { + IGuiController controller = new GuiController(client); + IPlayerLocator locator = Factory.CreatePlayerLocator(client); + IFileStore fileStore = Factory.createFileStore(client.runDirectory.getAbsolutePath()); + + new SaveCoordinatesGui(fileStore, locator, controller); + } +} diff --git a/src/main/java/me/bionicbeanie/mods/savecoords/gui/impl/PingOperation.java b/src/main/java/me/bionicbeanie/mods/savecoords/gui/impl/PingOperation.java new file mode 100644 index 0000000..bb614fb --- /dev/null +++ b/src/main/java/me/bionicbeanie/mods/savecoords/gui/impl/PingOperation.java @@ -0,0 +1,20 @@ +package me.bionicbeanie.mods.savecoords.gui.impl; + +import java.util.function.Supplier; + +import me.bionicbeanie.mods.savecoords.IFileStore; +import me.bionicbeanie.mods.savecoords.model.PlayerRawPosition; +import net.minecraft.client.MinecraftClient; + +class PingOperation extends ViewOperationBase{ + + public PingOperation(IFileStore fileStore, Supplier stateSupplier) { + super(fileStore, stateSupplier); + } + + @SuppressWarnings("resource") + @Override + protected void executeOperation(IFileStore fileStore, PlayerRawPosition position) throws Exception { + MinecraftClient.getInstance().player.sendChatMessage(position.toString()); + } +} diff --git a/src/main/java/me/bionicbeanie/mods/gui/RootGridPanel.java b/src/main/java/me/bionicbeanie/mods/savecoords/gui/impl/RootGridPanel.java similarity index 57% rename from src/main/java/me/bionicbeanie/mods/gui/RootGridPanel.java rename to src/main/java/me/bionicbeanie/mods/savecoords/gui/impl/RootGridPanel.java index fcf378b..8b752a8 100644 --- a/src/main/java/me/bionicbeanie/mods/gui/RootGridPanel.java +++ b/src/main/java/me/bionicbeanie/mods/savecoords/gui/impl/RootGridPanel.java @@ -1,18 +1,22 @@ -package me.bionicbeanie.mods.gui; +package me.bionicbeanie.mods.savecoords.gui.impl; import java.util.ArrayList; import java.util.List; +import io.github.cottonmc.cotton.gui.GuiDescription; import io.github.cottonmc.cotton.gui.widget.WGridPanel; import io.github.cottonmc.cotton.gui.widget.WWidget; -import me.bionicbeanie.mods.api.IRootGridPanel; +import me.bionicbeanie.mods.savecoords.gui.IRootPanel; -public class RootGridPanel extends WGridPanel implements IRootGridPanel{ +class RootGridPanel extends WGridPanel implements IRootPanel{ private List children; + private GuiDescription guiDescription; - public RootGridPanel() { + public RootGridPanel(GuiDescription guiDescription) { this(18); + + this.guiDescription = guiDescription; } public RootGridPanel(int gridSize){ @@ -32,4 +36,8 @@ public class RootGridPanel extends WGridPanel implements IRootGridPanel{ children.clear(); } + @Override + public void validate() { + validate(guiDescription); + } } diff --git a/src/main/java/me/bionicbeanie/mods/savecoords/gui/impl/SaveCoordinatesGui.java b/src/main/java/me/bionicbeanie/mods/savecoords/gui/impl/SaveCoordinatesGui.java new file mode 100644 index 0000000..1f23ed2 --- /dev/null +++ b/src/main/java/me/bionicbeanie/mods/savecoords/gui/impl/SaveCoordinatesGui.java @@ -0,0 +1,94 @@ +package me.bionicbeanie.mods.savecoords.gui.impl; + +import me.bionicbeanie.mods.savecoords.IFileStore; +import me.bionicbeanie.mods.savecoords.IPlayerLocator; +import me.bionicbeanie.mods.savecoords.gui.IGuiController; +import me.bionicbeanie.mods.savecoords.gui.IViewHandler; +import me.bionicbeanie.mods.savecoords.model.PlayerPosition; +import me.bionicbeanie.mods.savecoords.model.PlayerRawPosition; + +public class SaveCoordinatesGui { + + private IGuiController screenController; + private IFileStore fileStore; + private IViewHandler defaultHandler; + private IViewHandler listHandler; + private IPlayerLocator locator; + + SaveCoordinatesGui(IFileStore fileStore, IPlayerLocator locator, IGuiController screenController) { + this.screenController = screenController; + this.fileStore = fileStore; + this.locator = locator; + + this.defaultHandler = CreateDefaultViewHandler(); + this.listHandler = CreateListViewHandler(); + + showDefaultView(null); + } + + private IViewHandler CreateDefaultViewHandler() { + DefaultViewHandler handler = new DefaultViewHandler(fileStore, locator); + + handler.onClose(screenController::closeScreen); + handler.onSave(this::onSavePosition); + handler.onList(this::showListView); + handler.onPing(new PingOperation(fileStore, locator::locate)); + + return handler; + } + + private IViewHandler CreateListViewHandler() { + ListViewHandler handler = new ListViewHandler(fileStore, this::onDeletePosition, this::onEditPosition, + this::onPingPosition); + + handler.onBack(() -> showDefaultView(null)); + + return handler; + } + + private void onSavePosition() { + new SaveOperation(fileStore, defaultHandler::getState).run(); + showListView(); + } + + private void onDeletePosition(PlayerPosition position) { + new DeleteOperation(fileStore, () -> position.getId()).run(); + showListView(); + } + + private void onEditPosition(PlayerPosition position) { + showDefaultView(position); + } + + private void onPingPosition(PlayerRawPosition position) { + new PingOperation(fileStore, () -> position).run(); + } + + private void showDefaultView(PlayerPosition position) { + screenController.openScreen(this.defaultHandler.createView(position)); + } + + private void showListView() { + screenController.openScreen(this.listHandler.createView(null)); + } + +// public void showConfigView() { +// +// ConfigBuilder builder = ConfigBuilder.create() +// .setParentScreen(this.screen) +// .setTitle(new LiteralText("Save Coordinates config")); +// +// ConfigCategory general = builder.getOrCreateCategory(new LiteralText("Generic")); +// ConfigEntryBuilder entryBuilder = builder.entryBuilder(); +// +// general.addEntry(entryBuilder.startKeyCodeField(new LiteralText("Default Keybind"), +// InputUtil.Type.KEYSYM.createFromCode(GLFW.GLFW_KEY_H)) +// .setDefaultValue(InputUtil.Type.KEYSYM.createFromCode(GLFW.GLFW_KEY_H)) +// .setTooltip(new LiteralText("Keybind to open Save Coordinates menu")) +// .setSaveConsumer(newValue -> newValue = newValue) +// .build()); +// +// screenController.openScreen(builder.build()); +// } + +} diff --git a/src/main/java/me/bionicbeanie/mods/savecoords/gui/impl/SaveOperation.java b/src/main/java/me/bionicbeanie/mods/savecoords/gui/impl/SaveOperation.java new file mode 100644 index 0000000..acbb605 --- /dev/null +++ b/src/main/java/me/bionicbeanie/mods/savecoords/gui/impl/SaveOperation.java @@ -0,0 +1,19 @@ +package me.bionicbeanie.mods.savecoords.gui.impl; + +import java.util.function.Supplier; + +import me.bionicbeanie.mods.savecoords.IFileStore; +import me.bionicbeanie.mods.savecoords.model.PlayerPosition; + +class SaveOperation extends ViewOperationBase { + + public SaveOperation(IFileStore fileStore, Supplier stateSupplier) { + super(fileStore, stateSupplier); + } + + @Override + protected void executeOperation(IFileStore fileStore, PlayerPosition position) throws Exception { + fileStore.save(position); + fileStore.setDefaultWorld(position.getPositionMetadata().getWorldName()); + } +} diff --git a/src/main/java/me/bionicbeanie/mods/savecoords/gui/impl/ViewHandlerBase.java b/src/main/java/me/bionicbeanie/mods/savecoords/gui/impl/ViewHandlerBase.java new file mode 100644 index 0000000..0d1adb9 --- /dev/null +++ b/src/main/java/me/bionicbeanie/mods/savecoords/gui/impl/ViewHandlerBase.java @@ -0,0 +1,42 @@ +package me.bionicbeanie.mods.savecoords.gui.impl; + +import java.util.function.Supplier; + +import io.github.cottonmc.cotton.gui.client.LightweightGuiDescription; +import me.bionicbeanie.mods.savecoords.gui.IRootPanel; +import me.bionicbeanie.mods.savecoords.gui.IViewHandler; +import me.bionicbeanie.mods.savecoords.gui.SaveCoordinatesScreen; +import net.minecraft.client.gui.screen.Screen; + +abstract class ViewHandlerBase extends LightweightGuiDescription implements IViewHandler { + + private IRootPanel rootGridPanel; + private Supplier stateSupplier; + + protected ViewHandlerBase() { + this.rootGridPanel = createRootPanel(); + } + + protected abstract Supplier placeWidgets(IRootPanel rootGridPanel, T state); + + @Override + public Screen createView(T state) { + rootGridPanel.reset(); + this.stateSupplier = placeWidgets(rootGridPanel, state); + rootGridPanel.validate(); + return new SaveCoordinatesScreen(this); + } + + @Override + public T getState() { + return stateSupplier.get(); + } + + private IRootPanel createRootPanel() { + RootGridPanel panel = new RootGridPanel(this); + panel.setSize(15 * 18, 10 * 18); + + setRootPanel(panel); + return panel; + } +} diff --git a/src/main/java/me/bionicbeanie/mods/savecoords/gui/impl/ViewOperationBase.java b/src/main/java/me/bionicbeanie/mods/savecoords/gui/impl/ViewOperationBase.java new file mode 100644 index 0000000..c2ce5c3 --- /dev/null +++ b/src/main/java/me/bionicbeanie/mods/savecoords/gui/impl/ViewOperationBase.java @@ -0,0 +1,27 @@ +package me.bionicbeanie.mods.savecoords.gui.impl; + +import java.util.function.Supplier; + +import me.bionicbeanie.mods.savecoords.IFileStore; + +abstract class ViewOperationBase implements Runnable { + + private IFileStore fileStore; + private Supplier stateSupplier; + + public ViewOperationBase(IFileStore fileStore,Supplier stateSupplier) { + this.fileStore = fileStore; + this.stateSupplier = stateSupplier; + } + + @Override + public void run() { + try { + executeOperation(fileStore, stateSupplier.get()); + } catch (Exception e) { + e.printStackTrace(); + } + } + + protected abstract void executeOperation(IFileStore fileStore, T state) throws Exception; +} diff --git a/src/main/java/me/bionicbeanie/mods/savecoords/impl/Factory.java b/src/main/java/me/bionicbeanie/mods/savecoords/impl/Factory.java new file mode 100644 index 0000000..ca53b93 --- /dev/null +++ b/src/main/java/me/bionicbeanie/mods/savecoords/impl/Factory.java @@ -0,0 +1,16 @@ +package me.bionicbeanie.mods.savecoords.impl; + +import me.bionicbeanie.mods.savecoords.IFileStore; +import me.bionicbeanie.mods.savecoords.IPlayerLocator; +import net.minecraft.client.MinecraftClient; + +public class Factory { + + public static IFileStore createFileStore(String baseDirectory) { + return new FileStore(baseDirectory); + } + + public static IPlayerLocator CreatePlayerLocator(MinecraftClient client) { + return new PlayerLocator(client); + } +} diff --git a/src/main/java/me/bionicbeanie/mods/impl/FileStore.java b/src/main/java/me/bionicbeanie/mods/savecoords/impl/FileStore.java similarity index 92% rename from src/main/java/me/bionicbeanie/mods/impl/FileStore.java rename to src/main/java/me/bionicbeanie/mods/savecoords/impl/FileStore.java index aeb6eee..410d645 100644 --- a/src/main/java/me/bionicbeanie/mods/impl/FileStore.java +++ b/src/main/java/me/bionicbeanie/mods/savecoords/impl/FileStore.java @@ -1,4 +1,4 @@ -package me.bionicbeanie.mods.impl; +package me.bionicbeanie.mods.savecoords.impl; import java.io.IOException; import java.nio.file.FileAlreadyExistsException; @@ -14,11 +14,11 @@ import org.apache.commons.lang3.StringUtils; import com.google.gson.Gson; import com.google.gson.GsonBuilder; -import me.bionicbeanie.mods.api.IFileStore; -import me.bionicbeanie.mods.model.ModData; -import me.bionicbeanie.mods.model.PlayerPosition; +import me.bionicbeanie.mods.savecoords.IFileStore; +import me.bionicbeanie.mods.savecoords.model.ModData; +import me.bionicbeanie.mods.savecoords.model.PlayerPosition; -public class FileStore implements IFileStore { +class FileStore implements IFileStore { private static String DEFAULT_DIR = "saveCoordinates"; private static String DEFAULT_FILE = "coordiates.json"; diff --git a/src/main/java/me/bionicbeanie/mods/impl/PlayerLocator.java b/src/main/java/me/bionicbeanie/mods/savecoords/impl/PlayerLocator.java similarity index 52% rename from src/main/java/me/bionicbeanie/mods/impl/PlayerLocator.java rename to src/main/java/me/bionicbeanie/mods/savecoords/impl/PlayerLocator.java index c95d5fe..9e99500 100644 --- a/src/main/java/me/bionicbeanie/mods/impl/PlayerLocator.java +++ b/src/main/java/me/bionicbeanie/mods/savecoords/impl/PlayerLocator.java @@ -1,13 +1,19 @@ -package me.bionicbeanie.mods.impl; +package me.bionicbeanie.mods.savecoords.impl; -import me.bionicbeanie.mods.api.IPlayerLocator; -import me.bionicbeanie.mods.model.PlayerRawPosition; +import me.bionicbeanie.mods.savecoords.IPlayerLocator; +import me.bionicbeanie.mods.savecoords.model.PlayerRawPosition; import net.minecraft.client.MinecraftClient; import net.minecraft.util.math.Vec3d; -public class PlayerLocator implements IPlayerLocator { +class PlayerLocator implements IPlayerLocator { - public PlayerRawPosition locate(MinecraftClient client) { + private MinecraftClient client; + + public PlayerLocator(MinecraftClient client) { + this.client = client; + } + + public PlayerRawPosition locate() { Vec3d pos = client.player.getPos(); long x = Math.round(pos.x); long y = Math.round(pos.y); diff --git a/src/main/java/me/bionicbeanie/mods/model/ModData.java b/src/main/java/me/bionicbeanie/mods/savecoords/model/ModData.java similarity index 69% rename from src/main/java/me/bionicbeanie/mods/model/ModData.java rename to src/main/java/me/bionicbeanie/mods/savecoords/model/ModData.java index 144c016..a304109 100644 --- a/src/main/java/me/bionicbeanie/mods/model/ModData.java +++ b/src/main/java/me/bionicbeanie/mods/savecoords/model/ModData.java @@ -1,4 +1,4 @@ -package me.bionicbeanie.mods.model; +package me.bionicbeanie.mods.savecoords.model; public class ModData { diff --git a/src/main/java/me/bionicbeanie/mods/model/PlayerPosition.java b/src/main/java/me/bionicbeanie/mods/savecoords/model/PlayerPosition.java similarity index 95% rename from src/main/java/me/bionicbeanie/mods/model/PlayerPosition.java rename to src/main/java/me/bionicbeanie/mods/savecoords/model/PlayerPosition.java index 0b98386..9667522 100644 --- a/src/main/java/me/bionicbeanie/mods/model/PlayerPosition.java +++ b/src/main/java/me/bionicbeanie/mods/savecoords/model/PlayerPosition.java @@ -1,4 +1,4 @@ -package me.bionicbeanie.mods.model; +package me.bionicbeanie.mods.savecoords.model; public class PlayerPosition extends PlayerRawPosition { diff --git a/src/main/java/me/bionicbeanie/mods/model/PlayerRawPosition.java b/src/main/java/me/bionicbeanie/mods/savecoords/model/PlayerRawPosition.java similarity index 93% rename from src/main/java/me/bionicbeanie/mods/model/PlayerRawPosition.java rename to src/main/java/me/bionicbeanie/mods/savecoords/model/PlayerRawPosition.java index 5b896d9..bb777f7 100644 --- a/src/main/java/me/bionicbeanie/mods/model/PlayerRawPosition.java +++ b/src/main/java/me/bionicbeanie/mods/savecoords/model/PlayerRawPosition.java @@ -1,4 +1,4 @@ -package me.bionicbeanie.mods.model; +package me.bionicbeanie.mods.savecoords.model; public class PlayerRawPosition { private long x, y, z; diff --git a/src/main/java/me/bionicbeanie/mods/model/PositionMetadata.java b/src/main/java/me/bionicbeanie/mods/savecoords/model/PositionMetadata.java similarity index 95% rename from src/main/java/me/bionicbeanie/mods/model/PositionMetadata.java rename to src/main/java/me/bionicbeanie/mods/savecoords/model/PositionMetadata.java index 110b3c4..e9eec81 100644 --- a/src/main/java/me/bionicbeanie/mods/model/PositionMetadata.java +++ b/src/main/java/me/bionicbeanie/mods/savecoords/model/PositionMetadata.java @@ -1,4 +1,4 @@ -package me.bionicbeanie.mods.model; +package me.bionicbeanie.mods.savecoords.model; import java.time.LocalDateTime; diff --git a/src/main/java/me/bionicbeanie/mods/savecoords/util/ResourceUtils.java b/src/main/java/me/bionicbeanie/mods/savecoords/util/ResourceUtils.java new file mode 100644 index 0000000..9e63116 --- /dev/null +++ b/src/main/java/me/bionicbeanie/mods/savecoords/util/ResourceUtils.java @@ -0,0 +1,45 @@ +package me.bionicbeanie.mods.savecoords.util; + +import io.github.cottonmc.cotton.gui.widget.WSprite; +import io.github.cottonmc.cotton.gui.widget.WWidget; +import io.github.cottonmc.cotton.gui.widget.icon.Icon; +import io.github.cottonmc.cotton.gui.widget.icon.TextureIcon; +import net.minecraft.util.Identifier; + +public class ResourceUtils { + + public static WWidget CreateWorldIcon(String dimension) { + return new WSprite(CreateWorldIconIdentifier(dimension)); + } + + public static Identifier CreateWorldIconIdentifier(String dimension) { + + if (dimension == null) + return CreateIdentifier("nonExistent"); + if (dimension.contains("overworld")) + return CreateIdentifier("overworld"); + if (dimension.contains("nether")) + return CreateIdentifier("nether"); + if (dimension.contains("end")) + return CreateIdentifier("end"); + + return CreateIdentifier("nonExistent"); + + } + + public static Icon CreatePingIcon() { + return new TextureIcon(CreateIdentifier("ping")); + } + + public static Icon CreateCloseIcon() { + return new TextureIcon(CreateIdentifier("close")); + } + + public static Icon CreateDetailsIcon() { + return new TextureIcon(CreateIdentifier("more")); + } + + private static Identifier CreateIdentifier(String file) { + return new Identifier("savecoords", "textures/gui/" + file + ".png"); + } +} diff --git a/src/main/java/me/bionicbeanie/mods/util/DimensionSpriteUtil.java b/src/main/java/me/bionicbeanie/mods/util/DimensionSpriteUtil.java deleted file mode 100644 index 56c2138..0000000 --- a/src/main/java/me/bionicbeanie/mods/util/DimensionSpriteUtil.java +++ /dev/null @@ -1,27 +0,0 @@ -package me.bionicbeanie.mods.util; - -import io.github.cottonmc.cotton.gui.widget.WSprite; -import io.github.cottonmc.cotton.gui.widget.WWidget; -import net.minecraft.util.Identifier; - -public class DimensionSpriteUtil { - - public static WWidget CreateWorldIcon(String dimension) { - return new WSprite(CreateWorldIconIdentifier(dimension)); - } - - public static Identifier CreateWorldIconIdentifier(String dimension) { - String dimensionItem = "netherite_ingot"; - - if(dimension == null) { - dimensionItem = "barrier"; - } - else if (dimension.contains("overworld")) { - dimensionItem = "totem_of_undying"; - } else if (dimension.contains("end")) { - dimensionItem = "ender_eye"; - } - - return new Identifier("minecraft:textures/item/" + dimensionItem + ".png"); - } -} diff --git a/src/main/resources/assets/savecoords/textures/gui/close.png b/src/main/resources/assets/savecoords/textures/gui/close.png new file mode 100644 index 0000000000000000000000000000000000000000..cfa36444ce94d53c9b2930c2fc56771500d04914 GIT binary patch literal 183 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
  • (u{Y^SfL9ZM-YdZYV( za^t+hWp>y2jXyoDl}`|NpYzGNak`B2X*U=CMH5+yOfp5p^0l9lGK6=q=W;IFjteb-Rn>cdwUWPtBB#;P>)S!zN*ikCU73C7 zu@G<4-=|v~a}ua^z3=?Ad=@ zYNB)C%{@7{9{dhsZ(`w`bY)(Q@`Nc-6ZQqq(|z0Z#p05|ZIf@G=CMpyXeqqj*uOe! z=epaDKc|&9ZD-J1EnR%+26 z+0)+HSLWuH+}6oUl#Ns7-In4irrr4ZgHxixMnfkfwP-2MIX5Jlxu-VS^|CCL?5Td5 zmf6pd+L*ih>{*$A2gRq>+`p0_tP^Rm*Lk5S_sT}cOX;5GNmE~$nLm5cyEMqThwpai zwSIvH7DqF^+8GCr{)lk5oBhN=Yx=E-$hgO)Eeq|JtnGX^vvU5G1s$65E3^HXlqE0T zI&{Ca;!C@b2ipsil&3pxU-5}bIgVw>fid}JvC`&PVnWZiAv2c4h!3PyemC1m$yp8_c>3)gSb156}mTD98RnDu>3Dt ze$FCudaSny}NOS|I4wd(=vLj)&f2yqEN zerz?1$@XMRz_J{-f9t|l`;f*A-D~t(csT|K#1B4VJb(~jE zwNr7o{-l3hFh$HqFXG>{Guxuy-1#}r(OvKHmvH7YSE}!SKVI!AIjibvfs=->nCcp( zR_&wLo*#bNt!9~=`)OB((K`OWDn&x38=nR(@m#fje(Rl#Ya3nvbWJF&h@b9tKd)3E z;iA`()ViH<3p78hFbpX2xT+d^dSm~bdK1p`Qq4*MTP>Hz-z z;<4Vn*W=f(+hujK{d$Ek=lnA&?4OF_9OWJ#Z4r^zG050_;MANwt}?f`yj?umy5zdL zS)JA|i`SdK!m2t}nbPvRW~1^UVo8o!JUKGZx<7Up0RY*sAAnM>{e>ZWI6xB2_~kORv%6hG&fSzbWo?>zPo_A4R{cG_vaYh++xVDNPHb6Mw<&;$U~uU>5c literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/savecoords/textures/gui/more.png b/src/main/resources/assets/savecoords/textures/gui/more.png new file mode 100644 index 0000000000000000000000000000000000000000..4cb908fe90d48b5c7aad603aa3af286a164d8599 GIT binary patch literal 195 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 u&wmZRxby!9e(@MV^WXm?IUoLKKJr=o