Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/v9' into v10
Browse files Browse the repository at this point in the history
# Conflicts:
#	common/src/main/java/me/shedaniel/clothconfig2/gui/entries/BaseListEntry.java
#	common/src/main/java/me/shedaniel/clothconfig2/gui/widget/DynamicEntryListWidget.java
#	gradle.properties
  • Loading branch information
shedaniel committed Jul 9, 2023
2 parents b5a442c + 0492eca commit 84b6468
Show file tree
Hide file tree
Showing 42 changed files with 700 additions and 156 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,7 @@
import com.mojang.blaze3d.platform.InputConstants;
import me.shedaniel.autoconfig.util.Utils;
import me.shedaniel.clothconfig2.api.*;
import me.shedaniel.clothconfig2.gui.entries.MultiElementListEntry;
import me.shedaniel.clothconfig2.gui.entries.NestedListListEntry;
import me.shedaniel.clothconfig2.gui.entries.*;
import me.shedaniel.clothconfig2.impl.builders.DropdownMenuBuilder;
import me.shedaniel.clothconfig2.impl.builders.SubCategoryBuilder;
import net.minecraft.ChatFormatting;
Expand Down Expand Up @@ -79,6 +78,10 @@ public int hashCode() {
return result;
}
}

enum DependencyDemoEnum {
EXCELLENT, GOOD, OKAY, BAD, HORRIBLE
}

ConfigBuilder builder = ConfigBuilder.create().setTitle(Component.translatable("title.cloth-config.config"));
builder.setDefaultBackgroundTexture(new ResourceLocation("minecraft:textures/block/oak_planks.png"));
Expand Down Expand Up @@ -154,6 +157,51 @@ public int hashCode() {
}
}
));

SubCategoryBuilder depends = entryBuilder.startSubCategory(Component.literal("Dependencies")).setExpanded(true);
BooleanListEntry dependency = entryBuilder.startBooleanToggle(Component.literal("A cool toggle"), false).setTooltip(Component.literal("Toggle me...")).build();
depends.add(dependency);
Collection<BooleanListEntry> toggles = new LinkedList<>();
toggles.add(entryBuilder.startBooleanToggle(Component.literal("I only work when cool is toggled..."), true)
.setRequirement(Requirement.isTrue(dependency)).build());
toggles.add(entryBuilder.startBooleanToggle(Component.literal("I only appear when cool is toggled..."), true)
.setDisplayRequirement(Requirement.isTrue(dependency)).build());
depends.addAll(toggles);
depends.add(entryBuilder.startBooleanToggle(Component.literal("I only work when cool matches both of these toggles ^^"), true)
.setRequirement(Requirement.all(
toggles.stream()
.map(toggle -> Requirement.matches(dependency, toggle))
.toArray(Requirement[]::new)))
.build());
SubCategoryBuilder dependantSub = entryBuilder.startSubCategory(Component.literal("Sub-categories can have requirements too..."))
.setRequirement(Requirement.isTrue(dependency));
dependantSub.add(entryBuilder.startTextDescription(Component.literal("This sub category depends on Cool being toggled")).build());
dependantSub.add(entryBuilder.startBooleanToggle(Component.literal("Example entry"), true).build());
dependantSub.add(entryBuilder.startBooleanToggle(Component.literal("Another example..."), true).build());
depends.add(dependantSub.build());
depends.add(entryBuilder.startLongList(Component.literal("Even lists!"), Arrays.asList(1L, 2L, 3L)).setDefaultValue(Arrays.asList(1L, 2L, 3L))
.setRequirement(Requirement.isTrue(dependency)).build());
EnumListEntry<DependencyDemoEnum> enumDependency = entryBuilder.startEnumSelector(Component.literal("Select a good or bad option"), DependencyDemoEnum.class, DependencyDemoEnum.OKAY).build();
depends.add(enumDependency);
IntegerSliderEntry intDependency = entryBuilder.startIntSlider(Component.literal("Select something big or small"), 50, -100, 100).build();
depends.add(intDependency);
depends.add(entryBuilder.startBooleanToggle(Component.literal("I only work when a good option is chosen..."), true).setTooltip(Component.literal("Select good or better above"))
.setRequirement(Requirement.isValue(enumDependency, DependencyDemoEnum.EXCELLENT, DependencyDemoEnum.GOOD))
.build());
depends.add(entryBuilder.startBooleanToggle(Component.literal("I need a good option AND a cool toggle!"), true).setTooltip(Component.literal("Select good or better and also toggle cool"))
.setRequirement(Requirement.all(
Requirement.isTrue(dependency),
Requirement.isValue(enumDependency, DependencyDemoEnum.EXCELLENT, DependencyDemoEnum.GOOD)))
.build());
depends.add(entryBuilder.startBooleanToggle(Component.literal("I only work when numbers are extreme!"), true)
.setTooltip(Component.literal("Move the slider..."))
.setRequirement(Requirement.any(
() -> intDependency.getValue() < -70,
() -> intDependency.getValue() > 70))
.build());

testing.addEntry(depends.build());

testing.addEntry(entryBuilder.startTextDescription(
Component.translatable("text.cloth-config.testing.1",
Component.literal("ClothConfig").withStyle(s -> s.withBold(true).withHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_ITEM, new HoverEvent.ItemStackInfo(Util.make(new ItemStack(Items.PINK_WOOL), stack -> stack.setHoverName(Component.literal("(\u30FB\u2200\u30FB)")).enchant(Enchantments.BLOCK_EFFICIENCY, 10)))))),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,21 +29,21 @@
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.minecraft.ChatFormatting;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.Font;
import net.minecraft.network.chat.Component;
import net.minecraft.network.chat.MutableComponent;
import net.minecraft.util.FormattedCharSequence;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Optional;
import java.util.*;
import java.util.function.Consumer;
import java.util.function.Supplier;

@Environment(EnvType.CLIENT)
public abstract class AbstractConfigEntry<T> extends DynamicElementListWidget.ElementEntry<AbstractConfigEntry<T>> implements ReferenceProvider<T> {
public abstract class AbstractConfigEntry<T> extends DynamicElementListWidget.ElementEntry<AbstractConfigEntry<T>> implements ReferenceProvider<T>, ValueHolder<T> {
private AbstractConfigScreen screen;
private Supplier<Optional<Component>> errorSupplier;
@Nullable
Expand Down Expand Up @@ -92,6 +92,8 @@ public Component getDisplayedFieldName() {
text = text.withStyle(ChatFormatting.ITALIC);
if (!hasError && !isEdited)
text = text.withStyle(ChatFormatting.GRAY);
if (!isEnabled())
text = text.withStyle(ChatFormatting.DARK_GRAY);
return text;
}

Expand All @@ -117,8 +119,6 @@ public void appendSearchTags(Iterable<String> tags) {
}
}

public abstract T getValue();

public final Optional<Component> getConfigError() {
if (errorSupplier != null && errorSupplier.get().isPresent())
return errorSupplier.get();
Expand Down Expand Up @@ -146,6 +146,19 @@ public final void addTooltip(@NotNull Tooltip tooltip) {
screen.addTooltip(tooltip);
}

protected FormattedCharSequence[] wrapLinesToScreen(Component[] lines) {
return wrapLines(lines, screen.width);
}

protected FormattedCharSequence[] wrapLines(Component[] lines, int width) {
final Font font = Minecraft.getInstance().font;

return Arrays.stream(lines)
.map(line -> font.split(line, width))
.flatMap(List::stream)
.toArray(FormattedCharSequence[]::new);
}

public void updateSelected(boolean isSelected) {}

@ApiStatus.Internal
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ public void setRequiresRestart(boolean requiresRestart) {
}

public boolean isEditable() {
return getConfigScreen().isEditable() && editable;
return getConfigScreen().isEditable() && editable && isEnabled();
}

public void setEditable(boolean editable) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/*
* This file is part of Cloth Config.
* Copyright (C) 2020 - 2021 shedaniel
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/

package me.shedaniel.clothconfig2.api;

import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Nullable;

@ApiStatus.Experimental
public interface DisableableWidget {

/**
* Checks whether this config entry gui is enabled.
*
* <p>Requirements are checked independently (once per tick). This method simply reads the result of the latest
* check, making it extremely cheap to run.
*
* <p>If {@link HideableWidget#isDisplayed()} is false, this will also be false.
*
* @return whether the config entry is enabled
* @see HideableWidget#isDisplayed()
* @see TickableWidget#tick()
*/
boolean isEnabled();

void setRequirement(@Nullable Requirement requirement);

@Nullable Requirement getRequirement();

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/*
* This file is part of Cloth Config.
* Copyright (C) 2020 - 2021 shedaniel
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/

package me.shedaniel.clothconfig2.api;

import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Nullable;

@ApiStatus.Experimental
public interface HideableWidget {

/**
* Checks whether this config entry gui is shown on screen.
*
* <p>Requirements are checked independently (once per tick). This method simply reads the result of the latest
* check, making it extremely cheap to run.
*
* @return whether to display the config entry
* @see DisableableWidget#isEnabled()
* @see TickableWidget#tick()
*/
boolean isDisplayed();

void setDisplayRequirement(@Nullable Requirement requirement);

@Nullable Requirement getDisplayRequirement();
}
124 changes: 124 additions & 0 deletions common/src/main/java/me/shedaniel/clothconfig2/api/Requirement.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
/*
* This file is part of Cloth Config.
* Copyright (C) 2020 - 2021 shedaniel
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/

package me.shedaniel.clothconfig2.api;

import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Nullable;

import java.util.Arrays;
import java.util.HashSet;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
* Represents a predicate (boolean-valued function) without arguments.
*
* <p>This is a <a href="{@docRoot}/java/util/function/package-summary.html">functional interface</a>
* whose functional method is {@link #check()}.
*/
@FunctionalInterface
@ApiStatus.Experimental
public interface Requirement {

/**
* Checks if this requirement is currently true.
*/
boolean check();

/**
* Generates a {@link Requirement} that is true when {@code dependency}'s value is one of the provided values.
*/
@SafeVarargs
static <T> Requirement isValue(ValueHolder<T> dependency, @Nullable T firstValue, @Nullable T... otherValues) {
Set<@Nullable T> values = Stream.concat(Stream.of(firstValue), Arrays.stream(otherValues))
.collect(Collectors.toCollection(HashSet::new));

return () -> values.contains(dependency.getValue());
}

/**
* Generates a {@link Requirement} that is true when {@code firstDependency}'s value equals {@code secondDependency}'s value.
*/
static <T> Requirement matches(ValueHolder<T> firstDependency, ValueHolder<T> secondDependency) {
return () -> Objects.equals(firstDependency.getValue(), secondDependency.getValue());
}

/**
* Generates a {@link Requirement} that is true when {@code dependency}'s value is true.
*/
static Requirement isTrue(ValueHolder<Boolean> dependency) {
return () -> Boolean.TRUE.equals(dependency.getValue());
}

/**
* Generates a {@link Requirement} that is true when {@code dependency}'s value is false.
*/
static Requirement isFalse(ValueHolder<Boolean> dependency) {
return () -> Boolean.FALSE.equals(dependency.getValue());
}

/**
* Generates a {@link Requirement} that is true when the given {@code requirement} is false.
*/
static Requirement not(Requirement requirement) {
return () -> !requirement.check();
}

/**
* Generates a {@link Requirement} that is true when all the given requirements are true.
*/
static Requirement all(Requirement... requirements) {
return () -> Arrays.stream(requirements).allMatch(Requirement::check);
}

/**
* Generates a {@link Requirement} that is true when any of the given requirements are true.
*/
static Requirement any(Requirement... requirements) {
return () -> Arrays.stream(requirements).anyMatch(Requirement::check);
}

/**
* Generates a {@link Requirement} that is true when none of the given requirements are true, i.e. all are false.
*/
static Requirement none(Requirement... requirements) {
return () -> Arrays.stream(requirements).noneMatch(Requirement::check);
}

/**
* Generates a {@link Requirement} that is true when precisely one of the given requirements is true.
*/
static Requirement one(Requirement... requirements) {
return () -> {
// Use a for loop instead of Stream.count() so that we can return early. We only need to count past 1.
boolean oneFound = false;
for (Requirement requirement : requirements) {
if (!requirement.check())
continue;
if (oneFound)
return false;
oneFound = true;
}
return oneFound;
};
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/*
* This file is part of Cloth Config.
* Copyright (C) 2020 - 2021 shedaniel
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/

package me.shedaniel.clothconfig2.api;

import org.jetbrains.annotations.Nullable;

public interface ValueHolder<T> {
/**
* Get the value held by this Value Holder.
*
* <p>Depending on the implementation, this method may or may not be {@link Nullable}.
*
* @return the current value.
*/
T getValue();
}
Loading

0 comments on commit 84b6468

Please sign in to comment.