RecipeViewingScreen.java 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483
  1. /*
  2. * Roughly Enough Items by Danielshe.
  3. * Licensed under the MIT License.
  4. */
  5. package me.shedaniel.rei.gui;
  6. import com.google.common.collect.Lists;
  7. import com.mojang.blaze3d.platform.GlStateManager;
  8. import me.shedaniel.cloth.api.ClientUtils;
  9. import me.shedaniel.rei.RoughlyEnoughItemsCore;
  10. import me.shedaniel.rei.api.*;
  11. import me.shedaniel.rei.client.ScreenHelper;
  12. import me.shedaniel.rei.gui.widget.*;
  13. import net.minecraft.client.MinecraftClient;
  14. import net.minecraft.client.gui.Element;
  15. import net.minecraft.client.gui.screen.Screen;
  16. import net.minecraft.client.render.GuiLighting;
  17. import net.minecraft.client.resource.language.I18n;
  18. import net.minecraft.client.sound.PositionedSoundInstance;
  19. import net.minecraft.client.util.Window;
  20. import net.minecraft.item.ItemStack;
  21. import net.minecraft.sound.SoundEvents;
  22. import net.minecraft.text.LiteralText;
  23. import net.minecraft.text.TranslatableText;
  24. import net.minecraft.util.Formatting;
  25. import net.minecraft.util.Identifier;
  26. import net.minecraft.util.math.MathHelper;
  27. import java.awt.*;
  28. import java.util.List;
  29. import java.util.*;
  30. import java.util.function.Supplier;
  31. public class RecipeViewingScreen extends Screen {
  32. public static final Identifier CHEST_GUI_TEXTURE = new Identifier("roughlyenoughitems", "textures/gui/recipecontainer.png");
  33. private static final int TABS_PER_PAGE = 5;
  34. private final List<Widget> preWidgets;
  35. private final List<Widget> widgets;
  36. private final List<TabWidget> tabs;
  37. private final Map<RecipeCategory<?>, List<RecipeDisplay>> categoriesMap;
  38. private final List<RecipeCategory<?>> categories;
  39. public int guiWidth;
  40. public int guiHeight;
  41. public int page, categoryPages;
  42. public int largestWidth, largestHeight;
  43. public boolean choosePageActivated;
  44. public RecipeChoosePageWidget recipeChoosePageWidget;
  45. private Rectangle bounds;
  46. private RecipeCategory<RecipeDisplay> selectedCategory;
  47. private ButtonWidget recipeBack, recipeNext, categoryBack, categoryNext;
  48. public RecipeViewingScreen(Map<RecipeCategory<?>, List<RecipeDisplay>> categoriesMap) {
  49. super(new LiteralText(""));
  50. this.categoryPages = 0;
  51. this.preWidgets = Lists.newArrayList();
  52. this.widgets = Lists.newArrayList();
  53. Window window = MinecraftClient.getInstance().window;
  54. this.bounds = new Rectangle(window.getScaledWidth() / 2 - guiWidth / 2, window.getScaledHeight() / 2 - guiHeight / 2, 176, 186);
  55. this.categoriesMap = categoriesMap;
  56. this.categories = Lists.newArrayList();
  57. RecipeHelper.getInstance().getAllCategories().forEach(category -> {
  58. if (categoriesMap.containsKey(category))
  59. categories.add(category);
  60. });
  61. this.selectedCategory = (RecipeCategory<RecipeDisplay>) categories.get(0);
  62. this.tabs = new ArrayList<>();
  63. this.choosePageActivated = false;
  64. }
  65. @Override
  66. public boolean keyPressed(int int_1, int int_2, int int_3) {
  67. if (int_1 == 256 && choosePageActivated) {
  68. choosePageActivated = false;
  69. init();
  70. return true;
  71. }
  72. if ((int_1 == 256 || this.minecraft.options.keyInventory.matchesKey(int_1, int_2)) && this.shouldCloseOnEsc()) {
  73. MinecraftClient.getInstance().openScreen(ScreenHelper.getLastContainerScreen());
  74. ScreenHelper.getLastOverlay().init();
  75. return true;
  76. }
  77. if (int_1 == 258) {
  78. boolean boolean_1 = !hasShiftDown();
  79. if (!this.changeFocus(boolean_1))
  80. this.changeFocus(boolean_1);
  81. return true;
  82. }
  83. if (choosePageActivated)
  84. return recipeChoosePageWidget.keyPressed(int_1, int_2, int_3);
  85. else if (ClientHelper.getInstance().getNextPageKeyBinding().matchesKey(int_1, int_2)) {
  86. if (recipeNext.enabled)
  87. recipeNext.onPressed();
  88. return recipeNext.enabled;
  89. } else if (ClientHelper.getInstance().getPreviousPageKeyBinding().matchesKey(int_1, int_2)) {
  90. if (recipeBack.enabled)
  91. recipeBack.onPressed();
  92. return recipeBack.enabled;
  93. }
  94. for (Element element : children())
  95. if (element.keyPressed(int_1, int_2, int_3))
  96. return true;
  97. return super.keyPressed(int_1, int_2, int_3);
  98. }
  99. @Override
  100. public boolean isPauseScreen() {
  101. return false;
  102. }
  103. @Override
  104. public void init() {
  105. super.init();
  106. this.children.clear();
  107. this.tabs.clear();
  108. this.preWidgets.clear();
  109. this.widgets.clear();
  110. this.largestWidth = width - 100;
  111. this.largestHeight = height - 40;
  112. this.guiWidth = MathHelper.clamp(getCurrentDisplayed().stream().map(display -> selectedCategory.getDisplayWidth(display)).max(Integer::compareTo).orElse(150) + 30, 0, largestWidth);
  113. this.guiHeight = MathHelper.floor(MathHelper.clamp((selectedCategory.getDisplayHeight() + 7d) * (getRecipesPerPage() + 1d) + 40d, 186d, (double) largestHeight));
  114. this.bounds = new Rectangle(width / 2 - guiWidth / 2, height / 2 - guiHeight / 2, guiWidth, guiHeight);
  115. this.page = MathHelper.clamp(page, 0, getTotalPages(selectedCategory) - 1);
  116. ButtonWidget w, w2;
  117. this.widgets.add(w = new ButtonWidget(bounds.x + 2, bounds.y - 16, 10, 10, new TranslatableText("text.rei.left_arrow")) {
  118. @Override
  119. public void onPressed() {
  120. categoryPages--;
  121. if (categoryPages < 0)
  122. categoryPages = MathHelper.ceil(categories.size() / (float) TABS_PER_PAGE) - 1;
  123. RecipeViewingScreen.this.init();
  124. }
  125. });
  126. this.widgets.add(w2 = new ButtonWidget(bounds.x + bounds.width - 12, bounds.y - 16, 10, 10, new TranslatableText("text.rei.right_arrow")) {
  127. @Override
  128. public void onPressed() {
  129. categoryPages++;
  130. if (categoryPages > MathHelper.ceil(categories.size() / (float) TABS_PER_PAGE) - 1)
  131. categoryPages = 0;
  132. RecipeViewingScreen.this.init();
  133. }
  134. });
  135. w.enabled = w2.enabled = categories.size() > TABS_PER_PAGE;
  136. widgets.add(categoryBack = new ButtonWidget((int) bounds.getX() + 5, (int) bounds.getY() + 5, 12, 12, new TranslatableText("text.rei.left_arrow")) {
  137. @Override
  138. public void onPressed() {
  139. int currentCategoryIndex = categories.indexOf(selectedCategory);
  140. currentCategoryIndex--;
  141. if (currentCategoryIndex < 0)
  142. currentCategoryIndex = categories.size() - 1;
  143. selectedCategory = (RecipeCategory<RecipeDisplay>) categories.get(currentCategoryIndex);
  144. categoryPages = MathHelper.floor(currentCategoryIndex / (double) TABS_PER_PAGE);
  145. page = 0;
  146. RecipeViewingScreen.this.init();
  147. }
  148. @Override
  149. public Optional<String> getTooltips() {
  150. return Optional.ofNullable(I18n.translate("text.rei.previous_category"));
  151. }
  152. });
  153. widgets.add(new ClickableLabelWidget((int) bounds.getCenterX(), (int) bounds.getY() + 7, "") {
  154. @Override
  155. public void render(int mouseX, int mouseY, float delta) {
  156. this.text = selectedCategory.getCategoryName();
  157. super.render(mouseX, mouseY, delta);
  158. }
  159. @Override
  160. public Optional<String> getTooltips() {
  161. return Optional.ofNullable(I18n.translate("text.rei.view_all_categories"));
  162. }
  163. @Override
  164. public void onLabelClicked() {
  165. MinecraftClient.getInstance().getSoundManager().play(PositionedSoundInstance.master(SoundEvents.UI_BUTTON_CLICK, 1.0F));
  166. ClientHelper.getInstance().executeViewAllRecipesKeyBind();
  167. }
  168. });
  169. widgets.add(categoryNext = new ButtonWidget((int) bounds.getMaxX() - 17, (int) bounds.getY() + 5, 12, 12, new TranslatableText("text.rei.right_arrow")) {
  170. @Override
  171. public void onPressed() {
  172. int currentCategoryIndex = categories.indexOf(selectedCategory);
  173. currentCategoryIndex++;
  174. if (currentCategoryIndex >= categories.size())
  175. currentCategoryIndex = 0;
  176. selectedCategory = (RecipeCategory<RecipeDisplay>) categories.get(currentCategoryIndex);
  177. categoryPages = MathHelper.floor(currentCategoryIndex / (double) TABS_PER_PAGE);
  178. page = 0;
  179. RecipeViewingScreen.this.init();
  180. }
  181. @Override
  182. public Optional<String> getTooltips() {
  183. return Optional.ofNullable(I18n.translate("text.rei.next_category"));
  184. }
  185. });
  186. categoryBack.enabled = categories.size() > 1;
  187. categoryNext.enabled = categories.size() > 1;
  188. widgets.add(recipeBack = new ButtonWidget((int) bounds.getX() + 5, (int) bounds.getY() + 21, 12, 12, new TranslatableText("text.rei.left_arrow")) {
  189. @Override
  190. public void onPressed() {
  191. page--;
  192. if (page < 0)
  193. page = getTotalPages(selectedCategory) - 1;
  194. RecipeViewingScreen.this.init();
  195. }
  196. @Override
  197. public Optional<String> getTooltips() {
  198. return Optional.ofNullable(I18n.translate("text.rei.previous_page"));
  199. }
  200. });
  201. widgets.add(new ClickableLabelWidget((int) bounds.getCenterX(), (int) bounds.getY() + 23, "", categoriesMap.get(selectedCategory).size() > getRecipesPerPageByHeight()) {
  202. @Override
  203. public void render(int mouseX, int mouseY, float delta) {
  204. this.text = String.format("%d/%d", page + 1, getTotalPages(selectedCategory));
  205. super.render(mouseX, mouseY, delta);
  206. }
  207. @Override
  208. public Optional<String> getTooltips() {
  209. return Optional.ofNullable(I18n.translate("text.rei.choose_page"));
  210. }
  211. @Override
  212. public void onLabelClicked() {
  213. MinecraftClient.getInstance().getSoundManager().play(PositionedSoundInstance.master(SoundEvents.UI_BUTTON_CLICK, 1.0F));
  214. RecipeViewingScreen.this.choosePageActivated = true;
  215. RecipeViewingScreen.this.init();
  216. }
  217. });
  218. widgets.add(recipeNext = new ButtonWidget((int) bounds.getMaxX() - 17, (int) bounds.getY() + 21, 12, 12, new TranslatableText("text.rei.right_arrow")) {
  219. @Override
  220. public void onPressed() {
  221. page++;
  222. if (page >= getTotalPages(selectedCategory))
  223. page = 0;
  224. RecipeViewingScreen.this.init();
  225. }
  226. @Override
  227. public Optional<String> getTooltips() {
  228. return Optional.ofNullable(I18n.translate("text.rei.next_page"));
  229. }
  230. });
  231. recipeBack.enabled = recipeNext.enabled = categoriesMap.get(selectedCategory).size() > getRecipesPerPageByHeight();
  232. for (int i = 0; i < TABS_PER_PAGE; i++) {
  233. int j = i + categoryPages * TABS_PER_PAGE;
  234. if (categories.size() > j) {
  235. TabWidget tab;
  236. tabs.add(tab = new TabWidget(i, new Rectangle(bounds.x + bounds.width / 2 - Math.min(categories.size() - categoryPages * TABS_PER_PAGE, TABS_PER_PAGE) * 14 + i * 28, bounds.y - 28, 28, 28)) {
  237. @Override
  238. public boolean mouseClicked(double mouseX, double mouseY, int button) {
  239. if (getBounds().contains(mouseX, mouseY)) {
  240. MinecraftClient.getInstance().getSoundManager().play(PositionedSoundInstance.master(SoundEvents.UI_BUTTON_CLICK, 1.0F));
  241. if (getId() + categoryPages * TABS_PER_PAGE == categories.indexOf(selectedCategory))
  242. return false;
  243. selectedCategory = (RecipeCategory<RecipeDisplay>) categories.get(getId() + categoryPages * TABS_PER_PAGE);
  244. page = 0;
  245. RecipeViewingScreen.this.init();
  246. return true;
  247. }
  248. return false;
  249. }
  250. });
  251. tab.setRenderer(categories.get(j), categories.get(j).getIcon(), categories.get(j).getCategoryName(), tab.getId() + categoryPages * TABS_PER_PAGE == categories.indexOf(selectedCategory));
  252. }
  253. }
  254. Optional<ButtonAreaSupplier> supplier = RecipeHelper.getInstance().getSpeedCraftButtonArea(selectedCategory);
  255. int recipeHeight = selectedCategory.getDisplayHeight();
  256. List<RecipeDisplay> currentDisplayed = getCurrentDisplayed();
  257. for (int i = 0; i < currentDisplayed.size(); i++) {
  258. int finalI = i;
  259. final Supplier<RecipeDisplay> displaySupplier = () -> currentDisplayed.get(finalI);
  260. int displayWidth = selectedCategory.getDisplayWidth(displaySupplier.get());
  261. final Rectangle displayBounds = new Rectangle((int) getBounds().getCenterX() - displayWidth / 2, getBounds().y + 40 + recipeHeight * i + 7 * i, displayWidth, recipeHeight);
  262. widgets.addAll(selectedCategory.setupDisplay(displaySupplier, displayBounds));
  263. if (supplier.isPresent() && supplier.get().get(displayBounds) != null)
  264. widgets.add(new AutoCraftingButtonWidget(supplier.get().get(displayBounds), supplier.get().getButtonText(), displaySupplier));
  265. }
  266. if (choosePageActivated)
  267. recipeChoosePageWidget = new RecipeChoosePageWidget(this, page, getTotalPages(selectedCategory));
  268. else
  269. recipeChoosePageWidget = null;
  270. List<List<ItemStack>> workingStations = RoughlyEnoughItemsCore.getRecipeHelper().getWorkingStations(selectedCategory.getIdentifier());
  271. if (!workingStations.isEmpty()) {
  272. int hh = MathHelper.floor((bounds.height - 16) / 18f);
  273. int actualHeight = Math.min(hh, workingStations.size());
  274. int innerWidth = MathHelper.ceil(workingStations.size() / ((float) hh));
  275. int xx = bounds.x - (10 + innerWidth * 18) + 6;
  276. int yy = bounds.y + 16;
  277. preWidgets.add(new CategoryBaseWidget(new Rectangle(xx - 6, yy - 6, 15 + innerWidth * 18, 11 + actualHeight * 18)));
  278. int index = 0;
  279. List<String> list = Collections.singletonList(Formatting.YELLOW.toString() + I18n.translate("text.rei.working_station"));
  280. xx += (innerWidth - 1) * 18;
  281. for (List<ItemStack> workingStation : workingStations) {
  282. preWidgets.add(new SlotWidget(xx, yy, Renderer.fromItemStacks(workingStation), true, true, true) {
  283. @Override
  284. protected List<String> getExtraItemToolTips(ItemStack stack) {
  285. return list;
  286. }
  287. });
  288. index++;
  289. yy += 18;
  290. if (index >= hh) {
  291. index = 0;
  292. yy = bounds.y + 16;
  293. xx -= 18;
  294. }
  295. }
  296. }
  297. children.addAll(tabs);
  298. children.add(ScreenHelper.getLastOverlay(true, false));
  299. children.addAll(widgets);
  300. children.addAll(preWidgets);
  301. }
  302. public List<Widget> getWidgets() {
  303. return widgets;
  304. }
  305. public List<RecipeDisplay> getCurrentDisplayed() {
  306. List<RecipeDisplay> list = Lists.newArrayList();
  307. int recipesPerPage = getRecipesPerPage();
  308. for (int i = 0; i <= recipesPerPage; i++)
  309. if (page * (recipesPerPage + 1) + i < categoriesMap.get(selectedCategory).size())
  310. list.add(categoriesMap.get(selectedCategory).get(page * (recipesPerPage + 1) + i));
  311. return list;
  312. }
  313. public RecipeCategory<?> getSelectedCategory() {
  314. return selectedCategory;
  315. }
  316. public int getPage() {
  317. return page;
  318. }
  319. public int getCategoryPage() {
  320. return categoryPages;
  321. }
  322. private int getRecipesPerPage() {
  323. if (selectedCategory.getFixedRecipesPerPage() > 0)
  324. return selectedCategory.getFixedRecipesPerPage() - 1;
  325. int height = selectedCategory.getDisplayHeight();
  326. return MathHelper.clamp(MathHelper.floor(((double) largestHeight - 40d) / ((double) height + 7d)) - 1, 0, Math.min(RoughlyEnoughItemsCore.getConfigManager().getConfig().getMaxRecipePerPage() - 1, selectedCategory.getMaximumRecipePerPage() - 1));
  327. }
  328. private int getRecipesPerPageByHeight() {
  329. int height = selectedCategory.getDisplayHeight();
  330. return MathHelper.clamp(MathHelper.floor(((double) guiHeight - 40d) / ((double) height + 7d)), 0, Math.min(RoughlyEnoughItemsCore.getConfigManager().getConfig().getMaxRecipePerPage() - 1, selectedCategory.getMaximumRecipePerPage() - 1));
  331. }
  332. @Override
  333. public void render(int mouseX, int mouseY, float delta) {
  334. this.fillGradient(0, 0, this.width, this.height, -1072689136, -804253680);
  335. preWidgets.forEach(widget -> {
  336. GuiLighting.disable();
  337. widget.render(mouseX, mouseY, delta);
  338. });
  339. if (selectedCategory != null)
  340. selectedCategory.drawCategoryBackground(bounds, mouseX, mouseY, delta);
  341. else {
  342. new CategoryBaseWidget(bounds).render();
  343. if (ScreenHelper.isDarkModeEnabled()) {
  344. fill(bounds.x + 17, bounds.y + 5, bounds.x + bounds.width - 17, bounds.y + 17, 0xFF404040);
  345. fill(bounds.x + 17, bounds.y + 21, bounds.x + bounds.width - 17, bounds.y + 33, 0xFF404040);
  346. } else {
  347. fill(bounds.x + 17, bounds.y + 5, bounds.x + bounds.width - 17, bounds.y + 17, 0xFF9E9E9E);
  348. fill(bounds.x + 17, bounds.y + 21, bounds.x + bounds.width - 17, bounds.y + 33, 0xFF9E9E9E);
  349. }
  350. }
  351. tabs.stream().filter(tabWidget -> !tabWidget.isSelected()).forEach(tabWidget -> tabWidget.render(mouseX, mouseY, delta));
  352. GuiLighting.disable();
  353. super.render(mouseX, mouseY, delta);
  354. widgets.forEach(widget -> {
  355. GuiLighting.disable();
  356. widget.render(mouseX, mouseY, delta);
  357. });
  358. GlStateManager.color4f(1.0F, 1.0F, 1.0F, 1.0F);
  359. GuiLighting.disable();
  360. tabs.stream().filter(TabWidget::isSelected).forEach(tabWidget -> tabWidget.render(mouseX, mouseY, delta));
  361. GuiLighting.disable();
  362. ScreenHelper.getLastOverlay().render(mouseX, mouseY, delta);
  363. ScreenHelper.getLastOverlay().lateRender(mouseX, mouseY, delta);
  364. if (choosePageActivated) {
  365. blitOffset = 500;
  366. this.fillGradient(0, 0, this.width, this.height, -1072689136, -804253680);
  367. blitOffset = 0;
  368. recipeChoosePageWidget.render(mouseX, mouseY, delta);
  369. }
  370. }
  371. public int getTotalPages(RecipeCategory category) {
  372. return MathHelper.ceil(categoriesMap.get(category).size() / (double) (getRecipesPerPage() + 1));
  373. }
  374. public Rectangle getBounds() {
  375. return bounds;
  376. }
  377. @Override
  378. public boolean charTyped(char char_1, int int_1) {
  379. if (choosePageActivated) {
  380. return recipeChoosePageWidget.charTyped(char_1, int_1);
  381. }
  382. for (Element listener : children())
  383. if (listener.charTyped(char_1, int_1))
  384. return true;
  385. return super.charTyped(char_1, int_1);
  386. }
  387. @Override
  388. public boolean mouseDragged(double double_1, double double_2, int int_1, double double_3, double double_4) {
  389. if (choosePageActivated) {
  390. return recipeChoosePageWidget.mouseDragged(double_1, double_2, int_1, double_3, double_4);
  391. }
  392. return super.mouseDragged(double_1, double_2, int_1, double_3, double_4);
  393. }
  394. @Override
  395. public boolean mouseReleased(double double_1, double double_2, int int_1) {
  396. if (choosePageActivated) {
  397. return recipeChoosePageWidget.mouseReleased(double_1, double_2, int_1);
  398. }
  399. return super.mouseReleased(double_1, double_2, int_1);
  400. }
  401. @Override
  402. public boolean mouseScrolled(double i, double j, double amount) {
  403. for (Element listener : children())
  404. if (listener.mouseScrolled(i, j, amount))
  405. return true;
  406. if (getBounds().contains(ClientUtils.getMouseLocation())) {
  407. if (amount > 0 && recipeBack.enabled)
  408. recipeBack.onPressed();
  409. else if (amount < 0 && recipeNext.enabled)
  410. recipeNext.onPressed();
  411. }
  412. if ((new Rectangle(bounds.x, bounds.y - 28, bounds.width, 28)).contains(ClientUtils.getMouseLocation())) {
  413. if (amount > 0 && categoryBack.enabled)
  414. categoryBack.onPressed();
  415. else if (amount < 0 && categoryNext.enabled)
  416. categoryNext.onPressed();
  417. }
  418. return super.mouseScrolled(i, j, amount);
  419. }
  420. @Override
  421. public boolean mouseClicked(double double_1, double double_2, int int_1) {
  422. if (choosePageActivated)
  423. if (recipeChoosePageWidget.containsMouse(double_1, double_2)) {
  424. return recipeChoosePageWidget.mouseClicked(double_1, double_2, int_1);
  425. } else {
  426. choosePageActivated = false;
  427. init();
  428. return false;
  429. }
  430. for (Element entry : children())
  431. if (entry.mouseClicked(double_1, double_2, int_1)) {
  432. setFocused(entry);
  433. if (int_1 == 0)
  434. setDragging(true);
  435. return true;
  436. }
  437. return false;
  438. }
  439. @Override
  440. public Element getFocused() {
  441. if (choosePageActivated)
  442. return recipeChoosePageWidget;
  443. return super.getFocused();
  444. }
  445. }