EntryWidget.java 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450
  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.widget;
  24. import com.mojang.blaze3d.platform.InputConstants;
  25. import com.mojang.blaze3d.systems.RenderSystem;
  26. import com.mojang.blaze3d.vertex.PoseStack;
  27. import me.shedaniel.clothconfig2.api.ModifierKeyCode;
  28. import me.shedaniel.math.Point;
  29. import me.shedaniel.math.Rectangle;
  30. import me.shedaniel.math.impl.PointHelper;
  31. import me.shedaniel.rei.api.*;
  32. import me.shedaniel.rei.api.favorites.FavoriteEntry;
  33. import me.shedaniel.rei.api.widgets.Slot;
  34. import me.shedaniel.rei.api.widgets.Tooltip;
  35. import me.shedaniel.rei.gui.ContainerScreenOverlay;
  36. import me.shedaniel.rei.impl.ScreenHelper;
  37. import net.minecraft.client.gui.components.events.GuiEventListener;
  38. import net.minecraft.client.resources.language.I18n;
  39. import net.minecraft.network.chat.TextComponent;
  40. import net.minecraft.resources.ResourceLocation;
  41. import net.minecraft.util.Mth;
  42. import org.jetbrains.annotations.ApiStatus;
  43. import org.jetbrains.annotations.NotNull;
  44. import org.jetbrains.annotations.Nullable;
  45. import java.util.ArrayList;
  46. import java.util.Collection;
  47. import java.util.Collections;
  48. import java.util.List;
  49. import java.util.stream.Collectors;
  50. import java.util.stream.Stream;
  51. public class EntryWidget extends Slot {
  52. @ApiStatus.Internal
  53. public static long stackDisplayOffset = 0;
  54. protected static final ResourceLocation RECIPE_GUI = new ResourceLocation("roughlyenoughitems", "textures/gui/recipecontainer.png");
  55. protected static final ResourceLocation RECIPE_GUI_DARK = new ResourceLocation("roughlyenoughitems", "textures/gui/recipecontainer_dark.png");
  56. @ApiStatus.Internal
  57. private byte noticeMark = 0;
  58. protected boolean highlight = true;
  59. protected boolean tooltips = true;
  60. protected boolean background = true;
  61. protected boolean interactable = true;
  62. protected boolean interactableFavorites = true;
  63. protected boolean wasClicked = false;
  64. private Rectangle bounds;
  65. private List<EntryStack> entryStacks;
  66. protected EntryWidget(int x, int y) {
  67. this(new Point(x, y));
  68. }
  69. protected EntryWidget(Point point) {
  70. this.bounds = new Rectangle(point.x - 1, point.y - 1, 18, 18);
  71. this.entryStacks = new ArrayList<>();
  72. }
  73. /**
  74. * @see me.shedaniel.rei.api.widgets.Widgets#createSlot(me.shedaniel.math.Point)
  75. */
  76. @ApiStatus.ScheduledForRemoval
  77. @Deprecated
  78. @NotNull
  79. public static EntryWidget create(int x, int y) {
  80. return create(new Point(x, y));
  81. }
  82. /**
  83. * @see me.shedaniel.rei.api.widgets.Widgets#createSlot(me.shedaniel.math.Point)
  84. */
  85. @ApiStatus.ScheduledForRemoval
  86. @Deprecated
  87. @NotNull
  88. public static EntryWidget create(Point point) {
  89. return new EntryWidget(point);
  90. }
  91. @Override
  92. @NotNull
  93. public EntryWidget unmarkInputOrOutput() {
  94. noticeMark = 0;
  95. return this;
  96. }
  97. public EntryWidget markIsInput() {
  98. noticeMark = 1;
  99. return this;
  100. }
  101. public EntryWidget markIsOutput() {
  102. noticeMark = 2;
  103. return this;
  104. }
  105. @Override
  106. public byte getNoticeMark() {
  107. return noticeMark;
  108. }
  109. @Override
  110. public void setNoticeMark(byte noticeMark) {
  111. this.noticeMark = noticeMark;
  112. }
  113. @Override
  114. public void setInteractable(boolean interactable) {
  115. interactable(interactable);
  116. }
  117. @Override
  118. public boolean isInteractable() {
  119. return this.interactable;
  120. }
  121. @Override
  122. public void setInteractableFavorites(boolean interactableFavorites) {
  123. interactableFavorites(interactableFavorites);
  124. }
  125. @Override
  126. public boolean isInteractableFavorites() {
  127. return interactableFavorites;
  128. }
  129. public EntryWidget disableInteractions() {
  130. return interactable(false);
  131. }
  132. @NotNull
  133. @Override
  134. public EntryWidget interactable(boolean b) {
  135. interactable = b;
  136. interactableFavorites = interactableFavorites && interactable;
  137. return this;
  138. }
  139. public EntryWidget disableFavoritesInteractions() {
  140. return interactableFavorites(false);
  141. }
  142. @NotNull
  143. @Override
  144. public EntryWidget interactableFavorites(boolean b) {
  145. interactableFavorites = b && interactable;
  146. return this;
  147. }
  148. public EntryWidget noHighlight() {
  149. return highlight(false);
  150. }
  151. public EntryWidget highlight(boolean b) {
  152. highlight = b;
  153. return this;
  154. }
  155. @Override
  156. public boolean isHighlightEnabled() {
  157. return highlight;
  158. }
  159. @Override
  160. public void setHighlightEnabled(boolean highlights) {
  161. highlight(highlights);
  162. }
  163. public EntryWidget noTooltips() {
  164. return tooltips(false);
  165. }
  166. public EntryWidget tooltips(boolean b) {
  167. tooltips = b;
  168. return this;
  169. }
  170. @Override
  171. public void setTooltipsEnabled(boolean tooltipsEnabled) {
  172. tooltips(tooltipsEnabled);
  173. }
  174. @Override
  175. public boolean isTooltipsEnabled() {
  176. return tooltips;
  177. }
  178. public EntryWidget noBackground() {
  179. return background(false);
  180. }
  181. public EntryWidget background(boolean b) {
  182. background = b;
  183. return this;
  184. }
  185. @Override
  186. public void setBackgroundEnabled(boolean backgroundEnabled) {
  187. background(backgroundEnabled);
  188. }
  189. @Override
  190. public boolean isBackgroundEnabled() {
  191. return background;
  192. }
  193. public EntryWidget clearStacks() {
  194. entryStacks.clear();
  195. return this;
  196. }
  197. @NotNull
  198. @Override
  199. public Slot clearEntries() {
  200. return clearStacks();
  201. }
  202. @NotNull
  203. @Override
  204. public EntryWidget entry(EntryStack stack) {
  205. entryStacks.add(stack);
  206. return this;
  207. }
  208. @NotNull
  209. @Override
  210. public EntryWidget entries(Collection<EntryStack> stacks) {
  211. entryStacks.addAll(stacks);
  212. return this;
  213. }
  214. protected EntryStack getCurrentEntry() {
  215. if (entryStacks.isEmpty())
  216. return EntryStack.empty();
  217. if (entryStacks.size() == 1)
  218. return entryStacks.get(0);
  219. return entryStacks.get(Mth.floor(((System.currentTimeMillis() + stackDisplayOffset) / 1000 % (double) entryStacks.size())));
  220. }
  221. @NotNull
  222. @Override
  223. public List<EntryStack> getEntries() {
  224. return entryStacks;
  225. }
  226. public List<EntryStack> entries() {
  227. return entryStacks;
  228. }
  229. @NotNull
  230. @Override
  231. public Rectangle getBounds() {
  232. return bounds;
  233. }
  234. protected Rectangle getInnerBounds() {
  235. return new Rectangle(bounds.x + 1, bounds.y + 1, bounds.width - 2, bounds.height - 2);
  236. }
  237. @Override
  238. public void render(PoseStack matrices, int mouseX, int mouseY, float delta) {
  239. drawBackground(matrices, mouseX, mouseY, delta);
  240. drawCurrentEntry(matrices, mouseX, mouseY, delta);
  241. boolean highlighted = containsMouse(mouseX, mouseY);
  242. if (hasTooltips() && highlighted) {
  243. queueTooltip(matrices, mouseX, mouseY, delta);
  244. }
  245. if (hasHighlight() && highlighted) {
  246. drawHighlighted(matrices, mouseX, mouseY, delta);
  247. }
  248. }
  249. public final boolean hasTooltips() {
  250. return isTooltipsEnabled();
  251. }
  252. public final boolean hasHighlight() {
  253. return isHighlightEnabled();
  254. }
  255. protected void drawBackground(PoseStack matrices, int mouseX, int mouseY, float delta) {
  256. if (background) {
  257. minecraft.getTextureManager().bind(REIHelper.getInstance().isDarkThemeEnabled() ? RECIPE_GUI_DARK : RECIPE_GUI);
  258. blit(matrices, bounds.x, bounds.y, 0, 222, bounds.width, bounds.height);
  259. }
  260. }
  261. protected void drawCurrentEntry(PoseStack matrices, int mouseX, int mouseY, float delta) {
  262. EntryStack entry = getCurrentEntry();
  263. entry.setZ(100);
  264. entry.render(matrices, getInnerBounds(), mouseX, mouseY, delta);
  265. }
  266. protected void queueTooltip(PoseStack matrices, int mouseX, int mouseY, float delta) {
  267. Tooltip tooltip = getCurrentTooltip(new Point(mouseX, mouseY));
  268. if (tooltip != null) {
  269. if (interactableFavorites && ConfigObject.getInstance().doDisplayFavoritesTooltip() && !ConfigObject.getInstance().getFavoriteKeyCode().isUnknown()) {
  270. String name = ConfigObject.getInstance().getFavoriteKeyCode().getLocalizedName().getString();
  271. if (reverseFavoritesAction())
  272. tooltip.getText().addAll(Stream.of(I18n.get("text.rei.remove_favorites_tooltip", name).split("\n"))
  273. .map(TextComponent::new).collect(Collectors.toList()));
  274. else
  275. tooltip.getText().addAll(Stream.of(I18n.get("text.rei.favorites_tooltip", name).split("\n"))
  276. .map(TextComponent::new).collect(Collectors.toList()));
  277. }
  278. tooltip.queue();
  279. }
  280. }
  281. @Override
  282. public @Nullable Tooltip getCurrentTooltip(me.shedaniel.math.Point point) {
  283. return getCurrentEntry().getTooltip(point);
  284. }
  285. protected void drawHighlighted(PoseStack matrices, int mouseX, int mouseY, float delta) {
  286. RenderSystem.disableDepthTest();
  287. RenderSystem.colorMask(true, true, true, false);
  288. int color = REIHelper.getInstance().isDarkThemeEnabled() ? -1877929711 : -2130706433;
  289. setZ(300);
  290. Rectangle bounds = getInnerBounds();
  291. fillGradient(matrices, bounds.x, bounds.y, bounds.getMaxX(), bounds.getMaxY(), color, color);
  292. setZ(0);
  293. RenderSystem.colorMask(true, true, true, true);
  294. RenderSystem.enableDepthTest();
  295. }
  296. @Override
  297. public List<? extends GuiEventListener> children() {
  298. return Collections.emptyList();
  299. }
  300. protected boolean wasClicked() {
  301. boolean b = this.wasClicked;
  302. this.wasClicked = false;
  303. return b;
  304. }
  305. @Override
  306. public boolean mouseClicked(double mouseX, double mouseY, int button) {
  307. if (containsMouse(mouseX, mouseY))
  308. this.wasClicked = true;
  309. return super.mouseClicked(mouseX, mouseY, button);
  310. }
  311. @Override
  312. public boolean mouseScrolled(double mouseX, double mouseY, double amount) {
  313. if (ScreenHelper.isWithinRecipeViewingScreen && entryStacks.size() > 1 && containsMouse(mouseX, mouseY)) {
  314. if (amount < 0) {
  315. EntryWidget.stackDisplayOffset += 500;
  316. return true;
  317. } else if (amount > 0) {
  318. EntryWidget.stackDisplayOffset -= 500;
  319. return true;
  320. }
  321. }
  322. return false;
  323. }
  324. @Override
  325. public boolean mouseReleased(double mouseX, double mouseY, int button) {
  326. if (!interactable)
  327. return false;
  328. if (wasClicked() && containsMouse(mouseX, mouseY)) {
  329. if (interactableFavorites && ConfigObject.getInstance().isFavoritesEnabled() && containsMouse(PointHelper.ofMouse()) && !getCurrentEntry().isEmpty()) {
  330. ModifierKeyCode keyCode = ConfigObject.getInstance().getFavoriteKeyCode();
  331. if (keyCode.matchesMouse(button)) {
  332. FavoriteEntry favoriteEntry = asFavoriteEntry();
  333. if (reverseFavoritesAction())
  334. ConfigObject.getInstance().getFavoriteEntries().remove(favoriteEntry);
  335. else if (!ConfigObject.getInstance().getFavoriteEntries().contains(favoriteEntry))
  336. ConfigObject.getInstance().getFavoriteEntries().add(favoriteEntry);
  337. ConfigManager.getInstance().saveConfig();
  338. FavoritesListWidget favoritesListWidget = ContainerScreenOverlay.getFavoritesListWidget();
  339. if (favoritesListWidget != null)
  340. favoritesListWidget.updateSearch(ContainerScreenOverlay.getEntryListWidget(), ScreenHelper.getSearchField().getText());
  341. return true;
  342. }
  343. }
  344. if ((ConfigObject.getInstance().getRecipeKeybind().getType() != InputConstants.Type.MOUSE && button == 0) || ConfigObject.getInstance().getRecipeKeybind().matchesMouse(button))
  345. return ClientHelper.getInstance().openView(ClientHelper.ViewSearchBuilder.builder().addRecipesFor(getCurrentEntry()).setOutputNotice(getCurrentEntry()).fillPreferredOpenedCategory());
  346. else if ((ConfigObject.getInstance().getUsageKeybind().getType() != InputConstants.Type.MOUSE && button == 1) || ConfigObject.getInstance().getUsageKeybind().matchesMouse(button))
  347. return ClientHelper.getInstance().openView(ClientHelper.ViewSearchBuilder.builder().addUsagesFor(getCurrentEntry()).setInputNotice(getCurrentEntry()).fillPreferredOpenedCategory());
  348. }
  349. return false;
  350. }
  351. @ApiStatus.Internal
  352. protected FavoriteEntry asFavoriteEntry() {
  353. return FavoriteEntry.fromEntryStack(getCurrentEntry().copy());
  354. }
  355. @ApiStatus.Internal
  356. protected boolean cancelDeleteItems(EntryStack stack) {
  357. return false;
  358. }
  359. protected boolean reverseFavoritesAction() {
  360. return false;
  361. }
  362. @Override
  363. public boolean keyPressed(int int_1, int int_2, int int_3) {
  364. if (!interactable)
  365. return false;
  366. if (containsMouse(PointHelper.ofMouse())) {
  367. if (interactableFavorites && ConfigObject.getInstance().isFavoritesEnabled() && containsMouse(PointHelper.ofMouse()) && !getCurrentEntry().isEmpty()) {
  368. ModifierKeyCode keyCode = ConfigObject.getInstance().getFavoriteKeyCode();
  369. if (keyCode.matchesKey(int_1, int_2)) {
  370. FavoriteEntry favoriteEntry = asFavoriteEntry();
  371. if (reverseFavoritesAction())
  372. ConfigObject.getInstance().getFavoriteEntries().remove(favoriteEntry);
  373. else if (!ConfigObject.getInstance().getFavoriteEntries().contains(favoriteEntry))
  374. ConfigObject.getInstance().getFavoriteEntries().add(favoriteEntry);
  375. ConfigManager.getInstance().saveConfig();
  376. FavoritesListWidget favoritesListWidget = ContainerScreenOverlay.getFavoritesListWidget();
  377. if (favoritesListWidget != null)
  378. favoritesListWidget.updateSearch(ContainerScreenOverlay.getEntryListWidget(), ScreenHelper.getSearchField().getText());
  379. return true;
  380. }
  381. }
  382. if (ConfigObject.getInstance().getRecipeKeybind().matchesKey(int_1, int_2))
  383. return ClientHelper.getInstance().openView(ClientHelper.ViewSearchBuilder.builder().addRecipesFor(getCurrentEntry()).setOutputNotice(getCurrentEntry()).fillPreferredOpenedCategory());
  384. else if (ConfigObject.getInstance().getUsageKeybind().matchesKey(int_1, int_2))
  385. return ClientHelper.getInstance().openView(ClientHelper.ViewSearchBuilder.builder().addUsagesFor(getCurrentEntry()).setInputNotice(getCurrentEntry()).fillPreferredOpenedCategory());
  386. }
  387. return false;
  388. }
  389. }