aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorvicisacat <victor.branchu@gmail.com>2024-04-14 17:36:21 +0200
committervicisacat <victor.branchu@gmail.com>2024-04-20 16:09:49 +0200
commitb791e38cbd36360935164e337fa992bf514cbb36 (patch)
treef245ad744288d6f62452c26161878d59f651971d
parent3581365e654306cf08d0c4284ddfa454befd519d (diff)
downloadSkyblocker-b791e38cbd36360935164e337fa992bf514cbb36.tar.gz
Skyblocker-b791e38cbd36360935164e337fa992bf514cbb36.tar.bz2
Skyblocker-b791e38cbd36360935164e337fa992bf514cbb36.zip
it works so cool
-rw-r--r--src/main/java/de/hysky/skyblocker/mixin/WindowMixin.java16
-rw-r--r--src/main/java/de/hysky/skyblocker/skyblock/FancyStatusBars.java205
-rw-r--r--src/main/java/de/hysky/skyblocker/skyblock/auction/EditBidPopup.java13
-rw-r--r--src/main/java/de/hysky/skyblocker/skyblock/fancybars/BarGrid.java37
-rw-r--r--src/main/java/de/hysky/skyblocker/skyblock/fancybars/EditBarColorPopup.java117
-rw-r--r--src/main/java/de/hysky/skyblocker/skyblock/fancybars/EditBarWidget.java274
-rw-r--r--src/main/java/de/hysky/skyblocker/skyblock/fancybars/StatusBar.java194
-rw-r--r--src/main/java/de/hysky/skyblocker/skyblock/fancybars/StatusBarsConfigScreen.java217
-rw-r--r--src/main/java/de/hysky/skyblocker/utils/render/gui/AbstractPopupScreen.java37
-rw-r--r--src/main/resources/assets/skyblocker/lang/en_us.json10
-rw-r--r--src/main/resources/skyblocker.mixins.json1
11 files changed, 1008 insertions, 113 deletions
diff --git a/src/main/java/de/hysky/skyblocker/mixin/WindowMixin.java b/src/main/java/de/hysky/skyblocker/mixin/WindowMixin.java
new file mode 100644
index 00000000..92ca967d
--- /dev/null
+++ b/src/main/java/de/hysky/skyblocker/mixin/WindowMixin.java
@@ -0,0 +1,16 @@
+package de.hysky.skyblocker.mixin;
+
+import de.hysky.skyblocker.skyblock.FancyStatusBars;
+import net.minecraft.client.util.Window;
+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(Window.class)
+public class WindowMixin {
+ @Inject(method = "setScaleFactor", at = @At("TAIL"))
+ public void onScaleFactorChange(double scaleFactor, CallbackInfo ci) {
+ FancyStatusBars.updatePositions();
+ }
+}
diff --git a/src/main/java/de/hysky/skyblocker/skyblock/FancyStatusBars.java b/src/main/java/de/hysky/skyblocker/skyblock/FancyStatusBars.java
index 1a582863..6bf012ff 100644
--- a/src/main/java/de/hysky/skyblocker/skyblock/FancyStatusBars.java
+++ b/src/main/java/de/hysky/skyblocker/skyblock/FancyStatusBars.java
@@ -1,5 +1,6 @@
package de.hysky.skyblocker.skyblock;
+import com.google.gson.JsonObject;
import de.hysky.skyblocker.SkyblockerMod;
import de.hysky.skyblocker.config.SkyblockerConfigManager;
import de.hysky.skyblocker.skyblock.fancybars.BarGrid;
@@ -8,25 +9,37 @@ import de.hysky.skyblocker.skyblock.fancybars.StatusBarsConfigScreen;
import de.hysky.skyblocker.utils.Utils;
import de.hysky.skyblocker.utils.render.RenderHelper;
import de.hysky.skyblocker.utils.scheduler.Scheduler;
-import jdk.jshell.EvalException;
import net.fabricmc.fabric.api.client.command.v2.ClientCommandManager;
import net.fabricmc.fabric.api.client.command.v2.ClientCommandRegistrationCallback;
+import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientLifecycleEvents;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.font.TextRenderer;
import net.minecraft.client.gui.DrawContext;
import net.minecraft.client.texture.Sprite;
import net.minecraft.client.util.math.MatrixStack;
+import net.minecraft.text.Text;
import net.minecraft.util.Identifier;
+import net.minecraft.util.math.MathHelper;
+import org.lwjgl.glfw.GLFW;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
import java.awt.*;
-import java.util.Collection;
-import java.util.HashMap;
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.NoSuchFileException;
+import java.nio.file.Path;
+import java.util.*;
import java.util.List;
-import java.util.Map;
+import java.util.concurrent.CompletableFuture;
import java.util.function.Supplier;
public class FancyStatusBars {
private static final Identifier BARS = new Identifier(SkyblockerMod.NAMESPACE, "textures/gui/bars.png");
+ private static final Path FILE = SkyblockerMod.CONFIG_DIR.resolve("status_bars.json");
+ private static final Logger LOGGER = LoggerFactory.getLogger(FancyStatusBars.class);
private final MinecraftClient client = MinecraftClient.getInstance();
private final StatusBarTracker statusBarTracker = SkyblockerMod.getInstance().statusBarTracker;
@@ -48,26 +61,138 @@ public class FancyStatusBars {
public static Map<String, StatusBar> statusBars = new HashMap<>();
public static void init() {
- statusBars.put("health", new StatusBar(new Identifier(SkyblockerMod.NAMESPACE, "bars/icons/health"), new Color[]{new Color(255, 0, 0), new Color(255, 220, 0)}, true, null, "health"));
- statusBars.put("intelligence", new StatusBar(new Identifier(SkyblockerMod.NAMESPACE, "bars/icons/intelligence"), new Color[]{new Color(0, 255, 255), new Color(180, 0, 255)}, true, null, "intelligence"));
- statusBars.put("defense", new StatusBar(new Identifier(SkyblockerMod.NAMESPACE, "bars/icons/defense"), new Color[]{new Color(255, 255, 255)}, false, null, "defense"));
- statusBars.put("experience", new StatusBar(new Identifier(SkyblockerMod.NAMESPACE, "bars/icons/experience"), new Color[]{new Color(100, 220, 70)}, false, null, "experience"));
-
-
- barGrid.addRow(1, false);
+ statusBars.put("health", new StatusBar(new Identifier(SkyblockerMod.NAMESPACE, "bars/icons/health"),
+ new Color[]{new Color(255, 0, 0), new Color(255, 220, 0)},
+ true, new Color(255, 85, 85), Text.translatable("skyblocker.bars.config.health")));
+ statusBars.put("intelligence", new StatusBar(new Identifier(SkyblockerMod.NAMESPACE, "bars/icons/intelligence"),
+ new Color[]{new Color(0, 255, 255), new Color(180, 0, 255)},
+ true, new Color(85, 255, 255), Text.translatable("skyblocker.bars.config.intelligence")));
+ statusBars.put("defense", new StatusBar(new Identifier(SkyblockerMod.NAMESPACE, "bars/icons/defense"),
+ new Color[]{new Color(255, 255, 255)},
+ false, new Color(185, 185, 185), Text.translatable("skyblocker.bars.config.defense")));
+ statusBars.put("experience", new StatusBar(new Identifier(SkyblockerMod.NAMESPACE, "bars/icons/experience"),
+ new Color[]{new Color(100, 230, 70)},
+ false, new Color(128, 255, 32), Text.translatable("skyblocker.bars.config.experience")));
+
+ // Default positions
+ StatusBar health = statusBars.get("health");
+ health.gridX = 1;
+ health.gridY = 1;
+ StatusBar intelligence = statusBars.get("intelligence");
+ intelligence.gridX = 2;
+ intelligence.gridY = 1;
+ StatusBar defense = statusBars.get("defense");
+ defense.gridX = 1;
+ defense.gridY = -1;
+ StatusBar experience = statusBars.get("experience");
+ experience.gridX = 1;
+ experience.gridY = 2;
+
+ CompletableFuture.supplyAsync(FancyStatusBars::loadBarConfig).thenAccept(object -> {
+ if (object != null) {
+ for (String s : object.keySet()) {
+ if (statusBars.containsKey(s)) {
+ try {
+ statusBars.get(s).loadFromJson(object.get(s).getAsJsonObject());
+ } catch (Exception e) {
+ LOGGER.error("[Skyblocker] Failed to load {} status bar", s, e);
+ }
+ } else {
+ LOGGER.warn("[Skyblocker] Unknown status bar: {}", s);
+ }
+ }
+ }
+ placeBarsInGrid();
+ configLoaded = true;
+ });
+ ClientLifecycleEvents.CLIENT_STOPPING.register((client) -> {
+ saveBarConfig();
+ GLFW.glfwDestroyCursor(StatusBarsConfigScreen.RESIZE_CURSOR);
+ });
+ /*barGrid.addRow(1, false);
barGrid.add(1, 1, statusBars.get("health"));
barGrid.add(2, 1, statusBars.get("intelligence"));
barGrid.addRow(2, false);
barGrid.add(1, 2, statusBars.get("experience"));
barGrid.addRow(-1, true);
- barGrid.add(1, -1, statusBars.get("defense"));
+ barGrid.add(1, -1, statusBars.get("defense"));*/
+ //placeBarsInGrid();
ClientCommandRegistrationCallback.EVENT.register((dispatcher, registryAccess) -> dispatcher.register(
ClientCommandManager.literal("skyblocker")
.then(ClientCommandManager.literal("bar_test").executes(Scheduler.queueOpenScreenCommand(StatusBarsConfigScreen::new)))));
}
+ private static boolean configLoaded = false;
+
+ private static void placeBarsInGrid() {
+ List<StatusBar> original = statusBars.values().stream().toList();
+
+ // TOP
+ List<StatusBar> barList = new ArrayList<>(original.stream().filter(statusBar -> statusBar.gridY > 0).toList());
+ barList.sort((a, b) -> a.gridY == b.gridY ? Integer.compare(a.gridX, b.gridX) : Integer.compare(a.gridY, b.gridY));
+
+ int y = 0;
+ int rowNum = 0;
+ for (StatusBar statusBar : barList) {
+ if (statusBar.gridY > y) {
+ barGrid.addRowToEnd(true, false);
+ rowNum++;
+ y = statusBar.gridY;
+ }
+ barGrid.addToEndOfRow(rowNum, false, statusBar);
+ }
+
+ // BOTTOM LEFT
+ barList.clear();
+ barList.addAll(original.stream().filter(statusBar -> statusBar.gridY < 0 && statusBar.gridX < 0).toList());
+ barList.sort((a, b) -> a.gridY == b.gridY ? -Integer.compare(a.gridX, b.gridX) : -Integer.compare(a.gridY, b.gridY));
+ doBottom(false, barList);
+
+ // BOTTOM RIGHT
+ barList.clear();
+ barList.addAll(original.stream().filter(statusBar -> statusBar.gridY < 0 && statusBar.gridX > 0).toList());
+ barList.sort((a, b) -> a.gridY == b.gridY ? Integer.compare(a.gridX, b.gridX) : -Integer.compare(a.gridY, b.gridY));
+ doBottom(true, barList);
+ }
+
+ private static void doBottom(boolean right, List<StatusBar> barList) {
+ int y = 0;
+ int rowNum = 0;
+ for (StatusBar statusBar : barList) {
+ if (statusBar.gridY < y) {
+ barGrid.addRowToEnd(false, right);
+ rowNum--;
+ y = statusBar.gridY;
+ }
+ barGrid.addToEndOfRow(rowNum, right, statusBar);
+ }
+ }
+
+ public static JsonObject loadBarConfig() {
+ try (BufferedReader reader = Files.newBufferedReader(FILE)) {
+ return SkyblockerMod.GSON.fromJson(reader, JsonObject.class);
+ } catch (NoSuchFileException e) {
+ LOGGER.warn("[Skyblocker] No status bar config file found, using defaults");
+ } catch (Exception e) {
+ LOGGER.error("[Skyblocker] Failed to load status bars config", e);
+ }
+ return null;
+ }
+
+ public static void saveBarConfig() {
+ JsonObject output = new JsonObject();
+ statusBars.forEach((s, statusBar) -> output.add(s, statusBar.toJson()));
+ try (BufferedWriter writer = Files.newBufferedWriter(FILE)) {
+ SkyblockerMod.GSON.toJson(output, writer);
+ LOGGER.info("[Skyblocker] Saved status bars config");
+ } catch (IOException e) {
+ LOGGER.error("[Skyblocker] Failed to save status bars config", e);
+ }
+ }
+
public static void updatePositions() {
+ if (!configLoaded) return;
final float hotbarSize = 182;
final int width = MinecraftClient.getInstance().getWindow().getScaledWidth();
final int height = MinecraftClient.getInstance().getWindow().getScaledHeight();
@@ -83,6 +208,7 @@ public class FancyStatusBars {
// THE TOP
for (int i = 0; i < barGrid.getTopSize(); i++) {
List<StatusBar> row = barGrid.getRow(i + 1, false);
+ System.out.println(row);
if (row.isEmpty()) continue;
int totalSize = 0;
for (StatusBar bar : row) {
@@ -90,10 +216,11 @@ public class FancyStatusBars {
}
// Fix sizing
- whileLoop: while (totalSize != 12) {
+ whileLoop:
+ while (totalSize != 12) {
if (totalSize > 12) {
for (StatusBar bar : row) {
- if (bar.size > 2) { // TODO: this can cause infinite looping if we add more than 6 bars
+ if (bar.size > bar.getMinimumSize()) { // TODO: this can cause infinite looping if we add more than 6 bars
bar.size--;
totalSize--;
}
@@ -101,7 +228,7 @@ public class FancyStatusBars {
}
} else {
for (StatusBar bar : row) {
- if (bar.size < 12) {
+ if (bar.size < bar.getMaximumSize()) {
bar.size++;
totalSize++;
}
@@ -110,52 +237,52 @@ public class FancyStatusBars {
}
}
- int x = width/2 - 91;
- int y = height - 33 - 10*i;
+ int x = width / 2 - 91;
+ int y = height - 33 - 10 * i;
+ int size = 0;
for (int j = 0; j < row.size(); j++) {
StatusBar bar = row.get(j);
- bar.setX(x);
+ bar.setX(x + (int) ((size / 12.d) * hotbarSize));
bar.setY(y);
- bar.setWidth((int) ((bar.size / 12.f)*hotbarSize));
- x += bar.getWidth();
- bar.gridY = i+1;
- bar.gridX = j+1;
+ bar.setWidth((int) ((bar.size / 12.d) * hotbarSize));
+ size += bar.size;
+ bar.gridY = i + 1;
+ bar.gridX = j + 1;
}
}
- final int maxSize = 3;
// BOTTOM LEFT
for (int i = 0; i < barGrid.getBottomLeftSize(); i++) {
List<StatusBar> row = barGrid.getRow(-(i + 1), false);
if (row.isEmpty()) continue;
- int x = width/2 - 91 - 2;
- int y = height - 15-10*i;
+ int x = width / 2 - 91 - 2;
+ int y = height - 15 - 10 * (barGrid.getBottomLeftSize() - i - 1);
for (int j = 0; j < row.size(); j++) {
StatusBar bar = row.get(j);
- bar.size = Math.min(bar.size, maxSize);
+ bar.size = MathHelper.clamp(bar.size, bar.getMinimumSize(), bar.getMaximumSize());
bar.setY(y);
- bar.setWidth(bar.size*25);
+ bar.setWidth(bar.size * 25);
x -= bar.getWidth();
bar.setX(x);
- bar.gridX = -j-1;
- bar.gridY = -i-1;
+ bar.gridX = -j - 1;
+ bar.gridY = -i - 1;
}
}
// BOTTOM RIGHT
for (int i = 0; i < barGrid.getBottomRightSize(); i++) {
List<StatusBar> row = barGrid.getRow(-(i + 1), true);
if (row.isEmpty()) continue;
- int x = width/2 + 91 + 2;
- int y = height - 15-10*i;
+ int x = width / 2 + 91 + 2;
+ int y = height - 15 - 10 * (barGrid.getBottomRightSize() - i - 1);
for (int j = 0; j < row.size(); j++) {
StatusBar bar = row.get(j);
- bar.size = Math.min(bar.size, maxSize);
+ bar.size = MathHelper.clamp(bar.size, bar.getMinimumSize(), bar.getMaximumSize());
bar.setX(x);
bar.setY(y);
- bar.setWidth(bar.size*25);
+ bar.setWidth(bar.size * 25);
x += bar.getWidth();
- bar.gridX = j+1;
- bar.gridY = -i-1;
+ bar.gridX = j + 1;
+ bar.gridY = -i - 1;
}
}
}
@@ -186,7 +313,7 @@ public class FancyStatusBars {
value.render(context, -1, -1, client.getLastFrameDuration());
}
for (StatusBar statusBar : barCollection) {
- statusBar.renderText(context);
+ if (statusBar.showText()) statusBar.renderText(context);
}
StatusBarTracker.Resource health = statusBarTracker.getHealth();
statusBars.get("health").updateValues(health.value() / (float) health.max(), health.overflow() / (float) health.max(), health.value());
@@ -236,7 +363,7 @@ public class FancyStatusBars {
MatrixStack matrices = context.getMatrices();
matrices.push();
matrices.translate(50, 50, 0);
- matrices.scale(2,2,1);
+ matrices.scale(2, 2, 1);
context.drawSprite(0, 0, 0, 60, 5, SUPPLIER.get(), 1, 0.25f, 0.25f, 1);
matrices.pop();
return true;
@@ -314,13 +441,13 @@ public class FancyStatusBars {
context.drawTexture(BARS, anchorsX[anchorNum] + offsetX, anchorsY[anchorNum], 0, v, 9, 9);
// Draw the background for the bar
- context.drawGuiTexture(BAR_BACK, anchorsX[anchorNum]+ offsetX+10, anchorsY[anchorNum]+1, bar_width, 7);
+ context.drawGuiTexture(BAR_BACK, anchorsX[anchorNum] + offsetX + 10, anchorsY[anchorNum] + 1, bar_width, 7);
// Draw the filled part of the bar
for (int i = 0; i < fill.length; i++) {
int fill_width = this.fill[i] * (bar_width - 2) / 100;
if (fill_width >= 1) {
- RenderHelper.renderNineSliceColored(context, BAR_FILL, anchorsX[anchorNum] + offsetX + 11, anchorsY[anchorNum]+2, fill_width, 5, colors[i]);
+ RenderHelper.renderNineSliceColored(context, BAR_FILL, anchorsX[anchorNum] + offsetX + 11, anchorsY[anchorNum] + 2, fill_width, 5, colors[i]);
}
}
}
diff --git a/src/main/java/de/hysky/skyblocker/skyblock/auction/EditBidPopup.java b/src/main/java/de/hysky/skyblocker/skyblock/auction/EditBidPopup.java
index 6b90b86c..9d460803 100644
--- a/src/main/java/de/hysky/skyblocker/skyblock/auction/EditBidPopup.java
+++ b/src/main/java/de/hysky/skyblocker/skyblock/auction/EditBidPopup.java
@@ -34,18 +34,7 @@ public class EditBidPopup extends AbstractPopupScreen {
super.init();
layout = DirectionalLayoutWidget.vertical();
layout.spacing(8).getMainPositioner().alignHorizontalCenter();
- textFieldWidget = new TextFieldWidget(textRenderer, 120, 15, Text.empty()) {
- @Override
- public boolean keyPressed(int keyCode, int scanCode, int modifiers) {
- if (!super.keyPressed(keyCode, scanCode, modifiers)) {
- if (keyCode == GLFW.GLFW_KEY_ENTER || keyCode == GLFW.GLFW_KEY_KP_ENTER) {
- done(null);
- return true;
- }
- } else return true;
- return false;
- }
- };
+ textFieldWidget = new EnterConfirmTextFieldWidget(textRenderer, 120, 15, Text.empty(), () -> done(null));
textFieldWidget.setTextPredicate(this::isStringGood);
layout.add(new TextWidget(Text.literal("- Set Bid -").fillStyle(Style.EMPTY.withBold(true)), textRenderer));
layout.add(textFieldWidget);
diff --git a/src/main/java/de/hysky/skyblocker/skyblock/fancybars/BarGrid.java b/src/main/java/de/hysky/skyblocker/skyblock/fancybars/BarGrid.java
index 93219240..384250f9 100644
--- a/src/main/java/de/hysky/skyblocker/skyblock/fancybars/BarGrid.java
+++ b/src/main/java/de/hysky/skyblocker/skyblock/fancybars/BarGrid.java
@@ -53,6 +53,28 @@ public class BarGrid {
}
}
+ public void addRowToEnd(boolean top, boolean right) {
+ if (top) {
+ this.top.add(new LinkedList<>());
+ } else {
+ (right ? bottomRight: bottomLeft).add(new LinkedList<>());
+ }
+ }
+
+ public void addToEndOfRow(int row, boolean right, StatusBar bar) {
+ if (row>0) {
+ LinkedList<StatusBar> statusBars = top.get(row - 1);
+ statusBars.add(bar);
+ bar.gridY = row;
+ bar.gridX = statusBars.indexOf(bar)+1;
+ } else if (row<0) {
+ LinkedList<StatusBar> statusBars = (right? bottomRight: bottomLeft).get(Math.abs(row)-1);
+ statusBars.add(bar);
+ bar.gridY = row;
+ bar.gridX = (statusBars.indexOf(bar)+1) * (right ? 1: -1);
+ }
+ }
+
public void remove(int x, int y) {
System.out.println("Removing x: " + x + " y: " + y);
if (y > 0) {
@@ -92,6 +114,21 @@ public class BarGrid {
}
}
+ public boolean coordinatesExist(int x, int y) {
+ if (x == 0 || y == 0) return false;
+ if (y > 0) {
+ if (y > getTopSize()) return false;
+ return x <= getRow(y, false).size();
+ } else {
+ if (Math.abs(y) > (x < 0 ? getBottomLeftSize(): getBottomRightSize())) return false;
+ return Math.abs(x) <= getRow(y, x > 0).size();
+ }
+ }
+
+ public StatusBar getBar(int x, int y) {
+ return getRow(y, x>0).get(Math.abs(x)-1);
+ }
+
public int getTopSize() {return top.size();}
public int getBottomLeftSize() {return bottomLeft.size();}
diff --git a/src/main/java/de/hysky/skyblocker/skyblock/fancybars/EditBarColorPopup.java b/src/main/java/de/hysky/skyblocker/skyblock/fancybars/EditBarColorPopup.java
new file mode 100644
index 00000000..a3ce4e43
--- /dev/null
+++ b/src/main/java/de/hysky/skyblocker/skyblock/fancybars/EditBarColorPopup.java
@@ -0,0 +1,117 @@
+package de.hysky.skyblocker.skyblock.fancybars;
+
+import de.hysky.skyblocker.utils.render.gui.AbstractPopupScreen;
+import net.minecraft.client.MinecraftClient;
+import net.minecraft.client.gui.DrawContext;
+import net.minecraft.client.gui.Element;
+import net.minecraft.client.gui.screen.Screen;
+import net.minecraft.client.gui.screen.narration.NarrationMessageBuilder;
+import net.minecraft.client.gui.widget.*;
+import net.minecraft.text.Style;
+import net.minecraft.text.Text;
+
+import java.awt.*;
+import java.util.List;
+import java.util.function.Consumer;
+
+public class EditBarColorPopup extends AbstractPopupScreen {
+
+ private final Consumer<Color> setColor;
+
+ private DirectionalLayoutWidget layout = DirectionalLayoutWidget.vertical();
+ private BasicColorSelector colorSelector;
+
+ protected EditBarColorPopup(Text title, Screen backgroundScreen, Consumer<Color> setColor) {
+ super(title, backgroundScreen);
+ this.setColor = setColor;
+ }
+
+ @Override
+ protected void init() {
+ super.init();
+ layout = DirectionalLayoutWidget.vertical();
+ layout.spacing(8).getMainPositioner().alignHorizontalCenter();
+ layout.add(new TextWidget(title.copy().fillStyle(Style.EMPTY.withBold(true)), MinecraftClient.getInstance().textRenderer));
+ colorSelector = new BasicColorSelector(0, 0, 150, () -> done(null));
+ layout.add(colorSelector);
+
+ DirectionalLayoutWidget horizontal = DirectionalLayoutWidget.horizontal();
+ ButtonWidget buttonWidget = ButtonWidget.builder(Text.literal("Cancel"), button -> close()).width(80).build();
+ horizontal.add(buttonWidget);
+ horizontal.add(ButtonWidget.builder(Text.literal("Done"), this::done).width(80).build());
+
+ layout.add(horizontal);
+ layout.forEachChild(this::addDrawableChild);
+ this.layout.refreshPositions();
+ SimplePositioningWidget.setPos(layout, this.getNavigationFocus());
+ }
+
+ private void done(Object object) {
+ if (colorSelector.validColor) setColor.accept(new Color(colorSelector.getColor()));
+ close();
+ }
+
+ @Override
+ public void renderBackground(DrawContext context, int mouseX, int mouseY, float delta) {
+ super.renderBackground(context, mouseX, mouseY, delta);
+ drawPopupBackground(context, layout.getX(), layout.getY(), layout.getWidth(), layout.getHeight());
+ }
+
+ private static class BasicColorSelector extends ContainerWidget {
+
+ private final EnterConfirmTextFieldWidget textFieldWidget;
+
+ public BasicColorSelector(int x, int y, int width, Runnable onEnter) {
+ super(x, y, width, 15, Text.literal("edit color"));
+ textFieldWidget = new EnterConfirmTextFieldWidget(MinecraftClient.getInstance().textRenderer, getX() + 16, getY(), width - 16, 15, Text.empty(), onEnter);
+ textFieldWidget.setChangedListener(this::onTextChange);
+ textFieldWidget.setTextPredicate(s -> s.length() <= 6);
+ }
+
+ @Override
+ public List<? extends Element> children() {
+ return List.of(textFieldWidget);
+ }
+
+ public int getColor() {
+ return color;
+ }
+
+ private int color = 0xFF000000;
+ private boolean validColor = false;
+
+ private void onTextChange(String text) {
+ try {
+ color = Integer.parseInt(text, 16) | 0xFF000000;
+ validColor = true;
+ } catch (NumberFormatException e) {
+ color = 0;
+ validColor = false;
+ }
+ }
+
+ @Override
+ protected void renderWidget(DrawContext context, int mouseX, int mouseY, float delta) {
+ context.drawBorder(getX(), getY(), 15, 15, validColor ? -1 : 0xFFDD0000);
+ context.fill(getX() + 1, getY() + 1, getX() + 14, getY() + 14, color);
+ textFieldWidget.renderWidget(context, mouseX, mouseY, delta);
+ }
+
+ @Override
+ protected void appendClickableNarrations(NarrationMessageBuilder builder) {
+
+ }
+
+ @Override
+ public void setX(int x) {
+ super.setX(x);
+ textFieldWidget.setX(getX()+16);
+ }
+
+ @Override
+ public void setY(int y) {
+ super.setY(y);
+ textFieldWidget.setY(getY());
+ }
+ }
+}
diff --git a/src/main/java/de/hysky/skyblocker/skyblock/fancybars/EditBarWidget.java b/src/main/java/de/hysky/skyblocker/skyblock/fancybars/EditBarWidget.java
new file mode 100644
index 00000000..54af7a03
--- /dev/null
+++ b/src/main/java/de/hysky/skyblocker/skyblock/fancybars/EditBarWidget.java
@@ -0,0 +1,274 @@
+package de.hysky.skyblocker.skyblock.fancybars;
+
+import net.minecraft.client.MinecraftClient;
+import net.minecraft.client.font.TextRenderer;
+import net.minecraft.client.gui.DrawContext;
+import net.minecraft.client.gui.Element;
+import net.minecraft.client.gui.screen.Screen;
+import net.minecraft.client.gui.screen.narration.NarrationMessageBuilder;
+import net.minecraft.client.gui.tooltip.TooltipBackgroundRenderer;
+import net.minecraft.client.gui.widget.ClickableWidget;
+import net.minecraft.client.gui.widget.ContainerWidget;
+import net.minecraft.client.gui.widget.TextWidget;
+import net.minecraft.client.util.math.MatrixStack;
+import net.minecraft.text.MutableText;
+import net.minecraft.text.Text;
+import net.minecraft.util.Colors;
+import net.minecraft.util.Formatting;
+
+import java.awt.*;
+import java.util.List;
+import java.util.function.Consumer;
+
+public class EditBarWidget extends ContainerWidget {
+
+ private final EnumCyclingOption<StatusBar.IconPosition> iconOption;
+ private final BooleanOption booleanOption;
+
+ private final ColorOption color1;
+ private final ColorOption color2;
+ private final ColorOption textColor;
+ private final TextWidget nameWidget;
+
+ private int contentsWidth = 0;
+
+ public EditBarWidget(int x, int y, Screen parent) {
+ super(x, y, 100, 66, Text.literal("Edit bar"));
+
+ TextRenderer textRenderer = MinecraftClient.getInstance().textRenderer;
+
+ nameWidget = new TextWidget(Text.empty(), textRenderer);
+
+ MutableText translatable = Text.translatable("skyblocker.bars.config.icon");
+ contentsWidth = Math.max(contentsWidth, textRenderer.getWidth(translatable) + textRenderer.getWidth("RIGHT") + 10);
+ iconOption = new EnumCyclingOption<>(0, 11, getWidth(), translatable, StatusBar.IconPosition.class);
+
+ translatable = Text.translatable("skyblocker.bars.config.showValue");
+ contentsWidth = Math.max(contentsWidth, textRenderer.getWidth(translatable) + 9 + 10);
+ booleanOption = new BooleanOption(0, 22, getWidth(), translatable);
+
+ // COLO(u)RS
+ translatable = Text.translatable("skyblocker.bars.config.mainColor");
+ contentsWidth = Math.max(contentsWidth, textRenderer.getWidth(translatable) + 9 + 10);
+ color1 = new ColorOption(0, 33, getWidth(), translatable, parent);
+
+ translatable = Text.translatable("skyblocker.bars.config.overflowColor");
+ contentsWidth = Math.max(contentsWidth, textRenderer.getWidth(translatable) + 9 + 10);
+ color2 = new ColorOption(0, 44, getWidth(), translatable, parent);
+
+ translatable = Text.translatable("skyblocker.bars.config.textColor");
+ contentsWidth = Math.max(contentsWidth, textRenderer.getWidth(translatable) + 9 + 10);
+ textColor = new ColorOption(0, 55, getWidth(), translatable, parent);
+
+ setWidth(contentsWidth);
+ }
+
+ @Override
+ public List<? extends Element> children() {
+ return List.of(iconOption, booleanOption, color1, color2, textColor);
+ }
+
+ public int insideMouseX = 0;
+ public int insideMouseY = 0;
+
+ @Override
+ protected void renderWidget(DrawContext context, int mouseX, int mouseY, float delta) {
+ if (isHovered()) {
+ insideMouseX = mouseX;
+ insideMouseY = mouseY;
+ } else {
+ int i = mouseX - insideMouseX;
+ int j = mouseY - insideMouseY;
+ if (i * i + j * j > 30 * 30) visible = false;
+ }
+ TooltipBackgroundRenderer.render(context, getX(), getY(), getWidth(), getHeight(), 0);
+ MatrixStack matrices = context.getMatrices();
+ matrices.push();
+ matrices.translate(getX(), getY(), 0);
+ nameWidget.render(context, mouseX, mouseY, delta);
+ iconOption.renderWidget(context, mouseX - getX(), mouseY - getY(), delta);
+ booleanOption.renderWidget(context, mouseX - getX(), mouseY - getY(), delta);
+ color1.renderWidget(context, mouseX - getX(), mouseY - getY(), delta);
+ color2.renderWidget(context, mouseX - getX(), mouseY - getY(), delta);
+ textColor.renderWidget(context, mouseX - getX(), mouseY - getY(), delta);
+ matrices.pop();
+ }
+
+ @Override
+ protected void appendClickableNarrations(NarrationMessageBuilder builder) {
+ }
+
+ @Override
+ public boolean mouseClicked(double mouseX, double mouseY, int button) {
+ if (!visible) return false;
+ if (!isHovered()) visible = false;
+ return super.mouseClicked(mouseX - getX(), mouseY - getY(), button);
+ }
+
+ public void setStatusBar(StatusBar statusBar) {
+ iconOption.setCurrent(statusBar.getIconPosition());
+ iconOption.setOnChange(statusBar::setIconPosition);
+ booleanOption.setCurrent(statusBar.showText());
+ booleanOption.setOnChange(statusBar::setShowText);
+
+ color1.setCurrent(statusBar.getColors()[0].getRGB());
+ color1.setOnChange(color -> statusBar.getColors()[0] = color);
+
+ color2.active = statusBar.hasOverflow();
+ if (color2.active) {
+ color2.setCurrent(statusBar.getColors()[1].getRGB());
+ color2.setOnChange(color -> statusBar.getColors()[1] = color);
+ }
+
+ if (statusBar.getTextColor() != null) {
+ textColor.setCurrent(statusBar.getTextColor().getRGB());
+ }
+ textColor.setOnChange(statusBar::setTextColor);
+
+ MutableText formatted = statusBar.getName().copy().formatted(Formatting.BOLD);
+ nameWidget.setMessage(formatted);
+ setWidth(Math.max(MinecraftClient.getInstance().textRenderer.getWidth(formatted), contentsWidth));
+ }
+
+ @Override
+ public void setWidth(int width) {
+ super.setWidth(width);
+ iconOption.setWidth(width);
+ booleanOption.setWidth(width);
+ color1.setWidth(width);
+ color2.setWidth(width);
+ textColor.setWidth(width);
+ nameWidget.setWidth(width);
+
+ }
+
+ public static class EnumCyclingOption<T extends Enum<T>> extends ClickableWidget {
+
+ private T current;
+ private final T[] values;
+ private Consumer<T> onChange = null;
+
+ public EnumCyclingOption(int x, int y, int width, Text message, Class<T> enumClass) {
+ super(x, y, width, 11, message);
+ values = enumClass.getEnumConstants();
+ current = values[0];
+ }
+
+ @Override
+ protected void renderWidget(DrawContext context, int mouseX, int mouseY, float delta) {
+ if (isMouseOver(mouseX, mouseY)) {
+ context.fill(getX(), getY(), getRight(), getBottom(), 0x20FFFFFF);
+ }
+ TextRenderer textRenderer = MinecraftClient.getInstance().textRenderer;
+ context.drawText(textRenderer, getMessage(), getX() + 1, getY() + 1, -1, true);
+ String string = current.toString();
+ context.drawText(textRenderer, string, getRight() - textRenderer.getWidth(string) - 1, getY() + 1, -1, true);
+ }
+
+ public void setCurrent(T current) {
+ this.current = current;
+ }
+
+ @Override
+ public void onClick(double mouseX, double mouseY) {
+ current = values[(current.ordinal() + 1) % values.length];
+ if (onChange != null) onChange.accept(current);
+ super.onClick(mouseX, mouseY);
+ }
+
+ @Override
+ protected void appendClickableNarrations(NarrationMessageBuilder builder) {
+ }
+
+ public void setOnChange(Consumer<T> onChange) {
+ this.onChange = onChange;
+ }
+ }
+
+ public static class BooleanOption extends ClickableWidget {
+
+ private boolean current = false;
+ private Consumer<Boolean> onChange = null;
+
+ public BooleanOption(int x, int y, int width, Text message) {
+ super(x, y, width, 11, message);
+ }
+
+ @Override
+ protected void renderWidget(DrawContext context, int mouseX, int mouseY, float delta) {
+ if (isMouseOver(mouseX, mouseY)) {
+ context.fill(getX(), getY(), getRight(), getBottom(), 0x20FFFFFF);
+ }
+ TextRenderer textRenderer = MinecraftClient.getInstance().textRenderer;
+ context.drawText(textRenderer, getMessage(), getX() + 1, getY() + 1, -1, true);
+ context.drawBorder(getRight() - 10, getY() + 1, 9, 9, -1);
+ if (current) context.fill(getRight() - 8, getY() + 3, getRight() - 3, getY() + 8, -1);
+ }
+
+ @Override
+ public void onClick(double mouseX, double mouseY) {
+ current = !current;
+ if (onChange != null) onChange.accept(current);
+ super.onClick(mouseX, mouseY);
+ }
+
+ @Override
+ protected void appendClickableNarrations(NarrationMessageBuilder builder) {
+ }
+
+ public void setCurrent(boolean current) {
+ this.current = current;
+ }
+
+ public void setOnChange(Consumer<Boolean> onChange) {
+ this.onChange = onChange;
+ }
+ }
+
+ public static class ColorOption extends ClickableWidget {
+
+ public void setCurrent(int current) {
+ this.current = current;
+ }
+
+ private int current