lost in the depths

- add a search box to config screens
- add a new screen that gives easier access to other mod's configs
- add a couple more config annotations
This commit is contained in:
zelophed 2021-08-07 02:09:12 +02:00
parent ca02ac038f
commit 370c813287
15 changed files with 702 additions and 169 deletions

View file

@ -1,5 +1,7 @@
package com.simibubi.create.foundation.config;
import com.simibubi.create.foundation.config.ui.ConfigAnnotations;
public class CClient extends ConfigBase {
public ConfigGroup client = group(0, "client",
@ -42,11 +44,11 @@ public class CClient extends ConfigBase {
"Offset the overlay from goggle- and hover- information by this many pixels on the Y axis; Use /create overlay");
public ConfigBool overlayCustomColor = b(false, "customColorsOverlay", "Enable this to use your custom colors for the Goggle- and Hover- Overlay");
public ConfigInt overlayBackgroundColor = i(0xf0_100010, Integer.MIN_VALUE, Integer.MAX_VALUE, "customBackgroundOverlay",
"The custom background color to use for the Goggle- and Hover- Overlays, if enabled", "[in Hex: #AaRrGgBb]", "[@cui:IntDisplay:#]");
"The custom background color to use for the Goggle- and Hover- Overlays, if enabled", "[in Hex: #AaRrGgBb]", ConfigAnnotations.IntDisplay.HEX.asComment());
public ConfigInt overlayBorderColorTop = i(0x50_5000ff, Integer.MIN_VALUE, Integer.MAX_VALUE, "customBorderTopOverlay",
"The custom top color of the border gradient to use for the Goggle- and Hover- Overlays, if enabled", "[in Hex: #AaRrGgBb]", "[@cui:IntDisplay:#]");
"The custom top color of the border gradient to use for the Goggle- and Hover- Overlays, if enabled", "[in Hex: #AaRrGgBb]", ConfigAnnotations.IntDisplay.HEX.asComment());
public ConfigInt overlayBorderColorBot = i(0x50_28007f, Integer.MIN_VALUE, Integer.MAX_VALUE, "customBorderBotOverlay",
"The custom bot color of the border gradient to use for the Goggle- and Hover- Overlays, if enabled", "[in Hex: #AaRrGgBb]", "[@cui:IntDisplay:#]");
"The custom bot color of the border gradient to use for the Goggle- and Hover- Overlays, if enabled", "[in Hex: #AaRrGgBb]", ConfigAnnotations.IntDisplay.HEX.asComment());
//placement assist group
public ConfigGroup placementAssist = group(1, "placementAssist", "Settings for the Placement Assist");

View file

@ -1,11 +1,13 @@
package com.simibubi.create.foundation.config;
import com.simibubi.create.foundation.config.ui.ConfigAnnotations;
public class CKinetics extends ConfigBase {
public ConfigBool disableStress = b(false, "disableStress", Comments.disableStress);
public ConfigInt maxBeltLength = i(20, 5, "maxBeltLength", Comments.maxBeltLength);
public ConfigInt crushingDamage = i(4, 0, "crushingDamage", Comments.crushingDamage);
public ConfigInt maxMotorSpeed = i(256, 64, "maxMotorSpeed", Comments.rpm, Comments.maxMotorSpeed);
public ConfigInt maxMotorSpeed = i(256, 64, "maxMotorSpeed", Comments.rpm, Comments.maxMotorSpeed, ConfigAnnotations.RequiresRestart.BOTH.asComment());
public ConfigInt waterWheelBaseSpeed = i(4, 1, "waterWheelBaseSpeed", Comments.rpm, Comments.waterWheelBaseSpeed);
public ConfigInt waterWheelFlowSpeed = i(4, 1, "waterWheelFlowSpeed", Comments.rpm, Comments.waterWheelFlowSpeed);
public ConfigInt furnaceEngineSpeed = i(16, 1, "furnaceEngineSpeed", Comments.rpm, Comments.furnaceEngineSpeed);

View file

@ -1,6 +1,9 @@
package com.simibubi.create.foundation.config.ui;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.function.UnaryOperator;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
@ -26,43 +29,65 @@ import net.minecraftforge.fml.config.ModConfig;
public class BaseConfigScreen extends ConfigScreen {
private static final DelegatedStencilElement.ElementRenderer DISABLED_RENDERER = (ms, width, height, alpha) -> UIRenderHelper.angledGradient(ms, 0, 0, height / 2, height, width, Theme.p(Theme.Key.BUTTON_DISABLE));
public static final DelegatedStencilElement.ElementRenderer DISABLED_RENDERER = (ms, width, height, alpha) -> UIRenderHelper.angledGradient(ms, 0, 0, height / 2, height, width, Theme.p(Theme.Key.BUTTON_DISABLE));
private static final Map<String, UnaryOperator<BaseConfigScreen>> defaults = new HashMap<>();
static {
defaults.put("create", (base) -> base
.withTitles("Client Settings", "World Generation Settings", "Gameplay Settings")
.withSpecs(AllConfigs.CLIENT.specification, AllConfigs.COMMON.specification, AllConfigs.SERVER.specification)
);
}
/**
* If you are a Create Addon dev and want to change the config labels,
* add a default action here.
*
* Make sure you call either {@link #withSpecs(ForgeConfigSpec, ForgeConfigSpec, ForgeConfigSpec)}
* or {@link #searchForSpecsInModContainer()}
*
* @param modID the modID of your addon/mod
*/
public static void setDefaultActionFor(String modID, UnaryOperator<BaseConfigScreen> transform) {
if (modID.equalsIgnoreCase("create"))
return;
defaults.put(modID, transform);
}
public static BaseConfigScreen forCreate(Screen parent) {
return new BaseConfigScreen(parent)
.withTitles("Client Settings", "World Generation Settings", "Gameplay Settings")
.withSpecs(AllConfigs.CLIENT.specification, AllConfigs.COMMON.specification, AllConfigs.SERVER.specification);
return new BaseConfigScreen(parent);
}
BoxWidget clientConfigWidget;
BoxWidget commonConfigWidget;
BoxWidget serverConfigWidget;
BoxWidget goBack;
BoxWidget others;
BoxWidget title;
ForgeConfigSpec clientSpec;
ForgeConfigSpec commonSpec;
ForgeConfigSpec serverSpec;
String clientTile = "CLIENT CONFIG";
String commonTile = "COMMON CONFIG";
String serverTile = "SERVER CONFIG";
String modID = Create.ID;
String clientTile = "Client Config";
String commonTile = "Common Config";
String serverTile = "Server Config";
String modID;
protected boolean returnOnClose;
/**
* If you are a Create Addon dev and want to make use of the same GUI
* for your mod's config, use this Constructor to create a entry point
*
* @param parent the previously opened screen
* @param modID the modID of your addon/mod
*/
public BaseConfigScreen(Screen parent, @Nonnull String modID) {
this(parent);
super(parent);
this.modID = modID;
if (defaults.containsKey(modID))
defaults.get(modID).apply(this);
else {
this.searchForSpecsInModContainer();
}
}
private BaseConfigScreen(Screen parent) {
super(parent);
this(parent, Create.ID);
}
/**
@ -70,22 +95,26 @@ public class BaseConfigScreen extends ConfigScreen {
* please use {@link #withSpecs(ForgeConfigSpec, ForgeConfigSpec, ForgeConfigSpec)} instead
*/
public BaseConfigScreen searchForSpecsInModContainer() {
if (!ConfigHelper.hasAnyConfig(this.modID)){
return this;
}
try {
clientSpec = ConfigHelper.findConfigSpecFor(ModConfig.Type.CLIENT, this.modID);
} catch (Exception e) {
Create.LOGGER.warn("Unable to find ClientConfigSpec for mod: " + this.modID);
Create.LOGGER.debug("Unable to find ClientConfigSpec for mod: " + this.modID);
}
try {
commonSpec = ConfigHelper.findConfigSpecFor(ModConfig.Type.COMMON, this.modID);
} catch (Exception e) {
Create.LOGGER.warn("Unable to find CommonConfigSpec for mod: " + this.modID, e);
Create.LOGGER.debug("Unable to find CommonConfigSpec for mod: " + this.modID);
}
try {
serverSpec = ConfigHelper.findConfigSpecFor(ModConfig.Type.SERVER, this.modID);
} catch (Exception e) {
Create.LOGGER.warn("Unable to find ServerConfigSpec for mod: " + this.modID, e);
Create.LOGGER.debug("Unable to find ServerConfigSpec for mod: " + this.modID);
}
return this;
@ -191,6 +220,13 @@ public class BaseConfigScreen extends ConfigScreen {
goBack.getToolTip()
.add(new StringTextComponent("Go Back"));
widgets.add(goBack);
TextStencilElement othersText = new TextStencilElement(minecraft.font, new StringTextComponent("Access Configs of other Mods")).centered(true, true);
others = new BoxWidget(width / 2 - 100, height / 2 - 15 + 90, 200, 16).showingElement(othersText);
othersText.withElementRenderer(BoxWidget.gradientFactory.apply(others));
others.withCallback(() -> linkTo(new ConfigModListScreen(this)));
widgets.add(others);
}
@Override

View file

@ -0,0 +1,112 @@
package com.simibubi.create.foundation.config.ui;
public class ConfigAnnotations {
/**
* Changes the way the Integer value is display.
*/
public enum IntDisplay implements ConfigAnnotation {
HEX("#"),
ZERO_X("0x"),
ZERO_B("0b");
private final String value;
IntDisplay(String value) {
this.value = value;
}
@Override
public String getName() {
return "IntDisplay";
}
@Override
public String getValue() {
return value;
}
}
/**
* Indicates to the player that changing this value will require a restart to take full effect
*/
public enum RequiresRestart implements ConfigAnnotation {
CLIENT("client"),
SERVER("server"),
BOTH("both");
private final String value;
RequiresRestart(String value) {
this.value = value;
}
@Override
public String getName() {
return "RequiresReload";
}
@Override
public String getValue() {
return value;
}
}
/**
* Indicates to the player that changing this value will require them to relog to take full effect
*/
public enum RequiresRelog implements ConfigAnnotation {
TRUE;
@Override
public String getName() {
return "RequiresRelog";
}
}
/**
* Changing a value that is annotated with Execute will cause the player to run the given command automatically.
*/
public static class Execute implements ConfigAnnotation {
private final String command;
public static Execute run(String command) {
return new Execute(command);
}
private Execute(String command) {
this.command = command;
}
@Override
public String getName() {
return "Execute";
}
@Override
public String getValue() {
return command;
}
}
public interface ConfigAnnotation {
String getName();
default String getValue() {
return null;
}
default String asComment() {
String comment = "[@cui:" + getName();
String value = getValue();
if (value != null) {
comment = comment + ":" + value;
}
comment = comment + "]";
return comment;
}
}
}

View file

@ -2,18 +2,25 @@ package com.simibubi.create.foundation.config.ui;
import java.util.Arrays;
import java.util.EnumMap;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.simibubi.create.Create;
import com.simibubi.create.foundation.config.AllConfigs;
import com.simibubi.create.foundation.utility.Pair;
import net.minecraftforge.common.ForgeConfigSpec;
import net.minecraftforge.fml.ModContainer;
@ -23,6 +30,10 @@ import net.minecraftforge.fml.config.ModConfig;
public class ConfigHelper {
public static final Pattern unitPattern = Pattern.compile("\\[(in .*)]");
public static final Pattern annotationPattern = Pattern.compile("\\[@cui:([^:]*)(?::(.*))?]");
public static final Map<String, ConfigChange> changes = new HashMap<>();
private static final LoadingCache<String, EnumMap<ModConfig.Type, ModConfig>> configCache = CacheBuilder.newBuilder().expireAfterAccess(5, TimeUnit.MINUTES).build(
new CacheLoader<String, EnumMap<ModConfig.Type, ModConfig>>() {
@Override
@ -54,6 +65,11 @@ public class ConfigHelper {
return null;
}
public static boolean hasAnyConfig(String modID) {
EnumMap<ModConfig.Type, ModConfig> map = configCache.getUnchecked(modID);
return map.entrySet().size() > 0;
}
//Directly set a value
public static <T> void setConfigValue(ConfigPath path, String value) throws InvalidValueException {
ForgeConfigSpec spec = findConfigSpecFor(path.getType(), path.getModID());
@ -68,18 +84,51 @@ public class ConfigHelper {
}
//Add a value to the current UI's changes list
public static <T> void setValue(String path, ForgeConfigSpec.ConfigValue<T> configValue, T value) {
public static <T> void setValue(String path, ForgeConfigSpec.ConfigValue<T> configValue, T value, @Nullable Map<String, String> annotations) {
if (value.equals(configValue.get())) {
ConfigScreen.changes.remove(path);
changes.remove(path);
} else {
ConfigScreen.changes.put(path, value);
changes.put(path, annotations == null ? new ConfigChange(value) : new ConfigChange(value, annotations));
}
}
//Get a value from the current UI's changes list or the config value, if its unchanged
public static <T> T getValue(String path, ForgeConfigSpec.ConfigValue<T> configValue) {
//noinspection unchecked
return (T) ConfigScreen.changes.getOrDefault(path, configValue.get());
ConfigChange configChange = changes.get(path);
if (configChange != null)
//noinspection unchecked
return (T) configChange.value;
else
return configValue.get();
}
public static Pair<String, Map<String, String>> readMetadataFromComment(List<String> commentLines) {
AtomicReference<String> unit = new AtomicReference<>();
Map<String, String> annotations = new HashMap<>();
commentLines.removeIf(line -> {
if (line.trim().isEmpty()) {
return true;
}
Matcher matcher = annotationPattern.matcher(line);
if (matcher.matches()) {
String annotation = matcher.group(1);
String aValue = matcher.group(2);
annotations.putIfAbsent(annotation, aValue);
return true;
}
matcher = unitPattern.matcher(line);
if (matcher.matches()) {
unit.set(matcher.group(1));
}
return false;
});
return Pair.of(unit.get(), annotations);
}
public static class ConfigPath {
@ -138,5 +187,20 @@ public class ConfigHelper {
}
}
public static class ConfigChange {
Object value;
Map<String, String> annotations;
ConfigChange(Object value) {
this.value = value;
}
ConfigChange(Object value, Map<String, String> annotations) {
this(value);
this.annotations = new HashMap<>();
this.annotations.putAll(annotations);
}
}
public static class InvalidValueException extends Exception {}
}

View file

@ -0,0 +1,156 @@
package com.simibubi.create.foundation.config.ui;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import com.mojang.blaze3d.matrix.MatrixStack;
import com.simibubi.create.foundation.gui.AllIcons;
import com.simibubi.create.foundation.gui.DelegatedStencilElement;
import com.simibubi.create.foundation.gui.ScreenOpener;
import com.simibubi.create.foundation.gui.Theme;
import com.simibubi.create.foundation.gui.widgets.BoxWidget;
import com.simibubi.create.foundation.item.TooltipHelper;
import net.minecraft.client.gui.screen.Screen;
import net.minecraft.util.text.StringTextComponent;
import net.minecraft.util.text.TextFormatting;
import net.minecraftforge.fml.ModList;
import net.minecraftforge.fml.loading.moddiscovery.ModInfo;
public class ConfigModListScreen extends ConfigScreen {
ConfigScreenList list;
HintableTextFieldWidget search;
BoxWidget goBack;
List<ModEntry> allEntries;
public ConfigModListScreen(Screen parent) {
super(parent);
}
@Override
public void tick() {
super.tick();
list.tick();
}
@Override
protected void init() {
widgets.clear();
super.init();
int listWidth = Math.min(width - 80, 300);
list = new ConfigScreenList(minecraft, listWidth, height - 60, 15, height - 45, 40);
list.setLeftPos(this.width / 2 - list.getWidth() / 2);
children.add(list);
allEntries = new ArrayList<>();
ModList.get().getMods().stream().map(ModInfo::getModId).forEach(id -> allEntries.add(new ModEntry(id, this)));
allEntries.sort((e1, e2) -> {
int empty = (e2.button.active ? 1 : 0) - (e1.button.active ? 1 : 0);
if (empty != 0)
return empty;
return e1.id.compareToIgnoreCase(e2.id);
});
list.children().clear();
list.children().addAll(allEntries);
goBack = new BoxWidget(width / 2 - listWidth / 2 - 30, height / 2 + 65, 20, 20).withPadding(2, 2)
.withCallback(this::onClose);
goBack.showingElement(AllIcons.I_CONFIG_BACK.asStencil()
.withElementRenderer(BoxWidget.gradientFactory.apply(goBack)));
goBack.getToolTip()
.add(new StringTextComponent("Go Back"));
widgets.add(goBack);
search = new HintableTextFieldWidget(font, width / 2 - listWidth / 2, height - 35, listWidth, 20);
search.setResponder(this::updateFilter);
search.setHint("Search..");
search.moveCursorToStart();
widgets.add(search);
}
@Override
protected void renderWindow(MatrixStack ms, int mouseX, int mouseY, float partialTicks) {
list.render(ms, mouseX, mouseY, partialTicks);
}
@Override
public void onClose() {
super.onClose();
ScreenOpener.open(parent);
}
private void updateFilter(String search) {
list.children().clear();
allEntries
.stream()
.filter(modEntry -> modEntry.id.contains(search.toLowerCase(Locale.ROOT)))
.forEach(list.children()::add);
list.setScrollAmount(list.getScrollAmount());
if (list.children().size() > 0) {
this.search.setTextColor(Theme.i(Theme.Key.TEXT));
} else {
this.search.setTextColor(Theme.i(Theme.Key.BUTTON_FAIL));
}
}
public static class ModEntry extends ConfigScreenList.LabeledEntry {
protected BoxWidget button;
protected String id;
public ModEntry(String id, Screen parent) {
super(toHumanReadable(id));
this.id = id;
button = new BoxWidget(0, 0, 35, 16)
.showingElement(AllIcons.I_CONFIG_OPEN.asStencil().at(10, 0));
button.modifyElement(e -> ((DelegatedStencilElement) e).withElementRenderer(BoxWidget.gradientFactory.apply(button)));
if (ConfigHelper.hasAnyConfig(id)) {
button.withCallback(() -> ScreenOpener.open(new BaseConfigScreen(parent, id)));
} else {
button.active = false;
button.updateColorsFromState();
button.modifyElement(e -> ((DelegatedStencilElement) e).withElementRenderer(BaseConfigScreen.DISABLED_RENDERER));
labelTooltip.add(new StringTextComponent(toHumanReadable(id)));
labelTooltip.addAll(TooltipHelper.cutTextComponent(new StringTextComponent("This Mod does not have any configs registered or is not using Forge's config system"), TextFormatting.GRAY, TextFormatting.GRAY));
}
listeners.add(button);
}
public String getId() {
return id;
}
@Override
public void tick() {
super.tick();
button.tick();
}
@Override
public void render(MatrixStack ms, int index, int y, int x, int width, int height, int mouseX, int mouseY, boolean p_230432_9_, float partialTicks) {
super.render(ms, index, y, x, width, height, mouseX, mouseY, p_230432_9_, partialTicks);
button.x = x + width - 108;
button.y = y + 10;
button.setHeight(height - 20);
button.render(ms, mouseX, mouseY, partialTicks);
}
@Override
protected int getLabelWidth(int totalWidth) {
return (int) (totalWidth * labelWidthMult) + 30;
}
}
}

View file

@ -39,11 +39,9 @@ public abstract class ConfigScreen extends AbstractSimiScreen {
/*
*
* zelo's list for configUI
* TO DO
*
* reduce number of packets sent to the server when saving a bunch of values
* maybe replace java's awt color with something mutable
* find out why framebuffer blending is incorrect
*
* FIXME
*
@ -54,7 +52,6 @@ public abstract class ConfigScreen extends AbstractSimiScreen {
public static final Map<String, TriConsumer<Screen, MatrixStack, Float>> backgrounds = new HashMap<>();
public static final PhysicalFloat cogSpin = PhysicalFloat.create().withLimit(10f).withDrag(0.3).addForce(new Force.Static(.2f));
public static final BlockState cogwheelState = AllBlocks.LARGE_COGWHEEL.getDefaultState().setValue(CogWheelBlock.AXIS, Direction.Axis.Y);
public static final Map<String, Object> changes = new HashMap<>();
public static String modID = null;
protected final Screen parent;
@ -147,7 +144,7 @@ public abstract class ConfigScreen extends AbstractSimiScreen {
public static String toHumanReadable(String key) {
String s = key.replaceAll("_", " ");
s = Arrays.stream(StringUtils.splitByCharacterTypeCamelCase(s)).map(StringUtils::capitalize).collect(Collectors.joining(" "));
s = s.replaceAll("\\s\\s+", " ");
s = StringUtils.normalizeSpace(s);
return s;
}

View file

@ -1,7 +1,11 @@
package com.simibubi.create.foundation.config.ui;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Optional;
import org.lwjgl.opengl.GL11;
@ -12,6 +16,7 @@ import com.simibubi.create.foundation.gui.TextStencilElement;
import com.simibubi.create.foundation.gui.Theme;
import com.simibubi.create.foundation.gui.UIRenderHelper;
import com.simibubi.create.foundation.utility.Color;
import com.simibubi.create.foundation.utility.animation.LerpedFloat;
import net.minecraft.client.MainWindow;
import net.minecraft.client.Minecraft;
@ -28,8 +33,6 @@ public class ConfigScreenList extends ExtendedList<ConfigScreenList.Entry> {
public static TextFieldWidget currentText;
public boolean isForServer = false;
public ConfigScreenList(Minecraft client, int width, int height, int top, int bottom, int elementHeight) {
super(client, width, height, top, bottom, elementHeight);
setRenderBackground(false);
@ -77,13 +80,41 @@ public class ConfigScreenList extends ExtendedList<ConfigScreenList.Entry> {
}
public void tick() {
for(int i = 0; i < getItemCount(); ++i) {
/*for(int i = 0; i < getItemCount(); ++i) {
int top = this.getRowTop(i);
int bot = top + itemHeight;
if (bot >= this.y0 && top <= this.y1)
this.getEntry(i).tick();
}*/
children().forEach(Entry::tick);
}
public boolean search(String query) {
if (query == null || query.isEmpty()) {
setScrollAmount(0);
return true;
}
String q = query.toLowerCase(Locale.ROOT);
Optional<Entry> first = children().stream().filter(entry -> {
if (entry.path == null)
return false;
String[] split = entry.path.split("\\.");
String key = split[split.length - 1].toLowerCase(Locale.ROOT);
return key.contains(q);
}).findFirst();
if (!first.isPresent()) {
setScrollAmount(0);
return false;
}
Entry e = first.get();
e.annotations.put("highlight", "(:");
centerScrollOn(e);
return true;
}
public void bumpCog(float force) {
@ -92,9 +123,12 @@ public class ConfigScreenList extends ExtendedList<ConfigScreenList.Entry> {
public static abstract class Entry extends ExtendedList.AbstractListEntry<Entry> {
protected List<IGuiEventListener> listeners;
protected Map<String, String> annotations;
protected String path;
protected Entry() {
listeners = new ArrayList<>();
annotations = new HashMap<>();
}
@Override
@ -119,6 +153,13 @@ public class ConfigScreenList extends ExtendedList<ConfigScreenList.Entry> {
}
protected void setEditable(boolean b) {}
protected boolean isCurrentValueChanged() {
if (path == null) {
return false;
}
return ConfigHelper.changes.containsKey(path);
}
}
public static class LabeledEntry extends Entry {
@ -128,6 +169,8 @@ public class ConfigScreenList extends ExtendedList<ConfigScreenList.Entry> {
protected TextStencilElement label;
protected List<ITextComponent> labelTooltip;
protected String unit = null;
protected LerpedFloat differenceAnimation = LerpedFloat.linear().startWithValue(0);
protected LerpedFloat highlightAnimation = LerpedFloat.linear().startWithValue(0);
public LabeledEntry(String label) {
this.label = new TextStencilElement(Minecraft.getInstance().font, label);
@ -135,8 +178,41 @@ public class ConfigScreenList extends ExtendedList<ConfigScreenList.Entry> {
labelTooltip = new ArrayList<>();
}
public LabeledEntry(String label, String path) {
this(label);
this.path = path;
}
@Override
public void tick() {
differenceAnimation.tickChaser();
highlightAnimation.tickChaser();
super.tick();
}
@Override
public void render(MatrixStack ms, int index, int y, int x, int width, int height, int mouseX, int mouseY, boolean p_230432_9_, float partialTicks) {
if (isCurrentValueChanged()) {
if (differenceAnimation.getChaseTarget() != 1)
differenceAnimation.chase(1, .5f, LerpedFloat.Chaser.EXP);
} else {
if (differenceAnimation.getChaseTarget() != 0)
differenceAnimation.chase(0, .6f, LerpedFloat.Chaser.EXP);
}
float animation = differenceAnimation.getValue(partialTicks);
if (animation > .1f) {
int offset = (int) (30 * (1 - animation));
if (annotations.containsKey(ConfigAnnotations.RequiresRestart.CLIENT.getName())) {
UIRenderHelper.streak(ms, 180, x + width + 10 + offset, y + height / 2, height - 6, 110, new Color(0x50_601010));
} else if (annotations.containsKey(ConfigAnnotations.RequiresRelog.TRUE.getName())) {
UIRenderHelper.streak(ms, 180, x + width + 10 + offset, y + height / 2, height - 6, 110, new Color(0x40_eefb17));
}
UIRenderHelper.breadcrumbArrow(ms, x - 10 - offset, y + 6, 0, -20, 24, -18, new Color(0x70_ffffff), Color.TRANSPARENT_BLACK);
}
UIRenderHelper.streak(ms, 0, x - 10, y + height / 2, height - 6, width / 8 * 7, 0xdd_000000);
UIRenderHelper.streak(ms, 180, x + (int) (width * 1.35f) + 10, y + height / 2, height - 6, width / 8 * 7, 0xdd_000000);
IFormattableTextComponent component = label.getComponent();
@ -152,6 +228,20 @@ public class ConfigScreenList extends ExtendedList<ConfigScreenList.Entry> {
label.at(x + 10, y + height / 2 - 4, 0).render(ms);
}
if (annotations.containsKey("highlight")) {
highlightAnimation.startWithValue(1).chase(0, 0.1f, LerpedFloat.Chaser.LINEAR);
annotations.remove("highlight");
}
animation = highlightAnimation.getValue(partialTicks);
if (animation > .01f) {
Color highlight = new Color(0xa0_ffffff).scaleAlpha(animation);
UIRenderHelper.streak(ms, 0, x - 10, y + height / 2, height - 6, 5, highlight);
UIRenderHelper.streak(ms, 180, x + width, y + height / 2, height - 6, 5, highlight);
UIRenderHelper.streak(ms, 90, x + width / 2 - 5, y + 3, width + 10, 5, highlight);
UIRenderHelper.streak(ms, -90, x + width / 2 - 5, y + height - 3, width + 10, 5, highlight);
}
if (mouseX > x && mouseX < x + getLabelWidth(width) && mouseY > y + 5 && mouseY < y + height - 5) {
List<ITextComponent> tooltip = getLabelTooltip();

View file

@ -1,20 +1,11 @@
package com.simibubi.create.foundation.config.ui;
import org.lwjgl.glfw.GLFW;
import net.minecraft.client.gui.FontRenderer;
import net.minecraft.client.gui.widget.TextFieldWidget;
import net.minecraft.util.text.StringTextComponent;
public class ConfigTextField extends TextFieldWidget {
public class ConfigTextField extends HintableTextFieldWidget {
protected FontRenderer font;
protected String unit;
public ConfigTextField(FontRenderer font, int x, int y, int width, int height, String unit) {
super(font, x, y, width, height, StringTextComponent.EMPTY);
this.font = font;
this.unit = unit;
public ConfigTextField(FontRenderer font, int x, int y, int width, int height) {
super(font, x, y, width, height);
}
@Override
@ -33,29 +24,4 @@ public class ConfigTextField extends TextFieldWidget {
ConfigScreenList.currentText = this;
}
@Override
public boolean keyPressed(int p_231046_1_, int p_231046_2_, int p_231046_3_) {
//prevent input of hex-keys from exiting the current config screen, in case they match the inventory key
boolean ret = false;
switch (p_231046_1_) {
case GLFW.GLFW_KEY_A:
case GLFW.GLFW_KEY_B:
case GLFW.GLFW_KEY_C:
case GLFW.GLFW_KEY_D:
case GLFW.GLFW_KEY_E:
case GLFW.GLFW_KEY_F:
ret = true;
break;
default:
break;
}
if (ret)
return true;
return super.keyPressed(p_231046_1_, p_231046_2_, p_231046_3_);
}
}

View file

@ -0,0 +1,62 @@
package com.simibubi.create.foundation.config.ui;
import org.lwjgl.glfw.GLFW;
import com.mojang.blaze3d.matrix.MatrixStack;
import com.simibubi.create.foundation.gui.Theme;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.FontRenderer;
import net.minecraft.client.gui.widget.TextFieldWidget;
import net.minecraft.client.util.InputMappings;
import net.minecraft.util.text.StringTextComponent;
public class HintableTextFieldWidget extends TextFieldWidget {
protected FontRenderer font;
protected String hint;
public HintableTextFieldWidget(FontRenderer font, int x, int y, int width, int height) {
super(font, x, y, width, height, StringTextComponent.EMPTY);
this.font = font;
}
public void setHint(String hint) {
this.hint = hint;
}
@Override
public void renderButton(MatrixStack ms, int mouseX, int mouseY, float partialTicks) {
super.renderButton(ms, mouseX, mouseY, partialTicks);
if (hint == null || hint.isEmpty())
return;
if (!getValue().isEmpty())
return;
font.draw(ms, hint, x + 5, this.y + (this.height - 8) / 2, Theme.c(Theme.Key.TEXT).scaleAlpha(.75f).getRGB());
}
@Override
public boolean mouseClicked(double x, double y, int button) {
if (!isMouseOver(x, y))
return false;
if (button == GLFW.GLFW_MOUSE_BUTTON_RIGHT) {
setValue("");
return true;
} else
return super.mouseClicked(x, y, button);
}
@Override
public boolean keyPressed(int code, int p_keyPressed_2_, int p_keyPressed_3_) {
InputMappings.Input mouseKey = InputMappings.getKey(code, p_keyPressed_2_);
if (Minecraft.getInstance().options.keyInventory.isActiveAndMatches(mouseKey)) {
return true;
}
return super.keyPressed(code, p_keyPressed_2_, p_keyPressed_3_);
}
}

View file

@ -1,9 +1,14 @@
package com.simibubi.create.foundation.config.ui;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Consumer;
import javax.annotation.Nonnull;
@ -33,6 +38,7 @@ import com.simibubi.create.foundation.item.TooltipHelper;
import com.simibubi.create.foundation.networking.AllPackets;
import com.simibubi.create.foundation.utility.Color;
import com.simibubi.create.foundation.utility.Couple;
import com.simibubi.create.foundation.utility.Pair;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.IGuiEventListener;
@ -55,13 +61,15 @@ public class SubMenuConfigScreen extends ConfigScreen {
protected BoxWidget discardChanges;
protected BoxWidget goBack;
protected BoxWidget serverLocked;
protected HintableTextFieldWidget search;
protected int listWidth;
protected String title;
protected Set<String> highlights = new HashSet<>();
public static SubMenuConfigScreen find(ConfigHelper.ConfigPath path) {
ForgeConfigSpec spec = ConfigHelper.findConfigSpecFor(path.getType(), path.getModID());
UnmodifiableConfig values = spec.getValues();
BaseConfigScreen base = new BaseConfigScreen(null, path.getModID()).searchForSpecsInModContainer();
BaseConfigScreen base = new BaseConfigScreen(null, path.getModID());
SubMenuConfigScreen screen = new SubMenuConfigScreen(base, "root", path.getType(), spec, values);
List<String> remainingPath = Lists.newArrayList(path.getPath());
@ -75,6 +83,7 @@ public class SubMenuConfigScreen extends ConfigScreen {
if (!(obj instanceof AbstractConfig)) {
//highlight entry
screen.highlights.add(path.getPath()[path.getPath().length - 1]);
continue;
}
@ -107,7 +116,7 @@ public class SubMenuConfigScreen extends ConfigScreen {
}
protected void clearChanges() {
changes.clear();
ConfigHelper.changes.clear();
list.children()
.stream()
.filter(e -> e instanceof ValueEntry)
@ -116,11 +125,18 @@ public class SubMenuConfigScreen extends ConfigScreen {
protected void saveChanges() {
UnmodifiableConfig values = spec.getValues();
changes.forEach((path, value) -> {
ConfigHelper.changes.forEach((path, change) -> {
ForgeConfigSpec.ConfigValue configValue = values.get(path);
configValue.set(value);
configValue.set(change.value);
if (type == ModConfig.Type.SERVER) {
AllPackets.channel.sendToServer(new CConfigureConfigPacket<>(ConfigScreen.modID, path, value));
AllPackets.channel.sendToServer(new CConfigureConfigPacket<>(ConfigScreen.modID, path, change.value));
}
String command = change.annotations.get("Execute");
if (Minecraft.getInstance().player != null && command != null && command.startsWith("/")) {
Minecraft.getInstance().player.chat(command);
//AllPackets.channel.sendToServer(new CChatMessagePacket(command));
}
});
clearChanges();
@ -133,8 +149,10 @@ public class SubMenuConfigScreen extends ConfigScreen {
} else if (obj instanceof ForgeConfigSpec.ConfigValue<?>) {
ForgeConfigSpec.ConfigValue configValue = (ForgeConfigSpec.ConfigValue<?>) obj;
ForgeConfigSpec.ValueSpec valueSpec = spec.getRaw((List<String>) configValue.getPath());
List<String> comments = new ArrayList<>(Arrays.asList(valueSpec.getComment().split("\n")));
Pair<String, Map<String, String>> metadata = ConfigHelper.readMetadataFromComment(comments);
ConfigHelper.setValue(String.join(".", configValue.getPath()), configValue, valueSpec.getDefault());
ConfigHelper.setValue(String.join(".", configValue.getPath()), configValue, valueSpec.getDefault(), metadata.getSecond());
}
});
@ -181,17 +199,18 @@ public class SubMenuConfigScreen extends ConfigScreen {
saveChanges = new BoxWidget(listL - 30, yCenter - 25, 20, 20)
.withPadding(2, 2)
.withCallback((x, y) -> {
if (changes.isEmpty())
if (ConfigHelper.changes.isEmpty())
return;
new ConfirmationScreen()
ConfirmationScreen confirm = new ConfirmationScreen()
.centered()
.withText(ITextProperties.of("Saving " + changes.size() + " changed value" + (changes.size() != 1 ? "s" : "") + ""))
.withText(ITextProperties.of("Saving " + ConfigHelper.changes.size() + " changed value" + (ConfigHelper.changes.size() != 1 ? "s" : "") + ""))
.withAction(success -> {
if (success)
saveChanges();
})
.open(this);
});
addAnnotationsToConfirm(confirm).open(this);
});
saveChanges.showingElement(AllIcons.I_CONFIG_SAVE.asStencil().withElementRenderer(BoxWidget.gradientFactory.apply(saveChanges)));
saveChanges.getToolTip().add(new StringTextComponent("Save Changes"));
@ -200,12 +219,12 @@ public class SubMenuConfigScreen extends ConfigScreen {
discardChanges = new BoxWidget(listL - 30, yCenter + 5, 20, 20)
.withPadding(2, 2)
.withCallback((x, y) -> {
if (changes.isEmpty())
if (ConfigHelper.changes.isEmpty())
return;
new ConfirmationScreen()
.centered()
.withText(ITextProperties.of("Discarding " + changes.size() + " unsaved change" + (changes.size() != 1 ? "s" : "") + ""))
.withText(ITextProperties.of("Discarding " + ConfigHelper.changes.size() + " unsaved change" + (ConfigHelper.changes.size() != 1 ? "s" : "") + ""))
.withAction(success -> {
if (success)
clearChanges();
@ -227,16 +246,23 @@ public class SubMenuConfigScreen extends ConfigScreen {
widgets.add(discardChanges);
widgets.add(goBack);
list = new ConfigScreenList(minecraft, listWidth, height - 60, 45, height - 15, 40);
list = new ConfigScreenList(minecraft, listWidth, height - 80, 35, height - 45, 40);
list.setLeftPos(this.width / 2 - list.getWidth() / 2);
children.add(list);
search = new ConfigTextField(font, width / 2 - listWidth / 2, height - 35, listWidth, 20);
search.setResponder(this::updateFilter);
search.setHint("Search..");
search.moveCursorToStart();
widgets.add(search);
configGroup.valueMap().forEach((key, obj) -> {
String humanKey = toHumanReadable(key);
if (obj instanceof AbstractConfig) {
SubMenuEntry entry = new SubMenuEntry(this, humanKey, spec, (UnmodifiableConfig) obj);
entry.path = key;
list.children().add(entry);
if (configGroup.valueMap()
.size() == 1)
@ -247,23 +273,23 @@ public class SubMenuConfigScreen extends ConfigScreen {
ForgeConfigSpec.ConfigValue<?> configValue = (ForgeConfigSpec.ConfigValue<?>) obj;
ForgeConfigSpec.ValueSpec valueSpec = spec.getRaw(configValue.getPath());
Object value = configValue.get();
ConfigScreenList.Entry entry = null;
if (value instanceof Boolean) {
BooleanEntry entry = new BooleanEntry(humanKey, (ForgeConfigSpec.ConfigValue<Boolean>) configValue, valueSpec);
list.children().add(entry);
entry = new BooleanEntry(humanKey, (ForgeConfigSpec.ConfigValue<Boolean>) configValue, valueSpec);
} else if (value instanceof Enum) {
EnumEntry entry = new EnumEntry(humanKey, (ForgeConfigSpec.ConfigValue<Enum<?>>) configValue, valueSpec);
list.children().add(entry);
entry = new EnumEntry(humanKey, (ForgeConfigSpec.ConfigValue<Enum<?>>) configValue, valueSpec);
} else if (value instanceof Number) {
NumberEntry<? extends Number> entry = NumberEntry.create(value, humanKey, configValue, valueSpec);
if (entry != null) {
list.children().add(entry);
} else {
list.children().add(new ConfigScreenList.LabeledEntry("n-" + obj.getClass().getSimpleName() + " " + humanKey + " : " + value));
}
} else {
list.children().add(new ConfigScreenList.LabeledEntry(humanKey + " : " + value));
entry = NumberEntry.create(value, humanKey, configValue, valueSpec);
}
if (entry == null)
entry = new LabeledEntry("Impl missing - " + configValue.get().getClass().getSimpleName() + " " + humanKey + " : " + value);
if (highlights.contains(key))
entry.annotations.put("highlight", ":)");
list.children().add(entry);
}
});
@ -281,13 +307,14 @@ public class SubMenuConfigScreen extends ConfigScreen {
return group;
});
list.search(highlights.stream().findFirst().orElse(""));
//extras for server configs
if (type != ModConfig.Type.SERVER)
return;
if (minecraft.hasSingleplayerServer())
return;
list.isForServer = true;
boolean canEdit = minecraft != null && minecraft.player != null && minecraft.player.hasPermissions(2);
Couple<Color> red = Theme.p(Theme.Key.BUTTON_FAIL);
@ -355,6 +382,12 @@ public class SubMenuConfigScreen extends ConfigScreen {
if (super.keyPressed(code, p_keyPressed_2_, p_keyPressed_3_))
return true;
if (Screen.hasControlDown()) {
if (code == GLFW.GLFW_KEY_F) {
search.setFocus(true);
}
}
if (code == GLFW.GLFW_KEY_BACKSPACE) {
attemptBackstep();
}
@ -362,8 +395,16 @@ public class SubMenuConfigScreen extends ConfigScreen {
return false;
}
private void updateFilter(String search) {
if (list.search(search)) {
this.search.setTextColor(Theme.i(Theme.Key.TEXT));
} else {
this.search.setTextColor(Theme.i(Theme.Key.BUTTON_FAIL));
}
}
private void attemptBackstep() {
if (changes.isEmpty() || !(parent instanceof BaseConfigScreen)) {
if (ConfigHelper.changes.isEmpty() || !(parent instanceof BaseConfigScreen)) {
ScreenOpener.open(parent);
return;
}
@ -373,7 +414,7 @@ public class SubMenuConfigScreen extends ConfigScreen {
return;
if (success == Response.Confirm)
saveChanges();
changes.clear();
ConfigHelper.changes.clear();
ScreenOpener.open(parent);
};
@ -382,7 +423,7 @@ public class SubMenuConfigScreen extends ConfigScreen {
@Override
public void onClose() {
if (changes.isEmpty()) {
if (ConfigHelper.changes.isEmpty()) {
super.onClose();
ScreenOpener.open(parent);
return;
@ -393,7 +434,7 @@ public class SubMenuConfigScreen extends ConfigScreen {
return;
if (success == Response.Confirm)
saveChanges();
changes.clear();
ConfigHelper.changes.clear();
super.onClose();
};
@ -401,11 +442,37 @@ public class SubMenuConfigScreen extends ConfigScreen {
}
public void showLeavingPrompt(Consumer<ConfirmationScreen.Response> action) {
new ConfirmationScreen().centered()
.addText(ITextProperties.of("Leaving with " + changes.size() + " unsaved change"
+ (changes.size() != 1 ? "s" : "") + " for this config"))
ConfirmationScreen screen = new ConfirmationScreen()
.centered()
.withThreeActions(action)
.open(this);
.addText(ITextProperties.of("Leaving with " + ConfigHelper.changes.size() + " unsaved change"
+ (ConfigHelper.changes.size() != 1 ? "s" : "") + " for this config"));
addAnnotationsToConfirm(screen).open(this);
}
private ConfirmationScreen addAnnotationsToConfirm(ConfirmationScreen screen) {
AtomicBoolean relog = new AtomicBoolean(false);
AtomicBoolean restart = new AtomicBoolean(false);
ConfigHelper.changes.values().forEach(change -> {
if (change.annotations.containsKey(ConfigAnnotations.RequiresRelog.TRUE.getName()))
relog.set(true);
if (change.annotations.containsKey(ConfigAnnotations.RequiresRestart.CLIENT.getName()))
restart.set(true);
});
if (relog.get()) {
screen.addText(ITextProperties.of(" "));
screen.addText(ITextProperties.of("At least one changed value will require you to relog to take full effect"));
}
if (restart.get()) {
screen.addText(ITextProperties.of(" "));
screen.addText(ITextProperties.of("At least one changed value will require you to restart your game to take full effect"));
}
return screen;
}
}

View file

@ -3,6 +3,7 @@ package com.simibubi.create.foundation.config.ui.entries;
import java.util.Locale;
import com.mojang.blaze3d.matrix.MatrixStack;
import com.simibubi.create.foundation.config.ui.ConfigScreen;
import com.simibubi.create.foundation.gui.AllIcons;
import com.simibubi.create.foundation.gui.BoxElement;
import com.simibubi.create.foundation.gui.DelegatedStencilElement;
@ -103,10 +104,6 @@ public class EnumEntry extends ValueEntry<Enum<?>> {
@Override
public void onValueChange(Enum<?> newValue) {
super.onValueChange(newValue);
valueText.withText(newValue.name()
.substring(0, 1)
+ newValue.name()
.substring(1)
.toLowerCase(Locale.ROOT));
valueText.withText(ConfigScreen.toHumanReadable(newValue.name().toLowerCase(Locale.ROOT)));
}
}

View file

@ -39,7 +39,7 @@ public abstract class NumberEntry<T extends Number> extends ValueEntry<T> {
public NumberEntry(String label, ForgeConfigSpec.ConfigValue<T> value, ForgeConfigSpec.ValueSpec spec) {
super(label, value, spec);
textField = new ConfigTextField(Minecraft.getInstance().font, 0, 0, 200, 20, unit);
textField = new ConfigTextField(Minecraft.getInstance().font, 0, 0, 200, 20);
if (this instanceof IntegerEntry && annotations.containsKey("IntDisplay")) {
String intDisplay = annotations.get("IntDisplay");
int intValue = (Integer) getValue();

View file

@ -1,19 +1,16 @@
package com.simibubi.create.foundation.config.ui.entries;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import javax.annotation.Nonnull;
import org.apache.commons.lang3.ArrayUtils;
import com.google.common.base.Predicates;
import com.mojang.blaze3d.matrix.MatrixStack;
import com.simibubi.create.foundation.config.ui.ConfigAnnotations;
import com.simibubi.create.foundation.config.ui.ConfigHelper;
import com.simibubi.create.foundation.config.ui.ConfigScreen;
import com.simibubi.create.foundation.config.ui.ConfigScreenList;
@ -21,25 +18,20 @@ import com.simibubi.create.foundation.gui.AllIcons;
import com.simibubi.create.foundation.gui.DelegatedStencilElement;
import com.simibubi.create.foundation.gui.widgets.BoxWidget;
import com.simibubi.create.foundation.item.TooltipHelper;
import com.simibubi.create.foundation.utility.Pair;
import net.minecraft.util.text.IFormattableTextComponent;
import net.minecraft.util.text.StringTextComponent;
import net.minecraft.util.text.TextFormatting;
import net.minecraftforge.common.ForgeConfigSpec;
public class ValueEntry<T> extends ConfigScreenList.LabeledEntry {
protected static final IFormattableTextComponent modComponent = new StringTextComponent("* ").withStyle(TextFormatting.BOLD, TextFormatting.DARK_BLUE).append(StringTextComponent.EMPTY.plainCopy().withStyle(TextFormatting.RESET));
protected static final int resetWidth = 28;//including 6px offset on either side
public static final Pattern unitPattern = Pattern.compile("\\[(in .*)]");
public static final Pattern annotationPattern = Pattern.compile("\\[@cui:([^:]*)(?::(.*))?]");
protected ForgeConfigSpec.ConfigValue<T> value;
protected ForgeConfigSpec.ValueSpec spec;
protected Map<String, String> annotations;
protected BoxWidget resetButton;
protected boolean editable = true;
protected String path;
public ValueEntry(String label, ForgeConfigSpec.ConfigValue<T> value, ForgeConfigSpec.ValueSpec spec) {
super(label);
@ -57,50 +49,36 @@ public class ValueEntry<T> extends ConfigScreenList.LabeledEntry {
listeners.add(resetButton);
annotations = new HashMap<>();
List<String> path = value.getPath();
labelTooltip.add(new StringTextComponent(label).withStyle(TextFormatting.WHITE));
String comment = spec.getComment();
if (comment == null || comment.isEmpty())
return;
String[] commentLines = comment.split("\n");
//find unit in the comment
for (int i = 0; i < commentLines.length; i++) {
if (commentLines[i].isEmpty()) {
commentLines = ArrayUtils.remove(commentLines, i);
i--;
continue;
}
Matcher matcher = annotationPattern.matcher(commentLines[i]);
if (matcher.matches()) {
String annotation = matcher.group(1);
String aValue = matcher.group(2);
annotations.putIfAbsent(annotation, aValue);
List<String> commentLines = new ArrayList<>(Arrays.asList(comment.split("\n")));
commentLines = ArrayUtils.remove(commentLines, i);
i--;
continue;
}
matcher = unitPattern.matcher(commentLines[i]);
if (matcher.matches()) {
String u = matcher.group(1);
if (u.equals("in Revolutions per Minute"))
u = "in RPM";
if (u.equals("in Stress Units"))
u = "in SU";
unit = u;
}
Pair<String, Map<String, String>> metadata = ConfigHelper.readMetadataFromComment(commentLines);
if (metadata.getFirst() != null) {
unit = metadata.getFirst();
}
if (metadata.getSecond() != null && !metadata.getSecond().isEmpty()) {
annotations.putAll(metadata.getSecond());
}
// add comment to tooltip
labelTooltip.addAll(Arrays.stream(commentLines)
labelTooltip.addAll(commentLines.stream()
.filter(Predicates.not(s -> s.startsWith("Range")))
.map(StringTextComponent::new)
.flatMap(stc -> TooltipHelper.cutTextComponent(stc, TextFormatting.GRAY, TextFormatting.GRAY)
.stream())
.collect(Collectors.toList()));
if (annotations.containsKey(ConfigAnnotations.RequiresRelog.TRUE.getName()))
labelTooltip.addAll(TooltipHelper.cutTextComponent(new StringTextComponent("Changing this value will require a _relog_ to take full effect"), TextFormatting.GRAY, TextFormatting.GOLD));
if (annotations.containsKey(ConfigAnnotations.RequiresRestart.CLIENT.getName()))
labelTooltip.addAll(TooltipHelper.cutTextComponent(new StringTextComponent("Changing this value will require a _restart_ to take full effect"), TextFormatting.GRAY, TextFormatting.RED));
labelTooltip.add(new StringTextComponent(ConfigScreen.modID + ":" + path.get(path.size() - 1)).withStyle(TextFormatting.DARK_GRAY));
}
@ -119,15 +97,7 @@ public class ValueEntry<T> extends ConfigScreenList.LabeledEntry {
@Override
public void render(MatrixStack ms, int index, int y, int x, int width, int height, int mouseX, int mouseY, boolean p_230432_9_, float partialTicks) {
if (isCurrentValueChanged()) {
IFormattableTextComponent original = label.getComponent();
IFormattableTextComponent changed = modComponent.plainCopy().append(original);
label.withText(changed);
super.render(ms, index, y, x, width, height, mouseX, mouseY, p_230432_9_, partialTicks);
label.withText(original);
} else {
super.render(ms, index, y, x, width, height, mouseX, mouseY, p_230432_9_, partialTicks);
}
super.render(ms, index, y, x, width, height, mouseX, mouseY, p_230432_9_, partialTicks);
resetButton.x = x + width - resetWidth + 6;
resetButton.y = y + 10;
@ -140,7 +110,7 @@ public class ValueEntry<T> extends ConfigScreenList.LabeledEntry {
}
public void setValue(@Nonnull T value) {
ConfigHelper.setValue(path, this.value, value);
ConfigHelper.setValue(path, this.value, value, annotations);
onValueChange(value);
}
@ -149,10 +119,6 @@ public class ValueEntry<T> extends ConfigScreenList.LabeledEntry {
return ConfigHelper.getValue(path, this.value);
}
protected boolean isCurrentValueChanged() {
return ConfigScreen.changes.containsKey(path);
}
protected boolean isCurrentValueDefault() {
return spec.getDefault().equals(getValue());
}

View file

@ -102,6 +102,22 @@ public class UIRenderHelper {
ms.popPose();
}
public static void streak(MatrixStack ms, float angle, int x, int y, int breadth, int length, Color c) {
Color color = c.copy().setImmutable();
int c1 = color.scaleAlpha(0.625f).getRGB();
int c2 = color.scaleAlpha(0.5f).getRGB();
int c3 = color.scaleAlpha(0.0625f).getRGB();
int c4 = color.scaleAlpha(0f).getRGB();
ms.pushPose();
ms.translate(x, y, 0);
ms.mulPose(Vector3f.ZP.rotationDegrees(angle - 90));
streak(ms, breadth / 2, length, c1, c2, c3, c4);
ms.popPose();
}
private static void streak(MatrixStack ms, int width, int height, int c1, int c2, int c3, int c4) {
double split1 = .5;
double split2 = .75;