DisplayHelperImpl.java 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171
  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.impl;
  24. import com.google.common.collect.Lists;
  25. import com.google.common.collect.Maps;
  26. import me.shedaniel.math.Rectangle;
  27. import me.shedaniel.rei.api.BaseBoundsHandler;
  28. import me.shedaniel.rei.api.DisplayHelper;
  29. import me.shedaniel.rei.api.OverlayDecider;
  30. import me.shedaniel.rei.gui.config.DisplayPanelLocation;
  31. import me.shedaniel.rei.utils.CollectionUtils;
  32. import net.fabricmc.api.EnvType;
  33. import net.fabricmc.api.Environment;
  34. import net.minecraft.client.MinecraftClient;
  35. import net.minecraft.client.util.Window;
  36. import org.jetbrains.annotations.ApiStatus;
  37. import java.util.Collections;
  38. import java.util.Comparator;
  39. import java.util.List;
  40. import java.util.Map;
  41. @ApiStatus.Internal
  42. @Environment(EnvType.CLIENT)
  43. public class DisplayHelperImpl implements DisplayHelper {
  44. private static final Comparator<OverlayDecider> BOUNDS_HANDLER_COMPARATOR = Comparator.comparingDouble(OverlayDecider::getPriority).reversed();
  45. private static final DisplayBoundsHandler<Object> EMPTY = new DisplayBoundsHandler<Object>() {
  46. @Override
  47. public Class<Object> getBaseSupportedClass() {
  48. return null;
  49. }
  50. @Override
  51. public Rectangle getLeftBounds(Object screen) {
  52. return new Rectangle();
  53. }
  54. @Override
  55. public Rectangle getRightBounds(Object screen) {
  56. return new Rectangle();
  57. }
  58. @Override
  59. public float getPriority() {
  60. return -10f;
  61. }
  62. };
  63. private List<OverlayDecider> screenDisplayBoundsHandlers = Lists.newArrayList();
  64. private Map<Class<?>, DisplayBoundsHandler<?>> handlerCache = Maps.newHashMap();
  65. private Map<Class<?>, List<OverlayDecider>> deciderSortedCache = Maps.newHashMap();
  66. private Map<Class<?>, List<DisplayBoundsHandler<?>>> handlerSortedCache = Maps.newHashMap();
  67. private BaseBoundsHandler baseBoundsHandler;
  68. private Class<?> tempScreen;
  69. @SuppressWarnings("rawtypes")
  70. @Override
  71. public List<DisplayBoundsHandler<?>> getSortedBoundsHandlers(Class<?> screenClass) {
  72. List<DisplayBoundsHandler<?>> possibleCached = handlerSortedCache.get(screenClass);
  73. if (possibleCached != null)
  74. return possibleCached;
  75. tempScreen = screenClass;
  76. handlerSortedCache.put(screenClass, (List) CollectionUtils.castAndMap(CollectionUtils.filter(screenDisplayBoundsHandlers, this::filterResponsible), DisplayBoundsHandler.class));
  77. return handlerSortedCache.get(screenClass);
  78. }
  79. @Override
  80. public List<OverlayDecider> getSortedOverlayDeciders(Class<?> screenClass) {
  81. List<OverlayDecider> possibleCached = deciderSortedCache.get(screenClass);
  82. if (possibleCached != null)
  83. return possibleCached;
  84. tempScreen = screenClass;
  85. deciderSortedCache.put(screenClass, (List) CollectionUtils.filter(screenDisplayBoundsHandlers, this::filterResponsible));
  86. return deciderSortedCache.get(screenClass);
  87. }
  88. @Override
  89. public List<OverlayDecider> getAllOverlayDeciders() {
  90. return Collections.unmodifiableList(screenDisplayBoundsHandlers);
  91. }
  92. @Override
  93. public DisplayBoundsHandler<?> getResponsibleBoundsHandler(Class<?> screenClass) {
  94. DisplayBoundsHandler<?> possibleCached = handlerCache.get(screenClass);
  95. if (possibleCached != null)
  96. return possibleCached;
  97. List<DisplayBoundsHandler<?>> handlers = getSortedBoundsHandlers(screenClass);
  98. handlerCache.put(screenClass, handlers.isEmpty() ? EMPTY : handlers.get(0));
  99. return handlerCache.get(screenClass);
  100. }
  101. @Override
  102. public <T> Rectangle getOverlayBounds(DisplayPanelLocation location, T screen) {
  103. Window window = MinecraftClient.getInstance().getWindow();
  104. int scaledWidth = window.getScaledWidth();
  105. int scaledHeight = window.getScaledHeight();
  106. for (OverlayDecider decider : getSortedOverlayDeciders(screen.getClass())) {
  107. if (decider instanceof DisplayBoundsProvider) {
  108. Rectangle containerBounds = ((DisplayBoundsProvider<T>) decider).getScreenBounds(screen);
  109. if (location == DisplayPanelLocation.LEFT) {
  110. if (containerBounds.x < 10) continue;
  111. return new Rectangle(2, 0, containerBounds.x - 2, scaledHeight);
  112. } else {
  113. if (scaledWidth - containerBounds.getMaxX() < 10) continue;
  114. return new Rectangle(containerBounds.getMaxX() + 2, 0, scaledWidth - containerBounds.getMaxX() - 4, scaledHeight);
  115. }
  116. } else if (decider instanceof DisplayBoundsHandler) {
  117. return location == DisplayPanelLocation.LEFT ? ((DisplayBoundsHandler<T>) decider).getLeftBounds(screen) : ((DisplayBoundsHandler<T>) decider).getRightBounds(screen);
  118. }
  119. }
  120. return new Rectangle();
  121. }
  122. private boolean filterResponsible(OverlayDecider handler) {
  123. return handler.isHandingScreen(tempScreen);
  124. }
  125. @Override
  126. public void registerHandler(OverlayDecider decider) {
  127. screenDisplayBoundsHandlers.add(decider);
  128. screenDisplayBoundsHandlers.sort(BOUNDS_HANDLER_COMPARATOR);
  129. }
  130. @ApiStatus.Internal
  131. @Override
  132. public BaseBoundsHandler getBaseBoundsHandler() {
  133. return baseBoundsHandler;
  134. }
  135. @ApiStatus.Internal
  136. public void setBaseBoundsHandler(BaseBoundsHandler baseBoundsHandler) {
  137. this.baseBoundsHandler = baseBoundsHandler;
  138. }
  139. @ApiStatus.Internal
  140. public void resetData() {
  141. screenDisplayBoundsHandlers.clear();
  142. }
  143. @ApiStatus.Experimental
  144. @Override
  145. public void resetCache() {
  146. handlerCache.clear();
  147. deciderSortedCache.clear();
  148. handlerSortedCache.clear();
  149. }
  150. }