aboutsummaryrefslogtreecommitdiff
path: root/src/main/java
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java')
-rw-r--r--src/main/java/io/github/cottonmc/cotton/gui/client/modmenu/ConfigGui.java16
-rw-r--r--src/main/java/io/github/cottonmc/cotton/gui/widget/WAbstractSlider.java210
-rw-r--r--src/main/java/io/github/cottonmc/cotton/gui/widget/WLabeledSlider.java116
-rw-r--r--src/main/java/io/github/cottonmc/cotton/gui/widget/WSlider.java95
4 files changed, 436 insertions, 1 deletions
diff --git a/src/main/java/io/github/cottonmc/cotton/gui/client/modmenu/ConfigGui.java b/src/main/java/io/github/cottonmc/cotton/gui/client/modmenu/ConfigGui.java
index 23c5b6f..b8abd22 100644
--- a/src/main/java/io/github/cottonmc/cotton/gui/client/modmenu/ConfigGui.java
+++ b/src/main/java/io/github/cottonmc/cotton/gui/client/modmenu/ConfigGui.java
@@ -30,7 +30,21 @@ public class ConfigGui extends LightweightGuiDescription {
WTextField testField = new WTextField();
testField.setSuggestion("test");
root.add(testField, 0, 3, 4, 1);
-
+
+ /*
+ WSlider verticalSlider = new WSlider(-100, 100, Axis.VERTICAL);
+ verticalSlider.setDraggingFinishedListener(() -> System.out.println("Mouse released"));
+ verticalSlider.setValueChangeListener(System.out::println);
+
+ WLabeledSlider horizontalSlider = new WLabeledSlider(0, 500);
+ horizontalSlider.setLabelUpdater(value -> new LiteralText(value + "!"));
+ horizontalSlider.setDraggingFinishedListener(() -> System.out.println("Mouse released"));
+ horizontalSlider.setValue(250);
+
+ root.add(verticalSlider, 6, 0, 1, 3);
+ root.add(horizontalSlider, 1, 4, 4, 1);
+ */
+
root.add(new WKirbSprite(), 5, 4);
WButton doneButton = new WButton(new TranslatableText("gui.done"));
diff --git a/src/main/java/io/github/cottonmc/cotton/gui/widget/WAbstractSlider.java b/src/main/java/io/github/cottonmc/cotton/gui/widget/WAbstractSlider.java
new file mode 100644
index 0000000..074371f
--- /dev/null
+++ b/src/main/java/io/github/cottonmc/cotton/gui/widget/WAbstractSlider.java
@@ -0,0 +1,210 @@
+package io.github.cottonmc.cotton.gui.widget;
+
+import net.minecraft.util.math.MathHelper;
+import org.lwjgl.glfw.GLFW;
+
+import javax.annotation.Nullable;
+import java.util.function.IntConsumer;
+
+/**
+ * A base class for slider widgets that can be used to select int values.
+ *
+ * <p>You can set two listeners on a slider:
+ * <ul>
+ * <li>
+ * A value change listener that gets all value changes (including direct setValue calls).
+ * </li>
+ * <li>
+ * A dragging finished listener that gets called when the player stops dragging the slider
+ * or modifies the value with the keyboard.
+ * For example, this can be used for sending sync packets to the server
+ * when the player has selected a value.
+ * </li>
+ * </ul>
+ */
+public abstract class WAbstractSlider extends WWidget {
+ protected final int min, max;
+ protected final Axis axis;
+
+ protected int value;
+
+ /**
+ * True if the user is currently dragging the thumb.
+ * Used for visuals.
+ */
+ protected boolean dragging = false;
+
+ /**
+ * A value:coordinate ratio. Used for converting user input into values.
+ */
+ protected float valueToCoordRatio;
+
+ /**
+ * A coordinate:value ratio. Used for rendering the thumb.
+ */
+ protected float coordToValueRatio;
+
+ /**
+ * True if there is a pending dragging finished event caused by the keyboard.
+ */
+ private boolean valueChangedWithKeys = false;
+
+ @Nullable private IntConsumer valueChangeListener = null;
+ @Nullable private Runnable draggingFinishedListener = null;
+
+ protected WAbstractSlider(int min, int max, Axis axis) {
+ if (max <= min)
+ throw new IllegalArgumentException("Minimum value must be smaller than the maximum!");
+
+ this.min = min;
+ this.max = max;
+ this.axis = axis;
+ this.value = min;
+ }
+
+ /**
+ * @return the thumb size along the slider axis
+ */
+ protected abstract int getThumbWidth();
+
+ /**
+ * Checks if the mouse cursor is close enough to the slider that the user can start dragging.
+ *
+ * @param x the mouse x position
+ * @param y the mouse y position
+ * @return if the cursor is inside dragging bounds
+ */
+ protected abstract boolean isMouseInsideBounds(int x, int y);
+
+ @Override
+ public void setSize(int x, int y) {
+ super.setSize(x, y);
+ int trackHeight = (axis == Axis.HORIZONTAL ? x : y) - getThumbWidth();
+ valueToCoordRatio = (float) (max - min) / trackHeight;
+ coordToValueRatio = 1 / valueToCoordRatio;
+ }
+
+ @Override
+ public boolean canResize() {
+ return true;
+ }
+
+ @Override
+ public boolean canFocus() {
+ return true;
+ }
+
+ @Override
+ public WWidget onMouseDown(int x, int y, int button) {
+ // Check if cursor is inside or <=2px away from track
+ if (isMouseInsideBounds(x, y)) {
+ requestFocus();
+ }
+ return super.onMouseDown(x, y, button);
+ }
+
+ @Override
+ public void onMouseDrag(int x, int y, int button) {
+ if (isFocused()) {
+ dragging = true;
+ moveSlider(x, y);
+ }
+ }
+
+ @Override
+ public void onClick(int x, int y, int button) {
+ moveSlider(x, y);
+ if (draggingFinishedListener != null) draggingFinishedListener.run();
+ }
+
+ private void moveSlider(int x, int y) {
+ int pos = (axis == Axis.VERTICAL ? (height - y) : x) - getThumbWidth() / 2;
+ int rawValue = min + Math.round(valueToCoordRatio * pos);
+ int previousValue = value;
+ value = MathHelper.clamp(rawValue, min, max);
+ if (value != previousValue) onValueChanged(value);
+ }
+
+ @Override
+ public WWidget onMouseUp(int x, int y, int button) {
+ dragging = false;
+ if (draggingFinishedListener != null) draggingFinishedListener.run();
+ return super.onMouseUp(x, y, button);
+ }
+
+ public int getValue() {
+ return value;
+ }
+
+ public void setValue(int value) {
+ this.value = value;
+ onValueChanged(value);
+ }
+
+ public void setValueChangeListener(@Nullable IntConsumer valueChangeListener) {
+ this.valueChangeListener = valueChangeListener;
+ }
+
+ public void setDraggingFinishedListener(@Nullable Runnable draggingFinishedListener) {
+ this.draggingFinishedListener = draggingFinishedListener;
+ }
+
+ public int getMinValue() {
+ return min;
+ }
+
+ public int getMaxValue() {
+ return max;
+ }
+
+ public Axis getAxis() {
+ return axis;
+ }
+
+ protected void onValueChanged(int value) {
+ if (valueChangeListener != null) valueChangeListener.accept(value);
+ }
+
+ @Override
+ public void onKeyPressed(int ch, int key, int modifiers) {
+ boolean valueChanged = false;
+ if (modifiers == 0) {
+ if (isDecreasingKey(ch) && value > min) {
+ value--;
+ valueChanged = true;
+ } else if (isIncreasingKey(ch) && value < max) {
+ value++;
+ valueChanged = true;
+ }
+ } else if (modifiers == GLFW.GLFW_MOD_CONTROL) {
+ if (isDecreasingKey(ch) && value != min) {
+ value = min;
+ valueChanged = true;
+ } else if (isIncreasingKey(ch) && value != max) {
+ value = max;
+ valueChanged = true;
+ }
+ }
+
+ if (valueChanged) {
+ onValueChanged(value);
+ valueChangedWithKeys = true;
+ }
+ }
+
+ @Override
+ public void onKeyReleased(int ch, int key, int modifiers) {
+ if (valueChangedWithKeys && (isDecreasingKey(ch) || isIncreasingKey(ch))) {
+ if (draggingFinishedListener != null) draggingFinishedListener.run();
+ valueChangedWithKeys = false;
+ }
+ }
+
+ private static boolean isDecreasingKey(int ch) {
+ return ch == GLFW.GLFW_KEY_LEFT || ch == GLFW.GLFW_KEY_DOWN;
+ }
+
+ private static boolean isIncreasingKey(int ch) {
+ return ch == GLFW.GLFW_KEY_RIGHT || ch == GLFW.GLFW_KEY_UP;
+ }
+}
diff --git a/src/main/java/io/github/cottonmc/cotton/gui/widget/WLabeledSlider.java b/src/main/java/io/github/cottonmc/cotton/gui/widget/WLabeledSlider.java
new file mode 100644
index 0000000..37fe464
--- /dev/null
+++ b/src/main/java/io/github/cottonmc/cotton/gui/widget/WLabeledSlider.java
@@ -0,0 +1,116 @@
+package io.github.cottonmc.cotton.gui.widget;
+
+import io.github.cottonmc.cotton.gui.client.ScreenDrawing;
+import net.fabricmc.api.EnvType;
+import net.fabricmc.api.Environment;
+import net.minecraft.client.gui.widget.AbstractButtonWidget;
+import net.minecraft.text.Text;
+
+import javax.annotation.Nullable;
+
+/**
+ * A vanilla-style labeled slider widget.
+ *
+ * <p>In addition to the standard slider listeners,
+ * labeled sliders also support "label updaters" that can update the label
+ * when the value is changed.
+ *
+ * @see WAbstractSlider for more information about listeners
+ */
+public class WLabeledSlider extends WAbstractSlider {
+ @Nullable private Text label = null;
+ @Nullable private LabelUpdater labelUpdater = null;
+
+ public WLabeledSlider(int min, int max) {
+ super(min, max, Axis.HORIZONTAL);
+ }
+
+ public WLabeledSlider(int min, int max, Text label) {
+ this(min, max);
+ this.label = label;
+ }
+
+
+ @Override
+ public void setSize(int x, int y) {
+ super.setSize(x, 20);
+ }
+
+ @Nullable
+ public Text getLabel() {
+ return label;
+ }
+
+ public void setLabel(@Nullable Text label) {
+ this.label = label;
+ }
+
+ @Override
+ protected void onValueChanged(int value) {
+ super.onValueChanged(value);
+ if (labelUpdater != null) {
+ label = labelUpdater.updateLabel(value);
+ }
+ }
+
+ public void setLabelUpdater(@Nullable LabelUpdater labelUpdater) {
+ this.labelUpdater = labelUpdater;
+ }
+
+ @Override
+ protected int getThumbWidth() {
+ return 8;
+ }
+
+ @Override
+ protected boolean isMouseInsideBounds(int x, int y) {
+ return x >= 0 && x <= width && y >= 0 && y <= height;
+ }
+
+ @Environment(EnvType.CLIENT)
+ @Override
+ public void paintBackground(int x, int y, int mouseX, int mouseY) {
+ drawButton(x, y, 0, width);
+
+ // 1: regular, 2: hovered, 0: disabled/dragging
+ int thumbX = Math.round(coordToValueRatio * (value - min));
+ int thumbY = 0;
+ int thumbWidth = getThumbWidth();
+ int thumbHeight = height;
+ boolean hovering = mouseX >= thumbX && mouseX <= thumbX + thumbWidth && mouseY >= thumbY && mouseY <= thumbY + thumbHeight;
+ int thumbState = dragging || hovering ? 2 : 1;
+
+ drawButton(x + thumbX, y + thumbY, thumbState, thumbWidth);
+
+ if (thumbState == 1 && isFocused()) {
+ float px = 1 / 32f;
+ ScreenDrawing.rect(WSlider.TEXTURE, x + thumbX, y + thumbY, thumbWidth, thumbHeight, 24*px, 0*px, 32*px, 20*px, 0xFFFFFFFF);
+ }
+
+ if (label != null) {
+ int color = isMouseInsideBounds(mouseX, mouseY) ? 0xFFFFA0 : 0xE0E0E0;
+ ScreenDrawing.drawCenteredWithShadow(label.asFormattedString(), x + width / 2, y + height / 2 - 4, color);
+ }
+ }
+
+ // state = 1: regular, 2: hovered, 0: disabled/dragging
+ @Environment(EnvType.CLIENT)
+ private void drawButton(int x, int y, int state, int width) {
+ float px = 1 / 256f;
+ float buttonLeft = 0 * px;
+ float buttonTop = (46 + (state * 20)) * px;
+ int halfWidth = width / 2;
+ if (halfWidth > 198) halfWidth = 198;
+ float buttonWidth = halfWidth * px;
+ float buttonHeight = 20 * px;
+ float buttonEndLeft = (200 - halfWidth) * px;
+
+ ScreenDrawing.rect(AbstractButtonWidget.WIDGETS_LOCATION, x, y, halfWidth, 20, buttonLeft, buttonTop, buttonLeft + buttonWidth, buttonTop + buttonHeight, 0xFFFFFFFF);
+ ScreenDrawing.rect(AbstractButtonWidget.WIDGETS_LOCATION, x + halfWidth, y, halfWidth, 20, buttonEndLeft, buttonTop, 200 * px, buttonTop + buttonHeight, 0xFFFFFFFF);
+ }
+
+ @FunctionalInterface
+ public interface LabelUpdater {
+ Text updateLabel(int value);
+ }
+}
diff --git a/src/main/java/io/github/cottonmc/cotton/gui/widget/WSlider.java b/src/main/java/io/github/cottonmc/cotton/gui/widget/WSlider.java
new file mode 100644
index 0000000..c57b705
--- /dev/null
+++ b/src/main/java/io/github/cottonmc/cotton/gui/widget/WSlider.java
@@ -0,0 +1,95 @@
+package io.github.cottonmc.cotton.gui.widget;
+
+import io.github.cottonmc.cotton.gui.client.BackgroundPainter;
+import io.github.cottonmc.cotton.gui.client.ScreenDrawing;
+import net.fabricmc.api.EnvType;
+import net.fabricmc.api.Environment;
+import net.minecraft.util.Identifier;
+
+import javax.annotation.Nullable;
+
+/**
+ * A simple slider widget that can be used to select int values.
+ *
+ * @see WAbstractSlider for supported listeners
+ */
+public class WSlider extends WAbstractSlider {
+ public static final int TRACK_WIDTH = 6;
+ public static final int THUMB_SIZE = 8;
+ public static final Identifier TEXTURE = new Identifier("libgui", "textures/widget/slider.png");
+
+ @Environment(EnvType.CLIENT)
+ @Nullable
+ private BackgroundPainter backgroundPainter = null;
+
+ public WSlider(int min, int max, Axis axis) {
+ super(min, max, axis);
+ }
+
+ public WSlider(int max, Axis axis) {
+ this(0, max, axis);
+ }
+
+ @Override
+ protected int getThumbWidth() {
+ return THUMB_SIZE;
+ }
+
+ @Override
+ protected boolean isMouseInsideBounds(int x, int y) {
+ // ao = axis-opposite mouse coordinate, aoCenter = center of ao's axis
+ int ao = axis == Axis.HORIZONTAL ? y : x;
+ int aoCenter = (axis == Axis.HORIZONTAL ? height : width) / 2;
+
+ // Check if cursor is inside or <=2px away from track
+ return ao >= aoCenter - TRACK_WIDTH / 2 - 2 && ao <= aoCenter + TRACK_WIDTH / 2 + 2;
+ }
+
+ @Environment(EnvType.CLIENT)
+ @Override
+ public void paintBackground(int x, int y, int mouseX, int mouseY) {
+ if (backgroundPainter != null) {
+ backgroundPainter.paintBackground(x, y, this);
+ } else {
+ float px = 1 / 32f;
+ // thumbX/Y: thumb position in widget-space
+ int thumbX, thumbY;
+ // thumbXOffset: thumb texture x offset in pixels
+ int thumbXOffset;
+
+ if (axis == Axis.VERTICAL) {
+ int trackX = x + width / 2 - TRACK_WIDTH / 2;
+ thumbX = width / 2 - THUMB_SIZE / 2;
+ thumbY = height - THUMB_SIZE + 1 - (int) (coordToValueRatio * (value - min));
+ thumbXOffset = 0;
+
+ ScreenDrawing.rect(TEXTURE, trackX, y + 1, TRACK_WIDTH, 1, 16*px, 0*px, 22*px, 1*px, 0xFFFFFFFF);
+ ScreenDrawing.rect(TEXTURE, trackX, y + 2, TRACK_WIDTH, height - 2, 16*px, 1*px, 22*px, 2*px, 0xFFFFFFFF);
+ ScreenDrawing.rect(TEXTURE, trackX, y + height, TRACK_WIDTH, 1, 16*px, 2*px, 22*px, 3*px, 0xFFFFFFFF);
+ } else {
+ int trackY = y + height / 2 - TRACK_WIDTH / 2;
+ thumbX = Math.round(coordToValueRatio * (value - min));
+ thumbY = height / 2 - THUMB_SIZE / 2;
+ thumbXOffset = 8;
+
+ ScreenDrawing.rect(TEXTURE, x, trackY, 1, TRACK_WIDTH, 16*px, 3*px, 17*px, 9*px, 0xFFFFFFFF);
+ ScreenDrawing.rect(TEXTURE, x + 1, trackY, width - 2, TRACK_WIDTH, 17*px, 3*px, 18*px, 9*px, 0xFFFFFFFF);
+ ScreenDrawing.rect(TEXTURE, x + width - 1, trackY, 1, TRACK_WIDTH, 18*px, 3*px, 19*px, 9*px, 0xFFFFFFFF);
+ }
+
+ // thumbState values:
+ // 0: default, 1: dragging, 2: hovered
+ int thumbState = dragging ? 1 : (mouseX >= thumbX && mouseX <= thumbX + THUMB_SIZE && mouseY >= thumbY && mouseY <= thumbY + THUMB_SIZE ? 2 : 0);
+ ScreenDrawing.rect(TEXTURE, x + thumbX, y + thumbY, THUMB_SIZE, THUMB_SIZE, thumbXOffset*px, 0*px + thumbState * 8*px, (thumbXOffset + 8)*px, 8*px + thumbState * 8*px, 0xFFFFFFFF);
+
+ if (thumbState == 0 && isFocused()) {
+ ScreenDrawing.rect(TEXTURE, x + thumbX, y + thumbY, THUMB_SIZE, THUMB_SIZE, 0*px, 24*px, 8*px, 32*px, 0xFFFFFFFF);
+ }
+ }
+ }
+
+ @Environment(EnvType.CLIENT)
+ public void setBackgroundPainter(BackgroundPainter backgroundPainter) {
+ this.backgroundPainter = backgroundPainter;
+ }
+}