diff options
author | xander <xander@isxander.dev> | 2022-09-02 22:04:35 +0100 |
---|---|---|
committer | xander <xander@isxander.dev> | 2022-09-02 22:04:35 +0100 |
commit | 7f88ddf36ba7804211e8aae6b3723bd8f37c82c5 (patch) | |
tree | 791e5cb050aa1ea17bd86d0bdd64fa441b2ab123 /src | |
parent | dd24e4f8e84ed02913196c8ad6093275acc92219 (diff) | |
download | YetAnotherConfigLib-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')
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 Binary files differdeleted file mode 100644 index 3f6dae4..0000000 --- a/src/main/resources/icon.png +++ /dev/null diff --git a/src/main/resources/yacl-128x.png b/src/main/resources/yacl-128x.png Binary files differnew file mode 100644 index 0000000..c86981c --- /dev/null +++ b/src/main/resources/yacl-128x.png 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; |