Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Config entry dependencies #195

Draft
wants to merge 135 commits into
base: v8
Choose a base branch
from
Draft

Conversation

MattSturgeon
Copy link
Contributor

@MattSturgeon MattSturgeon commented Mar 1, 2023

As per #26, it'd be great if config entries could be hidden or disabled when a different config entry is in a certain state. i.e. one depends on the other.

This branch has evolved significantly since initially opening this as a Draft PR. It is becoming a fairly comprehensive dependency system, although that has lead to a few areas that are perhaps overly complex. At the very least there are edge-cases that have had little-to-no testing.

The main usage scenario (any entry depending on a boolean entry) is feature complete, barring any design changes.

Original description

I've begun working on implementing this, it's currently in early stages (hence draft PR) but I was hoping to spark discussion so I don't spend ages implementing things in a way you disagree with or make it difficult to achieve other features you have planned.

Goals:

  • Disable a dependant config when a specified boolean config is in a given state
  • Optionally hide the config instead of just disabling it
  • Autoconfig (annotation based) implementation
    • @EnableIf and @EnableIfGroup declare a dependency that will enable/disable the annotated config entry
    • @ShowIf and @ShowIfGroup declare a dependency that will show/hide the annotated config entry
    • Declaring multiple @EnableIf or @ShowIf annotations on a single field will automatically create a group that depends on all of those dependencies.
    • Might consider renaming these... Maybe @EnabledWhen, @DisabledUnless, etc?
  • Consider UI for different config types
    • Editing is disabled
    • Text is darkened
    • Sub-Category & List icons are darkened
    • Sub-Category & Lists are always collapsed/cannot be expanded
    • (Not sure what other edge-cases need to be covered?)
  • Refine UI for various edge-cases (edge-cases still need to be identified)
    • I noticed some config entries don't support tooltips, such as the List entries.
  • Improve generated tooltips when there are multiple dependencies, groups, multiple conditions, etc
  • Refine the cloth-config dependency API using a dependency builder
  • Groups of dependencies
  • Boolean dependencies
  • Enum dependencies
  • Numeric dependencies
  • Numeric comparison conditions (i.e. greater than, less than, equal, not equal)
  • KeyBind dependencies?
  • List dependencies?
  • String/text dependencies?
  • RegexCondition class for text-based dependencies?
  • Separate API from implementation (as much as practical)

Adding the colour formatting programmatically allows us to use the same text in contexts where we don't want colour
Not functional yet, but can be set on fields
The sub-category implementation is pretty clean, but for some reason the root level needed a whole separate filter, because hidden entries were being added back at the wrong index...
Implement disablement for lists and sub-categories
@MattSturgeon

This comment was marked as outdated.

To allow for having multiple dependencies and non-boolean dependencies, lets store dependencies in the form of a class.

This allows any one entry to depend on multiple other entries. **All** dependencies must be met for the entry to be enabled.

A dependency can be set to "hide when disabled", if one of the unmet dependencies has this set, the entry is not shown at all.

A dependency can have multiple conditions defined, if **any** condition is met the dependency is satisfied.
Static dependency generator methods seem cleaner than having the user construct an object and then run setter methods on it.

Similar UX to using builders.
- Move dependency generators to the base `Dependency` class.
- Move all dependencies into the same package, to allow constructors to be package-private.
- Add javadoc to `Dependency`'s methods.
`SelectionDependency` allows entries to depend on a `SelectionListEntry` being set to a particular value.

Multiple condition values can also be set. If any are met the dependency is satisfied.

Sub-classes such as `EnumListEntry` are also supported.
@MattSturgeon
Copy link
Contributor Author

Selection/Enum entries can now be dependencies too:

selection.dependencies.mp4

Multiple condition values can be set, e.g:

entryBuilder.startBooleanToggle(Component.literal("I only work when a good option is chosen..."), true)
    .setTooltip(Component.literal("Select good or better above"))
    .addDependency(Dependency.disabledWhenNotSatisfied(enumDependency,
                                                       DependencyDemoEnum.EXCELLENT,
                                                       DependencyDemoEnum.GOOD))
    .build();

@shedaniel
Copy link
Owner

Ah, looks good after a primitive check! Thanks for implementing it!

Also, satisfied -> met.
Untested, but hopefully mostly ok... Need to setup an Autoconfig demo that can be run in-game...

TODO: Allow a single field to have multiple dependencies.

Config fields can be annotated with `@DependsOn()` which can have the following parameters set:

- `value` the i18n key of the field to be depended on
- `hiddenWhenNotMet` whether the dependency should cause the dependent entry to become completely hidden
- `conditions` an array of condition strings that will be parsed to the appropriate type and compared against the dependency's value.
A wrapper annotation which contains a list of `@DependsOn` annotations. ConfigScreenProvider iterates over each DependsOn and adds multiple dependencies.
@MattSturgeon
Copy link
Contributor Author

MattSturgeon commented Mar 12, 2023

Added an initial annotation implementation. I've haven't tested it yet because I couldn't figure out an easy way to run the Autoconfig demo in my debugging environment.

No compiler warnings though so fingers crossed... 🤞

EDIT: Figured out I can shift-click the button in mod-menu to open the autoconfig example.

Ah, looks good after a primitive check! Thanks for implementing it!

Thanks, looking forward to more detailed feedback when you have time 👍

Move most of the implementation into the abstract class `ConfigEntryDependency`
Instead of each config entry having a collection of dependencies, create a new special DependencyGroup that can contain multiple other dependencies.

`DependencyGroup` implements `Dependency`, so it can be nested if desired. However, the annotation equivalent (`@DependsOnGroup`) cannot be nested due to annotation design limitations.

In other words, Autoconfig can only have one group per field.

Conditions are effectively logical operators, allowing for more advanced dependencies. E.g. `ALL` requires all dependencies be met, while `ANY` only requires one-or-more.
`DependencyManager` is used to build and setup dependencies in Autoconfig.

It needs to keep track of all config entries created/transformed.

Made it so that GuiRegistryAccess can have a DependencyManager instance associated with it.
Each condition type has its own interface, e.g. Comparative for numeric comparison or Equality for simple `.equals()`, `MultiCondition` for dealing with collections.

Additionally, there are interfaces that control how the value is provided.

- `StaticCondition` has a constant value
- `MatcherCondition` uses the value of another `ConfigEntry`

This breaks tooltip generation _again_
This could be used for e.g. comparative checking on numeric list entries
The underlying cloth-config implementation shouldn't need flags
If a dependency has multiple conditions it may make sense to match "all" instead of "any"...
Allow the predicate to take two dissimilar params, e.g. regex Pattern and String. Most of the time, both params will just be the Condition's <T>
Instead of implementing a whole bunch of condition implementations, lets have a single predicate-based condition and move all the complexity into various builders.
Generate tooltips and descriptions when building rather than while rendering
Instead of building a specific implementation of Dependency
They are redundant now that we use builders
MattSturgeon added a commit to MattSturgeon/cloth-config that referenced this pull request May 7, 2023
Ported the demo from my previous draft PR shedaniel#195
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants