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:
parent
ca02ac038f
commit
370c813287
15 changed files with 702 additions and 169 deletions
|
@ -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");
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -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 {}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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_);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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_);
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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)));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in a new issue