From 9954713e457a5a5d80025432961b56ab0003bbb0 Mon Sep 17 00:00:00 2001 From: shedaniel Date: Thu, 28 Jul 2022 01:39:41 +0800 Subject: More work on config module --- .../rei/impl/client/config/ConfigManagerImpl.java | 59 +--- .../rei/impl/client/config/ConfigObjectImpl.java | 10 +- .../config/entries/RecipeScreenTypeEntry.java | 1 - .../client/config/entries/ReloadPluginsEntry.java | 118 ++++++++ .../entries/UncertainDisplayViewingScreen.java | 304 +++++++++++++++++++++ .../client/gui/credits/CreditsEntryListWidget.java | 216 +++++++++++++++ .../rei/impl/client/gui/credits/CreditsScreen.java | 165 +++++++++++ 7 files changed, 820 insertions(+), 53 deletions(-) create mode 100644 runtime-engine/configs/src/main/java/me/shedaniel/rei/impl/client/config/entries/ReloadPluginsEntry.java create mode 100644 runtime-engine/configs/src/main/java/me/shedaniel/rei/impl/client/config/entries/UncertainDisplayViewingScreen.java create mode 100644 runtime-engine/configs/src/main/java/me/shedaniel/rei/impl/client/gui/credits/CreditsEntryListWidget.java create mode 100644 runtime-engine/configs/src/main/java/me/shedaniel/rei/impl/client/gui/credits/CreditsScreen.java (limited to 'runtime-engine/configs/src/main/java/me') diff --git a/runtime-engine/configs/src/main/java/me/shedaniel/rei/impl/client/config/ConfigManagerImpl.java b/runtime-engine/configs/src/main/java/me/shedaniel/rei/impl/client/config/ConfigManagerImpl.java index 2970c5b62..df956d185 100644 --- a/runtime-engine/configs/src/main/java/me/shedaniel/rei/impl/client/config/ConfigManagerImpl.java +++ b/runtime-engine/configs/src/main/java/me/shedaniel/rei/impl/client/config/ConfigManagerImpl.java @@ -23,7 +23,6 @@ package me.shedaniel.rei.impl.client.config; -import com.google.common.collect.Lists; import com.google.gson.Gson; import com.google.gson.GsonBuilder; import com.mojang.blaze3d.platform.InputConstants; @@ -51,6 +50,7 @@ import me.shedaniel.rei.api.client.REIRuntime; import me.shedaniel.rei.api.client.config.ConfigManager; import me.shedaniel.rei.api.client.config.addon.ConfigAddonRegistry; import me.shedaniel.rei.api.client.config.entry.EntryStackProvider; +import me.shedaniel.rei.api.client.entry.filtering.FilteringRule; import me.shedaniel.rei.api.client.favorites.FavoriteEntry; import me.shedaniel.rei.api.client.gui.config.CheatingMode; import me.shedaniel.rei.api.client.gui.config.DisplayScreenType; @@ -58,14 +58,11 @@ import me.shedaniel.rei.api.client.gui.config.SyntaxHighlightingMode; import me.shedaniel.rei.api.client.overlay.ScreenOverlay; import me.shedaniel.rei.api.client.registry.entry.EntryRegistry; import me.shedaniel.rei.api.common.entry.EntryStack; -import me.shedaniel.rei.api.common.util.CollectionUtils; import me.shedaniel.rei.api.common.util.ImmutableTextComponent; -import me.shedaniel.rei.impl.client.REIRuntimeImpl; +import me.shedaniel.rei.impl.Internals; import me.shedaniel.rei.impl.client.config.addon.ConfigAddonRegistryImpl; import me.shedaniel.rei.impl.client.config.entries.*; -import me.shedaniel.rei.impl.client.entry.filtering.FilteringRule; import me.shedaniel.rei.impl.client.entry.filtering.rules.ManualFilteringRule; -import me.shedaniel.rei.impl.client.gui.ScreenOverlayImpl; import me.shedaniel.rei.impl.client.gui.credits.CreditsScreen; import me.shedaniel.rei.impl.client.gui.performance.entry.PerformanceEntry; import me.shedaniel.rei.impl.common.InternalLogger; @@ -88,7 +85,6 @@ import org.jetbrains.annotations.ApiStatus; import java.lang.reflect.Field; import java.util.*; -import java.util.function.Consumer; import java.util.function.Supplier; import static me.shedaniel.autoconfig.util.Utils.getUnsafely; @@ -123,18 +119,9 @@ public class ConfigManagerImpl implements ConfigManagerInternal { guiRegistry.registerAnnotationProvider((i13n, field, config, defaults, guiProvider) -> Collections.singletonList(new SearchFilterSyntaxHighlightingEntry(new TranslatableComponent(i13n), getUnsafely(field, config, SyntaxHighlightingMode.COLORFUL), getUnsafely(field, defaults), type -> setUnsafely(field, config, type))) , (field) -> field.getType() == SyntaxHighlightingMode.class, ConfigObjectImpl.UseSpecialSearchFilterSyntaxHighlightingScreen.class); - guiRegistry.registerAnnotationProvider((i13n, field, config, defaults, guiProvider) -> { - List> value = CollectionUtils.map(Utils.>>getUnsafely(field, config, new ArrayList<>()), EntryStackProvider::provide); - List> defaultValue = CollectionUtils.map(Utils.>>getUnsafely(field, defaults), EntryStackProvider::provide); - Consumer>> saveConsumer = (newValue) -> { - setUnsafely(field, config, CollectionUtils.map(newValue, EntryStackProvider::ofStack)); - }; - return REIRuntime.getInstance().getPreviousContainerScreen() == null || Minecraft.getInstance().getConnection() == null || Minecraft.getInstance().getConnection().getRecipeManager() == null ? - Collections.singletonList(new NoFilteringEntry(220, value, defaultValue, saveConsumer)) - : - Collections.singletonList(new FilteringEntry(220, value, ((ConfigObjectImpl.Advanced.Filtering) config).filteringRules, defaultValue, saveConsumer, list -> ((ConfigObjectImpl.Advanced.Filtering) config).filteringRules = Lists.newArrayList(list))); - } - , (field) -> field.getType() == List.class, ConfigObjectImpl.UseFilteringScreen.class); + for (SystemSetup setup : Internals.resolveServices(SystemSetup.class)) { + setup.setup(guiRegistry); + } InternalLogger.getInstance().info("Config loaded"); saveConfig(); } @@ -234,32 +221,6 @@ public class ConfigManagerImpl implements ConfigManagerInternal { } }); - // FilteringRule - builder.registerSerializer(FilteringRule.class, (value, marshaller) -> { - try { - return marshaller.serialize(FilteringRule.save(value, new CompoundTag())); - } catch (Exception e) { - e.printStackTrace(); - return JsonNull.INSTANCE; - } - }); - builder.registerDeserializer(Tag.class, FilteringRule.class, (value, marshaller) -> { - try { - return FilteringRule.read((CompoundTag) value); - } catch (Exception e) { - e.printStackTrace(); - return null; - } - }); - builder.registerDeserializer(String.class, FilteringRule.class, (value, marshaller) -> { - try { - return FilteringRule.read(TagParser.parseTag(value)); - } catch (Exception e) { - e.printStackTrace(); - return null; - } - }); - // FavoriteEntry builder.registerSerializer(FavoriteEntry.class, (value, marshaller) -> { try { @@ -282,6 +243,10 @@ public class ConfigManagerImpl implements ConfigManagerInternal { } }); + for (SystemSetup setup : Internals.resolveServices(SystemSetup.class)) { + setup.setup(builder); + } + return builder.build(); } @@ -295,7 +260,7 @@ public class ConfigManagerImpl implements ConfigManagerInternal { @Override public void saveConfig() { - if (getConfig().getFilteringRules().stream().noneMatch(filteringRule -> filteringRule instanceof ManualFilteringRule)) { + if (getConfig().getFilteringRules().stream().noneMatch(FilteringRule::isManual)) { getConfig().getFilteringRules().add(new ManualFilteringRule()); } AutoConfig.getConfigHolder(ConfigObjectImpl.class).registerLoadListener((configHolder, configObject) -> { @@ -427,8 +392,8 @@ public class ConfigManagerImpl implements ConfigManagerInternal { saveConfig(); EntryRegistry.getInstance().refilter(); REIRuntime.getInstance().getOverlay().ifPresent(ScreenOverlay::queueReloadOverlay); - if (REIRuntimeImpl.getSearchField() != null) { - ScreenOverlayImpl.getEntryListWidget().updateSearch(REIRuntimeImpl.getSearchField().getText(), true); + if (REIRuntime.getInstance().getSearchTextField() != null) { + REIRuntime.getInstance().getOverlay().ifPresent(ScreenOverlay::queueReloadSearch); } }).build(); }); diff --git a/runtime-engine/configs/src/main/java/me/shedaniel/rei/impl/client/config/ConfigObjectImpl.java b/runtime-engine/configs/src/main/java/me/shedaniel/rei/impl/client/config/ConfigObjectImpl.java index d0b63a1ff..ae2008f0a 100644 --- a/runtime-engine/configs/src/main/java/me/shedaniel/rei/impl/client/config/ConfigObjectImpl.java +++ b/runtime-engine/configs/src/main/java/me/shedaniel/rei/impl/client/config/ConfigObjectImpl.java @@ -33,10 +33,10 @@ import me.shedaniel.clothconfig2.api.Modifier; import me.shedaniel.clothconfig2.api.ModifierKeyCode; import me.shedaniel.rei.api.client.config.ConfigObject; import me.shedaniel.rei.api.client.config.entry.EntryStackProvider; +import me.shedaniel.rei.api.client.entry.filtering.FilteringRule; import me.shedaniel.rei.api.client.favorites.FavoriteEntry; import me.shedaniel.rei.api.client.gui.config.*; -import me.shedaniel.rei.impl.client.entry.filtering.FilteringRule; -import me.shedaniel.rei.impl.client.gui.widget.favorites.FavoritesEntriesManager; +import me.shedaniel.rei.impl.ClientInternals; import net.fabricmc.api.EnvType; import net.fabricmc.api.Environment; import net.minecraft.client.Minecraft; @@ -341,7 +341,7 @@ public class ConfigObjectImpl implements ConfigObject, ConfigData { @Override public List getFavoriteEntries() { - return FavoritesEntriesManager.INSTANCE.asListView(); + return ClientInternals.getFavoritesEntriesList(); } public List getConfigFavoriteEntries() { @@ -368,7 +368,7 @@ public class ConfigObjectImpl implements ConfigObject, ConfigData { } @ApiStatus.Internal - public List> getFilteringRules() { + public List getFilteringRules() { return advanced.filtering.filteringRules; } @@ -652,7 +652,7 @@ public class ConfigObjectImpl implements ConfigObject, ConfigData { public static class Filtering { @UseFilteringScreen private List> filteredStacks = new ArrayList<>(); public boolean shouldFilterDisplays = true; - @ConfigEntry.Gui.Excluded public List> filteringRules = new ArrayList<>(); + @ConfigEntry.Gui.Excluded public List filteringRules = new ArrayList<>(); } } } diff --git a/runtime-engine/configs/src/main/java/me/shedaniel/rei/impl/client/config/entries/RecipeScreenTypeEntry.java b/runtime-engine/configs/src/main/java/me/shedaniel/rei/impl/client/config/entries/RecipeScreenTypeEntry.java index bc37f2a35..514f8bd3f 100644 --- a/runtime-engine/configs/src/main/java/me/shedaniel/rei/impl/client/config/entries/RecipeScreenTypeEntry.java +++ b/runtime-engine/configs/src/main/java/me/shedaniel/rei/impl/client/config/entries/RecipeScreenTypeEntry.java @@ -28,7 +28,6 @@ import com.mojang.blaze3d.platform.Window; import com.mojang.blaze3d.vertex.PoseStack; import me.shedaniel.clothconfig2.gui.entries.TooltipListEntry; import me.shedaniel.rei.api.client.gui.config.DisplayScreenType; -import me.shedaniel.rei.impl.client.gui.screen.UncertainDisplayViewingScreen; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.chat.NarratorChatListener; import net.minecraft.client.gui.components.AbstractWidget; diff --git a/runtime-engine/configs/src/main/java/me/shedaniel/rei/impl/client/config/entries/ReloadPluginsEntry.java b/runtime-engine/configs/src/main/java/me/shedaniel/rei/impl/client/config/entries/ReloadPluginsEntry.java new file mode 100644 index 000000000..45ab4ef4e --- /dev/null +++ b/runtime-engine/configs/src/main/java/me/shedaniel/rei/impl/client/config/entries/ReloadPluginsEntry.java @@ -0,0 +1,118 @@ +/* + * 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.config.entries; + +import com.google.common.collect.ImmutableList; +import com.mojang.blaze3d.platform.Window; +import com.mojang.blaze3d.vertex.PoseStack; +import me.shedaniel.clothconfig2.api.AbstractConfigListEntry; +import me.shedaniel.rei.RoughlyEnoughItemsCore; +import me.shedaniel.rei.RoughlyEnoughItemsCoreClient; +import me.shedaniel.rei.api.client.search.SearchProvider; +import me.shedaniel.rei.api.common.plugins.PluginManager; +import me.shedaniel.rei.impl.client.gui.screen.ConfigReloadingScreen; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.chat.NarratorChatListener; +import net.minecraft.client.gui.components.AbstractWidget; +import net.minecraft.client.gui.components.Button; +import net.minecraft.client.gui.components.events.GuiEventListener; +import net.minecraft.client.gui.narration.NarratableEntry; +import net.minecraft.client.gui.screens.Screen; +import net.minecraft.network.chat.TranslatableComponent; +import net.minecraft.util.Unit; +import org.jetbrains.annotations.ApiStatus; + +import java.util.List; +import java.util.Optional; + +@ApiStatus.Internal +public class ReloadPluginsEntry extends AbstractConfigListEntry { + private int width; + private AbstractWidget reloadPluginsButton = new Button(0, 0, 0, 20, NarratorChatListener.NO_TITLE, button -> { + RoughlyEnoughItemsCore.PERFORMANCE_LOGGER.clear(); + RoughlyEnoughItemsCoreClient.reloadPlugins(null, null); + }) { + @Override + public void render(PoseStack matrices, int mouseX, int mouseY, float delta) { + if (PluginManager.areAnyReloading()) { + Screen screen = Minecraft.getInstance().screen; + Minecraft.getInstance().setScreen(new ConfigReloadingScreen(new TranslatableComponent("text.rei.config.is.reloading"), PluginManager::areAnyReloading, () -> Minecraft.getInstance().setScreen(screen))); + } else { + super.render(matrices, mouseX, mouseY, delta); + } + } + }; + private AbstractWidget reloadSearchButton = new Button(0, 0, 0, 20, NarratorChatListener.NO_TITLE, button -> { + SearchProvider.getInstance().clearCache(); + }); + private List children = ImmutableList.of(reloadPluginsButton, reloadSearchButton); + + public ReloadPluginsEntry(int width) { + super(NarratorChatListener.NO_TITLE, false); + this.width = width; + reloadPluginsButton.setMessage(new TranslatableComponent("text.rei.reload_config")); + reloadSearchButton.setMessage(new TranslatableComponent("text.rei.reload_search")); + } + + @Override + public Unit getValue() { + return Unit.INSTANCE; + } + + @Override + public Optional getDefaultValue() { + return Optional.of(Unit.INSTANCE); + } + + @Override + public void save() { + + } + + @Override + public void render(PoseStack matrices, int index, int y, int x, int entryWidth, int entryHeight, int mouseX, int mouseY, boolean isSelected, float delta) { + super.render(matrices, index, y, x, entryWidth, entryHeight, mouseX, mouseY, isSelected, delta); + Window window = Minecraft.getInstance().getWindow(); + this.reloadPluginsButton.active = this.isEditable(); + this.reloadPluginsButton.y = y; + this.reloadPluginsButton.setWidth(width / 2 - 2); + this.reloadPluginsButton.x = x + entryWidth / 2 - width / 2; + this.reloadPluginsButton.render(matrices, mouseX, mouseY, delta); + this.reloadSearchButton.active = this.isEditable() && SearchProvider.getInstance().hasCache(); + this.reloadSearchButton.y = y; + this.reloadSearchButton.setWidth(width / 2 - 2); + this.reloadSearchButton.x = x + entryWidth / 2 + 2; + this.reloadSearchButton.render(matrices, mouseX, mouseY, delta); + } + + @Override + public List children() { + return children; + } + + @Override + public List narratables() { + return children; + } +} diff --git a/runtime-engine/configs/src/main/java/me/shedaniel/rei/impl/client/config/entries/UncertainDisplayViewingScreen.java b/runtime-engine/configs/src/main/java/me/shedaniel/rei/impl/client/config/entries/UncertainDisplayViewingScreen.java new file mode 100644 index 000000000..96c3542c6 --- /dev/null +++ b/runtime-engine/configs/src/main/java/me/shedaniel/rei/impl/client/config/entries/UncertainDisplayViewingScreen.java @@ -0,0 +1,304 @@ +/* + * 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.config.entries; + +import com.google.common.collect.Lists; +import com.mojang.blaze3d.systems.RenderSystem; +import com.mojang.blaze3d.vertex.PoseStack; +import com.mojang.math.Matrix4f; +import dev.architectury.platform.Platform; +import it.unimi.dsi.fastutil.booleans.BooleanConsumer; +import me.shedaniel.clothconfig2.api.ScissorsHandler; +import me.shedaniel.clothconfig2.api.animator.NumberAnimator; +import me.shedaniel.clothconfig2.api.animator.ValueAnimator; +import me.shedaniel.clothconfig2.gui.widget.DynamicNewSmoothScrollingEntryListWidget; +import me.shedaniel.clothconfig2.impl.EasingMethod; +import me.shedaniel.math.Point; +import me.shedaniel.math.Rectangle; +import me.shedaniel.rei.api.client.ClientHelper; +import me.shedaniel.rei.api.client.REIRuntime; +import me.shedaniel.rei.api.client.config.ConfigObject; +import me.shedaniel.rei.api.client.gui.config.DisplayScreenType; +import me.shedaniel.rei.api.client.gui.widgets.Button; +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.ImmutableTextComponent; +import me.shedaniel.rei.impl.ClientInternals; +import me.shedaniel.rei.impl.client.config.ConfigManagerInternal; +import net.minecraft.ChatFormatting; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.chat.NarratorChatListener; +import net.minecraft.client.gui.components.AbstractSliderButton; +import net.minecraft.client.gui.components.events.GuiEventListener; +import net.minecraft.client.gui.screens.Screen; +import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen; +import net.minecraft.network.chat.TextComponent; +import net.minecraft.network.chat.TranslatableComponent; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.util.FormattedCharSequence; +import net.minecraft.util.Mth; +import org.jetbrains.annotations.ApiStatus; + +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; + +@ApiStatus.Internal +public class UncertainDisplayViewingScreen extends Screen { + private static final ResourceLocation DEFAULT = new ResourceLocation("roughlyenoughitems", "textures/gui/screenshot_default.png"); + private static final ResourceLocation COMPOSITE = new ResourceLocation("roughlyenoughitems", "textures/gui/screenshot_composite.png"); + private final List widgets; + protected long start; + protected long duration; + private boolean isSet; + private boolean original; + private double frame = 0; + private double target = 0; + private BooleanConsumer callback; + private Button button; + private Screen parent; + private Widget slider; + private boolean showTips; + public NumberAnimator scroll = ValueAnimator.ofDouble(); + private List allModsUsingJEI = null; + private boolean jeiEnabled = false; + + public UncertainDisplayViewingScreen(Screen parent, DisplayScreenType type, boolean showTips, BooleanConsumer callback) { + super(ImmutableTextComponent.EMPTY); + this.widgets = Lists.newArrayList(); + if (type == DisplayScreenType.UNSET) { + this.isSet = false; + this.original = true; + } else { + this.isSet = true; + this.original = type == DisplayScreenType.ORIGINAL; + moveFrameTo(original ? 0 : 1, false, 0); + } + this.scroll.setAs(0); + this.callback = callback; + this.parent = parent; + this.showTips = showTips; + if (showTips && false && Platform.isForge()) { + this.jeiEnabled = ConfigObject.getInstance().isJEICompatibilityLayerEnabled(); + allModsUsingJEI = ClientInternals.getJeiCompatMods().stream() + .distinct() + .map(ClientHelper.getInstance()::getModFromModId) + .collect(Collectors.toList()); + } + } + + public final double clamp(double v) { + return clamp(v, 30); + } + + public final double clamp(double v, double clampExtension) { + return Mth.clamp(v, -clampExtension, 1 + clampExtension); + } + + private void moveFrameTo(double value, boolean animated, long duration) { + target = clamp(value); + + if (animated) { + start = System.currentTimeMillis(); + this.duration = duration; + } else { + frame = target; + } + } + + @Override + public void init() { + this.children().clear(); + this.widgets.clear(); + this._children().add(button = Widgets.createButton(new Rectangle(width / 2 - 100, height - 40, 200, 20), NarratorChatListener.NO_TITLE) + .onRender((matrices, button) -> { + button.setEnabled(isSet); + if (scroll.target() != 0 && allModsUsingJEI != null) { + button.setText(new TranslatableComponent("gui.done")); + } else { + button.setText(isSet ? new TranslatableComponent("text.rei.select") : new TranslatableComponent("config.roughlyenoughitems.recipeScreenType.unset")); + } + }) + .onClick(button -> { + if (scroll.target() == 0 && allModsUsingJEI != null) { + scroll.setTo(200, 450); + } else if (allModsUsingJEI != null && jeiEnabled) { + ConfigManagerInternal.getInstance().set("advanced.enableJeiCompatibilityLayer", jeiEnabled); + Minecraft.getInstance().setScreen(REIRuntime.getInstance().getPreviousScreen()); + } else { + callback.accept(original); + } + })); + this.widgets.add(transformScroll(new ScreenTypeSelection(width / 2 - 200 - 5, height / 2 - 112 / 2 - 10, DisplayScreenType.ORIGINAL))); + this.widgets.add(transformScroll(Widgets.createLabel(new Point(width / 2 - 200 - 5 + 104, height / 2 - 112 / 2 + 115), new TranslatableComponent("config.roughlyenoughitems.recipeScreenType.original")).noShadow().color(-1124073473))); + this.widgets.add(transformScroll(new ScreenTypeSelection(width / 2 + 5, height / 2 - 112 / 2 - 10, DisplayScreenType.COMPOSITE))); + this.widgets.add(transformScroll(Widgets.createLabel(new Point(width / 2 + 5 + 104, height / 2 - 112 / 2 + 115), new TranslatableComponent("config.roughlyenoughitems.recipeScreenType.composite")).noShadow().color(-1124073473))); + this.widgets.add(slider = transformScroll(Widgets.wrapVanillaWidget(new AbstractSliderButton(width / 2 - 100, height * 2 - 64, 200, 20, new TranslatableComponent("text.rei.jei_compat.false"), 0) { + @Override + protected void updateMessage() { + setMessage(new TranslatableComponent("text.rei.jei_compat." + (jeiEnabled = value == 1f))); + } + + @Override + protected void applyValue() { + + } + + @Override + public void renderButton(PoseStack poseStack, int i, int j, float f) { + y = UncertainDisplayViewingScreen.this.height * 2 - 64; + super.renderButton(poseStack, i, j, f); + y = UncertainDisplayViewingScreen.this.height * 2 - 64 - (int) (scroll.floatValue() / 200f * height); + } + }))); + this._children().addAll(widgets); + } + + public List _children() { + return (List) children(); + } + + private Widget transformScroll(Widget widget) { + return Widgets.withTranslate(widget, () -> Matrix4f.createTranslateMatrix(0, -(scroll.floatValue() / 200f * height), 0)); + } + + @Override + public void render(PoseStack matrices, int int_1, int int_2, float float_1) { + scroll.update(float_1); + if (this.minecraft.level != null) { + renderBackground(matrices); + } else { + this.fillGradient(matrices, 0, 0, this.width, this.height, -16777216, -16777216); + } + if (scroll.target() == 0) { + drawCenteredString(matrices, this.font, new TranslatableComponent("text.rei.recipe_screen_type.selection"), this.width / 2, 20, 16777215); + } else { + drawCenteredString(matrices, this.font, new TranslatableComponent("text.rei.jei_compat"), this.width / 2, 20, 16777215); + } + ScissorsHandler.INSTANCE.scissor(new Rectangle(0, 20 + font.lineHeight + 2, width, height - 42)); + if (showTips) { + float i = 32 - (scroll.floatValue() / 200f * height); + for (FormattedCharSequence s : this.font.split(new TranslatableComponent("text.rei.recipe_screen_type.selection.sub").withStyle(ChatFormatting.GRAY), width - 30)) { + font.drawShadow(matrices, s, width / 2 - font.width(s) / 2, i, -1); + i += 10; + } + if (allModsUsingJEI != null) { + i = 32 + height - (scroll.floatValue() / 200f * height); + for (FormattedCharSequence s : this.font.split(new TranslatableComponent("text.rei.jei_compat.sub", new TranslatableComponent("text.rei.jei_compat.sub.stability"), + new TextComponent(String.join(", ", allModsUsingJEI))).withStyle(ChatFormatting.GRAY), width - 30)) { + font.drawShadow(matrices, s, width / 2 - font.width(s) / 2, i, -1); + i += 10; + } + } + } + super.render(matrices, int_1, int_2, float_1); + for (Widget widget : widgets) { + widget.render(matrices, int_1, int_2, float_1); + } + if (isSet) { + matrices.pushPose(); + matrices.translate(0, -(scroll.floatValue() / 200f * height), 0); + updateFramePosition(float_1); + int x = (int) (width / 2 - 205 + (210 * frame)); + int y = height / 2 - 112 / 2 - 10; + fillGradient(matrices, x - 2, y - 4, x - 6 + 208, y - 4 + 2, -1778384897, -1778384897); + fillGradient(matrices, x - 2, y - 4 + 126 - 2, x - 6 + 208, y - 4 + 126, -1778384897, -1778384897); + fillGradient(matrices, x - 4, y - 4, x - 4 + 2, y - 4 + 126, -1778384897, -1778384897); + fillGradient(matrices, x - 4 + 208 - 2, y - 4, x - 4 + 208, y - 4 + 126, -1778384897, -1778384897); + matrices.popPose(); + } + ScissorsHandler.INSTANCE.removeLastScissor(); + button.render(matrices, int_1, int_2, float_1); + } + + private void updateFramePosition(float delta) { + target = clamp(target); + if (!DynamicNewSmoothScrollingEntryListWidget.Precision.almostEquals(frame, target, DynamicNewSmoothScrollingEntryListWidget.Precision.FLOAT_EPSILON)) + frame = ease(frame, target, Math.min((System.currentTimeMillis() - start) / (double) duration * delta * 3.0D, 1)); + else + frame = target; + } + + private double ease(double start, double end, double amount) { + return start + (end - start) * EasingMethod.EasingMethodImpl.LINEAR.apply(amount); + } + + @Override + public boolean keyPressed(int int_1, int int_2, int int_3) { + if (int_1 == 256 || this.minecraft.options.keyInventory.matches(int_1, int_2)) { + Minecraft.getInstance().setScreen(parent); + if (parent instanceof AbstractContainerScreen) { + REIRuntime.getInstance().getOverlay().get().queueReloadOverlay(); + } + return true; + } + return super.keyPressed(int_1, int_2, int_3); + } + + @Override + public boolean mouseDragged(double d, double e, int i, double f, double g) { + return slider.mouseDragged(d, e, i, f, g) || super.mouseDragged(d, e, i, f, g); + } + + public class ScreenTypeSelection extends WidgetWithBounds { + private final DisplayScreenType type; + private Rectangle bounds; + + public ScreenTypeSelection(int x, int y, DisplayScreenType type) { + this.type = type; + this.bounds = new Rectangle(x - 4 + 16, y - 4, 176 + 8, 120 + 8); + } + + @Override + public Rectangle getBounds() { + return bounds; + } + + @Override + public void render(PoseStack matrices, int i, int i1, float delta) { + RenderSystem.setShaderTexture(0, type == DisplayScreenType.ORIGINAL ? DEFAULT : COMPOSITE); + blit(matrices, bounds.x + (type == DisplayScreenType.ORIGINAL ? 8 : 4), bounds.y + 4, bounds.width - 8, bounds.height - 8, 113, type == DisplayScreenType.ORIGINAL ? 16 : 27, 854 - 113 * 2, 480 - 27 * 2, 854, 480); + } + + @Override + public boolean mouseClicked(double double_1, double double_2, int int_1) { + if (containsMouse(double_1, double_2)) { + original = (type == DisplayScreenType.ORIGINAL); + if (!isSet) { + moveFrameTo(original ? 0 : 1, false, 0); + } + isSet = true; + moveFrameTo(original ? 0 : 1, true, 500); + return true; + } + return false; + } + + @Override + public List children() { + return Collections.emptyList(); + } + } +} diff --git a/runtime-engine/configs/src/main/java/me/shedaniel/rei/impl/client/gui/credits/CreditsEntryListWidget.java b/runtime-engine/configs/src/main/java/me/shedaniel/rei/impl/client/gui/credits/CreditsEntryListWidget.java new file mode 100644 index 000000000..9a576014c --- /dev/null +++ b/runtime-engine/configs/src/main/java/me/shedaniel/rei/impl/client/gui/credits/CreditsEntryListWidget.java @@ -0,0 +1,216 @@ +/* + * 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.credits; + +import com.mojang.blaze3d.vertex.PoseStack; +import me.shedaniel.clothconfig2.gui.widget.DynamicSmoothScrollingEntryListWidget; +import me.shedaniel.rei.impl.client.gui.text.TextTransformations; +import net.minecraft.ChatFormatting; +import net.minecraft.Util; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.GuiComponent; +import net.minecraft.client.gui.narration.NarratableEntry; +import net.minecraft.client.resources.sounds.SimpleSoundInstance; +import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.TextComponent; +import net.minecraft.sounds.SoundEvents; +import net.minecraft.util.FormattedCharSequence; +import org.jetbrains.annotations.ApiStatus; + +import java.net.URI; +import java.net.URISyntaxException; +import java.util.Collections; +import java.util.List; + +@ApiStatus.Internal +public class CreditsEntryListWidget extends DynamicSmoothScrollingEntryListWidget { + private boolean inFocus; + + public CreditsEntryListWidget(Minecraft client, int width, int height, int startY, int endY) { + super(client, width, height, startY, endY, GuiComponent.BACKGROUND_LOCATION); + } + + @Override + public boolean changeFocus(boolean boolean_1) { + if (!this.inFocus && this.getItemCount() == 0) { + return false; + } else { + this.inFocus = !this.inFocus; + if (this.inFocus && this.getFocused() == null && this.getItemCount() > 0) { + this.moveSelection(1); + } else if (this.inFocus && this.getFocused() != null) { + this.moveSelection(0); + } + + return this.inFocus; + } + } + + public void creditsClearEntries() { + clearItems(); + } + + private CreditsItem rei_getEntry(int index) { + return this.children().get(index); + } + + public void creditsAddEntry(CreditsItem entry) { + addItem(entry); + } + + @Override + public int getItemWidth() { + return width - 80; + } + + @Override + protected int getScrollbarPosition() { + return width - 40; + } + + public static abstract class CreditsItem extends DynamicSmoothScrollingEntryListWidget.Entry { + @Override + public List narratables() { + return Collections.emptyList(); + } + } + + public static class TextCreditsItem extends CreditsItem { + private Component text; + + public TextCreditsItem(Component text) { + this.text = text; + } + + @Override + public void render(PoseStack matrices, int index, int y, int x, int entryWidth, int entryHeight, int mouseX, int mouseY, boolean isSelected, float delta) { + Minecraft.getInstance().font.drawShadow(matrices, text.getVisualOrderText(), x + 5, y + 5, -1); + } + + @Override + public int getItemHeight() { + return 12; + } + + @Override + public boolean changeFocus(boolean boolean_1) { + return false; + } + } + + public static class TranslationCreditsItem extends CreditsItem { + private Component language; + private List translators; + private int maxWidth; + + public TranslationCreditsItem(Component language, Component translators, int width, int maxWidth) { + this.language = language; + this.translators = Minecraft.getInstance().font.split(translators, width); + this.maxWidth = maxWidth; + } + + @Override + public void render(PoseStack matrices, int index, int y, int x, int entryWidth, int entryHeight, int mouseX, int mouseY, boolean isSelected, float delta) { + Minecraft.getInstance().font.drawShadow(matrices, language.getVisualOrderText(), x + 5, y + 5, -1); + int yy = y + 5; + for (FormattedCharSequence translator : translators) { + Minecraft.getInstance().font.drawShadow(matrices, translator, x + 5 + maxWidth, yy, -1); + yy += 12; + } + } + + @Override + public int getItemHeight() { + return 12 * translators.size(); + } + + @Override + public boolean changeFocus(boolean boolean_1) { + return false; + } + } + + public static class LinkItem extends CreditsItem { + private Component text; + private List textSplit; + private String link; + private boolean contains; + private boolean rainbow; + + public LinkItem(Component text, String link, int width, boolean rainbow) { + this.text = text; + this.textSplit = Minecraft.getInstance().font.split(text, width); + this.link = link; + this.rainbow = rainbow; + } + + @Override + public void render(PoseStack matrices, int index, int y, int x, int entryWidth, int entryHeight, int mouseX, int mouseY, boolean isSelected, float delta) { + contains = mouseX >= x && mouseX <= x + entryWidth && mouseY >= y && mouseY <= y + entryHeight; + if (contains) { + Minecraft.getInstance().screen.renderTooltip(matrices, new TextComponent("Click to open link."), mouseX, mouseY); + int yy = y; + for (FormattedCharSequence textSp : textSplit) { + FormattedCharSequence underlined = characterVisitor -> { + return textSp.accept((charIndex, style, codePoint) -> characterVisitor.accept(charIndex, style.applyFormat(ChatFormatting.UNDERLINE), codePoint)); + }; + if (rainbow) underlined = TextTransformations.applyRainbow(underlined, x + 5, yy); + Minecraft.getInstance().font.drawShadow(matrices, underlined, x + 5, yy, 0xff1fc3ff); + yy += 12; + } + } else { + int yy = y; + for (FormattedCharSequence textSp : textSplit) { + if (rainbow) textSp = TextTransformations.applyRainbow(textSp, x + 5, yy); + Minecraft.getInstance().font.drawShadow(matrices, textSp, x + 5, yy, 0xff1fc3ff); + yy += 12; + } + } + } + + @Override + public int getItemHeight() { + return 12 * textSplit.size(); + } + + @Override + public boolean changeFocus(boolean boolean_1) { + return false; + } + + @Override + public boolean mouseClicked(double mouseX, double mouseY, int button) { + if (contains && button == 0) { + Minecraft.getInstance().getSoundManager().play(SimpleSoundInstance.forUI(SoundEvents.UI_BUTTON_CLICK, 1.0F)); + try { + Util.getPlatform().openUri(new URI(link)); + return true; + } catch (URISyntaxException e) { + e.printStackTrace(); + } + } + return false; + } + } +} diff --git a/runtime-engine/configs/src/main/java/me/shedaniel/rei/impl/client/gui/credits/CreditsScreen.java b/runtime-engine/configs/src/main/java/me/shedaniel/rei/impl/client/gui/credits/CreditsScreen.java new file mode 100644 index 000000000..8ca4fe7ef --- /dev/null +++ b/runtime-engine/configs/src/main/java/me/shedaniel/rei/impl/client/gui/credits/CreditsScreen.java @@ -0,0 +1,165 @@ +/* + * 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.credits; + +import com.google.common.collect.Lists; +import com.mojang.blaze3d.vertex.PoseStack; +import dev.architectury.platform.Platform; +import me.shedaniel.rei.api.common.util.ImmutableTextComponent; +import me.shedaniel.rei.impl.client.gui.credits.CreditsEntryListWidget.TextCreditsItem; +import me.shedaniel.rei.impl.client.gui.credits.CreditsEntryListWidget.TranslationCreditsItem; +import net.minecraft.ChatFormatting; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.chat.NarratorChatListener; +import net.minecraft.client.gui.components.AbstractButton; +import net.minecraft.client.gui.components.Button; +import net.minecraft.client.gui.screens.Screen; +import net.minecraft.client.resources.language.I18n; +import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.MutableComponent; +import net.minecraft.network.chat.TextComponent; +import net.minecraft.network.chat.TranslatableComponent; +import net.minecraft.util.Tuple; +import org.jetbrains.annotations.ApiStatus; + +import java.lang.reflect.InvocationTargetException; +import java.util.List; +import java.util.Locale; +import java.util.stream.Collectors; + +@ApiStatus.Internal +public class CreditsScreen extends Screen { + private Screen parent; + private AbstractButton buttonDone; + private CreditsEntryListWidget entryListWidget; + + public CreditsScreen(Screen parent) { + super(new TextComponent("")); + this.parent = parent; + } + + @Override + public boolean keyPressed(int keyCode, int scanCode, int modifiers) { + if (keyCode == 256 && this.shouldCloseOnEsc()) { + openPrevious(); + return true; + } + return super.keyPressed(keyCode, scanCode, modifiers); + } + + public static class TranslatorEntry { + private final String name; + private final boolean proofreader; + + public TranslatorEntry(String name) { + this(name, false); + } + + public TranslatorEntry(String name, boolean proofreader) { + this.name = name; + this.proofreader = proofreader; + } + + public String getName() { + return name; + } + } + + @Override + public void init() { + addWidget(entryListWidget = new CreditsEntryListWidget(minecraft, width, height, 32, height - 32)); + entryListWidget.creditsClearEntries(); + List>> translators = Lists.newArrayList(); + Exception[] exception = {null}; + fillTranslators(exception, translators); + List>> translatorsMapped = translators.stream().map(pair -> { + return new Tuple<>( + " " + (I18n.exists("language.roughlyenoughitems." + pair.getA().toLowerCase(Locale.ROOT).replace(' ', '_')) ? I18n.get("language.roughlyenoughitems." + pair.getA().toLowerCase(Locale.ROOT).replace(' ', '_')) : pair.getA()), + pair.getB() + ); + }).collect(Collectors.toList()); + int i = width - 80 - 6; + for (String line : String.format("§lRoughly Enough Items (v%s)\n§7Originally a fork for Almost Enough Items.\n\n§lLanguage Translation\n%s\n\n§lLicense\n§7Roughly Enough Items is licensed under MIT.", Platform.getMod("roughlyenoughitems").getVersion(), "%translators%").split("\n")) + if (line.equalsIgnoreCase("%translators%")) { + if (exception[0] != null) { + entryListWidget.creditsAddEntry(new TextCreditsItem(new ImmutableTextComponent("Failed to get translators: " + exception[0].toString()))); + for (StackTraceElement traceElement : exception[0].getStackTrace()) + entryListWidget.creditsAddEntry(new TextCreditsItem(new ImmutableTextComponent(" at " + traceElement))); + } else { + int maxWidth = translatorsMapped.stream().mapToInt(pair -> font.width(pair.getA())).max().orElse(0) + 5; + for (Tuple> pair : translatorsMapped) { + MutableComponent text = new TextComponent(""); + boolean isFirst = true; + for (TranslatorEntry entry : pair.getB()) { + if (!isFirst) { + text = text.append(new TextComponent(", ")); + } + isFirst = false; + MutableComponent component = new TextComponent(entry.getName()); + if (entry.proofreader) + component = component.withStyle(ChatFormatting.GOLD); + text = text.append(component); + } + entryListWidget.creditsAddEntry(new TranslationCreditsItem(new TranslatableComponent(pair.getA()), text, i - maxWidth - 10, maxWidth)); + } + } + } else entryListWidget.creditsAddEntry(new TextCreditsItem(new ImmutableTextComponent(line))); + entryListWidget.creditsAddEntry(new TextCreditsItem(NarratorChatListener.NO_TITLE)); + entryListWidget.creditsAddEntry(new CreditsEntryListWidget.LinkItem(new ImmutableTextComponent("Visit the project at GitHub."), "https://www.github.com/shedaniel/RoughlyEnoughItems", entryListWidget.getItemWidth(), false)); + entryListWidget.creditsAddEntry(new CreditsEntryListWidget.LinkItem(new ImmutableTextComponent("Visit the project page at CurseForge."), "https://www.curseforge.com/minecraft/mc-mods/roughly-enough-items", entryListWidget.getItemWidth(), false)); + entryListWidget.creditsAddEntry(new CreditsEntryListWidget.LinkItem(new ImmutableTextComponent("Support the project via Patreon!"), "https://patreon.com/shedaniel", entryListWidget.getItemWidth(), true)); + entryListWidget.creditsAddEntry(new TextCreditsItem(NarratorChatListener.NO_TITLE)); + addRenderableWidget(buttonDone = new Button(width / 2 - 100, height - 26, 200, 20, new TranslatableComponent("gui.done"), button -> openPrevious())); + } + + private static void fillTranslators(Exception[] exception, List>> translators) { + try { + Class.forName("me.shedaniel.rei.impl.client.gui.credits.%s.CreditsScreenImpl".formatted(Platform.isForge() ? "forge" : "fabric")) + .getDeclaredMethod("fillTranslators", Exception[].class, List.class) + .invoke(null, exception, translators); + } catch (IllegalAccessException | ClassNotFoundException | NoSuchMethodException | InvocationTargetException e) { + throw new RuntimeException(e); + } + } + + private void openPrevious() { + Minecraft.getInstance().setScreen(parent); + } + + @Override + public boolean mouseScrolled(double mouseX, double mouseY, double amount) { + if (entryListWidget.mouseScrolled(mouseX, mouseY, amount)) + return true; + return super.mouseScrolled(mouseX, mouseY, amount); + } + + @Override + public void render(PoseStack matrices, int mouseX, int mouseY, float delta) { + this.renderDirtBackground(0); + this.entryListWidget.render(matrices, mouseX, mouseY, delta); + drawCenteredString(matrices, this.font, I18n.get("text.rei.credits"), this.width / 2, 16, 16777215); + super.render(matrices, mouseX, mouseY, delta); + } + +} -- cgit