Skip to content

Commit

Permalink
Example requirement api
Browse files Browse the repository at this point in the history
  • Loading branch information
MattSturgeon committed Aug 2, 2023
1 parent e216516 commit d370095
Show file tree
Hide file tree
Showing 3 changed files with 332 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@

package me.shedaniel.autoconfig.annotation;

import me.shedaniel.autoconfig.requirements.DefaultRequirements;
import me.shedaniel.clothconfig2.api.DisableableWidget;
import me.shedaniel.clothconfig2.api.HideableWidget;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
Expand Down Expand Up @@ -154,4 +158,119 @@ enum EnumDisplayOption {
}
}
}

public static class Requirements {
private Requirements() {}

/**
* Defines zero or more {@link Requirement} definitions that will control whether this Config Entry GUI is
* enabled or disabled.
*
* @see Requirement
* @see DisableableWidget
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface EnableIf {
Requirement[] value();
Quantifier quantifier() default Quantifier.ALL;
}

/**
* Defines zero or more {@link Requirement} definitions that will control whether this Config Entry GUI is
* displayed on the screen.
*
* @see Requirement
* @see HideableWidget
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface DisplayIf {
Requirement[] value();
Quantifier quantifier() default Quantifier.ALL;
}

/**
* Defines a Requirement, which is a reference to a method handler
* along with a set of arguments to be passed to the Handler.
*/
@Retention(RetentionPolicy.RUNTIME)
public @interface Requirement {
/**
* The name of the Handler method to be used.
*/
String value();

/**
* The class in which to look for the Handler method. Defaults to {@link DefaultRequirements}.
*
* @see DefaultRequirements
*/
Class<?> cls() default DefaultRequirements.class;

/**
* Zero or more {@link ConfigEntry.Ref references} to Config Entries, whose value should be passed to the handler method.
*/
ConfigEntry.Ref[] refArgs() default {};

/**
* Zero or more static values to be passed to the handler method.
*
* <p>The following parameter types are supported:
* <ul>
* <li>{@link String}: The arg will be used as-is.
* <li>{@link Character}: The first char of the arg will be used. The arg must be exactly 1 characters long.
* <li>{@link Boolean}: The arg will be checked against the following values (case-insensitive):
* <ul>
* <li>{@code true} for: {@code "true"}, {@code "t"}, or {@code "1"}.
* <li>{@code false} for: {@code "false"}, {@code "f"}, or {@code "0"}.
* </ul>
* <li>{@link Enum}: The arg will be compared against the {@link Enum#name() name values} of the expected Enum.
* The value must be an exact match (case-sensitive).
* <li>{@code short int long float double}: The arg will be parsed using the appropriate method,
* such as {@link Integer#valueOf(String)}.
* </ul>
* <p>An exception will be thrown if a match is not found, parsing fails, or the expected type is not listed above.
*/
String[] staticArgs() default {};
}

/**
* Can be applied to a handler method to declare a list of {@link Ref refs} that should be passed to the handler
* as its initial arguments.
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface ConstParams {
Ref[] value();
}
}

/**
* Defines a reference to a {@link ConfigEntry}.
*
* <p>{@code value} should be the name of the {@code ConfigEntry}'s defining field.
* <p>{@code cls} defines the class in which to look for the field.
* If {@code cls} is set to the default value ({@link None None.class}),
* then the annotated class is searched instead.
*/
@Retention(RetentionPolicy.RUNTIME)
public @interface Ref {
String value();
Class<?> cls() default None.class;
}

/**
* A quantifier representing how many things are required.
*/
public enum Quantifier {
ALL, ANY, ONE, NONE
}

/**
* Used by ConfigEntry annotations to indicate no class is set.
*/
private static class None {
private None() {}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@
import me.shedaniel.autoconfig.ConfigData;
import me.shedaniel.autoconfig.annotation.Config;
import me.shedaniel.autoconfig.annotation.ConfigEntry;
import me.shedaniel.autoconfig.annotation.ConfigEntry.Gui.EnumHandler.EnumDisplayOption;
import me.shedaniel.autoconfig.requirements.DefaultRequirements;
import me.shedaniel.autoconfig.serializer.PartitioningSerializer;
import org.jetbrains.annotations.ApiStatus;

Expand All @@ -47,6 +49,10 @@ public class ExampleConfig extends PartitioningSerializer.GlobalData {
@ConfigEntry.Gui.TransitiveObject
public ModuleB moduleB = new ModuleB();

@ConfigEntry.Category("c")
@ConfigEntry.Gui.TransitiveObject
public ModuleC moduleC = new ModuleC();

enum ExampleEnum {
FOO,
BAR,
Expand All @@ -62,7 +68,7 @@ public static class ModuleA implements ConfigData {
public ExampleEnum anEnum = ExampleEnum.FOO;

@ConfigEntry.Gui.Tooltip(count = 2)
@ConfigEntry.Gui.EnumHandler(option = ConfigEntry.Gui.EnumHandler.EnumDisplayOption.BUTTON)
@ConfigEntry.Gui.EnumHandler(option = EnumDisplayOption.BUTTON)
public ExampleEnum anEnumWithButton = ExampleEnum.FOO;

@Comment("This tooltip was automatically applied from a Jankson @Comment")
Expand Down Expand Up @@ -98,6 +104,145 @@ public static class ModuleB implements ConfigData {
public int color = 0xFFFFFF;
}

@Config(name = "module_c")
public static class ModuleC implements ConfigData {

public static class Handlers {

@ConfigEntry.Requirements.ConstParams(
@ConfigEntry.Ref(cls = DependencySubCategory.class, value = "coolToggle")
)
private static boolean coolToggleIsEnabled(boolean coolToggle) {
return coolToggle;
}

@ConfigEntry.Requirements.ConstParams({
@ConfigEntry.Ref(cls = DependencySubCategory.class, value = "coolToggle"),
@ConfigEntry.Ref(cls = DependencySubCategory.class, value = "lameToggle")
})
private static Boolean coolToggleMatchesLameToggle(boolean coolToggle, Boolean lameToggle) {
return coolToggle == lameToggle;
}

@ConfigEntry.Requirements.ConstParams(
@ConfigEntry.Ref(cls = DependencySubCategory.class, value = "intSlider")
)
private static boolean intSliderIsBigOrSmall(int intSlider) {
return intSlider > 70 || intSlider < -70;
}

@ConfigEntry.Requirements.ConstParams(
@ConfigEntry.Ref(cls = DependencySubCategory.class, value = "coolEnum")
)
private static boolean coolEnumIsGoodOrBetter(DependencyDemoEnum coolEnum) {
return coolEnum == DependencyDemoEnum.GOOD || coolEnum == DependencyDemoEnum.EXCELLENT;
}
}

@ConfigEntry.Gui.PrefixText
@ConfigEntry.Gui.CollapsibleObject(startExpanded = true)
public DependencySubCategory dependencySubCategory = new DependencySubCategory();
public static class DependencySubCategory {

@ConfigEntry.Gui.Tooltip
public boolean coolToggle = false;

public boolean lameToggle = true;

@ConfigEntry.Gui.EnumHandler(option = EnumDisplayOption.BUTTON)
public DependencyDemoEnum coolEnum = DependencyDemoEnum.OKAY;

@ConfigEntry.BoundedDiscrete(min = -100, max = 100)
public int intSlider = 50;

@ConfigEntry.Requirements.EnableIf(@ConfigEntry.Requirements.Requirement(
value = "isTrue",
refArgs = @ConfigEntry.Ref("coolToggle")
))
public boolean dependsOnCoolToggle1 = false;

@ConfigEntry.Requirements.DisplayIf(@ConfigEntry.Requirements.Requirement(
value = "coolToggleIsEnabled",
cls = Handlers.class
))

public boolean dependsOnCoolToggle2 = false;

@ConfigEntry.Requirements.EnableIf(@ConfigEntry.Requirements.Requirement(
value = DefaultRequirements.IS,
refArgs = {
@ConfigEntry.Ref(cls = DependencySubCategory.class, value = "coolToggle"),
@ConfigEntry.Ref(cls = DependencySubCategory.class, value = "lameToggle")
}
))
public boolean dependsOnToggleMatch = false;

@ConfigEntry.Requirements.EnableIf(@ConfigEntry.Requirements.Requirement(cls = Handlers.class, value = "intSliderIsBigOrSmall"))
public boolean dependsOnIntSlider = true;

@ConfigEntry.Gui.TransitiveObject
@ConfigEntry.Requirements.EnableIf(
value = {
@ConfigEntry.Requirements.Requirement(
value = DefaultRequirements.TRUE,
refArgs = @ConfigEntry.Ref(cls = DependencySubCategory.class, value = "coolToggle")
),
@ConfigEntry.Requirements.Requirement(
value = DefaultRequirements.ANY_MATCH,
refArgs = @ConfigEntry.Ref(cls = DependencySubCategory.class, value = "coolEnum"),
staticArgs = { "GOOD", "EXCELLENT" }
)
},
quantifier = ConfigEntry.Quantifier.ALL
)
public DependantObject dependantObject = new DependantObject();
public static class DependantObject {
@ConfigEntry.Gui.PrefixText
public boolean toggle1 = false;

@ConfigEntry.Requirements.EnableIf(
@ConfigEntry.Requirements.Requirement(
value = DefaultRequirements.ANY_MATCH,
refArgs = @ConfigEntry.Ref(cls = DependencySubCategory.class, value = "intSlider"),
staticArgs = { "50", "100" }
)
)
public boolean toggle2 = true;
}

@ConfigEntry.Gui.CollapsibleObject(startExpanded = true)
@ConfigEntry.Requirements.EnableIf(
@ConfigEntry.Requirements.Requirement(
value = DefaultRequirements.TRUE,
refArgs = @ConfigEntry.Ref(cls = DependencySubCategory.class, value = "coolToggle")
)
)
public DependantCollapsible dependantCollapsible = new DependantCollapsible();
public static class DependantCollapsible {
public boolean toggle1 = false;
public boolean toggle2 = true;
}

@ConfigEntry.Requirements.EnableIf(
@ConfigEntry.Requirements.Requirement(
value = DefaultRequirements.TRUE,
refArgs = @ConfigEntry.Ref(cls = DependencySubCategory.class, value = "coolToggle")
)
)
public List<Integer> list = Arrays.asList(1, 2, 3);

}

@ConfigEntry.Requirements.EnableIf(
@ConfigEntry.Requirements.Requirement(
value = DefaultRequirements.TRUE,
refArgs = @ConfigEntry.Ref(cls = DependencySubCategory.class, value = "coolToggle")
)
)
public boolean dependsOnCoolToggleOutside = false;

}

@Config(name = "empty")
public static class Empty implements ConfigData {

Expand Down Expand Up @@ -133,4 +278,8 @@ public PairOfIntPairs(PairOfInts first, PairOfInts second) {
this.second = second;
}
}

enum DependencyDemoEnum {
EXCELLENT, GOOD, OKAY, BAD, HORRIBLE
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package me.shedaniel.autoconfig.requirements;

import java.util.Objects;

public class DefaultRequirements {
private DefaultRequirements() {}

/**
* The condition is met when the argument is {@code true}.
*
* @see #isTrue(Boolean)
*/
public static final String TRUE = "isTrue";

/**
* The condition is met when the argument is {@code false}.
*
* @see #isFalse(Boolean)
*/
public static final String FALSE = "isFalse";

/**
* The condition is met when the first argument is equal to the second.
*
* @see #is(Object, Object)
*/
public static final String IS = "is";

/**
* The condition is met when at least one {@code arg} is equal to another.
*
* @see #anyMatch(Object[])
*/
public static final String ANY_MATCH = "anyMatch";

private static boolean isTrue(Boolean value) {
return Boolean.TRUE.equals(value);
}

private static boolean isFalse(Boolean value) {
return Boolean.FALSE.equals(value);
}

private static <T> boolean is(T value, T condition) {
return Objects.equals(value, condition);
}

private static <T> boolean anyMatch(T ...args) {
for (int i = 0; i < args.length; i++) {
for (int j = 0; j < i; j++) {
if (Objects.equals(args[i], args[j])) {
return true;
}
}
for (int j = i+1; j < args.length; j++) {
if (Objects.equals(args[i], args[j])) {
return true;
}
}
}
return false;
}
}

0 comments on commit d370095

Please sign in to comment.