From 47ae69cd83f36aa5acf4bfedf9bbccc0eec34b67 Mon Sep 17 00:00:00 2001 From: shedaniel Date: Sat, 3 Dec 2022 22:17:27 +0800 Subject: Add init progress to IME --- .../method/CharacterUnpackingInputMethod.java | 5 +- .../rei/api/client/search/method/InputMethod.java | 97 ++++++++++++++++++++++ .../client/gui/screen/ConfigReloadingScreen.java | 33 ++++---- .../gui/widget/CraftableFilterButtonWidget.java | 10 ++- .../search/method/unihan/UniHanInputMethod.java | 10 ++- .../client/search/method/unihan/UniHanManager.java | 14 +++- .../assets/roughlyenoughitems/lang/en_us.json | 1 + 7 files changed, 145 insertions(+), 25 deletions(-) diff --git a/api/src/main/java/me/shedaniel/rei/api/client/search/method/CharacterUnpackingInputMethod.java b/api/src/main/java/me/shedaniel/rei/api/client/search/method/CharacterUnpackingInputMethod.java index f14e1b334..f7f1779f8 100644 --- a/api/src/main/java/me/shedaniel/rei/api/client/search/method/CharacterUnpackingInputMethod.java +++ b/api/src/main/java/me/shedaniel/rei/api/client/search/method/CharacterUnpackingInputMethod.java @@ -45,9 +45,8 @@ public interface CharacterUnpackingInputMethod extends InputMethod { default String suggestInputString(String str) { return str.codePoints().mapToObj(c -> { List chars = expendSourceChar(c); - String result; - if (chars.isEmpty()) return result = ((char) c) + ""; - result = chars.get(0).phonemes().stream() + if (chars.isEmpty()) return ((char) c) + ""; + String result = chars.get(0).phonemes().stream() .flatMap(integers -> integers.intStream().mapToObj(value -> ((char) value) + "")) .collect(Collectors.joining()); if (result.codePointCount(0, result.length()) == 1 && result.codePointAt(0) == c) { diff --git a/api/src/main/java/me/shedaniel/rei/api/client/search/method/InputMethod.java b/api/src/main/java/me/shedaniel/rei/api/client/search/method/InputMethod.java index 90e6ba092..fc81acd33 100644 --- a/api/src/main/java/me/shedaniel/rei/api/client/search/method/InputMethod.java +++ b/api/src/main/java/me/shedaniel/rei/api/client/search/method/InputMethod.java @@ -34,35 +34,132 @@ import java.util.List; import java.util.concurrent.CompletableFuture; import java.util.concurrent.Executor; +/** + * An input method to match a search filter to another source input. + * + * @param the type of expansion from the search filter + * @see me.shedaniel.rei.impl.client.search.method.DefaultInputMethod + */ @ApiStatus.Experimental public interface InputMethod { + /** + * Returns the active input method. + * + * @return the active input method + */ static InputMethod active() { return InputMethodRegistry.getInstance().getOrDefault(ConfigObject.getInstance().getInputMethodId()); } + /** + * Returns the list of all Minecraft supported locales. + * + * @return the list of all Minecraft supported locales + */ static List getAllLocales() { return CollectionUtils.map(Minecraft.getInstance().getLanguageManager().getLanguages(), info -> new Locale(info.getCode(), Component.literal(info.getName()))); } + /** + * Returns the list of locales that are supported by this input method. + * You should use {@link #getAllLocales()} to get the list of all Minecraft supported locales. + * + * @return the list of locales that are supported by this input method + */ List getMatchingLocales(); + /** + * Returns all the possible expansions from the search filter. + * + * @param filter the search filter + * @return all the possible expansions from the search filter + */ Iterable expendFilter(String filter); + /** + * Returns whether the search filter matches the input. + * + * @param str the input + * @param substr the expanded search filter + * @return whether the search filter matches the input + */ boolean contains(String str, T substr); + /** + * Returns a suggested expansion from the search filter. + * + * @param str the input + * @return a suggested expansion from the search filter, or {@code null} if no suggestion is available + */ @Nullable default String suggestInputString(String str) { return null; } + /** + * Prepares the input method for activation. + * + * @param executor the executor to run the preparation on + * @return a future that completes when the preparation is done + */ CompletableFuture prepare(Executor executor); + /** + * Prepares the input method for activation. + * + * @param executor the executor to run the preparation on + * @param progressCallback the callback to call when the progress is updated + * @return a future that completes when the preparation is done + */ + default CompletableFuture prepare(Executor executor, ProgressCallback progressCallback) { + progressCallback.onProgress(0.0); + return prepare(executor).whenComplete((aVoid, throwable) -> progressCallback.onProgress(1.0)); + } + + /** + * Disposes the input method. + * + * @param executor the executor to run the disposal on + * @return a future that completes when the disposal is done + */ CompletableFuture dispose(Executor executor); + /** + * Disposes the input method. + * + * @param executor the executor to run the disposal on + * @param progressCallback the callback to call when the progress is updated + * @return a future that completes when the disposal is done + */ + default CompletableFuture dispose(Executor executor, ProgressCallback progressCallback) { + progressCallback.onProgress(0.0); + return dispose(executor).whenComplete((aVoid, throwable) -> progressCallback.onProgress(1.0)); + } + + /** + * Returns the name of this input method. + * + * @return the name of this input method + */ Component getName(); + /** + * Returns the description of this input method. + * + * @return the description of this input method + */ Component getDescription(); record Locale(String code, Component name) {} + + @FunctionalInterface + interface ProgressCallback { + /** + * Called when the progress of the preparation is updated. + * + * @param progress the progress of the preparation, between 0.0 and 1.0 + */ + void onProgress(double progress); + } } diff --git a/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/screen/ConfigReloadingScreen.java b/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/screen/ConfigReloadingScreen.java index eeb1a1852..082ac44ec 100644 --- a/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/screen/ConfigReloadingScreen.java +++ b/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/screen/ConfigReloadingScreen.java @@ -28,14 +28,17 @@ import net.minecraft.Util; import net.minecraft.client.gui.screens.Screen; import net.minecraft.network.chat.Component; import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.Nullable; import java.util.function.BooleanSupplier; +import java.util.function.Supplier; @ApiStatus.Internal public class ConfigReloadingScreen extends Screen { private final Component title; private final BooleanSupplier predicate; - private Runnable parent; + private Supplier<@Nullable Component> subtitle = () -> null; + private final Runnable parent; public ConfigReloadingScreen(Component title, BooleanSupplier predicate, Runnable parent) { super(Component.empty()); @@ -44,6 +47,10 @@ public class ConfigReloadingScreen extends Screen { this.parent = parent; } + public void setSubtitle(Supplier<@Nullable Component> subtitle) { + this.subtitle = subtitle; + } + @Override public boolean shouldCloseOnEsc() { return false; @@ -56,21 +63,17 @@ public class ConfigReloadingScreen extends Screen { parent.run(); return; } - drawCenteredString(matrices, this.font, title, this.width / 2, this.height / 2 - 50, 16777215); - String text; - switch ((int) (Util.getMillis() / 300L % 4L)) { - case 0: - default: - text = "O o o"; - break; - case 1: - case 3: - text = "o O o"; - break; - case 2: - text = "o o O"; + drawCenteredString(matrices, this.font, title, this.width / 2, this.height / 2 - 50, 0xffffff); + String text = switch ((int) (Util.getMillis() / 300L % 4L)) { + case 1, 3 -> "o O o"; + case 2 -> "o o O"; + default -> "O o o"; + }; + drawCenteredString(matrices, this.font, text, this.width / 2, this.height / 2 - 50 + 9, 0x808080); + Component subtitle = this.subtitle.get(); + if (subtitle != null) { + drawCenteredString(matrices, this.font, subtitle, this.width / 2, this.height / 2 - 50 + 9 + 9, 0x808080); } - drawCenteredString(matrices, this.font, text, this.width / 2, this.height / 2 - 41, 8421504); super.render(matrices, mouseX, mouseY, delta); } } diff --git a/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/widget/CraftableFilterButtonWidget.java b/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/widget/CraftableFilterButtonWidget.java index 94bf01921..d0d611890 100644 --- a/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/widget/CraftableFilterButtonWidget.java +++ b/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/widget/CraftableFilterButtonWidget.java @@ -49,6 +49,7 @@ import net.minecraft.client.gui.screens.Screen; import net.minecraft.client.renderer.entity.ItemRenderer; import net.minecraft.network.chat.Component; import net.minecraft.resources.ResourceLocation; +import net.minecraft.util.Mth; import net.minecraft.world.item.ItemStack; import net.minecraft.world.level.block.Blocks; @@ -130,7 +131,8 @@ public class CraftableFilterButtonWidget { ConfigManagerImpl.getInstance().getConfig().setInputMethodId(new ResourceLocation("rei:default")); }).join(); - CompletableFuture future = pair.getValue().prepare(service).whenComplete((unused, throwable) -> { + double[] progress = {0}; + CompletableFuture future = pair.getValue().prepare(service, p -> progress[0] = Mth.clamp(p, 0, 1)).whenComplete((unused, throwable) -> { if (throwable != null) { InternalLogger.getInstance().error("Failed to prepare input method", throwable); ConfigManagerImpl.getInstance().getConfig().setInputMethodId(new ResourceLocation("rei:default")); @@ -139,10 +141,12 @@ public class CraftableFilterButtonWidget { } }); Screen screen = Minecraft.getInstance().screen; - Minecraft.getInstance().setScreen(new ConfigReloadingScreen(Component.translatable("text.rei.input.methods.initializing"), + ConfigReloadingScreen reloadingScreen = new ConfigReloadingScreen(Component.translatable("text.rei.input.methods.initializing"), () -> !future.isDone(), () -> { Minecraft.getInstance().setScreen(screen); - })); + }); + reloadingScreen.setSubtitle(() -> new TranslatableComponent("text.rei.input.methods.reload.progress", String.format("%.2f", progress[0] * 100))); + Minecraft.getInstance().setScreen(reloadingScreen); future.whenComplete((unused, throwable) -> { service.shutdown(); }); diff --git a/runtime/src/main/java/me/shedaniel/rei/impl/client/search/method/unihan/UniHanInputMethod.java b/runtime/src/main/java/me/shedaniel/rei/impl/client/search/method/unihan/UniHanInputMethod.java index 1402c54ba..91619f644 100644 --- a/runtime/src/main/java/me/shedaniel/rei/impl/client/search/method/unihan/UniHanInputMethod.java +++ b/runtime/src/main/java/me/shedaniel/rei/impl/client/search/method/unihan/UniHanInputMethod.java @@ -50,9 +50,15 @@ public abstract class UniHanInputMethod implements InputMethod { @Override public CompletableFuture prepare(Executor executor) { + return this.prepare(executor, p -> {}); + } + + @Override + public CompletableFuture prepare(Executor executor, ProgressCallback progressCallback) { return dispose(executor) - .thenRunAsync(manager::download, executor) - .thenRunAsync(this::load, executor); + .thenRunAsync(() -> manager.download(p -> progressCallback.onProgress(p * 0.99)), executor) + .thenRunAsync(this::load, executor) + .whenComplete((aVoid, throwable) -> progressCallback.onProgress(1.0)); } public void load() { diff --git a/runtime/src/main/java/me/shedaniel/rei/impl/client/search/method/unihan/UniHanManager.java b/runtime/src/main/java/me/shedaniel/rei/impl/client/search/method/unihan/UniHanManager.java index bf36fc130..f2f9a5b74 100644 --- a/runtime/src/main/java/me/shedaniel/rei/impl/client/search/method/unihan/UniHanManager.java +++ b/runtime/src/main/java/me/shedaniel/rei/impl/client/search/method/unihan/UniHanManager.java @@ -23,6 +23,7 @@ package me.shedaniel.rei.impl.client.search.method.unihan; +import me.shedaniel.rei.api.client.search.method.InputMethod; import me.shedaniel.rei.impl.common.InternalLogger; import org.apache.commons.io.IOUtils; import org.apache.commons.io.LineIterator; @@ -50,10 +51,18 @@ public class UniHanManager { return Files.exists(unihanPath); } - public void download() { + public void download(InputMethod.ProgressCallback progressCallback) { + try { + download("https://www.shedaniel.moe/uoAqECzbQo5s.zip", progressCallback); + } catch (Exception e) { + download("https://www.unicode.org/Public/UCD/latest/ucd/Unihan.zip", progressCallback); + } + } + + public void download(String URL, InputMethod.ProgressCallback progressCallback) { if (downloaded()) return; try { - URL url = new URL("https://www.unicode.org/Public/UCD/latest/ucd/Unihan.zip"); + URL url = new URL("https://shedaniel.moe/uoAqECzbQo5s.zip"); Files.deleteIfExists(unihanPath); Path parent = unihanPath.getParent(); if (parent != null) Files.createDirectories(parent); @@ -74,6 +83,7 @@ public class UniHanManager { lastPercent = percent; InternalLogger.getInstance().debug("Downloading UniHan Progress: %d%%".formatted(percent)); } + progressCallback.onProgress(progress); bufferedStream.write(data, 0, x); } bufferedStream.close(); diff --git a/runtime/src/main/resources/assets/roughlyenoughitems/lang/en_us.json b/runtime/src/main/resources/assets/roughlyenoughitems/lang/en_us.json index c487cfae8..796f5bf9e 100755 --- a/runtime/src/main/resources/assets/roughlyenoughitems/lang/en_us.json +++ b/runtime/src/main/resources/assets/roughlyenoughitems/lang/en_us.json @@ -78,6 +78,7 @@ "text.rei.addons": "REI Addons", "text.rei.shapeless": "Shapeless", "text.rei.input.methods": "Input Methods", + "text.rei.input.methods.reload.progress": "Progress: %s%%", "text.rei.input.methods.default": "Default", "text.rei.input.methods.default.description": "The default input method. This does not transform the search term.", "text.rei.input.methods.jyutping": "Jyutping (Cantonese)", -- cgit From 640704af10d61dede9a88728b1d51438233e8cdb Mon Sep 17 00:00:00 2001 From: shedaniel Date: Fri, 9 Dec 2022 02:08:13 +0800 Subject: Add Hint Onboarding, Fix #1156, Fix #1256 --- .../rei/api/client/config/ConfigObject.java | 3 + .../api/client/favorites/FavoriteMenuEntry.java | 15 + .../method/CharacterUnpackingInputMethod.java | 4 +- .../rei/api/client/search/method/InputMethod.java | 10 + .../me/shedaniel/rei/impl/ClientInternals.java | 12 + .../rei/RoughlyEnoughItemsCoreClient.java | 6 + .../rei/impl/client/config/ConfigObjectImpl.java | 10 + .../rei/impl/client/gui/ScreenOverlayImpl.java | 28 +- .../impl/client/gui/modules/AbstractMenuEntry.java | 4 +- .../rei/impl/client/gui/modules/Menu.java | 32 +- .../rei/impl/client/gui/modules/MenuAccess.java | 3 +- .../rei/impl/client/gui/modules/MenuEntry.java | 44 --- .../client/gui/modules/entries/SubMenuEntry.java | 19 +- .../impl/client/gui/widget/ConfigButtonWidget.java | 4 +- .../gui/widget/CraftableFilterButtonWidget.java | 50 ++- .../rei/impl/client/gui/widget/EntryWidget.java | 2 +- .../widget/favorites/FavoritesEntriesManager.java | 10 + .../impl/client/gui/widget/hint/HintWidget.java | 176 +++++++++++ .../gui/widget/hint/HintsContainerWidget.java | 91 ++++++ .../gui/widget/region/RegionEntryWidget.java | 50 +-- .../gui/widget/search/OverlaySearchField.java | 21 +- .../method/unihan/DoublePinyinInputMethod.java | 351 +++++++++++++++++++++ .../client/runtime/DefaultClientRuntimePlugin.java | 6 +- .../plugin/client/runtime/InputMethodWatcher.java | 6 +- .../assets/roughlyenoughitems/lang/en_us.json | 16 +- 25 files changed, 825 insertions(+), 148 deletions(-) delete mode 100644 runtime/src/main/java/me/shedaniel/rei/impl/client/gui/modules/MenuEntry.java create mode 100644 runtime/src/main/java/me/shedaniel/rei/impl/client/gui/widget/hint/HintWidget.java create mode 100644 runtime/src/main/java/me/shedaniel/rei/impl/client/gui/widget/hint/HintsContainerWidget.java create mode 100644 runtime/src/main/java/me/shedaniel/rei/impl/client/search/method/unihan/DoublePinyinInputMethod.java diff --git a/api/src/main/java/me/shedaniel/rei/api/client/config/ConfigObject.java b/api/src/main/java/me/shedaniel/rei/api/client/config/ConfigObject.java index a932071db..f62e3590a 100644 --- a/api/src/main/java/me/shedaniel/rei/api/client/config/ConfigObject.java +++ b/api/src/main/java/me/shedaniel/rei/api/client/config/ConfigObject.java @@ -265,6 +265,9 @@ public interface ConfigObject { boolean doDisplayFavoritesTooltip(); + @ApiStatus.Experimental + boolean doDisplayIMEHints(); + boolean doesFastEntryRendering(); boolean doesCacheEntryRendering(); diff --git a/api/src/main/java/me/shedaniel/rei/api/client/favorites/FavoriteMenuEntry.java b/api/src/main/java/me/shedaniel/rei/api/client/favorites/FavoriteMenuEntry.java index 61655c432..35bc5a10c 100644 --- a/api/src/main/java/me/shedaniel/rei/api/client/favorites/FavoriteMenuEntry.java +++ b/api/src/main/java/me/shedaniel/rei/api/client/favorites/FavoriteMenuEntry.java @@ -23,14 +23,29 @@ package me.shedaniel.rei.api.client.favorites; +import dev.architectury.utils.value.BooleanValue; import me.shedaniel.rei.api.client.gui.widgets.Widget; +import me.shedaniel.rei.impl.ClientInternals; import net.fabricmc.api.EnvType; import net.fabricmc.api.Environment; +import net.minecraft.network.chat.Component; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.Nullable; +import java.util.List; + @Environment(EnvType.CLIENT) public abstract class FavoriteMenuEntry extends Widget { + @ApiStatus.Experimental + public static FavoriteMenuEntry createSubMenu(Component text, List entries) { + return ClientInternals.createSubMenuEntry(text, entries); + } + + @ApiStatus.Experimental + public static FavoriteMenuEntry createToggle(Component text, BooleanValue value) { + return ClientInternals.createToggleEntry(text, value); + } + @Nullable @ApiStatus.Internal public Runnable closeMenu = null; diff --git a/api/src/main/java/me/shedaniel/rei/api/client/search/method/CharacterUnpackingInputMethod.java b/api/src/main/java/me/shedaniel/rei/api/client/search/method/CharacterUnpackingInputMethod.java index f7f1779f8..c27f25dbd 100644 --- a/api/src/main/java/me/shedaniel/rei/api/client/search/method/CharacterUnpackingInputMethod.java +++ b/api/src/main/java/me/shedaniel/rei/api/client/search/method/CharacterUnpackingInputMethod.java @@ -24,6 +24,7 @@ package me.shedaniel.rei.api.client.search.method; import it.unimi.dsi.fastutil.ints.IntList; +import net.minecraft.util.Mth; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.Nullable; @@ -46,7 +47,8 @@ public interface CharacterUnpackingInputMethod extends InputMethod { return str.codePoints().mapToObj(c -> { List chars = expendSourceChar(c); if (chars.isEmpty()) return ((char) c) + ""; - String result = chars.get(0).phonemes().stream() + int i = Mth.floor((System.currentTimeMillis() / 1000L % (double) chars.size())); + String result = chars.get(i).phonemes().stream() .flatMap(integers -> integers.intStream().mapToObj(value -> ((char) value) + "")) .collect(Collectors.joining()); if (result.codePointCount(0, result.length()) == 1 && result.codePointAt(0) == c) { diff --git a/api/src/main/java/me/shedaniel/rei/api/client/search/method/InputMethod.java b/api/src/main/java/me/shedaniel/rei/api/client/search/method/InputMethod.java index fc81acd33..6a19e5ecb 100644 --- a/api/src/main/java/me/shedaniel/rei/api/client/search/method/InputMethod.java +++ b/api/src/main/java/me/shedaniel/rei/api/client/search/method/InputMethod.java @@ -24,6 +24,7 @@ package me.shedaniel.rei.api.client.search.method; import me.shedaniel.rei.api.client.config.ConfigObject; +import me.shedaniel.rei.api.client.favorites.FavoriteMenuEntry; import me.shedaniel.rei.api.common.util.CollectionUtils; import net.minecraft.client.Minecraft; import net.minecraft.network.chat.Component; @@ -151,6 +152,15 @@ public interface InputMethod { */ Component getDescription(); + /** + * Returns the list of menu entries that will be used as options. + * + * @return the list of menu entries that will be used as options + */ + default List getOptionsMenuEntries() { + return List.of(); + } + record Locale(String code, Component name) {} @FunctionalInterface diff --git a/api/src/main/java/me/shedaniel/rei/impl/ClientInternals.java b/api/src/main/java/me/shedaniel/rei/impl/ClientInternals.java index 2f4a8f45c..3b6e62019 100644 --- a/api/src/main/java/me/shedaniel/rei/impl/ClientInternals.java +++ b/api/src/main/java/me/shedaniel/rei/impl/ClientInternals.java @@ -25,12 +25,14 @@ package me.shedaniel.rei.impl; import com.mojang.math.Matrix4f; import com.mojang.serialization.DataResult; +import dev.architectury.utils.value.BooleanValue; import me.shedaniel.math.Point; import me.shedaniel.math.Rectangle; import me.shedaniel.rei.api.client.ClientHelper; import me.shedaniel.rei.api.client.entry.filtering.FilteringRuleTypeRegistry; import me.shedaniel.rei.api.client.entry.renderer.EntryRenderer; import me.shedaniel.rei.api.client.favorites.FavoriteEntry; +import me.shedaniel.rei.api.client.favorites.FavoriteMenuEntry; import me.shedaniel.rei.api.client.gui.DrawableConsumer; import me.shedaniel.rei.api.client.gui.Renderer; import me.shedaniel.rei.api.client.gui.widgets.*; @@ -70,6 +72,8 @@ public final class ClientInternals { private static Supplier> emptyEntryRenderer = ClientInternals::throwNotSetup; private static Supplier filteringRuleTypeRegistry = ClientInternals::throwNotSetup; private static BiFunction>, Supplier, FavoriteEntry> delegateFavoriteEntry = (supplier, toJson) -> throwNotSetup(); + private static BiFunction, FavoriteMenuEntry> subMenuEntry = (supplier, toJson) -> throwNotSetup(); + private static BiFunction toggleEntry = (supplier, toJson) -> throwNotSetup(); private static Function> favoriteEntryFromJson = (object) -> throwNotSetup(); private static Function clickAreaHandlerResult = (result) -> throwNotSetup(); private static BiConsumer, TooltipComponent> clientTooltipComponentProvider = (tooltip, result) -> throwNotSetup(); @@ -149,6 +153,14 @@ public final class ClientInternals { return delegateFavoriteEntry.apply(supplier, toJoin); } + public static FavoriteMenuEntry createSubMenuEntry(Component name, List entries) { + return subMenuEntry.apply(name, entries); + } + + public static FavoriteMenuEntry createToggleEntry(Component name, BooleanValue value) { + return toggleEntry.apply(name, value); + } + public static DataResult favoriteEntryFromJson(CompoundTag tag) { return favoriteEntryFromJson.apply(tag); } diff --git a/runtime/src/main/java/me/shedaniel/rei/RoughlyEnoughItemsCoreClient.java b/runtime/src/main/java/me/shedaniel/rei/RoughlyEnoughItemsCoreClient.java index 16558a54e..b911f9968 100644 --- a/runtime/src/main/java/me/shedaniel/rei/RoughlyEnoughItemsCoreClient.java +++ b/runtime/src/main/java/me/shedaniel/rei/RoughlyEnoughItemsCoreClient.java @@ -34,6 +34,7 @@ import dev.architectury.event.events.client.ClientGuiEvent; import dev.architectury.event.events.client.ClientRecipeUpdateEvent; import dev.architectury.event.events.client.ClientScreenInputEvent; import dev.architectury.networking.NetworkManager; +import dev.architectury.utils.value.BooleanValue; import me.shedaniel.math.Point; import me.shedaniel.rei.api.client.REIRuntime; import me.shedaniel.rei.api.client.config.ConfigObject; @@ -41,6 +42,7 @@ import me.shedaniel.rei.api.client.entry.filtering.FilteringRuleTypeRegistry; import me.shedaniel.rei.api.client.entry.renderer.EntryRenderer; import me.shedaniel.rei.api.client.favorites.FavoriteEntry; import me.shedaniel.rei.api.client.favorites.FavoriteEntryType; +import me.shedaniel.rei.api.client.favorites.FavoriteMenuEntry; import me.shedaniel.rei.api.client.gui.screen.DisplayScreen; import me.shedaniel.rei.api.client.gui.widgets.Tooltip; import me.shedaniel.rei.api.client.gui.widgets.TooltipContext; @@ -66,6 +68,8 @@ import me.shedaniel.rei.impl.client.entry.renderer.EntryRendererRegistryImpl; import me.shedaniel.rei.impl.client.favorites.DelegatingFavoriteEntryProviderImpl; import me.shedaniel.rei.impl.client.favorites.FavoriteEntryTypeRegistryImpl; import me.shedaniel.rei.impl.client.gui.ScreenOverlayImpl; +import me.shedaniel.rei.impl.client.gui.modules.entries.SubMenuEntry; +import me.shedaniel.rei.impl.client.gui.modules.entries.ToggleMenuEntry; import me.shedaniel.rei.impl.client.gui.widget.InternalWidgets; import me.shedaniel.rei.impl.client.gui.widget.QueuedTooltip; import me.shedaniel.rei.impl.client.gui.widget.TooltipContextImpl; @@ -151,6 +155,8 @@ public class RoughlyEnoughItemsCoreClient { ClientInternals.attachInstance((BiFunction<@Nullable Point, Collection, Tooltip>) QueuedTooltip::impl, "tooltipProvider"); ClientInternals.attachInstance((TriFunction) TooltipContextImpl::new, "tooltipContextProvider"); ClientInternals.attachInstance((Function) QueuedTooltip.TooltipEntryImpl::new, "tooltipEntryProvider"); + ClientInternals.attachInstance((BiFunction, FavoriteMenuEntry>) SubMenuEntry::new, "subMenuEntry"); + ClientInternals.attachInstance((BiFunction) (text, value) -> ToggleMenuEntry.of(text, value::get, value), "toggleEntry"); ClientInternals.attachInstance((Function<@Nullable Boolean, ClickArea.Result>) successful -> new ClickArea.Result() { private List> categories = Lists.newArrayList(); private BooleanSupplier execute = () -> { diff --git a/runtime/src/main/java/me/shedaniel/rei/impl/client/config/ConfigObjectImpl.java b/runtime/src/main/java/me/shedaniel/rei/impl/client/config/ConfigObjectImpl.java index a18d7aeab..6b2e7c5c8 100644 --- a/runtime/src/main/java/me/shedaniel/rei/impl/client/config/ConfigObjectImpl.java +++ b/runtime/src/main/java/me/shedaniel/rei/impl/client/config/ConfigObjectImpl.java @@ -279,6 +279,15 @@ public class ConfigObjectImpl implements ConfigObject, ConfigData { return isFavoritesEnabled() && advanced.tooltips.displayFavoritesTooltip; } + @Override + public boolean doDisplayIMEHints() { + return advanced.tooltips.displayIMEHints; + } + + public void setDoDisplayIMEHints(boolean displayIMEHints) { + advanced.tooltips.displayIMEHints = displayIMEHints; + } + @Override public boolean doesFastEntryRendering() { return advanced.miscellaneous.newFastEntryRendering; @@ -661,6 +670,7 @@ public class ConfigObjectImpl implements ConfigObject, ConfigData { public static class Tooltips { @Comment("Declares whether REI should append mod names to entries.") private boolean appendModNames = true; @Comment("Declares whether favorites tooltip should be displayed.") private boolean displayFavoritesTooltip = false; + @ConfigEntry.Gui.Excluded private boolean displayIMEHints = true; } public static class Layout { diff --git a/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/ScreenOverlayImpl.java b/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/ScreenOverlayImpl.java index fe6abb1ba..7ceaa4123 100644 --- a/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/ScreenOverlayImpl.java +++ b/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/ScreenOverlayImpl.java @@ -59,6 +59,7 @@ import me.shedaniel.rei.impl.client.gui.widget.entrylist.EntryListWidget; import me.shedaniel.rei.impl.client.gui.widget.entrylist.PaginatedEntryListWidget; import me.shedaniel.rei.impl.client.gui.widget.entrylist.ScrolledEntryListWidget; import me.shedaniel.rei.impl.client.gui.widget.favorites.FavoritesListWidget; +import me.shedaniel.rei.impl.client.gui.widget.hint.HintsContainerWidget; import me.shedaniel.rei.impl.client.gui.widget.search.OverlaySearchField; import me.shedaniel.rei.impl.common.util.RectangleUtils; import net.minecraft.client.Minecraft; @@ -86,10 +87,11 @@ public abstract class ScreenOverlayImpl extends ScreenOverlay { private Rectangle bounds; private Window window; private Widget configButton; - private CurrentDraggingStack draggingStack = new CurrentDraggingStack(); + private final CurrentDraggingStack draggingStack = new CurrentDraggingStack(); @Nullable public DefaultDisplayChoosePageWidget choosePageWidget; - private MenuHolder menuHolder = new MenuHolder(); + private final MenuHolder menuHolder = new MenuHolder(); + private final HintsContainerWidget hintsWidget = new HintsContainerWidget(); public static EntryListWidget getEntryListWidget() { boolean widgetScrolled = ConfigObject.getInstance().isEntryListWidgetScrolled(); @@ -118,7 +120,7 @@ public abstract class ScreenOverlayImpl extends ScreenOverlay { } public static ScreenOverlayImpl getInstance() { - return (ScreenOverlayImpl) REIRuntime.getInstance().getOverlay().get(); + return (ScreenOverlayImpl) REIRuntime.getInstance().getOverlay().orElseThrow(); } public void tick() { @@ -183,6 +185,8 @@ public abstract class ScreenOverlayImpl extends ScreenOverlay { } this.widgets.add(draggingStack); + this.widgets.add(InternalWidgets.wrapLateRenderable(hintsWidget)); + this.hintsWidget.init(); } private Rectangle getSearchFieldArea() { @@ -294,6 +298,8 @@ public abstract class ScreenOverlayImpl extends ScreenOverlay { for (Widget widget : widgets) { if (widget instanceof LateRenderable && widget != menuHolder.widget()) widget.render(matrices, mouseX, mouseY, delta); + else if (widget instanceof OverlaySearchField field) + field.laterRender(matrices, mouseX, mouseY, delta); } matrices.pushPose(); matrices.translate(0, 0, 500); @@ -347,6 +353,8 @@ public abstract class ScreenOverlayImpl extends ScreenOverlay { return false; if (menuHolder.mouseScrolled(mouseX, mouseY, amount)) return true; + if (hintsWidget.mouseScrolled(mouseX, mouseY, amount)) + return true; if (isInside(mouseX, mouseY) && getEntryListWidget().mouseScrolled(mouseX, mouseY, amount)) { return true; } @@ -357,6 +365,7 @@ public abstract class ScreenOverlayImpl extends ScreenOverlay { for (Widget widget : widgets) if (widget != getEntryListWidget() && (favoritesListWidget == null || widget != favoritesListWidget) && widget != menuHolder.widget() + && widget != hintsWidget && widget.mouseScrolled(mouseX, mouseY, amount)) return true; return false; @@ -488,6 +497,13 @@ public abstract class ScreenOverlayImpl extends ScreenOverlay { REIRuntimeImpl.getSearchField().setFocused(false); return true; } + if (hintsWidget.mouseClicked(mouseX, mouseY, button)) { + this.setFocused(hintsWidget); + if (button == 0) + this.setDragging(true); + REIRuntimeImpl.getSearchField().setFocused(false); + return true; + } } if (ConfigObject.getInstance().areClickableRecipeArrowsEnabled()) { Screen screen = Minecraft.getInstance().screen; @@ -500,7 +516,7 @@ public abstract class ScreenOverlayImpl extends ScreenOverlay { return false; } for (GuiEventListener element : widgets) { - if (element != configButton && element != menuHolder.widget() && element.mouseClicked(mouseX, mouseY, button)) { + if (element != configButton && element != menuHolder.widget() && element != hintsWidget && element.mouseClicked(mouseX, mouseY, button)) { this.setFocused(element); if (button == 0) this.setDragging(true); @@ -568,4 +584,8 @@ public abstract class ScreenOverlayImpl extends ScreenOverlay { public MenuAccess menuAccess() { return menuHolder; } + + public HintsContainerWidget getHintsContainer() { + return this.hintsWidget; + } } diff --git a/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/modules/AbstractMenuEntry.java b/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/modules/AbstractMenuEntry.java index 47fc0b085..2f87ca127 100644 --- a/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/modules/AbstractMenuEntry.java +++ b/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/modules/AbstractMenuEntry.java @@ -23,7 +23,9 @@ package me.shedaniel.rei.impl.client.gui.modules; -public abstract class AbstractMenuEntry extends MenuEntry { +import me.shedaniel.rei.api.client.favorites.FavoriteMenuEntry; + +public abstract class AbstractMenuEntry extends FavoriteMenuEntry { private int x, y, width; private boolean selected, containsMouse, rendering; diff --git a/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/modules/Menu.java b/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/modules/Menu.java index 05173e464..af6a07740 100644 --- a/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/modules/Menu.java +++ b/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/modules/Menu.java @@ -31,7 +31,9 @@ import me.shedaniel.clothconfig2.api.scroll.ScrollingContainer; import me.shedaniel.math.Point; import me.shedaniel.math.Rectangle; import me.shedaniel.rei.api.client.REIRuntime; +import me.shedaniel.rei.api.client.favorites.FavoriteMenuEntry; import me.shedaniel.rei.api.client.gui.widgets.WidgetWithBounds; +import me.shedaniel.rei.impl.client.gui.ScreenOverlayImpl; import me.shedaniel.rei.impl.client.gui.modules.entries.SubMenuEntry; import me.shedaniel.rei.impl.client.gui.widget.LateRenderable; import net.minecraft.client.Minecraft; @@ -50,12 +52,12 @@ public class Menu extends WidgetWithBounds implements LateRenderable { public final Point menuStartPoint; public final boolean facingRight; public final boolean facingDownwards; - private final List entries = Lists.newArrayList(); + private final List entries = Lists.newArrayList(); public final ScrollingContainer scrolling = new ScrollingContainer() { @Override public int getMaxScrollHeight() { int i = 0; - for (MenuEntry entry : children()) { + for (FavoriteMenuEntry entry : children()) { i += entry.getEntryHeight(); } return i; @@ -72,7 +74,7 @@ public class Menu extends WidgetWithBounds implements LateRenderable { } }; - public Menu(Rectangle menuStart, Collection entries, boolean sort) { + public Menu(Rectangle menuStart, Collection entries, boolean sort) { buildEntries(entries, sort); int fullWidth = Minecraft.getInstance().screen.width; int fullHeight = Minecraft.getInstance().screen.height; @@ -91,16 +93,18 @@ public class Menu extends WidgetWithBounds implements LateRenderable { this.menuStartPoint = new Point(x, y); } - @SuppressWarnings("deprecation") - private void buildEntries(Collection entries, boolean sort) { + private void buildEntries(Collection entries, boolean sort) { this.entries.clear(); this.entries.addAll(entries); if (sort) { this.entries.sort(Comparator.comparing(entry -> entry instanceof SubMenuEntry ? 0 : 1) .thenComparing(entry -> entry instanceof SubMenuEntry menuEntry ? menuEntry.text.getString() : "")); } - for (MenuEntry entry : this.entries) { - entry.parent = this; + for (FavoriteMenuEntry entry : this.entries) { + entry.closeMenu = ScreenOverlayImpl.getInstance().menuAccess()::close; + if (entry instanceof SubMenuEntry menuEntry) { + menuEntry.setParent(this); + } } } @@ -123,7 +127,7 @@ public class Menu extends WidgetWithBounds implements LateRenderable { public int getMaxEntryWidth() { int i = 0; - for (MenuEntry entry : children()) { + for (FavoriteMenuEntry entry : children()) { if (entry.getEntryWidth() > i) i = entry.getEntryWidth(); } @@ -137,9 +141,9 @@ public class Menu extends WidgetWithBounds implements LateRenderable { fill(matrices, bounds.x, bounds.y, bounds.getMaxX(), bounds.getMaxY(), containsMouse(mouseX, mouseY) ? (REIRuntime.getInstance().isDarkThemeEnabled() ? -17587 : -1) : -6250336); fill(matrices, innerBounds.x, innerBounds.y, innerBounds.getMaxX(), innerBounds.getMaxY(), -16777216); boolean contains = innerBounds.contains(mouseX, mouseY); - MenuEntry focused = getFocused() instanceof MenuEntry menuEntry ? menuEntry : null; + FavoriteMenuEntry focused = getFocused() instanceof FavoriteMenuEntry menuEntry ? menuEntry : null; int currentY = innerBounds.y - scrolling.scrollAmountInt(); - for (MenuEntry child : children()) { + for (FavoriteMenuEntry child : children()) { boolean containsMouse = contains && mouseY >= currentY && mouseY < currentY + child.getEntryHeight(); if (containsMouse) { focused = child; @@ -148,7 +152,7 @@ public class Menu extends WidgetWithBounds implements LateRenderable { } currentY = innerBounds.y - scrolling.scrollAmountInt(); ScissorsHandler.INSTANCE.scissor(scrolling.getScissorBounds()); - for (MenuEntry child : children()) { + for (FavoriteMenuEntry child : children()) { boolean rendering = currentY + child.getEntryHeight() >= innerBounds.y && currentY <= innerBounds.getMaxY(); boolean containsMouse = contains && mouseY >= currentY && mouseY < currentY + child.getEntryHeight(); child.updateInformation(innerBounds.x, currentY, focused == child || containsMouse, containsMouse, rendering, getMaxEntryWidth()); @@ -183,7 +187,7 @@ public class Menu extends WidgetWithBounds implements LateRenderable { scrolling.offset(ClothConfigInitializer.getScrollStep() * -amount, true); return true; } - for (MenuEntry child : children()) { + for (FavoriteMenuEntry child : children()) { if (child instanceof SubMenuEntry) { if (child.mouseScrolled(mouseX, mouseY, amount)) return true; @@ -195,7 +199,7 @@ public class Menu extends WidgetWithBounds implements LateRenderable { @Override public boolean containsMouse(double mouseX, double mouseY) { if (super.containsMouse(mouseX, mouseY)) return true; - for (MenuEntry child : children()) { + for (FavoriteMenuEntry child : children()) { if (child.containsMouse(mouseX, mouseY)) { return true; } @@ -204,7 +208,7 @@ public class Menu extends WidgetWithBounds implements LateRenderable { } @Override - public List children() { + public List children() { return entries; } } diff --git a/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/modules/MenuAccess.java b/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/modules/MenuAccess.java index 3d8d6896f..a826fbcfe 100644 --- a/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/modules/MenuAccess.java +++ b/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/modules/MenuAccess.java @@ -26,6 +26,7 @@ package me.shedaniel.rei.impl.client.gui.modules; import me.shedaniel.math.Point; import me.shedaniel.math.Rectangle; import me.shedaniel.math.impl.PointHelper; +import me.shedaniel.rei.api.client.favorites.FavoriteMenuEntry; import java.util.Collection; import java.util.UUID; @@ -43,7 +44,7 @@ public interface MenuAccess { void open(UUID uuid, Menu menu, Predicate or, Predicate and); - default void openOrClose(UUID uuid, Rectangle selfBounds, Supplier> menuSupplier) { + default void openOrClose(UUID uuid, Rectangle selfBounds, Supplier> menuSupplier) { boolean isOpened = isOpened(uuid); if (isOpened || !isAnyOpened()) { boolean inBounds = (isValidPoint(PointHelper.ofMouse()) && selfBounds.contains(PointHelper.ofMouse())) || isInBounds(uuid); diff --git a/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/modules/MenuEntry.java b/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/modules/MenuEntry.java deleted file mode 100644 index c2cf89925..000000000 --- a/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/modules/MenuEntry.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * This file is licensed under the MIT License, part of Roughly Enough Items. - * Copyright (c) 2018, 2019, 2020, 2021, 2022 shedaniel - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package me.shedaniel.rei.impl.client.gui.modules; - -import me.shedaniel.rei.api.client.gui.widgets.Widget; -import org.jetbrains.annotations.ApiStatus; - -@ApiStatus.Internal -public abstract class MenuEntry extends Widget { - @ApiStatus.Internal - @Deprecated - Menu parent = null; - - public final Menu getParent() { - return parent; - } - - public abstract int getEntryWidth(); - - public abstract int getEntryHeight(); - - public abstract void updateInformation(int xPos, int yPos, boolean selected, boolean containsMouse, boolean rendering, int width); -} diff --git a/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/modules/entries/SubMenuEntry.java b/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/modules/entries/SubMenuEntry.java index 1113bb457..3fb54a619 100644 --- a/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/modules/entries/SubMenuEntry.java +++ b/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/modules/entries/SubMenuEntry.java @@ -29,10 +29,10 @@ import com.mojang.blaze3d.systems.RenderSystem; import com.mojang.blaze3d.vertex.PoseStack; import me.shedaniel.clothconfig2.api.ScissorsHandler; import me.shedaniel.math.Rectangle; +import me.shedaniel.rei.api.client.favorites.FavoriteMenuEntry; import me.shedaniel.rei.impl.client.gui.InternalTextures; import me.shedaniel.rei.impl.client.gui.modules.AbstractMenuEntry; import me.shedaniel.rei.impl.client.gui.modules.Menu; -import me.shedaniel.rei.impl.client.gui.modules.MenuEntry; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.components.events.GuiEventListener; import net.minecraft.network.chat.Component; @@ -44,22 +44,27 @@ import java.util.function.Supplier; public class SubMenuEntry extends AbstractMenuEntry { public final Component text; private int textWidth = -69; - protected List entries; + protected List entries; + protected Menu parent; protected Menu childMenu; public SubMenuEntry(Component text) { this(text, Collections.emptyList()); } - public SubMenuEntry(Component text, Supplier> entries) { + public SubMenuEntry(Component text, Supplier> entries) { this(text, entries.get()); } - public SubMenuEntry(Component text, List entries) { + public SubMenuEntry(Component text, List entries) { this.text = MoreObjects.firstNonNull(text, Component.empty()); this.entries = entries; } + public void setParent(Menu parent) { + this.parent = parent; + } + private int getTextWidth() { if (textWidth == -69) { this.textWidth = Math.max(0, font.width(text)); @@ -69,7 +74,7 @@ public class SubMenuEntry extends AbstractMenuEntry { public Menu getChildMenu() { if (childMenu == null) { - this.childMenu = new Menu(new Rectangle(getParent().getBounds().x + 1, getY() - 1, getParent().getBounds().width - 2, getEntryHeight() - 2), entries, false); + this.childMenu = new Menu(new Rectangle(parent.getBounds().x + 1, getY() - 1, parent.getBounds().width - 2, getEntryHeight() - 2), entries, false); } return childMenu; } @@ -91,11 +96,11 @@ public class SubMenuEntry extends AbstractMenuEntry { if (!entries.isEmpty()) { Menu menu = getChildMenu(); - Rectangle menuStart = new Rectangle(getParent().getBounds().x, getY(), getParent().getBounds().width, getEntryHeight()); + Rectangle menuStart = new Rectangle(parent.getBounds().x, getY(), parent.getBounds().width, getEntryHeight()); int fullWidth = Minecraft.getInstance().screen.width; int fullHeight = Minecraft.getInstance().screen.height; - boolean facingRight = getParent().facingRight; + boolean facingRight = parent.facingRight; int menuWidth = menu.getMaxEntryWidth() + 2 + (menu.hasScrollBar() ? 6 : 0); if (facingRight && fullWidth - menuStart.getMaxX() < menuWidth + 10) { facingRight = false; diff --git a/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/widget/ConfigButtonWidget.java b/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/widget/ConfigButtonWidget.java index cf4a4703f..c61118b5b 100644 --- a/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/widget/ConfigButtonWidget.java +++ b/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/widget/ConfigButtonWidget.java @@ -30,6 +30,7 @@ import me.shedaniel.rei.api.client.ClientHelper; import me.shedaniel.rei.api.client.REIRuntime; import me.shedaniel.rei.api.client.config.ConfigManager; import me.shedaniel.rei.api.client.config.ConfigObject; +import me.shedaniel.rei.api.client.favorites.FavoriteMenuEntry; import me.shedaniel.rei.api.client.gui.config.DisplayPanelLocation; import me.shedaniel.rei.api.client.gui.config.SyntaxHighlightingMode; import me.shedaniel.rei.api.client.gui.screen.DisplayScreen; @@ -43,7 +44,6 @@ import me.shedaniel.rei.impl.client.config.ConfigObjectImpl; import me.shedaniel.rei.impl.client.gui.InternalTextures; import me.shedaniel.rei.impl.client.gui.ScreenOverlayImpl; import me.shedaniel.rei.impl.client.gui.modules.MenuAccess; -import me.shedaniel.rei.impl.client.gui.modules.MenuEntry; import me.shedaniel.rei.impl.client.gui.modules.entries.*; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.screens.Screen; @@ -95,7 +95,7 @@ public class ConfigButtonWidget { return Widgets.concat(configButton, overlayWidget); } - private static Collection menuEntries() { + private static Collection menuEntries() { ConfigObjectImpl config = ConfigManagerImpl.getInstance().getConfig(); return List.of( ToggleMenuEntry.of(Component.translatable("text.rei.cheating"), diff --git a/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/widget/CraftableFilterButtonWidget.java b/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/widget/CraftableFilterButtonWidget.java index d0d611890..e76f9f022 100644 --- a/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/widget/CraftableFilterButtonWidget.java +++ b/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/widget/CraftableFilterButtonWidget.java @@ -24,8 +24,12 @@ package me.shedaniel.rei.impl.client.gui.widget; import com.mojang.math.Vector4f; +import dev.architectury.utils.value.BooleanValue; +import me.shedaniel.math.Point; import me.shedaniel.math.Rectangle; import me.shedaniel.rei.api.client.config.ConfigManager; +import me.shedaniel.rei.api.client.config.ConfigObject; +import me.shedaniel.rei.api.client.favorites.FavoriteMenuEntry; import me.shedaniel.rei.api.client.gui.config.SearchFieldLocation; import me.shedaniel.rei.api.client.gui.widgets.Button; import me.shedaniel.rei.api.client.gui.widgets.Tooltip; @@ -39,10 +43,11 @@ import me.shedaniel.rei.impl.client.config.ConfigManagerImpl; import me.shedaniel.rei.impl.client.config.ConfigObjectImpl; import me.shedaniel.rei.impl.client.gui.ScreenOverlayImpl; import me.shedaniel.rei.impl.client.gui.modules.MenuAccess; -import me.shedaniel.rei.impl.client.gui.modules.MenuEntry; +import me.shedaniel.rei.impl.client.gui.modules.entries.SeparatorMenuEntry; import me.shedaniel.rei.impl.client.gui.modules.entries.SubMenuEntry; import me.shedaniel.rei.impl.client.gui.modules.entries.ToggleMenuEntry; import me.shedaniel.rei.impl.client.gui.screen.ConfigReloadingScreen; +import me.shedaniel.rei.impl.client.search.method.DefaultInputMethod; import me.shedaniel.rei.impl.common.InternalLogger; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.screens.Screen; @@ -57,6 +62,7 @@ import java.util.*; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; +import java.util.stream.Collectors; public class CraftableFilterButtonWidget { public static final UUID FILTER_MENU_UUID = UUID.fromString("2839e998-1679-4f9e-a257-37411d16f1e6"); @@ -75,7 +81,7 @@ public class CraftableFilterButtonWidget { .onRender((matrices, button) -> { button.setTint(ConfigManager.getInstance().isCraftableOnlyEnabled() ? 0x3800d907 : 0x38ff0000); - access.openOrClose(FILTER_MENU_UUID, button.getBounds(), CraftableFilterButtonWidget::menuEntries); + access.openOrClose(FILTER_MENU_UUID, button.getBounds(), () -> menuEntries(access)); }) .containsMousePredicate((button, point) -> button.getBounds().contains(point) && overlay.isNotInExclusionZones(point.x, point.y)) .tooltipLineSupplier(button -> Component.translatable(ConfigManager.getInstance().isCraftableOnlyEnabled() ? "text.rei.showing_craftable" : "text.rei.showing_all")); @@ -89,11 +95,11 @@ public class CraftableFilterButtonWidget { return Widgets.concat(filterButton, overlayWidget); } - private static Collection menuEntries() { + private static Collection menuEntries(MenuAccess access) { ConfigObjectImpl config = ConfigManagerImpl.getInstance().getConfig(); - ArrayList entries = new ArrayList<>(List.of( + ArrayList entries = new ArrayList<>(List.of( new SubMenuEntry(Component.translatable("text.rei.config.menu.search_field.position"), Arrays.stream(SearchFieldLocation.values()) - .map(location -> ToggleMenuEntry.of(Component.literal(location.toString()), + .map(location -> ToggleMenuEntry.of(Component.literal(location.toString()), () -> config.getSearchFieldLocation() == location, bool -> config.setSearchFieldLocation(location)) .withActive(() -> config.getSearchFieldLocation() != location) @@ -103,7 +109,7 @@ public class CraftableFilterButtonWidget { List>> applicableInputMethods = getApplicableInputMethods(); if (applicableInputMethods.size() > 1) { - entries.add(new SubMenuEntry(Component.translatable("text.rei.config.menu.search_field.input_method"), createInputMethodEntries(applicableInputMethods))); + entries.add(new SubMenuEntry(Component.translatable("text.rei.config.menu.search_field.input_method"), createInputMethodEntries(access, applicableInputMethods))); } return entries; @@ -116,10 +122,10 @@ public class CraftableFilterButtonWidget { .toList(); } - public static List createInputMethodEntries(List>> applicableInputMethods) { + public static List createInputMethodEntries(MenuAccess access, List>> applicableInputMethods) { ConfigObjectImpl config = ConfigManagerImpl.getInstance().getConfig(); - return applicableInputMethods.stream() - .map(pair -> ToggleMenuEntry.of(pair.getValue().getName(), + List entries = applicableInputMethods.stream() + .map(pair -> ToggleMenuEntry.of(pair.getValue().getName(), () -> Objects.equals(config.getInputMethodId(), pair.getKey()), bool -> { ExecutorService service = Executors.newSingleThreadExecutor(); @@ -147,14 +153,38 @@ public class CraftableFilterButtonWidget { }); reloadingScreen.setSubtitle(() -> new TranslatableComponent("text.rei.input.methods.reload.progress", String.format("%.2f", progress[0] * 100))); Minecraft.getInstance().setScreen(reloadingScreen); + access.close(); future.whenComplete((unused, throwable) -> { service.shutdown(); }); + ScreenOverlayImpl.getInstance().getHintsContainer().addHint(12, () -> new Point(getCraftableFilterBounds().getCenterX(), getCraftableFilterBounds().getCenterY()), + "text.rei.hint.input.methods", List.of(new TranslatableComponent("text.rei.hint.input.methods"))); }) .withActive(() -> !Objects.equals(config.getInputMethodId(), pair.getKey())) .withTooltip(() -> Tooltip.create(Widget.mouse(), pair.getValue().getDescription())) ) - .toList(); + .collect(Collectors.toList()); + InputMethod active = InputMethod.active(); + if (!(active instanceof DefaultInputMethod)) { + entries.add(0, new SeparatorMenuEntry()); + entries.add(0, FavoriteMenuEntry.createToggle(new TranslatableComponent("text.rei.input.methods.tooltip.hints"), new BooleanValue() { + @Override + public void accept(boolean t) { + ConfigManagerImpl.getInstance().getConfig().setDoDisplayIMEHints(!getAsBoolean()); + } + + @Override + public boolean getAsBoolean() { + return ConfigObject.getInstance().doDisplayIMEHints(); + } + })); + } + List optionsMenuEntries = active.getOptionsMenuEntries(); + if (!optionsMenuEntries.isEmpty()) { + entries.add(new SeparatorMenuEntry()); + entries.addAll(optionsMenuEntries); + } + return entries; } private static Rectangle getCraftableFilterBounds() { diff --git a/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/widget/EntryWidget.java b/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/widget/EntryWidget.java index 7bc07b1ba..979085034 100644 --- a/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/widget/EntryWidget.java +++ b/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/widget/EntryWidget.java @@ -491,7 +491,7 @@ public class EntryWidget extends Slot implements DraggableStackProviderWidget { } } - if (!tooltip.entries().isEmpty()) { + if (!tooltip.entries().isEmpty() && ConfigObject.getInstance().doDisplayIMEHints()) { Tooltip.Entry entry = tooltip.entries().get(0); if (entry.isText()) { diff --git a/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/widget/favorites/FavoritesEntriesManager.java b/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/widget/favorites/FavoritesEntriesManager.java index 38416b7cf..8dfd0b076 100644 --- a/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/widget/favorites/FavoritesEntriesManager.java +++ b/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/widget/favorites/FavoritesEntriesManager.java @@ -23,6 +23,8 @@ package me.shedaniel.rei.impl.client.gui.widget.favorites; +import me.shedaniel.math.Point; +import me.shedaniel.math.Rectangle; import me.shedaniel.rei.api.client.config.ConfigManager; import me.shedaniel.rei.api.client.favorites.FavoriteEntry; import me.shedaniel.rei.api.client.favorites.FavoriteEntryType; @@ -30,10 +32,12 @@ import me.shedaniel.rei.api.common.util.CollectionUtils; import me.shedaniel.rei.impl.client.config.ConfigManagerImpl; import me.shedaniel.rei.impl.client.config.ConfigObjectImpl; import me.shedaniel.rei.impl.client.gui.ScreenOverlayImpl; +import net.minecraft.network.chat.TranslatableComponent; import java.util.AbstractList; import java.util.ArrayList; import java.util.List; +import java.util.function.Supplier; import java.util.stream.Collectors; import java.util.stream.Stream; import java.util.stream.StreamSupport; @@ -64,6 +68,12 @@ public class FavoritesEntriesManager { config.getConfigFavoriteEntries().remove(entry); if (getDefaultFavorites().anyMatch(e -> e.equals(entry)) && !config.getHiddenFavoriteEntries().contains(entry)) { config.getHiddenFavoriteEntries().add(entry); + FavoritesListWidget widget = ScreenOverlayImpl.getFavoritesListWidget(); + if (widget != null) { + Supplier buttonBounds = widget.togglePanelButton::getBounds; + ScreenOverlayImpl.getInstance().getHintsContainer().addHint(12, () -> new Point(buttonBounds.get().getCenterX(), buttonBounds.get().getCenterY()), + "text.rei.hint.favorites.discover", List.of(new TranslatableComponent("text.rei.hint.favorites.discover"))); + } } ConfigManager.getInstance().saveConfig(); diff --git a/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/widget/hint/HintWidget.java b/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/widget/hint/HintWidget.java new file mode 100644 index 000000000..7abf1f83b --- /dev/null +++ b/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/widget/hint/HintWidget.java @@ -0,0 +1,176 @@ +package me.shedaniel.rei.impl.client.gui.widget.hint; + +import com.mojang.blaze3d.systems.RenderSystem; +import com.mojang.blaze3d.vertex.*; +import com.mojang.math.Matrix4f; +import me.shedaniel.clothconfig2.ClothConfigInitializer; +import me.shedaniel.clothconfig2.api.animator.NumberAnimator; +import me.shedaniel.clothconfig2.api.animator.ValueAnimator; +import me.shedaniel.clothconfig2.api.scroll.ScrollingContainer; +import me.shedaniel.math.Point; +import me.shedaniel.math.Rectangle; +import me.shedaniel.rei.api.client.gui.widgets.CloseableScissors; +import me.shedaniel.rei.api.client.gui.widgets.Widget; +import me.shedaniel.rei.api.client.gui.widgets.WidgetWithBounds; +import me.shedaniel.rei.api.client.gui.widgets.Widgets; +import me.shedaniel.rei.api.common.util.CollectionUtils; +import me.shedaniel.rei.impl.client.gui.ScreenOverlayImpl; +import net.minecraft.ChatFormatting; +import net.minecraft.client.gui.components.events.GuiEventListener; +import net.minecraft.client.renderer.GameRenderer; +import net.minecraft.network.chat.FormattedText; +import net.minecraft.network.chat.MutableComponent; +import net.minecraft.network.chat.TranslatableComponent; +import net.minecraft.util.FormattedCharSequence; +import net.minecraft.util.Mth; + +import java.util.Collection; +import java.util.Comparator; +imp