aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorxander <xander@isxander.dev>2022-09-02 22:04:35 +0100
committerxander <xander@isxander.dev>2022-09-02 22:04:35 +0100
commit7f88ddf36ba7804211e8aae6b3723bd8f37c82c5 (patch)
tree791e5cb050aa1ea17bd86d0bdd64fa441b2ab123 /src
parentdd24e4f8e84ed02913196c8ad6093275acc92219 (diff)
downloadYetAnotherConfigLib-7f88ddf36ba7804211e8aae6b3723bd8f37c82c5.tar.gz
YetAnotherConfigLib-7f88ddf36ba7804211e8aae6b3723bd8f37c82c5.tar.bz2
YetAnotherConfigLib-7f88ddf36ba7804211e8aae6b3723bd8f37c82c5.zip
implement keyboard-operation for all controllers
fix crash when focusing on option list with tab add tooltip to groups check that pending values actually applied on save, if not, error in log and the save button displays an error when trying to escape with unsaved changes, save button text goes green and bold YACLConstants file to change certain behaviours, could evolve into its own settings! update icon
Diffstat (limited to 'src')
-rw-r--r--src/main/java/dev/isxander/yacl/api/ConfigCategory.java2
-rw-r--r--src/main/java/dev/isxander/yacl/api/OptionGroup.java32
-rw-r--r--src/main/java/dev/isxander/yacl/gui/CategoryWidget.java3
-rw-r--r--src/main/java/dev/isxander/yacl/gui/OptionListWidget.java60
-rw-r--r--src/main/java/dev/isxander/yacl/gui/YACLScreen.java39
-rw-r--r--src/main/java/dev/isxander/yacl/gui/controllers/ActionController.java20
-rw-r--r--src/main/java/dev/isxander/yacl/gui/controllers/BooleanController.java20
-rw-r--r--src/main/java/dev/isxander/yacl/gui/controllers/ControllerWidget.java20
-rw-r--r--src/main/java/dev/isxander/yacl/gui/controllers/EnumController.java35
-rw-r--r--src/main/java/dev/isxander/yacl/gui/controllers/TickBoxController.java21
-rw-r--r--src/main/java/dev/isxander/yacl/gui/controllers/slider/SliderControllerElement.java26
-rw-r--r--src/main/java/dev/isxander/yacl/impl/OptionGroupImpl.java4
-rw-r--r--src/main/java/dev/isxander/yacl/impl/OptionImpl.java4
-rw-r--r--src/main/java/dev/isxander/yacl/impl/YACLConstants.java23
-rw-r--r--src/main/resources/assets/yet-another-config-lib/lang/en_us.json3
-rw-r--r--src/main/resources/fabric.mod.json2
-rw-r--r--src/main/resources/icon.pngbin17130 -> 0 bytes
-rw-r--r--src/main/resources/yacl-128x.pngbin0 -> 13813 bytes
-rw-r--r--src/testmod/java/dev/isxander/yacl/test/ModMenuIntegration.java155
19 files changed, 362 insertions, 107 deletions
diff --git a/src/main/java/dev/isxander/yacl/api/ConfigCategory.java b/src/main/java/dev/isxander/yacl/api/ConfigCategory.java
index 5aafc62..9f2f954 100644
--- a/src/main/java/dev/isxander/yacl/api/ConfigCategory.java
+++ b/src/main/java/dev/isxander/yacl/api/ConfigCategory.java
@@ -138,7 +138,7 @@ public interface ConfigCategory {
Validate.notNull(name, "`name` must not be null to build `ConfigCategory`");
List<OptionGroup> combinedGroups = new ArrayList<>();
- combinedGroups.add(new OptionGroupImpl(Text.empty(), ImmutableList.copyOf(rootOptions), true));
+ combinedGroups.add(new OptionGroupImpl(Text.empty(), Text.empty(), ImmutableList.copyOf(rootOptions), true));
combinedGroups.addAll(groups);
Validate.notEmpty(combinedGroups, "at least one option must be added to build `ConfigCategory`");
diff --git a/src/main/java/dev/isxander/yacl/api/OptionGroup.java b/src/main/java/dev/isxander/yacl/api/OptionGroup.java
index 6a302c4..9376b8e 100644
--- a/src/main/java/dev/isxander/yacl/api/OptionGroup.java
+++ b/src/main/java/dev/isxander/yacl/api/OptionGroup.java
@@ -2,6 +2,7 @@ package dev.isxander.yacl.api;
import com.google.common.collect.ImmutableList;
import dev.isxander.yacl.impl.OptionGroupImpl;
+import net.minecraft.text.MutableText;
import net.minecraft.text.Text;
import org.apache.commons.lang3.Validate;
import org.jetbrains.annotations.NotNull;
@@ -23,6 +24,11 @@ public interface OptionGroup {
Text name();
/**
+ * Tooltip displayed on hover.
+ */
+ Text tooltip();
+
+ /**
* List of all options in the group
*/
@NotNull ImmutableList<Option<?>> options();
@@ -42,6 +48,7 @@ public interface OptionGroup {
class Builder {
private Text name = Text.empty();
+ private final List<Text> tooltipLines = new ArrayList<>();
private final List<Option<?>> options = new ArrayList<>();
private Builder() {
@@ -61,6 +68,20 @@ public interface OptionGroup {
}
/**
+ * Sets the tooltip to be used by the option group.
+ * Can be invoked twice to append more lines.
+ * No need to wrap the text yourself, the gui does this itself.
+ *
+ * @param tooltips text lines - merged with a new-line on {@link Builder#build()}.
+ */
+ public Builder tooltip(@NotNull Text... tooltips) {
+ Validate.notEmpty(tooltips, "`tooltips` cannot be empty");
+
+ tooltipLines.addAll(List.of(tooltips));
+ return this;
+ }
+
+ /**
* Adds an option to group.
* To construct an option, use {@link Option#createBuilder(Class)}
*
@@ -89,7 +110,16 @@ public interface OptionGroup {
public OptionGroup build() {
Validate.notEmpty(options, "`options` must not be empty to build `OptionGroup`");
- return new OptionGroupImpl(name, ImmutableList.copyOf(options), false);
+ MutableText concatenatedTooltip = Text.empty();
+ boolean first = true;
+ for (Text line : tooltipLines) {
+ if (!first) concatenatedTooltip.append("\n");
+ first = false;
+
+ concatenatedTooltip.append(line);
+ }
+
+ return new OptionGroupImpl(name, concatenatedTooltip, ImmutableList.copyOf(options), false);
}
}
}
diff --git a/src/main/java/dev/isxander/yacl/gui/CategoryWidget.java b/src/main/java/dev/isxander/yacl/gui/CategoryWidget.java
index f4a50e8..04c4c22 100644
--- a/src/main/java/dev/isxander/yacl/gui/CategoryWidget.java
+++ b/src/main/java/dev/isxander/yacl/gui/CategoryWidget.java
@@ -1,6 +1,7 @@
package dev.isxander.yacl.gui;
import dev.isxander.yacl.api.ConfigCategory;
+import dev.isxander.yacl.impl.YACLConstants;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.gui.widget.ButtonWidget;
import net.minecraft.client.util.math.MatrixStack;
@@ -22,7 +23,7 @@ public class CategoryWidget extends ButtonWidget {
public void render(MatrixStack matrices, int mouseX, int mouseY, float delta) {
super.render(matrices, mouseX, mouseY, delta);
- if (isHovered() && prevMouseX == mouseX && prevMouseY == mouseY) {
+ if (isHovered() && (!YACLConstants.HOVER_MOUSE_RESET || (prevMouseX == mouseX && prevMouseY == mouseY))) {
hoveredTicks += delta;
} else {
hoveredTicks = 0;
diff --git a/src/main/java/dev/isxander/yacl/gui/OptionListWidget.java b/src/main/java/dev/isxander/yacl/gui/OptionListWidget.java
index 8cb1160..1f118cc 100644
--- a/src/main/java/dev/isxander/yacl/gui/OptionListWidget.java
+++ b/src/main/java/dev/isxander/yacl/gui/OptionListWidget.java
@@ -1,17 +1,24 @@
package dev.isxander.yacl.gui;
+import com.google.common.collect.ImmutableList;
import dev.isxander.yacl.api.ConfigCategory;
import dev.isxander.yacl.api.Option;
import dev.isxander.yacl.api.OptionGroup;
import dev.isxander.yacl.api.utils.Dimension;
import dev.isxander.yacl.gui.controllers.ControllerWidget;
+import dev.isxander.yacl.impl.YACLConstants;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.font.TextRenderer;
import net.minecraft.client.gui.Element;
import net.minecraft.client.gui.Selectable;
+import net.minecraft.client.gui.screen.Screen;
+import net.minecraft.client.gui.screen.narration.NarrationMessageBuilder;
+import net.minecraft.client.gui.screen.narration.NarrationPart;
import net.minecraft.client.gui.widget.ElementListWidget;
import net.minecraft.client.util.math.MatrixStack;
+import net.minecraft.text.OrderedText;
+import java.util.Collections;
import java.util.List;
public class OptionListWidget extends ElementListWidget<OptionListWidget.Entry> {
@@ -23,7 +30,7 @@ public class OptionListWidget extends ElementListWidget<OptionListWidget.Entry>
for (OptionGroup group : category.groups()) {
if (!group.isRoot())
- addEntry(new GroupSeparatorEntry(group));
+ addEntry(new GroupSeparatorEntry(group, screen));
for (Option<?> option : group.options()) {
addEntry(new OptionEntry(option.controller().provideWidget(screen, null)));
}
@@ -45,6 +52,13 @@ public class OptionListWidget extends ElementListWidget<OptionListWidget.Entry>
return left + super.getScrollbarPositionX();
}
+ @Override
+ protected void renderBackground(MatrixStack matrices) {
+ setRenderBackground(client.world == null);
+ if (client.world != null)
+ fill(matrices, left, top, right, bottom, 0x6B000000);
+ }
+
public static abstract class Entry extends ElementListWidget.Entry<Entry> {
}
@@ -70,36 +84,68 @@ public class OptionListWidget extends ElementListWidget<OptionListWidget.Entry>
@Override
public List<? extends Selectable> selectableChildren() {
- return List.of(widget);
+ return ImmutableList.of(widget);
}
@Override
public List<? extends Element> children() {
- return List.of(widget);
+ return ImmutableList.of(widget);
}
}
private static class GroupSeparatorEntry extends Entry {
private final OptionGroup group;
+ private final List<OrderedText> wrappedTooltip;
- public GroupSeparatorEntry(OptionGroup group) {
+ private float hoveredTicks = 0;
+ private int prevMouseX, prevMouseY;
+
+ private final Screen screen;
+ private final TextRenderer textRenderer = MinecraftClient.getInstance().textRenderer;
+
+ public GroupSeparatorEntry(OptionGroup group, Screen screen) {
this.group = group;
+ this.screen = screen;
+ this.wrappedTooltip = textRenderer.wrapLines(group.tooltip(), screen.width / 2);
}
@Override
public void render(MatrixStack matrices, int index, int y, int x, int entryWidth, int entryHeight, int mouseX, int mouseY, boolean hovered, float tickDelta) {
- TextRenderer textRenderer = MinecraftClient.getInstance().textRenderer;
+ if (hovered && (!YACLConstants.HOVER_MOUSE_RESET || (mouseX == prevMouseX && mouseY == prevMouseY)))
+ hoveredTicks += tickDelta;
+ else
+ hoveredTicks = 0;
+
drawCenteredText(matrices, textRenderer, group.name(), x + entryWidth / 2, y + entryHeight / 2 - textRenderer.fontHeight / 2, -1);
+
+ if (hoveredTicks >= YACLConstants.HOVER_TICKS) {
+ screen.renderOrderedTooltip(matrices, wrappedTooltip, x - 6, y + entryHeight);
+ }
+
+ prevMouseX = mouseX;
+ prevMouseY = mouseY;
}
@Override
public List<? extends Selectable> selectableChildren() {
- return List.of();
+ return ImmutableList.of(new Selectable() {
+ @Override
+ public Selectable.SelectionType getType() {
+ return Selectable.SelectionType.HOVERED;
+ }
+
+ @Override
+ public void appendNarrations(NarrationMessageBuilder builder) {
+ builder.put(NarrationPart.TITLE, group.name());
+ }
+ });
}
@Override
public List<? extends Element> children() {
- return List.of();
+ return Collections.emptyList();
}
+
+
}
}
diff --git a/src/main/java/dev/isxander/yacl/gui/YACLScreen.java b/src/main/java/dev/isxander/yacl/gui/YACLScreen.java
index 6ecbcc7..6da1c29 100644
--- a/src/main/java/dev/isxander/yacl/gui/YACLScreen.java
+++ b/src/main/java/dev/isxander/yacl/gui/YACLScreen.java
@@ -5,10 +5,12 @@ import dev.isxander.yacl.api.Option;
import dev.isxander.yacl.api.YetAnotherConfigLib;
import dev.isxander.yacl.api.utils.Dimension;
import dev.isxander.yacl.api.utils.OptionUtils;
+import dev.isxander.yacl.impl.YACLConstants;
import net.minecraft.client.gui.screen.Screen;
import net.minecraft.client.gui.widget.ButtonWidget;
import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.text.Text;
+import net.minecraft.util.Formatting;
import java.util.ArrayList;
import java.util.List;
@@ -24,6 +26,10 @@ public class YACLScreen extends Screen {
public final List<CategoryWidget> categoryButtons;
public ButtonWidget finishedSaveButton, cancelResetButton, undoButton;
+ public Text saveButtonMessage;
+ private int saveButtonMessageTime;
+
+
public YACLScreen(YetAnotherConfigLib config, Screen parent) {
super(config.title());
this.config = config;
@@ -57,8 +63,16 @@ public class YACLScreen extends Screen {
Dimension<Integer> actionDim = Dimension.ofInt(padding, height - padding - 20, columnWidth - padding * 2, 20);
finishedSaveButton = new ButtonWidget(actionDim.x(), actionDim.y(), actionDim.width(), actionDim.height(), Text.empty(), (btn) -> {
+ saveButtonMessage = null;
+
if (pendingChanges()) {
OptionUtils.forEachOptions(config, Option::applyValue);
+ OptionUtils.forEachOptions(config, option -> {
+ if (option.changed()) {
+ YACLConstants.LOGGER.error("Option '{}' was saved as '{}' but the changes don't seem to have applied.", option.name().getString(), option.pendingValue());
+ setSaveButtonMessage(Text.translatable("yocl.gui.fail_apply").formatted(Formatting.RED));
+ }
+ });
config.saveFunction().run();
} else close();
});
@@ -78,9 +92,9 @@ public class YACLScreen extends Screen {
});
updateActionAvailability();
- addDrawableChild(finishedSaveButton);
addDrawableChild(cancelResetButton);
addDrawableChild(undoButton);
+ addDrawableChild(finishedSaveButton);
ConfigCategory currentCategory = config.categories().get(currentCategoryIdx);
optionList = new OptionListWidget(currentCategory, this, client, width, height);
@@ -98,7 +112,7 @@ public class YACLScreen extends Screen {
optionList.render(matrices, mouseX, mouseY, delta);
for (CategoryWidget categoryWidget : categoryButtons) {
- if (categoryWidget.hoveredTicks > 30) {
+ if (categoryWidget.hoveredTicks > YACLConstants.HOVER_TICKS) {
renderOrderedTooltip(matrices, categoryWidget.wrappedDescription, mouseX, mouseY);
}
}
@@ -107,6 +121,21 @@ public class YACLScreen extends Screen {
@Override
public void tick() {
updateActionAvailability();
+
+ if (saveButtonMessage != null) {
+ if (saveButtonMessageTime > 140) {
+ saveButtonMessage = null;
+ saveButtonMessageTime = 0;
+ } else {
+ saveButtonMessageTime++;
+ finishedSaveButton.setMessage(saveButtonMessage);
+ }
+ }
+ }
+
+ private void setSaveButtonMessage(Text message) {
+ saveButtonMessage = message;
+ saveButtonMessageTime = 0;
}
public void changeCategory(int idx) {
@@ -146,7 +175,11 @@ public class YACLScreen extends Screen {
@Override
public boolean shouldCloseOnEsc() {
- return !undoButton.active;
+ if (pendingChanges()) {
+ setSaveButtonMessage(finishedSaveButton.getMessage().copy().formatted(Formatting.GREEN, Formatting.BOLD));
+ return false;
+ }
+ return true;
}
@Override
diff --git a/src/main/java/dev/isxander/yacl/gui/controllers/ActionController.java b/src/main/java/dev/isxander/yacl/gui/controllers/ActionController.java
index 92ef3d5..d198c96 100644
--- a/src/main/java/dev/isxander/yacl/gui/controllers/ActionController.java
+++ b/src/main/java/dev/isxander/yacl/gui/controllers/ActionController.java
@@ -6,6 +6,7 @@ import dev.isxander.yacl.api.utils.Dimension;
import dev.isxander.yacl.gui.YACLScreen;
import net.minecraft.text.Text;
import org.jetbrains.annotations.ApiStatus;
+import org.lwjgl.glfw.GLFW;
import java.util.function.Consumer;
@@ -71,17 +72,32 @@ public class ActionController implements Controller<Consumer<YACLScreen>> {
super(control, screen, dim);
}
+ public void executeAction() {
+ playDownSound();
+ control.option().action().accept(screen);
+ }
+
@Override
public boolean mouseClicked(double mouseX, double mouseY, int button) {
if (isMouseOver(mouseX, mouseY)) {
- playDownSound();
- control.option().action().accept(screen);
+ executeAction();
return true;
}
return false;
}
@Override
+ public boolean keyPressed(int keyCode, int scanCode, int modifiers) {
+ if (keyCode != GLFW.GLFW_KEY_ENTER && keyCode != GLFW.GLFW_KEY_SPACE && keyCode != GLFW.GLFW_KEY_KP_ENTER) {
+ return false;
+ }
+
+ executeAction();
+
+ return true;
+ }
+
+ @Override
protected int getHoveredControlWidth() {
return getUnhoveredControlWidth();
}
diff --git a/src/main/java/dev/isxander/yacl/gui/controllers/BooleanController.java b/src/main/java/dev/isxander/yacl/gui/controllers/BooleanController.java
index 6bf0b80..ac8754f 100644
--- a/src/main/java/dev/isxander/yacl/gui/controllers/BooleanController.java
+++ b/src/main/java/dev/isxander/yacl/gui/controllers/BooleanController.java
@@ -9,6 +9,7 @@ import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.text.Text;
import net.minecraft.util.Formatting;
import org.jetbrains.annotations.ApiStatus;
+import org.lwjgl.glfw.GLFW;
import java.util.function.Function;
@@ -117,8 +118,7 @@ public class BooleanController implements Controller<Boolean> {
if (!isMouseOver(mouseX, mouseY))
return false;
- control.option().requestSet(!control.option().pendingValue());
- playDownSound();
+ toggleSetting();
return true;
}
@@ -127,6 +127,11 @@ public class BooleanController implements Controller<Boolean> {
return getUnhoveredControlWidth();
}
+ public void toggleSetting() {
+ control.option().requestSet(!control.option().pendingValue());
+ playDownSound();
+ }
+
@Override
protected Text getValueText() {
if (control.coloured()) {
@@ -135,5 +140,16 @@ public class BooleanController implements Controller<Boolean> {
return super.getValueText();
}
+
+ @Override
+ public boolean keyPressed(int keyCode, int scanCode, int modifiers) {
+ if (keyCode != GLFW.GLFW_KEY_ENTER && keyCode != GLFW.GLFW_KEY_SPACE && keyCode != GLFW.GLFW_KEY_KP_ENTER) {
+ return false;
+ }
+
+ toggleSetting();
+
+ return true;
+ }
}
}
diff --git a/src/main/java/dev/isxander/yacl/gui/controllers/ControllerWidget.java b/src/main/java/dev/isxander/yacl/gui/controllers/ControllerWidget.java
index 77ac9ca..d712e56 100644
--- a/src/main/java/dev/isxander/yacl/gui/controllers/ControllerWidget.java
+++ b/src/main/java/dev/isxander/yacl/gui/controllers/ControllerWidget.java
@@ -4,6 +4,7 @@ import dev.isxander.yacl.api.Controller;
import dev.isxander.yacl.api.utils.Dimension;
import dev.isxander.yacl.gui.AbstractWidget;
import dev.isxander.yacl.gui.YACLScreen;
+import dev.isxander.yacl.impl.YACLConstants;
import net.minecraft.client.gui.DrawableHelper;
import net.minecraft.client.gui.screen.narration.NarrationMessageBuilder;
import net.minecraft.client.util.math.MatrixStack;
@@ -19,6 +20,7 @@ public abstract class ControllerWidget<T extends Controller<?>> extends Abstract
protected Dimension<Integer> dim;
protected final YACLScreen screen;
+ protected boolean focused = false;
protected boolean hovered = false;
protected float hoveredTicks = 0;
@@ -34,7 +36,7 @@ public abstract class ControllerWidget<T extends Controller<?>> extends Abstract
@Override
public void render(MatrixStack matrices, int mouseX, int mouseY, float delta) {
hovered = isMouseOver(mouseX, mouseY);
- if (hovered && mouseX == prevMouseX && mouseY == prevMouseY) {
+ if (hovered && (!YACLConstants.HOVER_MOUSE_RESET || (mouseX == prevMouseX && mouseY == prevMouseY))) {
hoveredTicks += delta;
} else {
hoveredTicks = 0;
@@ -53,18 +55,18 @@ public abstract class ControllerWidget<T extends Controller<?>> extends Abstract
Text shortenedName = Text.literal(nameString).fillStyle(name.getStyle());
- drawButtonRect(matrices, dim.x(), dim.y(), dim.xLimit(), dim.yLimit(), hovered);
+ drawButtonRect(matrices, dim.x(), dim.y(), dim.xLimit(), dim.yLimit(), hovered || focused);
matrices.push();
matrices.translate(dim.x() + getXPadding(), getTextY(), 0);
textRenderer.drawWithShadow(matrices, shortenedName, 0, 0, -1);
matrices.pop();
drawValueText(matrices, mouseX, mouseY, delta);
- if (hovered) {
+ if (hovered || focused) {
drawHoveredControl(matrices, mouseX, mouseY, delta);
}
- if (hoveredTicks > 30) {
+ if (hoveredTicks > YACLConstants.HOVER_TICKS) {
screen.renderOrderedTooltip(matrices, wrappedTooltip, mouseX, mouseY);
}
@@ -90,7 +92,7 @@ public abstract class ControllerWidget<T extends Controller<?>> extends Abstract
}
protected int getControlWidth() {
- return hovered ? getHoveredControlWidth() : getUnhoveredControlWidth();
+ return hovered || focused ? getHoveredControlWidth() : getUnhoveredControlWidth();
}
protected abstract int getHoveredControlWidth();
@@ -127,8 +129,14 @@ public abstract class ControllerWidget<T extends Controller<?>> extends Abstract
}
@Override
+ public boolean changeFocus(boolean lookForwards) {
+ this.focused = !this.focused;
+ return this.focused;
+ }
+
+ @Override
public SelectionType getType() {
- return hovered ? SelectionType.HOVERED : SelectionType.NONE;
+ return focused ? SelectionType.FOCUSED : hovered ? SelectionType.HOVERED : SelectionType.NONE;
}
@Override
diff --git a/src/main/java/dev/isxander/yacl/gui/controllers/EnumController.java b/src/main/java/dev/isxander/yacl/gui/controllers/EnumController.java
index f4c4006..af52255 100644
--- a/src/main/java/dev/isxander/yacl/gui/controllers/EnumController.java
+++ b/src/main/java/dev/isxander/yacl/gui/controllers/EnumController.java
@@ -8,6 +8,7 @@ import dev.isxander.yacl.gui.YACLScreen;
import net.minecraft.client.gui.screen.Screen;
import net.minecraft.text.Text;
import org.jetbrains.annotations.ApiStatus;
+import org.lwjgl.glfw.GLFW;
import java.util.function.Function;
@@ -85,21 +86,41 @@ public class EnumController<T extends Enum<T>> implements Controller<T> {
this.values = values;
}
+ public void cycleValue(int increment) {
+ int targetIdx = control.option().pendingValue().ordinal() + increment;
+ if (targetIdx >= values.length) {
+ targetIdx -= values.length;
+ } else if (targetIdx < 0) {
+ targetIdx += values.length;
+ }
+ control.option().requestSet(values[targetIdx]);
+ }
+
@Override
public boolean mouseClicked(double mouseX, double mouseY, int button) {
if (!isMouseOver(mouseX, mouseY) || (button != 0 && button != 1))
return false;
playDownSound();
+ cycleValue(button == 1 || Screen.hasShiftDown() || Screen.hasControlDown() ? -1 : 1);
- int change = button == 1 || Screen.hasShiftDown() ? -1 : 1;
- int targetIdx = control.option().pendingValue().ordinal() + change;
- if (targetIdx >= values.length) {
- targetIdx -= values.length;
- } else if (targetIdx < 0) {
- targetIdx += values.length;
+ return true;
+ }
+
+ @Override
+ public boolean keyPressed(int keyCode, int scanCode, int modifiers) {
+ switch (keyCode) {
+ case GLFW.GLFW_KEY_LEFT, GLFW.GLFW_KEY_DOWN ->
+ cycleValue(-1);
+ case GLFW.GLFW_KEY_RIGHT, GLFW.GLFW_KEY_UP ->
+ cycleValue(1);
+ case GLFW.GLFW_KEY_ENTER, GLFW.GLFW_KEY_SPACE, GLFW.GLFW_KEY_KP_ENTER ->
+ cycleValue(Screen.hasControlDown() || Screen.hasShiftDown() ? -1 : 1);
+ default -> {
+ return false;
+ }
}
- control.option().requestSet(values[targetIdx]);
+
return true;
}
diff --git a/src/main/java/dev/isxander/yacl/gui/controllers/TickBoxController.java b/src/main/java/dev/isxander/yacl/gui/controllers/TickBoxController.java
index 1d78c78..a1c0095 100644
--- a/src/main/java/dev/isxander/yacl/gui/controllers/TickBoxController.java
+++ b/src/main/java/dev/isxander/yacl/gui/controllers/TickBoxController.java
@@ -5,10 +5,10 @@ import dev.isxander.yacl.api.Option;
import dev.isxander.yacl.api.utils.Dimension;
import dev.isxander.yacl.gui.YACLScreen;
import net.minecraft.client.gui.DrawableHelper;
-import net.minecraft.client.gui.screen.Screen;
import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.text.Text;
import org.jetbrains.annotations.ApiStatus;
+import org.lwjgl.glfw.GLFW;
/**
* This controller renders a tickbox
@@ -81,8 +81,7 @@ public class TickBoxController implements Controller<Boolean> {
if (!isMouseOver(mouseX, mouseY))
return false;
- control.option().requestSet(!control.option().pendingValue());
- playDownSound();
+ toggleSetting();
return true;
}
@@ -90,5 +89,21 @@ public class TickBoxController implements Controller<Boolean> {
protected int getHoveredControlWidth() {
return 10;
}
+
+ public void toggleSetting() {
+ control.option().requestSet(!control.option().pendingValue());
+ playDownSound();
+ }
+
+ @Override
+ public boolean keyPressed(int keyCode, int scanCode, int modifiers) {
+ if (keyCode != GLFW.GLFW_KEY_ENTER && keyCode != GLFW.GLFW_KEY_SPACE && keyCode != GLFW.GLFW_KEY_KP_ENTER) {
+ return false;
+ }
+
+ toggleSetting();
+
+ return true;
+ }
}
}
diff --git a/src/main/java/dev/isxander/yacl/gui/controllers/slider/SliderControllerElement.java b/src/main/java/dev/isxander/yacl/gui/controllers/slider/SliderControllerElement.java
index 8af2433..a6f046a 100644
--- a/src/main/java/dev/isxander/yacl/gui/controllers/slider/SliderControllerElement.java
+++ b/src/main/java/dev/isxander/yacl/gui/controllers/slider/SliderControllerElement.java
@@ -8,6 +8,7 @@ import net.minecraft.client.gui.screen.Screen;
import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.util.math.MathHelper;
import org.jetbrains.annotations.ApiStatus;
+import org.lwjgl.glfw.GLFW;
@ApiStatus.Internal
public class SliderControllerElement extends ControllerWidget<ISliderController<?>> {
@@ -49,7 +50,7 @@ public class SliderControllerElement extends ControllerWidget<ISliderController<
@Override
protected void drawValueText(MatrixStack matrices, int mouseX, int mouseY, float delta) {
matrices.push();
- if (hovered)
+ if (hovered || focused)
matrices.translate(-(sliderBounds.width() + 6 + getThumbWidth() / 2f), 0, 0);
super.drawValueText(matrices, mouseX, mouseY, delta);
matrices.pop();
@@ -75,13 +76,17 @@ public class SliderControllerElement extends ControllerWidget<ISliderController<
return true;
}
+ public void incrementValue(double amount) {
+ control.setPendingValue(MathHelper.clamp(control.pendingValue() + interval * amount, min, max));
+ calculateInterpolation();
+ }
+
@Override
public boolean mouseScrolled(double mouseX, double mouseY, double amount) {
- if (!isMouseOver(mouseX, mouseY) || !Screen.hasShiftDown())
+ if ((!isMouseOver(mouseX, mouseY)) || (!Screen.hasShiftDown() && !Screen.hasControlDown()))
return false;
- control.setPendingValue(MathHelper.clamp(control.pendingValue() + interval * amount, min, max));
- calculateInterpolation();
+ incrementValue(amount);
return true;
}
@@ -95,6 +100,19 @@ public class SliderControllerElement extends ControllerWidget<ISliderController<
}
@Override
+ public boolean keyPressed(int keyCode, int scanCode, int modifiers) {
+ switch (keyCode) {
+ case GLFW.GLFW_KEY_LEFT, GLFW.GLFW_KEY_DOWN -> incrementValue(-1);
+ case GLFW.GLFW_KEY_RIGHT, GLFW.GLFW_KEY_UP -> incrementValue(1);
+ default -> {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ @Override
public boolean isMouseOver(double mouseX, double mouseY) {
return super.isMouseOver(mouseX, mouseY) || mouseDown;
}
diff --git a/src/main/java/dev/isxander/yacl/impl/OptionGroupImpl.java b/src/main/java/dev/isxander/yacl/impl/OptionGroupImpl.java
index 73bff07..1f2d4e2 100644
--- a/src/main/java/dev/isxander/yacl/impl/OptionGroupImpl.java
+++ b/src/main/java/dev/isxander/yacl/impl/OptionGroupImpl.java
@@ -4,7 +4,7 @@ import com.google.common.collect.ImmutableList;
import dev.isxander.yacl.api.Option;
import dev.isxander.yacl.api.OptionGroup;
import net.minecraft.text.Text;
-import org.jetbrains.annotations.Nullable;
+import org.jetbrains.annotations.NotNull;
-public record OptionGroupImpl(@Nullable Text name, ImmutableList<Option<?>> options, boolean isRoot) implements OptionGroup {
+public record OptionGroupImpl(@NotNull Text name, @NotNull Text tooltip, ImmutableList<Option<?>> options, boolean isRoot) implements OptionGroup {
}
diff --git a/src/main/java/dev/isxander/yacl/impl/OptionImpl.java b/src/main/java/dev/isxander/yacl/impl/OptionImpl.java
index c61eaa1..176bf93 100644
--- a/src/main/java/dev/isxander/yacl/impl/OptionImpl.java
+++ b/src/main/java/dev/isxander/yacl/impl/OptionImpl.java
@@ -69,7 +69,9 @@ public class OptionImpl<T> implements Option<T> {
@Override
public void applyValue() {
- binding().setValue(pendingValue);
+ if (changed()) {
+ binding().setValue(pendingValue);
+ }
}
@Override
diff --git a/src/main/java/dev/isxander/yacl/impl/YACLConstants.java b/src/main/java/dev/isxander/yacl/impl/YACLConstants.java
new file mode 100644
index 0000000..28d6a65
--- /dev/null
+++ b/src/main/java/dev/isxander/yacl/impl/YACLConstants.java
@@ -0,0 +1,23 @@
+package dev.isxander.yacl.impl;
+
+import org.jetbrains.annotations.ApiStatus;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class YACLConstants {
+ /**
+ * Logger used by YACL
+ */
+ @ApiStatus.Internal
+ public static final Logger LOGGER = LoggerFactory.getLogger("YetAnotherConfigLib");
+
+ /**
+ * Amount of ticks to hover before showing tooltips.
+ */
+ public static final int HOVER_TICKS = 20;
+
+ /**
+ * Reset hover ticks back to 0 when the mouse is moved.
+ */
+ public static final boolean HOVER_MOUSE_RESET = true;
+}
diff --git a/src/main/resources/assets/yet-another-config-lib/lang/en_us.json b/src/main/resources/assets/yet-another-config-lib/lang/en_us.json
index 7f609d3..78945de 100644
--- a/src/main/resources/assets/yet-another-config-lib/lang/en_us.json
+++ b/src/main/resources/assets/yet-another-config-lib/lang/en_us.json
@@ -12,5 +12,6 @@
"yacl.gui.finished": "Finished",
"yacl.gui.cancel": "Cancel",
"yacl.gui.reset": "Reset",
- "yacl.gui.undo": "Undo"
+ "yacl.gui.undo": "Undo",
+ "yocl.gui.fail_apply": "Failed to apply"
}
diff --git a/src/main/resources/fabric.mod.json b/src/main/resources/fabric.mod.json
index 6c28f3c..b40f7eb 100644
--- a/src/main/resources/fabric.mod.json
+++ b/src/main/resources/fabric.mod.json
@@ -12,7 +12,7 @@
"issues": "https://github.com/${github}/issues",
"sources": "https://github.com/${github}"
},
- "icon": "icon.png",
+ "icon": "yacl-128x.png",
"license": "LGPL-3.0-or-later",
"environment": "client",
"entrypoints": {
diff --git a/src/main/resources/icon.png b/src/main/resources/icon.png
deleted file mode 100644
index 3f6dae4..0000000
--- a/src/main/resources/icon.png
+++ /dev/null
Binary files differ
diff --git a/src/main/resources/yacl-128x.png b/src/main/resources/yacl-128x.png
new file mode 100644
index 0000000..c86981c
--- /dev/null
+++ b/src/main/resources/yacl-128x.png
Binary files differ
diff --git a/src/testmod/java/dev/isxander/yacl/test/ModMenuIntegration.java b/src/testmod/java/dev/isxander/yacl/test/ModMenuIntegration.java
index d1fad67..57d809b 100644
--- a/src/testmod/java/dev/isxander/yacl/test/ModMenuIntegration.java
+++ b/src/testmod/java/dev/isxander/yacl/test/ModMenuIntegration.java
@@ -27,74 +27,98 @@ public class ModMenuIntegration implements ModMenuApi {
.category(ConfigCategory.createBuilder()
.name(Text.of("Control Examples"))
.tooltip(Text.of("Example Category Description"))
- .option(Option.createBuilder(boolean.class)
- .name(Text.of("Boolean Toggle"))
- .binding(
- false,
- () -> TestSettings.booleanToggle,
- (value) -> TestSettings.booleanToggle = value
- )
- .controller(BooleanController::new)
- .build())
- .option(Option.createBuilder(boolean.class)
- .name(Text.of("Tick Box"))
- .tooltip(Text.of("Super long tooltip that is very descriptive to show off the text wrapping features of the thingy yes whwowwoow"))
- .binding(
- false,
- () -> TestSettings.tickbox,
- (value) -> TestSettings.tickbox = value
- )
- .controller(TickBoxController::new)
- .build())
- .option(Option.createBuilder(int.class)
- .name(Text.of("Int Slider that is cut off because the slider"))
- .binding(
- 0,
- () -> TestSettings.intSlider,
- (value) -> TestSettings.intSlider = value
- )
- .controller(opt -> new IntegerSliderController(opt, 0, 3, 1))
- .build())
- .option(Option.createBuilder(double.class)
- .name(Text.of("Double Slider"))
- .binding(
- 0.0,
- () -> TestSettings.doubleSlider,
- (value) -> TestSettings.doubleSlider = value
- )
- .controller(opt -> new DoubleSliderController(opt, 0, 3, 0.05))
- .build())
- .option(Option.createBuilder(float.class)
- .name(Text.of("Float Slider"))
- .binding(
- 0f,
- () -> TestSettings.floatSlider,
- (value) -> TestSettings.floatSlider = value
- )
- .controller(opt -> new FloatSliderController(opt, 0, 3, 0.1f))
+ .group(OptionGroup.createBuilder()
+ .name(Text.of("Boolean Controllers"))
+ .tooltip(Text.of("Test!"))
+ .option(Option.createBuilder(boolean.class)
+ .name(Text.of("Boolean Toggle"))
+ .tooltip(Text.of("A simple toggle button."))
+ .binding(
+ false,
+ () -> TestSettings.booleanToggle,
+ (value) -> TestSettings.booleanToggle = value
+ )
+ .controller(BooleanController::new)
+ .build())
+ .option(Option.createBuilder(boolean.class)
+ .name(Text.of("Custom Boolean Toggle"))
+ .tooltip(Text.of("You can customize these controllers like this!"))
+ .binding(
+ false,
+ () -> TestSettings.customBooleanToggle,
+ (value) -> TestSettings.customBooleanToggle = value
+ )
+ .controller(opt -> new BooleanController(opt, state -> state ? Text.of("Amazing") : Text.of("Not Amazing"), true))
+ .build())
+ .option(Option.createBuilder(boolean.class)
+ .name(Text.of("Tick Box"))
+ .tooltip(Text.of("There are even alternate methods of displaying the same data type!"))
+ .binding(
+ false,
+ () -> TestSettings.tickbox,
+ (value) -> TestSettings.tickbox = value
+ )
+ .controller(TickBoxController::new)
+ .build())
.build())
- .option(Option.createBuilder(long.class)
- .name(Text.of("Long Slider"))
- .binding(
- 0L,
- () -> TestSettings.longSlider,
- (value) -> TestSettings.longSlider = value
- )
- .controller(opt -> new LongSliderController(opt, 0, 1_000_000, 100))
+ .group(OptionGroup.createBuilder()
+ .name(Text.of("Slider Controllers"))
+ .option(Option.createBuilder(int.class)
+ .name(Text.of("Int Slider that is cut off because the slider"))
+ .binding(
+ 0,
+ () -> TestSettings.intSlider,
+ (value) -> TestSettings.intSlider = value
+ )
+ .controller(opt -> new IntegerSliderController(opt, 0, 3, 1))
+ .build())
+ .option(Option.createBuilder(double.class)
+ .name(Text.of("Double Slider"))
+ .binding(
+ 0.0,
+ () -> TestSettings.doubleSlider,
+ (value) -> TestSettings.doubleSlider = value
+ )
+ .controller(opt -> new DoubleSliderController(opt, 0, 3, 0.05))
+ .build())
+ .option(Option.createBuilder(float.class)
+ .name(Text.of("Float Slider"))
+ .binding(
+ 0f,
+ () -> TestSettings.floatSlider,
+ (value) -> TestSettings.floatSlider = value
+ )
+ .controller(opt -> new FloatSliderController(opt, 0, 3, 0.1f))
+ .build())
+ .option(Option.createBuilder(long.class)
+ .name(Text.of("Long Slider"))
+ .binding(
+ 0L,
+ () -> TestSettings.longSlider,
+ (value) -> TestSettings.longSlider = value
+ )
+ .controller(opt -> new LongSliderController(opt, 0, 1_000_000, 100))
+ .build())
.build())
- .option(Option.createBuilder(TestSettings.Alphabet.class)
- .name(Text.of("Enum Cycler"))
- .binding(
- TestSettings.Alphabet.A,
- () -> TestSettings.enumOption,
- (value) -> TestSettings.enumOption = value
- )
- .controller(opt -> new EnumController<>(opt, TestSettings.Alphabet.class))
+ .group(OptionGroup.createBuilder()
+ .name(Text.of("Enum Controllers"))
+ .option(Option.createBuilder(TestSettings.Alphabet.class)
+ .name(Text.of("Enum Cycler"))
+ .binding(
+ TestSettings.Alphabet.A,
+ () -> TestSettings.enumOption,
+ (value) -> TestSettings.enumOption = value
+ )
+ .controller(opt -> new EnumController<>(opt, TestSettings.Alphabet.class))
+ .build())
.build())
- .option(ButtonOption.createBuilder()
- .name(Text.of("Button \"Option\""))
- .action(screen -> SystemToast.add(MinecraftClient.getInstance().getToastManager(), SystemToast.Type.TUTORIAL_HINT, Text.of("Button Pressed"), Text.of("Button option was invoked!")))
- .controller(ActionController::new)
+ .group(OptionGroup.createBuilder()
+ .name(Text.of("Buttons!"))
+ .option(ButtonOption.createBuilder()
+ .name(Text.of("Button \"Option\""))
+ .action(screen -> SystemToast.add(MinecraftClient.getInstance().getToastManager(), SystemToast.Type.TUTORIAL_HINT, Text.of("Button Pressed"), Text.of("Button option was invoked!")))
+ .controller(ActionController::new)
+ .build())
.build())
.build())
.category(ConfigCategory.createBuilder()
@@ -289,6 +313,7 @@ public class ModMenuIntegration implements ModMenuApi {
private static class TestSettings {
private static boolean booleanToggle = false;
+ private static boolean customBooleanToggle = false;
private static boolean tickbox = false;
private static int intSlider = 0;
private static double doubleSlider = 0;