Skip to content

Commit 6f3dc40

Browse files
committed
lots of QOL and minor improvements
smooth category scrolling individual reset buttons for all controllers separate Dimension into Dimension and MutableDimension to prevent mods from modifying controller dimensions without invoking the hooks made the dimension field private in AbstractWidget so people can't modify it without the method setDimension new Option API method to check if pending value is equal to default value add documentation to ConfigInstance fix bug where Option#requestSetDefault and Option#forgetPendingValue implementations weren't notifying listeners
1 parent b9052c9 commit 6f3dc40

20 files changed

Lines changed: 229 additions & 63 deletions

src/main/java/dev/isxander/yacl/api/Controller.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
import dev.isxander.yacl.gui.AbstractWidget;
55
import dev.isxander.yacl.gui.YACLScreen;
66
import net.minecraft.text.Text;
7-
import org.jetbrains.annotations.ApiStatus;
87

98
/**
109
* Provides a widget to control the option.

src/main/java/dev/isxander/yacl/api/Option.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,11 @@ public interface Option<T> {
104104
*/
105105
void requestSetDefault();
106106

107+
/**
108+
* Checks if the current pending value is equal to its default value
109+
*/
110+
boolean isPendingValueDefault();
111+
107112
/**
108113
* Adds a listener for when the pending value changes
109114
*/

src/main/java/dev/isxander/yacl/api/utils/Dimension.java

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -17,25 +17,17 @@ public interface Dimension<T extends Number> {
1717

1818
boolean isPointInside(T x, T y);
1919

20-
Dimension<T> clone();
21-
22-
Dimension<T> setX(T x);
23-
Dimension<T> setY(T y);
24-
Dimension<T> setWidth(T width);
25-
Dimension<T> setHeight(T height);
20+
MutableDimension<T> clone();
2621

2722
Dimension<T> withX(T x);
2823
Dimension<T> withY(T y);
2924
Dimension<T> withWidth(T width);
3025
Dimension<T> withHeight(T height);
3126

32-
Dimension<T> move(T x, T y);
33-
Dimension<T> expand(T width, T height);
34-
3527
Dimension<T> moved(T x, T y);
3628
Dimension<T> expanded(T width, T height);
3729

38-
static Dimension<Integer> ofInt(int x, int y, int width, int height) {
30+
static MutableDimension<Integer> ofInt(int x, int y, int width, int height) {
3931
return new DimensionIntegerImpl(x, y, width, height);
4032
}
4133
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package dev.isxander.yacl.api.utils;
2+
3+
public interface MutableDimension<T extends Number> extends Dimension<T> {
4+
MutableDimension<T> setX(T x);
5+
MutableDimension<T> setY(T y);
6+
MutableDimension<T> setWidth(T width);
7+
MutableDimension<T> setHeight(T height);
8+
9+
MutableDimension<T> move(T x, T y);
10+
MutableDimension<T> expand(T width, T height);
11+
}

src/main/java/dev/isxander/yacl/config/ConfigInstance.java

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,16 @@
55
import java.lang.reflect.InvocationTargetException;
66
import java.util.function.BiFunction;
77

8+
/**
9+
* Responsible for handing the actual config data type.
10+
* Holds the instance along with a final default instance
11+
* to reference default values for options and should not be changed.
12+
*
13+
* Abstract methods to save and load the class, implementations are responsible for
14+
* how it saves and load.
15+
*
16+
* @param <T> config data type
17+
*/
818
public abstract class ConfigInstance<T> {
919
private final Class<T> configClass;
1020
private final T defaultInstance;
@@ -18,7 +28,6 @@ public ConfigInstance(Class<T> configClass) {
1828
} catch (InstantiationException | IllegalAccessException | NoSuchMethodException | InvocationTargetException e) {
1929
throw new IllegalStateException(String.format("Could not create default instance of config for %s. Make sure there is a default constructor!", this.configClass.getSimpleName()));
2030
}
21-
2231
}
2332

2433
public T getConfig() {

src/main/java/dev/isxander/yacl/config/YACLConfigManager.java

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,24 +3,58 @@
33
import java.util.HashMap;
44
import java.util.Map;
55

6+
/**
7+
* Simple storage of config instances ({@link ConfigInstance}) for ease of access.
8+
*/
69
@SuppressWarnings("unchecked")
710
public class YACLConfigManager {
811
private static final Map<Class<?>, ConfigInstance<?>> configs = new HashMap<>();
912

13+
/**
14+
* Registers and loads a config instance
15+
*
16+
* @param configInstance config to register
17+
* @param <T> config data type
18+
*/
1019
public static <T> void register(ConfigInstance<T> configInstance) {
1120
configs.put(configInstance.getConfigClass(), configInstance);
1221
configInstance.load();
1322
}
1423

24+
/**
25+
* Retrieves config data for a certain config.
26+
* <p>
27+
* Shorthand of {@code YACLConfigManager.getConfigInstance(configClass).getConfig()}
28+
*
29+
* @param configClass config data to get
30+
* @return config data
31+
* @param <T> config data type
32+
*/
1533
public static <T> T getConfigData(Class<T> configClass) {
1634
return ((ConfigInstance<T>) configs.get(configClass)).getConfig();
1735
}
1836

37+
/**
38+
* Retrieves the config instance for a certain config.
39+
*
40+
* @param configClass config data type instance is bound to
41+
* @return config instance
42+
* @param <T> config data type
43+
*/
1944
public static <T> ConfigInstance<T> getConfigInstance(Class<T> configClass) {
2045
return (ConfigInstance<T>) configs.get(configClass);
2146
}
2247

23-
public static <T, I extends ConfigInstance<T>> I getConfigInstanceType(Class<T> configClass) {
24-
return (I) configs.get(configClass);
48+
/**
49+
* Very similar to {@link YACLConfigManager#getConfigInstance(Class)} but can retrieve
50+
* a certain implementation of {@link ConfigInstance}
51+
*
52+
* @param configClass config data type is bound to
53+
* @return config instance
54+
* @param <T> config data type
55+
* @param <U> config instance type
56+
*/
57+
public static <T, U extends ConfigInstance<T>> U getConfigInstanceType(Class<T> configClass) {
58+
return (U) configs.get(configClass);
2559
}
2660
}

src/main/java/dev/isxander/yacl/gui/AbstractWidget.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ public abstract class AbstractWidget implements Element, Drawable, Selectable {
2222
protected final TextRenderer textRenderer = client.textRenderer;
2323
protected final int inactiveColor = 0xFFA0A0A0;
2424

25-
protected Dimension<Integer> dim;
25+
private Dimension<Integer> dim;
2626

2727
public AbstractWidget(Dimension<Integer> dim) {
2828
this.dim = dim;
@@ -32,6 +32,10 @@ public void postRender(MatrixStack matrices, int mouseX, int mouseY, float delta
3232

3333
}
3434

35+
public boolean canReset() {
36+
return false;
37+
}
38+
3539
@Override
3640
public boolean isMouseOver(double mouseX, double mouseY) {
3741
if (dim == null) return false;

src/main/java/dev/isxander/yacl/gui/OptionListWidget.java

Lines changed: 57 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,9 @@ public class OptionListWidget extends ElementListWidget<OptionListWidget.Entry>
2828

2929
private ImmutableList<Entry> viewableChildren;
3030

31+
private double smoothScrollAmount = getScrollAmount();
32+
private boolean returnSmoothAmount = false;
33+
3134
public OptionListWidget(YACLScreen screen, MinecraftClient client, int width, int height) {
3235
super(client, width / 3 * 2, height, 0, height, 22);
3336
this.yaclScreen = screen;
@@ -62,7 +65,7 @@ public void refreshOptions() {
6265

6366
List<OptionEntry> optionEntries = new ArrayList<>();
6467
for (Option<?> option : group.options()) {
65-
OptionEntry entry = new OptionEntry(category, group, option.controller().provideWidget(yaclScreen, Dimension.ofInt(getRowLeft(), 0, getRowWidth(), 20)), viewableSupplier);
68+
OptionEntry entry = new OptionEntry(option, category, group, option.controller().provideWidget(yaclScreen, Dimension.ofInt(getRowLeft(), 0, getRowWidth(), 20)), viewableSupplier);
6669
addEntry(entry);
6770
optionEntries.add(entry);
6871
}
@@ -148,14 +151,34 @@ protected void renderList(MatrixStack matrices, int mouseX, int mouseY, float de
148151
}
149152
}
150153

154+
/* END cloth config code */
155+
156+
@Override
157+
public void render(MatrixStack matrices, int mouseX, int mouseY, float delta) {
158+
smoothScrollAmount = MathHelper.lerp(MinecraftClient.getInstance().getLastFrameDuration() * 0.5, smoothScrollAmount, getScrollAmount());
159+
returnSmoothAmount = true;
160+
super.render(matrices, mouseX, mouseY, delta);
161+
returnSmoothAmount = false;
162+
}
163+
164+
/**
165+
* awful code to only use smooth scroll state when rendering,
166+
* not other code that needs target scroll amount
167+
*/
168+
@Override
169+
public double getScrollAmount() {
170+
if (returnSmoothAmount)
171+
return smoothScrollAmount;
172+
173+
return super.getScrollAmount();
174+
}
175+
151176
public void postRender(MatrixStack matrices, int mouseX, int mouseY, float delta) {
152177
for (Entry entry : children()) {
153178
entry.postRender(matrices, mouseX, mouseY, delta);
154179
}
155180
}
156181

157-
/* END cloth config code */
158-
159182
@Override
160183
public int getRowWidth() {
161184
return Math.min(396, (int)(width / 1.3f));
@@ -178,7 +201,7 @@ public boolean mouseScrolled(double mouseX, double mouseY, double amount) {
178201
return true;
179202
}
180203

181-
this.setScrollAmount(this.getScrollAmount() - amount * (double) (getMaxScroll() / getEntryCount()) / 2.0D);
204+
this.setScrollAmount(this.getScrollAmount() - amount * 20 /* * (double) (getMaxScroll() / getEntryCount()) / 2.0D */);
182205
return true;
183206
}
184207

@@ -221,7 +244,7 @@ public void recacheViewableChildren() {
221244
int i = 0;
222245
for (Entry entry : viewableChildren) {
223246
if (entry instanceof OptionEntry optionEntry)
224-
optionEntry.widget.setDimension(optionEntry.widget.getDimension().setY(getRowTop(i)));
247+
optionEntry.widget.setDimension(optionEntry.widget.getDimension().withY(getRowTop(i)));
225248
i++;
226249
}
227250
}
@@ -250,29 +273,48 @@ protected boolean isHovered() {
250273
}
251274

252275
public class OptionEntry extends Entry {
276+
public final Option<?> option;
253277
public final ConfigCategory category;
254278
public final OptionGroup group;
255279

256280
public final AbstractWidget widget;
257281
private final Supplier<Boolean> viewableSupplier;
258282

283+
private final TextScaledButtonWidget resetButton;
284+
259285
private final String categoryName;
260286
private final String groupName;
261287

262-
private OptionEntry(ConfigCategory category, OptionGroup group, AbstractWidget widget, Supplier<Boolean> viewableSupplier) {
288+
private OptionEntry(Option<?> option, ConfigCategory category, OptionGroup group, AbstractWidget widget, Supplier<Boolean> viewableSupplier) {
289+
this.option = option;
263290
this.category = category;
264291
this.group = group;
265292
this.widget = widget;
266293
this.viewableSupplier = viewableSupplier;
267294
this.categoryName = category.name().getString().toLowerCase();
268295
this.groupName = group.name().getString().toLowerCase();
296+
if (this.widget.canReset()) {
297+
this.widget.setDimension(this.widget.getDimension().expanded(-21, 0));
298+
this.resetButton = new TextScaledButtonWidget(widget.getDimension().xLimit() + 1, -50, 20, 20, 2f, Text.of("\u21BB"), button -> {
299+
option.requestSetDefault();
300+
});
301+
option.addListener((opt, val) -> this.resetButton.active = !opt.isPendingValueDefault() && opt.available());
302+
this.resetButton.active = !option.isPendingValueDefault() && option.available();
303+
} else {
304+
this.resetButton = null;
305+
}
269306
}
270307

271308
@Override
272309
public void render(MatrixStack matrices, int index, int y, int x, int entryWidth, int entryHeight, int mouseX, int mouseY, boolean hovered, float tickDelta) {
273-
widget.setDimension(widget.getDimension().setY(y));
310+
widget.setDimension(widget.getDimension().withY(y));
274311

275312
widget.render(matrices, mouseX, mouseY, tickDelta);
313+
314+
if (resetButton != null) {
315+
resetButton.y = y;
316+
resetButton.render(matrices, mouseX, mouseY, tickDelta);
317+
}
276318
}
277319

278320
@Override
@@ -312,12 +354,18 @@ public int getItemHeight() {
312354

313355
@Override
314356
public List<? extends Selectable> selectableChildren() {
315-
return ImmutableList.of(widget);
357+
if (resetButton == null)
358+
return ImmutableList.of(widget);
359+
360+
return ImmutableList.of(widget, resetButton);
316361
}
317362

318363
@Override
319364
public List<? extends Element> children() {
320-
return ImmutableList.of(widget);
365+
if (resetButton == null)
366+
return ImmutableList.of(widget);
367+
368+
return ImmutableList.of(widget, resetButton);
321369
}
322370
}
323371

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
package dev.isxander.yacl.gui;
2+
3+
import net.minecraft.client.MinecraftClient;
4+
import net.minecraft.client.font.TextRenderer;
5+
import net.minecraft.client.gui.widget.ButtonWidget;
6+
import net.minecraft.client.util.math.MatrixStack;
7+
import net.minecraft.text.OrderedText;
8+
import net.minecraft.text.Text;
9+
import net.minecraft.util.math.MathHelper;
10+
11+
public class TextScaledButtonWidget extends ButtonWidget {
12+
public float textScale;
13+
14+
public TextScaledButtonWidget(int x, int y, int width, int height, float textScale, Text message, PressAction onPress) {
15+
super(x, y, width, height, message, onPress);
16+
this.textScale = textScale;
17+
}
18+
19+
public TextScaledButtonWidget(int x, int y, int width, int height, float textScale, Text message, PressAction onPress, TooltipSupplier tooltipSupplier) {
20+
super(x, y, width, height, message, onPress, tooltipSupplier);
21+
this.textScale = textScale;
22+
}
23+
24+
@Override
25+
public void renderButton(MatrixStack matrices, int mouseX, int mouseY, float delta) {
26+
// prevents super from rendering text
27+
Text message = getMessage();
28+
setMessage(Text.empty());
29+
30+
super.renderButton(matrices, mouseX, mouseY, delta);
31+
32+
setMessage(message);
33+
int j = this.active ? 16777215 : 10526880;
34+
OrderedText orderedText = getMessage().asOrderedText();
35+
TextRenderer textRenderer = MinecraftClient.getInstance().textRenderer;
36+
37+
matrices.push();
38+
matrices.translate(((this.x + this.width / 2f) - textRenderer.getWidth(orderedText) * textScale / 2), (float)this.y + (this.height - 8 * textScale) / 2f / textScale, 0);
39+
matrices.scale(textScale, textScale, 1);
40+
textRenderer.drawWithShadow(matrices, orderedText, 0, 0, j | MathHelper.ceil(this.alpha * 255.0F) << 24);
41+
matrices.pop();
42+
}
43+
}

src/main/java/dev/isxander/yacl/gui/YACLScreen.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import com.mojang.blaze3d.systems.RenderSystem;
44
import dev.isxander.yacl.api.*;
55
import dev.isxander.yacl.api.utils.Dimension;
6+
import dev.isxander.yacl.api.utils.MutableDimension;
67
import dev.isxander.yacl.api.utils.OptionUtils;
78
import net.minecraft.client.font.MultilineText;
89
import net.minecraft.client.font.TextRenderer;
@@ -48,7 +49,7 @@ protected void init() {
4849
columnWidth = Math.min(columnWidth, 400);
4950
int paddedWidth = columnWidth - padding * 2;
5051

51-
Dimension<Integer> actionDim = Dimension.ofInt(width / 3 / 2, height - padding - 20, paddedWidth, 20);
52+
MutableDimension<Integer> actionDim = Dimension.ofInt(width / 3 / 2, height - padding - 20, paddedWidth, 20);
5253
finishedSaveButton = new TooltipButtonWidget(this, actionDim.x() - actionDim.width() / 2, actionDim.y(), actionDim.width(), actionDim.height(), Text.empty(), Text.empty(), (btn) -> {
5354
saveButtonMessage = null;
5455

0 commit comments

Comments
 (0)