From 47ae69cd83f36aa5acf4bfedf9bbccc0eec34b67 Mon Sep 17 00:00:00 2001 From: shedaniel Date: Sat, 3 Dec 2022 22:17:27 +0800 Subject: Add init progress to IME --- .../client/gui/screen/ConfigReloadingScreen.java | 33 ++++++++++++---------- .../gui/widget/CraftableFilterButtonWidget.java | 10 +++++-- .../search/method/unihan/UniHanInputMethod.java | 10 +++++-- .../client/search/method/unihan/UniHanManager.java | 14 +++++++-- 4 files changed, 45 insertions(+), 22 deletions(-) (limited to 'runtime/src/main/java/me/shedaniel') diff --git a/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/screen/ConfigReloadingScreen.java b/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/screen/ConfigReloadingScreen.java index eeb1a1852..082ac44ec 100644 --- a/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/screen/ConfigReloadingScreen.java +++ b/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/screen/ConfigReloadingScreen.java @@ -28,14 +28,17 @@ import net.minecraft.Util; import net.minecraft.client.gui.screens.Screen; import net.minecraft.network.chat.Component; import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.Nullable; import java.util.function.BooleanSupplier; +import java.util.function.Supplier; @ApiStatus.Internal public class ConfigReloadingScreen extends Screen { private final Component title; private final BooleanSupplier predicate; - private Runnable parent; + private Supplier<@Nullable Component> subtitle = () -> null; + private final Runnable parent; public ConfigReloadingScreen(Component title, BooleanSupplier predicate, Runnable parent) { super(Component.empty()); @@ -44,6 +47,10 @@ public class ConfigReloadingScreen extends Screen { this.parent = parent; } + public void setSubtitle(Supplier<@Nullable Component> subtitle) { + this.subtitle = subtitle; + } + @Override public boolean shouldCloseOnEsc() { return false; @@ -56,21 +63,17 @@ public class ConfigReloadingScreen extends Screen { parent.run(); return; } - drawCenteredString(matrices, this.font, title, this.width / 2, this.height / 2 - 50, 16777215); - String text; - switch ((int) (Util.getMillis() / 300L % 4L)) { - case 0: - default: - text = "O o o"; - break; - case 1: - case 3: - text = "o O o"; - break; - case 2: - text = "o o O"; + drawCenteredString(matrices, this.font, title, this.width / 2, this.height / 2 - 50, 0xffffff); + String text = switch ((int) (Util.getMillis() / 300L % 4L)) { + case 1, 3 -> "o O o"; + case 2 -> "o o O"; + default -> "O o o"; + }; + drawCenteredString(matrices, this.font, text, this.width / 2, this.height / 2 - 50 + 9, 0x808080); + Component subtitle = this.subtitle.get(); + if (subtitle != null) { + drawCenteredString(matrices, this.font, subtitle, this.width / 2, this.height / 2 - 50 + 9 + 9, 0x808080); } - drawCenteredString(matrices, this.font, text, this.width / 2, this.height / 2 - 41, 8421504); super.render(matrices, mouseX, mouseY, delta); } } diff --git a/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/widget/CraftableFilterButtonWidget.java b/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/widget/CraftableFilterButtonWidget.java index 94bf01921..d0d611890 100644 --- a/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/widget/CraftableFilterButtonWidget.java +++ b/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/widget/CraftableFilterButtonWidget.java @@ -49,6 +49,7 @@ import net.minecraft.client.gui.screens.Screen; import net.minecraft.client.renderer.entity.ItemRenderer; import net.minecraft.network.chat.Component; import net.minecraft.resources.ResourceLocation; +import net.minecraft.util.Mth; import net.minecraft.world.item.ItemStack; import net.minecraft.world.level.block.Blocks; @@ -130,7 +131,8 @@ public class CraftableFilterButtonWidget { ConfigManagerImpl.getInstance().getConfig().setInputMethodId(new ResourceLocation("rei:default")); }).join(); - CompletableFuture future = pair.getValue().prepare(service).whenComplete((unused, throwable) -> { + double[] progress = {0}; + CompletableFuture future = pair.getValue().prepare(service, p -> progress[0] = Mth.clamp(p, 0, 1)).whenComplete((unused, throwable) -> { if (throwable != null) { InternalLogger.getInstance().error("Failed to prepare input method", throwable); ConfigManagerImpl.getInstance().getConfig().setInputMethodId(new ResourceLocation("rei:default")); @@ -139,10 +141,12 @@ public class CraftableFilterButtonWidget { } }); Screen screen = Minecraft.getInstance().screen; - Minecraft.getInstance().setScreen(new ConfigReloadingScreen(Component.translatable("text.rei.input.methods.initializing"), + ConfigReloadingScreen reloadingScreen = new ConfigReloadingScreen(Component.translatable("text.rei.input.methods.initializing"), () -> !future.isDone(), () -> { Minecraft.getInstance().setScreen(screen); - })); + }); + reloadingScreen.setSubtitle(() -> new TranslatableComponent("text.rei.input.methods.reload.progress", String.format("%.2f", progress[0] * 100))); + Minecraft.getInstance().setScreen(reloadingScreen); future.whenComplete((unused, throwable) -> { service.shutdown(); }); diff --git a/runtime/src/main/java/me/shedaniel/rei/impl/client/search/method/unihan/UniHanInputMethod.java b/runtime/src/main/java/me/shedaniel/rei/impl/client/search/method/unihan/UniHanInputMethod.java index 1402c54ba..91619f644 100644 --- a/runtime/src/main/java/me/shedaniel/rei/impl/client/search/method/unihan/UniHanInputMethod.java +++ b/runtime/src/main/java/me/shedaniel/rei/impl/client/search/method/unihan/UniHanInputMethod.java @@ -50,9 +50,15 @@ public abstract class UniHanInputMethod implements InputMethod { @Override public CompletableFuture prepare(Executor executor) { + return this.prepare(executor, p -> {}); + } + + @Override + public CompletableFuture prepare(Executor executor, ProgressCallback progressCallback) { return dispose(executor) - .thenRunAsync(manager::download, executor) - .thenRunAsync(this::load, executor); + .thenRunAsync(() -> manager.download(p -> progressCallback.onProgress(p * 0.99)), executor) + .thenRunAsync(this::load, executor) + .whenComplete((aVoid, throwable) -> progressCallback.onProgress(1.0)); } public void load() { diff --git a/runtime/src/main/java/me/shedaniel/rei/impl/client/search/method/unihan/UniHanManager.java b/runtime/src/main/java/me/shedaniel/rei/impl/client/search/method/unihan/UniHanManager.java index bf36fc130..f2f9a5b74 100644 --- a/runtime/src/main/java/me/shedaniel/rei/impl/client/search/method/unihan/UniHanManager.java +++ b/runtime/src/main/java/me/shedaniel/rei/impl/client/search/method/unihan/UniHanManager.java @@ -23,6 +23,7 @@ package me.shedaniel.rei.impl.client.search.method.unihan; +import me.shedaniel.rei.api.client.search.method.InputMethod; import me.shedaniel.rei.impl.common.InternalLogger; import org.apache.commons.io.IOUtils; import org.apache.commons.io.LineIterator; @@ -50,10 +51,18 @@ public class UniHanManager { return Files.exists(unihanPath); } - public void download() { + public void download(InputMethod.ProgressCallback progressCallback) { + try { + download("https://www.shedaniel.moe/uoAqECzbQo5s.zip", progressCallback); + } catch (Exception e) { + download("https://www.unicode.org/Public/UCD/latest/ucd/Unihan.zip", progressCallback); + } + } + + public void download(String URL, InputMethod.ProgressCallback progressCallback) { if (downloaded()) return; try { - URL url = new URL("https://www.unicode.org/Public/UCD/latest/ucd/Unihan.zip"); + URL url = new URL("https://shedaniel.moe/uoAqECzbQo5s.zip"); Files.deleteIfExists(unihanPath); Path parent = unihanPath.getParent(); if (parent != null) Files.createDirectories(parent); @@ -74,6 +83,7 @@ public class UniHanManager { lastPercent = percent; InternalLogger.getInstance().debug("Downloading UniHan Progress: %d%%".formatted(percent)); } + progressCallback.onProgress(progress); bufferedStream.write(data, 0, x); } bufferedStream.close(); -- cgit From 640704af10d61dede9a88728b1d51438233e8cdb Mon Sep 17 00:00:00 2001 From: shedaniel Date: Fri, 9 Dec 2022 02:08:13 +0800 Subject: Add Hint Onboarding, Fix #1156, Fix #1256 --- .../rei/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/shedaniel') 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.Confi