aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/main/java/io/github/cottonmc/cotton/gui/client/modmenu/ConfigGui.java5
-rw-r--r--src/main/java/io/github/cottonmc/cotton/gui/widget/WAbstractSlider.java185
-rw-r--r--src/main/java/io/github/cottonmc/cotton/gui/widget/WLabeledSlider.java80
-rw-r--r--src/main/java/io/github/cottonmc/cotton/gui/widget/WSlider.java147
4 files changed, 274 insertions, 143 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 fc3e3b5..97b3cfc 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
@@ -6,6 +6,7 @@ import io.github.cottonmc.cotton.gui.client.LightweightGuiDescription;
import io.github.cottonmc.cotton.gui.widget.Axis;
import io.github.cottonmc.cotton.gui.widget.WButton;
import io.github.cottonmc.cotton.gui.widget.WGridPanel;
+import io.github.cottonmc.cotton.gui.widget.WLabeledSlider;
import io.github.cottonmc.cotton.gui.widget.WSlider;
import io.github.cottonmc.cotton.gui.widget.WTextField;
import io.github.cottonmc.cotton.gui.widget.WToggleButton;
@@ -32,8 +33,8 @@ public class ConfigGui extends LightweightGuiDescription {
WTextField testField = new WTextField();
testField.setSuggestion("test");
root.add(testField, 0, 3, 4, 1);
- root.add(new WSlider(-1, 1, Axis.VERTICAL).setValueChangeListener(System.out::println), 6, 0, 1, 3);
- root.add(new WSlider(1, 2, Axis.HORIZONTAL).setValueChangeListener(System.out::println), 1, 4, 4, 1);
+ root.add(new WSlider(-100, 100, Axis.VERTICAL).setValueChangeListener(System.out::println), 6, 0, 1, 3);
+ root.add(new WLabeledSlider(1, 100).setValueChangeListener(System.out::println), 1, 4, 4, 1);
root.add(new WKirbSprite(), 5, 4);
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..bfa455e
--- /dev/null
+++ b/src/main/java/io/github/cottonmc/cotton/gui/widget/WAbstractSlider.java
@@ -0,0 +1,185 @@
+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 (except direct setValue calls).
+ * </li>
+ * <li>
+ * A focus release listener that gets called when the player stops dragging the slider.
+ * 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;
+
+ @Nullable private IntConsumer valueChangeListener = null;
+ @Nullable private Runnable focusReleaseListener = 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() + 1;
+ 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);
+ }
+
+ 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 && valueChangeListener != null) valueChangeListener.accept(value);
+ }
+
+ @Override
+ public WWidget onMouseUp(int x, int y, int button) {
+ dragging = false;
+ return super.onMouseUp(x, y, button);
+ }
+
+ @Override
+ public void onFocusLost() {
+ if (focusReleaseListener != null) focusReleaseListener.run();
+ }
+
+ public int getValue() {
+ return value;
+ }
+
+ public void setValue(int value) {
+ this.value = value;
+ }
+
+ public WAbstractSlider setValueChangeListener(@Nullable IntConsumer valueChangeListener) {
+ this.valueChangeListener = valueChangeListener;
+ return this;
+ }
+
+ public WAbstractSlider setFocusReleaseListener(@Nullable Runnable focusReleaseListener) {
+ this.focusReleaseListener = focusReleaseListener;
+ return this;
+ }
+
+ public int getMinValue() {
+ return min;
+ }
+
+ public int getMaxValue() {
+ return max;
+ }
+
+ public Axis getAxis() {
+ return axis;
+ }
+
+ @Override
+ public void onKeyPressed(int ch, int key, int modifiers) {
+ boolean valueChanged = false;
+ if (modifiers == 0) {
+ if ((ch == GLFW.GLFW_KEY_LEFT || ch == GLFW.GLFW_KEY_DOWN) && value > min) {
+ value--;
+ valueChanged = true;
+ } else if ((ch == GLFW.GLFW_KEY_RIGHT || ch == GLFW.GLFW_KEY_UP) && value < max) {
+ value++;
+ valueChanged = true;
+ }
+ } else if (modifiers == GLFW.GLFW_MOD_CONTROL) {
+ if ((ch == GLFW.GLFW_KEY_LEFT || ch == GLFW.GLFW_KEY_DOWN) && value != min) {
+ value = min;
+ valueChanged = true;
+ } else if ((ch == GLFW.GLFW_KEY_RIGHT || ch == GLFW.GLFW_KEY_UP) && value != max) {
+ value = max;
+ valueChanged = true;
+ }
+ }
+
+ if (valueChanged && valueChangeListener != null) valueChangeListener.accept(value);
+ }
+}
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..77a3325
--- /dev/null
+++ b/src/main/java/io/github/cottonmc/cotton/gui/widget/WLabeledSlider.java
@@ -0,0 +1,80 @@
+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.util.Identifier;
+
+/**
+ * A vanilla-style labeled slider widget.
+ *
+ * @see WAbstractSlider for more information about listeners
+ */
+/*
+ TODO:
+ - Add the labels
+ - Better textures for thumbs when dragging
+ - The thumb goes 1px outside the track on the right side
+ */
+public class WLabeledSlider extends WAbstractSlider {
+ private static final Identifier TEXTURE = AbstractButtonWidget.WIDGETS_LOCATION;
+
+ public WLabeledSlider(int min, int max) {
+ super(min, max, Axis.HORIZONTAL);
+ }
+
+ public WLabeledSlider(int max) {
+ this(0, max);
+ }
+
+ @Override
+ public void setSize(int x, int y) {
+ super.setSize(x, 20);
+ }
+
+ @Override
+ protected int getThumbWidth() {
+ return 6;
+ }
+
+ @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 = (int) (coordToValueRatio * (value - min));
+ int thumbY = 0;
+ int thumbWidth = getThumbWidth();
+ int thumbHeight = height;
+ int thumbState = dragging ? 0 : (mouseX >= thumbX && mouseX <= thumbX + thumbWidth && mouseY >= thumbY && mouseY <= thumbY + thumbHeight ? 2 : 1);
+
+ drawButton(x + thumbX, y + thumbY, thumbState, thumbWidth);
+
+ if (thumbState == 1 && isFocused()) {
+ // TODO: draw the focus border
+ }
+ }
+
+ // 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);
+ }
+}
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
index d981304..14e9972 100644
--- a/src/main/java/io/github/cottonmc/cotton/gui/widget/WSlider.java
+++ b/src/main/java/io/github/cottonmc/cotton/gui/widget/WSlider.java
@@ -5,11 +5,8 @@ import io.github.cottonmc.cotton.gui.client.ScreenDrawing;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.minecraft.util.Identifier;
-import net.minecraft.util.math.MathHelper;
-import org.lwjgl.glfw.GLFW;
import javax.annotation.Nullable;
-import java.util.function.IntConsumer;
/**
* A slider widget that can be used to select int values.
@@ -26,47 +23,17 @@ import java.util.function.IntConsumer;
* </li>
* </ul>
*/
-public class WSlider extends WWidget {
+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");
- private final int min, max;
- private final Axis axis;
-
- private int value;
-
- /**
- * True if the user is currently dragging the thumb.
- * Used for visuals.
- */
- private boolean dragging = false;
-
- /**
- * A value:coordinate ratio. Used for converting user input into values.
- */
- private float valueToCoordRatio;
-
- /**
- * A coordinate:value ratio. Used for rendering the thumb.
- */
- private float coordToValueRatio;
-
- @Nullable private IntConsumer valueChangeListener = null;
- @Nullable private Runnable focusReleaseListener = null;
-
@Environment(EnvType.CLIENT)
@Nullable
private BackgroundPainter backgroundPainter = null;
public WSlider(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;
+ super(min, max, axis);
}
public WSlider(int max, Axis axis) {
@@ -74,66 +41,18 @@ public class WSlider extends WWidget {
}
@Override
- public void setSize(int x, int y) {
- super.setSize(x, y);
- int trackHeight = (axis == Axis.HORIZONTAL ? x : y) - THUMB_SIZE + 1;
- valueToCoordRatio = (float) (max - min) / trackHeight;
- coordToValueRatio = 1 / valueToCoordRatio;
- }
-
- @Override
- public boolean canResize() {
- return true;
- }
-
- @Override
- public boolean canFocus() {
- return true;
+ protected int getThumbWidth() {
+ return THUMB_SIZE;
}
@Override
- public WWidget onMouseDown(int x, int y, int button) {
+ 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
- if (ao >= aoCenter - TRACK_WIDTH / 2 - 2 && ao <= aoCenter + TRACK_WIDTH / 2 + 2) {
- 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);
- }
-
- private void moveSlider(int x, int y) {
- int pos = (axis == Axis.VERTICAL ? (height - y) : x) - THUMB_SIZE / 2;
- int rawValue = min + Math.round(valueToCoordRatio * pos);
- int previousValue = value;
- value = MathHelper.clamp(rawValue, min, max);
- if (value != previousValue && valueChangeListener != null) valueChangeListener.accept(value);
- }
-
- @Override
- public WWidget onMouseUp(int x, int y, int button) {
- dragging = false;
- return super.onMouseUp(x, y, button);
- }
-
- @Override
- public void onFocusLost() {
- if (focusReleaseListener != null) focusReleaseListener.run();
+ return ao >= aoCenter - TRACK_WIDTH / 2 - 2 && ao <= aoCenter + TRACK_WIDTH / 2 + 2;
}
@Environment(EnvType.CLIENT)
@@ -179,62 +98,8 @@ public class WSlider extends WWidget {
}
}
- public int getValue() {
- return value;
- }
-
- public void setValue(int value) {
- this.value = value;
- }
-
- public WSlider setValueChangeListener(@Nullable IntConsumer valueChangeListener) {
- this.valueChangeListener = valueChangeListener;
- return this;
- }
-
- public WSlider setFocusReleaseListener(@Nullable Runnable focusReleaseListener) {
- this.focusReleaseListener = focusReleaseListener;
- return this;
- }
-
- public int getMinValue() {
- return min;
- }
-
- public int getMaxValue() {
- return max;
- }
-
- public Axis getAxis() {
- return axis;
- }
-
@Environment(EnvType.CLIENT)
public void setBackgroundPainter(BackgroundPainter backgroundPainter) {
this.backgroundPainter = backgroundPainter;
}
-
- @Override
- public void onKeyPressed(int ch, int key, int modifiers) {
- boolean valueChanged = false;
- if (modifiers == 0) {
- if ((ch == GLFW.GLFW_KEY_LEFT || ch == GLFW.GLFW_KEY_DOWN) && value > min) {
- value--;
- valueChanged = true;
- } else if ((ch == GLFW.GLFW_KEY_RIGHT || ch == GLFW.GLFW_KEY_UP) && value < max) {
- value++;
- valueChanged = true;
- }
- } else if (modifiers == GLFW.GLFW_MOD_CONTROL) {
- if ((ch == GLFW.GLFW_KEY_LEFT || ch == GLFW.GLFW_KEY_DOWN) && value != min) {
- value = min;
- valueChanged = true;
- } else if ((ch == GLFW.GLFW_KEY_RIGHT || ch == GLFW.GLFW_KEY_UP) && value != max) {
- value = max;
- valueChanged = true;
- }
- }
-
- if (valueChanged && valueChangeListener != null) valueChangeListener.accept(value);
- }
}