From 3632ccf292634fa785982a1d61e8cb8647a497e0 Mon Sep 17 00:00:00 2001 From: Juuxel Date: Wed, 28 Aug 2019 22:19:44 +0300 Subject: Add WAbstractSlider and do some initial code for labeled sliders --- .../cotton/gui/client/modmenu/ConfigGui.java | 5 +- .../cotton/gui/widget/WAbstractSlider.java | 185 +++++++++++++++++++++ .../cottonmc/cotton/gui/widget/WLabeledSlider.java | 80 +++++++++ .../github/cottonmc/cotton/gui/widget/WSlider.java | 147 +--------------- 4 files changed, 274 insertions(+), 143 deletions(-) create mode 100644 src/main/java/io/github/cottonmc/cotton/gui/widget/WAbstractSlider.java create mode 100644 src/main/java/io/github/cottonmc/cotton/gui/widget/WLabeledSlider.java 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. + * + *

You can set two listeners on a slider: + *

+ */ +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; * * */ -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); - } } -- cgit