From 9234b35eec82abe78aba344dd9544915007d80e8 Mon Sep 17 00:00:00 2001
From: tanishisherewith <120117618+tanishisherewithhh@users.noreply.github.com>
Date: Tue, 2 Apr 2024 23:25:45 +0530
Subject: [PATCH 01/33] Update README.md
---
README.md | 15 ++++-----------
1 file changed, 4 insertions(+), 11 deletions(-)
diff --git a/README.md b/README.md
index 8108cd5..de919a7 100644
--- a/README.md
+++ b/README.md
@@ -1,22 +1,15 @@
# DynamicHUD
A library to create HUD elements and display them on the screen. **Fabric only**\
+### Version `1.3.0`
-## Short introduction
-Dynamic HUD is a library used to create and display "widgets" on the screen.
-
-Included widgets by the library are Armor Widget, Item Widget and Text Widget.
-
-The library also includes basic utility features like a menu, slider and a Color Picker. Overall, this mod contains all the features you will need for a Hud editor.
-
-## Note: This is a library meaning it does not do / display anything on its own, It is mainly to help other developers and ease of adding or displaying stuff on the screen
+## This is a test branch for dynamichud 1.20.4. This branch will contain the majorly reworked version of dynamichud and all previous versions will be abandoned/not supported.
+## The current version might be backported upto 1.16 fabric.
+## Only fabric
# Examples
-- [_DynamicHUDtest.java_](src/main/java/com/tanishisherewith/dynamichud/DynamicHUDtest.java)
# [Modrinth](https://modrinth.com/mod/dynamichud)
-## A LIBRARY SIMILAR TO THIS WHICH SUPPORTS OLDER MINECRAFT VERSIONS: https://github.com/LaconicLizard/HudElements
-
# Demo:
### *May vary from version to version*
## New Version 1.2.0
From 8b8b7082c7947c4ef43f4516ed97dc64afdb697b Mon Sep 17 00:00:00 2001
From: tanishisherewith <120117618+tanishisherewithhh@users.noreply.github.com>
Date: Tue, 2 Apr 2024 23:29:04 +0530
Subject: [PATCH 02/33] Update README.md
---
README.md | 20 +++++++++++---------
1 file changed, 11 insertions(+), 9 deletions(-)
diff --git a/README.md b/README.md
index de919a7..cf61b52 100644
--- a/README.md
+++ b/README.md
@@ -1,22 +1,24 @@
# DynamicHUD
-A library to create HUD elements and display them on the screen. **Fabric only**\
+A library to display stuff on the screen. **Fabric only**
### Version `1.3.0`
-## This is a test branch for dynamichud 1.20.4. This branch will contain the majorly reworked version of dynamichud and all previous versions will be abandoned/not supported.
-## The current version might be backported upto 1.16 fabric.
-## Only fabric
-
-# Examples
+# ⚠ IN PROGRESS ⚠
-# [Modrinth](https://modrinth.com/mod/dynamichud)
+## This is a test branch for dynamichud 1.20.4. This branch will contain the majorly reworked version of dynamichud and all previous versions will be abandoned/not supported.
+## The current version might be backported upto 1.16 fabric on demand.
+## Only for fabric.
+The reworked version could be found in the `newTrials` directory.
# Demo:
### *May vary from version to version*
-## New Version 1.2.0
+## Developed stage
+> ---
+
+## Mid stage (1.2.0)
https://github.com/V-Fast/DynamicHUD/assets/120117618/2abfcdf5-d786-4e58-acae-aefe51b77b4a
-## Old Version
+## Early stages
https://github.com/V-Fast/DynamicHUD/assets/120117618/04de9319-69cd-4456-a555-c026c7e053a2
From c62cda62b5eee22c9b0b98d8a7b5ddb3ad69fde8 Mon Sep 17 00:00:00 2001
From: tanishisherewithhh
<120117618+tanishisherewithhh@users.noreply.github.com>
Date: Tue, 2 Apr 2024 23:30:49 +0530
Subject: [PATCH 03/33] Bug fixing (On trial) 2
---
build.gradle | 4 +-
gradle.properties | 14 +-
gradle/wrapper/gradle-wrapper.properties | 2 +-
.../dynamichud/DynamicHUD.java | 22 +-
.../dynamichud/DynamicHudIntegration.java | 48 ++++
.../dynamichud/helpers/DrawHelper.java | 22 ++
.../dynamichud/mixins/ScreenMixin.java | 22 ++
.../dynamichud/mixins/TitleScreenMixin.java | 104 --------
.../dynamichud/newTrial/DynamicHUD.java | 122 +++++++++
.../dynamichud/newTrial/DynamicHudTest.java | 60 +++++
.../newTrial/utils/DynamicValueRegistry.java | 32 +++
.../dynamichud/newTrial/utils/UID.java | 30 +++
.../dynamichud/newTrial/widget/Widget.java | 244 ++++++++++++++++++
.../newTrial/widget/WidgetData.java | 20 ++
.../newTrial/widget/WidgetManager.java | 149 +++++++++++
.../newTrial/widget/WidgetRenderer.java | 43 +++
.../newTrial/widgets/TextWidget.java | 129 +++++++++
.../dynamichud/util/DynamicUtil.java | 4 +-
.../util/contextmenu/TextWidgetButtonExt.java | 4 -
.../dynamichud/widget/WidgetBox.java | 12 +
.../dynamichud/widget/WidgetManager.java | 8 +-
src/main/resources/dynamichud.mixins.json | 2 +-
src/main/resources/fabric.mod.json | 7 +-
23 files changed, 969 insertions(+), 135 deletions(-)
create mode 100644 src/main/java/com/tanishisherewith/dynamichud/DynamicHudIntegration.java
create mode 100644 src/main/java/com/tanishisherewith/dynamichud/mixins/ScreenMixin.java
delete mode 100644 src/main/java/com/tanishisherewith/dynamichud/mixins/TitleScreenMixin.java
create mode 100644 src/main/java/com/tanishisherewith/dynamichud/newTrial/DynamicHUD.java
create mode 100644 src/main/java/com/tanishisherewith/dynamichud/newTrial/DynamicHudTest.java
create mode 100644 src/main/java/com/tanishisherewith/dynamichud/newTrial/utils/DynamicValueRegistry.java
create mode 100644 src/main/java/com/tanishisherewith/dynamichud/newTrial/utils/UID.java
create mode 100644 src/main/java/com/tanishisherewith/dynamichud/newTrial/widget/Widget.java
create mode 100644 src/main/java/com/tanishisherewith/dynamichud/newTrial/widget/WidgetData.java
create mode 100644 src/main/java/com/tanishisherewith/dynamichud/newTrial/widget/WidgetManager.java
create mode 100644 src/main/java/com/tanishisherewith/dynamichud/newTrial/widget/WidgetRenderer.java
create mode 100644 src/main/java/com/tanishisherewith/dynamichud/newTrial/widgets/TextWidget.java
diff --git a/build.gradle b/build.gradle
index 8511df6..4f9c008 100644
--- a/build.gradle
+++ b/build.gradle
@@ -1,5 +1,5 @@
plugins {
- id 'fabric-loom' version '1.2-SNAPSHOT'
+ id 'fabric-loom' version '1.3-SNAPSHOT'
id 'maven-publish'
}
@@ -28,7 +28,7 @@ dependencies {
modImplementation "net.fabricmc:fabric-loader:${project.loader_version}"
modImplementation "net.fabricmc.fabric-api:fabric-api:${project.fabric_version}"
- modApi "com.terraformersmc:modmenu:7.1.0"
+ modApi "com.terraformersmc:modmenu:9.0.0"
}
processResources {
diff --git a/gradle.properties b/gradle.properties
index 3a255cd..27d3423 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -1,16 +1,16 @@
-# Done to increase the memory available to gradle.
org.gradle.jvmargs=-Xmx3G
+org.gradle.parallel=true
# Fabric Properties
- minecraft_version=1.20.1
- yarn_mappings=1.20.1+build.1
- loader_version=0.14.21
+# check these on https://fabricmc.net/develop
+ minecraft_version=1.20.4
+ yarn_mappings=1.20.4+build.1
+ loader_version=0.15.0
# Mod Properties
- mod_version = 1.2.0
+ mod_version = 1.3
maven_group = com.tanishisherewith
archives_base_name = dynamichud
# Dependencies
- # check this on https://modmuss50.me/fabric.html
- fabric_version=0.83.1+1.20.1
+ fabric_version=0.91.1+1.20.4
\ No newline at end of file
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index 37aef8d..3499ded 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-8.1.1-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip
networkTimeout=10000
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
diff --git a/src/main/java/com/tanishisherewith/dynamichud/DynamicHUD.java b/src/main/java/com/tanishisherewith/dynamichud/DynamicHUD.java
index 6f43839..dd81dae 100644
--- a/src/main/java/com/tanishisherewith/dynamichud/DynamicHUD.java
+++ b/src/main/java/com/tanishisherewith/dynamichud/DynamicHUD.java
@@ -2,7 +2,9 @@
import com.tanishisherewith.dynamichud.huds.AbstractMoveableScreen;
import com.tanishisherewith.dynamichud.interfaces.IWigdets;
+import com.tanishisherewith.dynamichud.newTrial.utils.UID;
import com.tanishisherewith.dynamichud.util.DynamicUtil;
+import net.fabricmc.api.ClientModInitializer;
import net.fabricmc.api.ModInitializer;
import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents;
import net.fabricmc.fabric.api.client.keybinding.v1.KeyBindingHelper;
@@ -21,7 +23,9 @@
import java.io.File;
-public class DynamicHUD implements ModInitializer {
+public class DynamicHUD implements ClientModInitializer {
+
+ public static MinecraftClient MC = MinecraftClient.getInstance();
private static final Logger logger = LoggerFactory.getLogger("DynamicHud");
static AbstractMoveableScreen Screen;
@@ -29,12 +33,12 @@ public class DynamicHUD implements ModInitializer {
private static String TranslationKey = "DynamicHud Editor Screen";
private static InputUtil.Type inputType = InputUtil.Type.KEYSYM;
private static int key = GLFW.GLFW_KEY_RIGHT_SHIFT;
- public static final KeyBinding EditorScreenKeyBinding = KeyBindingHelper.registerKeyBinding(new KeyBinding(
- TranslationKey,
- inputType,
- key,
- keybingCategory
- ));
+ // public static final KeyBinding EditorScreenKeyBinding = KeyBindingHelper.registerKeyBinding(new KeyBinding(
+// TranslationKey,
+// inputType,
+// key,
+// keybingCategory
+// ));
private static String filename = "widgets.nbt";
private static File fileDirectory = FabricLoader.getInstance().getConfigDir().toFile();
public static final File WIDGETS_FILE = new File(fileDirectory, filename);
@@ -115,7 +119,7 @@ public static void printWarn(String msg) {
}
@Override
- public void onInitialize() {
+ public void onInitializeClient() {
dynamicutil = new DynamicUtil(mc);
printInfo("DynamicHud Initialised");
@@ -138,7 +142,7 @@ public void onInitialize() {
printInfo("Load file Directory: " + FileDirectory);
}
}
- DynamicUtil.openDynamicScreen(EditorScreenKeyBinding, Screen);
+ // DynamicUtil.openDynamicScreen(EditorScreenKeyBinding, Screen);
});
//RenderCallBack
diff --git a/src/main/java/com/tanishisherewith/dynamichud/DynamicHudIntegration.java b/src/main/java/com/tanishisherewith/dynamichud/DynamicHudIntegration.java
new file mode 100644
index 0000000..d47d8a4
--- /dev/null
+++ b/src/main/java/com/tanishisherewith/dynamichud/DynamicHudIntegration.java
@@ -0,0 +1,48 @@
+package com.tanishisherewith.dynamichud;
+
+import com.tanishisherewith.dynamichud.huds.AbstractMoveableScreen;
+import com.tanishisherewith.dynamichud.newTrial.widget.WidgetManager;
+import com.tanishisherewith.dynamichud.newTrial.widget.WidgetRenderer;
+import net.fabricmc.fabric.api.client.keybinding.v1.KeyBindingHelper;
+import net.fabricmc.loader.api.FabricLoader;
+import net.minecraft.client.option.KeyBinding;
+import net.minecraft.client.util.InputUtil;
+import org.lwjgl.glfw.GLFW;
+
+import java.io.File;
+
+public interface DynamicHudIntegration {
+ String KEYBIND_CATEGORY = "DynamicHud";
+ String TRANSLATION_KEY = "DynamicHud Editor Screen";
+ InputUtil.Type INPUT_TYPE = InputUtil.Type.KEYSYM;
+ int KEY = GLFW.GLFW_KEY_RIGHT_SHIFT;
+ KeyBinding EDITOR_SCREEN_KEY_BINDING = KeyBindingHelper.registerKeyBinding(new KeyBinding(
+ TRANSLATION_KEY,
+ INPUT_TYPE,
+ KEY,
+ KEYBIND_CATEGORY
+ ));
+ String FILENAME = "widgets.nbt";
+ File FILE_DIRECTORY = FabricLoader.getInstance().getConfigDir().toFile();
+ File WIDGETS_FILE = new File(FILE_DIRECTORY, FILENAME);
+
+ void init();
+ void addWidgets();
+ default void registerWidgets(){}
+
+ /**
+ * Returns the file where widgets are saved.
+ *
+ * @return The widgets file.
+ */
+ default File getWidgetsFile() {
+ return WIDGETS_FILE;
+ }
+
+ AbstractMoveableScreen getMovableScreen();
+
+ default WidgetRenderer getWidgetRenderer(){
+ return new WidgetRenderer(WidgetManager.getWidgets());
+ }
+}
+
diff --git a/src/main/java/com/tanishisherewith/dynamichud/helpers/DrawHelper.java b/src/main/java/com/tanishisherewith/dynamichud/helpers/DrawHelper.java
index 34f9115..75ac161 100644
--- a/src/main/java/com/tanishisherewith/dynamichud/helpers/DrawHelper.java
+++ b/src/main/java/com/tanishisherewith/dynamichud/helpers/DrawHelper.java
@@ -1,11 +1,13 @@
package com.tanishisherewith.dynamichud.helpers;
import com.mojang.blaze3d.systems.RenderSystem;
+import com.tanishisherewith.dynamichud.newTrial.DynamicHUD;
import com.tanishisherewith.dynamichud.util.CustomTextRenderer;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.font.TextRenderer;
import net.minecraft.client.gui.DrawContext;
import net.minecraft.client.render.*;
+import net.minecraft.client.util.math.MatrixStack;
import org.joml.Matrix4f;
public class DrawHelper extends DrawContext {
@@ -207,4 +209,24 @@ public static void drawOutlinedBox(DrawContext drawContext, int x1, int y1, int
drawContext.fill(x2 - 1, y1 + 1, x2, y2 - 1, color);
}
+ /**
+ * This method assumes that the element is widget in the top-left corner (i.e. all drawing happens with respect to the top-left corner).
+ * @param matrices
+ * @param x X position of top-left corner of widget
+ * @param y Y position of top-left corner of widget
+ * @param scale Scale the matrices
+ */
+ public static void scaleAndPosition(MatrixStack matrices, float x, float y,float z, float scale) {
+ matrices.push(); // Save the current transformation state
+
+ // Translate to the desired position
+ matrices.translate(x, y, z);
+
+ // Scale the matrix
+ matrices.scale(scale, scale, 1.0F);
+ }
+ public static void stopScaling(MatrixStack matrices){
+ matrices.pop(); // Restore the previous transformation state
+ }
+
}
diff --git a/src/main/java/com/tanishisherewith/dynamichud/mixins/ScreenMixin.java b/src/main/java/com/tanishisherewith/dynamichud/mixins/ScreenMixin.java
new file mode 100644
index 0000000..852887d
--- /dev/null
+++ b/src/main/java/com/tanishisherewith/dynamichud/mixins/ScreenMixin.java
@@ -0,0 +1,22 @@
+package com.tanishisherewith.dynamichud.mixins;
+
+import com.tanishisherewith.dynamichud.DynamicHudIntegration;
+import com.tanishisherewith.dynamichud.newTrial.DynamicHUD;
+import com.tanishisherewith.dynamichud.newTrial.DynamicHudTest;
+import com.tanishisherewith.dynamichud.newTrial.widget.WidgetRenderer;
+import net.minecraft.client.gui.DrawContext;
+import net.minecraft.client.gui.screen.Screen;
+import org.spongepowered.asm.mixin.Mixin;
+import org.spongepowered.asm.mixin.injection.At;
+import org.spongepowered.asm.mixin.injection.Inject;
+import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
+
+@Mixin(Screen.class)
+public abstract class ScreenMixin {
+ @Inject(at = @At("TAIL"), method = "render")
+ private void render(DrawContext context, int mouseX, int mouseY, float delta, CallbackInfo ci) {
+ for(WidgetRenderer widgetRenderer: DynamicHUD.getWidgetRenderers()){
+ widgetRenderer.renderWidgets(context);
+ }
+ }
+}
diff --git a/src/main/java/com/tanishisherewith/dynamichud/mixins/TitleScreenMixin.java b/src/main/java/com/tanishisherewith/dynamichud/mixins/TitleScreenMixin.java
deleted file mode 100644
index 6abfe5e..0000000
--- a/src/main/java/com/tanishisherewith/dynamichud/mixins/TitleScreenMixin.java
+++ /dev/null
@@ -1,104 +0,0 @@
-package com.tanishisherewith.dynamichud.mixins;
-
-import com.tanishisherewith.dynamichud.DynamicHUD;
-import com.tanishisherewith.dynamichud.handlers.DefaultDragHandler;
-import com.tanishisherewith.dynamichud.handlers.DefaultMouseHandler;
-import com.tanishisherewith.dynamichud.handlers.DragHandler;
-import com.tanishisherewith.dynamichud.handlers.MouseHandler;
-import com.tanishisherewith.dynamichud.util.DynamicUtil;
-import com.tanishisherewith.dynamichud.widget.Widget;
-import net.minecraft.client.gui.DrawContext;
-import net.minecraft.client.gui.screen.Screen;
-import net.minecraft.client.gui.screen.TitleScreen;
-import net.minecraft.text.Text;
-import org.spongepowered.asm.mixin.Mixin;
-import org.spongepowered.asm.mixin.injection.At;
-import org.spongepowered.asm.mixin.injection.Inject;
-import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
-
-@Mixin(TitleScreen.class)
-public abstract class TitleScreenMixin extends Screen {
- protected MouseHandler mouseHandler = new DefaultMouseHandler(null, null, null);
- protected DragHandler dragHandler = new DefaultDragHandler();
- protected Widget selectedWidget;
- protected int dragStartX = 0, dragStartY = 0; // The starting position of a drag operation
- protected int gridSize = 1; // The size of each grid cell in pixels
- DynamicUtil dynamicUtil = DynamicHUD.getDynamicUtil();
-
- protected TitleScreenMixin(Text title) {
- super(title);
- }
-
- public void setGridSize(int gridSize) {
- this.gridSize = gridSize;
- }
-
- @Inject(at = @At("TAIL"), method = "render")
- private void render(DrawContext context, int mouseX, int mouseY, float delta, CallbackInfo ci) {
- // Draw custom text on the title screen
- if (dynamicUtil != null && (dynamicUtil.MainMenuWidgetAdded || dynamicUtil.WidgetLoaded)) {
- dynamicUtil.render(context, delta);
- }
- }
-
- @Override
- public boolean mouseClicked(double mouseX, double mouseY, int button) {
- if (mouseHandler.mouseClicked(mouseX, mouseY, button)) {
- return true;
- }
-
- for (Widget widget : dynamicUtil.getWidgetManager().getMainMenuWidgets()) {
- if (widget.getWidgetBox().contains(widget, mouseX, mouseY,widget.getScale())) {
- if (button == 1) { // Right-click
- handleRightClickOnWidget(widget);
- } else if (button == 0) {
- widget.enabled = !widget.enabled;
- }
- if (dragHandler.startDragging(widget, mouseX, mouseY) && button == 0 && widget.isDraggable) {
- selectedWidget = widget;
- return true;
- }
- }
- }
- return super.mouseClicked(mouseX, mouseY, button);
- }
-
-
- @Override
- public boolean mouseDragged(double mouseX, double mouseY, int button, double deltaX, double deltaY) {
- if (mouseHandler.mouseDragged(mouseX, mouseY, button, deltaX, deltaY)) {
- return true;
- }
- if (selectedWidget != null && selectedWidget.isDraggable) {
- // Update the position of the widget while dragging
- int newX = (int) (mouseX - dragStartX);
- int newY = (int) (mouseY - dragStartY);
-
- // Snap the widget to the grid
- newX = (newX / gridSize) * gridSize;
- newY = (newY / gridSize) * gridSize;
-
- selectedWidget.setX(newX);
- selectedWidget.setY(newY);
- return true;
- }
- return super.mouseDragged(mouseX, mouseY, button, deltaX, deltaY);
- }
-
- @Override
- public boolean mouseReleased(double mouseX, double mouseY, int button) {
- if (mouseHandler.mouseReleased(mouseX, mouseY, button)) {
- return true;
- }
- if (selectedWidget != null) {
- selectedWidget = null;
- return true;
- }
- return super.mouseReleased(mouseX, mouseY, button);
- }
-
- protected void handleRightClickOnWidget(Widget widget) {
-
- }
-
-}
diff --git a/src/main/java/com/tanishisherewith/dynamichud/newTrial/DynamicHUD.java b/src/main/java/com/tanishisherewith/dynamichud/newTrial/DynamicHUD.java
new file mode 100644
index 0000000..f2dda44
--- /dev/null
+++ b/src/main/java/com/tanishisherewith/dynamichud/newTrial/DynamicHUD.java
@@ -0,0 +1,122 @@
+package com.tanishisherewith.dynamichud.newTrial;
+
+import com.tanishisherewith.dynamichud.DynamicHudIntegration;
+import com.tanishisherewith.dynamichud.huds.AbstractMoveableScreen;
+import com.tanishisherewith.dynamichud.newTrial.widget.WidgetManager;
+import com.tanishisherewith.dynamichud.newTrial.widget.WidgetRenderer;
+import com.tanishisherewith.dynamichud.util.DynamicUtil;
+import net.fabricmc.api.ClientModInitializer;
+import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents;
+import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents;
+import net.fabricmc.fabric.api.networking.v1.ServerPlayConnectionEvents;
+import net.fabricmc.loader.api.FabricLoader;
+import net.fabricmc.loader.api.metadata.ModMetadata;
+import net.minecraft.client.MinecraftClient;
+import net.minecraft.client.option.KeyBinding;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+public class DynamicHUD implements ClientModInitializer {
+
+ public static MinecraftClient MC = MinecraftClient.getInstance();
+ private static final Logger logger = LoggerFactory.getLogger("DynamicHud");
+ private static final List widgetRenderers = new ArrayList<>();
+
+ public static void addWidgetRenderer(WidgetRenderer widgetRenderer) {
+ widgetRenderers.add(widgetRenderer);
+ }
+
+ public static List getWidgetRenderers() {
+ return widgetRenderers;
+ }
+
+ public static void printInfo(String msg) {
+ logger.info(msg);
+ }
+
+ public static void printWarn(String msg) {
+ logger.warn(msg);
+ }
+
+ @Override
+ public void onInitializeClient() {
+ printInfo("DynamicHud Initialised");
+
+ //Save and Load
+ /* ClientTickEvents.START_CLIENT_TICK.register(server -> {
+ if (iWigdets != null) {
+ if (!WIDGETS_FILE.exists()) {
+ if (!dynamicutil.WidgetAdded) {
+ iWigdets.addWigdets(dynamicutil);
+ }
+ if (!dynamicutil.MainMenuWidgetAdded) {
+ iWigdets.addMainMenuWigdets(dynamicutil);
+ }
+ }
+
+ if (WIDGETS_FILE.exists() && !dynamicutil.WidgetLoaded) {
+ iWigdets.loadWigdets(dynamicutil);
+ printInfo("Widgets loaded");
+ File FileDirectory = new File(fileDirectory, filename);
+ printInfo("Load file Directory: " + FileDirectory);
+ }
+ }
+ DynamicUtil.openDynamicScreen(EditorScreenKeyBinding, Screen);
+ });
+
+ */
+ FabricLoader.getInstance().getEntrypointContainers("dynamicHud", DynamicHudIntegration.class).forEach(entrypoint -> {
+ ModMetadata metadata = entrypoint.getProvider().getMetadata();
+ String modId = metadata.getId();
+ AbstractMoveableScreen screen;
+ try {
+ DynamicHudIntegration DHIntegration = entrypoint.getEntrypoint();
+ DHIntegration.init();
+
+ File widgetsFile = DHIntegration.getWidgetsFile();
+
+ if(widgetsFile.exists()) {
+ WidgetManager.loadWidgets(widgetsFile);
+ }else{
+ DHIntegration.addWidgets();
+ }
+
+ screen = DHIntegration.getMovableScreen();
+ KeyBinding binding =DHIntegration.EDITOR_SCREEN_KEY_BINDING;
+ WidgetRenderer widgetRenderer = DHIntegration.getWidgetRenderer();
+ addWidgetRenderer(widgetRenderer);
+
+ //Register events for rendering, saving, loading, and opening the hudEditor
+ ClientTickEvents.START_CLIENT_TICK.register((client)->{
+ DynamicUtil.openDynamicScreen(binding, screen);
+ });
+
+ // Save during exiting a world, server or Minecraft itself
+ ServerLifecycleEvents.SERVER_STOPPING.register(server -> saveWidgetsSafely(widgetsFile));
+ ServerLifecycleEvents.END_DATA_PACK_RELOAD.register((server, resourceManager, s) -> saveWidgetsSafely(widgetsFile));
+ ServerPlayConnectionEvents.DISCONNECT.register((handler, packetSender) -> saveWidgetsSafely(widgetsFile));
+ Runtime.getRuntime().addShutdownHook(new Thread(() -> saveWidgetsSafely(widgetsFile)));
+ } catch (Throwable e) {
+ if(e instanceof IOException){
+ logger.warn("An error has occured while loading widgets of mod {}", modId,e);
+ }else {
+ logger.warn("Mod {} has incorrect implementation of dynamicHud", modId, e);
+ }
+ }
+ });
+ }
+ private void saveWidgetsSafely(File widgetsFile) {
+ try {
+ WidgetManager.saveWidgets(widgetsFile);
+ } catch (IOException e) {
+ logger.error("Failed to save widgets");
+ throw new RuntimeException(e);
+ }
+ }
+
+}
diff --git a/src/main/java/com/tanishisherewith/dynamichud/newTrial/DynamicHudTest.java b/src/main/java/com/tanishisherewith/dynamichud/newTrial/DynamicHudTest.java
new file mode 100644
index 0000000..78177ee
--- /dev/null
+++ b/src/main/java/com/tanishisherewith/dynamichud/newTrial/DynamicHudTest.java
@@ -0,0 +1,60 @@
+package com.tanishisherewith.dynamichud.newTrial;
+
+import com.tanishisherewith.dynamichud.DynamicHudIntegration;
+import com.tanishisherewith.dynamichud.huds.AbstractMoveableScreen;
+import com.tanishisherewith.dynamichud.huds.MoveableScreen;
+import com.tanishisherewith.dynamichud.newTrial.utils.DynamicValueRegistry;
+import com.tanishisherewith.dynamichud.newTrial.widget.WidgetManager;
+import com.tanishisherewith.dynamichud.newTrial.widget.WidgetRenderer;
+import com.tanishisherewith.dynamichud.newTrial.widgets.TextWidget;
+import com.tanishisherewith.dynamichud.util.DynamicUtil;
+import net.minecraft.client.gui.screen.GameMenuScreen;
+import net.minecraft.client.gui.screen.TitleScreen;
+import net.minecraft.client.gui.screen.multiplayer.MultiplayerScreen;
+import net.minecraft.text.Text;
+
+import java.lang.reflect.Array;
+import java.util.Arrays;
+import java.util.Collections;
+
+public class DynamicHudTest implements DynamicHudIntegration {
+ TextWidget textWidget;
+
+ DynamicValueRegistry registry = new DynamicValueRegistry();
+ WidgetRenderer renderer;
+ @Override
+ public void init() {
+ DynamicValueRegistry.registerGlobal("FPS",() -> String.valueOf(DynamicHUD.MC.getCurrentFps()));
+ registry.registerLocal("FPS",()->String.valueOf(DynamicHUD.MC.getCurrentFps()));
+ }
+
+ @Override
+ public void addWidgets() {
+ textWidget = new TextWidget.Builder()
+ .setX(0.6f)
+ .setY(0.6f)
+ .setDraggable(true)
+ .shouldScale(false)
+ .rainbow(false)
+ .setDHKey("FPS")
+ .build();
+
+
+ WidgetManager.addWidget(textWidget);
+ }
+
+ @Override
+ public AbstractMoveableScreen getMovableScreen() {
+ return new MoveableScreen(Text.of("Editor LOL"),new DynamicUtil(DynamicHUD.MC));
+ }
+
+ @Override
+ public WidgetRenderer getWidgetRenderer() {
+ renderer = new WidgetRenderer(Collections.singletonList(textWidget));
+ renderer.shouldRenderInGameHud(true);
+ renderer.addScreen(TitleScreen.class);
+ renderer.addScreen(MultiplayerScreen.class);
+ return renderer;
+ }
+
+}
diff --git a/src/main/java/com/tanishisherewith/dynamichud/newTrial/utils/DynamicValueRegistry.java b/src/main/java/com/tanishisherewith/dynamichud/newTrial/utils/DynamicValueRegistry.java
new file mode 100644
index 0000000..2927a35
--- /dev/null
+++ b/src/main/java/com/tanishisherewith/dynamichud/newTrial/utils/DynamicValueRegistry.java
@@ -0,0 +1,32 @@
+package com.tanishisherewith.dynamichud.newTrial.utils;
+
+import java.util.*;
+import java.util.function.Supplier;
+
+public class DynamicValueRegistry {
+ private static final Map> globalRegistry = new HashMap<>();
+ private final Map> localRegistry = new HashMap<>();
+
+ public static void registerGlobal(String key, Supplier> supplier) {
+ globalRegistry.put(key, supplier);
+ }
+
+ public void registerLocal(String key, Supplier> supplier) {
+ localRegistry.put(key, supplier);
+ }
+ public static Supplier> getGlobal(String key) {
+ return globalRegistry.get(key);
+ }
+
+ public Supplier> get(String key) {
+ // First, try to get the supplier from the local registry
+ Supplier> supplier = localRegistry.get(key);
+
+ // If the supplier is not in the local registry, try the global registry
+ if (supplier == null) {
+ supplier = globalRegistry.get(key);
+ }
+
+ return supplier;
+ }
+}
diff --git a/src/main/java/com/tanishisherewith/dynamichud/newTrial/utils/UID.java b/src/main/java/com/tanishisherewith/dynamichud/newTrial/utils/UID.java
new file mode 100644
index 0000000..cbe721b
--- /dev/null
+++ b/src/main/java/com/tanishisherewith/dynamichud/newTrial/utils/UID.java
@@ -0,0 +1,30 @@
+package com.tanishisherewith.dynamichud.newTrial.utils;
+
+import java.util.Random;
+
+public class UID {
+ private static final String ALPHANUMERIC = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_";
+ private static final int LENGTH = 6;
+ private static final Random RANDOM = new Random();
+ public String uniqueID;
+
+ public UID(String id) {
+ this.uniqueID = id;
+ }
+
+ public static UID generate() {
+ StringBuilder sb = new StringBuilder(LENGTH);
+ for (int i = 0; i < LENGTH; i++) {
+ sb.append(ALPHANUMERIC.charAt(RANDOM.nextInt(ALPHANUMERIC.length())));
+ }
+ return new UID(sb.toString());
+ }
+
+ public String getUniqueID() {
+ return uniqueID;
+ }
+
+ public void setUniqueID(String uniqueID) {
+ this.uniqueID = uniqueID;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/tanishisherewith/dynamichud/newTrial/widget/Widget.java b/src/main/java/com/tanishisherewith/dynamichud/newTrial/widget/Widget.java
new file mode 100644
index 0000000..ce5950d
--- /dev/null
+++ b/src/main/java/com/tanishisherewith/dynamichud/newTrial/widget/Widget.java
@@ -0,0 +1,244 @@
+package com.tanishisherewith.dynamichud.newTrial.widget;
+
+import com.tanishisherewith.dynamichud.DynamicHUD;
+import com.tanishisherewith.dynamichud.helpers.ColorHelper;
+import com.tanishisherewith.dynamichud.helpers.DrawHelper;
+import com.tanishisherewith.dynamichud.newTrial.utils.UID;
+import com.tanishisherewith.dynamichud.widget.WidgetBox;
+import net.minecraft.client.MinecraftClient;
+import net.minecraft.client.gui.DrawContext;
+import net.minecraft.nbt.NbtCompound;
+
+import java.util.Set;
+
+public abstract class Widget {
+
+ protected MinecraftClient mc = MinecraftClient.getInstance();
+
+ //This is the UID of the widget used to identify during loading and saving
+ public UID uid = UID.generate();
+ public boolean display = true; // Whether the widget is enabled
+ public boolean isDraggable = true;
+ protected float xPercent; // The x position of the widget as a percentage of the screen width
+ protected float yPercent; // The y position of the widget as a percentage of the screen height
+ public boolean shouldScale = false;
+ protected WidgetBox widgetBox;
+ public static WidgetData> DATA;
+
+ public Widget(WidgetData> DATA){
+ Widget.DATA = DATA;
+ widgetBox = new WidgetBox(0,0,0,0,0);
+ init();
+ }
+
+ public void init(){
+
+ }
+ /**
+ * Returns the x position of the widget.
+ *
+ * @return The x position of the widget in pixels
+ */
+ public float getX() {
+ return DynamicHUD.MC.getWindow().getScaledWidth() * xPercent;
+ }
+
+ /**
+ * Returns the y position of the widget.
+ *
+ * @return The y position of the widget in pixels
+ */
+ public float getY() {
+ return DynamicHUD.MC.getWindow().getScaledHeight() * yPercent;
+ }
+
+ public void setDraggable(boolean draggable) {
+ isDraggable = draggable;
+ }
+ public boolean isOverlapping(Set other) {
+ for (Widget widgetBox : other) {
+ if ((this.getX() < widgetBox.getX() + widgetBox.getWidgetBox().getWidth() && this.getX() + this.getWidgetBox().getWidth() > widgetBox.getX() &&
+ this.getY() < widgetBox.getY() + widgetBox.getWidgetBox().getHeight() && this.getY() + this.getWidgetBox().getHeight() > widgetBox.getY())) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public boolean isOverlapping(Widget other) {
+ return this.getX() < other.getX() + other.getWidgetBox().getWidth() && this.getX() + this.getWidgetBox().getWidth() > other.getX() &&
+ this.getY() < other.getY() + other.getWidgetBox().getHeight() && this.getY() + this.getWidgetBox().getHeight() > other.getY();
+ }
+
+ /**
+ * Renders the widget on the screen.
+ */
+ public void render(DrawContext drawContext){
+ if(shouldScale) {
+ DrawHelper.scaleAndPosition(drawContext.getMatrices(), getX(), getY(),0, 1.0f);
+ }
+
+ renderWidget(drawContext);
+
+ if(shouldScale){
+ DrawHelper.stopScaling(drawContext.getMatrices());
+ }
+ }
+ /**
+ * Renders the widget on the editor screen.
+ */
+ public void renderInEditor(DrawContext drawContext){
+ if(shouldScale) {
+ DrawHelper.scaleAndPosition(drawContext.getMatrices(), getX(), getY(),0, 1.0f);
+ }
+
+ renderWidgetInEditor(drawContext);
+
+ if(shouldScale){
+ DrawHelper.stopScaling(drawContext.getMatrices());
+ }
+ }
+
+
+ /**
+ * Renders the widget on the screen
+ *
+ * @param context
+ */
+ public abstract void renderWidget(DrawContext context);
+
+
+ /**
+ * Renders the widget in the editor screen with a background.
+ * Override this method without super call to remove the background.
+ * Could also be used to display placeholder values.
+ *
+ * @param context
+ */
+ public void renderWidgetInEditor(DrawContext context){
+ displayBg(context);
+
+ renderWidget(context);
+ }
+
+ /**
+ * Displays a faint grayish background if enabled or faint reddish background if disabled.
+ * Drawn with 2 pixel offset to all sides
+ *
+ * @param context
+ */
+ protected void displayBg(DrawContext context){
+ int backgroundColor = this.shouldDisplay() ? ColorHelper.getColor(0, 0, 0, 128) : ColorHelper.getColor(255, 0, 0, 128);
+ WidgetBox box = this.getWidgetBox();
+ DrawHelper.fill(context, (int) (box.x1 - 2), (int) (box.y1 - 2), (int) (box.x2 + 2), (int) (box.y2 + 2), backgroundColor);
+ }
+
+
+ public void readFromTag(NbtCompound tag) {
+ uid = new UID(tag.getString("UID"));
+ xPercent = tag.getFloat("xPercent");
+ yPercent = tag.getFloat("yPercent");
+ display = tag.getBoolean("Display");
+ isDraggable = tag.getBoolean("isDraggable");
+ }
+
+ /**
+ * Writes the state of this widget to the given tag.
+ *
+ * @param tag The tag to write to
+ */
+ public void writeToTag(NbtCompound tag) {
+ tag.putString("UID", uid.getUniqueID());
+ tag.putBoolean("isDraggable", isDraggable);
+ tag.putFloat("xPercent", xPercent);
+ tag.putFloat("yPercent", yPercent);
+ tag.putBoolean("Display", display);
+ }
+
+ public boolean shouldDisplay() {
+ return display;
+ }
+
+ public WidgetBox getWidgetBox() {
+ return widgetBox;
+ }
+
+ public void setxPercent(float xPercent) {
+ this.xPercent = xPercent;
+ }
+
+ public void setyPercent(float yPercent) {
+ this.yPercent = yPercent;
+ }
+
+ public void setUid(UID uid) {
+ this.uid = uid;
+ }
+
+ public void setShouldScale(boolean shouldScale) {
+ this.shouldScale = shouldScale;
+ }
+
+ @Override
+ public String toString() {
+ return "Widget{" +
+ "uniqueId='" + uid.getUniqueID() + '\'' +
+ ", xPercent=" + xPercent +
+ ", yPercent=" + yPercent +
+ ", display=" + display +
+ '}';
+ }
+
+ public abstract static class WidgetBuilder {
+ protected float xPercent;
+ protected float yPercent;
+ protected boolean display = true;
+ protected boolean isDraggable = true;
+ protected boolean shouldScale = true;
+
+ /**
+ * X Position of the widget relative to the screen.
+ * Should be between 0f - 1f
+ *
+ * @param xPercent
+ * @return
+ */
+ public T setX(float xPercent) {
+ this.xPercent = xPercent;
+ return self();
+ }
+
+ /**
+ * Y Position of the widget relative to the screen.
+ * Should be between 0f - 1f
+ *
+ * @param yPercent
+ * @return
+ */
+ public T setY(float yPercent) {
+ this.yPercent = yPercent;
+ return self();
+ }
+
+ public T setDisplay(boolean display) {
+ this.display = display;
+ return self();
+ }
+
+ public T setDraggable(boolean isDraggable) {
+ this.isDraggable = isDraggable;
+ return self();
+ }
+
+ public T shouldScale(boolean shouldScale) {
+ this.shouldScale = shouldScale;
+ return self();
+ }
+
+ // Method to be overridden in subclasses to return "this" correctly
+ protected abstract T self();
+
+ // Method to construct a Widget object
+ public abstract S build();
+ }
+}
diff --git a/src/main/java/com/tanishisherewith/dynamichud/newTrial/widget/WidgetData.java b/src/main/java/com/tanishisherewith/dynamichud/newTrial/widget/WidgetData.java
new file mode 100644
index 0000000..7939e21
--- /dev/null
+++ b/src/main/java/com/tanishisherewith/dynamichud/newTrial/widget/WidgetData.java
@@ -0,0 +1,20 @@
+package com.tanishisherewith.dynamichud.newTrial.widget;
+
+import java.util.function.Supplier;
+
+public record WidgetData(String name, String description, Supplier widgetFactory) {
+ @Override
+ public String name() {
+ return name;
+ }
+
+ @Override
+ public String description() {
+ return description;
+ }
+
+ public Widget createWidget() {
+ return widgetFactory.get();
+ }
+
+}
diff --git a/src/main/java/com/tanishisherewith/dynamichud/newTrial/widget/WidgetManager.java b/src/main/java/com/tanishisherewith/dynamichud/newTrial/widget/WidgetManager.java
new file mode 100644
index 0000000..14f8dfc
--- /dev/null
+++ b/src/main/java/com/tanishisherewith/dynamichud/newTrial/widget/WidgetManager.java
@@ -0,0 +1,149 @@
+package com.tanishisherewith.dynamichud.newTrial.widget;
+
+import java.io.*;
+import java.nio.file.Files;
+import java.nio.file.StandardCopyOption;
+import java.util.*;
+
+import net.fabricmc.fabric.api.util.NbtType;
+import net.minecraft.nbt.*;
+
+import static com.tanishisherewith.dynamichud.DynamicHUD.printInfo;
+import static com.tanishisherewith.dynamichud.DynamicHUD.printWarn;
+
+/**
+ * Manages a collection of widgets, providing methods to add, remove, save, and load widgets.
+ */
+public class WidgetManager {
+ /**
+ * The list of widgets managed by this manager.
+ */
+ private static final List widgets = new ArrayList<>();
+
+ /**
+ * A map from widget names to WidgetData objects, used for creating new widgets.
+ */
+ private static final Map> widgetDataMap = new TreeMap<>();
+
+ /**
+ * Adds a WidgetData object to the map.
+ *
+ * @param data The WidgetData object to add.
+ */
+ public static void addWidgetData(WidgetData> data){
+ widgetDataMap.put(data.name(),data);
+ }
+
+ /**
+ * Adds multiple WidgetData objects to the map.
+ *
+ * @param widgetDatas The WidgetData objects to add.
+ */
+ public static void addWidgetDatas(WidgetData>... widgetDatas){
+ for(WidgetData> data: widgetDatas) {
+ widgetDataMap.put(data.name(), data);
+ }
+ }
+
+ /**
+ * Adds a widget to the list of managed widgets.
+ *
+ * @param widget The widget to add.
+ */
+ public static void addWidget(Widget widget) {
+ widgets.add(widget);
+ }
+
+ /**
+ * Adds multiple widgets to the list of managed widgets.
+ *
+ * @param widget The widgets to add.
+ */
+ public static void addWidgets(Widget... widget) {
+ widgets.addAll(Arrays.stream(widget).toList());
+ }
+
+ /**
+ * Removes a widget from the list of managed widgets.
+ *
+ * @param widget The widget to remove.
+ */
+ public static void removeWidget(Widget widget) {
+ widgets.remove(widget);
+ }
+
+ /**
+ * Saves the state of all widgets to the given file.
+ *
+ * @param file The file to save to
+ */
+ public static void saveWidgets(File file) throws IOException {
+ NbtCompound rootTag = new NbtCompound();
+ NbtList widgetList = new NbtList();
+
+ printInfo("Saving widgets");
+
+ if (widgets.isEmpty()) {
+ printWarn("Widgets are empty.. Saving interrupted to prevent empty file");
+ return;
+ }
+
+ Set widgetSet = new HashSet<>();
+ for (Widget widget : widgets) {
+ NbtCompound widgetTag = new NbtCompound();
+ widget.writeToTag(widgetTag);
+ // Check for duplicates
+ if (widgetSet.add(widgetTag.toString())) {
+ widgetList.add(widgetTag);
+ }
+ }
+
+ rootTag.put("widgets", widgetList);
+
+ // Use a temporary file to write the data
+ File tempFile = new File(file.getAbsolutePath() + ".tmp");
+ try (DataOutputStream out = new DataOutputStream(new FileOutputStream(tempFile))) {
+ NbtIo.write(rootTag, out);
+ // Check if the data has been written successfully
+ if (tempFile.length() > 0) {
+ // Check if the temporary file exists and can be renamed
+ Files.move(tempFile.toPath(), file.toPath(), StandardCopyOption.REPLACE_EXISTING);
+ } else {
+ throw new IOException("Failed to write data to temporary file OR Empty data passed");
+ }
+ }
+ }
+
+ /**
+ * Loads the state of all widgets from the given file.
+ *
+ * @param file The file to load from
+ */
+ public static void loadWidgets(File file) throws IOException {
+ widgets.clear();
+
+ if (file.exists()) {
+ NbtCompound rootTag = NbtIo.read(file.toPath());
+ NbtList widgetList = rootTag.getList("widgets", NbtType.COMPOUND);
+
+ for (int i = 0; i < widgetList.size(); i++) {
+ NbtCompound widgetTag = widgetList.getCompound(i);
+ WidgetData> widgetData = widgetDataMap.get(widgetTag.getString("name"));
+ Widget widget = widgetData.createWidget();
+ widget.readFromTag(widgetTag);
+ widgets.add(widget);
+ }
+ }else{
+ printWarn("Widget File does not exist. Try saving one first");
+ }
+ }
+
+ /**
+ * Returns the list of managed widgets.
+ *
+ * @return The list of managed widgets.
+ */
+ public static List getWidgets() {
+ return widgets;
+ }
+}
diff --git a/src/main/java/com/tanishisherewith/dynamichud/newTrial/widget/WidgetRenderer.java b/src/main/java/com/tanishisherewith/dynamichud/newTrial/widget/WidgetRenderer.java
new file mode 100644
index 0000000..6580509
--- /dev/null
+++ b/src/main/java/com/tanishisherewith/dynamichud/newTrial/widget/WidgetRenderer.java
@@ -0,0 +1,43 @@
+package com.tanishisherewith.dynamichud.newTrial.widget;
+
+import com.tanishisherewith.dynamichud.DynamicHUD;
+import net.minecraft.client.gui.DrawContext;
+import net.minecraft.client.gui.screen.Screen;
+
+import java.util.List;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+public class WidgetRenderer {
+ public final List> allowedScreens = new CopyOnWriteArrayList<>();
+ private boolean renderInGameHud = true;
+ List widgets;
+ public WidgetRenderer(List widgets){
+ this.widgets = widgets;
+ }
+
+ public void addScreen(Class extends Screen> screen){
+ allowedScreens.add(screen);
+ }
+
+ public void shouldRenderInGameHud(boolean renderInGameHud) {
+ this.renderInGameHud = renderInGameHud;
+ }
+
+ public void renderWidgets(DrawContext context) {
+ if(WidgetManager.getWidgets().isEmpty()) return;
+
+ Screen currentScreen = DynamicHUD.MC.currentScreen;
+ if(currentScreen == null && renderInGameHud){
+ for (Widget widget : widgets) {
+ widget.render(context);
+ }
+ return;
+ }
+ if (currentScreen!= null && allowedScreens.contains(DynamicHUD.MC.currentScreen.getClass())) {
+ for (Widget widget : widgets) {
+ // System.out.println("Rendering Widget: " + widget);
+ widget.render(context);
+ }
+ }
+ }
+}
diff --git a/src/main/java/com/tanishisherewith/dynamichud/newTrial/widgets/TextWidget.java b/src/main/java/com/tanishisherewith/dynamichud/newTrial/widgets/TextWidget.java
new file mode 100644
index 0000000..5701b0b
--- /dev/null
+++ b/src/main/java/com/tanishisherewith/dynamichud/newTrial/widgets/TextWidget.java
@@ -0,0 +1,129 @@
+package com.tanishisherewith.dynamichud.newTrial.widgets;
+
+import com.tanishisherewith.dynamichud.DynamicHUD;
+import com.tanishisherewith.dynamichud.helpers.ColorHelper;
+import com.tanishisherewith.dynamichud.helpers.DrawHelper;
+import com.tanishisherewith.dynamichud.newTrial.utils.DynamicValueRegistry;
+import com.tanishisherewith.dynamichud.newTrial.widget.Widget;
+import com.tanishisherewith.dynamichud.newTrial.widget.WidgetData;
+import net.minecraft.client.gui.DrawContext;
+import net.minecraft.nbt.NbtCompound;
+
+import java.awt.*;
+import java.util.function.Supplier;
+
+public class TextWidget extends Widget {
+ public static WidgetData DATA = new WidgetData<>("TextWidget","Display Text on screen",TextWidget::new);
+ Supplier textSupplier;
+ String dynamicRegistryKey;
+ protected boolean shadow; // Whether to draw a shadow behind the text
+ protected boolean rainbow; // Whether to apply a rainbow effect to the text
+
+ public TextWidget() {
+ this(null,false,false);
+ }
+
+ /**
+ * Searches for the supplier within the {@link DynamicValueRegistry#globalRegistry} using the given registryKey
+ *
+ * @param dynamicRegistryKey
+ * @param shadow
+ * @param rainbow
+ */
+ public TextWidget(String dynamicRegistryKey, boolean shadow, boolean rainbow) {
+ super(DATA);
+ this.dynamicRegistryKey = dynamicRegistryKey;
+ textSupplier = (Supplier) DynamicValueRegistry.getGlobal(dynamicRegistryKey);
+ this.shadow = shadow;
+ this.rainbow = rainbow;
+ }
+
+ /**
+ * Searches for the supplier within the {@link DynamicValueRegistry#localRegistry} using the given registryKey and registryValue
+ *
+ * @param dynamicRegistryKey
+ * @param shadow
+ * @param rainbow
+ */
+ public TextWidget(DynamicValueRegistry dynamicValueRegistry,String dynamicRegistryKey, boolean shadow, boolean rainbow) {
+ super(DATA);
+ this.dynamicRegistryKey = dynamicRegistryKey;
+ textSupplier = (Supplier) dynamicValueRegistry.get(dynamicRegistryKey);
+ this.shadow = shadow;
+ this.rainbow = rainbow;
+ }
+
+ @Override
+ public void renderWidget(DrawContext drawContext) {
+ drawContext.getMatrices().push();
+ drawContext.getMatrices().translate(0, 0, 420);
+ int color = rainbow ? ColorHelper.getColorFromHue((System.currentTimeMillis() % 10000) / (1 * 400f)) : Color.WHITE.getRGB();
+ if(textSupplier != null) {
+ String text = textSupplier.get();
+ drawContext.drawText(mc.textRenderer, text, (int) getX(), (int) getY(), color, shadow);
+ widgetBox.setSize(getX(),getY(),mc.textRenderer.getWidth(text),mc.textRenderer.fontHeight);
+ }
+ drawContext.getMatrices().pop();
+ }
+
+ @Override
+ public void writeToTag(NbtCompound tag) {
+ super.writeToTag(tag);
+ tag.putString("DRKey",dynamicRegistryKey);
+ tag.putBoolean("shadow",shadow);
+ tag.putBoolean("rainbow",rainbow);
+ }
+
+ @Override
+ public void readFromTag(NbtCompound tag) {
+ super.readFromTag(tag);
+ shadow = tag.getBoolean("shadow");
+ rainbow = tag.getBoolean("rainbow");
+ dynamicRegistryKey = tag.getString("DRKey");
+ }
+ public static class Builder extends WidgetBuilder {
+
+ protected boolean shadow = false;
+ protected boolean rainbow = false;
+ protected String dynamicRegistryKey = "";
+ DynamicValueRegistry dynamicValueRegistry = null;
+
+ public Builder shadow(boolean shadow) {
+ this.shadow = shadow;
+ return self();
+ }
+
+ public Builder rainbow(boolean rainbow) {
+ this.rainbow = rainbow;
+ return self();
+ }
+ public Builder setDHKey(String dynamicRegistryKey) {
+ this.dynamicRegistryKey = dynamicRegistryKey;
+ return self();
+ }
+ public Builder setDH(DynamicValueRegistry dynamicValueRegistry) {
+ this.dynamicValueRegistry = dynamicValueRegistry;
+ return self();
+ }
+
+ @Override
+ protected Builder self() {
+ return this;
+ }
+
+ @Override
+ public TextWidget build() {
+ TextWidget widget;
+ if(dynamicValueRegistry == null) {
+ widget = new TextWidget(dynamicRegistryKey, shadow, rainbow);
+ }else{
+ widget = new TextWidget(dynamicValueRegistry,dynamicRegistryKey, shadow, rainbow);
+ }
+ widget.setxPercent(xPercent);
+ widget.setyPercent(yPercent);
+ widget.setDraggable(isDraggable);
+ widget.setShouldScale(shouldScale);
+ return widget;
+ }
+ }
+}
diff --git a/src/main/java/com/tanishisherewith/dynamichud/util/DynamicUtil.java b/src/main/java/com/tanishisherewith/dynamichud/util/DynamicUtil.java
index 742792b..b169aa4 100644
--- a/src/main/java/com/tanishisherewith/dynamichud/util/DynamicUtil.java
+++ b/src/main/java/com/tanishisherewith/dynamichud/util/DynamicUtil.java
@@ -39,7 +39,7 @@ public DynamicUtil(MinecraftClient client) {
}
/**
- * Opens the MoveScreen when the specified key is pressed.
+ * Opens the MovableScreen when the specified key is pressed.
*
* @param key The key to listen for
* @param screen The AbstractMoveableScreen instance to use to set the screen
@@ -73,7 +73,7 @@ public void render(DrawContext context, float delta) {
}
// Draw each widget
- if (!client.options.debugEnabled || client.currentScreen instanceof AbstractMoveableScreen) {
+ if (client.currentScreen instanceof AbstractMoveableScreen) {
for (Widget widget : widgets) {
if (client.currentScreen instanceof AbstractMoveableScreen) {
widget.render(context);
diff --git a/src/main/java/com/tanishisherewith/dynamichud/util/contextmenu/TextWidgetButtonExt.java b/src/main/java/com/tanishisherewith/dynamichud/util/contextmenu/TextWidgetButtonExt.java
index fa9b90c..60be115 100644
--- a/src/main/java/com/tanishisherewith/dynamichud/util/contextmenu/TextWidgetButtonExt.java
+++ b/src/main/java/com/tanishisherewith/dynamichud/util/contextmenu/TextWidgetButtonExt.java
@@ -10,8 +10,4 @@ public TextWidgetButtonExt(TextRenderer textRenderer, int x, int y, int width, i
super(textRenderer, x, y, width, height, text);
}
- @Override
- public void renderButton(DrawContext context, int mouseX, int mouseY, float delta) {
- super.renderButton(context, mouseX, mouseY, delta);
- }
}
diff --git a/src/main/java/com/tanishisherewith/dynamichud/widget/WidgetBox.java b/src/main/java/com/tanishisherewith/dynamichud/widget/WidgetBox.java
index 067f6aa..6733d3f 100644
--- a/src/main/java/com/tanishisherewith/dynamichud/widget/WidgetBox.java
+++ b/src/main/java/com/tanishisherewith/dynamichud/widget/WidgetBox.java
@@ -44,4 +44,16 @@ public float getWidth() {
public float getHeight() {
return height;
}
+ public void setSize(float x,float y, float width, float height){
+ this.x1 = x;
+ this.x2 = x + width;
+ this.y1 = y;
+ this.y2 = y + height;
+ }
+ public void setPosition(float x,float y, float x2, float y2){
+ this.x1 = x;
+ this.x2 = x2;
+ this.y1 = y;
+ this.y2 = y2;
+ }
}
diff --git a/src/main/java/com/tanishisherewith/dynamichud/widget/WidgetManager.java b/src/main/java/com/tanishisherewith/dynamichud/widget/WidgetManager.java
index c930768..a16c90a 100644
--- a/src/main/java/com/tanishisherewith/dynamichud/widget/WidgetManager.java
+++ b/src/main/java/com/tanishisherewith/dynamichud/widget/WidgetManager.java
@@ -107,7 +107,7 @@ public void saveWidgets(File file) {
printInfo("Saving widgets");
- if (widgets.size() < 1 && MainMenuWidgets.size() < 1) {
+ if (widgets.isEmpty() && MainMenuWidgets.isEmpty()) {
printInfo("Widgets are empty.. Saving interrupted to prevent empty file");
return;
}
@@ -160,7 +160,8 @@ public Set loadWigdets(File file) {
if (file.exists()) {
printInfo("Widgets File exists");
try (DataInputStream in = new DataInputStream(new FileInputStream(file))) {
- NbtCompound rootTag = NbtIo.readCompressed(in);
+ DataInput input = new DataInputStream(in);
+ NbtCompound rootTag = NbtIo.readCompound(input);
NbtList widgetList = rootTag.getList("Widgets", NbtType.COMPOUND);
for (int i = 0; i < widgetList.size(); i++) {
NbtCompound widgetTag = widgetList.getCompound(i);
@@ -180,7 +181,8 @@ public Set loadMainMenuWigdets(File file) {
Set MainMenuwidgets = new HashSet<>();
if (file.exists()) {
try (DataInputStream in = new DataInputStream(new FileInputStream(file))) {
- NbtCompound rootTag = NbtIo.readCompressed(in);
+ DataInput input = new DataInputStream(in);
+ NbtCompound rootTag = NbtIo.readCompound(input);
NbtList MainMenuwidgetList = rootTag.getList("MainMenuWidgets", NbtType.COMPOUND);
for (int i = 0; i < MainMenuwidgetList.size(); i++) {
NbtCompound widgetTag = MainMenuwidgetList.getCompound(i);
diff --git a/src/main/resources/dynamichud.mixins.json b/src/main/resources/dynamichud.mixins.json
index f8d9096..2dc77ad 100644
--- a/src/main/resources/dynamichud.mixins.json
+++ b/src/main/resources/dynamichud.mixins.json
@@ -10,6 +10,6 @@
},
"client": [
"OptionsScreenMixin",
- "TitleScreenMixin"
+ "ScreenMixin"
]
}
diff --git a/src/main/resources/fabric.mod.json b/src/main/resources/fabric.mod.json
index 005e1b6..c694091 100644
--- a/src/main/resources/fabric.mod.json
+++ b/src/main/resources/fabric.mod.json
@@ -18,8 +18,11 @@
"license": "All-Rights-Reserved",
"environment": "client",
"entrypoints": {
- "main": [
- "com.tanishisherewith.dynamichud.DynamicHUD"
+ "client": [
+ "com.tanishisherewith.dynamichud.newTrial.DynamicHUD"
+ ],
+ "dynamicHud": [
+ "com.tanishisherewith.dynamichud.newTrial.DynamicHudTest"
],
"modmenu": [
"com.tanishisherewith.dynamichud.ModMenuIntegration"
From 55b21a8719cf057324c8d3a9c382f83f437f0963 Mon Sep 17 00:00:00 2001
From: tanishisherewithhh
<120117618+tanishisherewithhh@users.noreply.github.com>
Date: Thu, 4 Apr 2024 20:36:17 +0530
Subject: [PATCH 04/33] Major new additions.
---
build.gradle | 8 +
gradle.properties | 3 +-
.../dynamichud/DynamicHUD.java | 2 +-
.../dynamichud/ModMenuIntegration.java | 6 +-
.../dynamichud/helpers/DrawHelper.java | 8 +-
.../dynamichud/mixins/OptionsScreenMixin.java | 2 -
.../dynamichud/mixins/ScreenMixin.java | 17 ++
.../dynamichud/newTrial/DynamicHUD.java | 48 ++---
.../dynamichud/newTrial/DynamicHudTest.java | 33 ++--
.../newTrial/config/GlobalConfig.java | 51 ++++++
.../screens/AbstractMoveableScreen.java | 79 ++++++++
.../newTrial/utils/DynamicValueRegistry.java | 11 +-
.../dynamichud/newTrial/utils/System.java | 24 +++
.../dynamichud/newTrial/widget/Widget.java | 169 ++++++++++++++----
.../newTrial/widget/WidgetManager.java | 100 +++++++++--
.../newTrial/widget/WidgetRenderer.java | 84 ++++++++-
.../newTrial/widgets/TextWidget.java | 41 ++---
.../dynamichud/widget/WidgetBox.java | 4 +-
.../assets/DynamicHUD/DynamicHud-logo.png | Bin 550679 -> 0 bytes
src/main/resources/assets/DynamicHUD/logo.png | Bin 0 -> 192496 bytes
src/main/resources/dynamichud.mixins.json | 2 +
src/main/resources/fabric.mod.json | 2 +-
22 files changed, 577 insertions(+), 117 deletions(-)
create mode 100644 src/main/java/com/tanishisherewith/dynamichud/newTrial/config/GlobalConfig.java
create mode 100644 src/main/java/com/tanishisherewith/dynamichud/newTrial/screens/AbstractMoveableScreen.java
create mode 100644 src/main/java/com/tanishisherewith/dynamichud/newTrial/utils/System.java
delete mode 100644 src/main/resources/assets/DynamicHUD/DynamicHud-logo.png
create mode 100644 src/main/resources/assets/DynamicHUD/logo.png
diff --git a/build.gradle b/build.gradle
index 4f9c008..904421d 100644
--- a/build.gradle
+++ b/build.gradle
@@ -18,7 +18,13 @@ repositories {
// Loom adds the essential maven repositories to download Minecraft and libraries from automatically.
// See https://docs.gradle.org/current/userguide/declaring_repositories.html
// for more information about repositories.
+ mavenCentral()
+ jcenter()
maven { url "https://maven.terraformersmc.com/releases/" }
+ maven {
+ name 'Xander Maven'
+ url 'https://maven.isxander.dev/releases'
+ }
}
dependencies {
@@ -28,6 +34,8 @@ dependencies {
modImplementation "net.fabricmc:fabric-loader:${project.loader_version}"
modImplementation "net.fabricmc.fabric-api:fabric-api:${project.fabric_version}"
+ modImplementation "dev.isxander.yacl:yet-another-config-lib-fabric:${project.yacl_version}"
+
modApi "com.terraformersmc:modmenu:9.0.0"
}
diff --git a/gradle.properties b/gradle.properties
index 27d3423..ba63423 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -13,4 +13,5 @@ org.gradle.parallel=true
archives_base_name = dynamichud
# Dependencies
- fabric_version=0.91.1+1.20.4
\ No newline at end of file
+ fabric_version=0.91.1+1.20.4
+ yacl_version=3.3.2+1.20.4
\ No newline at end of file
diff --git a/src/main/java/com/tanishisherewith/dynamichud/DynamicHUD.java b/src/main/java/com/tanishisherewith/dynamichud/DynamicHUD.java
index dd81dae..18b14c4 100644
--- a/src/main/java/com/tanishisherewith/dynamichud/DynamicHUD.java
+++ b/src/main/java/com/tanishisherewith/dynamichud/DynamicHUD.java
@@ -27,7 +27,7 @@ public class DynamicHUD implements ClientModInitializer {
public static MinecraftClient MC = MinecraftClient.getInstance();
- private static final Logger logger = LoggerFactory.getLogger("DynamicHud");
+ public static final Logger logger = LoggerFactory.getLogger("DynamicHud");
static AbstractMoveableScreen Screen;
private static String keybingCategory = "DynamicHud";
private static String TranslationKey = "DynamicHud Editor Screen";
diff --git a/src/main/java/com/tanishisherewith/dynamichud/ModMenuIntegration.java b/src/main/java/com/tanishisherewith/dynamichud/ModMenuIntegration.java
index 07cb3aa..c9aec49 100644
--- a/src/main/java/com/tanishisherewith/dynamichud/ModMenuIntegration.java
+++ b/src/main/java/com/tanishisherewith/dynamichud/ModMenuIntegration.java
@@ -1,11 +1,15 @@
package com.tanishisherewith.dynamichud;
+import com.tanishisherewith.dynamichud.newTrial.config.GlobalConfig;
import com.terraformersmc.modmenu.api.ConfigScreenFactory;
import com.terraformersmc.modmenu.api.ModMenuApi;
+import net.minecraft.client.gui.screen.Screen;
+
public class ModMenuIntegration implements ModMenuApi {
+ public static Screen YACL_CONFIG_SCREEN = GlobalConfig.get().createYACLGUI();
@Override
public ConfigScreenFactory> getModConfigScreenFactory() {
- return parent -> DynamicHUD.getScreen();
+ return parent -> YACL_CONFIG_SCREEN;
}
}
diff --git a/src/main/java/com/tanishisherewith/dynamichud/helpers/DrawHelper.java b/src/main/java/com/tanishisherewith/dynamichud/helpers/DrawHelper.java
index 75ac161..b87dd3e 100644
--- a/src/main/java/com/tanishisherewith/dynamichud/helpers/DrawHelper.java
+++ b/src/main/java/com/tanishisherewith/dynamichud/helpers/DrawHelper.java
@@ -211,20 +211,20 @@ public static void drawOutlinedBox(DrawContext drawContext, int x1, int y1, int
/**
* This method assumes that the element is widget in the top-left corner (i.e. all drawing happens with respect to the top-left corner).
- * @param matrices
* @param x X position of top-left corner of widget
* @param y Y position of top-left corner of widget
* @param scale Scale the matrices
*/
- public static void scaleAndPosition(MatrixStack matrices, float x, float y,float z, float scale) {
+ public static void scaleAndPosition(MatrixStack matrices, float x, float y, float scale) {
matrices.push(); // Save the current transformation state
- // Translate to the desired position
- matrices.translate(x, y, z);
+ // Translate the origin back to the desired position
+ matrices.translate(x, y, 0);
// Scale the matrix
matrices.scale(scale, scale, 1.0F);
}
+
public static void stopScaling(MatrixStack matrices){
matrices.pop(); // Restore the previous transformation state
}
diff --git a/src/main/java/com/tanishisherewith/dynamichud/mixins/OptionsScreenMixin.java b/src/main/java/com/tanishisherewith/dynamichud/mixins/OptionsScreenMixin.java
index 345e818..a3f274a 100644
--- a/src/main/java/com/tanishisherewith/dynamichud/mixins/OptionsScreenMixin.java
+++ b/src/main/java/com/tanishisherewith/dynamichud/mixins/OptionsScreenMixin.java
@@ -20,8 +20,6 @@
@Mixin(OptionsScreen.class)
public class OptionsScreenMixin extends Screen {
private static final DecimalFormat DECIMAL_FORMAT = new DecimalFormat("#.##");
- private final GameOptions settings = MinecraftClient.getInstance().options;
-
protected OptionsScreenMixin(Text title) {
super(title);
diff --git a/src/main/java/com/tanishisherewith/dynamichud/mixins/ScreenMixin.java b/src/main/java/com/tanishisherewith/dynamichud/mixins/ScreenMixin.java
index 852887d..d1e4259 100644
--- a/src/main/java/com/tanishisherewith/dynamichud/mixins/ScreenMixin.java
+++ b/src/main/java/com/tanishisherewith/dynamichud/mixins/ScreenMixin.java
@@ -3,20 +3,37 @@
import com.tanishisherewith.dynamichud.DynamicHudIntegration;
import com.tanishisherewith.dynamichud.newTrial.DynamicHUD;
import com.tanishisherewith.dynamichud.newTrial.DynamicHudTest;
+import com.tanishisherewith.dynamichud.newTrial.widget.WidgetManager;
import com.tanishisherewith.dynamichud.newTrial.widget.WidgetRenderer;
+import net.minecraft.client.MinecraftClient;
import net.minecraft.client.gui.DrawContext;
import net.minecraft.client.gui.screen.Screen;
import org.spongepowered.asm.mixin.Mixin;
+import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(Screen.class)
public abstract class ScreenMixin {
+ @Shadow public int width;
+
+ @Shadow public int height;
+
@Inject(at = @At("TAIL"), method = "render")
private void render(DrawContext context, int mouseX, int mouseY, float delta, CallbackInfo ci) {
for(WidgetRenderer widgetRenderer: DynamicHUD.getWidgetRenderers()){
widgetRenderer.renderWidgets(context);
}
}
+ @Inject(at = @At("HEAD"), method = "resize")
+ private void onScreenResize(MinecraftClient client,int width, int height, CallbackInfo ci) {
+ WidgetManager.onScreenResized(width,height,this.width,this.height);
+ }
+ @Inject(at = @At("TAIL"), method = "close")
+ private void render(CallbackInfo ci) {
+ for(WidgetRenderer widgetRenderer: DynamicHUD.getWidgetRenderers()){
+ widgetRenderer.onCloseScreen();
+ }
+ }
}
diff --git a/src/main/java/com/tanishisherewith/dynamichud/newTrial/DynamicHUD.java b/src/main/java/com/tanishisherewith/dynamichud/newTrial/DynamicHUD.java
index f2dda44..b73464d 100644
--- a/src/main/java/com/tanishisherewith/dynamichud/newTrial/DynamicHUD.java
+++ b/src/main/java/com/tanishisherewith/dynamichud/newTrial/DynamicHUD.java
@@ -2,8 +2,10 @@
import com.tanishisherewith.dynamichud.DynamicHudIntegration;
import com.tanishisherewith.dynamichud.huds.AbstractMoveableScreen;
+import com.tanishisherewith.dynamichud.newTrial.config.GlobalConfig;
import com.tanishisherewith.dynamichud.newTrial.widget.WidgetManager;
import com.tanishisherewith.dynamichud.newTrial.widget.WidgetRenderer;
+import com.tanishisherewith.dynamichud.newTrial.widgets.TextWidget;
import com.tanishisherewith.dynamichud.util.DynamicUtil;
import net.fabricmc.api.ClientModInitializer;
import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents;
@@ -12,6 +14,7 @@
import net.fabricmc.loader.api.FabricLoader;
import net.fabricmc.loader.api.metadata.ModMetadata;
import net.minecraft.client.MinecraftClient;
+import net.minecraft.client.gui.screen.Screen;
import net.minecraft.client.option.KeyBinding;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -24,6 +27,7 @@
public class DynamicHUD implements ClientModInitializer {
public static MinecraftClient MC = MinecraftClient.getInstance();
+ public static String MOD_ID = "dynamichud";
private static final Logger logger = LoggerFactory.getLogger("DynamicHud");
private static final List widgetRenderers = new ArrayList<>();
@@ -45,31 +49,16 @@ public static void printWarn(String msg) {
@Override
public void onInitializeClient() {
- printInfo("DynamicHud Initialised");
-
- //Save and Load
- /* ClientTickEvents.START_CLIENT_TICK.register(server -> {
- if (iWigdets != null) {
- if (!WIDGETS_FILE.exists()) {
- if (!dynamicutil.WidgetAdded) {
- iWigdets.addWigdets(dynamicutil);
- }
- if (!dynamicutil.MainMenuWidgetAdded) {
- iWigdets.addMainMenuWigdets(dynamicutil);
- }
- }
+ printInfo("Initialising DynamicHud");
- if (WIDGETS_FILE.exists() && !dynamicutil.WidgetLoaded) {
- iWigdets.loadWigdets(dynamicutil);
- printInfo("Widgets loaded");
- File FileDirectory = new File(fileDirectory, filename);
- printInfo("Load file Directory: " + FileDirectory);
- }
- }
- DynamicUtil.openDynamicScreen(EditorScreenKeyBinding, Screen);
- });
+ // Add WidgetData of included widgets
+ WidgetManager.addWidgetDatas(
+ TextWidget.DATA
+ );
+ //YACL load
+ GlobalConfig.HANDLER.load();
- */
+ printInfo("Integrating mods...");
FabricLoader.getInstance().getEntrypointContainers("dynamicHud", DynamicHudIntegration.class).forEach(entrypoint -> {
ModMetadata metadata = entrypoint.getProvider().getMetadata();
String modId = metadata.getId();
@@ -88,11 +77,14 @@ public void onInitializeClient() {
screen = DHIntegration.getMovableScreen();
KeyBinding binding =DHIntegration.EDITOR_SCREEN_KEY_BINDING;
+
WidgetRenderer widgetRenderer = DHIntegration.getWidgetRenderer();
addWidgetRenderer(widgetRenderer);
//Register events for rendering, saving, loading, and opening the hudEditor
- ClientTickEvents.START_CLIENT_TICK.register((client)->{
+ ClientTickEvents.START_CLIENT_TICK.register((client)-> {
+ System.out.println("TICK FOR : " + modId);
+ System.out.println(binding);
DynamicUtil.openDynamicScreen(binding, screen);
});
@@ -101,6 +93,9 @@ public void onInitializeClient() {
ServerLifecycleEvents.END_DATA_PACK_RELOAD.register((server, resourceManager, s) -> saveWidgetsSafely(widgetsFile));
ServerPlayConnectionEvents.DISCONNECT.register((handler, packetSender) -> saveWidgetsSafely(widgetsFile));
Runtime.getRuntime().addShutdownHook(new Thread(() -> saveWidgetsSafely(widgetsFile)));
+
+
+ printInfo(String.format("Integration of mod %s was successful",modId));
} catch (Throwable e) {
if(e instanceof IOException){
logger.warn("An error has occured while loading widgets of mod {}", modId,e);
@@ -109,6 +104,11 @@ public void onInitializeClient() {
}
}
});
+
+ ServerLifecycleEvents.SERVER_STOPPING.register(server -> GlobalConfig.HANDLER.save());
+ ServerLifecycleEvents.END_DATA_PACK_RELOAD.register((server, resourceManager, s) -> GlobalConfig.HANDLER.save());
+ ServerPlayConnectionEvents.DISCONNECT.register((handler, packetSender) -> GlobalConfig.HANDLER.save());
+ Runtime.getRuntime().addShutdownHook(new Thread(() -> GlobalConfig.HANDLER.save()));
}
private void saveWidgetsSafely(File widgetsFile) {
try {
diff --git a/src/main/java/com/tanishisherewith/dynamichud/newTrial/DynamicHudTest.java b/src/main/java/com/tanishisherewith/dynamichud/newTrial/DynamicHudTest.java
index 78177ee..efad069 100644
--- a/src/main/java/com/tanishisherewith/dynamichud/newTrial/DynamicHudTest.java
+++ b/src/main/java/com/tanishisherewith/dynamichud/newTrial/DynamicHudTest.java
@@ -4,6 +4,7 @@
import com.tanishisherewith.dynamichud.huds.AbstractMoveableScreen;
import com.tanishisherewith.dynamichud.huds.MoveableScreen;
import com.tanishisherewith.dynamichud.newTrial.utils.DynamicValueRegistry;
+import com.tanishisherewith.dynamichud.newTrial.widget.Widget;
import com.tanishisherewith.dynamichud.newTrial.widget.WidgetManager;
import com.tanishisherewith.dynamichud.newTrial.widget.WidgetRenderer;
import com.tanishisherewith.dynamichud.newTrial.widgets.TextWidget;
@@ -16,41 +17,45 @@
import java.lang.reflect.Array;
import java.util.Arrays;
import java.util.Collections;
+import java.util.List;
+import java.util.stream.Collectors;
public class DynamicHudTest implements DynamicHudIntegration {
TextWidget textWidget;
-
- DynamicValueRegistry registry = new DynamicValueRegistry();
+ DynamicValueRegistry registry = new DynamicValueRegistry(DynamicHUD.MOD_ID);
WidgetRenderer renderer;
@Override
public void init() {
- DynamicValueRegistry.registerGlobal("FPS",() -> String.valueOf(DynamicHUD.MC.getCurrentFps()));
- registry.registerLocal("FPS",()->String.valueOf(DynamicHUD.MC.getCurrentFps()));
- }
+ DynamicValueRegistry.registerGlobal("FPS",() -> "FPS: "+ DynamicHUD.MC.getCurrentFps());
+ registry.registerLocal("FPS",()-> "FPS: "+ DynamicHUD.MC.getCurrentFps());
- @Override
- public void addWidgets() {
textWidget = new TextWidget.Builder()
- .setX(0.6f)
- .setY(0.6f)
+ .setX(300)
+ .setY(60)
.setDraggable(true)
- .shouldScale(false)
.rainbow(false)
- .setDHKey("FPS")
+ .setDRKey("FPS")
+ .setModID(DynamicHUD.MOD_ID)
+ .shouldScale(false)
.build();
+ }
-
+ @Override
+ public void addWidgets() {
WidgetManager.addWidget(textWidget);
}
@Override
public AbstractMoveableScreen getMovableScreen() {
- return new MoveableScreen(Text.of("Editor LOL"),new DynamicUtil(DynamicHUD.MC));
+ return new MoveableScreen(Text.of("Editor"),new DynamicUtil(DynamicHUD.MC));
}
@Override
public WidgetRenderer getWidgetRenderer() {
- renderer = new WidgetRenderer(Collections.singletonList(textWidget));
+ // Get the widgets for this mod
+ List widgets = WidgetManager.getWidgetsForMod(DynamicHUD.MOD_ID);
+
+ renderer = new WidgetRenderer(widgets);
renderer.shouldRenderInGameHud(true);
renderer.addScreen(TitleScreen.class);
renderer.addScreen(MultiplayerScreen.class);
diff --git a/src/main/java/com/tanishisherewith/dynamichud/newTrial/config/GlobalConfig.java b/src/main/java/com/tanishisherewith/dynamichud/newTrial/config/GlobalConfig.java
new file mode 100644
index 0000000..22ae290
--- /dev/null
+++ b/src/main/java/com/tanishisherewith/dynamichud/newTrial/config/GlobalConfig.java
@@ -0,0 +1,51 @@
+package com.tanishisherewith.dynamichud.newTrial.config;
+
+import dev.isxander.yacl3.api.*;
+import dev.isxander.yacl3.api.controller.FloatSliderControllerBuilder;
+import dev.isxander.yacl3.api.controller.TickBoxControllerBuilder;
+import dev.isxander.yacl3.config.v2.api.ConfigClassHandler;
+import dev.isxander.yacl3.config.v2.api.SerialEntry;
+import dev.isxander.yacl3.config.v2.api.serializer.GsonConfigSerializerBuilder;
+import dev.isxander.yacl3.gui.controllers.slider.FloatSliderController;
+import net.fabricmc.loader.api.FabricLoader;
+import net.minecraft.client.gui.screen.Screen;
+import net.minecraft.text.Text;
+import net.minecraft.util.Identifier;
+
+public class GlobalConfig {
+ private static GlobalConfig INSTANCE = new GlobalConfig();
+ public static ConfigClassHandler HANDLER = ConfigClassHandler.createBuilder(GlobalConfig.class)
+ .id(new Identifier("dynamichud", "dynamichud_config"))
+ .serializer(config -> GsonConfigSerializerBuilder.create(config)
+ .setPath(FabricLoader.getInstance().getConfigDir().resolve("dynamichud.json5"))
+ .setJson5(true)
+ .build())
+ .build();
+
+ @SerialEntry
+ public float scale = 1.0f;
+
+ public Screen createYACLGUI() {
+ return YetAnotherConfigLib.createBuilder()
+ .title(Text.literal("DynamicHUD config screen."))
+ .category(ConfigCategory.createBuilder()
+ .name(Text.literal("General"))
+ .tooltip(Text.literal("Set the general settings for all widgets."))
+ .group(OptionGroup.createBuilder()
+ .name(Text.literal("Global"))
+ .description(OptionDescription.of(Text.literal("Global settings for all widgets.")))
+ .option(Option.createBuilder()
+ .name(Text.literal("Scale"))
+ .description(OptionDescription.of(Text.literal("Set scale for all widgets.")))
+ .binding(1.0f, () -> this.scale, newVal -> this.scale = newVal)
+ .controller(floatOption -> FloatSliderControllerBuilder.create(floatOption).range(0.1f,2.5f).step(0.1f))
+ .build())
+ .build())
+ .build())
+ .build()
+ .generateScreen(null);
+ }
+ public static GlobalConfig get(){
+ return INSTANCE;
+ }
+}
diff --git a/src/main/java/com/tanishisherewith/dynamichud/newTrial/screens/AbstractMoveableScreen.java b/src/main/java/com/tanishisherewith/dynamichud/newTrial/screens/AbstractMoveableScreen.java
new file mode 100644
index 0000000..4908b11
--- /dev/null
+++ b/src/main/java/com/tanishisherewith/dynamichud/newTrial/screens/AbstractMoveableScreen.java
@@ -0,0 +1,79 @@
+package com.tanishisherewith.dynamichud.newTrial.screens;
+
+import com.tanishisherewith.dynamichud.newTrial.widget.WidgetRenderer;
+import com.tanishisherewith.dynamichud.widget.Widget;
+import net.minecraft.client.MinecraftClient;
+import net.minecraft.client.gui.DrawContext;
+import net.minecraft.client.gui.screen.Screen;
+import net.minecraft.text.Text;
+public abstract class AbstractMoveableScreen extends Screen {
+ protected boolean ShouldPause = false; // To pause if the screen is opened or not
+ public final WidgetRenderer widgetRenderer;
+
+
+ /**
+ * Constructs a AbstractMoveableScreen object.
+ *
+ */
+ public AbstractMoveableScreen(Text title, WidgetRenderer renderer) {
+ super(title);
+ this.widgetRenderer = renderer;
+ }
+
+ @Override
+ public boolean mouseDragged(double mouseX, double mouseY, int button, double deltaX, double deltaY) {
+ widgetRenderer.mouseDragged(mouseX,mouseY,button);
+ return false;
+ }
+
+ @Override
+ public boolean mouseClicked(double mouseX, double mouseY, int button) {
+ widgetRenderer.mouseClicked(mouseX,mouseY,button);
+ return false;
+ }
+
+ @Override
+ public boolean mouseReleased(double mouseX, double mouseY, int button) {
+ widgetRenderer.mouseReleased(mouseX,mouseY,button);
+ return false;
+ }
+
+ /**
+ * Renders this screen and its widgets on the screen.
+ *
+ * @param drawContext The matrix stack used for rendering
+ * @param mouseX The current x position of the mouse cursor
+ * @param mouseY The current y position of the mouse cursor
+ * @param delta The time elapsed since the last frame in seconds
+ */
+ @Override
+ public void render(DrawContext drawContext, int mouseX, int mouseY, float delta) {
+ super.render(drawContext, mouseX, mouseY, delta);
+
+ // Draw each widget
+ widgetRenderer.isInEditor = true;
+ widgetRenderer.renderWidgets(drawContext);
+ }
+
+ @Override
+ public void close() {
+ super.close();
+ widgetRenderer.isInEditor = false;
+ }
+
+ public void setShouldPause(boolean shouldPause) {
+ this.ShouldPause = shouldPause;
+ }
+
+
+ @Override
+ public void resize(MinecraftClient client, int width, int height) {
+ super.resize(client, width, height);
+ }
+
+ @Override
+ public boolean shouldPause() {
+ return ShouldPause;
+ }
+}
+
diff --git a/src/main/java/com/tanishisherewith/dynamichud/newTrial/utils/DynamicValueRegistry.java b/src/main/java/com/tanishisherewith/dynamichud/newTrial/utils/DynamicValueRegistry.java
index 2927a35..43d62da 100644
--- a/src/main/java/com/tanishisherewith/dynamichud/newTrial/utils/DynamicValueRegistry.java
+++ b/src/main/java/com/tanishisherewith/dynamichud/newTrial/utils/DynamicValueRegistry.java
@@ -3,10 +3,13 @@
import java.util.*;
import java.util.function.Supplier;
-public class DynamicValueRegistry {
+public class DynamicValueRegistry extends System {
private static final Map> globalRegistry = new HashMap<>();
private final Map> localRegistry = new HashMap<>();
-
+ public DynamicValueRegistry(String modId) {
+ super(modId);
+ instances.computeIfAbsent(modId, k -> new ArrayList<>()).add(this);
+ }
public static void registerGlobal(String key, Supplier> supplier) {
globalRegistry.put(key, supplier);
}
@@ -29,4 +32,8 @@ public Supplier> get(String key) {
return supplier;
}
+ public void setLocalRegistry(Map> map){
+ localRegistry.putAll(map);
+ }
+
}
diff --git a/src/main/java/com/tanishisherewith/dynamichud/newTrial/utils/System.java b/src/main/java/com/tanishisherewith/dynamichud/newTrial/utils/System.java
new file mode 100644
index 0000000..8022537
--- /dev/null
+++ b/src/main/java/com/tanishisherewith/dynamichud/newTrial/utils/System.java
@@ -0,0 +1,24 @@
+package com.tanishisherewith.dynamichud.newTrial.utils;
+
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+public abstract class System {
+ protected final String modId;
+
+ // A map to store all instances of DynamicValueRegistry by modId
+ protected static final Map> instances = new ConcurrentHashMap<>();
+
+ public System(String modId) {
+ this.modId = modId;
+ }
+
+ public String getModId() {
+ return modId;
+ }
+
+ public static List getInstances(String modId) {
+ return instances.get(modId);
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/tanishisherewith/dynamichud/newTrial/widget/Widget.java b/src/main/java/com/tanishisherewith/dynamichud/newTrial/widget/Widget.java
index ce5950d..4f1ea10 100644
--- a/src/main/java/com/tanishisherewith/dynamichud/newTrial/widget/Widget.java
+++ b/src/main/java/com/tanishisherewith/dynamichud/newTrial/widget/Widget.java
@@ -1,13 +1,14 @@
package com.tanishisherewith.dynamichud.newTrial.widget;
-import com.tanishisherewith.dynamichud.DynamicHUD;
import com.tanishisherewith.dynamichud.helpers.ColorHelper;
import com.tanishisherewith.dynamichud.helpers.DrawHelper;
+import com.tanishisherewith.dynamichud.newTrial.config.GlobalConfig;
import com.tanishisherewith.dynamichud.newTrial.utils.UID;
import com.tanishisherewith.dynamichud.widget.WidgetBox;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.gui.DrawContext;
import net.minecraft.nbt.NbtCompound;
+import org.lwjgl.glfw.GLFW;
import java.util.Set;
@@ -17,20 +18,44 @@ public abstract class Widget {
//This is the UID of the widget used to identify during loading and saving
public UID uid = UID.generate();
- public boolean display = true; // Whether the widget is enabled
+ public boolean isInEditor = false;
+ // Whether the widget is enabled and should be displayed.
+ public boolean display = true;
public boolean isDraggable = true;
- protected float xPercent; // The x position of the widget as a percentage of the screen width
- protected float yPercent; // The y position of the widget as a percentage of the screen height
- public boolean shouldScale = false;
+
+ //Boolean to check if the widget is being dragged
+ public boolean dragging;
+
+ //To enable/disable snapping
+ public boolean shiftDown = false;
+
+ // Used for dragging and snapping
+ int startX, startY, snapSize = 120;
+
+ // Absolute position of the widget on screen in pixels.
+ public int x,y;
+
+ // The x position of the widget as a percentage of the screen width, i.e. the relative x position of the widget for resizing and scaling
+ protected float xPercent;
+ // The y position of the widget as a percentage of the screen height, i.e. the relative y position of the widget for resizing and scaling
+ protected float yPercent;
+ public boolean shouldScale = true;
+ public String modId = "unknown";
+
+ //Dimensions of the widget
protected WidgetBox widgetBox;
public static WidgetData> DATA;
- public Widget(WidgetData> DATA){
+ public Widget(WidgetData> DATA, String modId){
Widget.DATA = DATA;
- widgetBox = new WidgetBox(0,0,0,0,0);
+ widgetBox = new WidgetBox(0,0,0,0,1);
+ this.modId = modId;
init();
}
+ /**
+ * This method is called at the end of the {@link Widget#Widget(WidgetData)} constructor.
+ */
public void init(){
}
@@ -39,8 +64,8 @@ public void init(){
*
* @return The x position of the widget in pixels
*/
- public float getX() {
- return DynamicHUD.MC.getWindow().getScaledWidth() * xPercent;
+ public int getX() {
+ return x;
}
/**
@@ -48,8 +73,20 @@ public float getX() {
*
* @return The y position of the widget in pixels
*/
- public float getY() {
- return DynamicHUD.MC.getWindow().getScaledHeight() * yPercent;
+ public int getY() {
+ return y;
+ }
+
+ public float getWidth(){
+ return widgetBox.getWidth();
+ }
+ public float getHeight(){
+ return widgetBox.getHeight();
+ }
+
+ public void setPosition(int x, int y){
+ this.x = x;
+ this.y = y;
}
public void setDraggable(boolean draggable) {
@@ -74,13 +111,16 @@ public boolean isOverlapping(Widget other) {
* Renders the widget on the screen.
*/
public void render(DrawContext drawContext){
+ if(!shouldDisplay())return;
+
if(shouldScale) {
- DrawHelper.scaleAndPosition(drawContext.getMatrices(), getX(), getY(),0, 1.0f);
+ DrawHelper.scaleAndPosition(drawContext.getMatrices(), getX(), getY(), GlobalConfig.get().scale);
+ scale(GlobalConfig.get().scale);
}
-
renderWidget(drawContext);
if(shouldScale){
+ reverseScale(GlobalConfig.get().scale);
DrawHelper.stopScaling(drawContext.getMatrices());
}
}
@@ -89,12 +129,14 @@ public void render(DrawContext drawContext){
*/
public void renderInEditor(DrawContext drawContext){
if(shouldScale) {
- DrawHelper.scaleAndPosition(drawContext.getMatrices(), getX(), getY(),0, 1.0f);
+ DrawHelper.scaleAndPosition(drawContext.getMatrices(), getX(), getY(), GlobalConfig.get().scale);
+ scale(GlobalConfig.get().scale);
}
renderWidgetInEditor(drawContext);
if(shouldScale){
+ reverseScale(GlobalConfig.get().scale);
DrawHelper.stopScaling(drawContext.getMatrices());
}
}
@@ -115,12 +157,51 @@ public void renderInEditor(DrawContext drawContext){
*
* @param context
*/
- public void renderWidgetInEditor(DrawContext context){
+ private void renderWidgetInEditor(DrawContext context){
displayBg(context);
renderWidget(context);
}
+ public void mouseClicked(double mouseX, double mouseY, int button){
+ if(button == GLFW.GLFW_MOUSE_BUTTON_LEFT){
+ toggle();
+ }
+ }
+ public void mouseDragged(double mouseX, double mouseY, int button){
+ if(this.isDraggable && button == GLFW.GLFW_MOUSE_BUTTON_LEFT){
+ //Start dragging
+ startX = (int) (mouseX - x);
+ startY = (int) (mouseY - y);
+ dragging = true;
+ this.x = startX;
+ this.y = startY;
+
+ // Divides the screen into several "grid boxes" which the elements snap to. Higher the snapSize, more the grid boxes
+ if (this.shiftDown) {
+ // Calculate the size of each snap box
+ int snapBoxWidth = (int) (widgetBox.getWidth() / this.snapSize);
+ int snapBoxHeight = (int) (widgetBox.getHeight() / this.snapSize);
+
+ // Calculate the index of the snap box that the mouse is currently in
+ int snapBoxX = (int) (mouseX / snapBoxWidth);
+ int snapBoxY = (int) (mouseY / snapBoxHeight);
+
+ // Snap the element to the top-left corner of the snap box
+ this.x = snapBoxX * snapBoxWidth;
+ this.y = snapBoxY * snapBoxHeight;
+ }
+ }
+ }
+ public void mouseReleased(double mouseX, double mouseY, int button) {
+ dragging = false;
+ }
+
+
+ public boolean toggle(){
+ return this.display = !this.display;
+ }
+
/**
* Displays a faint grayish background if enabled or faint reddish background if disabled.
* Drawn with 2 pixel offset to all sides
@@ -135,11 +216,13 @@ protected void displayBg(DrawContext context){
public void readFromTag(NbtCompound tag) {
+ modId = tag.getString("modId");
uid = new UID(tag.getString("UID"));
- xPercent = tag.getFloat("xPercent");
- yPercent = tag.getFloat("yPercent");
+ x = tag.getInt("x");
+ y = tag.getInt("y");
display = tag.getBoolean("Display");
isDraggable = tag.getBoolean("isDraggable");
+ shouldScale = tag.getBoolean("shouldScale");
}
/**
@@ -148,10 +231,13 @@ public void readFromTag(NbtCompound tag) {
* @param tag The tag to write to
*/
public void writeToTag(NbtCompound tag) {
+ tag.putString("name", DATA.name());
+ tag.putString("modId",modId);
tag.putString("UID", uid.getUniqueID());
tag.putBoolean("isDraggable", isDraggable);
- tag.putFloat("xPercent", xPercent);
- tag.putFloat("yPercent", yPercent);
+ tag.putBoolean("shouldScale", shouldScale);
+ tag.putInt("x", x);
+ tag.putInt("y", y);
tag.putBoolean("Display", display);
}
@@ -179,6 +265,18 @@ public void setShouldScale(boolean shouldScale) {
this.shouldScale = shouldScale;
}
+ public String getModId() {
+ return modId;
+ }
+ public void scale(float scale) {
+ this.xPercent *= scale;
+ this.yPercent *= scale;
+ }
+ public void reverseScale(float scale) {
+ this.xPercent /= scale;
+ this.yPercent /= scale;
+ }
+
@Override
public String toString() {
return "Widget{" +
@@ -188,23 +286,24 @@ public String toString() {
", display=" + display +
'}';
}
-
public abstract static class WidgetBuilder {
- protected float xPercent;
- protected float yPercent;
+ protected int x;
+ protected int y;
protected boolean display = true;
protected boolean isDraggable = true;
protected boolean shouldScale = true;
+ protected String modID = "unknown";
+
/**
* X Position of the widget relative to the screen.
* Should be between 0f - 1f
*
- * @param xPercent
- * @return
+ * @param x
+ * @return Builder
*/
- public T setX(float xPercent) {
- this.xPercent = xPercent;
+ public T setX(int x) {
+ this.x = x;
return self();
}
@@ -212,11 +311,11 @@ public T setX(float xPercent) {
* Y Position of the widget relative to the screen.
* Should be between 0f - 1f
*
- * @param yPercent
+ * @param y
* @return
*/
- public T setY(float yPercent) {
- this.yPercent = yPercent;
+ public T setY(int y) {
+ this.y = y;
return self();
}
@@ -234,11 +333,19 @@ public T shouldScale(boolean shouldScale) {
this.shouldScale = shouldScale;
return self();
}
+ public T setModID(String modID) {
+ this.modID = modID;
+ return self();
+ }
- // Method to be overridden in subclasses to return "this" correctly
+ /**
+ * Method to be overridden in subclasses to return "this" correctly
+ */
protected abstract T self();
- // Method to construct a Widget object
+ /**
+ * Method to construct a Widget object
+ */
public abstract S build();
}
}
diff --git a/src/main/java/com/tanishisherewith/dynamichud/newTrial/widget/WidgetManager.java b/src/main/java/com/tanishisherewith/dynamichud/newTrial/widget/WidgetManager.java
index 14f8dfc..02f384c 100644
--- a/src/main/java/com/tanishisherewith/dynamichud/newTrial/widget/WidgetManager.java
+++ b/src/main/java/com/tanishisherewith/dynamichud/newTrial/widget/WidgetManager.java
@@ -5,11 +5,12 @@
import java.nio.file.StandardCopyOption;
import java.util.*;
+import com.tanishisherewith.dynamichud.DynamicHUD;
import net.fabricmc.fabric.api.util.NbtType;
import net.minecraft.nbt.*;
+import net.minecraft.util.math.MathHelper;
-import static com.tanishisherewith.dynamichud.DynamicHUD.printInfo;
-import static com.tanishisherewith.dynamichud.DynamicHUD.printWarn;
+import static com.tanishisherewith.dynamichud.DynamicHUD.*;
/**
* Manages a collection of widgets, providing methods to add, remove, save, and load widgets.
@@ -71,6 +72,77 @@ public static void addWidgets(Widget... widget) {
public static void removeWidget(Widget widget) {
widgets.remove(widget);
}
+ /* public static void onScreenResized(int newWidth, int newHeight) {
+ float scaleX = (float) newWidth / MC.getWindow().getScaledWidth();
+ float scaleY = (float) newHeight / MC.getWindow().getScaledHeight();
+ System.out.println("newWidth: " + newWidth);
+ System.out.println("newHeight: " + newHeight);
+
+ System.out.println("scaleX: " + scaleX);
+
+ for (Widget widget : widgets) {
+ float newX = widget.getX() * scaleX;
+ float newY = widget.getY() * scaleY;
+ System.out.println("newX: " + newX);
+
+ // Ensure the widget is within the screen bounds
+ newX = MathHelper.clamp(newX,0,newWidth - widget.getWidth());
+ newY = MathHelper.clamp(newY,0,newHeight - widget.getHeight());
+ System.out.println("newX2: " + newX);
+
+ widget.setPosition(newX, newY);
+ }
+ }
+
+ */
+
+ /**
+ * Attempts to restore the widgets back to their place on screen resize.
+ *
+ * It works by storing the position of widgets as relative to the screen size before the resize
+ * and then using that percentage to restore the widget to their appropriate place.
+ *
+ * Widgets will move around on smaller GUI scales.
+ * Larger the GUI scale, more accurate is the position.
+ *
+ *
+ * Called in {@link com.tanishisherewith.dynamichud.mixins.ScreenMixin}
+ *
+ *
+ *
+ * @param newWidth Screen width after resize
+ * @param newHeight Screen height after resize
+ * @param previousWidth Screen width before resize
+ * @param previousHeight Screen height before resize
+ */
+ public static void onScreenResized(int newWidth, int newHeight, int previousWidth, int previousHeight) {
+ for (Widget widget : widgets) {
+
+ // To ensure that infinite coords is not returned
+ if(widget.xPercent <= 0f){
+ widget.xPercent = (float) widget.getX()/previousWidth;
+ }
+ if(widget.yPercent <= 0f){
+ widget.yPercent = (float) widget.getY()/previousHeight;
+ }
+
+ // Use the stored percentages to calculate the new position
+ int newX = (int) (widget.xPercent * newWidth);
+ int newY = (int) (widget.yPercent * newHeight);
+
+ // Ensure the widget is within the screen bounds
+ newX = (int) MathHelper.clamp(newX, 0, newWidth - widget.getWidth());
+ newY = (int) MathHelper.clamp(newY, 0, newHeight - widget.getHeight());
+
+ // Update the widget's position
+ widget.setPosition(newX, newY);
+
+ // Update the stored percentages with the new screen size (after resize).
+ widget.xPercent = (float) widget.getX() / newWidth;
+ widget.yPercent = (float) widget.getY() / newHeight;
+ }
+ }
+
/**
* Saves the state of all widgets to the given file.
@@ -100,17 +172,11 @@ public static void saveWidgets(File file) throws IOException {
rootTag.put("widgets", widgetList);
- // Use a temporary file to write the data
- File tempFile = new File(file.getAbsolutePath() + ".tmp");
- try (DataOutputStream out = new DataOutputStream(new FileOutputStream(tempFile))) {
+ //Write the data to the file
+ try (DataOutputStream out = new DataOutputStream(new FileOutputStream(file))) {
NbtIo.write(rootTag, out);
- // Check if the data has been written successfully
- if (tempFile.length() > 0) {
- // Check if the temporary file exists and can be renamed
- Files.move(tempFile.toPath(), file.toPath(), StandardCopyOption.REPLACE_EXISTING);
- } else {
- throw new IOException("Failed to write data to temporary file OR Empty data passed");
- }
+ }catch (IOException e){
+ DynamicHUD.logger.warn("Error while saving",e);
}
}
@@ -146,4 +212,14 @@ public static void loadWidgets(File file) throws IOException {
public static List getWidgets() {
return widgets;
}
+ /**
+ * Returns the list of managed widgets with the same modID.
+ *
+ * @return The list of managed widgets with the same modID.
+ */
+ public static List getWidgetsForMod(String modID) {
+ return getWidgets().stream()
+ .filter(widget -> modID.equalsIgnoreCase(widget.getModId()))
+ .toList();
+ }
}
diff --git a/src/main/java/com/tanishisherewith/dynamichud/newTrial/widget/WidgetRenderer.java b/src/main/java/com/tanishisherewith/dynamichud/newTrial/widget/WidgetRenderer.java
index 6580509..fffdd2d 100644
--- a/src/main/java/com/tanishisherewith/dynamichud/newTrial/widget/WidgetRenderer.java
+++ b/src/main/java/com/tanishisherewith/dynamichud/newTrial/widget/WidgetRenderer.java
@@ -1,8 +1,13 @@
package com.tanishisherewith.dynamichud.newTrial.widget;
import com.tanishisherewith.dynamichud.DynamicHUD;
+import com.tanishisherewith.dynamichud.helpers.ColorHelper;
+import com.tanishisherewith.dynamichud.helpers.DrawHelper;
+import com.tanishisherewith.dynamichud.huds.AbstractMoveableScreen;
+import com.tanishisherewith.dynamichud.widget.WidgetBox;
import net.minecraft.client.gui.DrawContext;
import net.minecraft.client.gui.screen.Screen;
+import org.lwjgl.glfw.GLFW;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
@@ -10,10 +15,14 @@
public class WidgetRenderer {
public final List> allowedScreens = new CopyOnWriteArrayList<>();
private boolean renderInGameHud = true;
+ public boolean isInEditor = false;
List widgets;
public WidgetRenderer(List widgets){
this.widgets = widgets;
}
+ public void addWidget(Widget widget){
+ widgets.add(widget);
+ }
public void addScreen(Class extends Screen> screen){
allowedScreens.add(screen);
@@ -27,17 +36,88 @@ public void renderWidgets(DrawContext context) {
if(WidgetManager.getWidgets().isEmpty()) return;
Screen currentScreen = DynamicHUD.MC.currentScreen;
+
if(currentScreen == null && renderInGameHud){
for (Widget widget : widgets) {
+ widget.isInEditor = false;
widget.render(context);
}
return;
}
- if (currentScreen!= null && allowedScreens.contains(DynamicHUD.MC.currentScreen.getClass())) {
+ if(currentScreen instanceof AbstractMoveableScreen){
+ for (Widget widget : widgets) {
+ widget.isInEditor = true;
+ widget.renderInEditor(context);
+ }
+ return;
+ }
+ if (currentScreen != null && allowedScreens.contains(DynamicHUD.MC.currentScreen.getClass()) && !this.isInEditor) {
for (Widget widget : widgets) {
- // System.out.println("Rendering Widget: " + widget);
+ widget.isInEditor = false;
widget.render(context);
}
}
}
+ public void mouseClicked(double mouseX, double mouseY, int button){
+ Screen currentScreen = DynamicHUD.MC.currentScreen;
+ if(currentScreen == null) {
+ return;
+ }
+ if(currentScreen instanceof AbstractMoveableScreen){
+ for (Widget widget : widgets) {
+ widget.mouseClicked(mouseX,mouseY,button);
+ }
+ }
+ }
+ public void mouseDragged(double mouseX, double mouseY, int button){
+ Screen currentScreen = DynamicHUD.MC.currentScreen;
+ if(currentScreen == null) {
+ return;
+ }
+ if(currentScreen instanceof AbstractMoveableScreen){
+ for (Widget widget : widgets) {
+ widget.mouseDragged(mouseX,mouseY,button);
+ }
+ }
+ }
+
+ public void keyPressed(int keyCode){
+ Screen currentScreen = DynamicHUD.MC.currentScreen;
+ if(currentScreen instanceof AbstractMoveableScreen && (keyCode == GLFW.GLFW_KEY_LEFT_SHIFT || keyCode == GLFW.GLFW_KEY_RIGHT_SHIFT)) {
+ for (Widget widget : widgets) {
+ widget.shiftDown = true;
+ }
+ }
+ }
+ public void keyReleased(int keyCode){
+ Screen currentScreen = DynamicHUD.MC.currentScreen;
+ if(currentScreen instanceof AbstractMoveableScreen && (keyCode == GLFW.GLFW_KEY_LEFT_SHIFT || keyCode == GLFW.GLFW_KEY_RIGHT_SHIFT)) {
+ for (Widget widget : widgets) {
+ widget.shiftDown = false;
+ }
+ }
+ }
+ public void onCloseScreen(){
+ if(DynamicHUD.MC.currentScreen instanceof AbstractMoveableScreen){
+ for (Widget widget : widgets) {
+ widget.shiftDown = false;
+ }
+ }
+ }
+
+ public List getWidgets() {
+ return widgets;
+ }
+
+ public void mouseReleased(double mouseX, double mouseY, int button){
+ Screen currentScreen = DynamicHUD.MC.currentScreen;
+ if(currentScreen == null) {
+ return;
+ }
+ if(currentScreen instanceof AbstractMoveableScreen){
+ for (Widget widget : widgets) {
+ widget.mouseReleased(mouseX,mouseY,button);
+ }
+ }
+ }
}
diff --git a/src/main/java/com/tanishisherewith/dynamichud/newTrial/widgets/TextWidget.java b/src/main/java/com/tanishisherewith/dynamichud/newTrial/widgets/TextWidget.java
index 5701b0b..0c816a1 100644
--- a/src/main/java/com/tanishisherewith/dynamichud/newTrial/widgets/TextWidget.java
+++ b/src/main/java/com/tanishisherewith/dynamichud/newTrial/widgets/TextWidget.java
@@ -1,8 +1,7 @@
package com.tanishisherewith.dynamichud.newTrial.widgets;
-import com.tanishisherewith.dynamichud.DynamicHUD;
import com.tanishisherewith.dynamichud.helpers.ColorHelper;
-import com.tanishisherewith.dynamichud.helpers.DrawHelper;
+import com.tanishisherewith.dynamichud.newTrial.config.GlobalConfig;
import com.tanishisherewith.dynamichud.newTrial.utils.DynamicValueRegistry;
import com.tanishisherewith.dynamichud.newTrial.widget.Widget;
import com.tanishisherewith.dynamichud.newTrial.widget.WidgetData;
@@ -16,11 +15,12 @@ public class TextWidget extends Widget {
public static WidgetData DATA = new WidgetData<>("TextWidget","Display Text on screen",TextWidget::new);
Supplier textSupplier;
String dynamicRegistryKey;
+ DynamicValueRegistry dynamicValueRegistry;
protected boolean shadow; // Whether to draw a shadow behind the text
protected boolean rainbow; // Whether to apply a rainbow effect to the text
public TextWidget() {
- this(null,false,false);
+ this(null,false,false,"unknown");
}
/**
@@ -30,8 +30,8 @@ public TextWidget() {
* @param shadow
* @param rainbow
*/
- public TextWidget(String dynamicRegistryKey, boolean shadow, boolean rainbow) {
- super(DATA);
+ public TextWidget(String dynamicRegistryKey, boolean shadow, boolean rainbow,String modID) {
+ super(DATA,modID);
this.dynamicRegistryKey = dynamicRegistryKey;
textSupplier = (Supplier) DynamicValueRegistry.getGlobal(dynamicRegistryKey);
this.shadow = shadow;
@@ -45,9 +45,10 @@ public TextWidget(String dynamicRegistryKey, boolean shadow, boolean rainbow) {
* @param shadow
* @param rainbow
*/
- public TextWidget(DynamicValueRegistry dynamicValueRegistry,String dynamicRegistryKey, boolean shadow, boolean rainbow) {
- super(DATA);
+ public TextWidget(DynamicValueRegistry dynamicValueRegistry,String dynamicRegistryKey, boolean shadow, boolean rainbow,String modID) {
+ super(DATA,modID);
this.dynamicRegistryKey = dynamicRegistryKey;
+ this.dynamicValueRegistry = dynamicValueRegistry;
textSupplier = (Supplier) dynamicValueRegistry.get(dynamicRegistryKey);
this.shadow = shadow;
this.rainbow = rainbow;
@@ -55,15 +56,12 @@ public TextWidget(DynamicValueRegistry dynamicValueRegistry,String dynamicRegist
@Override
public void renderWidget(DrawContext drawContext) {
- drawContext.getMatrices().push();
- drawContext.getMatrices().translate(0, 0, 420);
- int color = rainbow ? ColorHelper.getColorFromHue((System.currentTimeMillis() % 10000) / (1 * 400f)) : Color.WHITE.getRGB();
+ int color = rainbow ? ColorHelper.getColorFromHue((System.currentTimeMillis() % 10000) / 10000f) : Color.WHITE.getRGB();
if(textSupplier != null) {
String text = textSupplier.get();
- drawContext.drawText(mc.textRenderer, text, (int) getX(), (int) getY(), color, shadow);
- widgetBox.setSize(getX(),getY(),mc.textRenderer.getWidth(text),mc.textRenderer.fontHeight);
+ drawContext.drawText(mc.textRenderer, text, (int) getX(), (int)getY(), color, shadow);
+ widgetBox.setSize(getX() - 2,getY() - 2,mc.textRenderer.getWidth(text) + 2,mc.textRenderer.fontHeight + 2);
}
- drawContext.getMatrices().pop();
}
@Override
@@ -80,9 +78,13 @@ public void readFromTag(NbtCompound tag) {
shadow = tag.getBoolean("shadow");
rainbow = tag.getBoolean("rainbow");
dynamicRegistryKey = tag.getString("DRKey");
+
+ for(DynamicValueRegistry dvr: DynamicValueRegistry.getInstances(modId)){
+ textSupplier = (Supplier) dvr.get(dynamicRegistryKey);
+ break;
+ }
}
public static class Builder extends WidgetBuilder {
-
protected boolean shadow = false;
protected boolean rainbow = false;
protected String dynamicRegistryKey = "";
@@ -97,11 +99,11 @@ public Builder rainbow(boolean rainbow) {
this.rainbow = rainbow;
return self();
}
- public Builder setDHKey(String dynamicRegistryKey) {
+ public Builder setDRKey(String dynamicRegistryKey) {
this.dynamicRegistryKey = dynamicRegistryKey;
return self();
}
- public Builder setDH(DynamicValueRegistry dynamicValueRegistry) {
+ public Builder setDVR(DynamicValueRegistry dynamicValueRegistry) {
this.dynamicValueRegistry = dynamicValueRegistry;
return self();
}
@@ -115,12 +117,11 @@ protected Builder self() {
public TextWidget build() {
TextWidget widget;
if(dynamicValueRegistry == null) {
- widget = new TextWidget(dynamicRegistryKey, shadow, rainbow);
+ widget = new TextWidget(dynamicRegistryKey, shadow, rainbow,modID);
}else{
- widget = new TextWidget(dynamicValueRegistry,dynamicRegistryKey, shadow, rainbow);
+ widget = new TextWidget(dynamicValueRegistry,dynamicRegistryKey, shadow, rainbow,modID);
}
- widget.setxPercent(xPercent);
- widget.setyPercent(yPercent);
+ widget.setPosition(x,y);
widget.setDraggable(isDraggable);
widget.setShouldScale(shouldScale);
return widget;
diff --git a/src/main/java/com/tanishisherewith/dynamichud/widget/WidgetBox.java b/src/main/java/com/tanishisherewith/dynamichud/widget/WidgetBox.java
index 6733d3f..fd845fa 100644
--- a/src/main/java/com/tanishisherewith/dynamichud/widget/WidgetBox.java
+++ b/src/main/java/com/tanishisherewith/dynamichud/widget/WidgetBox.java
@@ -6,8 +6,8 @@ public class WidgetBox {
public float x1 = 0, x2 = 0, y1 = 0, y2 = 0;
public WidgetBox(float x1, float y1, float x2, float y2, float scale) {
- this.width = (float) ((x2 - x1) * scale);
- this.height = (float) ((y2 - y1) * scale);
+ this.width = (x2 - x1) * scale;
+ this.height = (y2 - y1) * scale;
this.x1 = x1;
this.x2 = x1 + width;
this.y1 = y1;
diff --git a/src/main/resources/assets/DynamicHUD/DynamicHud-logo.png b/src/main/resources/assets/DynamicHUD/DynamicHud-logo.png
deleted file mode 100644
index e2848ae71f8f028597cd56737872c6b7f3f4cce2..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001
literal 550679
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