diff --git a/.gitignore b/.gitignore index 7e06a181c..872402574 100644 --- a/.gitignore +++ b/.gitignore @@ -23,3 +23,8 @@ build/* changelog.html /classes /logs +/build/ +\.classpath +\.project +\.settings/ +*.launch diff --git a/src/main/java/mezz/jei/Internal.java b/src/main/java/mezz/jei/Internal.java index 28a1ee95f..9f0681339 100644 --- a/src/main/java/mezz/jei/Internal.java +++ b/src/main/java/mezz/jei/Internal.java @@ -3,6 +3,8 @@ import javax.annotation.Nullable; import com.google.common.base.Preconditions; + +import mezz.jei.bookmarks.BookmarkList; import mezz.jei.color.ColorNamer; import mezz.jei.gui.GuiEventHandler; import mezz.jei.ingredients.IngredientFilter; @@ -33,6 +35,8 @@ public final class Internal { private static GuiEventHandler guiEventHandler; @Nullable private static InputHandler inputHandler; + @Nullable + private static BookmarkList bookmarkList; private Internal() { @@ -117,4 +121,14 @@ public static void setInputHandler(InputHandler inputHandler) { Internal.inputHandler = inputHandler; MinecraftForge.EVENT_BUS.register(inputHandler); } + + public static BookmarkList getBookmarkList() { + Preconditions.checkState(bookmarkList != null, "BookmarkList has not been created yet."); + return bookmarkList; + } + + public static void setBookmarkList(BookmarkList bookmarkList) { + Internal.bookmarkList = bookmarkList; + } + } diff --git a/src/main/java/mezz/jei/bookmarks/BookmarkList.java b/src/main/java/mezz/jei/bookmarks/BookmarkList.java new file mode 100644 index 000000000..8ace2abac --- /dev/null +++ b/src/main/java/mezz/jei/bookmarks/BookmarkList.java @@ -0,0 +1,222 @@ +package mezz.jei.bookmarks; + +import mezz.jei.api.ingredients.IIngredientHelper; +import mezz.jei.api.ingredients.VanillaTypes; +import mezz.jei.api.recipe.IIngredientType; +import mezz.jei.config.Config; +import mezz.jei.gui.ingredients.IIngredientListElement; +import mezz.jei.gui.overlay.IIngredientGridSource; +import mezz.jei.ingredients.IngredientListElementFactory; +import mezz.jei.ingredients.IngredientRegistry; +import mezz.jei.startup.ForgeModIdHelper; +import mezz.jei.util.LegacyUtil; +import mezz.jei.util.Log; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.JsonToNBT; +import net.minecraft.nbt.NBTException; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraftforge.fluids.FluidStack; +import org.apache.commons.io.IOUtils; + +import javax.annotation.Nullable; +import java.io.File; +import java.io.FileReader; +import java.io.FileWriter; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.LinkedList; +import java.util.List; + +public class BookmarkList implements IIngredientGridSource { + + private static final String MARKER_OTHER = "O:"; + private static final String MARKER_STACK = "T:"; + + private final List list = new LinkedList<>(); + private final List ingredientListElements = new LinkedList<>(); + private final IngredientRegistry ingredientRegistry; + private final List listeners = new ArrayList<>(); + + public BookmarkList(IngredientRegistry ingredientRegistry) { + this.ingredientRegistry = ingredientRegistry; + } + + public boolean add(T ingredient) { + Object normalized = normalize(ingredient); + if (!contains(normalized)) { + if (addToLists(normalized, true)) { + notifyListenersOfChange(); + saveBookmarks(); + return true; + } + } + return false; + } + + protected T normalize(T ingredient) { + IIngredientHelper ingredientHelper = ingredientRegistry.getIngredientHelper(ingredient); + T copy = LegacyUtil.getIngredientCopy(ingredient, ingredientHelper); + if (copy instanceof ItemStack) { + ((ItemStack) copy).setCount(1); + } else if (copy instanceof FluidStack) { + ((FluidStack) copy).amount = 1000; + } + return copy; + } + + private boolean contains(Object ingredient) { + // We cannot assume that ingredients have a working equals() implementation. Even ItemStack doesn't have one... + IIngredientHelper ingredientHelper = ingredientRegistry.getIngredientHelper(ingredient); + for (Object existing : list) { + if (ingredient == existing) { + return true; + } + if (existing != null && existing.getClass() == ingredient.getClass()) { + if (ingredientHelper.getUniqueId(existing).equals(ingredientHelper.getUniqueId(ingredient))) { + return true; + } + } + } + return false; + } + + public boolean remove(Object ingredient) { + int index = 0; + for (Object existing : list) { + if (ingredient == existing) { + list.remove(index); + ingredientListElements.remove(index); + notifyListenersOfChange(); + saveBookmarks(); + return true; + } + index++; + } + return false; + } + + public void saveBookmarks() { + List strings = new ArrayList<>(); + for (IIngredientListElement element : ingredientListElements) { + Object object = element.getIngredient(); + if (object instanceof ItemStack) { + strings.add(MARKER_STACK + ((ItemStack) object).writeToNBT(new NBTTagCompound()).toString()); + } else { + strings.add(MARKER_OTHER + getUid(element)); + } + } + File file = Config.getBookmarkFile(); + if (file != null) { + try (FileWriter writer = new FileWriter(file)) { + IOUtils.writeLines(strings, "\n", writer); + } catch (IOException e) { + Log.get().error("Failed to save bookmarks list to file {}", file, e); + } + } + } + + private static String getUid(IIngredientListElement element) { + IIngredientHelper ingredientHelper = element.getIngredientHelper(); + return ingredientHelper.getUniqueId(element.getIngredient()); + } + + public void loadBookmarks() { + File file = Config.getBookmarkFile(); + if (file == null || !file.exists()) { + return; + } + List ingredientJsonStrings; + try (FileReader reader = new FileReader(file)) { + ingredientJsonStrings = IOUtils.readLines(reader); + } catch (IOException e) { + Log.get().error("Failed to load bookmarks from file {}", file, e); + return; + } + + Collection otherIngredientTypes = new ArrayList<>(ingredientRegistry.getRegisteredIngredientTypes()); + otherIngredientTypes.remove(VanillaTypes.ITEM); + + list.clear(); + ingredientListElements.clear(); + for (String ingredientJsonString : ingredientJsonStrings) { + if (ingredientJsonString.startsWith(MARKER_STACK)) { + String itemStackAsJson = ingredientJsonString.substring(MARKER_STACK.length()); + try { + NBTTagCompound itemStackAsNbt = JsonToNBT.getTagFromJson(itemStackAsJson); + ItemStack itemStack = new ItemStack(itemStackAsNbt); + if (!itemStack.isEmpty()) { + ItemStack normalized = normalize(itemStack); + addToLists(normalized, false); + } else { + Log.get().warn("Failed to load bookmarked ItemStack from json string, the item no longer exists:\n{}", itemStackAsJson); + } + } catch (NBTException e) { + Log.get().error("Failed to load bookmarked ItemStack from json string:\n{}", itemStackAsJson, e); + } + } else if (ingredientJsonString.startsWith(MARKER_OTHER)) { + String uid = ingredientJsonString.substring(MARKER_OTHER.length()); + Object ingredient = getUnknownIngredientByUid(otherIngredientTypes, uid); + if (ingredient != null) { + Object normalized = normalize(ingredient); + addToLists(normalized, false); + } + } else { + Log.get().error("Failed to load unknown bookmarked ingredient:\n{}", ingredientJsonString); + } + } + notifyListenersOfChange(); + } + + @Nullable + private Object getUnknownIngredientByUid(Collection ingredientTypes, String uid) { + for (IIngredientType ingredientType : ingredientTypes) { + Object ingredient = ingredientRegistry.getIngredientByUid(ingredientType, uid); + if (ingredient != null) { + return ingredient; + } + } + return null; + } + + private boolean addToLists(T ingredient, boolean addToFront) { + IIngredientType ingredientType = ingredientRegistry.getIngredientType(ingredient); + IIngredientListElement element = IngredientListElementFactory.createUnorderedElement(ingredientRegistry, ingredientType, ingredient, ForgeModIdHelper.getInstance()); + if (element != null) { + if (addToFront) { + list.add(0, ingredient); + ingredientListElements.add(0, element); + } else { + list.add(ingredient); + ingredientListElements.add(element); + } + return true; + } + return false; + } + + @Override + public List getIngredientList() { + return ingredientListElements; + } + + @Override + public int size() { + return ingredientListElements.size(); + } + + public boolean isEmpty() { + return ingredientListElements.isEmpty(); + } + + @Override + public void addListener(IIngredientGridSource.Listener listener) { + listeners.add(listener); + } + + private void notifyListenersOfChange() { + for (IIngredientGridSource.Listener listener : listeners) { + listener.onChange(); + } + } +} diff --git a/src/main/java/mezz/jei/bookmarks/package-info.java b/src/main/java/mezz/jei/bookmarks/package-info.java new file mode 100644 index 000000000..1b2df458d --- /dev/null +++ b/src/main/java/mezz/jei/bookmarks/package-info.java @@ -0,0 +1,9 @@ +@ParametersAreNonnullByDefault +@FieldsAreNonnullByDefault +@MethodsReturnNonnullByDefault +package mezz.jei.bookmarks; + +import mcp.MethodsReturnNonnullByDefault; +import mezz.jei.util.FieldsAreNonnullByDefault; + +import javax.annotation.ParametersAreNonnullByDefault; \ No newline at end of file diff --git a/src/main/java/mezz/jei/collect/package-info.java b/src/main/java/mezz/jei/collect/package-info.java new file mode 100644 index 000000000..c098e5916 --- /dev/null +++ b/src/main/java/mezz/jei/collect/package-info.java @@ -0,0 +1,9 @@ +@ParametersAreNonnullByDefault +@FieldsAreNonnullByDefault +@MethodsReturnNonnullByDefault +package mezz.jei.collect; + +import mcp.MethodsReturnNonnullByDefault; +import mezz.jei.util.FieldsAreNonnullByDefault; + +import javax.annotation.ParametersAreNonnullByDefault; \ No newline at end of file diff --git a/src/main/java/mezz/jei/config/BookmarkOverlayToggleEvent.java b/src/main/java/mezz/jei/config/BookmarkOverlayToggleEvent.java new file mode 100644 index 000000000..b6477486b --- /dev/null +++ b/src/main/java/mezz/jei/config/BookmarkOverlayToggleEvent.java @@ -0,0 +1,15 @@ +package mezz.jei.config; + +import net.minecraftforge.fml.common.eventhandler.Event; + +public class BookmarkOverlayToggleEvent extends Event { + private final boolean bookmarkOverlayEnabled; + + public BookmarkOverlayToggleEvent(boolean bookmarkOverlayEnabled) { + this.bookmarkOverlayEnabled = bookmarkOverlayEnabled; + } + + public boolean isBookmarkOverlayEnabled() { + return bookmarkOverlayEnabled; + } +} diff --git a/src/main/java/mezz/jei/config/Config.java b/src/main/java/mezz/jei/config/Config.java index 94d1ed49c..e9125014a 100644 --- a/src/main/java/mezz/jei/config/Config.java +++ b/src/main/java/mezz/jei/config/Config.java @@ -1,15 +1,5 @@ package mezz.jei.config; -import javax.annotation.Nullable; -import java.awt.Color; -import java.io.File; -import java.util.Collections; -import java.util.EnumSet; -import java.util.HashSet; -import java.util.List; -import java.util.Locale; -import java.util.Set; - import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableMap; import mezz.jei.Internal; @@ -37,6 +27,16 @@ import net.minecraftforge.fml.client.FMLClientHandler; import net.minecraftforge.fml.common.event.FMLPreInitializationEvent; +import javax.annotation.Nullable; +import java.awt.Color; +import java.io.File; +import java.util.Collections; +import java.util.EnumSet; +import java.util.HashSet; +import java.util.List; +import java.util.Locale; +import java.util.Set; + public final class Config { private static final String configKeyPrefix = "config.jei"; @@ -58,6 +58,8 @@ public final class Config { private static LocalizedConfiguration itemBlacklistConfig; @Nullable private static LocalizedConfiguration searchColorsConfig; + @Nullable + private static File bookmarkFile; private static final ConfigValues defaultValues = new ConfigValues(); private static final ConfigValues values = new ConfigValues(); @@ -94,6 +96,27 @@ public static void toggleOverlayEnabled() { MinecraftForge.EVENT_BUS.post(new OverlayToggleEvent(values.overlayEnabled)); } + public static boolean isBookmarkOverlayEnabled() { + return isOverlayEnabled() && values.bookmarkOverlayEnabled; + } + + public static void toggleBookmarkEnabled() { + values.bookmarkOverlayEnabled = !values.bookmarkOverlayEnabled; + + if (worldConfig != null) { + NetworkManager networkManager = FMLClientHandler.instance().getClientToServerNetworkManager(); + final String worldCategory = ServerInfo.getWorldUid(networkManager); + Property property = worldConfig.get(worldCategory, "bookmarkOverlayEnabled", defaultValues.bookmarkOverlayEnabled); + property.set(values.bookmarkOverlayEnabled); + + if (worldConfig.hasChanged()) { + worldConfig.save(); + } + } + + MinecraftForge.EVENT_BUS.post(new BookmarkOverlayToggleEvent(values.bookmarkOverlayEnabled)); + } + public static boolean isCheatItemsEnabled() { return values.cheatItemsEnabled; } @@ -255,6 +278,11 @@ public static Configuration getWorldConfig() { return worldConfig; } + @Nullable + public static File getBookmarkFile() { + return bookmarkFile; + } + public static void preInit(FMLPreInitializationEvent event) { File jeiConfigurationDir = new File(event.getModConfigurationDirectory(), Constants.MOD_ID); @@ -274,6 +302,7 @@ public static void preInit(FMLPreInitializationEvent event) { final File itemBlacklistConfigFile = new File(jeiConfigurationDir, "itemBlacklist.cfg"); final File searchColorsConfigFile = new File(jeiConfigurationDir, "searchColors.cfg"); final File worldConfigFile = new File(jeiConfigurationDir, "worldSettings.cfg"); + bookmarkFile = new File(jeiConfigurationDir, "bookmarks.ini"); worldConfig = new Configuration(worldConfigFile, "0.1.0"); config = new LocalizedConfiguration(configKeyPrefix, configFile, "0.4.0"); itemBlacklistConfig = new LocalizedConfiguration(configKeyPrefix, itemBlacklistConfigFile, "0.1.0"); @@ -466,6 +495,12 @@ public static boolean syncWorldConfig(@Nullable NetworkManager networkManager) { MinecraftForge.EVENT_BUS.post(new EditModeToggleEvent(values.hideModeEnabled)); } + property = worldConfig.get(worldCategory, "bookmarkOverlayEnabled", defaultValues.bookmarkOverlayEnabled); + property.setLanguageKey("config.jei.interface.bookmarkOverlayEnabled"); + property.setComment(Translator.translateToLocal("config.jei.interface.bookmarkOverlayEnabled.comment")); + property.setShowInGui(false); + values.bookmarkOverlayEnabled = property.getBoolean(); + property = worldConfig.get(worldCategory, "filterText", defaultValues.filterText); property.setShowInGui(false); values.filterText = property.getString(); @@ -529,7 +564,7 @@ private static void updateBlacklist() { public static void addIngredientToConfigBlacklist(IngredientFilter ingredientFilter, IIngredientRegistry ingredientRegistry, V ingredient, IngredientBlacklistType blacklistType, IIngredientHelper ingredientHelper) { IIngredientType ingredientType = ingredientRegistry.getIngredientType(ingredient); - IIngredientListElement element = IngredientListElementFactory.createElement(ingredientRegistry, ingredientType, ingredient, ForgeModIdHelper.getInstance()); + IIngredientListElement element = IngredientListElementFactory.createUnorderedElement(ingredientRegistry, ingredientType, ingredient, ForgeModIdHelper.getInstance()); Preconditions.checkNotNull(element, "Failed to create element for blacklist"); // combine item-level blacklist into wildcard-level ones @@ -577,7 +612,7 @@ private static boolean areAllBlacklisted(List> ele public static void removeIngredientFromConfigBlacklist(IngredientFilter ingredientFilter, IIngredientRegistry ingredientRegistry, V ingredient, IngredientBlacklistType blacklistType, IIngredientHelper ingredientHelper) { IIngredientType ingredientType = ingredientRegistry.getIngredientType(ingredient); - IIngredientListElement element = IngredientListElementFactory.createElement(ingredientRegistry, ingredientType, ingredient, ForgeModIdHelper.getInstance()); + IIngredientListElement element = IngredientListElementFactory.createUnorderedElement(ingredientRegistry, ingredientType, ingredient, ForgeModIdHelper.getInstance()); Preconditions.checkNotNull(element, "Failed to create element for blacklist"); boolean updated = false; diff --git a/src/main/java/mezz/jei/config/ConfigValues.java b/src/main/java/mezz/jei/config/ConfigValues.java index 00eb9f16f..95dbad614 100644 --- a/src/main/java/mezz/jei/config/ConfigValues.java +++ b/src/main/java/mezz/jei/config/ConfigValues.java @@ -24,5 +24,6 @@ public class ConfigValues { public boolean overlayEnabled = true; public boolean cheatItemsEnabled = false; public boolean hideModeEnabled = false; + public boolean bookmarkOverlayEnabled = true; public String filterText = ""; } diff --git a/src/main/java/mezz/jei/config/KeyBindings.java b/src/main/java/mezz/jei/config/KeyBindings.java index 14f791a08..094d12962 100644 --- a/src/main/java/mezz/jei/config/KeyBindings.java +++ b/src/main/java/mezz/jei/config/KeyBindings.java @@ -21,6 +21,8 @@ public final class KeyBindings { public static final KeyBinding recipeBack; public static final KeyBinding previousPage; public static final KeyBinding nextPage; + public static final KeyBinding bookmark; + public static final KeyBinding toggleBookmarkOverlay; private static final List allBindings; static { @@ -32,7 +34,9 @@ public final class KeyBindings { showUses = new KeyBinding("key.jei.showUses", KeyConflictContext.GUI, Keyboard.KEY_U, categoryName), recipeBack = new KeyBinding("key.jei.recipeBack", KeyConflictContext.GUI, Keyboard.KEY_BACK, categoryName), previousPage = new KeyBinding("key.jei.previousPage", KeyConflictContext.GUI, Keyboard.KEY_PRIOR, categoryName), - nextPage = new KeyBinding("key.jei.nextPage", KeyConflictContext.GUI, Keyboard.KEY_NEXT, categoryName) + nextPage = new KeyBinding("key.jei.nextPage", KeyConflictContext.GUI, Keyboard.KEY_NEXT, categoryName), + bookmark = new KeyBinding("key.jei.bookmark", KeyConflictContext.GUI, Keyboard.KEY_A, categoryName), + toggleBookmarkOverlay = new KeyBinding("key.jei.toggleBookmarkOverlay", KeyConflictContext.GUI, Keyboard.KEY_NONE, categoryName) ); } diff --git a/src/main/java/mezz/jei/gui/GuiEventHandler.java b/src/main/java/mezz/jei/gui/GuiEventHandler.java index caaa6d106..5350dfbd2 100644 --- a/src/main/java/mezz/jei/gui/GuiEventHandler.java +++ b/src/main/java/mezz/jei/gui/GuiEventHandler.java @@ -3,6 +3,7 @@ import mezz.jei.config.Config; import mezz.jei.config.OverlayToggleEvent; import mezz.jei.gui.overlay.IngredientListOverlay; +import mezz.jei.gui.overlay.bookmarks.LeftAreaDispatcher; import mezz.jei.recipes.RecipeRegistry; import mezz.jei.util.Translator; import net.minecraft.client.Minecraft; @@ -16,10 +17,12 @@ public class GuiEventHandler { private final IngredientListOverlay ingredientListOverlay; + private final LeftAreaDispatcher leftAreaDispatcher; private final RecipeRegistry recipeRegistry; private boolean drawnOnBackground = false; - public GuiEventHandler(IngredientListOverlay ingredientListOverlay, RecipeRegistry recipeRegistry) { + public GuiEventHandler(LeftAreaDispatcher leftAreaDispatcher, IngredientListOverlay ingredientListOverlay, RecipeRegistry recipeRegistry) { + this.leftAreaDispatcher = leftAreaDispatcher; this.ingredientListOverlay = ingredientListOverlay; this.recipeRegistry = recipeRegistry; } @@ -28,27 +31,32 @@ public GuiEventHandler(IngredientListOverlay ingredientListOverlay, RecipeRegist public void onOverlayToggle(OverlayToggleEvent event) { GuiScreen currentScreen = Minecraft.getMinecraft().currentScreen; ingredientListOverlay.updateScreen(currentScreen, true); + leftAreaDispatcher.updateScreen(currentScreen); } @SubscribeEvent public void onGuiInit(GuiScreenEvent.InitGuiEvent.Post event) { GuiScreen gui = event.getGui(); ingredientListOverlay.updateScreen(gui, false); + leftAreaDispatcher.updateScreen(gui); } @SubscribeEvent public void onGuiOpen(GuiOpenEvent event) { GuiScreen gui = event.getGui(); ingredientListOverlay.updateScreen(gui, false); + leftAreaDispatcher.updateScreen(gui); } @SubscribeEvent public void onDrawBackgroundEventPost(GuiScreenEvent.BackgroundDrawnEvent event) { GuiScreen gui = event.getGui(); ingredientListOverlay.updateScreen(gui, false); + leftAreaDispatcher.updateScreen(gui); drawnOnBackground = true; ingredientListOverlay.drawScreen(gui.mc, event.getMouseX(), event.getMouseY(), gui.mc.getRenderPartialTicks()); + leftAreaDispatcher.drawScreen(gui.mc, event.getMouseX(), event.getMouseY(), gui.mc.getRenderPartialTicks()); } /** @@ -58,6 +66,7 @@ public void onDrawBackgroundEventPost(GuiScreenEvent.BackgroundDrawnEvent event) public void onDrawForegroundEvent(GuiContainerEvent.DrawForeground event) { GuiContainer gui = event.getGuiContainer(); ingredientListOverlay.drawOnForeground(gui, event.getMouseX(), event.getMouseY()); + leftAreaDispatcher.drawOnForeground(gui, event.getMouseX(), event.getMouseY()); } @SubscribeEvent @@ -65,9 +74,11 @@ public void onDrawScreenEventPost(GuiScreenEvent.DrawScreenEvent.Post event) { GuiScreen gui = event.getGui(); ingredientListOverlay.updateScreen(gui, false); + leftAreaDispatcher.updateScreen(gui); if (!drawnOnBackground) { ingredientListOverlay.drawScreen(gui.mc, event.getMouseX(), event.getMouseY(), gui.mc.getRenderPartialTicks()); + leftAreaDispatcher.drawScreen(gui.mc, event.getMouseX(), event.getMouseY(), gui.mc.getRenderPartialTicks()); } drawnOnBackground = false; @@ -80,6 +91,7 @@ public void onDrawScreenEventPost(GuiScreenEvent.DrawScreenEvent.Post event) { } ingredientListOverlay.drawTooltips(gui.mc, event.getMouseX(), event.getMouseY()); + leftAreaDispatcher.drawTooltips(gui.mc, event.getMouseX(), event.getMouseY()); } @SubscribeEvent diff --git a/src/main/java/mezz/jei/gui/GuiHelper.java b/src/main/java/mezz/jei/gui/GuiHelper.java index a5bb60d49..b37b1c083 100644 --- a/src/main/java/mezz/jei/gui/GuiHelper.java +++ b/src/main/java/mezz/jei/gui/GuiHelper.java @@ -26,6 +26,10 @@ public class GuiHelper implements IGuiHelper { private final IDrawableStatic arrowPrevious; private final IDrawableStatic arrowNext; private final IDrawableStatic recipeTransfer; + private final IDrawableStatic configButtonIcon; + private final IDrawableStatic configButtonCheatIcon; + private final IDrawableStatic bookmarkButtonDisabledIcon; + private final IDrawableStatic bookmarkButtonEnabledIcon; public GuiHelper(IIngredientRegistry ingredientRegistry) { this.ingredientRegistry = ingredientRegistry; @@ -45,6 +49,10 @@ public GuiHelper(IIngredientRegistry ingredientRegistry) { recipeTransfer = drawableBuilder(Constants.RECIPE_BACKGROUND, 212, 55, 6, 6) .addPadding(1, 0, 1, 0) .build(); + configButtonIcon = createDrawable(Constants.RECIPE_BACKGROUND, 0, 166, 16, 16); + configButtonCheatIcon = createDrawable(Constants.RECIPE_BACKGROUND, 16, 166, 16, 16); + bookmarkButtonDisabledIcon = createDrawable(Constants.RECIPE_BACKGROUND, 32, 166, 16, 16); + bookmarkButtonEnabledIcon = createDrawable(Constants.RECIPE_BACKGROUND, 48, 166, 16, 16); } @Override @@ -108,4 +116,20 @@ public IDrawableStatic getArrowNext() { public IDrawableStatic getRecipeTransfer() { return recipeTransfer; } + + public IDrawableStatic getConfigButtonIcon() { + return configButtonIcon; + } + + public IDrawableStatic getConfigButtonCheatIcon() { + return configButtonCheatIcon; + } + + public IDrawableStatic getBookmarkButtonDisabledIcon() { + return bookmarkButtonDisabledIcon; + } + + public IDrawableStatic getBookmarkButtonEnabledIcon() { + return bookmarkButtonEnabledIcon; + } } diff --git a/src/main/java/mezz/jei/gui/elements/GuiIconToggleButton.java b/src/main/java/mezz/jei/gui/elements/GuiIconToggleButton.java new file mode 100644 index 000000000..a7b17d910 --- /dev/null +++ b/src/main/java/mezz/jei/gui/elements/GuiIconToggleButton.java @@ -0,0 +1,66 @@ +package mezz.jei.gui.elements; + +import mezz.jei.api.gui.IDrawable; +import mezz.jei.config.Constants; +import mezz.jei.gui.TooltipRenderer; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.GuiButton; +import net.minecraftforge.fml.client.config.HoverChecker; + +import java.awt.Rectangle; +import java.util.ArrayList; +import java.util.List; + +public abstract class GuiIconToggleButton { + private final IDrawable offIcon; + private final IDrawable onIcon; + private final GuiButton button; + private final HoverChecker hoverChecker; + + public GuiIconToggleButton(IDrawable offIcon, IDrawable onIcon) { + this.offIcon = offIcon; + this.onIcon = onIcon; + this.button = new GuiButton(2, 0, 0, 0, 0, ""); + this.hoverChecker = new HoverChecker(this.button, 0); + } + + public void updateBounds(Rectangle area) { + this.button.width = area.width; + this.button.height = area.height; + this.button.x = area.x; + this.button.y = area.y; + } + + public void draw(Minecraft minecraft, int mouseX, int mouseY, float partialTicks) { + this.button.drawButton(minecraft, mouseX, mouseY, partialTicks); + IDrawable icon = isIconToggledOn() ? this.onIcon : this.offIcon; + icon.draw(minecraft, this.button.x + 2, this.button.y + 2); + } + + public final boolean isMouseOver(int mouseX, int mouseY) { + return this.hoverChecker.checkHover(mouseX, mouseY); + } + + public final boolean handleMouseClick(int mouseX, int mouseY) { + Minecraft minecraft = Minecraft.getMinecraft(); + if (button.mousePressed(minecraft, mouseX, mouseY) && onMouseClicked(mouseX, mouseY)) { + button.playPressSound(minecraft.getSoundHandler()); + return true; + } + return false; + } + + public final void drawTooltips(Minecraft minecraft, int mouseX, int mouseY) { + if (isMouseOver(mouseX, mouseY)) { + List tooltip = new ArrayList<>(); + getTooltips(tooltip); + TooltipRenderer.drawHoveringText(minecraft, tooltip, mouseX, mouseY, Constants.MAX_TOOLTIP_WIDTH); + } + } + + protected abstract void getTooltips(List tooltip); + + protected abstract boolean isIconToggledOn(); + + protected abstract boolean onMouseClicked(int mouseX, int mouseY); +} diff --git a/src/main/java/mezz/jei/gui/overlay/ConfigButton.java b/src/main/java/mezz/jei/gui/overlay/ConfigButton.java index 9f4dcebb9..5af5d50fe 100644 --- a/src/main/java/mezz/jei/gui/overlay/ConfigButton.java +++ b/src/main/java/mezz/jei/gui/overlay/ConfigButton.java @@ -1,93 +1,67 @@ package mezz.jei.gui.overlay; -import java.awt.Rectangle; -import java.util.ArrayList; -import java.util.List; - import mezz.jei.Internal; import mezz.jei.api.gui.IDrawable; import mezz.jei.config.Config; -import mezz.jei.config.Constants; import mezz.jei.config.JEIModConfigGui; import mezz.jei.config.KeyBindings; import mezz.jei.gui.GuiHelper; -import mezz.jei.gui.TooltipRenderer; +import mezz.jei.gui.elements.GuiIconToggleButton; import mezz.jei.util.Translator; import net.minecraft.client.Minecraft; -import net.minecraft.client.gui.GuiButton; import net.minecraft.client.gui.GuiScreen; import net.minecraft.client.settings.KeyBinding; -import net.minecraft.util.ResourceLocation; import net.minecraft.util.text.TextFormatting; -import net.minecraftforge.fml.client.config.HoverChecker; import org.lwjgl.input.Keyboard; -public class ConfigButton { - private final IngredientListOverlay parent; - private final GuiButton configButton; - private final IDrawable configButtonIcon; - private final IDrawable configButtonCheatIcon; - private final HoverChecker configButtonHoverChecker; +import java.util.List; - public ConfigButton(IngredientListOverlay parent) { - this.parent = parent; - this.configButton = new GuiButton(2, 0, 0, 0, 0, ""); - ResourceLocation configButtonIconLocation = Constants.RECIPE_BACKGROUND; +public class ConfigButton extends GuiIconToggleButton { + public static ConfigButton create(IngredientListOverlay parent) { GuiHelper guiHelper = Internal.getHelpers().getGuiHelper(); - this.configButtonIcon = guiHelper.createDrawable(configButtonIconLocation, 0, 166, 16, 16); - this.configButtonCheatIcon = guiHelper.createDrawable(configButtonIconLocation, 16, 166, 16, 16); - this.configButtonHoverChecker = new HoverChecker(this.configButton, 0); + return new ConfigButton(guiHelper.getConfigButtonIcon(), guiHelper.getConfigButtonCheatIcon(), parent); } - public void updateBounds(Rectangle area) { - this.configButton.width = area.width; - this.configButton.height = area.height; - this.configButton.x = area.x; - this.configButton.y = area.y; - } - - public void draw(Minecraft minecraft, int mouseX, int mouseY, float partialTicks) { - this.configButton.drawButton(minecraft, mouseX, mouseY, partialTicks); - - IDrawable icon = Config.isCheatItemsEnabled() ? this.configButtonCheatIcon : this.configButtonIcon; - icon.draw(minecraft, this.configButton.x + 2, this.configButton.y + 2); - } + private final IngredientListOverlay parent; - public boolean isMouseOver(int mouseX, int mouseY) { - return this.configButtonHoverChecker.checkHover(mouseX, mouseY); + private ConfigButton(IDrawable disabledIcon, IDrawable enabledIcon, IngredientListOverlay parent) { + super(disabledIcon, enabledIcon); + this.parent = parent; } - public void drawTooltips(Minecraft minecraft, int mouseX, int mouseY, boolean hasRoom) { - if (isMouseOver(mouseX, mouseY)) { - List tooltip = new ArrayList<>(); - tooltip.add(Translator.translateToLocal("jei.tooltip.config")); - if (!Config.isOverlayEnabled()) { - tooltip.add(TextFormatting.GOLD + Translator.translateToLocal("jei.tooltip.ingredient.list.disabled")); - tooltip.add(TextFormatting.GOLD + Translator.translateToLocalFormatted("jei.tooltip.ingredient.list.disabled.how.to.fix", KeyBindings.toggleOverlay.getDisplayName())); - } else if (!hasRoom) { - tooltip.add(TextFormatting.GOLD + Translator.translateToLocal("jei.tooltip.not.enough.space")); - } - if (Config.isCheatItemsEnabled()) { - tooltip.add(TextFormatting.RED + Translator.translateToLocal("jei.tooltip.cheat.mode.button.enabled")); - KeyBinding toggleCheatMode = KeyBindings.toggleCheatMode; - if (toggleCheatMode.getKeyCode() != 0) { - tooltip.add(TextFormatting.RED + Translator.translateToLocalFormatted("jei.tooltip.cheat.mode.how.to.disable.hotkey", toggleCheatMode.getDisplayName())); - } else { - String controlKeyLocalization = Translator.translateToLocal(Minecraft.IS_RUNNING_ON_MAC ? "key.jei.ctrl.mac" : "key.jei.ctrl"); - tooltip.add(TextFormatting.RED + Translator.translateToLocalFormatted("jei.tooltip.cheat.mode.how.to.disable.no.hotkey", controlKeyLocalization)); - } + @Override + protected void getTooltips(List tooltip) { + tooltip.add(Translator.translateToLocal("jei.tooltip.config")); + if (!Config.isOverlayEnabled()) { + tooltip.add(TextFormatting.GOLD + Translator.translateToLocal("jei.tooltip.ingredient.list.disabled")); + tooltip.add(TextFormatting.GOLD + Translator.translateToLocalFormatted("jei.tooltip.ingredient.list.disabled.how.to.fix", KeyBindings.toggleOverlay.getDisplayName())); + } else if (!parent.isListDisplayed()) { + tooltip.add(TextFormatting.GOLD + Translator.translateToLocal("jei.tooltip.not.enough.space")); + } + if (Config.isCheatItemsEnabled()) { + tooltip.add(TextFormatting.RED + Translator.translateToLocal("jei.tooltip.cheat.mode.button.enabled")); + KeyBinding toggleCheatMode = KeyBindings.toggleCheatMode; + if (toggleCheatMode.getKeyCode() != 0) { + tooltip.add(TextFormatting.RED + Translator.translateToLocalFormatted("jei.tooltip.cheat.mode.how.to.disable.hotkey", toggleCheatMode.getDisplayName())); + } else { + String controlKeyLocalization = Translator.translateToLocal(Minecraft.IS_RUNNING_ON_MAC ? "key.jei.ctrl.mac" : "key.jei.ctrl"); + tooltip.add(TextFormatting.RED + Translator.translateToLocalFormatted("jei.tooltip.cheat.mode.how.to.disable.no.hotkey", controlKeyLocalization)); } - TooltipRenderer.drawHoveringText(minecraft, tooltip, mouseX, mouseY, Constants.MAX_TOOLTIP_WIDTH); } } - public boolean handleMouseClick(int mouseX, int mouseY) { - Minecraft minecraft = Minecraft.getMinecraft(); - if (Config.isOverlayEnabled() && configButton.mousePressed(minecraft, mouseX, mouseY)) { - configButton.playPressSound(minecraft.getSoundHandler()); + @Override + protected boolean isIconToggledOn() { + return Config.isCheatItemsEnabled(); + } + + @Override + protected boolean onMouseClicked(int mouseX, int mouseY) { + if (Config.isOverlayEnabled()) { if (Keyboard.getEventKeyState() && (Keyboard.getEventKey() == Keyboard.KEY_LCONTROL || Keyboard.getEventKey() == Keyboard.KEY_RCONTROL)) { Config.toggleCheatItemsEnabled(); } else { + Minecraft minecraft = Minecraft.getMinecraft(); if (minecraft.currentScreen != null) { GuiScreen configScreen = new JEIModConfigGui(minecraft.currentScreen); parent.updateScreen(configScreen, false); diff --git a/src/main/java/mezz/jei/gui/overlay/GridAlignment.java b/src/main/java/mezz/jei/gui/overlay/GridAlignment.java new file mode 100644 index 000000000..be88c4257 --- /dev/null +++ b/src/main/java/mezz/jei/gui/overlay/GridAlignment.java @@ -0,0 +1,5 @@ +package mezz.jei.gui.overlay; + +public enum GridAlignment { + LEFT, RIGHT +} diff --git a/src/main/java/mezz/jei/gui/overlay/IIngredientGridSource.java b/src/main/java/mezz/jei/gui/overlay/IIngredientGridSource.java new file mode 100644 index 000000000..77b7ee6b5 --- /dev/null +++ b/src/main/java/mezz/jei/gui/overlay/IIngredientGridSource.java @@ -0,0 +1,15 @@ +package mezz.jei.gui.overlay; + +import mezz.jei.gui.ingredients.IIngredientListElement; + +import java.util.List; + +public interface IIngredientGridSource { + List getIngredientList(); + int size(); + void addListener(Listener listener); + + interface Listener { + void onChange(); + } +} diff --git a/src/main/java/mezz/jei/gui/overlay/IngredientGrid.java b/src/main/java/mezz/jei/gui/overlay/IngredientGrid.java index 7cab5f0d9..ef7d6e04c 100644 --- a/src/main/java/mezz/jei/gui/overlay/IngredientGrid.java +++ b/src/main/java/mezz/jei/gui/overlay/IngredientGrid.java @@ -37,11 +37,13 @@ public class IngredientGrid implements IShowsRecipeFocuses { private static final int INGREDIENT_PADDING = 1; public static final int INGREDIENT_WIDTH = GuiItemStackGroup.getWidth(INGREDIENT_PADDING); public static final int INGREDIENT_HEIGHT = GuiItemStackGroup.getHeight(INGREDIENT_PADDING); + private final GridAlignment alignment; private Rectangle area = new Rectangle(); protected final IngredientListBatchRenderer guiIngredientSlots; - public IngredientGrid() { + public IngredientGrid(GridAlignment alignment) { + this.alignment = alignment; this.guiIngredientSlots = new IngredientListBatchRenderer(); } @@ -56,7 +58,12 @@ public void updateBounds(Rectangle availableArea, int minWidth, Collection guiExclusionAreas = Collections.emptySet(); - public IngredientGridAll(IngredientFilter ingredientFilter) { - this.ingredientGrid = new IngredientGrid(); - this.ingredientFilter = ingredientFilter; + public IngredientGridWithNavigation(IIngredientGridSource ingredientSource, GridAlignment alignment) { + this.ingredientGrid = new IngredientGrid(alignment); + this.ingredientSource = ingredientSource; this.pageDelegate = new IngredientGridPaged(); this.navigation = new PageNavigation(this.pageDelegate, false); } - public void updateLayout(boolean filterChanged) { - if (filterChanged) { + public void updateLayout(boolean resetToFirstPage) { + if (resetToFirstPage) { firstItemIndex = 0; } this.ingredientGrid.updateLayout(this.guiExclusionAreas); - List ingredientList = ingredientFilter.getIngredientList(); + List ingredientList = ingredientSource.getIngredientList(); this.ingredientGrid.guiIngredientSlots.set(firstItemIndex, ingredientList); this.navigation.updatePageState(); } @@ -219,7 +218,7 @@ private static Set getGuiAreas() { private class IngredientGridPaged implements IPaged { @Override public boolean nextPage() { - final int itemsCount = ingredientFilter.size(); + final int itemsCount = ingredientSource.size(); if (itemsCount > 0) { firstItemIndex += ingredientGrid.size(); if (firstItemIndex >= itemsCount) { @@ -242,7 +241,7 @@ public boolean previousPage() { updateLayout(false); return false; } - final int itemsCount = ingredientFilter.size(); + final int itemsCount = ingredientSource.size(); int pageNum = firstItemIndex / itemsPerPage; if (pageNum == 0) { @@ -264,19 +263,19 @@ public boolean previousPage() { public boolean hasNext() { // true if there is more than one page because this wraps around int itemsPerPage = ingredientGrid.size(); - return itemsPerPage > 0 && ingredientFilter.size() > itemsPerPage; + return itemsPerPage > 0 && ingredientSource.size() > itemsPerPage; } @Override public boolean hasPrevious() { // true if there is more than one page because this wraps around int itemsPerPage = ingredientGrid.size(); - return itemsPerPage > 0 && ingredientFilter.size() > itemsPerPage; + return itemsPerPage > 0 && ingredientSource.size() > itemsPerPage; } @Override public int getPageCount() { - final int itemCount = ingredientFilter.size(); + final int itemCount = ingredientSource.size(); final int stacksPerPage = ingredientGrid.size(); if (stacksPerPage == 0) { return 1; diff --git a/src/main/java/mezz/jei/gui/overlay/IngredientListOverlay.java b/src/main/java/mezz/jei/gui/overlay/IngredientListOverlay.java index 03ca67d5e..3725fe56f 100644 --- a/src/main/java/mezz/jei/gui/overlay/IngredientListOverlay.java +++ b/src/main/java/mezz/jei/gui/overlay/IngredientListOverlay.java @@ -6,6 +6,7 @@ import mezz.jei.api.gui.IGuiProperties; import mezz.jei.config.Config; import mezz.jei.config.KeyBindings; +import mezz.jei.gui.elements.GuiIconToggleButton; import mezz.jei.gui.ghost.GhostIngredientDragManager; import mezz.jei.gui.ingredients.IIngredientListElement; import mezz.jei.gui.recipes.RecipesGui; @@ -51,8 +52,8 @@ private static boolean isSearchBarCentered(IGuiProperties guiProperties) { } private final IngredientFilter ingredientFilter; - private final ConfigButton configButton; - private final IngredientGridAll contents; + private final GuiIconToggleButton configButton; + private final IngredientGridWithNavigation contents; private final GuiTextFieldFilter searchField; private final GhostIngredientDragManager ghostIngredientDragManager; private Rectangle displayArea = new Rectangle(); @@ -64,9 +65,10 @@ private static boolean isSearchBarCentered(IGuiProperties guiProperties) { public IngredientListOverlay(IngredientFilter ingredientFilter) { this.ingredientFilter = ingredientFilter; - this.contents = new IngredientGridAll(ingredientFilter); + this.contents = new IngredientGridWithNavigation(ingredientFilter, GridAlignment.LEFT); + ingredientFilter.addListener(() -> onSetFilterText(Config.getFilterText())); this.searchField = new GuiTextFieldFilter(0, ingredientFilter); - this.configButton = new ConfigButton(this); + this.configButton = ConfigButton.create(this); this.ghostIngredientDragManager = new GhostIngredientDragManager(this.contents); this.setKeyboardFocus(false); } @@ -183,11 +185,11 @@ public void drawScreen(Minecraft minecraft, int mouseX, int mouseY, float partia public void drawTooltips(Minecraft minecraft, int mouseX, int mouseY) { if (isListDisplayed()) { - this.configButton.drawTooltips(minecraft, mouseX, mouseY, true); + this.configButton.drawTooltips(minecraft, mouseX, mouseY); this.ghostIngredientDragManager.drawTooltips(minecraft, mouseX, mouseY); this.contents.drawTooltips(minecraft, mouseX, mouseY); } else if (this.guiProperties != null) { - this.configButton.drawTooltips(minecraft, mouseX, mouseY, false); + this.configButton.drawTooltips(minecraft, mouseX, mouseY); } } diff --git a/src/main/java/mezz/jei/gui/overlay/bookmarks/BookmarkButton.java b/src/main/java/mezz/jei/gui/overlay/bookmarks/BookmarkButton.java new file mode 100644 index 000000000..7d9296075 --- /dev/null +++ b/src/main/java/mezz/jei/gui/overlay/bookmarks/BookmarkButton.java @@ -0,0 +1,58 @@ +package mezz.jei.gui.overlay.bookmarks; + +import mezz.jei.api.gui.IDrawable; +import mezz.jei.api.gui.IDrawableStatic; +import mezz.jei.bookmarks.BookmarkList; +import mezz.jei.config.Config; +import mezz.jei.config.KeyBindings; +import mezz.jei.gui.GuiHelper; +import mezz.jei.gui.elements.GuiIconToggleButton; +import mezz.jei.util.Translator; +import net.minecraft.client.settings.KeyBinding; +import net.minecraft.util.text.TextFormatting; + +import java.util.List; + +public class BookmarkButton extends GuiIconToggleButton { + public static BookmarkButton create(BookmarkOverlay bookmarkOverlay, BookmarkList bookmarkList, GuiHelper guiHelper) { + IDrawableStatic offIcon = guiHelper.getBookmarkButtonDisabledIcon(); + IDrawableStatic onIcon = guiHelper.getBookmarkButtonEnabledIcon(); + return new BookmarkButton(offIcon, onIcon, bookmarkOverlay, bookmarkList); + } + + private final BookmarkOverlay bookmarkOverlay; + private final BookmarkList bookmarkList; + + private BookmarkButton(IDrawable offIcon, IDrawable onIcon, BookmarkOverlay bookmarkOverlay, BookmarkList bookmarkList) { + super(offIcon, onIcon); + this.bookmarkOverlay = bookmarkOverlay; + this.bookmarkList = bookmarkList; + } + + @Override + protected void getTooltips(List tooltip) { + tooltip.add(Translator.translateToLocal("jei.tooltip.bookmarks")); + KeyBinding bookmarkKey = KeyBindings.bookmark; + if (bookmarkKey.getKeyCode() == 0) { + tooltip.add(TextFormatting.RED + Translator.translateToLocal("jei.tooltip.bookmarks.usage.nokey")); + } else if (!bookmarkOverlay.hasRoom()) { + tooltip.add(TextFormatting.GOLD + Translator.translateToLocal("jei.tooltip.bookmarks.not.enough.space")); + } else { + tooltip.add(TextFormatting.GRAY + Translator.translateToLocalFormatted("jei.tooltip.bookmarks.usage.key", bookmarkKey.getDisplayName())); + } + } + + @Override + protected boolean isIconToggledOn() { + return Config.isBookmarkOverlayEnabled() && !bookmarkList.isEmpty() && bookmarkOverlay.hasRoom(); + } + + @Override + protected boolean onMouseClicked(int mouseX, int mouseY) { + if (!bookmarkList.isEmpty() && bookmarkOverlay.hasRoom()) { + Config.toggleBookmarkEnabled(); + return true; + } + return false; + } +} diff --git a/src/main/java/mezz/jei/gui/overlay/bookmarks/BookmarkOverlay.java b/src/main/java/mezz/jei/gui/overlay/bookmarks/BookmarkOverlay.java new file mode 100644 index 000000000..da358d9c3 --- /dev/null +++ b/src/main/java/mezz/jei/gui/overlay/bookmarks/BookmarkOverlay.java @@ -0,0 +1,165 @@ +package mezz.jei.gui.overlay.bookmarks; + +import mezz.jei.bookmarks.BookmarkList; +import mezz.jei.config.Config; +import mezz.jei.gui.GuiHelper; +import mezz.jei.gui.elements.GuiIconToggleButton; +import mezz.jei.gui.overlay.GridAlignment; +import mezz.jei.gui.overlay.IngredientGrid; +import mezz.jei.gui.overlay.IngredientGridWithNavigation; +import mezz.jei.gui.recipes.RecipesGui; +import mezz.jei.input.IClickedIngredient; +import mezz.jei.input.IShowsRecipeFocuses; +import mezz.jei.util.CommandUtil; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.GuiScreen; +import net.minecraft.client.gui.inventory.GuiContainer; +import net.minecraft.item.ItemStack; + +import javax.annotation.Nullable; +import java.awt.Rectangle; + +public class BookmarkOverlay implements IShowsRecipeFocuses, ILeftAreaContent { + private static final int BUTTON_SIZE = 20; + + // areas + private Rectangle parentArea; + private Rectangle displayArea = new Rectangle(); + + // display elements + private final IngredientGridWithNavigation contents; + private final GuiIconToggleButton bookmarkButton; + + // visibility + private boolean hasRoom = false; + + // data + private final BookmarkList bookmarkList; + + public BookmarkOverlay(Rectangle area, BookmarkList bookmarkList, GuiHelper guiHelper) { + this.parentArea = area; + this.bookmarkList = bookmarkList; + this.bookmarkButton = BookmarkButton.create(this, bookmarkList, guiHelper); + this.contents = new IngredientGridWithNavigation(bookmarkList, GridAlignment.RIGHT); + bookmarkList.addListener(() -> contents.updateLayout(true)); + } + + private boolean isListDisplayed() { + return Config.isBookmarkOverlayEnabled() && hasRoom && !bookmarkList.isEmpty(); + } + + public boolean hasRoom() { + return hasRoom; + } + + @Override + public void updateBounds(Rectangle area) { + this.parentArea = area; + hasRoom = updateBounds(); + } + + @Override + public void drawScreen(Minecraft minecraft, int mouseX, int mouseY, float partialTicks) { + if (this.hasRoom && Config.isBookmarkOverlayEnabled()) { + this.contents.draw(minecraft, mouseX, mouseY, partialTicks); + } + this.bookmarkButton.draw(minecraft, mouseX, mouseY, partialTicks); + } + + @Override + public void drawOnForeground(GuiContainer gui, int mouseX, int mouseY) { + } + + @Override + public void drawTooltips(Minecraft minecraft, int mouseX, int mouseY) { + if (isListDisplayed()) { + this.contents.drawTooltips(minecraft, mouseX, mouseY); + } + bookmarkButton.drawTooltips(minecraft, mouseX, mouseY); + } + + private static int getMinWidth() { + return Math.max(4 * BUTTON_SIZE, Config.smallestNumColumns * IngredientGrid.INGREDIENT_WIDTH); + } + + public boolean updateBounds() { + displayArea = new Rectangle(parentArea); + + final int minWidth = getMinWidth(); + if (displayArea.width < minWidth) { + return false; + } + + Rectangle availableContentsArea = new Rectangle( + displayArea.x, + displayArea.y, + displayArea.width, + displayArea.height - (BUTTON_SIZE + 4) + ); + this.contents.updateBounds(availableContentsArea, minWidth); + + // update area to match contents size + Rectangle contentsArea = this.contents.getArea(); + displayArea.x = contentsArea.x; + displayArea.width = contentsArea.width; + + this.bookmarkButton.updateBounds(new Rectangle( + displayArea.x, + (int) Math.floor(displayArea.getMaxY()) - BUTTON_SIZE - 2, + BUTTON_SIZE, + BUTTON_SIZE + )); + + this.contents.updateLayout(false); + + return true; + } + + @Override + @Nullable + public IClickedIngredient getIngredientUnderMouse(int mouseX, int mouseY) { + if (isListDisplayed()) { + return this.contents.getIngredientUnderMouse(mouseX, mouseY); + } + return null; + } + + @Override + public boolean canSetFocusWithMouse() { + return this.isListDisplayed() && this.contents.canSetFocusWithMouse(); + } + + @Override + public boolean handleMouseScrolled(int mouseX, int mouseY, int scrollDelta) { + return isListDisplayed() && + displayArea.contains(mouseX, mouseY) && + this.contents.handleMouseScrolled(mouseX, mouseY, scrollDelta); + } + + @Override + public boolean handleMouseClicked(int mouseX, int mouseY, int mouseButton) { + if (displayArea.contains(mouseX, mouseY)) { + Minecraft minecraft = Minecraft.getMinecraft(); + GuiScreen currentScreen = minecraft.currentScreen; + if (currentScreen != null && !(currentScreen instanceof RecipesGui) + && (mouseButton == 0 || mouseButton == 1 || minecraft.gameSettings.keyBindPickBlock.isActiveAndMatches(mouseButton - 100))) { + IClickedIngredient clicked = getIngredientUnderMouse(mouseX, mouseY); + if (clicked != null) { + if (Config.isCheatItemsEnabled()) { + ItemStack itemStack = clicked.getCheatItemStack(); + if (!itemStack.isEmpty()) { + CommandUtil.giveStack(itemStack, mouseButton); + } + clicked.onClickHandled(); + return true; + } + } + } + } + if (bookmarkButton.isMouseOver(mouseX, mouseY)) { + return bookmarkButton.handleMouseClick(mouseX, mouseY); + } + return this.contents.handleMouseClicked(mouseX, mouseY, mouseButton); + } + +} diff --git a/src/main/java/mezz/jei/gui/overlay/bookmarks/ILeftAreaContent.java b/src/main/java/mezz/jei/gui/overlay/bookmarks/ILeftAreaContent.java new file mode 100644 index 000000000..97bd18922 --- /dev/null +++ b/src/main/java/mezz/jei/gui/overlay/bookmarks/ILeftAreaContent.java @@ -0,0 +1,23 @@ +package mezz.jei.gui.overlay.bookmarks; + +import mezz.jei.input.IShowsRecipeFocuses; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.inventory.GuiContainer; + +import java.awt.Rectangle; + +public interface ILeftAreaContent extends IShowsRecipeFocuses { + + void drawScreen(Minecraft minecraft, int mouseX, int mouseY, float partialTicks); + + void drawOnForeground(GuiContainer gui, int mouseX, int mouseY); + + void drawTooltips(Minecraft minecraft, int mouseX, int mouseY); + + void updateBounds(Rectangle area); + + boolean handleMouseScrolled(int mouseX, int mouseY, int dWheel); + + boolean handleMouseClicked(int mouseX, int mouseY, int mouseButton); + +} diff --git a/src/main/java/mezz/jei/gui/overlay/bookmarks/LeftAreaDispatcher.java b/src/main/java/mezz/jei/gui/overlay/bookmarks/LeftAreaDispatcher.java new file mode 100644 index 000000000..9ed3312ba --- /dev/null +++ b/src/main/java/mezz/jei/gui/overlay/bookmarks/LeftAreaDispatcher.java @@ -0,0 +1,196 @@ +package mezz.jei.gui.overlay.bookmarks; + +import mezz.jei.api.gui.IGuiProperties; +import mezz.jei.gui.PageNavigation; +import mezz.jei.gui.recipes.RecipesGui; +import mezz.jei.input.IClickedIngredient; +import mezz.jei.input.IPaged; +import mezz.jei.input.IShowsRecipeFocuses; +import mezz.jei.runtime.JeiRuntime; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.GuiScreen; +import net.minecraft.client.gui.inventory.GuiContainer; + +import javax.annotation.Nullable; +import java.awt.Rectangle; +import java.util.ArrayList; +import java.util.List; + +public class LeftAreaDispatcher implements IShowsRecipeFocuses, IPaged { + + private static final int BORDER_PADDING = 2; + private static final int NAVIGATION_HEIGHT = 20; + + private final List contents = new ArrayList<>(); + private final JeiRuntime runtime; + private int current = 0; + @Nullable + private IGuiProperties guiProperties; + private Rectangle naviArea = new Rectangle(); + private Rectangle displayArea = new Rectangle(); + private final PageNavigation navigation; + private boolean canShow = false; + + public LeftAreaDispatcher(JeiRuntime runtime) { + this.runtime = runtime; + this.navigation = new PageNavigation(this, false); + } + + public void addContent(ILeftAreaContent content) { + this.contents.add(content); + } + + private boolean hasContent() { + return current >= 0 && current < contents.size(); + } + + public void drawScreen(Minecraft minecraft, int mouseX, int mouseY, float partialTicks) { + if (canShow && hasContent()) { + contents.get(current).drawScreen(minecraft, mouseX, mouseY, partialTicks); + if (naviArea.height > 0) { + navigation.draw(minecraft, mouseX, mouseY, partialTicks); + } + } + } + + public void drawOnForeground(GuiContainer gui, int mouseX, int mouseY) { + if (canShow && hasContent()) { + contents.get(current).drawOnForeground(gui, mouseX, mouseY); + } + } + + public void drawTooltips(Minecraft minecraft, int mouseX, int mouseY) { + if (canShow && hasContent()) { + contents.get(current).drawTooltips(minecraft, mouseX, mouseY); + } + } + + public void updateScreen(@Nullable GuiScreen guiScreen) { + canShow = false; + if (hasContent()) { + IGuiProperties currentGuiProperties = runtime.getGuiProperties(guiScreen); + if (currentGuiProperties == null) { + guiProperties = null; + } else if (!areGuiPropertiesEqual(guiProperties, currentGuiProperties)) { + guiProperties = currentGuiProperties; + makeDisplayArea(guiProperties); + contents.get(current).updateBounds(displayArea); + canShow = true; + } else { + canShow = true; + } + } + } + + private static boolean areGuiPropertiesEqual(@Nullable IGuiProperties guiProperties1, IGuiProperties guiProperties2) { + return guiProperties1 != null && guiProperties1.getGuiClass().equals(guiProperties2.getGuiClass()) + && guiProperties1.getGuiLeft() == guiProperties2.getGuiLeft() && guiProperties1.getGuiXSize() == guiProperties2.getGuiXSize() + && guiProperties1.getScreenWidth() == guiProperties2.getScreenWidth() && guiProperties1.getScreenHeight() == guiProperties2.getScreenHeight(); + } + + private void makeDisplayArea(IGuiProperties guiProperties) { + final int x = BORDER_PADDING; + final int y = BORDER_PADDING; + int width = guiProperties.getGuiLeft() - x - BORDER_PADDING; + final int height = guiProperties.getScreenHeight() - y - BORDER_PADDING; + if (guiProperties.getGuiClass() == RecipesGui.class) { + // TODO: JEI doesn't define an exclusion area for its own side-tabs at the moment + width -= 22; + } + displayArea = new Rectangle(x, y, width, height); + if (contents.size() > 1) { + naviArea = new Rectangle(displayArea); + naviArea.height = NAVIGATION_HEIGHT; + displayArea.y += NAVIGATION_HEIGHT + BORDER_PADDING; + displayArea.height -= NAVIGATION_HEIGHT + BORDER_PADDING; + navigation.updateBounds(naviArea); + } else { + naviArea = new Rectangle(); + } + } + + @Override + @Nullable + public IClickedIngredient getIngredientUnderMouse(int mouseX, int mouseY) { + if (canShow && hasContent()) { + return contents.get(current).getIngredientUnderMouse(mouseX, mouseY); + } + return null; + } + + @Override + public boolean canSetFocusWithMouse() { + if (canShow && hasContent()) { + return contents.get(current).canSetFocusWithMouse(); + } + return false; + } + + public boolean handleMouseScrolled(int mouseX, int mouseY, int dWheel) { + if (canShow && hasContent()) { + if (displayArea.contains(mouseX, mouseY)) { + return contents.get(current).handleMouseScrolled(mouseX, mouseY, dWheel); + } else if (naviArea.contains(mouseX, mouseY)) { + if (dWheel < 0) { + nextPage(); + } else { + previousPage(); + } + return true; + } + } + return false; + } + + public boolean handleMouseClicked(int mouseX, int mouseY, int mouseButton) { + if (canShow && hasContent()) { + if (displayArea.contains(mouseX, mouseY)) { + return contents.get(current).handleMouseClicked(mouseX, mouseY, mouseButton); + } else if (naviArea.contains(mouseX, mouseY)) { + return navigation.handleMouseClickedButtons(mouseX, mouseY); + } + } + return false; + } + + @Override + public boolean nextPage() { + current++; + if (current >= contents.size()) { + current = 0; + } + navigation.updatePageState(); + return true; + } + + @Override + public boolean previousPage() { + current--; + if (current < 0) { + current = contents.size(); + } + navigation.updatePageState(); + return true; + } + + @Override + public boolean hasNext() { + return current < contents.size() - 1; + } + + @Override + public boolean hasPrevious() { + return current > 0; + } + + @Override + public int getPageCount() { + return contents.size(); + } + + @Override + public int getPageNumber() { + return current; + } + +} diff --git a/src/main/java/mezz/jei/gui/overlay/bookmarks/package-info.java b/src/main/java/mezz/jei/gui/overlay/bookmarks/package-info.java new file mode 100644 index 000000000..ae28fcb84 --- /dev/null +++ b/src/main/java/mezz/jei/gui/overlay/bookmarks/package-info.java @@ -0,0 +1,9 @@ +@ParametersAreNonnullByDefault +@FieldsAreNonnullByDefault +@MethodsReturnNonnullByDefault +package mezz.jei.gui.overlay.bookmarks; + +import mcp.MethodsReturnNonnullByDefault; +import mezz.jei.util.FieldsAreNonnullByDefault; + +import javax.annotation.ParametersAreNonnullByDefault; \ No newline at end of file diff --git a/src/main/java/mezz/jei/ingredients/IngredientFilter.java b/src/main/java/mezz/jei/ingredients/IngredientFilter.java index e7e791c20..4d9f59f1c 100644 --- a/src/main/java/mezz/jei/ingredients/IngredientFilter.java +++ b/src/main/java/mezz/jei/ingredients/IngredientFilter.java @@ -1,28 +1,17 @@ package mezz.jei.ingredients; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.List; -import java.util.function.Function; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - import com.google.common.collect.ImmutableList; import it.unimi.dsi.fastutil.chars.Char2ObjectMap; import it.unimi.dsi.fastutil.chars.Char2ObjectOpenHashMap; import it.unimi.dsi.fastutil.ints.IntIterator; import it.unimi.dsi.fastutil.ints.IntOpenHashSet; import it.unimi.dsi.fastutil.ints.IntSet; -import mezz.jei.Internal; import mezz.jei.api.IIngredientFilter; import mezz.jei.api.ingredients.IIngredientHelper; import mezz.jei.config.Config; import mezz.jei.config.EditModeToggleEvent; import mezz.jei.gui.ingredients.IIngredientListElement; -import mezz.jei.gui.overlay.IngredientListOverlay; -import mezz.jei.runtime.JeiRuntime; +import mezz.jei.gui.overlay.IIngredientGridSource; import mezz.jei.startup.PlayerJoinedWorldEvent; import mezz.jei.suffixtree.CombinedSearchTrees; import mezz.jei.suffixtree.GeneralizedSuffixTree; @@ -34,8 +23,16 @@ import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; import javax.annotation.Nullable; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.function.Function; +import java.util.regex.Matcher; +import java.util.regex.Pattern; -public class IngredientFilter implements IIngredientFilter { +public class IngredientFilter implements IIngredientFilter, IIngredientGridSource { private static final Pattern QUOTE_PATTERN = Pattern.compile("\""); private static final Pattern FILTER_SPLIT_PATTERN = Pattern.compile("(-?\".*?(?:\"|$)|\\S+)"); @@ -53,6 +50,7 @@ public class IngredientFilter implements IIngredientFilter { @Nullable private String filterCached; private List ingredientListCached = Collections.emptyList(); + private final List listeners = new ArrayList<>(); public IngredientFilter(IngredientBlacklistInternal blacklist) { this.blacklist = blacklist; @@ -180,6 +178,7 @@ public void updateHiddenState(IIngredientListElement element) { } } + @Override public List getIngredientList() { String filterText = Translator.toLowercaseWithLocale(Config.getFilterText()); if (!filterText.equals(filterCached)) { @@ -211,11 +210,7 @@ public String getFilterText() { public void setFilterText(String filterText) { ErrorUtil.checkNotNull(filterText, "filterText"); if (Config.setFilterText(filterText)) { - JeiRuntime runtime = Internal.getRuntime(); - if (runtime != null) { - IngredientListOverlay ingredientListOverlay = runtime.getIngredientListOverlay(); - ingredientListOverlay.onSetFilterText(filterText); - } + notifyListenersOfChange(); } } @@ -379,7 +374,19 @@ private static IntSet intersection(IntSet set1, IntSet set2) { } } + @Override public int size() { return getIngredientList().size(); } + + @Override + public void addListener(IIngredientGridSource.Listener listener) { + listeners.add(listener); + } + + private void notifyListenersOfChange() { + for (IIngredientGridSource.Listener listener : listeners) { + listener.onChange(); + } + } } diff --git a/src/main/java/mezz/jei/ingredients/IngredientListElement.java b/src/main/java/mezz/jei/ingredients/IngredientListElement.java index bcaad3556..204ed9ac2 100644 --- a/src/main/java/mezz/jei/ingredients/IngredientListElement.java +++ b/src/main/java/mezz/jei/ingredients/IngredientListElement.java @@ -1,17 +1,5 @@ package mezz.jei.ingredients; -import javax.annotation.Nullable; -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Locale; -import java.util.Map; -import java.util.Set; -import java.util.regex.Pattern; -import java.util.stream.Collectors; - import com.google.common.collect.ImmutableSet; import mezz.jei.api.ingredients.IIngredientHelper; import mezz.jei.api.ingredients.IIngredientRenderer; @@ -26,10 +14,18 @@ import net.minecraft.item.ItemStack; import net.minecraftforge.oredict.OreDictionary; +import javax.annotation.Nullable; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashSet; +import java.util.List; +import java.util.Locale; +import java.util.Set; +import java.util.regex.Pattern; +import java.util.stream.Collectors; + public class IngredientListElement implements IIngredientListElement { private static final Pattern SPACE_PATTERN = Pattern.compile("\\s"); - private static final Map WILDCARD_ADDED_ORDER = new HashMap<>(); - private static int ADDED_INDEX = 0; private final V ingredient; private final int orderIndex; @@ -42,18 +38,8 @@ public class IngredientListElement implements IIngredientListElement { private boolean visible = true; @Nullable - public static IngredientListElement create(V ingredient, IIngredientHelper ingredientHelper, IIngredientRenderer ingredientRenderer, IModIdHelper modIdHelper) { + public static IngredientListElement create(V ingredient, IIngredientHelper ingredientHelper, IIngredientRenderer ingredientRenderer, IModIdHelper modIdHelper, int orderIndex) { try { - final int orderIndex; - String uid = ingredientHelper.getWildcardId(ingredient); - if (WILDCARD_ADDED_ORDER.containsKey(uid)) { - orderIndex = WILDCARD_ADDED_ORDER.get(uid); - } else { - WILDCARD_ADDED_ORDER.put(uid, ADDED_INDEX); - orderIndex = ADDED_INDEX; - ADDED_INDEX++; - } - return new IngredientListElement<>(ingredient, orderIndex, ingredientHelper, ingredientRenderer, modIdHelper); } catch (RuntimeException e) { try { diff --git a/src/main/java/mezz/jei/ingredients/IngredientListElementFactory.java b/src/main/java/mezz/jei/ingredients/IngredientListElementFactory.java index f08fbb999..b3b8fe75d 100644 --- a/src/main/java/mezz/jei/ingredients/IngredientListElementFactory.java +++ b/src/main/java/mezz/jei/ingredients/IngredientListElementFactory.java @@ -13,6 +13,8 @@ import java.util.Collection; public final class IngredientListElementFactory { + private static IngredientOrderTracker ORDER_TRACKER = new IngredientOrderTracker(); + private IngredientListElementFactory() { } @@ -34,7 +36,8 @@ public static NonNullList> createList(IIngredientR NonNullList> list = NonNullList.create(); for (V ingredient : ingredients) { if (ingredient != null) { - IngredientListElement ingredientListElement = IngredientListElement.create(ingredient, ingredientHelper, ingredientRenderer, modIdHelper); + int orderIndex = ORDER_TRACKER.getOrderIndex(ingredient, ingredientHelper); + IngredientListElement ingredientListElement = IngredientListElement.create(ingredient, ingredientHelper, ingredientRenderer, modIdHelper, orderIndex); if (ingredientListElement != null) { list.add(ingredientListElement); } @@ -44,10 +47,10 @@ public static NonNullList> createList(IIngredientR } @Nullable - public static IIngredientListElement createElement(IIngredientRegistry ingredientRegistry, IIngredientType ingredientType, V ingredient, IModIdHelper modIdHelper) { + public static IIngredientListElement createUnorderedElement(IIngredientRegistry ingredientRegistry, IIngredientType ingredientType, V ingredient, IModIdHelper modIdHelper) { IIngredientHelper ingredientHelper = ingredientRegistry.getIngredientHelper(ingredientType); IIngredientRenderer ingredientRenderer = ingredientRegistry.getIngredientRenderer(ingredientType); - return IngredientListElement.create(ingredient, ingredientHelper, ingredientRenderer, modIdHelper); + return IngredientListElement.create(ingredient, ingredientHelper, ingredientRenderer, modIdHelper, 0); } private static void addToBaseList(NonNullList baseList, IIngredientRegistry ingredientRegistry, IIngredientType ingredientType, IModIdHelper modIdHelper) { @@ -59,7 +62,8 @@ private static void addToBaseList(NonNullList baseLi for (V ingredient : ingredients) { progressBar.step(""); if (ingredient != null) { - IngredientListElement ingredientListElement = IngredientListElement.create(ingredient, ingredientHelper, ingredientRenderer, modIdHelper); + int orderIndex = ORDER_TRACKER.getOrderIndex(ingredient, ingredientHelper); + IngredientListElement ingredientListElement = IngredientListElement.create(ingredient, ingredientHelper, ingredientRenderer, modIdHelper, orderIndex); if (ingredientListElement != null) { baseList.add(ingredientListElement); } diff --git a/src/main/java/mezz/jei/ingredients/IngredientOrderTracker.java b/src/main/java/mezz/jei/ingredients/IngredientOrderTracker.java new file mode 100644 index 000000000..87e549711 --- /dev/null +++ b/src/main/java/mezz/jei/ingredients/IngredientOrderTracker.java @@ -0,0 +1,23 @@ +package mezz.jei.ingredients; + +import mezz.jei.api.ingredients.IIngredientHelper; + +import java.util.HashMap; +import java.util.Map; + +public class IngredientOrderTracker { + private final Map wildcardAddedOrder = new HashMap<>(); + private int addedIndex = 0; + + public int getOrderIndex(V ingredient, IIngredientHelper ingredientHelper) { + String uid = ingredientHelper.getWildcardId(ingredient); + if (wildcardAddedOrder.containsKey(uid)) { + return wildcardAddedOrder.get(uid); + } else { + int index = addedIndex; + wildcardAddedOrder.put(uid, index); + addedIndex++; + return index; + } + } +} diff --git a/src/main/java/mezz/jei/ingredients/IngredientRegistry.java b/src/main/java/mezz/jei/ingredients/IngredientRegistry.java index 936842dfe..4b4f3d774 100644 --- a/src/main/java/mezz/jei/ingredients/IngredientRegistry.java +++ b/src/main/java/mezz/jei/ingredients/IngredientRegistry.java @@ -19,6 +19,7 @@ import net.minecraft.tileentity.TileEntityFurnace; import net.minecraft.util.NonNullList; +import javax.annotation.Nullable; import java.util.Collection; import java.util.Collections; import java.util.List; @@ -105,6 +106,17 @@ public Collection getAllIngredients(IIngredientType ingredientType) { } } + @Nullable + public V getIngredientByUid(IIngredientType ingredientType, String uid) { + @SuppressWarnings("unchecked") + IngredientSet ingredients = ingredientsMap.get(ingredientType); + if (ingredients == null) { + return null; + } else { + return ingredients.getByUid(uid); + } + } + @Override @Deprecated public Collection getAllIngredients(Class ingredientClass) { @@ -333,7 +345,7 @@ public void removeIngredientsAtRuntime(IIngredientType ingredientType, Co public boolean isIngredientVisible(V ingredient, IngredientFilter ingredientFilter) { IIngredientType ingredientType = getIngredientType(ingredient); - IIngredientListElement element = IngredientListElementFactory.createElement(this, ingredientType, ingredient, modIdHelper); + IIngredientListElement element = IngredientListElementFactory.createUnorderedElement(this, ingredientType, ingredient, modIdHelper); if (element == null) { return false; } diff --git a/src/main/java/mezz/jei/input/InputHandler.java b/src/main/java/mezz/jei/input/InputHandler.java index a7b8c7bd0..6693ade06 100644 --- a/src/main/java/mezz/jei/input/InputHandler.java +++ b/src/main/java/mezz/jei/input/InputHandler.java @@ -1,19 +1,17 @@ package mezz.jei.input; -import javax.annotation.Nullable; -import java.util.ArrayList; -import java.util.List; - import it.unimi.dsi.fastutil.ints.IntArraySet; import it.unimi.dsi.fastutil.ints.IntSet; import mezz.jei.api.ingredients.IIngredientHelper; import mezz.jei.api.ingredients.IIngredientRegistry; import mezz.jei.api.recipe.IFocus; +import mezz.jei.bookmarks.BookmarkList; import mezz.jei.config.Config; import mezz.jei.config.IngredientBlacklistType; import mezz.jei.config.KeyBindings; import mezz.jei.gui.Focus; import mezz.jei.gui.overlay.IngredientListOverlay; +import mezz.jei.gui.overlay.bookmarks.LeftAreaDispatcher; import mezz.jei.gui.recipes.RecipeClickableArea; import mezz.jei.gui.recipes.RecipesGui; import mezz.jei.ingredients.IngredientFilter; @@ -29,24 +27,33 @@ import org.lwjgl.input.Keyboard; import org.lwjgl.input.Mouse; +import javax.annotation.Nullable; +import java.util.ArrayList; +import java.util.List; + public class InputHandler { private final RecipeRegistry recipeRegistry; private final IIngredientRegistry ingredientRegistry; private final IngredientFilter ingredientFilter; private final RecipesGui recipesGui; private final IngredientListOverlay ingredientListOverlay; + private final LeftAreaDispatcher leftAreaDispatcher; + private final BookmarkList bookmarkList; private final List showsRecipeFocuses = new ArrayList<>(); private final IntSet clickHandled = new IntArraySet(); - public InputHandler(JeiRuntime runtime, IngredientListOverlay ingredientListOverlay) { + public InputHandler(JeiRuntime runtime, IngredientListOverlay ingredientListOverlay, LeftAreaDispatcher leftAreaDispatcher, BookmarkList bookmarkList) { this.recipeRegistry = runtime.getRecipeRegistry(); this.ingredientRegistry = runtime.getIngredientRegistry(); this.ingredientFilter = runtime.getIngredientFilter(); this.recipesGui = runtime.getRecipesGui(); this.ingredientListOverlay = ingredientListOverlay; + this.leftAreaDispatcher = leftAreaDispatcher; + this.bookmarkList = bookmarkList; this.showsRecipeFocuses.add(recipesGui); this.showsRecipeFocuses.add(ingredientListOverlay); + this.showsRecipeFocuses.add(leftAreaDispatcher); this.showsRecipeFocuses.add(new GuiContainerWrapper()); } @@ -102,7 +109,7 @@ public boolean handleMouseEvent(GuiScreen guiScreen, int mouseX, int mouseY) { } private boolean handleMouseScroll(int dWheel, int mouseX, int mouseY) { - return ingredientListOverlay.handleMouseScrolled(mouseX, mouseY, dWheel); + return ingredientListOverlay.handleMouseScrolled(mouseX, mouseY, dWheel) || leftAreaDispatcher.handleMouseScrolled(mouseX, mouseY, dWheel); } private boolean handleMouseClick(GuiScreen guiScreen, int mouseButton, int mouseX, int mouseY) { @@ -113,6 +120,9 @@ private boolean handleMouseClick(GuiScreen guiScreen, int mouseButton, int mouse if (ingredientListOverlay.handleMouseClicked(mouseX, mouseY, mouseButton)) { return true; } + if (leftAreaDispatcher.handleMouseClicked(mouseX, mouseY, mouseButton)) { + return true; + } if (clicked != null && handleMouseClickedFocus(mouseButton, clicked)) { return true; @@ -230,19 +240,38 @@ private boolean handleGlobalKeybinds(int eventKey) { Config.toggleOverlayEnabled(); return false; } + if (KeyBindings.toggleBookmarkOverlay.isActiveAndMatches(eventKey)) { + Config.toggleBookmarkEnabled(); + return false; + } return ingredientListOverlay.onGlobalKeyPressed(eventKey); } private boolean handleFocusKeybinds(int eventKey) { final boolean showRecipe = KeyBindings.showRecipe.isActiveAndMatches(eventKey); final boolean showUses = KeyBindings.showUses.isActiveAndMatches(eventKey); - if (showRecipe || showUses) { + final boolean bookmark = KeyBindings.bookmark.isActiveAndMatches(eventKey); + if (showRecipe || showUses || bookmark) { IClickedIngredient clicked = getIngredientUnderMouseForKey(MouseHelper.getX(), MouseHelper.getY()); if (clicked != null) { - IFocus.Mode mode = showRecipe ? IFocus.Mode.OUTPUT : IFocus.Mode.INPUT; - recipesGui.show(new Focus(mode, clicked.getValue())); - clicked.onClickHandled(); - return true; + if (bookmark) { + if (bookmarkList.remove(clicked.getValue())) { + if (bookmarkList.isEmpty() && Config.isBookmarkOverlayEnabled()) { + Config.toggleBookmarkEnabled(); + } + return true; + } else { + if (!Config.isBookmarkOverlayEnabled()) { + Config.toggleBookmarkEnabled(); + } + return bookmarkList.add(clicked.getValue()); + } + } else { + IFocus.Mode mode = showRecipe ? IFocus.Mode.OUTPUT : IFocus.Mode.INPUT; + recipesGui.show(new Focus(mode, clicked.getValue())); + clicked.onClickHandled(); + return true; + } } } return false; diff --git a/src/main/java/mezz/jei/startup/JeiStarter.java b/src/main/java/mezz/jei/startup/JeiStarter.java index c97612894..4fcbf1696 100644 --- a/src/main/java/mezz/jei/startup/JeiStarter.java +++ b/src/main/java/mezz/jei/startup/JeiStarter.java @@ -7,10 +7,13 @@ import mezz.jei.api.gui.IAdvancedGuiHandler; import mezz.jei.api.gui.IGhostIngredientHandler; import mezz.jei.api.gui.IGuiScreenHandler; +import mezz.jei.bookmarks.BookmarkList; import mezz.jei.config.Config; import mezz.jei.gui.GuiEventHandler; import mezz.jei.gui.ingredients.IIngredientListElement; import mezz.jei.gui.overlay.IngredientListOverlay; +import mezz.jei.gui.overlay.bookmarks.BookmarkOverlay; +import mezz.jei.gui.overlay.bookmarks.LeftAreaDispatcher; import mezz.jei.gui.recipes.RecipesGui; import mezz.jei.ingredients.IngredientBlacklistInternal; import mezz.jei.ingredients.IngredientFilter; @@ -26,6 +29,7 @@ import net.minecraft.util.NonNullList; import net.minecraftforge.fml.common.ProgressManager; +import java.awt.Rectangle; import java.util.Iterator; import java.util.List; import java.util.Map; @@ -78,11 +82,19 @@ public void start(List plugins) { Internal.setIngredientFilter(ingredientFilter); timer.stop(); + timer.start("Building bookmarks"); + BookmarkList bookmarkList = new BookmarkList(ingredientRegistry); + bookmarkList.loadBookmarks(); + Internal.setBookmarkList(bookmarkList); + timer.stop(); + timer.start("Building runtime"); List> advancedGuiHandlers = modRegistry.getAdvancedGuiHandlers(); Map guiScreenHandlers = modRegistry.getGuiScreenHandlers(); Map ghostIngredientHandlers = modRegistry.getGhostIngredientHandlers(); IngredientListOverlay ingredientListOverlay = new IngredientListOverlay(ingredientFilter); + + BookmarkOverlay bookmarkOverlay = new BookmarkOverlay(new Rectangle(), bookmarkList, jeiHelpers.getGuiHelper()); RecipesGui recipesGui = new RecipesGui(recipeRegistry); JeiRuntime jeiRuntime = new JeiRuntime(recipeRegistry, ingredientListOverlay, recipesGui, ingredientRegistry, advancedGuiHandlers, guiScreenHandlers, ghostIngredientHandlers, ingredientFilter); Internal.setRuntime(jeiRuntime); @@ -92,9 +104,12 @@ public void start(List plugins) { sendRuntime(plugins, jeiRuntime); - GuiEventHandler guiEventHandler = new GuiEventHandler(ingredientListOverlay, recipeRegistry); + LeftAreaDispatcher leftAreaDispatcher = new LeftAreaDispatcher(jeiRuntime); + leftAreaDispatcher.addContent(bookmarkOverlay); + + GuiEventHandler guiEventHandler = new GuiEventHandler(leftAreaDispatcher, ingredientListOverlay, recipeRegistry); Internal.setGuiEventHandler(guiEventHandler); - InputHandler inputHandler = new InputHandler(jeiRuntime, ingredientListOverlay); + InputHandler inputHandler = new InputHandler(jeiRuntime, ingredientListOverlay, leftAreaDispatcher, bookmarkList); Internal.setInputHandler(inputHandler); Config.checkForModNameFormatOverride(); diff --git a/src/main/java/mezz/jei/util/IngredientSet.java b/src/main/java/mezz/jei/util/IngredientSet.java index bc46c0f8e..40b4fabe4 100644 --- a/src/main/java/mezz/jei/util/IngredientSet.java +++ b/src/main/java/mezz/jei/util/IngredientSet.java @@ -7,6 +7,7 @@ import mezz.jei.startup.StackHelper; import net.minecraft.item.ItemStack; +import javax.annotation.Nullable; import java.util.AbstractSet; import java.util.Collection; import java.util.Iterator; @@ -68,6 +69,11 @@ public boolean contains(Object o) { return ingredients.containsKey(uid); } + @Nullable + public V getByUid(String uid) { + return ingredients.get(uid); + } + @Override public void clear() { ingredients.clear(); diff --git a/src/main/resources/assets/jei/lang/en_us.lang b/src/main/resources/assets/jei/lang/en_us.lang index 43f1cb18a..072d555aa 100644 --- a/src/main/resources/assets/jei/lang/en_us.lang +++ b/src/main/resources/assets/jei/lang/en_us.lang @@ -17,6 +17,10 @@ jei.tooltip.recipe.id=Recipe ID: %s jei.tooltip.not.enough.space=There is not enough space to display JEI here. jei.tooltip.ingredient.list.disabled=The JEI overlay is disabled. jei.tooltip.ingredient.list.disabled.how.to.fix=Press %s to enable it. +jei.tooltip.bookmarks=JEI Bookmarks +jei.tooltip.bookmarks.usage.nokey=Add a key binding for JEI bookmarks in your Controls settings. +jei.tooltip.bookmarks.usage.key=Hover over an ingredient and press "%s" to bookmark it. +jei.tooltip.bookmarks.not.enough.space=There is not enough space to display bookmarks here. # Error Tooltips jei.tooltip.error.recipe.transfer.missing=Missing Items @@ -40,6 +44,8 @@ key.jei.recipeBack=Show Previously Viewed Recipe key.jei.toggleCheatMode=Toggle Cheating Mode key.jei.previousPage=Show Previous Page key.jei.nextPage=Show Next Page +key.jei.bookmark=Add/Remove a Bookmarked Ingredient +key.jei.toggleBookmarkOverlay=Show/Hide Bookmarked Ingredients # Config config.jei.default=Default @@ -59,6 +65,9 @@ config.jei.interface.comment=Options relating to the User Interface. config.jei.interface.overlayEnabled=Show Ingredient List config.jei.interface.overlayEnabled.comment=Show the list of ingredients next to open guis. +config.jei.interface.bookmarkOverlayEnabled=Show Bookmarks +config.jei.interface.bookmarkOverlayEnabled.comment=Show the list of bookmarks next to open guis. + config.jei.search=Search Options config.jei.search.comment=Options relating to the search bar. config.jei.search.modNameSearchMode=@ModName diff --git a/src/main/resources/assets/jei/textures/gui/gui_vanilla.png b/src/main/resources/assets/jei/textures/gui/gui_vanilla.png index f1b02b11a..ac633be73 100644 Binary files a/src/main/resources/assets/jei/textures/gui/gui_vanilla.png and b/src/main/resources/assets/jei/textures/gui/gui_vanilla.png differ diff --git a/src/main/resources/assets/jei/textures/gui/recipe_background_2.png b/src/main/resources/assets/jei/textures/gui/recipe_background_2.png index 58dcc0b07..f46b3de98 100644 Binary files a/src/main/resources/assets/jei/textures/gui/recipe_background_2.png and b/src/main/resources/assets/jei/textures/gui/recipe_background_2.png differ