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/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 +- 19 files changed, 768 insertions(+), 145 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 (limited to 'runtime/src/main/java/me') 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; +import java.util.List; +import java.util.function.Supplier; + +public class HintWidget extends WidgetWithBounds { + private static final int MAX_WIDTH = 180; + private static final int MAX_HEIGHT = 130; + private final HintsContainerWidget parent; + private final Rectangle bounds = new Rectangle(); + private final Rectangle okayBounds = new Rectangle(); + private final int margin; + private final Supplier point; + private final String uuid; + private final Collection lines; + private List> wrapped; + private final NumberAnimator scroll = ValueAnimator.ofDouble(0); + private int contentHeight; + + public HintWidget(HintsContainerWidget parent, int margin, Supplier point, String uuid, Collection lines) { + this.parent = parent; + this.margin = margin; + this.point = point; + this.uuid = uuid; + this.lines = lines; + recalculateBounds(); + } + + void recalculateBounds() { + int screenWidth = minecraft.getWindow().getGuiScaledWidth(); + int screenHeight = minecraft.getWindow().getScreenHeight(); + int width = Mth.clamp(CollectionUtils.mapAndMax((Collection) lines, + l -> CollectionUtils.max(font.split(l, MAX_WIDTH - 8), + Comparator.comparingLong(value -> font.width(value) + 8)) + .map(value -> font.width(value) + 8).orElse(0), + Comparator.naturalOrder()) + .orElse(0), 60, MAX_WIDTH); + Point point = this.point.get(); + int spaceLeft = Math.max(point.x - 4, 0); + int spaceRight = Math.max(screenWidth - point.x - 4, 0); + this.bounds.width = Math.min(width, Math.max(spaceLeft, spaceRight)); + if (spaceRight >= spaceLeft) { + this.bounds.x = point.x + margin; + } else { + this.bounds.x = point.x - margin - this.bounds.width; + } + this.wrapped = CollectionUtils.map(lines, l -> font.split(l, this.bounds.width - 8)); + int height = 8 + 9; + for (List formattedCharSequences : wrapped) { + height += formattedCharSequences.size() * 9; + height += 2; + } + this.contentHeight = height - 9 - 2; + this.bounds.height = Math.min(height, MAX_HEIGHT); + this.bounds.y = Mth.clamp(point.y + margin - this.bounds.height, 4, screenHeight - this.bounds.height - 4); + } + + @Override + public Rectangle getBounds() { + return this.bounds; + } + + public String getUuid() { + return uuid; + } + + @Override + public void render(PoseStack poses, int mouseX, int mouseY, float delta) { + this.scroll.setTarget(ScrollingContainer.handleBounceBack(scroll.target(), this.contentHeight - (this.bounds.height - 8 - 9) - 9, delta, .08)); + this.scroll.update(delta); + + RenderSystem.disableDepthTest(); + Tesselator tesselator = Tesselator.getInstance(); + BufferBuilder bufferBuilder = tesselator.getBuilder(); + RenderSystem.setShader(GameRenderer::getPositionColorShader); + bufferBuilder.begin(VertexFormat.Mode.QUADS, DefaultVertexFormat.POSITION_COLOR); + poses.pushPose(); + poses.translate(0, 0, 450); + Matrix4f pose = poses.last().pose(); + int background = 0xf0100010; + int color1 = 0x505000ff; + int color2 = color1; + int x = this.bounds.x, y = this.bounds.y, width = this.bounds.width, height = this.bounds.height; + fillGradient(pose, bufferBuilder, x, y - 1, x + width, y, 400, background, background); + fillGradient(pose, bufferBuilder, x, y + height, x + width, y + height + 1, 400, background, background); + fillGradient(pose, bufferBuilder, x, y, x + width, y + height, 400, background, background); + fillGradient(pose, bufferBuilder, x - 1, y, x, y + height, 400, background, background); + fillGradient(pose, bufferBuilder, x + width, y, x + width + 1, y + height, 400, background, background); + fillGradient(pose, bufferBuilder, x, y + 1, x + 1, y + height - 1, 400, color1, color2); + fillGradient(pose, bufferBuilder, x + width - 1, y + 1, x + width, y + height - 1, 400, color1, color2); + fillGradient(pose, bufferBuilder, x, y, x + width, y + 1, 400, color1, color1); + fillGradient(pose, bufferBuilder, x, y + height - 1, x + width, y + height, 400, color2, color2); + bufferBuilder.end(); + BufferUploader.end(bufferBuilder); + int lineY = y + 4; + + try (CloseableScissors scissors = Widget.scissor(pose, new Rectangle(x + 4, y + 4, width - 8, height - 8 - 9 - 2))) { + for (List block : wrapped) { + for (FormattedCharSequence line : block) { + font.drawShadow(poses, line, x + 4, lineY - scroll.intValue(), 0xFFFFFFFF); + lineY += 9; + } + + lineY += 2; + } + } + + MutableComponent okay = new TranslatableComponent("gui.ok"); + int okayWidth = font.width(okay); + int midPoint = x + 4 + (width - 4) / 2; + this.okayBounds.setBounds(midPoint - okayWidth / 2, lineY, okayWidth, 9); + if (this.okayBounds.contains(mouseX, mouseY)) { + okay = okay.withStyle(ChatFormatting.UNDERLINE); + } + font.drawShadow(poses, okay, this.okayBounds.x, this.okayBounds.y, 0xFF999999); + + poses.popPose(); + RenderSystem.enableDepthTest(); + + if (this.bounds.contains(mouseX, mouseY)) { + ScreenOverlayImpl.getInstance().clearTooltips(); + } + } + + @Override + public List children() { + return List.of(); + } + + @Override + public boolean mouseClicked(double mouseX, double mouseY, int button) { + if (this.okayBounds.contains(mouseX, mouseY)) { + this.parent.removeHint(this); + Widgets.produceClickSound(); + return true; + } + + return super.mouseClicked(mouseX, mouseY, button); + } + + @Override + public boolean mouseScrolled(double mouseX, double mouseY, double amount) { + if (containsMouse(mouseX, mouseY)) { + scroll.setTo(scroll.target() + ClothConfigInitializer.getScrollStep() * amount * (getBounds().getWidth() / -50.0), ClothConfigInitializer.getScrollDuration()); + return true; + } + + return super.mouseScrolled(mouseX, mouseY, amount); + } +} diff --git a/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/widget/hint/HintsContainerWidget.java b/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/widget/hint/HintsContainerWidget.java new file mode 100644 index 000000000..1be29d6e3 --- /dev/null +++ b/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/widget/hint/HintsContainerWidget.java @@ -0,0 +1,91 @@ +package me.shedaniel.rei.impl.client.gui.widget.hint; + +import com.google.gson.Gson; +import dev.architectury.platform.Platform; +import me.shedaniel.math.Point; +import me.shedaniel.rei.api.client.gui.widgets.DelegateWidget; +import me.shedaniel.rei.api.client.gui.widgets.Widget; +import me.shedaniel.rei.api.client.gui.widgets.Widgets; +import net.minecraft.client.Minecraft; +import net.minecraft.network.chat.FormattedText; + +import java.io.IOException; +import java.io.Reader; +import java.io.Writer; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.StandardOpenOption; +import java.util.*; +import java.util.function.Supplier; + +public class HintsContainerWidget extends DelegateWidget { + private final List hints = new ArrayList<>(); + private final Widget delegate = Widgets.concat((List) (List) hints); + private HintsConfig config; + + public HintsContainerWidget() { + super(Widgets.noOp()); + this.read(); + } + + public void init() { + for (HintWidget hint : this.hints) { + hint.recalculateBounds(); + } + } + + @Override + protected Widget delegate() { + return this.delegate; + } + + public void addHint(int margin, Supplier point, String uuid, Collection lines) { + if (this.config.shownHints.add(uuid)) { + this.hints.removeIf(hintWidget -> hintWidget.getUuid().equals(uuid)); + this.hints.add(new HintWidget(this, margin, point, uuid, lines)); + this.write(); + } + } + + void removeHint(HintWidget hintWidget) { + this.hints.remove(hintWidget); + } + + public void read() { + Path path = Platform.getConfigFolder().resolve("roughlyenoughitems/hints.json"); + this.config = new HintsConfig(); + String uuid = Minecraft.getInstance().getUser().getUuid(); + if (Files.exists(path)) { + try (Reader reader = Files.newBufferedReader(path, StandardCharsets.UTF_8)) { + this.config = new Gson().fromJson(reader, HintsConfig.class); + if (!uuid.equals(this.config.UUID)) { + this.config = new HintsConfig(); + } + } catch (IOException e) { + e.printStackTrace(); + } + } + this.config.UUID = uuid; + write(); + } + + public void write() { + Path path = Platform.getConfigFolder().resolve("roughlyenoughitems/hints.json"); + try { + Files.createDirectories(path.getParent()); + } catch (IOException e) { + e.printStackTrace(); + } + try (Writer writer = Files.newBufferedWriter(path, StandardCharsets.UTF_8, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING)) { + new Gson().toJson(this.config, writer); + } catch (IOException e) { + e.printStackTrace(); + } + } + + private static class HintsConfig { + private String UUID; + private Set shownHints = new LinkedHashSet<>(); + } +} diff --git a/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/widget/region/RegionEntryWidget.java b/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/widget/region/RegionEntryWidget.java index c4a50ffd1..07f397c92 100644 --- a/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/widget/region/RegionEntryWidget.java +++ b/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/widget/region/RegionEntryWidget.java @@ -32,14 +32,13 @@ import me.shedaniel.rei.api.client.entry.region.RegionEntry; import me.shedaniel.rei.api.client.favorites.FavoriteEntry; import me.shedaniel.rei.api.client.favorites.FavoriteMenuEntry; import me.shedaniel.rei.api.client.overlay.ScreenOverlay; -import me.shedaniel.rei.api.common.util.CollectionUtils; 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.widget.DisplayedEntryWidget; -import net.minecraft.client.gui.components.events.GuiEventListener; -import java.util.*; +import java.util.Collection; +import java.util.Optional; +import java.util.UUID; import java.util.function.Supplier; public class RegionEntryWidget> extends DisplayedEntryWidget { @@ -77,54 +76,13 @@ public class RegionEntryWidget> extends DisplayedEntryW MenuAccess access = overlay.menuAccess(); UUID uuid = entry.getEntry().getUuid(); - access.openOrClose(uuid, getBounds(), () -> - CollectionUtils.map(menuEntries.get().get(), entry -> convertMenu(overlay, entry))); + access.openOrClose(uuid, getBounds(), menuEntries.get()); } Vector4f vector4f = new Vector4f(mouseX, mouseY, 0, 1.0F); vector4f.transform(matrices.last().pose()); super.render(matrices, (int) vector4f.x(), (int) vector4f.y(), delta); } - private MenuEntry convertMenu(ScreenOverlayImpl overlay, FavoriteMenuEntry entry) { - return new MenuEntry() { - @Override - public List children() { - return Collections.singletonList(entry); - } - - @Override - public void render(PoseStack poseStack, int i, int j, float f) { - entry.render(poseStack, i, j, f); - } - - @Override - public int getEntryWidth() { - return entry.getEntryWidth(); - } - - @Override - public int getEntryHeight() { - return entry.getEntryHeight(); - } - - @Override - public void updateInformation(int xPos, int yPos, boolean selected, boolean containsMouse, boolean rendering, int width) { - entry.closeMenu = overlay.menuAccess()::close; - entry.updateInformation(xPos, yPos, selected, containsMouse, rendering, width); - } - - @Override - public int getZ() { - return entry.getZ(); - } - - @Override - public void setZ(int z) { - entry.setZ(z); - } - }; - } - @Override protected boolean doAction(double mouseX, double mouseY, int button) { return entry.getEntry().doAction(button) || super.doAction(mouseX, mouseY, button); diff --git a/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/widget/search/OverlaySearchField.java b/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/widget/search/OverlaySearchField.java index 3180c2b0b..9bd784583 100644 --- a/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/widget/search/OverlaySearchField.java +++ b/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/widget/search/OverlaySearchField.java @@ -37,6 +37,7 @@ import me.shedaniel.math.Rectangle; import me.shedaniel.math.impl.PointHelper; import me.shedaniel.rei.api.client.REIRuntime; import me.shedaniel.rei.api.client.config.ConfigObject; +import me.shedaniel.rei.api.client.gui.config.SearchFieldLocation; import me.shedaniel.rei.api.client.gui.config.SyntaxHighlightingMode; import me.shedaniel.rei.api.client.gui.widgets.Tooltip; import me.shedaniel.rei.api.client.gui.widgets.Widgets; @@ -149,8 +150,6 @@ public class OverlaySearchField extends TextFieldWidget implements TextFieldWidg progress.update(delta); RenderSystem.disableDepthTest(); if (isMain) drawHint(matrices, mouseX, mouseY); - setSuggestion(!isFocused() && getText().isEmpty() ? I18n.get("text.rei.search.field.suggestion") : null); - super.render(matrices, mouseX, mouseY, delta); RenderSystem.enableDepthTest(); } @@ -192,8 +191,11 @@ public class OverlaySearchField extends TextFieldWidget implements TextFieldWidg return Color.ofRGBA(r, g, b, (color1.getAlpha() + color2.getAlpha()) / 2); }).orElse(Color.ofTransparent(0x50000000)); int height = 6 + font.lineHeight * sequences.size() + (hasProgress ? 2 : 0) + (buttons.isEmpty() ? 0 : (int) Math.ceil(buttons.size() / 3.0) * 20); + boolean top = ConfigObject.getInstance().getSearchFieldLocation() == SearchFieldLocation.TOP_SIDE; int x = getBounds().getX() + 2; - int y = getBounds().getY() - height; + int y = getBounds().getY() + (top ? getBounds().getHeight() : -height); + if (new Rectangle(x - 1, y - 1, width + 2, height + 2).contains(mouseX, mouseY)) + ScreenOverlayImpl.getInstance().clearTooltips(); Tesselator tesselator = Tesselator.getInstance(); BufferBuilder bufferBuilder = tesselator.getBuilder(); RenderSystem.setShader(GameRenderer::getPositionColorShader); @@ -202,15 +204,15 @@ public class OverlaySearchField extends TextFieldWidget implements TextFieldWidg int background = 0xf0100010; int color1 = color.getColor(); int color2 = color.darker(2).getColor(); - fillGradient(pose, bufferBuilder, x, y - 1, x + width, y, 400, background, background); - fillGradient(pose, bufferBuilder, x, y + height, x + width, y + height + 1, 400, background, background); + if (!top) fillGradient(pose, bufferBuilder, x, y - 1, x + width, y, 400, background, background); + if (top) fillGradient(pose, bufferBuilder, x, y + height, x + width, y + height + 1, 400, background, background); fillGradient(pose, bufferBuilder, x, y, x + width, y + height, 400, background, background); fillGradient(pose, bufferBuilder, x - 1, y, x, y + height, 400, background, background); fillGradient(pose, bufferBuilder, x + width, y, x + width + 1, y + height, 400, background, background); fillGradient(pose, bufferBuilder, x, y + 1, x + 1, y + height - 1, 400, color1, color2); fillGradient(pose, bufferBuilder, x + width - 1, y + 1, x + width, y + height - 1, 400, color1, color2); - fillGradient(pose, bufferBuilder, x, y, x + width, y + 1, 400, color1, color1); - fillGradient(pose, bufferBuilder, x, y + height - 1, x + width, y + height, 400, color2, color2); + if (!top) fillGradient(pose, bufferBuilder, x, y, x + width, y + 1, 400, color1, color1); + if (top) fillGradient(pose, bufferBuilder, x, y + height - 1, x + width, y + height, 400, color2, color2); if (hasProgress) { int progressWidth = (int) Math.round(width * this.progress.doubleValue()); @@ -362,6 +364,9 @@ public class OverlaySearchField extends TextFieldWidget implements TextFieldWidg @Override public void render(PoseStack matrices, int mouseX, int mouseY, float delta) { - laterRender(matrices, mouseX, mouseY, delta); + RenderSystem.disableDepthTest(); + setSuggestion(!isFocused() && getText().isEmpty() ? I18n.get("text.rei.search.field.suggestion") : null); + super.render(matrices, mouseX, mouseY, delta); + RenderSystem.enableDepthTest(); } } diff --git a/runtime/src/main/java/me/shedaniel/rei/impl/client/search/method/unihan/DoublePinyinInputMethod.java b/runtime/src/main/java/me/shedaniel/rei/impl/client/search/method/unihan/DoublePinyinInputMethod.java new file mode 100644 index 000000000..7f6124379 --- /dev/null +++ b/runtime/src/main/java/me/shedaniel/rei/impl/client/search/method/unihan/DoublePinyinInputMethod.java @@ -0,0 +1,351 @@ +package me.shedaniel.rei.impl.client.search.method.unihan; + +import com.google.common.collect.BiMap; +import com.google.common.collect.ImmutableBiMap; +import dev.architectury.platform.Platform; +import dev.architectury.utils.value.BooleanValue; +import it.unimi.dsi.fastutil.ints.IntList; +import it.unimi.dsi.fastutil.objects.Object2IntMap; +import it.unimi.dsi.fastutil.objects.Object2IntMaps; +import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; +import me.shedaniel.rei.api.client.favorites.FavoriteMenuEntry; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.screens.Screen; +import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.TranslatableComponent; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.StandardOpenOption; +import java.util.*; + +public class DoublePinyinInputMethod extends PinyinInputMethod { + private Converter converter = Converters.SOUGOU; + + public DoublePinyinInputMethod(UniHanManager manager) { + super(manager); + this.read(); + } + + private void read() { + Path path = Platform.getConfigFolder().resolve("roughlyenoughitems/pinyin_double.properties"); + this.converter = Converters.SOUGOU; + if (Files.exists(path)) { + try { + Properties properties = new Properties(); + try (InputStream stream = Files.newInputStream(path)) { + properties.load(stream); + } + this.converter = Converters.CONVERTERS.getOrDefault(Objects.toString(properties.getOrDefault("Converter", "sougou")), Converters.SOUGOU); + } catch (Exception e) { + e.printStackTrace(); + try { + Files.deleteIfExists(path); + } catch (IOException ex) { + throw new RuntimeException(ex); + } + } + } + this.write(); + } + + private void write() { + Path path = Platform.getConfigFolder().resolve("roughlyenoughitems/pinyin_double.properties"); + Properties properties = new Properties(); + properties.put("Converter", Converters.CONVERTERS.inverse().get(this.converter)); + try (OutputStream stream = Files.newOutputStream(path, StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.CREATE)) { + properties.store(stream, "Double Pinyin Options"); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + @Override + public Component getName() { + return new TranslatableComponent("text.rei.input.methods.pinyin.double"); + } + + @Override + public Component getDescription() { + return new TranslatableComponent("text.rei.input.methods.pinyin.double.description"); + } + + @Override + public List getOptionsMenuEntries() { + List innerEntries = new ArrayList<>(); + for (Map.Entry entry : Converters.CONVERTERS.entrySet()) { + innerEntries.add(FavoriteMenuEntry.createToggle(new TranslatableComponent("text.rei.input.methods.pinyin.double.scheme." + entry.getKey()), + new BooleanValue() { + @Override + public void accept(boolean t) { + DoublePinyinInputMethod.this.converter = entry.getValue(); + DoublePinyinInputMethod.this.write(); + DoublePinyinInputMethod.this.dataMap.clear(); + DoublePinyinInputMethod.this.load(); + } + + @Override + public boolean getAsBoolean() { + return DoublePinyinInputMethod.this.converter == entry.getValue(); + } + })); + } + return List.of(FavoriteMenuEntry.createSubMenu(new TranslatableComponent("text.rei.input.methods.pinyin.double.scheme"), + innerEntries)); + } + + @Override + protected ExpendedChar asExpendedChar(String string) { + IntList[] codepoints = new IntList[3]; + int skip = 2; + int tone = -1; + char[] chars = string.toCharArray(); + if (chars[0] == 's' && chars[1] == 'h') { + codepoints[0] = this.converter.convert("sh"); + } else if (chars[0] == 'c' && chars[1] == 'h') { + codepoints[0] = this.converter.convert("ch"); + } else if (chars[0] == 'z' && chars[1] == 'h') { + codepoints[0] = this.converter.convert("zh"); + } else { + skip = 1; + ToneEntry toneEntry = toneMap.get(chars[0]); + if (toneEntry == null) { + codepoints[0] = this.converter.convert(chars[0] + ""); + } else { + codepoints[0] = this.converter.convert(((char) toneEntry.codepoint()) + ""); + tone = toneEntry.tone(); + } + } + StringBuilder builder = new StringBuilder(); + for (int i = skip; i < chars.length;