diff --git a/src/main/java/org/saturnclient/ui2/Element.java b/src/main/java/org/saturnclient/ui2/Element.java index 4e4e00c..ec8b110 100644 --- a/src/main/java/org/saturnclient/ui2/Element.java +++ b/src/main/java/org/saturnclient/ui2/Element.java @@ -32,6 +32,15 @@ public class Element { public void charTyped(char typedChar) { } + public void mouseClicked(double mouseX, double mouseY, int button) { + } + + public void mouseDragged(double mouseX, double mouseY, int button, double deltaX, double deltaY) { + } + + public void mouseReleased(double mouseX, double mouseY, int button) { + } + public Element dimensions(int width, int height) { this.width = width; this.height = height; diff --git a/src/main/java/org/saturnclient/ui2/RenderScope.java b/src/main/java/org/saturnclient/ui2/RenderScope.java index 231428c..5cd7f59 100644 --- a/src/main/java/org/saturnclient/ui2/RenderScope.java +++ b/src/main/java/org/saturnclient/ui2/RenderScope.java @@ -398,4 +398,45 @@ public class RenderScope { sprite.getMinV(), sprite.getMaxV(), color); } } + + public void fill(int x1, int y1, int x2, int y2, int color) { + this.fill(x1, y1, x2, y2, 0, color); + } + + public void fill(int x1, int y1, int x2, int y2, int z, int color) { + this.fill(RenderLayer.getGui(), x1, y1, x2, y2, z, color); + } + + public void fill(RenderLayer layer, int x1, int y1, int x2, int y2, int color) { + this.fill(layer, x1, y1, x2, y2, 0, color); + } + + public void fill(RenderLayer layer, int x1, int y1, int x2, int y2, int z, int color) { + Matrix4f matrix4f = this.matrices.peek().getPositionMatrix(); + int i; + if (x1 < x2) { + i = x1; + x1 = x2; + x2 = i; + } + + if (y1 < y2) { + i = y1; + y1 = y2; + y2 = i; + } + + VertexConsumer vertexConsumer = this.vertexConsumers.getBuffer(layer); + vertexConsumer.vertex(matrix4f, (float) x1, (float) y1, (float) z).color(color); + vertexConsumer.vertex(matrix4f, (float) x1, (float) y2, (float) z).color(color); + vertexConsumer.vertex(matrix4f, (float) x2, (float) y2, (float) z).color(color); + vertexConsumer.vertex(matrix4f, (float) x2, (float) y1, (float) z).color(color); + } + + public void drawBorder(int x, int y, int width, int height, int color) { + this.fill(x, y, x + width, y + 1, color); + this.fill(x, y + height - 1, x + width, y + height, color); + this.fill(x, y + 1, x + 1, y + height - 1, color); + this.fill(x + width - 1, y + 1, x + width, y + height - 1, color); + } } diff --git a/src/main/java/org/saturnclient/ui2/components/dialog/ColorDialog.java b/src/main/java/org/saturnclient/ui2/components/dialog/ColorDialog.java deleted file mode 100644 index 9567218..0000000 --- a/src/main/java/org/saturnclient/ui2/components/dialog/ColorDialog.java +++ /dev/null @@ -1,246 +0,0 @@ -package org.saturnclient.ui2.components.dialog; - -import java.awt.Color; - -import org.saturnclient.saturnclient.SaturnClient; -import org.saturnclient.saturnclient.config.Property; -import org.saturnclient.saturnclient.mixin.DrawContextAccessor; -import org.saturnclient.ui2.RenderScope; -import org.saturnclient.ui2.SaturnScreen; - -import net.minecraft.client.gui.DrawContext; - -public class ColorDialog extends SaturnScreen { - private Property prop; - private SaturnScreen parent; - private static final int PICKER_WIDTH = 200; - private static final int PICKER_HEIGHT = 200; - private static final int SLIDER_WIDTH = 200; - private static final int SLIDER_HEIGHT = 10; - private static final int HUE_SLIDER_HEIGHT = 10; - private float hue = 0.0f; - - private int selectedX = 0; - private int selectedY = 0; - - private float opacity = 1.0f; - private boolean dragging = false; - - public ColorDialog(Property prop , SaturnScreen parent) { - super("Color Dialog"); - this.prop = prop; - this.parent = parent; - - int alpha = (prop.value >> 24) & 0xFF; - if (alpha == 0) alpha = 255; // fallback if no alpha specified - this.opacity = alpha / 255.0f; - syncFromColor(); - } - - @Override - public void ui() {} - - @Override - public void close() { - SaturnClient.client.setScreen(parent); - } - - @Override - public void render(DrawContext context, int mouseX, int mouseY, float delta) { - super.render(context, mouseX, mouseY, delta); - RenderScope renderScope = new RenderScope(context.getMatrices(), - ((DrawContextAccessor) context).getVertexConsumers()); - - renderScope.matrices.push(); - - renderScope.matrices.scale(0.5f, 0.5f, 0.5f); - - int xStart = (width - PICKER_WIDTH) / 2; - int yStart = (height - PICKER_HEIGHT) / 2; - - // Draw the HSV saturation/value square (with current hue) - for (int x = 0; x < PICKER_WIDTH; x++) { - for (int y = 0; y < PICKER_HEIGHT; y++) { - float saturation = x / (float) PICKER_WIDTH; - float value = 1.0f - y / (float) PICKER_HEIGHT; - int color = Color.HSBtoRGB(hue, saturation, value); - context.fill(xStart + x, yStart + y, xStart + x + 1, yStart + y + 1, 0xFF000000 | color); - } - } - - int x = (xStart + selectedX) - 4; - int y = (yStart + selectedY) - 4; - context.fill(x, y, x+8, y+8, 0xFF000000 | prop.value); - context.drawBorder(x, y, 8, 8, 0xFFffffff); - - renderSlider(context, mouseX, mouseY, delta); - renderHueSlider(context); - - renderScope.matrices.pop(); - } - - public void renderSlider(DrawContext context, int mouseX, int mouseY, float delta) { - int xStart = (width - SLIDER_WIDTH) / 2; - int yStart = (height - SLIDER_HEIGHT - PICKER_HEIGHT) / 2 - 10; - - // Draw slider background (left = transparent, right = white) - for (int x = 0; x < SLIDER_WIDTH; x++) { - float localOpacity = x / (float) SLIDER_WIDTH; - int alpha = (int)(localOpacity * 255) & 0xFF; - int color = (alpha << 24) | (prop.value & 0xFFFFFF); - context.fill(xStart + x, yStart, xStart + x + 1, yStart + SLIDER_HEIGHT, color); - } - - // Draw current value as a marker (thin black line) - int knobX = xStart + (int)(opacity * SLIDER_WIDTH); - context.fill(knobX - 1, yStart - 2, knobX + 1, yStart + SLIDER_HEIGHT + 2, 0xFF000000); - - // Draw opacity text - // context.drawText(textRenderer, String.format("Opacity: %.2f", opacity), xStart, yStart - 15, 0xFFFFFF, false); - } - - private void renderHueSlider(DrawContext context) { - int xStart = (width - SLIDER_WIDTH) / 2; - int yStart = (height + PICKER_HEIGHT) / 2 + 10; - - for (int x = 0; x < SLIDER_WIDTH; x++) { - float localHue = x / (float) SLIDER_WIDTH; - int color = Color.HSBtoRGB(localHue, 1.0f, 1.0f); - context.fill(xStart + x, yStart, xStart + x + 1, yStart + HUE_SLIDER_HEIGHT, 0xFF000000 | color); - } - - int knobX = xStart + (int)(hue * SLIDER_WIDTH); - context.fill(knobX - 1, yStart - 2, knobX + 1, yStart + HUE_SLIDER_HEIGHT + 2, 0xFF000000); - } - - @Override - public boolean mouseClicked(double mouseX, double mouseY, int button) { - mouseX *= 2; - mouseY *= 2; - - int xStart = (width - PICKER_WIDTH) / 2; - int yStart = (height - PICKER_HEIGHT) / 2; - - int relX = (int) mouseX - xStart; - int relY = (int) mouseY - yStart; - - if (relX >= 0 && relX < PICKER_WIDTH && relY >= 0 && relY < PICKER_HEIGHT) { - float saturation = relX / (float) PICKER_WIDTH; - float value = 1.0f - relY / (float) PICKER_HEIGHT; - prop.value = Color.HSBtoRGB(hue, saturation, value); - - int alpha = (int)(opacity * 255) & 0xFF; - prop.value = (alpha << 24) | (prop.value & 0xFFFFFF); - - selectedX = relX; - selectedY = relY; - - return true; - } else if (isInSlider(mouseX, mouseY)) { - updateOpacity(mouseX); - dragging = true; - return true; - } else if (isInHueSlider(mouseX, mouseY)) { - updateHue(mouseX); - return true; - } - - return super.mouseClicked(mouseX, mouseY, button); - } - - @Override - public boolean mouseReleased(double mouseX, double mouseY, int button) { - dragging = false; - return super.mouseReleased(mouseX, mouseY, button); - } - - @Override - public boolean mouseDragged(double mouseX, double mouseY, int button, double deltaX, double deltaY) { - mouseX *= 2; - mouseY *= 2; - - if (isInHueSlider(mouseX, mouseY)) { - updateHue(mouseX); - return true; - } else if (dragging) { - updateOpacity(mouseX); - return true; - } else { - int xStart = (width - PICKER_WIDTH) / 2; - int yStart = (height - PICKER_HEIGHT) / 2; - - int relX = (int) mouseX - xStart; - int relY = (int) mouseY - yStart; - - if (relX >= 0 && relX < PICKER_WIDTH && relY >= 0 && relY < PICKER_HEIGHT) { - float saturation = relX / (float) PICKER_WIDTH; - float value = 1.0f - relY / (float) PICKER_HEIGHT; - prop.value = Color.HSBtoRGB(hue, saturation, value); - - int alpha = (int)(opacity * 255) & 0xFF; - prop.value = (alpha << 24) | (prop.value & 0xFFFFFF); - - selectedX = relX; - selectedY = relY; - - return true; - } - } - - return super.mouseDragged(mouseX, mouseY, button, deltaX, deltaY); - } - - private boolean isInSlider(double mouseX, double mouseY) { - int xStart = (width - SLIDER_WIDTH) / 2; - int yStart = (height - SLIDER_HEIGHT - PICKER_HEIGHT) / 2 - 10; - return mouseX >= xStart && mouseX <= xStart + SLIDER_WIDTH && - mouseY >= yStart && mouseY <= yStart + SLIDER_HEIGHT; - } - - private boolean isInHueSlider(double mouseX, double mouseY) { - int xStart = (width - SLIDER_WIDTH) / 2; - int yStart = (height + PICKER_HEIGHT) / 2 + 10; - return mouseX >= xStart && mouseX <= xStart + SLIDER_WIDTH && - mouseY >= yStart && mouseY <= yStart + HUE_SLIDER_HEIGHT; - } - - private void updateOpacity(double mouseX) { - int xStart = (width - SLIDER_WIDTH) / 2; - float newOpacity = (float)((mouseX - xStart) / SLIDER_WIDTH); - opacity = Math.max(0.0f, Math.min(1.0f, newOpacity)); - - int alpha = (int)(opacity * 255) & 0xFF; - prop.value = (alpha << 24) | (prop.value & 0xFFFFFF); - } - - private void updateHue(double mouseX) { - int xStart = (width - SLIDER_WIDTH) / 2; - float newHue = (float)((mouseX - xStart) / SLIDER_WIDTH); - hue = Math.max(0.0f, Math.min(1.0f, newHue)); - - float saturation = selectedX / (float) PICKER_WIDTH; - float value = 1.0f - selectedY / (float) PICKER_HEIGHT; - prop.value = Color.HSBtoRGB(hue, saturation, value); - - int alpha = (int)(opacity * 255) & 0xFF; - prop.value = (alpha << 24) | (prop.value & 0xFFFFFF); - } - - private void syncFromColor() { - int rgb = prop.value & 0xFFFFFF; - float[] hsb = Color.RGBtoHSB((rgb >> 16) & 0xFF, (rgb >> 8) & 0xFF, rgb & 0xFF, null); - - this.hue = hsb[0]; - - float saturation = hsb[1]; - float value = hsb[2]; - - this.selectedX = (int)(saturation * PICKER_WIDTH); - this.selectedY = (int)((1.0f - value) * PICKER_HEIGHT); - - // Extract alpha and convert to opacity float - int alpha = (prop.value >> 24) & 0xFF; - if (alpha == 0) alpha = 255; - this.opacity = alpha / 255.0f; - } -} diff --git a/src/main/java/org/saturnclient/ui2/components/inputs/HexInput.java b/src/main/java/org/saturnclient/ui2/components/inputs/HexInput.java index 025bfd9..8a1864e 100644 --- a/src/main/java/org/saturnclient/ui2/components/inputs/HexInput.java +++ b/src/main/java/org/saturnclient/ui2/components/inputs/HexInput.java @@ -1,32 +1,182 @@ package org.saturnclient.ui2.components.inputs; -import org.saturnclient.saturnclient.SaturnClient; +import java.awt.Color; + import org.saturnclient.saturnclient.config.Property; import org.saturnclient.ui2.Element; import org.saturnclient.ui2.ElementContext; import org.saturnclient.ui2.RenderScope; -import org.saturnclient.ui2.SaturnScreen; -import org.saturnclient.ui2.components.dialog.ColorDialog; public class HexInput extends Element { public Property prop; - public SaturnScreen screen; - public HexInput(Property prop, SaturnScreen screen) { + // Picker state + private boolean open = false; + private float hue = 0f; + private float opacity = 1f; + private int selectedX = 0; + private int selectedY = 0; + + private boolean draggingOpacity = false; + + // Dimensions + private static final int PICKER_WIDTH = 200; + private static final int PICKER_HEIGHT = 200; + private static final int SLIDER_WIDTH = 200; + private static final int SLIDER_HEIGHT = 10; + private static final int HUE_SLIDER_HEIGHT = 10; + + public HexInput(Property prop) { + this.prop = prop; this.width = 58; this.height = 20; - this.prop = prop; - this.scale = 0.8f; - this.screen = screen; + syncFromColor(); } @Override public void click(int mouseX, int mouseY) { - SaturnClient.client.setScreen(new ColorDialog(prop, screen)); + open = !open; + } + + @Override + public void mouseClicked(double mouseX, double mouseY, int button) { + if (open && mouseX >= width + 8 && mouseX <= width && mouseY >= 0 && mouseY <= height) { + handlePickerClick(mouseX - width, mouseY); + } + } + + @Override + public void mouseDragged(double mouseX, double mouseY, int button, double deltaX, double deltaY) { + if (open) + handlePickerDrag(mouseX - width, mouseY); + } + + @Override + public void mouseReleased(double mouseX, double mouseY, int button) { + draggingOpacity = false; } @Override public void render(RenderScope renderScope, ElementContext ctx) { - renderScope.drawRoundedRectangle(0, 0, width, height, 17, prop.value); + // Draw the main color rectangle + renderScope.drawRoundedRectangle(0, 0, width, height, 6, prop.value); + + if (!open) + return; + + // Draw the picker relative to (width + 8, 0) + int pickerX = width + 8; + int pickerY = 0; + + renderColorSquare(renderScope, pickerX, pickerY); + renderOpacitySlider(renderScope, pickerX, pickerY - SLIDER_HEIGHT - 10); + renderHueSlider(renderScope, pickerX, pickerY + PICKER_HEIGHT + 10); } -} + + private void renderColorSquare(RenderScope renderScope, int xStart, int yStart) { + for (int x = 0; x < PICKER_WIDTH; x++) { + for (int y = 0; y < PICKER_HEIGHT; y++) { + float saturation = x / (float) PICKER_WIDTH; + float value = 1f - y / (float) PICKER_HEIGHT; + int color = Color.HSBtoRGB(hue, saturation, value); + int alpha = (int) (opacity * 255) & 0xFF; + color = (alpha << 24) | (color & 0xFFFFFF); + renderScope.fill(xStart + x, yStart + y, xStart + x + 1, yStart + y + 1, color); + } + } + + // Draw selection square + int selX = xStart + selectedX - 4; + int selY = yStart + selectedY - 4; + renderScope.fill(selX, selY, selX + 8, selY + 8, 0xFF000000 | prop.value); + renderScope.drawBorder(selX, selY, 8, 8, 0xFFFFFFFF); + } + + private void renderOpacitySlider(RenderScope renderScope, int xStart, int yStart) { + for (int x = 0; x < SLIDER_WIDTH; x++) { + float localOpacity = x / (float) SLIDER_WIDTH; + int alpha = (int) (localOpacity * 255) & 0xFF; + int color = (alpha << 24) | (prop.value & 0xFFFFFF); + renderScope.fill(xStart + x, yStart, xStart + x + 1, yStart + SLIDER_HEIGHT, color); + } + + int knobX = xStart + (int) (opacity * SLIDER_WIDTH); + renderScope.fill(knobX - 1, yStart - 2, knobX + 1, yStart + SLIDER_HEIGHT + 2, 0xFF000000); + } + + private void renderHueSlider(RenderScope renderScope, int xStart, int yStart) { + for (int x = 0; x < SLIDER_WIDTH; x++) { + float localHue = x / (float) SLIDER_WIDTH; + int color = Color.HSBtoRGB(localHue, 1f, 1f); + renderScope.fill(xStart + x, yStart, xStart + x + 1, yStart + HUE_SLIDER_HEIGHT, 0xFF000000 | color); + } + + int knobX = xStart + (int) (hue * SLIDER_WIDTH); + renderScope.fill(knobX - 1, yStart - 2, knobX + 1, yStart + HUE_SLIDER_HEIGHT + 2, 0xFF000000); + } + + private void handlePickerClick(double mouseX, double mouseY) { + // Color square + if (mouseX >= 0 && mouseX < PICKER_WIDTH && mouseY >= 0 && mouseY < PICKER_HEIGHT) { + selectedX = (int) mouseX; + selectedY = (int) mouseY; + updateColorFromSelection(); + } + + // Opacity slider + int opY = -SLIDER_HEIGHT - 10; + if (mouseX >= 0 && mouseX < SLIDER_WIDTH && mouseY >= opY && mouseY <= opY + SLIDER_HEIGHT) { + updateOpacity(mouseX); + draggingOpacity = true; + } + + // Hue slider + int hueY = PICKER_HEIGHT + 10; + if (mouseX >= 0 && mouseX < SLIDER_WIDTH && mouseY >= hueY && mouseY <= hueY + HUE_SLIDER_HEIGHT) { + updateHue(mouseX); + } + } + + private void handlePickerDrag(double mouseX, double mouseY) { + // Opacity + if (draggingOpacity) + updateOpacity(mouseX); + + // Color square + if (mouseX >= 0 && mouseX < PICKER_WIDTH && mouseY >= 0 && mouseY < PICKER_HEIGHT) { + selectedX = (int) mouseX; + selectedY = (int) mouseY; + updateColorFromSelection(); + } + } + + private void updateColorFromSelection() { + float saturation = selectedX / (float) PICKER_WIDTH; + float value = 1f - selectedY / (float) PICKER_HEIGHT; + int color = Color.HSBtoRGB(hue, saturation, value); + int alpha = (int) (opacity * 255) & 0xFF; + prop.value = (alpha << 24) | (color & 0xFFFFFF); + } + + private void updateHue(double mouseX) { + hue = (float) Math.max(0, Math.min(1, mouseX / SLIDER_WIDTH)); + updateColorFromSelection(); + } + + private void updateOpacity(double mouseX) { + opacity = (float) Math.max(0, Math.min(1, mouseX / SLIDER_WIDTH)); + updateColorFromSelection(); + } + + private void syncFromColor() { + int rgb = prop.value & 0xFFFFFF; + float[] hsb = Color.RGBtoHSB((rgb >> 16) & 0xFF, (rgb >> 8) & 0xFF, rgb & 0xFF, null); + hue = hsb[0]; + selectedX = (int) (hsb[1] * PICKER_WIDTH); + selectedY = (int) ((1f - hsb[2]) * PICKER_HEIGHT); + int alpha = (prop.value >> 24) & 0xFF; + if (alpha == 0) + alpha = 255; + opacity = alpha / 255f; + } +} \ No newline at end of file diff --git a/src/main/java/org/saturnclient/ui2/screens/ConfigEditor.java b/src/main/java/org/saturnclient/ui2/screens/ConfigEditor.java index 7b00966..c56ed4a 100644 --- a/src/main/java/org/saturnclient/ui2/screens/ConfigEditor.java +++ b/src/main/java/org/saturnclient/ui2/screens/ConfigEditor.java @@ -78,7 +78,7 @@ public class ConfigEditor extends SaturnScreen { break; case HEX: - configScroll.draw(new HexInput((Property) prop, this).position(w / 2, modY)); + configScroll.draw(new HexInput((Property) prop).position(w / 2, modY)); break; case INTEGER: