Skip to content

Commit a092a88

Browse files
committed
feat: config screen refactors
1 parent ec419a7 commit a092a88

14 files changed

Lines changed: 713 additions & 364 deletions

File tree

common/src/client/java/io/github/jamalam360/jamlib/client/config/gui/ConfigScreen.java

Lines changed: 22 additions & 360 deletions
Large diffs are not rendered by default.
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
package io.github.jamalam360.jamlib.client.config.gui.entry;
2+
3+
import net.minecraft.ChatFormatting;
4+
import net.minecraft.client.gui.components.AbstractWidget;
5+
import net.minecraft.client.gui.components.Button;
6+
import net.minecraft.network.chat.Component;
7+
import org.jetbrains.annotations.Nullable;
8+
9+
import java.lang.reflect.Field;
10+
import java.util.List;
11+
12+
public class BooleanConfigEntry extends ConfigEntry {
13+
@Nullable
14+
private Button button = null;
15+
16+
public BooleanConfigEntry(String modId, String configName, Field field) {
17+
super(modId, configName, field);
18+
}
19+
20+
@Override
21+
public List<AbstractWidget> createElementWidgets(int width) {
22+
this.button = Button.builder(this.getComponent(Boolean.TRUE.equals(this.getFieldValue())), button -> {
23+
this.setFieldValue(!(Boolean.TRUE.equals(this.getFieldValue())));
24+
}).pos(width - 188, 0).size(150, 20).build();
25+
26+
return List.of(this.button);
27+
}
28+
29+
@Override
30+
public void onChange() {
31+
super.onChange();
32+
33+
if (this.button != null) {
34+
this.button.setMessage(getComponent(Boolean.TRUE.equals(this.getFieldValue())));
35+
}
36+
}
37+
38+
private Component getComponent(boolean value) {
39+
return Component.literal(value ? "Yes" : "No").withStyle(s -> s.withColor(value ? ChatFormatting.GREEN : ChatFormatting.RED));
40+
}
41+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
package io.github.jamalam360.jamlib.client.config.gui.entry;
2+
3+
import net.minecraft.client.gui.components.AbstractWidget;
4+
import net.minecraft.client.gui.components.Button;
5+
import net.minecraft.network.chat.Component;
6+
7+
import java.lang.reflect.Field;
8+
import java.util.List;
9+
10+
public class CollectionConfigEntry extends ConfigEntry {
11+
public CollectionConfigEntry(String modId, String configName, Field field) {
12+
super(modId, configName, field);
13+
}
14+
15+
@Override
16+
public List<AbstractWidget> createElementWidgets(int width) {
17+
List<AbstractWidget> buttons = List.of(
18+
Button.builder(Component.literal("beans"), button -> {
19+
System.out.println("first");
20+
}).pos(width - 188, 0).size(150, 20).build(),
21+
Button.builder(Component.literal("means heinz"), button -> {
22+
System.out.println("second");
23+
}).pos(width - 188, 20).size(150, 20).build()
24+
);
25+
26+
return buttons;
27+
}
28+
29+
@Override
30+
public void onChange() {
31+
super.onChange();
32+
}
33+
}
Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
1+
package io.github.jamalam360.jamlib.client.config.gui.entry;
2+
3+
import io.github.jamalam360.jamlib.JamLib;
4+
import io.github.jamalam360.jamlib.client.config.gui.ConfigScreen;
5+
import io.github.jamalam360.jamlib.client.mixinsupport.MutableSpriteImageWidget$Sprite;
6+
import io.github.jamalam360.jamlib.config.ConfigExtensions;
7+
import io.github.jamalam360.jamlib.config.ConfigManager;
8+
import net.minecraft.client.Minecraft;
9+
import net.minecraft.client.gui.components.*;
10+
import net.minecraft.client.resources.language.I18n;
11+
import net.minecraft.network.chat.Component;
12+
import net.minecraft.util.FormattedCharSequence;
13+
import org.jetbrains.annotations.Nullable;
14+
15+
import java.lang.reflect.Field;
16+
import java.util.ArrayList;
17+
import java.util.Collection;
18+
import java.util.List;
19+
20+
public abstract class ConfigEntry {
21+
protected final Field field;
22+
protected final ConfigManager<?> configManager;
23+
private final Object originalValue;
24+
private final String translationKey;
25+
@Nullable
26+
private final List<FormattedCharSequence> tooltip;
27+
private boolean valid = true;
28+
private ImageWidget validationIcon;
29+
30+
public static ConfigEntry createFromField(String modId, String configName, Field field) {
31+
Class<?> c = field.getType();
32+
33+
if (c == boolean.class) {
34+
return new BooleanConfigEntry(modId, configName, field);
35+
} else if (c == float.class || c == double.class || c == int.class || c == long.class) {
36+
return new NumberConfigEntry(modId, configName, field);
37+
} else if (c == String.class) {
38+
return new StringConfigEntry(modId, configName, field);
39+
} else if (c.isEnum()) {
40+
return new EnumConfigEntry<>(modId, configName, field);
41+
} else if (Collection.class.isAssignableFrom(c)) {
42+
return new CollectionConfigEntry(modId, configName, field);
43+
} else {
44+
throw new IllegalArgumentException("Unsupported config field type " + c);
45+
}
46+
}
47+
48+
public ConfigEntry(String modId, String configName, Field field) {
49+
this.field = field;
50+
this.configManager = ConfigManager.MANAGERS.get(configName);
51+
this.originalValue = this.getFieldValue();
52+
this.translationKey = ConfigScreen.createTranslationKey(modId, configName, field.getName());
53+
54+
if (I18n.exists(this.translationKey + ".tooltip")) {
55+
this.tooltip = Minecraft.getInstance().font.split(Component.translatable(this.translationKey + ".tooltip"), 200);
56+
} else {
57+
this.tooltip = null;
58+
}
59+
}
60+
61+
public List<AbstractWidget> createWidgets(int width) {
62+
List<AbstractWidget> widgets = new ArrayList<>();
63+
64+
widgets.add(new FittingMultiLineTextWidget(12, Minecraft.getInstance().font.lineHeight / 2 + 1, width / 2 - 10, Minecraft.getInstance().font.lineHeight, Component.translatable(this.translationKey), Minecraft.getInstance().font));
65+
66+
this.validationIcon = ImageWidget.sprite(20, 20, JamLib.id("validation_warning"));
67+
this.validationIcon.setX(width - 212);
68+
this.validationIcon.setY(0);
69+
this.validationIcon.setTooltip(Tooltip.create(Component.translatable("config.jamlib.requires_restart_tooltip")));
70+
this.validationIcon.visible = false;
71+
widgets.add(this.validationIcon);
72+
73+
widgets.addAll(this.createElementWidgets(width));
74+
75+
SpriteIconButton resetButton = SpriteIconButton.builder(Component.translatable("config.jamlib.reset"), (button) -> {
76+
this.setFieldValue(this.originalValue);
77+
78+
if (widgets.get(1) instanceof EditBox box) {
79+
box.setValue(String.valueOf(this.originalValue));
80+
} else if (widgets.get(1) instanceof SliderButton slider) {
81+
slider.setValue((Double) this.originalValue);
82+
}
83+
}, true).sprite(JamLib.id("reset"), 16, 16).size(20, 20).build();
84+
resetButton.setX(width - 30);
85+
resetButton.setY(0);
86+
widgets.add(resetButton);
87+
88+
return widgets;
89+
}
90+
91+
public abstract List<AbstractWidget> createElementWidgets(int width);
92+
93+
public void onChange() {
94+
this.validate();
95+
}
96+
97+
private void validate() {
98+
Object newValue = this.getFieldValue();
99+
100+
if (this.configManager.get() instanceof ConfigExtensions<?> ext) {
101+
@SuppressWarnings("unchecked") List<ConfigExtensions.ValidationError> errors = ((ConfigExtensions<Object>) ext).getValidationErrors((ConfigManager<Object>) this.configManager, new ConfigExtensions.FieldValidationInfo(this.field.getName(), newValue, this.originalValue, this.field));
102+
errors.sort((o1, o2) -> o2.type().ordinal() - o1.type().ordinal());
103+
104+
if (!errors.isEmpty()) {
105+
this.valid = errors.getFirst().type() != ConfigExtensions.ValidationError.Type.ERROR;
106+
this.validationIcon.visible = true;
107+
108+
((MutableSpriteImageWidget$Sprite) this.validationIcon).setSprite(errors.getFirst().type().getTexture());
109+
this.validationIcon.setTooltip(Tooltip.create(errors.getFirst().message()));
110+
} else {
111+
this.valid = true;
112+
this.validationIcon.visible = false;
113+
}
114+
}
115+
}
116+
117+
public boolean hasChanged() {
118+
return this.getFieldValue().equals(this.originalValue);
119+
}
120+
121+
public boolean isValid() {
122+
return this.valid;
123+
}
124+
125+
public Component getName() {
126+
return Component.translatable(this.translationKey);
127+
}
128+
129+
public @Nullable List<FormattedCharSequence> getTooltip() {
130+
return this.tooltip;
131+
}
132+
133+
protected Object getFieldValue() {
134+
try {
135+
return this.field.get(this.configManager.get());
136+
} catch (IllegalAccessException e) {
137+
JamLib.LOGGER.error("Failed to access field for config {}", this.configManager.getConfigName(), e);
138+
return null;
139+
}
140+
}
141+
142+
protected void setFieldValue(Object v) {
143+
Object realValue = v;
144+
145+
if (v instanceof Number n) {
146+
Class<?> c = this.field.getType();
147+
148+
if (c == double.class || c == Double.class) {
149+
realValue = n.doubleValue();
150+
} else if (c == float.class || c == Float.class) {
151+
realValue = n.floatValue();
152+
} else if (c == int.class || c == Integer.class) {
153+
realValue = n.intValue();
154+
} else if (c == long.class || c == Long.class) {
155+
realValue = n.longValue();
156+
}
157+
}
158+
159+
try {
160+
this.field.set(this.configManager.get(), realValue);
161+
this.onChange();
162+
} catch (IllegalAccessException e) {
163+
JamLib.LOGGER.error("Failed to access field for config {}", this.configManager.getConfigName(), e);
164+
}
165+
}
166+
}

common/src/client/java/io/github/jamalam360/jamlib/client/config/gui/EnumButton.java renamed to common/src/client/java/io/github/jamalam360/jamlib/client/config/gui/entry/EnumButton.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package io.github.jamalam360.jamlib.client.config.gui;
1+
package io.github.jamalam360.jamlib.client.config.gui.entry;
22

33
import net.minecraft.client.gui.components.Button;
44
import net.minecraft.network.chat.CommonComponents;
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
package io.github.jamalam360.jamlib.client.config.gui.entry;
2+
3+
import io.github.jamalam360.jamlib.client.config.gui.ConfigScreen;
4+
import net.minecraft.client.gui.components.AbstractWidget;
5+
import net.minecraft.client.resources.language.I18n;
6+
import net.minecraft.network.chat.CommonComponents;
7+
import net.minecraft.network.chat.Component;
8+
import org.jetbrains.annotations.Nullable;
9+
10+
import java.lang.reflect.Field;
11+
import java.util.List;
12+
13+
public class EnumConfigEntry<E extends Enum<E>> extends ConfigEntry {
14+
@Nullable
15+
private EnumButton<E> button = null;
16+
17+
public EnumConfigEntry(String modId, String configName, Field field) {
18+
super(modId, configName, field);
19+
}
20+
21+
@Override
22+
public List<AbstractWidget> createElementWidgets(int width) {
23+
//noinspection unchecked
24+
this.button = new EnumButton<>(
25+
width - 188,
26+
0,
27+
150,
28+
20,
29+
CommonComponents.EMPTY.copy(),
30+
(Class<Enum<?>>) this.field.getType(),
31+
(b) -> this.setFieldValue(b.getValue())
32+
);
33+
this.button.setValue(this.getEnumValue());
34+
this.button.setMessage(this.getComponent());
35+
36+
return List.of(this.button);
37+
}
38+
39+
@Override
40+
public void onChange() {
41+
super.onChange();
42+
43+
if (this.button != null) {
44+
this.button.setMessage(this.getComponent());
45+
}
46+
}
47+
48+
private Component getComponent() {
49+
String translationKey = ConfigScreen.createTranslationKey(this.configManager.getModId(), this.configManager.getConfigName(), field.getName() + "." + this.getEnumValue().name().toLowerCase());
50+
51+
if (I18n.exists(translationKey)) {
52+
return Component.translatable(translationKey);
53+
} else {
54+
return Component.literal(this.getEnumValue().name());
55+
}
56+
}
57+
58+
private E getEnumValue() {
59+
//noinspection unchecked
60+
return (E) this.getFieldValue();
61+
}
62+
}

0 commit comments

Comments
 (0)