Browse Source

Allow a more specific click area handler.

Signed-off-by: shedaniel <daniel@shedaniel.me>
shedaniel 4 năm trước cách đây
mục cha
commit
23d5978888

+ 2 - 0
RoughlyEnoughItems-api/src/main/java/me/shedaniel/rei/api/AutoTransferHandler.java

@@ -48,6 +48,7 @@ public interface AutoTransferHandler {
     @NotNull
     Result handle(@NotNull Context context);
     
+    @ApiStatus.NonExtendable
     interface Result {
         /**
          * Creates a successful result, no further handlers will be called.
@@ -169,6 +170,7 @@ public interface AutoTransferHandler {
         IntList getIntegers();
     }
     
+    @ApiStatus.NonExtendable
     interface Context {
         static Context create(boolean actuallyCrafting, AbstractContainerScreen<?> containerScreen, RecipeDisplay recipeDisplay) {
             return new ContextImpl(actuallyCrafting, containerScreen, () -> recipeDisplay);

+ 68 - 0
RoughlyEnoughItems-api/src/main/java/me/shedaniel/rei/api/ClickAreaHandler.java

@@ -0,0 +1,68 @@
+/*
+ * This file is licensed under the MIT License, part of Roughly Enough Items.
+ * Copyright (c) 2018, 2019, 2020 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.api;
+
+import me.shedaniel.math.Point;
+import me.shedaniel.rei.impl.Internals;
+import net.minecraft.client.gui.screens.Screen;
+import net.minecraft.resources.ResourceLocation;
+import org.jetbrains.annotations.ApiStatus;
+
+import java.util.stream.Stream;
+
+@FunctionalInterface
+public interface ClickAreaHandler<T extends Screen> {
+    Result handle(ClickAreaContext<T> context);
+    
+    @ApiStatus.NonExtendable
+    interface ClickAreaContext<T extends Screen> {
+        T getScreen();
+        
+        Point getMousePosition();
+    }
+    
+    @ApiStatus.NonExtendable
+    interface Result {
+        static Result success() {
+            return Internals.createClickAreaHandlerResult(true);
+        }
+        
+        static Result fail() {
+            return Internals.createClickAreaHandlerResult(false);
+        }
+        
+        Result category(ResourceLocation category);
+        
+        default Result categories(Iterable<ResourceLocation> categories) {
+            for (ResourceLocation category : categories) {
+                category(category);
+            }
+            return this;
+        }
+        
+        boolean isSuccessful();
+        
+        Stream<ResourceLocation> getCategories();
+    }
+}

+ 18 - 1
RoughlyEnoughItems-api/src/main/java/me/shedaniel/rei/api/RecipeHelper.java

@@ -36,6 +36,7 @@ import org.jetbrains.annotations.ApiStatus;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
 
+import java.util.Collections;
 import java.util.List;
 import java.util.Map;
 import java.util.Optional;
@@ -302,17 +303,33 @@ public interface RecipeHelper {
      */
     <T extends Screen> void registerClickArea(ScreenClickAreaProvider<T> rectangleSupplier, Class<T> screenClass, ResourceLocation... categories);
     
+    /**
+     * Registers a click area handler for a screen. A handler allows more specific implementation of click areas.
+     *
+     * @param screenClass The class of the screen.
+     * @param handler     The click area handler that is offset to the window's top left corner.
+     * @param <T>         The screen type to be registered to.
+     * @see #registerClickArea(ScreenClickAreaProvider, Class, ResourceLocation...) for a simpler way to handle areas without custom categories.
+     */
+    <T extends Screen> void registerClickArea(Class<T> screenClass, ClickAreaHandler<T> handler);
+    
     <T extends Recipe<?>> void registerRecipes(ResourceLocation category, Class<T> recipeClass, Function<T, RecipeDisplay> mappingFunction);
     
     <T extends Recipe<?>> void registerRecipes(ResourceLocation category, Function<Recipe, Boolean> recipeFilter, Function<T, RecipeDisplay> mappingFunction);
     
     @ApiStatus.Internal
-    List<RecipeHelper.ScreenClickArea> getScreenClickAreas();
+    @Deprecated
+    @ApiStatus.ScheduledForRemoval(inVersion = "6.0")
+    default List<RecipeHelper.ScreenClickArea> getScreenClickAreas() {
+        return Collections.emptyList();
+    }
     
     @ApiStatus.Internal
     boolean arePluginsLoading();
     
     @ApiStatus.Internal
+    @Deprecated
+    @ApiStatus.ScheduledForRemoval(inVersion = "6.0")
     interface ScreenClickArea {
         Class<? extends Screen> getScreenClass();
         

+ 12 - 0
RoughlyEnoughItems-api/src/main/java/me/shedaniel/rei/api/ScreenClickAreaProvider.java

@@ -25,10 +25,22 @@ package me.shedaniel.rei.api;
 
 import me.shedaniel.math.Rectangle;
 import net.minecraft.client.gui.screens.Screen;
+import net.minecraft.resources.ResourceLocation;
 import org.jetbrains.annotations.NotNull;
 
+import java.util.Arrays;
+import java.util.function.Supplier;
+
 @FunctionalInterface
 public interface ScreenClickAreaProvider<T extends Screen> {
     @NotNull
     Rectangle provide(@NotNull T screen);
+    
+    default ClickAreaHandler<T> toHandler(Supplier<ResourceLocation[]> categories) {
+        return context -> {
+            return provide(context.getScreen()).contains(context.getMousePosition())
+                    ? ClickAreaHandler.Result.success().categories(Arrays.asList(categories.get()))
+                    : ClickAreaHandler.Result.fail();
+        };
+    }
 }

+ 7 - 0
RoughlyEnoughItems-api/src/main/java/me/shedaniel/rei/impl/Internals.java

@@ -45,6 +45,7 @@ import org.jetbrains.annotations.Nullable;
 import java.lang.reflect.Field;
 import java.util.Collection;
 import java.util.function.BiFunction;
+import java.util.function.Function;
 import java.util.function.Supplier;
 
 @ApiStatus.Internal
@@ -60,6 +61,7 @@ public final class Internals {
     private static Supplier<DisplayHelper> displayHelper = Internals::throwNotSetup;
     private static Supplier<WidgetsProvider> widgetsProvider = Internals::throwNotSetup;
     private static Supplier<ClientHelper.ViewSearchBuilder> viewSearchBuilder = Internals::throwNotSetup;
+    private static Function<@NotNull Boolean, ClickAreaHandler.Result> clickAreaHandlerResult = (result) -> throwNotSetup();
     private static BiFunction<@Nullable Point, Collection<Component>, Tooltip> tooltipProvider = (point, texts) -> throwNotSetup();
     private static Supplier<BuiltinPlugin> builtinPlugin = Internals::throwNotSetup;
     
@@ -123,6 +125,11 @@ public final class Internals {
         return viewSearchBuilder.get();
     }
     
+    @NotNull
+    public static ClickAreaHandler.Result createClickAreaHandlerResult(boolean applicable) {
+        return clickAreaHandlerResult.apply(applicable);
+    }
+    
     @NotNull
     public static Tooltip createTooltip(@Nullable Point point, Collection<Component> texts) {
         return tooltipProvider.apply(point, texts);

+ 21 - 0
RoughlyEnoughItems-runtime/src/main/java/me/shedaniel/rei/RoughlyEnoughItemsCore.java

@@ -87,6 +87,8 @@ import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
 import java.util.function.BiFunction;
+import java.util.function.Function;
+import java.util.stream.Stream;
 
 import static me.shedaniel.rei.impl.Internals.attachInstance;
 
@@ -179,6 +181,25 @@ public class RoughlyEnoughItemsCore implements ClientModInitializer {
             }
         }, Internals.WidgetsProvider.class);
         attachInstance((BiFunction<@Nullable Point, Collection<Component>, Tooltip>) QueuedTooltip::create, "tooltipProvider");
+        attachInstance((Function<@Nullable Boolean, ClickAreaHandler.Result>) successful -> new ClickAreaHandler.Result() {
+            private List<ResourceLocation> categories = Lists.newArrayList();
+            
+            @Override
+            public ClickAreaHandler.Result category(ResourceLocation category) {
+                this.categories.add(category);
+                return this;
+            }
+    
+            @Override
+            public boolean isSuccessful() {
+                return successful;
+            }
+    
+            @Override
+            public Stream<ResourceLocation> getCategories() {
+                return categories.stream();
+            }
+        }, "clickAreaHandlerResult");
     }
     
     /**

+ 40 - 14
RoughlyEnoughItems-runtime/src/main/java/me/shedaniel/rei/gui/ContainerScreenOverlay.java

@@ -43,10 +43,7 @@ import me.shedaniel.rei.gui.modules.Menu;
 import me.shedaniel.rei.gui.modules.entries.GameModeMenuEntry;
 import me.shedaniel.rei.gui.modules.entries.WeatherMenuEntry;
 import me.shedaniel.rei.gui.widget.*;
-import me.shedaniel.rei.impl.ClientHelperImpl;
-import me.shedaniel.rei.impl.InternalWidgets;
-import me.shedaniel.rei.impl.ScreenHelper;
-import me.shedaniel.rei.impl.Weather;
+import me.shedaniel.rei.impl.*;
 import me.shedaniel.rei.utils.CollectionUtils;
 import net.minecraft.client.Minecraft;
 import net.minecraft.client.gui.chat.NarratorChatListener;
@@ -75,6 +72,7 @@ import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
 
 import java.util.*;
+import java.util.stream.Collectors;
 
 @ApiStatus.Internal
 public class ContainerScreenOverlay extends WidgetWithBounds implements REIOverlay {
@@ -530,13 +528,27 @@ public class ContainerScreenOverlay extends WidgetWithBounds implements REIOverl
         if (ConfigObject.getInstance().areClickableRecipeArrowsEnabled()) {
             List<ResourceLocation> categories = null;
             Screen screen = Minecraft.getInstance().screen;
-            for (RecipeHelper.ScreenClickArea area : RecipeHelper.getInstance().getScreenClickAreas())
-                if (area.getScreenClass().equals(screen.getClass()))
-                    if (area.getRectangle().contains(mouseX, mouseY)) {
+            ClickAreaHandler.ClickAreaContext context = new ClickAreaHandler.ClickAreaContext<Screen>() {
+                @Override
+                public Screen getScreen() {
+                    return screen;
+                }
+                
+                @Override
+                public Point getMousePosition() {
+                    return new Point(mouseX, mouseY);
+                }
+            };
+            for (Map.Entry<Class<? extends Screen>, ClickAreaHandler<?>> area : ((RecipeHelperImpl) RecipeHelper.getInstance()).getClickAreas().entries()) {
+                if (area.getKey().equals(screen.getClass())) {
+                    ClickAreaHandler.Result result = area.getValue().handle(context);
+                    if (result.isSuccessful()) {
                         if (categories == null) {
-                            categories = new ArrayList<>(Arrays.asList(area.getCategories()));
-                        } else categories.addAll(Arrays.asList(area.getCategories()));
+                            categories = result.getCategories().collect(Collectors.toList());
+                        } else categories.addAll(result.getCategories().collect(Collectors.toList()));
                     }
+                }
+            }
             if (categories != null && !categories.isEmpty()) {
                 String collect = CollectionUtils.mapAndJoinToString(categories, identifier -> RecipeHelper.getInstance().getCategory(identifier).getCategoryName(), ", ");
                 Tooltip.create(new TranslatableComponent("text.rei.view_recipes_for", collect)).queue();
@@ -772,13 +784,27 @@ public class ContainerScreenOverlay extends WidgetWithBounds implements REIOverl
         if (ConfigObject.getInstance().areClickableRecipeArrowsEnabled()) {
             List<ResourceLocation> categories = null;
             Screen screen = Minecraft.getInstance().screen;
-            for (RecipeHelper.ScreenClickArea area : RecipeHelper.getInstance().getScreenClickAreas())
-                if (area.getScreenClass().equals(screen.getClass()))
-                    if (area.getRectangle().contains(mouseX, mouseY)) {
+            ClickAreaHandler.ClickAreaContext context = new ClickAreaHandler.ClickAreaContext<Screen>() {
+                @Override
+                public Screen getScreen() {
+                    return screen;
+                }
+                
+                @Override
+                public Point getMousePosition() {
+                    return new Point(mouseX, mouseY);
+                }
+            };
+            for (Map.Entry<Class<? extends Screen>, ClickAreaHandler<?>> area : ((RecipeHelperImpl) RecipeHelper.getInstance()).getClickAreas().entries()) {
+                if (area.getKey().equals(screen.getClass())) {
+                    ClickAreaHandler.Result result = area.getValue().handle(context);
+                    if (result.isSuccessful()) {
                         if (categories == null) {
-                            categories = new ArrayList<>(Arrays.asList(area.getCategories()));
-                        } else categories.addAll(Arrays.asList(area.getCategories()));
+                            categories = result.getCategories().collect(Collectors.toList());
+                        } else categories.addAll(result.getCategories().collect(Collectors.toList()));
                     }
+                }
+            }
             if (categories != null && !categories.isEmpty()) {
                 ClientHelper.getInstance().openView(ClientHelper.ViewSearchBuilder.builder().addCategories(categories).fillPreferredOpenedCategory());
                 Minecraft.getInstance().getSoundManager().play(SimpleSoundInstance.forUI(SoundEvents.UI_BUTTON_CLICK, 1.0F));

+ 8 - 33
RoughlyEnoughItems-runtime/src/main/java/me/shedaniel/rei/impl/RecipeHelperImpl.java

@@ -36,7 +36,6 @@ import me.shedaniel.rei.utils.CollectionUtils;
 import net.fabricmc.api.EnvType;
 import net.fabricmc.api.Environment;
 import net.minecraft.Util;
-import net.minecraft.client.Minecraft;
 import net.minecraft.client.gui.screens.Screen;
 import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen;
 import net.minecraft.resources.ResourceLocation;
@@ -53,7 +52,6 @@ import java.util.*;
 import java.util.function.Consumer;
 import java.util.function.Function;
 import java.util.function.Predicate;
-import java.util.function.Supplier;
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
 
@@ -69,7 +67,7 @@ public class RecipeHelperImpl implements RecipeHelper {
     private final List<FocusedStackProvider> focusedStackProviders = Lists.newArrayList();
     private final List<AutoTransferHandler> autoTransferHandlers = Lists.newArrayList();
     private final List<RecipeFunction> recipeFunctions = Lists.newArrayList();
-    private final List<ScreenClickArea> screenClickAreas = Lists.newArrayList();
+    private final Multimap<Class<? extends Screen>, ClickAreaHandler<?>> screenClickAreas = HashMultimap.create();
     private final int[] recipeCount = {0};
     private final Map<ResourceLocation, List<RecipeDisplay>> recipeDisplays = Maps.newHashMap();
     private final BiMap<RecipeCategory<?>, ResourceLocation> categories = HashBiMap.create();
@@ -606,7 +604,12 @@ public class RecipeHelperImpl implements RecipeHelper {
     
     @Override
     public <T extends Screen> void registerClickArea(ScreenClickAreaProvider<T> rectangleSupplier, Class<T> screenClass, ResourceLocation... categories) {
-        this.screenClickAreas.add(new ScreenClickAreaImpl(screenClass, () -> rectangleSupplier.provide((T) Minecraft.getInstance().screen), categories));
+        registerClickArea(screenClass, rectangleSupplier.toHandler(() -> categories));
+    }
+    
+    @Override
+    public <T extends Screen> void registerClickArea(Class<T> screenClass, ClickAreaHandler<T> handler) {
+        this.screenClickAreas.put(screenClass, handler);
     }
     
     @Override
@@ -631,38 +634,10 @@ public class RecipeHelperImpl implements RecipeHelper {
         liveRecipeGenerators.add((LiveRecipeGenerator<RecipeDisplay>) liveRecipeGenerator);
     }
     
-    @Override
-    public List<ScreenClickArea> getScreenClickAreas() {
+    public Multimap<Class<? extends Screen>, ClickAreaHandler<?>> getClickAreas() {
         return screenClickAreas;
     }
     
-    private static class ScreenClickAreaImpl implements ScreenClickArea {
-        private Class<? extends Screen> screenClass;
-        private Supplier<Rectangle> rectangleSupplier;
-        private ResourceLocation[] categories;
-        
-        private ScreenClickAreaImpl(Class<? extends Screen> screenClass, Supplier<Rectangle> rectangleSupplier, ResourceLocation[] categories) {
-            this.screenClass = screenClass;
-            this.rectangleSupplier = rectangleSupplier;
-            this.categories = categories;
-        }
-        
-        @Override
-        public Class<? extends Screen> getScreenClass() {
-            return screenClass;
-        }
-        
-        @Override
-        public Rectangle getRectangle() {
-            return rectangleSupplier.get();
-        }
-        
-        @Override
-        public ResourceLocation[] getCategories() {
-            return categories;
-        }
-    }
-    
     @SuppressWarnings("rawtypes")
     private static class RecipeFunction {
         private ResourceLocation category;

+ 2 - 4
RoughlyEnoughItems-runtime/src/main/java/me/shedaniel/rei/impl/ScreenHelper.java

@@ -24,7 +24,6 @@
 package me.shedaniel.rei.impl;
 
 import com.google.common.collect.Iterables;
-import com.google.common.collect.Lists;
 import com.google.common.collect.Sets;
 import com.mojang.blaze3d.platform.Window;
 import com.mojang.blaze3d.vertex.PoseStack;
@@ -44,8 +43,7 @@ import me.shedaniel.rei.gui.widget.TextFieldWidget;
 import net.fabricmc.api.ClientModInitializer;
 import net.fabricmc.api.EnvType;
 import net.fabricmc.api.Environment;
-import net.fabricmc.fabric.api.event.client.ClientTickCallback;
-import net.fabricmc.fabric.api.screenhandler.v1.ScreenHandlerRegistry;
+import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents;
 import net.fabricmc.loader.api.FabricLoader;
 import net.minecraft.client.Minecraft;
 import net.minecraft.client.gui.screens.Screen;
@@ -288,7 +286,7 @@ public class ScreenHelper implements ClientModInitializer, REIHelper {
             return;
         }
         Executor.run(() -> () -> {
-            ClientTickCallback.EVENT.register(minecraftClient -> {
+            ClientTickEvents.END_CLIENT_TICK.register(minecraft -> {
                 if (isOverlayVisible() && getSearchField() != null)
                     getSearchField().tick();
             });

+ 1 - 1
gradle.properties

@@ -1,5 +1,5 @@
 org.gradle.jvmargs=-Xmx3G
-mod_version=5.5.1
+mod_version=5.6.0
 supported_version=1.16.2/3
 minecraft_version=1.16.3
 fabricloader_version=0.9.1+build.205