ContainerScreenOverlay.java 35 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648
  1. /*
  2. * This file is licensed under the MIT License, part of Roughly Enough Items.
  3. * Copyright (c) 2018, 2019, 2020 shedaniel
  4. *
  5. * Permission is hereby granted, free of charge, to any person obtaining a copy
  6. * of this software and associated documentation files (the "Software"), to deal
  7. * in the Software without restriction, including without limitation the rights
  8. * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  9. * copies of the Software, and to permit persons to whom the Software is
  10. * furnished to do so, subject to the following conditions:
  11. *
  12. * The above copyright notice and this permission notice shall be included in all
  13. * copies or substantial portions of the Software.
  14. *
  15. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  16. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  17. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  18. * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  19. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  20. * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  21. * SOFTWARE.
  22. */
  23. package me.shedaniel.rei.gui;
  24. import com.google.common.collect.Lists;
  25. import com.mojang.blaze3d.systems.RenderSystem;
  26. import me.shedaniel.math.Point;
  27. import me.shedaniel.math.Rectangle;
  28. import me.shedaniel.math.impl.PointHelper;
  29. import me.shedaniel.rei.RoughlyEnoughItemsCore;
  30. import me.shedaniel.rei.api.*;
  31. import me.shedaniel.rei.api.widgets.Button;
  32. import me.shedaniel.rei.api.widgets.Tooltip;
  33. import me.shedaniel.rei.api.widgets.Widgets;
  34. import me.shedaniel.rei.gui.config.SearchFieldLocation;
  35. import me.shedaniel.rei.gui.subsets.SubsetsMenu;
  36. import me.shedaniel.rei.gui.widget.*;
  37. import me.shedaniel.rei.impl.ClientHelperImpl;
  38. import me.shedaniel.rei.impl.InternalWidgets;
  39. import me.shedaniel.rei.impl.ScreenHelper;
  40. import me.shedaniel.rei.impl.Weather;
  41. import me.shedaniel.rei.utils.CollectionUtils;
  42. import net.minecraft.block.Blocks;
  43. import net.minecraft.client.MinecraftClient;
  44. import net.minecraft.client.gui.Element;
  45. import net.minecraft.client.gui.screen.Screen;
  46. import net.minecraft.client.gui.screen.ingame.HandledScreen;
  47. import net.minecraft.client.render.Tessellator;
  48. import net.minecraft.client.render.VertexConsumerProvider;
  49. import net.minecraft.client.render.item.ItemRenderer;
  50. import net.minecraft.client.resource.language.I18n;
  51. import net.minecraft.client.sound.PositionedSoundInstance;
  52. import net.minecraft.client.util.NarratorManager;
  53. import net.minecraft.client.util.Window;
  54. import net.minecraft.client.util.math.MatrixStack;
  55. import net.minecraft.client.world.ClientWorld;
  56. import net.minecraft.item.ItemStack;
  57. import net.minecraft.screen.slot.Slot;
  58. import net.minecraft.sound.SoundEvents;
  59. import net.minecraft.text.TranslatableText;
  60. import net.minecraft.util.ActionResult;
  61. import net.minecraft.util.Identifier;
  62. import net.minecraft.util.math.Matrix4f;
  63. import net.minecraft.world.GameMode;
  64. import org.apache.logging.log4j.util.TriConsumer;
  65. import org.jetbrains.annotations.ApiStatus;
  66. import org.jetbrains.annotations.NotNull;
  67. import org.jetbrains.annotations.Nullable;
  68. import java.util.*;
  69. @ApiStatus.Internal
  70. public class ContainerScreenOverlay extends WidgetWithBounds {
  71. private static final Identifier CHEST_GUI_TEXTURE = new Identifier("roughlyenoughitems", "textures/gui/recipecontainer.png");
  72. private static final List<Tooltip> TOOLTIPS = Lists.newArrayList();
  73. private static final EntryListWidget ENTRY_LIST_WIDGET = new EntryListWidget();
  74. private static FavoritesListWidget favoritesListWidget = null;
  75. private final List<Widget> widgets = Lists.newLinkedList();
  76. public boolean shouldReInit = false;
  77. private int tooltipWidth;
  78. private int tooltipHeight;
  79. private List<String> tooltipLines;
  80. public final TriConsumer<Integer, Integer, Float> renderTooltipCallback = (x, y, aFloat) -> {
  81. RenderSystem.disableRescaleNormal();
  82. RenderSystem.disableDepthTest();
  83. setZOffset(999);
  84. this.fillGradient(x - 3, y - 4, x + tooltipWidth + 3, y - 3, -267386864, -267386864);
  85. this.fillGradient(x - 3, y + tooltipHeight + 3, x + tooltipWidth + 3, y + tooltipHeight + 4, -267386864, -267386864);
  86. this.fillGradient(x - 3, y - 3, x + tooltipWidth + 3, y + tooltipHeight + 3, -267386864, -267386864);
  87. this.fillGradient(x - 4, y - 3, x - 3, y + tooltipHeight + 3, -267386864, -267386864);
  88. this.fillGradient(x + tooltipWidth + 3, y - 3, x + tooltipWidth + 4, y + tooltipHeight + 3, -267386864, -267386864);
  89. this.fillGradient(x - 3, y - 3 + 1, x - 3 + 1, y + tooltipHeight + 3 - 1, 1347420415, 1344798847);
  90. this.fillGradient(x + tooltipWidth + 2, y - 3 + 1, x + tooltipWidth + 3, y + tooltipHeight + 3 - 1, 1347420415, 1344798847);
  91. this.fillGradient(x - 3, y - 3, x + tooltipWidth + 3, y - 3 + 1, 1347420415, 1347420415);
  92. this.fillGradient(x - 3, y + tooltipHeight + 2, x + tooltipWidth + 3, y + tooltipHeight + 3, 1344798847, 1344798847);
  93. int currentY = y;
  94. MatrixStack matrixStack_1 = new MatrixStack();
  95. VertexConsumerProvider.Immediate immediate = VertexConsumerProvider.immediate(Tessellator.getInstance().getBuffer());
  96. matrixStack_1.translate(0.0D, 0.0D, getZOffset());
  97. Matrix4f matrix4f_1 = matrixStack_1.peek().getModel();
  98. for (int lineIndex = 0; lineIndex < tooltipLines.size(); lineIndex++) {
  99. font.draw(tooltipLines.get(lineIndex), x, currentY, -1, true, matrix4f_1, immediate, false, 0, 15728880);
  100. currentY += lineIndex == 0 ? 12 : 10;
  101. }
  102. immediate.draw();
  103. setZOffset(0);
  104. RenderSystem.enableDepthTest();
  105. RenderSystem.enableRescaleNormal();
  106. };
  107. private Rectangle bounds;
  108. private Window window;
  109. private Button leftButton, rightButton;
  110. @ApiStatus.Experimental
  111. private Rectangle subsetsButtonBounds;
  112. @ApiStatus.Experimental
  113. @Nullable
  114. private SubsetsMenu subsetsMenu = null;
  115. private Widget wrappedSubsetsMenu = null;
  116. public static EntryListWidget getEntryListWidget() {
  117. return ENTRY_LIST_WIDGET;
  118. }
  119. @Nullable
  120. public static FavoritesListWidget getFavoritesListWidget() {
  121. return favoritesListWidget;
  122. }
  123. @ApiStatus.Experimental
  124. @Nullable
  125. public SubsetsMenu getSubsetsMenu() {
  126. return subsetsMenu;
  127. }
  128. public void init(boolean useless) {
  129. init();
  130. }
  131. public void init() {
  132. this.shouldReInit = false;
  133. //Update Variables
  134. this.children().clear();
  135. this.wrappedSubsetsMenu = null;
  136. this.subsetsMenu = null;
  137. this.window = MinecraftClient.getInstance().getWindow();
  138. @SuppressWarnings({"RawTypeCanBeGeneric", "rawtypes"})
  139. DisplayHelper.DisplayBoundsHandler boundsHandler = DisplayHelper.getInstance().getResponsibleBoundsHandler(MinecraftClient.getInstance().currentScreen.getClass());
  140. this.bounds = ConfigObject.getInstance().isLeftHandSidePanel() ? boundsHandler.getLeftBounds(MinecraftClient.getInstance().currentScreen) : boundsHandler.getRightBounds(MinecraftClient.getInstance().currentScreen);
  141. widgets.add(ENTRY_LIST_WIDGET);
  142. if (ConfigObject.getInstance().doDisplayFavoritesOnTheLeft() && ConfigObject.getInstance().isFavoritesEnabled()) {
  143. if (favoritesListWidget == null)
  144. favoritesListWidget = new FavoritesListWidget();
  145. widgets.add(favoritesListWidget);
  146. }
  147. ENTRY_LIST_WIDGET.updateArea(boundsHandler, ScreenHelper.getSearchField() == null ? "" : null);
  148. if (ScreenHelper.getSearchField() == null) {
  149. ScreenHelper.setSearchField(new OverlaySearchField(0, 0, 0, 0));
  150. }
  151. ScreenHelper.getSearchField().getBounds().setBounds(getSearchFieldArea());
  152. this.widgets.add(ScreenHelper.getSearchField());
  153. ScreenHelper.getSearchField().setChangedListener(s -> ENTRY_LIST_WIDGET.updateSearch(s, false));
  154. if (!ConfigObject.getInstance().isEntryListWidgetScrolled()) {
  155. widgets.add(leftButton = Widgets.createButton(new Rectangle(bounds.x, bounds.y + (ConfigObject.getInstance().getSearchFieldLocation() == SearchFieldLocation.TOP_SIDE ? 24 : 0) + 5, 16, 16), new TranslatableText("text.rei.left_arrow"))
  156. .onClick(button -> {
  157. ENTRY_LIST_WIDGET.previousPage();
  158. if (ENTRY_LIST_WIDGET.getPage() < 0)
  159. ENTRY_LIST_WIDGET.setPage(ENTRY_LIST_WIDGET.getTotalPages() - 1);
  160. ENTRY_LIST_WIDGET.updateEntriesPosition();
  161. })
  162. .containsMousePredicate((button, point) -> button.getBounds().contains(point) && isNotInExclusionZones(point.x, point.y))
  163. .tooltipLine(I18n.translate("text.rei.previous_page"))
  164. .focusable(false));
  165. widgets.add(rightButton = Widgets.createButton(new Rectangle(bounds.x + bounds.width - 18, bounds.y + (ConfigObject.getInstance().getSearchFieldLocation() == SearchFieldLocation.TOP_SIDE ? 24 : 0) + 5, 16, 16), new TranslatableText("text.rei.right_arrow"))
  166. .onClick(button -> {
  167. ENTRY_LIST_WIDGET.nextPage();
  168. if (ENTRY_LIST_WIDGET.getPage() >= ENTRY_LIST_WIDGET.getTotalPages())
  169. ENTRY_LIST_WIDGET.setPage(0);
  170. ENTRY_LIST_WIDGET.updateEntriesPosition();
  171. })
  172. .containsMousePredicate((button, point) -> button.getBounds().contains(point) && isNotInExclusionZones(point.x, point.y))
  173. .tooltipLine(I18n.translate("text.rei.next_page"))
  174. .focusable(false));
  175. }
  176. final Rectangle configButtonArea = getConfigButtonArea();
  177. Widget tmp;
  178. widgets.add(tmp = InternalWidgets.wrapLateRenderable(InternalWidgets.mergeWidgets(
  179. Widgets.createButton(configButtonArea, NarratorManager.EMPTY)
  180. .onClick(button -> {
  181. if (Screen.hasShiftDown()) {
  182. ClientHelper.getInstance().setCheating(!ClientHelper.getInstance().isCheating());
  183. return;
  184. }
  185. ConfigManager.getInstance().openConfigScreen(ScreenHelper.getLastHandledScreen());
  186. })
  187. .onRender(button -> {
  188. if (ClientHelper.getInstance().isCheating() && RoughlyEnoughItemsCore.hasOperatorPermission()) {
  189. button.setTint(RoughlyEnoughItemsCore.hasPermissionToUsePackets() ? 721354752 : 1476440063);
  190. } else {
  191. button.removeTint();
  192. }
  193. })
  194. .focusable(false)
  195. .containsMousePredicate((button, point) -> button.getBounds().contains(point) && isNotInExclusionZones(point.x, point.y))
  196. .tooltipSupplier(button -> {
  197. String tooltips = I18n.translate("text.rei.config_tooltip");
  198. tooltips += "\n ";
  199. if (!ClientHelper.getInstance().isCheating())
  200. tooltips += "\n" + I18n.translate("text.rei.cheating_disabled");
  201. else if (!RoughlyEnoughItemsCore.hasOperatorPermission())
  202. tooltips += "\n" + I18n.translate("text.rei.cheating_enabled_no_perms");
  203. else if (RoughlyEnoughItemsCore.hasPermissionToUsePackets())
  204. tooltips += "\n" + I18n.translate("text.rei.cheating_enabled");
  205. else
  206. tooltips += "\n" + I18n.translate("text.rei.cheating_limited_enabled");
  207. return tooltips;
  208. }),
  209. Widgets.createDrawableWidget((helper, mouseX, mouseY, delta) -> {
  210. helper.setZOffset(helper.getZOffset() + 1);
  211. MinecraftClient.getInstance().getTextureManager().bindTexture(CHEST_GUI_TEXTURE);
  212. helper.drawTexture(configButtonArea.x + 3, configButtonArea.y + 3, 0, 0, 14, 14);
  213. })
  214. )
  215. ));
  216. tmp.setZ(600);
  217. if (ConfigObject.getInstance().doesShowUtilsButtons()) {
  218. widgets.add(Widgets.createButton(ConfigObject.getInstance().isLowerConfigButton() ? new Rectangle(ConfigObject.getInstance().isLeftHandSidePanel() ? window.getScaledWidth() - 30 : 10, 10, 20, 20) : new Rectangle(ConfigObject.getInstance().isLeftHandSidePanel() ? window.getScaledWidth() - 55 : 35, 10, 20, 20), NarratorManager.EMPTY)
  219. .onClick(button -> MinecraftClient.getInstance().player.sendChatMessage(ConfigObject.getInstance().getGamemodeCommand().replaceAll("\\{gamemode}", getNextGameMode(Screen.hasShiftDown()).getName())))
  220. .onRender(button -> button.setText(getGameModeShortText(getCurrentGameMode())))
  221. .focusable(false)
  222. .tooltipLine(I18n.translate("text.rei.gamemode_button.tooltip", getGameModeText(getNextGameMode(Screen.hasShiftDown()))))
  223. .containsMousePredicate((button, point) -> button.getBounds().contains(point) && isNotInExclusionZones(point.x, point.y)));
  224. int xxx = ConfigObject.getInstance().isLeftHandSidePanel() ? window.getScaledWidth() - 30 : 10;
  225. for (Weather weather : Weather.values()) {
  226. Button weatherButton;
  227. widgets.add(weatherButton = Widgets.createButton(new Rectangle(xxx, 35, 20, 20), NarratorManager.EMPTY)
  228. .onClick(button -> MinecraftClient.getInstance().player.sendChatMessage(ConfigObject.getInstance().getWeatherCommand().replaceAll("\\{weather}", weather.name().toLowerCase(Locale.ROOT))))
  229. .tooltipLine(I18n.translate("text.rei.weather_button.tooltip", I18n.translate(weather.getTranslateKey())))
  230. .focusable(false)
  231. .containsMousePredicate((button, point) -> button.getBounds().contains(point) && isNotInExclusionZones(point.x, point.y)));
  232. widgets.add(Widgets.createDrawableWidget((helper, mouseX, mouseY, delta) -> {
  233. MinecraftClient.getInstance().getTextureManager().bindTexture(CHEST_GUI_TEXTURE);
  234. RenderSystem.color4f(1.0F, 1.0F, 1.0F, 1.0F);
  235. helper.drawTexture(weatherButton.getBounds().x + 3, weatherButton.getBounds().y + 3, weather.getId() * 14, 14, 14, 14);
  236. }));
  237. xxx += ConfigObject.getInstance().isLeftHandSidePanel() ? -25 : 25;
  238. }
  239. }
  240. subsetsButtonBounds = getSubsetsButtonBounds();
  241. if (ConfigObject.getInstance().isSubsetsEnabled()) {
  242. widgets.add(InternalWidgets.wrapLateRenderable(Widgets.createButton(subsetsButtonBounds, ((ClientHelperImpl) ClientHelper.getInstance()).isAprilFools.get() ? I18n.translate("text.rei.tiny_potato") : I18n.translate("text.rei.subsets"))
  243. .onClick(button -> {
  244. if (subsetsMenu == null) {
  245. wrappedSubsetsMenu = InternalWidgets.wrapTranslate(InternalWidgets.wrapLateRenderable(this.subsetsMenu = SubsetsMenu.createFromRegistry(new Point(this.subsetsButtonBounds.x, this.subsetsButtonBounds.getMaxY()))), 0, 0, 400);
  246. this.widgets.add(this.wrappedSubsetsMenu);
  247. } else {
  248. this.widgets.remove(this.wrappedSubsetsMenu);
  249. this.subsetsMenu = null;
  250. this.wrappedSubsetsMenu = null;
  251. }
  252. })));
  253. }
  254. if (!ConfigObject.getInstance().isEntryListWidgetScrolled()) {
  255. widgets.add(Widgets.createClickableLabel(new Point(bounds.x + (bounds.width / 2), bounds.y + (ConfigObject.getInstance().getSearchFieldLocation() == SearchFieldLocation.TOP_SIDE ? 24 : 0) + 10), "", label -> {
  256. ENTRY_LIST_WIDGET.setPage(0);
  257. ENTRY_LIST_WIDGET.updateEntriesPosition();
  258. }).tooltipLine(I18n.translate("text.rei.go_back_first_page")).focusable(false).onRender(label -> {
  259. label.setClickable(ENTRY_LIST_WIDGET.getTotalPages() > 1);
  260. label.setText(String.format("%s/%s", ENTRY_LIST_WIDGET.getPage() + 1, Math.max(ENTRY_LIST_WIDGET.getTotalPages(), 1)));
  261. }));
  262. }
  263. if (ConfigObject.getInstance().isCraftableFilterEnabled()) {
  264. Rectangle area = getCraftableToggleArea();
  265. ItemRenderer itemRenderer = MinecraftClient.getInstance().getItemRenderer();
  266. ItemStack icon = new ItemStack(Blocks.CRAFTING_TABLE);
  267. this.widgets.add(tmp = InternalWidgets.wrapLateRenderable(InternalWidgets.mergeWidgets(
  268. Widgets.createButton(area, NarratorManager.EMPTY)
  269. .focusable(false)
  270. .onClick(button -> {
  271. ConfigManager.getInstance().toggleCraftableOnly();
  272. ENTRY_LIST_WIDGET.updateSearch(ScreenHelper.getSearchField().getText(), true);
  273. })
  274. .onRender(button -> button.setTint(ConfigManager.getInstance().isCraftableOnlyEnabled() ? 939579655 : 956235776))
  275. .containsMousePredicate((button, point) -> button.getBounds().contains(point) && isNotInExclusionZones(point.x, point.y))
  276. .tooltipSupplier(button -> I18n.translate(ConfigManager.getInstance().isCraftableOnlyEnabled() ? "text.rei.showing_craftable" : "text.rei.showing_all")),
  277. Widgets.createDrawableWidget((helper, mouseX, mouseY, delta) -> {
  278. itemRenderer.zOffset = helper.getZOffset();
  279. itemRenderer.renderGuiItemIcon(icon, area.x + 2, area.y + 2);
  280. itemRenderer.zOffset = 0.0F;
  281. }))
  282. ));
  283. tmp.setZ(600);
  284. }
  285. }
  286. @ApiStatus.Experimental
  287. private Rectangle getSubsetsButtonBounds() {
  288. if (ConfigObject.getInstance().isSubsetsEnabled()) {
  289. if (MinecraftClient.getInstance().currentScreen instanceof RecipeViewingScreen) {
  290. RecipeViewingScreen widget = (RecipeViewingScreen) MinecraftClient.getInstance().currentScreen;
  291. return new Rectangle(widget.getBounds().x, 3, widget.getBounds().width, 18);
  292. }
  293. if (MinecraftClient.getInstance().currentScreen instanceof VillagerRecipeViewingScreen) {
  294. VillagerRecipeViewingScreen widget = (VillagerRecipeViewingScreen) MinecraftClient.getInstance().currentScreen;
  295. return new Rectangle(widget.bounds.x, 3, widget.bounds.width, 18);
  296. }
  297. return new Rectangle(ScreenHelper.getLastHandledScreen().x, 3, ScreenHelper.getLastHandledScreen().backgroundWidth, 18);
  298. }
  299. return null;
  300. }
  301. private Weather getNextWeather() {
  302. try {
  303. Weather current = getCurrentWeather();
  304. int next = current.getId() + 1;
  305. if (next >= 3)
  306. next = 0;
  307. return Weather.byId(next);
  308. } catch (Exception e) {
  309. return Weather.CLEAR;
  310. }
  311. }
  312. private Weather getCurrentWeather() {
  313. ClientWorld world = MinecraftClient.getInstance().world;
  314. if (world.isThundering())
  315. return Weather.THUNDER;
  316. if (world.getLevelProperties().isRaining())
  317. return Weather.RAIN;
  318. return Weather.CLEAR;
  319. }
  320. private String getGameModeShortText(GameMode gameMode) {
  321. return I18n.translate("text.rei.short_gamemode." + gameMode.getName());
  322. }
  323. private String getGameModeText(GameMode gameMode) {
  324. return I18n.translate("selectWorld.gameMode." + gameMode.getName());
  325. }
  326. private GameMode getNextGameMode(boolean reverse) {
  327. try {
  328. GameMode current = getCurrentGameMode();
  329. int next = current.getId() + 1;
  330. if (reverse)
  331. next -= 2;
  332. if (next > 3)
  333. next = 0;
  334. if (next < 0)
  335. next = 3;
  336. return GameMode.byId(next);
  337. } catch (Exception e) {
  338. return GameMode.NOT_SET;
  339. }
  340. }
  341. private GameMode getCurrentGameMode() {
  342. return MinecraftClient.getInstance().getNetworkHandler().getPlayerListEntry(MinecraftClient.getInstance().player.getGameProfile().getId()).getGameMode();
  343. }
  344. private Rectangle getSearchFieldArea() {
  345. int widthRemoved = 1 + (ConfigObject.getInstance().isCraftableFilterEnabled() ? 22 : 0) + (ConfigObject.getInstance().isLowerConfigButton() ? 22 : 0);
  346. SearchFieldLocation searchFieldLocation = ConfigObject.getInstance().getSearchFieldLocation();
  347. if (searchFieldLocation == SearchFieldLocation.BOTTOM_SIDE)
  348. return new Rectangle(bounds.x + 2, window.getScaledHeight() - 22, bounds.width - 6 - widthRemoved, 18);
  349. if (searchFieldLocation == SearchFieldLocation.TOP_SIDE)
  350. return new Rectangle(bounds.x + 2, 4, bounds.width - 6 - widthRemoved, 18);
  351. if (MinecraftClient.getInstance().currentScreen instanceof RecipeViewingScreen) {
  352. RecipeViewingScreen widget = (RecipeViewingScreen) MinecraftClient.getInstance().currentScreen;
  353. return new Rectangle(widget.getBounds().x, window.getScaledHeight() - 22, widget.getBounds().width - widthRemoved, 18);
  354. }
  355. if (MinecraftClient.getInstance().currentScreen instanceof VillagerRecipeViewingScreen) {
  356. VillagerRecipeViewingScreen widget = (VillagerRecipeViewingScreen) MinecraftClient.getInstance().currentScreen;
  357. return new Rectangle(widget.bounds.x, window.getScaledHeight() - 22, widget.bounds.width - widthRemoved, 18);
  358. }
  359. return new Rectangle(ScreenHelper.getLastHandledScreen().x, window.getScaledHeight() - 22, ScreenHelper.getLastHandledScreen().backgroundWidth - widthRemoved, 18);
  360. }
  361. private Rectangle getCraftableToggleArea() {
  362. Rectangle area = getSearchFieldArea();
  363. area.setLocation(area.x + area.width + 4, area.y - 1);
  364. area.setSize(20, 20);
  365. return area;
  366. }
  367. private Rectangle getConfigButtonArea() {
  368. if (ConfigObject.getInstance().isLowerConfigButton()) {
  369. Rectangle area = getSearchFieldArea();
  370. area.setLocation(area.x + area.width + (ConfigObject.getInstance().isCraftableFilterEnabled() ? 26 : 4), area.y - 1);
  371. area.setSize(20, 20);
  372. return area;
  373. }
  374. return new Rectangle(ConfigObject.getInstance().isLeftHandSidePanel() ? window.getScaledWidth() - 30 : 10, 10, 20, 20);
  375. }
  376. private String getCheatModeText() {
  377. return I18n.translate(String.format("%s%s", "text.rei.", ClientHelper.getInstance().isCheating() ? "cheat" : "nocheat"));
  378. }
  379. @NotNull
  380. @Override
  381. public Rectangle getBounds() {
  382. return bounds;
  383. }
  384. @Override
  385. public void render(int mouseX, int mouseY, float delta) {
  386. List<ItemStack> currentStacks = ClientHelper.getInstance().getInventoryItemsTypes();
  387. if (shouldReInit) {
  388. ENTRY_LIST_WIDGET.updateSearch(ScreenHelper.getSearchField().getText(), true);
  389. init();
  390. } else {
  391. for (DisplayHelper.DisplayBoundsHandler<?> handler : DisplayHelper.getInstance().getSortedBoundsHandlers(minecraft.currentScreen.getClass())) {
  392. if (handler != null && handler.shouldRecalculateArea(!ConfigObject.getInstance().isLeftHandSidePanel(), bounds)) {
  393. init();
  394. break;
  395. }
  396. }
  397. }
  398. if (ConfigManager.getInstance().isCraftableOnlyEnabled() && ((currentStacks.size() != ScreenHelper.inventoryStacks.size()) || !hasSameListContent(new LinkedList<>(ScreenHelper.inventoryStacks), currentStacks))) {
  399. ScreenHelper.inventoryStacks = currentStacks;
  400. ENTRY_LIST_WIDGET.updateSearch(ScreenHelper.getSearchField().getText(), true);
  401. }
  402. if (OverlaySearchField.isSearching) {
  403. setZOffset(200);
  404. if (MinecraftClient.getInstance().currentScreen instanceof HandledScreen) {
  405. HandledScreen<?> handledScreen = (HandledScreen<?>) MinecraftClient.getInstance().currentScreen;
  406. int x = handledScreen.x, y = handledScreen.y;
  407. for (Slot slot : handledScreen.getScreenHandler().slots)
  408. if (!slot.hasStack() || !ENTRY_LIST_WIDGET.canLastSearchTermsBeAppliedTo(EntryStack.create(slot.getStack())))
  409. fillGradient(x + slot.x, y + slot.y, x + slot.x + 16, y + slot.y + 16, -601874400, -601874400);
  410. }
  411. setZOffset(0);
  412. }
  413. RenderSystem.color4f(1.0F, 1.0F, 1.0F, 1.0F);
  414. this.renderWidgets(mouseX, mouseY, delta);
  415. if (MinecraftClient.getInstance().currentScreen instanceof HandledScreen && ConfigObject.getInstance().areClickableRecipeArrowsEnabled()) {
  416. HandledScreen<?> handledScreen = (HandledScreen<?>) MinecraftClient.getInstance().currentScreen;
  417. for (RecipeHelper.ScreenClickArea area : RecipeHelper.getInstance().getScreenClickAreas())
  418. if (area.getScreenClass().equals(MinecraftClient.getInstance().currentScreen.getClass()))
  419. if (area.getRectangle().contains(mouseX - handledScreen.x, mouseY - handledScreen.y)) {
  420. String collect = CollectionUtils.mapAndJoinToString(area.getCategories(), identifier -> RecipeHelper.getInstance().getCategory(identifier).getCategoryName(), ", ");
  421. TOOLTIPS.add(Tooltip.create(I18n.translate("text.rei.view_recipes_for", collect)));
  422. break;
  423. }
  424. }
  425. }
  426. public void lateRender(int mouseX, int mouseY, float delta) {
  427. if (ScreenHelper.isOverlayVisible()) {
  428. ScreenHelper.getSearchField().laterRender(mouseX, mouseY, delta);
  429. for (Widget widget : widgets) {
  430. if (widget instanceof LateRenderable && wrappedSubsetsMenu != widget)
  431. widget.render(mouseX, mouseY, delta);
  432. }
  433. }
  434. if (wrappedSubsetsMenu != null) {
  435. TOOLTIPS.clear();
  436. wrappedSubsetsMenu.render(mouseX, mouseY, delta);
  437. }
  438. Screen currentScreen = MinecraftClient.getInstance().currentScreen;
  439. if (!(currentScreen instanceof RecipeViewingScreen) || !((RecipeViewingScreen) currentScreen).choosePageActivated)
  440. for (Tooltip tooltip : TOOLTIPS) {
  441. if (tooltip != null)
  442. renderTooltip(tooltip);
  443. }
  444. TOOLTIPS.clear();
  445. }
  446. public void renderTooltip(Tooltip tooltip) {
  447. renderTooltip(tooltip.getText(), tooltip.getX(), tooltip.getY());
  448. }
  449. public void renderTooltip(List<String> lines, int mouseX, int mouseY) {
  450. if (lines.isEmpty())
  451. return;
  452. tooltipWidth = lines.stream().map(font::getStringWidth).max(Integer::compareTo).get();
  453. tooltipHeight = lines.size() <= 1 ? 8 : lines.size() * 10;
  454. tooltipLines = lines;
  455. ScreenHelper.drawHoveringWidget(mouseX, mouseY, renderTooltipCallback, tooltipWidth, tooltipHeight, 0);
  456. }
  457. private boolean hasSameListContent(List<ItemStack> list1, List<ItemStack> list2) {
  458. list1.sort(Comparator.comparing(Object::toString));
  459. list2.sort(Comparator.comparing(Object::toString));
  460. return CollectionUtils.mapAndJoinToString(list1, Object::toString, "").equals(CollectionUtils.mapAndJoinToString(list2, Object::toString, ""));
  461. }
  462. public void addTooltip(@Nullable Tooltip tooltip) {
  463. if (tooltip != null)
  464. TOOLTIPS.add(tooltip);
  465. }
  466. public void renderWidgets(int int_1, int int_2, float float_1) {
  467. if (!ScreenHelper.isOverlayVisible())
  468. return;
  469. if (!ConfigObject.getInstance().isEntryListWidgetScrolled()) {
  470. leftButton.setEnabled(ENTRY_LIST_WIDGET.getTotalPages() > 1);
  471. rightButton.setEnabled(ENTRY_LIST_WIDGET.getTotalPages() > 1);
  472. }
  473. for (Widget widget : widgets) {
  474. if (!(widget instanceof LateRenderable))
  475. widget.render(int_1, int_2, float_1);
  476. }
  477. }
  478. @Override
  479. public boolean mouseScrolled(double i, double j, double amount) {
  480. if (!ScreenHelper.isOverlayVisible())
  481. return false;
  482. if (wrappedSubsetsMenu != null && wrappedSubsetsMenu.mouseScrolled(i, j, amount))
  483. return true;
  484. if (isInside(PointHelper.ofMouse())) {
  485. if (!ConfigObject.getInstance().isEntryListWidgetScrolled()) {
  486. if (amount > 0 && leftButton.isEnabled())
  487. leftButton.onClick();
  488. else if (amount < 0 && rightButton.isEnabled())
  489. rightButton.onClick();
  490. else
  491. return false;
  492. return true;
  493. } else if (ENTRY_LIST_WIDGET.mouseScrolled(i, j, amount))
  494. return true;
  495. }
  496. if (isNotInExclusionZones(PointHelper.getMouseX(), PointHelper.getMouseY())) {
  497. if (favoritesListWidget != null && favoritesListWidget.mouseScrolled(i, j, amount))
  498. return true;
  499. }
  500. for (Widget widget : widgets)
  501. if (widget != ENTRY_LIST_WIDGET && (favoritesListWidget == null || widget != favoritesListWidget) && (wrappedSubsetsMenu == null || widget != wrappedSubsetsMenu) && widget.mouseScrolled(i, j, amount))
  502. return true;
  503. return false;
  504. }
  505. @Override
  506. public boolean keyPressed(int int_1, int int_2, int int_3) {
  507. if (ScreenHelper.isOverlayVisible()) {
  508. if (ScreenHelper.getSearchField().keyPressed(int_1, int_2, int_3))
  509. return true;
  510. for (Element listener : widgets)
  511. if (listener != ScreenHelper.getSearchField() && listener.keyPressed(int_1, int_2, int_3))
  512. return true;
  513. }
  514. if (ConfigObject.getInstance().getHideKeybind().matchesKey(int_1, int_2)) {
  515. ScreenHelper.toggleOverlayVisible();
  516. return true;
  517. }
  518. ItemStack itemStack = null;
  519. if (MinecraftClient.getInstance().currentScreen instanceof HandledScreen) {
  520. HandledScreen<?> handledScreen = (HandledScreen<?>) MinecraftClient.getInstance().currentScreen;
  521. if (handledScreen.focusedSlot != null && !handledScreen.focusedSlot.getStack().isEmpty())
  522. itemStack = handledScreen.focusedSlot.getStack();
  523. }
  524. if (itemStack != null && !itemStack.isEmpty()) {
  525. if (ConfigObject.getInstance().getRecipeKeybind().matchesKey(int_1, int_2))
  526. return ClientHelper.getInstance().executeRecipeKeyBind(itemStack);
  527. else if (ConfigObject.getInstance().getUsageKeybind().matchesKey(int_1, int_2))
  528. return ClientHelper.getInstance().executeUsageKeyBind(itemStack);
  529. }
  530. if (!ScreenHelper.isOverlayVisible())
  531. return false;
  532. if (ConfigObject.getInstance().getFocusSearchFieldKeybind().matchesKey(int_1, int_2)) {
  533. ScreenHelper.getSearchField().setFocused(true);
  534. setFocused(ScreenHelper.getSearchField());
  535. ScreenHelper.getSearchField().keybindFocusTime = System.currentTimeMillis();
  536. ScreenHelper.getSearchField().keybindFocusKey = int_1;
  537. return true;
  538. }
  539. return false;
  540. }
  541. @Override
  542. public boolean charTyped(char char_1, int int_1) {
  543. if (!ScreenHelper.isOverlayVisible())
  544. return false;
  545. if (ScreenHelper.getSearchField().charTyped(char_1, int_1))
  546. return true;
  547. for (Element listener : widgets)
  548. if (listener != ScreenHelper.getSearchField() && listener.charTyped(char_1, int_1))
  549. return true;
  550. return false;
  551. }
  552. @Override
  553. public List<Widget> children() {
  554. return widgets;
  555. }
  556. @Override
  557. public boolean mouseClicked(double double_1, double double_2, int int_1) {
  558. if (!ScreenHelper.isOverlayVisible())
  559. return false;
  560. if (wrappedSubsetsMenu != null && wrappedSubsetsMenu.mouseClicked(double_1, double_2, int_1)) {
  561. this.setFocused(wrappedSubsetsMenu);
  562. if (int_1 == 0)
  563. this.setDragging(true);
  564. ScreenHelper.getSearchField().setFocused(false);
  565. return true;
  566. }
  567. if (MinecraftClient.getInstance().currentScreen instanceof HandledScreen && ConfigObject.getInstance().areClickableRecipeArrowsEnabled()) {
  568. HandledScreen<?> handledScreen = (HandledScreen<?>) MinecraftClient.getInstance().currentScreen;
  569. for (RecipeHelper.ScreenClickArea area : RecipeHelper.getInstance().getScreenClickAreas())
  570. if (area.getScreenClass().equals(handledScreen.getClass()))
  571. if (area.getRectangle().contains(double_1 - handledScreen.x, double_2 - handledScreen.y)) {
  572. ClientHelper.getInstance().executeViewAllRecipesFromCategories(Arrays.asList(area.getCategories()));
  573. MinecraftClient.getInstance().getSoundManager().play(PositionedSoundInstance.master(SoundEvents.UI_BUTTON_CLICK, 1.0F));
  574. return true;
  575. }
  576. }
  577. for (Element element : widgets)
  578. if (element != wrappedSubsetsMenu && element.mouseClicked(double_1, double_2, int_1)) {
  579. this.setFocused(element);
  580. if (int_1 == 0)
  581. this.setDragging(true);
  582. if (!(element instanceof OverlaySearchField))
  583. ScreenHelper.getSearchField().setFocused(false);
  584. return true;
  585. }
  586. return false;
  587. }
  588. @Override
  589. public boolean mouseDragged(double double_1, double double_2, int int_1, double double_3, double double_4) {
  590. if (!ScreenHelper.isOverlayVisible())
  591. return false;
  592. return (this.getFocused() != null && this.isDragging() && int_1 == 0) && this.getFocused().mouseDragged(double_1, double_2, int_1, double_3, double_4);
  593. }
  594. public boolean isInside(double mouseX, double mouseY) {
  595. return bounds.contains(mouseX, mouseY) && isNotInExclusionZones(mouseX, mouseY);
  596. }
  597. public boolean isNotInExclusionZones(double mouseX, double mouseY) {
  598. for (DisplayHelper.DisplayBoundsHandler<?> handler : DisplayHelper.getInstance().getSortedBoundsHandlers(MinecraftClient.getInstance().currentScreen.getClass())) {
  599. ActionResult in = handler.isInZone(mouseX, mouseY);
  600. if (in != ActionResult.PASS)
  601. return in == ActionResult.SUCCESS;
  602. }
  603. return true;
  604. }
  605. public boolean isInside(Point point) {
  606. return isInside(point.getX(), point.getY());
  607. }
  608. }