diff --git a/src/main/java/de/fullrisk/citrusDev/UI/Module_Coords.java b/src/main/java/de/fullrisk/citrusDev/UI/Module_Coords.java index f23923f..8718bde 100644 --- a/src/main/java/de/fullrisk/citrusDev/UI/Module_Coords.java +++ b/src/main/java/de/fullrisk/citrusDev/UI/Module_Coords.java @@ -56,6 +56,10 @@ public class Module_Coords { coordinates.padding(Insets.of(padding)); } + public static FlowLayout getCoordinates() { + return coordinates; + } + public static void register() { Hud.add(Identifier.fromNamespaceAndPath("citrus-dev", "coords_display"), () -> { if (coordinates == null) { diff --git a/src/main/java/de/fullrisk/citrusDev/UI/Module_FPS.java b/src/main/java/de/fullrisk/citrusDev/UI/Module_FPS.java index f1cc175..8a8bbbf 100644 --- a/src/main/java/de/fullrisk/citrusDev/UI/Module_FPS.java +++ b/src/main/java/de/fullrisk/citrusDev/UI/Module_FPS.java @@ -52,6 +52,10 @@ public class Module_FPS { fpsContainer.padding(Insets.of(padding)); } + public static FlowLayout getFpsContainer() { + return fpsContainer; + } + public static void register() { Hud.add(Identifier.fromNamespaceAndPath("citrus-dev", "fps_display"), () -> { if (fpsContainer == null) { diff --git a/src/main/java/de/fullrisk/citrusDev/UI/Module_KeyStrokes.java b/src/main/java/de/fullrisk/citrusDev/UI/Module_KeyStrokes.java index 5ccc4b3..3e041ac 100644 --- a/src/main/java/de/fullrisk/citrusDev/UI/Module_KeyStrokes.java +++ b/src/main/java/de/fullrisk/citrusDev/UI/Module_KeyStrokes.java @@ -31,6 +31,10 @@ public class Module_KeyStrokes { static boolean toggle = CONFIG.keyEnabled; private static boolean shown = false; + public static FlowLayout getKeyContainer() { + return keyContainer; + } + public static boolean isEnabled() { return toggle; } @@ -61,7 +65,7 @@ public class Module_KeyStrokes { public static void register() { Hud.add(Identifier.fromNamespaceAndPath("citrus-dev", "keystrokes_display"), () -> { if (keyContainer == null) { - keySize = CONFIG.keySize > 0 ? CONFIG.keySize : 16; // load saved size, fallback to default + keySize = CONFIG.keySize > 0 ? CONFIG.keySize : 16; wLabel = new ScalableLabel(Component.literal("W")); aLabel = new ScalableLabel(Component.literal("A")); diff --git a/src/main/java/de/fullrisk/citrusDev/UI/MyScreen.java b/src/main/java/de/fullrisk/citrusDev/UI/MyScreen.java index 66b4c11..d1db45f 100644 --- a/src/main/java/de/fullrisk/citrusDev/UI/MyScreen.java +++ b/src/main/java/de/fullrisk/citrusDev/UI/MyScreen.java @@ -321,7 +321,26 @@ public class MyScreen extends BaseOwoScreen { private void buildSettingsPanel(FlowLayout content) { - content.child(UIComponents.label(Component.literal("Settings"))); + FlowLayout rowsContainer = UIContainers.verticalFlow(Sizing.fixed(CONTENT_WIDTH - 8), Sizing.content()); + rowsContainer.gap(2); + rowsContainer.verticalAlignment(VerticalAlignment.TOP); + rowsContainer.horizontalAlignment(HorizontalAlignment.LEFT); + rowsContainer.padding(Insets.of(0)); + + FlowLayout editButton = SettingsControls.makeTextButton(GLASS_PANEL, "Edit Positions", () -> { + DraggableHud.setEditMode(true); + Minecraft.getInstance().setScreen(null); + }); + editButton.horizontalSizing(Sizing.fixed(CONTENT_WIDTH - 8)); + rowsContainer.child(editButton); + + FlowLayout hintRow = UIContainers.verticalFlow(Sizing.fixed(CONTENT_WIDTH - 8), Sizing.content()); + hintRow.padding(Insets.of(4, 0, 0, 0)); + hintRow.child(UIComponents.label(Component.literal("Drag HUD modules to reposition. Press ESC to stop.")) + .color(Color.ofRgb(0x797b86))); + rowsContainer.child(hintRow); + + content.child(rowsContainer); } private void openModuleSettings(String title, List rows) { diff --git a/src/main/java/de/fullrisk/citrusDev/UIHelper/DraggableHud.java b/src/main/java/de/fullrisk/citrusDev/UIHelper/DraggableHud.java new file mode 100644 index 0000000..6b04b9f --- /dev/null +++ b/src/main/java/de/fullrisk/citrusDev/UIHelper/DraggableHud.java @@ -0,0 +1,221 @@ +package de.fullrisk.citrusDev.UIHelper; + +import de.fullrisk.citrusDev.Config; +import de.fullrisk.citrusDev.UI.Module_Coords; +import de.fullrisk.citrusDev.UI.Module_FPS; +import de.fullrisk.citrusDev.UI.Module_KeyStrokes; +import io.wispforest.owo.ui.container.FlowLayout; +import io.wispforest.owo.ui.core.Positioning; +import net.minecraft.client.Minecraft; +import org.lwjgl.BufferUtils; +import org.lwjgl.glfw.GLFW; + +import java.nio.DoubleBuffer; +import java.util.function.BooleanSupplier; +import java.util.function.IntConsumer; +import java.util.function.IntSupplier; +import java.util.function.Supplier; + +public class DraggableHud { + + private static boolean editMode = false; + private static boolean dragging = false; + private static FlowLayout dragTarget = null; + private static double dragStartMouseX, dragStartMouseY; + private static int dragModuleStartX, dragModuleStartY; + private static ModuleDragConfig currentConfig; + private static boolean wasLeftDown = false; + private static boolean cursorEnabled = false; + + private static final DoubleBuffer cursorXBuf = BufferUtils.createDoubleBuffer(1); + private static final DoubleBuffer cursorYBuf = BufferUtils.createDoubleBuffer(1); + + public static boolean isEditMode() { + return editMode; + } + + public static void setEditMode(boolean enabled) { + editMode = enabled; + if (!enabled) { + wasLeftDown = false; + } + } + + public static void tick() { + Minecraft mc = Minecraft.getInstance(); + if (mc.getWindow() == null) return; + long window = mc.getWindow().handle(); + + if (mc.screen != null) { + if (dragging) endDrag(); + if (editMode) { + editMode = false; + disableCursor(window); + } + wasLeftDown = false; + return; + } + + if (editMode && GLFW.glfwGetKey(window, GLFW.GLFW_KEY_ESCAPE) == GLFW.GLFW_PRESS) { + editMode = false; + disableCursor(window); + wasLeftDown = false; + return; + } + + boolean leftDown = GLFW.glfwGetMouseButton(window, GLFW.GLFW_MOUSE_BUTTON_LEFT) == GLFW.GLFW_PRESS; + + if (editMode && !cursorEnabled) { + int ww = mc.getWindow().getWidth(); + int wh = mc.getWindow().getHeight(); + GLFW.glfwSetCursorPos(window, ww / 2.0, wh / 2.0); + GLFW.glfwSetInputMode(window, GLFW.GLFW_CURSOR, GLFW.GLFW_CURSOR_NORMAL); + cursorEnabled = true; + } else if (!editMode && cursorEnabled) { + disableCursor(window); + wasLeftDown = leftDown; + return; + } + + if (cursorEnabled) { + cursorXBuf.clear(); + cursorYBuf.clear(); + GLFW.glfwGetCursorPos(window, cursorXBuf, cursorYBuf); + double mouseX = cursorXBuf.get(0); + double mouseY = cursorYBuf.get(0); + + if (leftDown && !wasLeftDown) { + tryStartDrag(mouseX, mouseY); + } else if (dragging && leftDown) { + updateDrag(mouseX, mouseY); + } else if (dragging && !leftDown) { + endDrag(); + } + } + + wasLeftDown = leftDown; + } + + private static void disableCursor(long window) { + if (cursorEnabled) { + GLFW.glfwSetInputMode(window, GLFW.GLFW_CURSOR, GLFW.GLFW_CURSOR_DISABLED); + cursorEnabled = false; + } + } + + private static void tryStartDrag(double mouseX, double mouseY) { + Minecraft mc = Minecraft.getInstance(); + var win = mc.getWindow(); + int sw = win.getWidth(); + int sh = win.getHeight(); + int gw = win.getGuiScaledWidth(); + int gh = win.getGuiScaledHeight(); + + double guiMouseX = mouseX * gw / sw; + double guiMouseY = mouseY * gh / sh; + + for (ModuleDragConfig mod : getModules()) { + FlowLayout container = mod.containerGetter.get(); + if (container == null || !mod.isVisible.getAsBoolean()) continue; + + int modX = mod.getX.getAsInt(); + int modY = mod.getY.getAsInt(); + int left = (int) ((gw - mod.hitW) * modX / 100.0); + int top = (int) ((gh - mod.hitH) * modY / 100.0); + + if (guiMouseX >= left && guiMouseX <= left + mod.hitW + && guiMouseY >= top && guiMouseY <= top + mod.hitH) { + dragging = true; + dragTarget = container; + dragStartMouseX = mouseX; + dragStartMouseY = mouseY; + dragModuleStartX = modX; + dragModuleStartY = modY; + currentConfig = mod; + return; + } + } + } + + private static void updateDrag(double mouseX, double mouseY) { + if (!dragging || dragTarget == null || currentConfig == null) return; + + Minecraft mc = Minecraft.getInstance(); + var win = mc.getWindow(); + int sw = win.getWidth(); + int sh = win.getHeight(); + int gw = win.getGuiScaledWidth(); + int gh = win.getGuiScaledHeight(); + + double deltaX = mouseX - dragStartMouseX; + double deltaY = mouseY - dragStartMouseY; + double guiDeltaX = deltaX * gw / sw; + double guiDeltaY = deltaY * gh / sh; + int newX = dragModuleStartX + (int) (guiDeltaX / (gw - currentConfig.hitW) * 100); + int newY = dragModuleStartY + (int) (guiDeltaY / (gh - currentConfig.hitH) * 100); + newX = Math.max(0, Math.min(100, newX)); + newY = Math.max(0, Math.min(100, newY)); + + dragTarget.positioning(Positioning.relative(newX, newY)); + currentConfig.setX.accept(newX); + currentConfig.setY.accept(newY); + } + + private static void endDrag() { + if (currentConfig != null) { + Config.get().save(); + } + dragging = false; + dragTarget = null; + currentConfig = null; + } + + private static ModuleDragConfig[] getModules() { + Config c = Config.get(); + return new ModuleDragConfig[]{ + new ModuleDragConfig( + Module_Coords::getCoordinates, 120, 50, + () -> c.coordsX, v -> c.coordsX = v, + () -> c.coordsY, v -> c.coordsY = v, + Module_Coords::isEnabled + ), + new ModuleDragConfig( + Module_KeyStrokes::getKeyContainer, + c.keySize * 3 + 20, c.keySize * 2 + 20, + () -> c.keyX, v -> c.keyX = v, + () -> c.keyY, v -> c.keyY = v, + Module_KeyStrokes::isEnabled + ), + new ModuleDragConfig( + Module_FPS::getFpsContainer, 100, 30, + () -> c.fpsX, v -> c.fpsX = v, + () -> c.fpsY, v -> c.fpsY = v, + Module_FPS::isEnabled + ) + }; + } + + private static class ModuleDragConfig { + Supplier containerGetter; + int hitW, hitH; + IntSupplier getX; + IntConsumer setX; + IntSupplier getY; + IntConsumer setY; + BooleanSupplier isVisible; + + ModuleDragConfig(Supplier containerGetter, int hitW, int hitH, + IntSupplier getX, IntConsumer setX, + IntSupplier getY, IntConsumer setY, + BooleanSupplier isVisible) { + this.containerGetter = containerGetter; + this.hitW = hitW; + this.hitH = hitH; + this.getX = getX; + this.setX = setX; + this.getY = getY; + this.setY = setY; + this.isVisible = isVisible; + } + } +} diff --git a/src/main/java/de/fullrisk/citrusDev/client/CitrusDevClient.java b/src/main/java/de/fullrisk/citrusDev/client/CitrusDevClient.java index 83a24f6..1aa88d8 100644 --- a/src/main/java/de/fullrisk/citrusDev/client/CitrusDevClient.java +++ b/src/main/java/de/fullrisk/citrusDev/client/CitrusDevClient.java @@ -3,6 +3,7 @@ package de.fullrisk.citrusDev.client; import de.fullrisk.citrusDev.Config; import de.fullrisk.citrusDev.UI.*; +import de.fullrisk.citrusDev.UIHelper.DraggableHud; import io.wispforest.owo.ui.hud.Hud; import net.fabricmc.api.ClientModInitializer; import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents; @@ -32,14 +33,18 @@ public class CitrusDevClient implements ClientModInitializer { )); ClientTickEvents.END_CLIENT_TICK.register(client -> { + /* if (client.player != null && !client.isPaused()) { PlaytimeTracker.tick(); QuestsPanel.tick(); } + */ + Module_FPS.tick(); Module_KeyStrokes.tick(); Module_Coords.tick(); + DraggableHud.tick(); while (openScreenKey.consumeClick()) { diff --git a/src/main/java/de/fullrisk/citrusDev/mixin/MouseHandlerMixin.java b/src/main/java/de/fullrisk/citrusDev/mixin/MouseHandlerMixin.java new file mode 100644 index 0000000..d3dffb9 --- /dev/null +++ b/src/main/java/de/fullrisk/citrusDev/mixin/MouseHandlerMixin.java @@ -0,0 +1,27 @@ +package de.fullrisk.citrusDev.mixin; + +import de.fullrisk.citrusDev.UIHelper.DraggableHud; +import net.minecraft.client.MouseHandler; +import net.minecraft.client.input.MouseButtonInfo; +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(MouseHandler.class) +public class MouseHandlerMixin { + + @Inject(method = "turnPlayer", at = @At("HEAD"), cancellable = true) + private void citrusDev$cancelTurnInEditMode(double cursorDelta, CallbackInfo ci) { + if (DraggableHud.isEditMode()) { + ci.cancel(); + } + } + + @Inject(method = "onButton", at = @At("HEAD"), cancellable = true) + private void citrusDev$cancelClickInEditMode(long windowHandle, MouseButtonInfo buttonInfo, int action, CallbackInfo ci) { + if (DraggableHud.isEditMode() && buttonInfo.button() == 0) { + ci.cancel(); + } + } +} diff --git a/src/main/resources/citrus-dev.mixins.json b/src/main/resources/citrus-dev.mixins.json index 188ba17..a4fcc62 100644 --- a/src/main/resources/citrus-dev.mixins.json +++ b/src/main/resources/citrus-dev.mixins.json @@ -6,7 +6,8 @@ "mixins": [ ], "client": [ - "SliderComponentMixin" + "SliderComponentMixin", + "MouseHandlerMixin" ], "injectors": { "defaultRequire": 1