From 4c7158c99f0186a8ed7dd2fd37db4b6df4164f4c Mon Sep 17 00:00:00 2001
From: Juuxel <kasperi.kauppi@gmail.com>
Date: Thu, 29 Aug 2019 12:11:03 +0300
Subject: Sliders are done!

---
 .../cotton/gui/client/modmenu/ConfigGui.java       |  19 ++++--
 .../cotton/gui/widget/WAbstractSlider.java         |  65 ++++++++++++++-------
 .../cottonmc/cotton/gui/widget/WLabeledSlider.java |  64 +++++++++++++++-----
 .../github/cottonmc/cotton/gui/widget/WSlider.java |  16 +----
 .../assets/libgui/textures/widget/slider.png       | Bin 2597 -> 2978 bytes
 5 files changed, 112 insertions(+), 52 deletions(-)

(limited to 'src')

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 97b3cfc..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
@@ -3,11 +3,8 @@ package io.github.cottonmc.cotton.gui.client.modmenu;
 import io.github.cottonmc.cotton.gui.client.BackgroundPainter;
 import io.github.cottonmc.cotton.gui.client.LibGuiClient;
 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;
 import net.minecraft.client.MinecraftClient;
@@ -33,8 +30,20 @@ public class ConfigGui extends LightweightGuiDescription {
 		WTextField testField = new WTextField();
 		testField.setSuggestion("test");
 		root.add(testField, 0, 3, 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);
+
+		/*
+		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);
 		
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
index bfa455e..074371f 100644
--- a/src/main/java/io/github/cottonmc/cotton/gui/widget/WAbstractSlider.java
+++ b/src/main/java/io/github/cottonmc/cotton/gui/widget/WAbstractSlider.java
@@ -12,10 +12,11 @@ import java.util.function.IntConsumer;
  * <p>You can set two listeners on a slider:
  * <ul>
  *     <li>
- *         A value change listener that gets all value changes (except direct setValue calls).
+ *         A value change listener that gets all value changes (including direct setValue calls).
  *     </li>
  *     <li>
- *         A focus release listener that gets called when the player stops dragging the slider.
+ *         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>
@@ -43,8 +44,13 @@ public abstract class WAbstractSlider extends WWidget {
 	 */
 	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 focusReleaseListener = null;
+	@Nullable private Runnable draggingFinishedListener = null;
 
 	protected WAbstractSlider(int min, int max, Axis axis) {
 		if (max <= min)
@@ -73,7 +79,7 @@ public abstract class WAbstractSlider extends WWidget {
 	@Override
 	public void setSize(int x, int y) {
 		super.setSize(x, y);
-		int trackHeight = (axis == Axis.HORIZONTAL ? x : y) - getThumbWidth() + 1;
+		int trackHeight = (axis == Axis.HORIZONTAL ? x : y) - getThumbWidth();
 		valueToCoordRatio = (float) (max - min) / trackHeight;
 		coordToValueRatio = 1 / valueToCoordRatio;
 	}
@@ -108,6 +114,7 @@ public abstract class WAbstractSlider extends WWidget {
 	@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) {
@@ -115,36 +122,31 @@ public abstract class WAbstractSlider extends WWidget {
 		int rawValue = min + Math.round(valueToCoordRatio * pos);
 		int previousValue = value;
 		value = MathHelper.clamp(rawValue, min, max);
-		if (value != previousValue && valueChangeListener != null) valueChangeListener.accept(value);
+		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);
 	}
 
-	@Override
-	public void onFocusLost() {
-		if (focusReleaseListener != null) focusReleaseListener.run();
-	}
-
 	public int getValue() {
 		return value;
 	}
 
 	public void setValue(int value) {
 		this.value = value;
+		onValueChanged(value);
 	}
 
-	public WAbstractSlider setValueChangeListener(@Nullable IntConsumer valueChangeListener) {
+	public void setValueChangeListener(@Nullable IntConsumer valueChangeListener) {
 		this.valueChangeListener = valueChangeListener;
-		return this;
 	}
 
-	public WAbstractSlider setFocusReleaseListener(@Nullable Runnable focusReleaseListener) {
-		this.focusReleaseListener = focusReleaseListener;
-		return this;
+	public void setDraggingFinishedListener(@Nullable Runnable draggingFinishedListener) {
+		this.draggingFinishedListener = draggingFinishedListener;
 	}
 
 	public int getMinValue() {
@@ -159,27 +161,50 @@ public abstract class WAbstractSlider extends WWidget {
 		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 ((ch == GLFW.GLFW_KEY_LEFT || ch == GLFW.GLFW_KEY_DOWN) && value > min) {
+			if (isDecreasingKey(ch) && value > min) {
 				value--;
 				valueChanged = true;
-			} else if ((ch == GLFW.GLFW_KEY_RIGHT || ch == GLFW.GLFW_KEY_UP) && value < max) {
+			} else if (isIncreasingKey(ch) && 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) {
+			if (isDecreasingKey(ch) && value != min) {
 				value = min;
 				valueChanged = true;
-			} else if ((ch == GLFW.GLFW_KEY_RIGHT || ch == GLFW.GLFW_KEY_UP) && value != max) {
+			} else if (isIncreasingKey(ch) && value != max) {
 				value = max;
 				valueChanged = true;
 			}
 		}
 
-		if (valueChanged && valueChangeListener != null) valueChangeListener.accept(value);
+		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
index 77a3325..37fe464 100644
--- a/src/main/java/io/github/cottonmc/cotton/gui/widget/WLabeledSlider.java
+++ b/src/main/java/io/github/cottonmc/cotton/gui/widget/WLabeledSlider.java
@@ -4,38 +4,62 @@ 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;
+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
  */
-/*
-	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;
+	@Nullable private Text label = null;
+	@Nullable private LabelUpdater labelUpdater = null;
 
 	public WLabeledSlider(int min, int max) {
 		super(min, max, Axis.HORIZONTAL);
 	}
 
-	public WLabeledSlider(int max) {
-		this(0, max);
+	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 6;
+		return 8;
 	}
 
 	@Override
@@ -49,16 +73,23 @@ public class WLabeledSlider extends WAbstractSlider {
 		drawButton(x, y, 0, width);
 
 		// 1: regular, 2: hovered, 0: disabled/dragging
-		int thumbX = (int) (coordToValueRatio * (value - min));
+		int thumbX = Math.round(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);
+		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()) {
-			// TODO: draw the focus border
+			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);
 		}
 	}
 
@@ -77,4 +108,9 @@ public class WLabeledSlider extends WAbstractSlider {
 		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
index 14e9972..c57b705 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
@@ -9,19 +9,9 @@ import net.minecraft.util.Identifier;
 import javax.annotation.Nullable;
 
 /**
- * A slider widget that can be used to select int values.
+ * A simple slider widget 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>
+ * @see WAbstractSlider for supported listeners
  */
 public class WSlider extends WAbstractSlider {
 	public static final int TRACK_WIDTH = 6;
@@ -78,7 +68,7 @@ public class WSlider extends WAbstractSlider {
 				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 = (int) (coordToValueRatio * (value - min));
+				thumbX = Math.round(coordToValueRatio * (value - min));
 				thumbY = height / 2 - THUMB_SIZE / 2;
 				thumbXOffset = 8;
 
diff --git a/src/main/resources/assets/libgui/textures/widget/slider.png b/src/main/resources/assets/libgui/textures/widget/slider.png
index ce8f434..e41250e 100644
Binary files a/src/main/resources/assets/libgui/textures/widget/slider.png and b/src/main/resources/assets/libgui/textures/widget/slider.png differ
-- 
cgit