From 3def879590eff933f0224d32c30249bbeb34c66a Mon Sep 17 00:00:00 2001 From: brachy84 <45517902+brachy84@users.noreply.github.com> Date: Sun, 1 Sep 2024 20:18:58 +0900 Subject: [PATCH] Detach Mui from GuiScreenWrapper (CleanroomMC#64) * detach GuiScreenWrapper * fix mouse input * slightly scuffed gui overlays * fix text widget for unusual scales * lots of small stuff * setup tests & own matrix and vector impl * dont use deprecated field * move overlay test to own class * test button overlapping * allow creating custom gui wrappers from UIFactory * helpers & javadoc for ui factories (cherry picked from commit 47cf883976524a59dc2085fa8a9d6d49180d07b6) --- addon.gradle | 46 + dependencies.gradle | 10 + .../modularui/test/MUITestMod.java | 86 ++ .../cleanroommc/modularui/test/SizerTest.java | 46 + src/functionalTest/resources/mcmod.info | 15 + .../modularui/ClientEventHandler.java | 29 + .../cleanroommc/modularui/ClientProxy.java | 21 +- .../cleanroommc/modularui/CommonProxy.java | 9 +- .../com/cleanroommc/modularui/ModularUI.java | 4 - .../cleanroommc/modularui/api/IMuiScreen.java | 96 ++ .../cleanroommc/modularui/api/MCHelper.java | 48 + .../cleanroommc/modularui/api/UIFactory.java | 18 + .../modularui/api/drawable/IDrawable.java | 14 +- .../api/event/KeyboardInputEvent.java | 35 + .../modularui/api/event/MouseInputEvent.java | 35 + .../modularui/api/layout/IViewportStack.java | 2 +- .../modularui/drawable/GuiDraw.java | 4 +- .../cleanroommc/modularui/drawable/Icon.java | 9 + .../modularui/drawable/TextRenderer.java | 3 +- .../modularui/factory/ClientGUI.java | 6 +- .../modularui/factory/GuiData.java | 9 + .../modularui/factory/GuiFactories.java | 39 + .../modularui/factory/GuiManager.java | 39 +- .../modularui/factory/ItemGuiFactory.java | 12 +- .../modularui/factory/PosGuiData.java | 3 + .../modularui/factory/SidedPosGuiData.java | 3 + .../factory/SidedTileEntityGuiFactory.java | 8 +- .../modularui/factory/SimpleGuiFactory.java | 30 +- .../factory/TileEntityGuiFactory.java | 10 +- .../modularui/holoui/HoloScreenEntity.java | 8 +- .../cleanroommc/modularui/holoui/Plane3D.java | 7 +- .../modularui/holoui/ScreenEntityRender.java | 4 +- .../nei/ModularUIContainerInputHandler.java | 52 ++ .../nei/ModularUIContainerObjectHandler.java | 6 +- .../nei/ModularUINEIGuiHandler.java | 43 + .../integration/nei/NEIModularUIConfig.java | 26 + .../modularui/integration/nei/NEIState.java | 2 +- .../modularui/mixinplugin/Mixins.java | 4 + .../mixins/early/minecraft/GuiAccessor.java | 16 + .../early/minecraft/GuiButtonMixin.java | 29 + .../early/minecraft/GuiContainerAccessor.java | 39 +- .../early/minecraft/GuiContainerMixin.java | 26 +- .../early/minecraft/GuiScreenAccessor.java | 61 ++ .../early/minecraft/GuiScreenMixin.java | 57 ++ .../early/minecraft/MinecraftMixin.java | 11 +- .../modularui/overlay/OverlayHandler.java | 42 + .../modularui/overlay/OverlayManager.java | 43 + .../modularui/overlay/OverlayStack.java | 114 +++ .../modularui/overlay/ScreenWrapper.java | 41 + .../modularui/screen/ClientScreenHandler.java | 540 +++++++++++ .../modularui/screen/GuiContainerWrapper.java | 36 + .../modularui/screen/GuiScreenWrapper.java | 485 +--------- .../modularui/screen/ModularPanel.java | 21 +- .../modularui/screen/ModularScreen.java | 64 +- .../modularui/screen/PanelManager.java | 8 +- .../cleanroommc/modularui/screen/Tooltip.java | 4 +- .../modularui/screen/viewport/GuiContext.java | 32 +- .../screen/viewport/GuiViewportStack.java | 7 +- .../screen/viewport/TransformationMatrix.java | 4 +- .../modularui/test/OverlayTest.java | 74 ++ .../cleanroommc/modularui/test/TestBlock.java | 3 +- .../cleanroommc/modularui/test/TestGui.java | 1 - .../cleanroommc/modularui/test/TestItem.java | 3 +- .../modularui/theme/WidgetTheme.java | 5 + .../modularui/utils/FpsCounter.java | 29 + .../cleanroommc/modularui/utils/GuiUtils.java | 1 - .../cleanroommc/modularui/utils/Matrix4f.java | 846 ++++++++++++++++++ .../cleanroommc/modularui/utils/Vector3f.java | 321 +++++++ .../modularui/widget/ParentWidget.java | 17 +- .../cleanroommc/modularui/widget/Widget.java | 6 +- .../modularui/widgets/ItemSlot.java | 62 +- .../modularui/widgets/ItemSlotLong.java | 64 +- .../modularui/widgets/TextWidget.java | 10 +- 73 files changed, 3291 insertions(+), 672 deletions(-) create mode 100644 addon.gradle create mode 100644 src/functionalTest/java/com/cleanroommc/modularui/test/MUITestMod.java create mode 100644 src/functionalTest/java/com/cleanroommc/modularui/test/SizerTest.java create mode 100644 src/functionalTest/resources/mcmod.info create mode 100644 src/main/java/com/cleanroommc/modularui/api/IMuiScreen.java create mode 100644 src/main/java/com/cleanroommc/modularui/api/MCHelper.java create mode 100644 src/main/java/com/cleanroommc/modularui/api/event/KeyboardInputEvent.java create mode 100644 src/main/java/com/cleanroommc/modularui/api/event/MouseInputEvent.java create mode 100644 src/main/java/com/cleanroommc/modularui/factory/GuiFactories.java create mode 100644 src/main/java/com/cleanroommc/modularui/integration/nei/ModularUIContainerInputHandler.java create mode 100644 src/main/java/com/cleanroommc/modularui/integration/nei/ModularUINEIGuiHandler.java create mode 100644 src/main/java/com/cleanroommc/modularui/integration/nei/NEIModularUIConfig.java create mode 100644 src/main/java/com/cleanroommc/modularui/mixins/early/minecraft/GuiAccessor.java create mode 100644 src/main/java/com/cleanroommc/modularui/mixins/early/minecraft/GuiButtonMixin.java create mode 100644 src/main/java/com/cleanroommc/modularui/mixins/early/minecraft/GuiScreenAccessor.java create mode 100644 src/main/java/com/cleanroommc/modularui/mixins/early/minecraft/GuiScreenMixin.java create mode 100644 src/main/java/com/cleanroommc/modularui/overlay/OverlayHandler.java create mode 100644 src/main/java/com/cleanroommc/modularui/overlay/OverlayManager.java create mode 100644 src/main/java/com/cleanroommc/modularui/overlay/OverlayStack.java create mode 100644 src/main/java/com/cleanroommc/modularui/overlay/ScreenWrapper.java create mode 100644 src/main/java/com/cleanroommc/modularui/screen/ClientScreenHandler.java create mode 100644 src/main/java/com/cleanroommc/modularui/screen/GuiContainerWrapper.java create mode 100644 src/main/java/com/cleanroommc/modularui/test/OverlayTest.java create mode 100644 src/main/java/com/cleanroommc/modularui/utils/FpsCounter.java create mode 100644 src/main/java/com/cleanroommc/modularui/utils/Matrix4f.java create mode 100644 src/main/java/com/cleanroommc/modularui/utils/Vector3f.java diff --git a/addon.gradle b/addon.gradle new file mode 100644 index 00000000..975d6dff --- /dev/null +++ b/addon.gradle @@ -0,0 +1,46 @@ +test { + useJUnitPlatform() + testLogging { + events "passed", "skipped", "failed" + } +} + +SourceSet functionalTestSet = null + +sourceSets { + functionalTestSet = create("functionalTest") { + java { + srcDir("src/functionalTest/java") + compileClasspath += sourceSets.patchedMc.output + sourceSets.main.output + } + } +} + +configurations { configs -> + // Keep all dependencies from the main mod in the functional test mod + named(functionalTestSet.compileClasspathConfigurationName).configure {it.extendsFrom(named("compileClasspath").get())} + named(functionalTestSet.runtimeClasspathConfigurationName).configure {it.extendsFrom(named("runtimeClasspath").get())} + named(functionalTestSet.annotationProcessorConfigurationName).configure {it.extendsFrom(named("annotationProcessor").get())} +} + +tasks.register(functionalTestSet.jarTaskName, Jar) { + from(functionalTestSet.output) + archiveClassifier.set("functionalTests") + // we don't care about the version number here, keep it stable to avoid polluting the tmp directory + archiveVersion.set("1.0") + destinationDirectory.set(new File(buildDir, "tmp")) +} +tasks.named("assemble").configure { + dependsOn(functionalTestSet.jarTaskName) +} + +// Run tests in the default runServer/runClient configurations +tasks.named("runServer", JavaExec).configure { + dependsOn(functionalTestSet.jarTaskName) + classpath(configurations.named(functionalTestSet.runtimeClasspathConfigurationName), tasks.named(functionalTestSet.jarTaskName)) +} + +tasks.named("runClient", JavaExec).configure { + dependsOn(functionalTestSet.jarTaskName) + classpath(configurations.named(functionalTestSet.runtimeClasspathConfigurationName), tasks.named(functionalTestSet.jarTaskName)) +} diff --git a/dependencies.gradle b/dependencies.gradle index 5552c902..28c73dec 100644 --- a/dependencies.gradle +++ b/dependencies.gradle @@ -40,4 +40,14 @@ dependencies { //runtimeOnlyNonPublishable("com.github.GTNewHorizons:NotEnoughItems:2.6.34-GTNH:dev") // For Thaumcraft runtime //runtimeOnlyNonPublishable("com.github.GTNewHorizons:Baubles:1.0.4:dev") + + testImplementation(platform('org.junit:junit-bom:5.9.2')) + testImplementation('org.junit.jupiter:junit-jupiter') + testImplementation("org.mockito:mockito-core:3.+") + + functionalTestImplementation(platform('org.junit:junit-bom:5.9.2')) + functionalTestImplementation('org.junit.jupiter:junit-jupiter') + functionalTestImplementation('org.junit.platform:junit-platform-engine') + functionalTestImplementation('org.junit.platform:junit-platform-launcher') + functionalTestImplementation('org.junit.platform:junit-platform-reporting') } diff --git a/src/functionalTest/java/com/cleanroommc/modularui/test/MUITestMod.java b/src/functionalTest/java/com/cleanroommc/modularui/test/MUITestMod.java new file mode 100644 index 00000000..56e855bf --- /dev/null +++ b/src/functionalTest/java/com/cleanroommc/modularui/test/MUITestMod.java @@ -0,0 +1,86 @@ +package com.cleanroommc.modularui.test; + +import java.io.File; +import java.io.PrintWriter; +import java.nio.file.FileSystems; +import java.nio.file.Path; + +import net.minecraft.server.MinecraftServer; +import net.minecraft.util.ChatComponentText; + +import org.apache.commons.io.output.CloseShieldOutputStream; +import org.junit.platform.engine.discovery.DiscoverySelectors; +import org.junit.platform.launcher.Launcher; +import org.junit.platform.launcher.LauncherDiscoveryRequest; +import org.junit.platform.launcher.LauncherSession; +import org.junit.platform.launcher.TestPlan; +import org.junit.platform.launcher.core.LauncherDiscoveryRequestBuilder; +import org.junit.platform.launcher.core.LauncherFactory; +import org.junit.platform.launcher.listeners.SummaryGeneratingListener; +import org.junit.platform.launcher.listeners.TestExecutionSummary; +import org.junit.platform.reporting.legacy.xml.LegacyXmlReportGeneratingListener; + +import cpw.mods.fml.common.FMLCommonHandler; +import cpw.mods.fml.common.Mod; +import cpw.mods.fml.common.event.FMLServerStartedEvent; + +@Mod(modid = "mui-tests", name = "MUI Dev Tests", version = "1.0", dependencies = "required-after:modularui2") +public class MUITestMod { + + @Mod.EventHandler + public void onServerStarted(FMLServerStartedEvent event) { + MinecraftServer.getServer() + .addChatMessage(new ChatComponentText("Running MUI unit tests...")); + runTests(); + MinecraftServer.getServer() + .addChatMessage(new ChatComponentText("Running MUI unit tests finished")); + } + + private void runTests() { + // https://junit.org/junit5/docs/current/user-guide/#launcher-api + System.setProperty("junit.platform.reporting.open.xml.enabled", "false"); + final Path testsXmlOutDir = FileSystems.getDefault() + .getPath("./junit-out/") + .toAbsolutePath(); + final File testsXmlOutDirFile = testsXmlOutDir.toFile(); + testsXmlOutDirFile.mkdirs(); + { + File[] fileList = testsXmlOutDirFile.listFiles(); + if (fileList != null) { + for (File child : fileList) { + if (child.isFile() && child.getName() + .endsWith(".xml")) { + child.delete(); + } + } + } + } + final LauncherDiscoveryRequest discovery = LauncherDiscoveryRequestBuilder.request() + .selectors(DiscoverySelectors.selectPackage("com.cleanroommc.modularui.test")) + .build(); + final SummaryGeneratingListener summaryGenerator = new SummaryGeneratingListener(); + final TestExecutionSummary summary; + try (PrintWriter stderrWriter = new PrintWriter(new CloseShieldOutputStream(System.err), true)) { + final LegacyXmlReportGeneratingListener xmlGenerator = new LegacyXmlReportGeneratingListener( + testsXmlOutDir, + stderrWriter); + try (LauncherSession session = LauncherFactory.openSession()) { + final Launcher launcher = session.getLauncher(); + final TestPlan plan = launcher.discover(discovery); + launcher.registerTestExecutionListeners(summaryGenerator, xmlGenerator); + launcher.execute(plan); + } + summary = summaryGenerator.getSummary(); + + summary.printFailuresTo(stderrWriter, 32); + summary.printTo(stderrWriter); + stderrWriter.flush(); + } + // Throw an exception if running via `runServer` + if (summary.getTotalFailureCount() > 0 && FMLCommonHandler.instance() + .getSide() + .isServer()) { + throw new RuntimeException("Some of the unit tests failed to execute, check the log for details"); + } + } +} diff --git a/src/functionalTest/java/com/cleanroommc/modularui/test/SizerTest.java b/src/functionalTest/java/com/cleanroommc/modularui/test/SizerTest.java new file mode 100644 index 00000000..696b1232 --- /dev/null +++ b/src/functionalTest/java/com/cleanroommc/modularui/test/SizerTest.java @@ -0,0 +1,46 @@ +package com.cleanroommc.modularui.test; + +import com.cleanroommc.modularui.overlay.ScreenWrapper; +import com.cleanroommc.modularui.screen.ModularPanel; +import com.cleanroommc.modularui.screen.ModularScreen; +import com.cleanroommc.modularui.screen.NEISettingsImpl; +import com.cleanroommc.modularui.widget.sizer.Area; +import com.cleanroommc.modularui.widgets.ButtonWidget; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class SizerTest { + + static final int W = 800, H = 450; + + @Test + void test() { + ModularPanel panel = panel().child(new ButtonWidget<>().center()); + testPanel(panel); + assertArea(panel.getArea(), W / 2 - 176 / 2, H / 2 - 166 / 2, 176, 166); + } + + ModularPanel panel(int w, int h) { + return ModularPanel.defaultPanel("main", w, h); + } + + ModularPanel panel() { + return ModularPanel.defaultPanel("main"); + } + + void assertArea(Area area, int x, int y, int w, int h) { + Area.SHARED.set(x, y, w, h); + assertEquals(Area.SHARED, area); + } + + ModularScreen testPanel(ModularPanel panel) { + ModularScreen screen = new ModularScreen(panel); + screen.getContext().setNEISettings(new NEISettingsImpl()); + ScreenWrapper wrapper = new ScreenWrapper(null, screen); + screen.construct(wrapper); + screen.onResize(W, H); + return screen; + } +} diff --git a/src/functionalTest/resources/mcmod.info b/src/functionalTest/resources/mcmod.info new file mode 100644 index 00000000..d8a8cfec --- /dev/null +++ b/src/functionalTest/resources/mcmod.info @@ -0,0 +1,15 @@ +[ + { + "modid":"MUI-tests", + "name":"MUI Dev Tests", + "description":"MUI Tests to run in the development environment", + "version":"1.0", + "mcversion":"1.7.10", + "url":"https://github.com/GTNewHorizons/ModularUI2", + "updateUrl":"", + "authorList":[], + "credits":"", + "logoFile":"", + "screenshots":[] + } +] diff --git a/src/main/java/com/cleanroommc/modularui/ClientEventHandler.java b/src/main/java/com/cleanroommc/modularui/ClientEventHandler.java index 76437596..a558363a 100644 --- a/src/main/java/com/cleanroommc/modularui/ClientEventHandler.java +++ b/src/main/java/com/cleanroommc/modularui/ClientEventHandler.java @@ -1,12 +1,19 @@ package com.cleanroommc.modularui; +import com.cleanroommc.modularui.api.event.KeyboardInputEvent; +import com.cleanroommc.modularui.api.event.MouseInputEvent; import com.cleanroommc.modularui.drawable.Stencil; +import com.cleanroommc.modularui.screen.GuiContainerWrapper; + +import cpw.mods.fml.common.eventhandler.EventPriority; import cpw.mods.fml.common.eventhandler.SubscribeEvent; import cpw.mods.fml.common.gameevent.TickEvent; import cpw.mods.fml.relauncher.Side; import cpw.mods.fml.relauncher.SideOnly; +import net.minecraftforge.client.event.GuiScreenEvent; + import org.lwjgl.opengl.GL11; @SideOnly(Side.CLIENT) @@ -32,4 +39,26 @@ public void preDraw(TickEvent.RenderTickEvent event) { } Stencil.reset(); } + + @SubscribeEvent(priority = EventPriority.HIGHEST) + public static void onGuiInput(MouseInputEvent.Pre event) { + if (hasDraggable(event)) { + // cancel interactions with other mods + event.gui.handleMouseInput(); + event.setCanceled(true); + } + } + + @SubscribeEvent(priority = EventPriority.HIGHEST) + public static void onGuiInput(KeyboardInputEvent.Pre event) { + if (hasDraggable(event)) { + // cancel interactions with other mods + event.gui.handleKeyboardInput(); + event.setCanceled(true); + } + } + + private static boolean hasDraggable(GuiScreenEvent event) { + return event.gui instanceof GuiContainerWrapper screenWrapper && screenWrapper.getScreen().getContext().hasDraggable(); + } } diff --git a/src/main/java/com/cleanroommc/modularui/ClientProxy.java b/src/main/java/com/cleanroommc/modularui/ClientProxy.java index d6f46d66..4cca0d55 100644 --- a/src/main/java/com/cleanroommc/modularui/ClientProxy.java +++ b/src/main/java/com/cleanroommc/modularui/ClientProxy.java @@ -1,13 +1,13 @@ package com.cleanroommc.modularui; -import codechicken.nei.guihook.GuiContainerManager; import com.cleanroommc.modularui.drawable.DrawableSerialization; -import com.cleanroommc.modularui.factory.GuiManager; import com.cleanroommc.modularui.holoui.HoloScreenEntity; import com.cleanroommc.modularui.holoui.ScreenEntityRender; -import com.cleanroommc.modularui.integration.nei.ModularUIContainerObjectHandler; import com.cleanroommc.modularui.mixins.early.forge.ForgeHooksClientMixin; +import com.cleanroommc.modularui.overlay.OverlayManager; +import com.cleanroommc.modularui.screen.ClientScreenHandler; import com.cleanroommc.modularui.test.EventHandler; +import com.cleanroommc.modularui.test.OverlayTest; import com.cleanroommc.modularui.theme.ThemeManager; import com.cleanroommc.modularui.theme.ThemeReloadCommand; import cpw.mods.fml.client.registry.RenderingRegistry; @@ -24,9 +24,6 @@ import net.minecraftforge.client.MinecraftForgeClient; import net.minecraftforge.common.MinecraftForge; -import static com.cleanroommc.modularui.ModularUI.MODID_NEI; -import static com.cleanroommc.modularui.ModularUI.isNEILoaded; - @SideOnly(Side.CLIENT) @SuppressWarnings("unused") public class ClientProxy extends CommonProxy { @@ -37,14 +34,13 @@ public class ClientProxy extends CommonProxy { void preInit(FMLPreInitializationEvent event) { super.preInit(event); - if (isNEILoaded) { - registerNEIHandler(); - } - FMLCommonHandler.instance().bus().register(new ClientEventHandler()); + MinecraftForge.EVENT_BUS.register(new ClientScreenHandler()); + MinecraftForge.EVENT_BUS.register(new OverlayManager()); if (ModularUIConfig.enableTestGuis) { MinecraftForge.EVENT_BUS.register(new EventHandler()); + OverlayTest.init(); } DrawableSerialization.init(); @@ -67,9 +63,4 @@ void postInit(FMLPostInitializationEvent event) { public Timer getTimer60Fps() { return this.timer60Fps; } - - @Optional.Method(modid = MODID_NEI) - private void registerNEIHandler() { - GuiContainerManager.addObjectHandler(new ModularUIContainerObjectHandler()); - } } diff --git a/src/main/java/com/cleanroommc/modularui/CommonProxy.java b/src/main/java/com/cleanroommc/modularui/CommonProxy.java index 61d171f3..adaaa978 100644 --- a/src/main/java/com/cleanroommc/modularui/CommonProxy.java +++ b/src/main/java/com/cleanroommc/modularui/CommonProxy.java @@ -1,9 +1,6 @@ package com.cleanroommc.modularui; -import com.cleanroommc.modularui.factory.GuiManager; -import com.cleanroommc.modularui.factory.ItemGuiFactory; -import com.cleanroommc.modularui.factory.SidedTileEntityGuiFactory; -import com.cleanroommc.modularui.factory.TileEntityGuiFactory; +import com.cleanroommc.modularui.factory.*; import com.cleanroommc.modularui.holoui.HoloScreenEntity; import com.cleanroommc.modularui.network.NetworkHandler; import com.cleanroommc.modularui.test.ItemEditorGui; @@ -39,9 +36,7 @@ void preInit(FMLPreInitializationEvent event) { NetworkHandler.init(); - GuiManager.registerFactory(TileEntityGuiFactory.INSTANCE); - GuiManager.registerFactory(SidedTileEntityGuiFactory.INSTANCE); - GuiManager.registerFactory(ItemGuiFactory.INSTANCE); + GuiFactories.init(); } void postInit(FMLPostInitializationEvent event) { diff --git a/src/main/java/com/cleanroommc/modularui/ModularUI.java b/src/main/java/com/cleanroommc/modularui/ModularUI.java index 7a015163..5f33cdb2 100644 --- a/src/main/java/com/cleanroommc/modularui/ModularUI.java +++ b/src/main/java/com/cleanroommc/modularui/ModularUI.java @@ -52,10 +52,6 @@ public void postInit(FMLPostInitializationEvent event) { proxy.postInit(event); } - @Mod.EventHandler - public void onLoadComplete(FMLLoadCompleteEvent event) { - } - @Mod.EventHandler public void onServerLoad(FMLServerStartingEvent event) { proxy.onServerLoad(event); diff --git a/src/main/java/com/cleanroommc/modularui/api/IMuiScreen.java b/src/main/java/com/cleanroommc/modularui/api/IMuiScreen.java new file mode 100644 index 00000000..9b21d991 --- /dev/null +++ b/src/main/java/com/cleanroommc/modularui/api/IMuiScreen.java @@ -0,0 +1,96 @@ +package com.cleanroommc.modularui.api; + +import com.cleanroommc.modularui.mixins.early.minecraft.GuiContainerAccessor; +import com.cleanroommc.modularui.screen.ClientScreenHandler; +import com.cleanroommc.modularui.screen.ModularScreen; + +import cpw.mods.fml.relauncher.Side; +import cpw.mods.fml.relauncher.SideOnly; + +import net.minecraft.client.gui.GuiScreen; +import net.minecraft.client.gui.inventory.GuiContainer; +import net.minecraft.inventory.Slot; + +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.NotNull; + +import java.awt.*; +import java.util.function.IntConsumer; + +/** + * Implement this interface on a {@link GuiScreen} to be able to use it as a custom wrapper. + * The GuiScreen should have final {@link ModularScreen} field, which is set from the constructor. + * Additionally, the GuiScreen MUST call {@link ModularScreen#construct(IMuiScreen)} in its constructor. + * See {@link com.cleanroommc.modularui.screen.GuiScreenWrapper GuiScreenWrapper} and {@link com.cleanroommc.modularui.screen.GuiContainerWrapper GuiContainerWrapper} + * for default implementations. + */ +@SideOnly(Side.CLIENT) +public interface IMuiScreen { + + /** + * Returns the {@link ModularScreen} that is being wrapped. This should return a final instance field. + * + * @return the wrapped modular screen + */ + @NotNull + ModularScreen getScreen(); + + /** + * This method decides how the gui background is drawn. + * The intended usage is to override {@link GuiScreen#drawWorldBackground(int)} and call this method + * with the super method reference as the second parameter. + * + * @param tint background color tint + * @param drawFunction a method reference to draw the world background normally with the tint as the parameter + */ + @ApiStatus.NonExtendable + default void handleDrawBackground(int tint, IntConsumer drawFunction) { + if (ClientScreenHandler.shouldDrawWorldBackground()) { + drawFunction.accept(tint); + } + ClientScreenHandler.drawDarkBackground(getGuiScreen(), tint); + } + + /** + * This method is called every time the {@link ModularScreen} resizes. + * This usually only affects {@link GuiContainer GuiContainers}. + * + * @param area area of the main panel + */ + default void updateGuiArea(Rectangle area) { + if (getGuiScreen() instanceof GuiContainer container) { + ClientScreenHandler.updateGuiArea(container, area); + } + } + + /** + * @return if this wrapper is a {@link GuiContainer} + */ + @ApiStatus.NonExtendable + default boolean isGuiContainer() { + return getGuiScreen() instanceof GuiContainer; + } + + /** + * Hovering widget is handled by {@link com.cleanroommc.modularui.screen.viewport.GuiContext}. + * If it detects a slot, this method is called. Only affects {@link GuiContainer GuiContainers}. + * + * @param slot hovered slot + */ + @ApiStatus.NonExtendable + default void setHoveredSlot(Slot slot) { + if (getGuiScreen() instanceof GuiContainerAccessor acc) { + acc.setHoveredSlot(slot); + } + } + + /** + * Returns the {@link GuiScreen} that wraps the {@link ModularScreen}. + * In most cases this does not need to be overridden as this interfaces should be implemented on {@link GuiScreen GuiScreens}. + * + * @return the wrapping gui screen + */ + default GuiScreen getGuiScreen() { + return (GuiScreen) this; + } +} diff --git a/src/main/java/com/cleanroommc/modularui/api/MCHelper.java b/src/main/java/com/cleanroommc/modularui/api/MCHelper.java new file mode 100644 index 00000000..7b212920 --- /dev/null +++ b/src/main/java/com/cleanroommc/modularui/api/MCHelper.java @@ -0,0 +1,48 @@ +package com.cleanroommc.modularui.api; + +import net.minecraft.client.Minecraft; +import net.minecraft.client.entity.EntityPlayerSP; +import net.minecraft.client.gui.GuiScreen; + +public class MCHelper { + + public static boolean hasMc() { + return getMc() != null; + } + + public static Minecraft getMc() { + return Minecraft.getMinecraft(); + } + + public static EntityPlayerSP getPlayer() { + if (hasMc()) { + return getMc().thePlayer; + } + return null; + } + + public static boolean closeScreen() { + if (!hasMc()) return false; + EntityPlayerSP player = Minecraft.getMinecraft().thePlayer; + if (player != null) { + player.closeScreen(); + return true; + } + Minecraft.getMinecraft().displayGuiScreen(null); + return false; + } + + public static boolean displayScreen(GuiScreen screen) { + Minecraft mc = getMc(); + if (mc != null) { + mc.displayGuiScreen(screen); + return true; + } + return false; + } + + public static GuiScreen getCurrentScreen() { + Minecraft mc = getMc(); + return mc != null ? mc.currentScreen : null; + } +} diff --git a/src/main/java/com/cleanroommc/modularui/api/UIFactory.java b/src/main/java/com/cleanroommc/modularui/api/UIFactory.java index 3c519fb8..5c11545b 100644 --- a/src/main/java/com/cleanroommc/modularui/api/UIFactory.java +++ b/src/main/java/com/cleanroommc/modularui/api/UIFactory.java @@ -1,6 +1,8 @@ package com.cleanroommc.modularui.api; import com.cleanroommc.modularui.factory.GuiData; +import com.cleanroommc.modularui.screen.GuiContainerWrapper; +import com.cleanroommc.modularui.screen.ModularContainer; import com.cleanroommc.modularui.screen.ModularPanel; import com.cleanroommc.modularui.screen.ModularScreen; import com.cleanroommc.modularui.value.sync.PanelSyncManager; @@ -48,6 +50,22 @@ public interface UIFactory { @ApiStatus.OverrideOnly ModularScreen createScreen(D guiData, ModularPanel mainPanel); + /** + * Creates the screen wrapper for the GUI. Is only called on client side. + * + * @param container container for the gui + * @param screen the screen which was created in {@link #createScreen(GuiData, ModularPanel)} + * @return new screen wrapper + * @throws IllegalStateException if the wrapping screen is not a {@link net.minecraft.client.gui.inventory.GuiContainer GuiContainer} or if the + * container inside is not the same as the one passed to this method. This method is not the thrower, but the + * caller of this method. + */ + @SideOnly(Side.CLIENT) + @ApiStatus.OverrideOnly + default IMuiScreen createScreenWrapper(ModularContainer container, ModularScreen screen) { + return new GuiContainerWrapper(container, screen); + } + /** * Writes the gui data to a buffer. * diff --git a/src/main/java/com/cleanroommc/modularui/api/drawable/IDrawable.java b/src/main/java/com/cleanroommc/modularui/api/drawable/IDrawable.java index a67f904e..f6f45505 100644 --- a/src/main/java/com/cleanroommc/modularui/api/drawable/IDrawable.java +++ b/src/main/java/com/cleanroommc/modularui/api/drawable/IDrawable.java @@ -1,14 +1,18 @@ package com.cleanroommc.modularui.api.drawable; +import com.cleanroommc.modularui.drawable.DrawableArray; import com.cleanroommc.modularui.drawable.Icon; import com.cleanroommc.modularui.screen.viewport.GuiContext; import com.cleanroommc.modularui.theme.WidgetTheme; import com.cleanroommc.modularui.widget.Widget; import com.cleanroommc.modularui.widget.sizer.Area; -import com.google.gson.JsonObject; + import cpw.mods.fml.relauncher.Side; import cpw.mods.fml.relauncher.SideOnly; +import com.google.gson.JsonObject; +import org.jetbrains.annotations.Nullable; + /** * An object which can be drawn. This is mainly used for backgrounds and overlays in * {@link com.cleanroommc.modularui.api.widget.IWidget}. @@ -137,6 +141,14 @@ default void loadFromJson(JsonObject json) { */ IDrawable NONE = (context, x, y, width, height, widgetTheme) -> {}; + static boolean isVisible(@Nullable IDrawable drawable) { + if (drawable == null || drawable == EMPTY || drawable == NONE) return false; + if (drawable instanceof DrawableArray array) { + return array.getDrawables().length > 0; + } + return true; + } + /** * A widget wrapping a drawable. The drawable is drawn between the background and the overlay. */ diff --git a/src/main/java/com/cleanroommc/modularui/api/event/KeyboardInputEvent.java b/src/main/java/com/cleanroommc/modularui/api/event/KeyboardInputEvent.java new file mode 100644 index 00000000..d89366c8 --- /dev/null +++ b/src/main/java/com/cleanroommc/modularui/api/event/KeyboardInputEvent.java @@ -0,0 +1,35 @@ +package com.cleanroommc.modularui.api.event; + +import cpw.mods.fml.common.eventhandler.Cancelable; + +import net.minecraft.client.gui.GuiScreen; +import net.minecraftforge.client.event.GuiScreenEvent; + +public class KeyboardInputEvent extends GuiScreenEvent { + public KeyboardInputEvent(GuiScreen gui) { + super(gui); + } + + /** + * This event fires when keyboard input is detected by a GuiScreen. + * Cancel this event to bypass {@link GuiScreen#handleKeyboardInput()}. + */ + @Cancelable + public static class Pre extends KeyboardInputEvent { + public Pre(GuiScreen gui) { + super(gui); + } + } + + /** + * This event fires after {@link GuiScreen#handleKeyboardInput()} provided that the active + * screen has not been changed as a result of {@link GuiScreen#handleKeyboardInput()}. + * Cancel this event when you successfully use the keyboard input to prevent other handlers from using the same input. + */ + @Cancelable + public static class Post extends KeyboardInputEvent { + public Post(GuiScreen gui) { + super(gui); + } + } +} diff --git a/src/main/java/com/cleanroommc/modularui/api/event/MouseInputEvent.java b/src/main/java/com/cleanroommc/modularui/api/event/MouseInputEvent.java new file mode 100644 index 00000000..de38d2c7 --- /dev/null +++ b/src/main/java/com/cleanroommc/modularui/api/event/MouseInputEvent.java @@ -0,0 +1,35 @@ +package com.cleanroommc.modularui.api.event; + +import cpw.mods.fml.common.eventhandler.Cancelable; + +import net.minecraft.client.gui.GuiScreen; +import net.minecraftforge.client.event.GuiScreenEvent; + +public class MouseInputEvent extends GuiScreenEvent { + public MouseInputEvent(GuiScreen gui) { + super(gui); + } + + /** + * This event fires when mouse input is detected by a GuiScreen. + * Cancel this event to bypass {@link GuiScreen#handleMouseInput()}. + */ + @Cancelable + public static class Pre extends MouseInputEvent { + public Pre(GuiScreen gui) { + super(gui); + } + } + + /** + * This event fires after {@link GuiScreen#handleMouseInput()} provided that the active + * screen has not been changed as a result of {@link GuiScreen#handleMouseInput()}. + * Cancel this event when you successfully use the mouse input to prevent other handlers from using the same input. + */ + @Cancelable + public static class Post extends MouseInputEvent { + public Post(GuiScreen gui) { + super(gui); + } + } +} diff --git a/src/main/java/com/cleanroommc/modularui/api/layout/IViewportStack.java b/src/main/java/com/cleanroommc/modularui/api/layout/IViewportStack.java index eb9eb273..86b64012 100644 --- a/src/main/java/com/cleanroommc/modularui/api/layout/IViewportStack.java +++ b/src/main/java/com/cleanroommc/modularui/api/layout/IViewportStack.java @@ -1,9 +1,9 @@ package com.cleanroommc.modularui.api.layout; import com.cleanroommc.modularui.screen.viewport.TransformationMatrix; +import com.cleanroommc.modularui.utils.Vector3f; import com.cleanroommc.modularui.widget.sizer.Area; import org.jetbrains.annotations.Nullable; -import org.lwjgl.util.vector.Vector3f; /** * This handles all viewports in a GUI. Also keeps track of a matrix stack used for rendering and diff --git a/src/main/java/com/cleanroommc/modularui/drawable/GuiDraw.java b/src/main/java/com/cleanroommc/modularui/drawable/GuiDraw.java index 042f6a15..9a4a3080 100644 --- a/src/main/java/com/cleanroommc/modularui/drawable/GuiDraw.java +++ b/src/main/java/com/cleanroommc/modularui/drawable/GuiDraw.java @@ -1,7 +1,7 @@ package com.cleanroommc.modularui.drawable; import com.cleanroommc.modularui.ModularUI; -import com.cleanroommc.modularui.screen.GuiScreenWrapper; +import com.cleanroommc.modularui.mixins.early.minecraft.GuiScreenAccessor; import com.cleanroommc.modularui.utils.Color; import com.mitchej123.hodgepodge.textures.IPatchedTextureAtlasSprite; @@ -327,7 +327,7 @@ public static void drawItem(ItemStack item, int x, int y, float width, float hei RenderHelper.enableGUIStandardItemLighting(); GL11.glEnable(GL11.GL_DEPTH_TEST); GL11.glScalef(width / 16f, height / 16f, 1); - RenderItem renderItem = GuiScreenWrapper.getItemRenderer(); + RenderItem renderItem = ((GuiScreenAccessor) Minecraft.getMinecraft().currentScreen).getItemRender(); renderItem.zLevel = 200; renderItem.renderItemAndEffectIntoGUI(Minecraft.getMinecraft().fontRenderer, Minecraft.getMinecraft().getTextureManager(), item, x, y); renderItem.zLevel = 0; diff --git a/src/main/java/com/cleanroommc/modularui/drawable/Icon.java b/src/main/java/com/cleanroommc/modularui/drawable/Icon.java index 6a99c6d6..17fb2543 100644 --- a/src/main/java/com/cleanroommc/modularui/drawable/Icon.java +++ b/src/main/java/com/cleanroommc/modularui/drawable/Icon.java @@ -20,6 +20,7 @@ public class Icon implements IIcon { private int width = 0, height = 0; private Alignment alignment = Alignment.Center; private final Box margin = new Box(); + private int color = 0; public Icon(IDrawable drawable) { this.drawable = drawable; @@ -55,6 +56,9 @@ public void draw(GuiContext context, int x, int y, int width, int height, Widget y += (int) (height * this.alignment.y - this.height * this.alignment.y); height = this.height; } + if (this.color != 0 && this.color != widgetTheme.getColor()) { + widgetTheme = widgetTheme.withColor(this.color); + } this.drawable.draw(context, x, y, width, height, widgetTheme); } @@ -85,6 +89,11 @@ public Icon alignment(Alignment alignment) { return this; } + public Icon color(int color) { + this.color = color; + return this; + } + public Icon margin(int left, int right, int top, int bottom) { this.margin.all(left, right, top, bottom); return this; diff --git a/src/main/java/com/cleanroommc/modularui/drawable/TextRenderer.java b/src/main/java/com/cleanroommc/modularui/drawable/TextRenderer.java index 416c80dc..72b8d851 100644 --- a/src/main/java/com/cleanroommc/modularui/drawable/TextRenderer.java +++ b/src/main/java/com/cleanroommc/modularui/drawable/TextRenderer.java @@ -75,7 +75,8 @@ protected void drawMeasuredLines(List measuredLines) { int y0 = getStartY(measuredLines.size()); for (Line measuredLine : measuredLines) { int x0 = getStartX(measuredLine.width); - maxW = Math.max(draw(measuredLine.text, x0, y0), maxW); + maxW = Math.max(maxW, measuredLine.width); + draw(measuredLine.text, x0, y0); y0 += (int) getFontHeight(); } this.lastWidth = this.maxWidth > 0 ? Math.min(maxW, this.maxWidth) : maxW; diff --git a/src/main/java/com/cleanroommc/modularui/factory/ClientGUI.java b/src/main/java/com/cleanroommc/modularui/factory/ClientGUI.java index 1eb38fc1..6b2d6a20 100644 --- a/src/main/java/com/cleanroommc/modularui/factory/ClientGUI.java +++ b/src/main/java/com/cleanroommc/modularui/factory/ClientGUI.java @@ -1,12 +1,12 @@ package com.cleanroommc.modularui.factory; +import com.cleanroommc.modularui.api.MCHelper; import com.cleanroommc.modularui.screen.ContainerCustomizer; import com.cleanroommc.modularui.screen.ModularScreen; import com.cleanroommc.modularui.screen.NEISettingsImpl; import cpw.mods.fml.relauncher.Side; import cpw.mods.fml.relauncher.SideOnly; -import net.minecraft.client.Minecraft; import net.minecraft.client.gui.GuiScreen; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -71,13 +71,13 @@ public static void open(@NotNull ModularScreen screen, @NotNull NEISettingsImpl * @param screen screen to open */ public static void open(GuiScreen screen) { - Minecraft.getMinecraft().displayGuiScreen(screen); + MCHelper.displayScreen(screen); } /** * Closes any GUI that is open in this tick. */ public static void close() { - Minecraft.getMinecraft().displayGuiScreen(null); + MCHelper.displayScreen(null); } } diff --git a/src/main/java/com/cleanroommc/modularui/factory/GuiData.java b/src/main/java/com/cleanroommc/modularui/factory/GuiData.java index ea213720..479bebce 100644 --- a/src/main/java/com/cleanroommc/modularui/factory/GuiData.java +++ b/src/main/java/com/cleanroommc/modularui/factory/GuiData.java @@ -8,6 +8,15 @@ import java.util.Objects; +/** + * This class and subclasses are holding necessary data to find the exact same GUI on client and server. + * For example, if the GUI was opened by right-clicking a TileEntity, then this data needs a world and a block pos. + * Additionally, this can be used to configure NEI via {@link #getNEISettings()}. + *

+ * Also see {@link PosGuiData} (useful for TileEntities), {@link SidedPosGuiData} (useful for covers from GregTech) + * for default implementations. + *

+ */ public class GuiData { private final EntityPlayer player; diff --git a/src/main/java/com/cleanroommc/modularui/factory/GuiFactories.java b/src/main/java/com/cleanroommc/modularui/factory/GuiFactories.java new file mode 100644 index 00000000..d876d51a --- /dev/null +++ b/src/main/java/com/cleanroommc/modularui/factory/GuiFactories.java @@ -0,0 +1,39 @@ +package com.cleanroommc.modularui.factory; + +import com.cleanroommc.modularui.api.IGuiHolder; + +import org.jetbrains.annotations.ApiStatus; + +import java.util.function.Supplier; + +public class GuiFactories { + + public static TileEntityGuiFactory tileEntity() { + return TileEntityGuiFactory.INSTANCE; + } + + public static SidedTileEntityGuiFactory sidedTileEntity() { + return SidedTileEntityGuiFactory.INSTANCE; + } + + public static ItemGuiFactory item() { + return ItemGuiFactory.INSTANCE; + } + + public static SimpleGuiFactory createSimple(String name, IGuiHolder holder) { + return new SimpleGuiFactory(name, holder); + } + + public static SimpleGuiFactory createSimple(String name, Supplier> holder) { + return new SimpleGuiFactory(name, holder); + } + + @ApiStatus.Internal + public static void init() { + GuiManager.registerFactory(tileEntity()); + GuiManager.registerFactory(sidedTileEntity()); + GuiManager.registerFactory(item()); + } + + private GuiFactories() {} +} diff --git a/src/main/java/com/cleanroommc/modularui/factory/GuiManager.java b/src/main/java/com/cleanroommc/modularui/factory/GuiManager.java index 00747266..f4b4aa6b 100644 --- a/src/main/java/com/cleanroommc/modularui/factory/GuiManager.java +++ b/src/main/java/com/cleanroommc/modularui/factory/GuiManager.java @@ -1,6 +1,8 @@ package com.cleanroommc.modularui.factory; +import com.cleanroommc.modularui.api.IMuiScreen; import com.cleanroommc.modularui.api.NEISettings; +import com.cleanroommc.modularui.api.MCHelper; import com.cleanroommc.modularui.api.UIFactory; import com.cleanroommc.modularui.network.NetworkHandler; import com.cleanroommc.modularui.network.packets.OpenGuiPacket; @@ -17,8 +19,9 @@ import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; -import net.minecraft.client.Minecraft; import net.minecraft.client.entity.EntityPlayerSP; +import net.minecraft.client.gui.GuiScreen; +import net.minecraft.client.gui.inventory.GuiContainer; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.entity.player.EntityPlayerMP; import net.minecraft.network.PacketBuffer; @@ -35,7 +38,7 @@ public class GuiManager { private static final Object2ObjectMap> FACTORIES = new Object2ObjectOpenHashMap<>(16); - private static GuiScreenWrapper lastMui; + private static IMuiScreen lastMui; private static final List openedContainers = new ArrayList<>(4); public static void registerFactory(UIFactory factory) { @@ -56,6 +59,10 @@ public static void registerFactory(UIFactory factory) { return factory; } + public static boolean hasFactory(String name) { + return FACTORIES.containsKey(name); + } + public static void open(@NotNull UIFactory factory, @NotNull T guiData, EntityPlayerMP player) { if (player instanceof FakePlayer || openedContainers.contains(player)) return; openedContainers.add(player); @@ -88,18 +95,28 @@ public static void open(int windowId, @NotNull UIFactory WidgetTree.collectSyncValues(syncManager, panel); ModularScreen screen = factory.createScreen(guiData, panel); screen.getContext().setNEISettings(neiSettings); - GuiScreenWrapper guiScreenWrapper = new GuiScreenWrapper(new ModularContainer(player, syncManager, panel.getName()), screen); - guiScreenWrapper.inventorySlots.windowId = windowId; - Minecraft.getMinecraft().displayGuiScreen(guiScreenWrapper); - player.openContainer = guiScreenWrapper.inventorySlots; + ModularContainer container = new ModularContainer(player, syncManager, panel.getName()); + IMuiScreen wrapper = factory.createScreenWrapper(container, screen); + if (!(wrapper.getGuiScreen() instanceof GuiContainer guiContainer)) { + throw new IllegalStateException("The wrapping screen must be a GuiContainer for synced GUIs!"); + } + if (guiContainer.inventorySlots != container) throw new IllegalStateException("Custom Containers are not yet allowed!"); + guiContainer.inventorySlots.windowId = windowId; + MCHelper.displayScreen(wrapper.getGuiScreen()); + player.openContainer = guiContainer.inventorySlots; syncManager.onOpen(); } @SideOnly(Side.CLIENT) - static void openScreen(ModularScreen screen, NEISettingsImpl jeiSettings, ContainerCustomizer containerCustomizer) { - screen.getContext().setNEISettings(jeiSettings); - GuiScreenWrapper screenWrapper = new GuiScreenWrapper(new ModularContainer(containerCustomizer), screen); - Minecraft.getMinecraft().displayGuiScreen(screenWrapper); + static void openScreen(ModularScreen screen, NEISettingsImpl neiSettings, ContainerCustomizer containerCustomizer) { + screen.getContext().setNEISettings(neiSettings); + GuiScreen guiScreen; + if (containerCustomizer == null) { + guiScreen = new GuiScreenWrapper(screen); + } else { + guiScreen = new GuiContainerWrapper(new ModularContainer(containerCustomizer), screen); + } + MCHelper.displayScreen(guiScreen); } @SubscribeEvent @@ -118,7 +135,7 @@ public void onGuiOpen(GuiOpenEvent event) { } lastMui.getScreen().getPanelManager().dispose(); lastMui = null; - } else if (event.gui instanceof GuiScreenWrapper screenWrapper) { + } else if (event.gui instanceof IMuiScreen screenWrapper) { if (lastMui == null) { lastMui = screenWrapper; } else if (lastMui == event.gui) { diff --git a/src/main/java/com/cleanroommc/modularui/factory/ItemGuiFactory.java b/src/main/java/com/cleanroommc/modularui/factory/ItemGuiFactory.java index d156983e..09604f21 100644 --- a/src/main/java/com/cleanroommc/modularui/factory/ItemGuiFactory.java +++ b/src/main/java/com/cleanroommc/modularui/factory/ItemGuiFactory.java @@ -16,10 +16,18 @@ private ItemGuiFactory() { super("mui:item"); } - public static void open(EntityPlayerMP player) { + public void open(EntityPlayer player) { + if (player instanceof EntityPlayerMP entityPlayerMP) { + open(entityPlayerMP); + return; + } + throw new IllegalStateException("Synced GUIs must be opened from server side"); + } + + public void open(EntityPlayerMP player) { Objects.requireNonNull(player); GuiData guiData = new GuiData(player); - GuiManager.open(INSTANCE, guiData, player); + GuiManager.open(this, guiData, player); } @Override diff --git a/src/main/java/com/cleanroommc/modularui/factory/PosGuiData.java b/src/main/java/com/cleanroommc/modularui/factory/PosGuiData.java index 0e00c3f4..47ad7e85 100644 --- a/src/main/java/com/cleanroommc/modularui/factory/PosGuiData.java +++ b/src/main/java/com/cleanroommc/modularui/factory/PosGuiData.java @@ -4,6 +4,9 @@ import net.minecraft.tileentity.TileEntity; import net.minecraft.world.World; +/** + * See {@link GuiData} for an explanation for what this is for. + */ public class PosGuiData extends GuiData { private final int x, y, z; diff --git a/src/main/java/com/cleanroommc/modularui/factory/SidedPosGuiData.java b/src/main/java/com/cleanroommc/modularui/factory/SidedPosGuiData.java index d9283494..52967071 100644 --- a/src/main/java/com/cleanroommc/modularui/factory/SidedPosGuiData.java +++ b/src/main/java/com/cleanroommc/modularui/factory/SidedPosGuiData.java @@ -3,6 +3,9 @@ import net.minecraft.entity.player.EntityPlayer; import net.minecraftforge.common.util.ForgeDirection; +/** + * See {@link GuiData} for an explanation for what this is for. + */ public class SidedPosGuiData extends PosGuiData { private final ForgeDirection side; diff --git a/src/main/java/com/cleanroommc/modularui/factory/SidedTileEntityGuiFactory.java b/src/main/java/com/cleanroommc/modularui/factory/SidedTileEntityGuiFactory.java index 89ccb0e0..74f2e6f3 100644 --- a/src/main/java/com/cleanroommc/modularui/factory/SidedTileEntityGuiFactory.java +++ b/src/main/java/com/cleanroommc/modularui/factory/SidedTileEntityGuiFactory.java @@ -15,7 +15,7 @@ public class SidedTileEntityGuiFactory extends AbstractUIFactory> void open(EntityPlayer player, T tile, ForgeDirection side) { + public > void open(EntityPlayer player, T tile, ForgeDirection side) { Objects.requireNonNull(player); Objects.requireNonNull(tile); Objects.requireNonNull(side); @@ -26,14 +26,14 @@ public static > void open(Ent throw new IllegalArgumentException("TileEntity must be in same dimension as the player!"); } SidedPosGuiData data = new SidedPosGuiData(player, tile.xCoord, tile.yCoord, tile.zCoord, side); - GuiManager.open(INSTANCE, data, (EntityPlayerMP) player); + GuiManager.open(this, data, (EntityPlayerMP) player); } - public static void open(EntityPlayer player, int x, int y, int z, ForgeDirection side) { + public void open(EntityPlayer player, int x, int y, int z, ForgeDirection side) { Objects.requireNonNull(player); Objects.requireNonNull(side); SidedPosGuiData data = new SidedPosGuiData(player, x, y, z, side); - GuiManager.open(INSTANCE, data, (EntityPlayerMP) player); + GuiManager.open(this, data, (EntityPlayerMP) player); } private SidedTileEntityGuiFactory() { diff --git a/src/main/java/com/cleanroommc/modularui/factory/SimpleGuiFactory.java b/src/main/java/com/cleanroommc/modularui/factory/SimpleGuiFactory.java index c5ed3aee..e146fce5 100644 --- a/src/main/java/com/cleanroommc/modularui/factory/SimpleGuiFactory.java +++ b/src/main/java/com/cleanroommc/modularui/factory/SimpleGuiFactory.java @@ -6,12 +6,36 @@ import net.minecraft.network.PacketBuffer; import org.jetbrains.annotations.NotNull; +import java.util.Objects; import java.util.function.Supplier; +/** + * Sometimes you don't want to open guis which are bound to a TileEntity or an Item. + * For example by a command. You are supposed to create one simple factory per GUI to make sure they are same + * on client and server. + * These factories are registered automatically. + */ public class SimpleGuiFactory extends AbstractUIFactory { private final Supplier> guiHolderSupplier; + private IGuiHolder guiHolder; + /** + * Creates a simple gui factory. + * + * @param name name of the factory + * @param guiHolder gui holder + */ + public SimpleGuiFactory(String name, IGuiHolder guiHolder) { + this(name, () -> guiHolder); + } + + /** + * Creates a simple gui factory. + * + * @param name name of the factory + * @param guiHolderSupplier a function which retrieves a gui holder. This is only called once and then cached. + */ public SimpleGuiFactory(String name, Supplier> guiHolderSupplier) { super(name); this.guiHolderSupplier = guiHolderSupplier; @@ -37,6 +61,10 @@ public void writeGuiData(GuiData guiData, PacketBuffer buffer) { @Override public @NotNull IGuiHolder getGuiHolder(GuiData data) { - return this.guiHolderSupplier.get(); + if (this.guiHolder == null) { + this.guiHolder = this.guiHolderSupplier.get(); + Objects.requireNonNull(this.guiHolder, "IGuiHolder must not be null"); + } + return this.guiHolder; } } diff --git a/src/main/java/com/cleanroommc/modularui/factory/TileEntityGuiFactory.java b/src/main/java/com/cleanroommc/modularui/factory/TileEntityGuiFactory.java index 60381083..e93379ab 100644 --- a/src/main/java/com/cleanroommc/modularui/factory/TileEntityGuiFactory.java +++ b/src/main/java/com/cleanroommc/modularui/factory/TileEntityGuiFactory.java @@ -14,10 +14,10 @@ public class TileEntityGuiFactory extends AbstractUIFactory { public static final TileEntityGuiFactory INSTANCE = new TileEntityGuiFactory(); private TileEntityGuiFactory() { - super("mui:tile"); + super("mui:tile_entity"); } - public static > void open(EntityPlayer player, T tile) { + public > void open(EntityPlayer player, T tile) { Objects.requireNonNull(player); Objects.requireNonNull(tile); if (tile.isInvalid()) { @@ -27,13 +27,13 @@ public static > void open(EntityPl throw new IllegalArgumentException("TileEntity must be in same dimension as the player!"); } PosGuiData data = new PosGuiData(player, tile.xCoord, tile.yCoord, tile.zCoord); - GuiManager.open(INSTANCE, data, (EntityPlayerMP) player); + GuiManager.open(this, data, (EntityPlayerMP) player); } - public static void open(EntityPlayer player, int x, int y, int z) { + public void open(EntityPlayer player, int x, int y, int z) { Objects.requireNonNull(player); PosGuiData data = new PosGuiData(player, x, y, z); - GuiManager.open(INSTANCE, data, (EntityPlayerMP) player); + GuiManager.open(this, data, (EntityPlayerMP) player); } @Override diff --git a/src/main/java/com/cleanroommc/modularui/holoui/HoloScreenEntity.java b/src/main/java/com/cleanroommc/modularui/holoui/HoloScreenEntity.java index fa32e087..f09989b8 100644 --- a/src/main/java/com/cleanroommc/modularui/holoui/HoloScreenEntity.java +++ b/src/main/java/com/cleanroommc/modularui/holoui/HoloScreenEntity.java @@ -1,7 +1,7 @@ package com.cleanroommc.modularui.holoui; import com.cleanroommc.modularui.mixins.early.minecraft.EntityAccessor; -import com.cleanroommc.modularui.screen.GuiScreenWrapper; +import com.cleanroommc.modularui.screen.GuiContainerWrapper; import com.cleanroommc.modularui.screen.ModularContainer; import com.cleanroommc.modularui.screen.ModularScreen; import cpw.mods.fml.relauncher.Side; @@ -20,7 +20,7 @@ @ApiStatus.Experimental public class HoloScreenEntity extends Entity { - private GuiScreenWrapper wrapper; + private GuiContainerWrapper wrapper; private ModularScreen screen; private final Plane3D plane3D; private static final int ORIENTATION = 16; @@ -36,7 +36,7 @@ public HoloScreenEntity(World world) { public void setScreen(ModularScreen screen) { this.screen = screen; - this.wrapper = new GuiScreenWrapper(new ModularContainer(null), screen); + this.wrapper = new GuiContainerWrapper(new ModularContainer(null), screen); this.wrapper.setWorldAndResolution(Minecraft.getMinecraft(), (int) this.plane3D.getWidth(), (int) this.plane3D.getHeight()); } @@ -44,7 +44,7 @@ public ModularScreen getScreen() { return this.screen; } - public GuiScreenWrapper getWrapper() { + public GuiContainerWrapper getWrapper() { return this.wrapper; } diff --git a/src/main/java/com/cleanroommc/modularui/holoui/Plane3D.java b/src/main/java/com/cleanroommc/modularui/holoui/Plane3D.java index 4725ba5e..eeb7514d 100644 --- a/src/main/java/com/cleanroommc/modularui/holoui/Plane3D.java +++ b/src/main/java/com/cleanroommc/modularui/holoui/Plane3D.java @@ -1,9 +1,12 @@ package com.cleanroommc.modularui.holoui; import com.cleanroommc.modularui.utils.GuiUtils; -import org.jetbrains.annotations.ApiStatus; + +import com.cleanroommc.modularui.utils.Matrix4f; + import org.lwjgl.opengl.GL11; -import org.lwjgl.util.vector.Matrix4f; + +import org.jetbrains.annotations.ApiStatus; /** * Highly experimental diff --git a/src/main/java/com/cleanroommc/modularui/holoui/ScreenEntityRender.java b/src/main/java/com/cleanroommc/modularui/holoui/ScreenEntityRender.java index dfe5a498..d9ed6acd 100644 --- a/src/main/java/com/cleanroommc/modularui/holoui/ScreenEntityRender.java +++ b/src/main/java/com/cleanroommc/modularui/holoui/ScreenEntityRender.java @@ -1,6 +1,6 @@ package com.cleanroommc.modularui.holoui; -import com.cleanroommc.modularui.screen.GuiScreenWrapper; +import com.cleanroommc.modularui.screen.GuiContainerWrapper; import net.minecraft.client.Minecraft; import net.minecraft.client.renderer.entity.Render; import net.minecraft.entity.Entity; @@ -28,7 +28,7 @@ protected ResourceLocation getEntityTexture(Entity entity) { @Override public void doRender(@NotNull Entity e, double x, double y, double z, float entityYaw, float partialTicks) { HoloScreenEntity entity = (HoloScreenEntity) e; - GuiScreenWrapper screenWrapper = entity.getWrapper(); + GuiContainerWrapper screenWrapper = entity.getWrapper(); if (screenWrapper == null) return; Plane3D plane3D = entity.getPlane3D(); diff --git a/src/main/java/com/cleanroommc/modularui/integration/nei/ModularUIContainerInputHandler.java b/src/main/java/com/cleanroommc/modularui/integration/nei/ModularUIContainerInputHandler.java new file mode 100644 index 00000000..0d99ca7a --- /dev/null +++ b/src/main/java/com/cleanroommc/modularui/integration/nei/ModularUIContainerInputHandler.java @@ -0,0 +1,52 @@ +package com.cleanroommc.modularui.integration.nei; + +import codechicken.nei.guihook.IContainerInputHandler; + +import com.cleanroommc.modularui.screen.ClientScreenHandler; + +import net.minecraft.client.gui.inventory.GuiContainer; + +import java.io.IOException; + +public class ModularUIContainerInputHandler implements IContainerInputHandler { + + @Override + public boolean keyTyped(GuiContainer gui, char keyChar, int keyCode) { + // This input handler must be after LayoutManager but not in lastKeyTyped because it cannot handle esc key + try { + return ClientScreenHandler.handleKeyboardInput(ClientScreenHandler.getMuiScreen(), gui); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + @Override + public void onKeyTyped(GuiContainer gui, char keyChar, int keyID) {} + + @Override + public boolean lastKeyTyped(GuiContainer gui, char keyChar, int keyID) { + return false; + } + + @Override + public boolean mouseClicked(GuiContainer gui, int mousex, int mousey, int button) { + return false; + } + + @Override + public void onMouseClicked(GuiContainer gui, int mousex, int mousey, int button) {} + + @Override + public void onMouseUp(GuiContainer gui, int mousex, int mousey, int button) {} + + @Override + public boolean mouseScrolled(GuiContainer gui, int mousex, int mousey, int scrolled) { + return false; + } + + @Override + public void onMouseScrolled(GuiContainer gui, int mousex, int mousey, int scrolled) {} + + @Override + public void onMouseDragged(GuiContainer gui, int mousex, int mousey, int button, long heldTime) {} +} diff --git a/src/main/java/com/cleanroommc/modularui/integration/nei/ModularUIContainerObjectHandler.java b/src/main/java/com/cleanroommc/modularui/integration/nei/ModularUIContainerObjectHandler.java index ad501418..699de898 100644 --- a/src/main/java/com/cleanroommc/modularui/integration/nei/ModularUIContainerObjectHandler.java +++ b/src/main/java/com/cleanroommc/modularui/integration/nei/ModularUIContainerObjectHandler.java @@ -2,7 +2,7 @@ import codechicken.nei.guihook.IContainerObjectHandler; import com.cleanroommc.modularui.api.widget.IGuiElement; -import com.cleanroommc.modularui.screen.GuiScreenWrapper; +import com.cleanroommc.modularui.screen.GuiContainerWrapper; import net.minecraft.client.gui.inventory.GuiContainer; import net.minecraft.item.ItemStack; @@ -19,8 +19,8 @@ public void load(GuiContainer gui) {} @Override public ItemStack getStackUnderMouse(GuiContainer gui, int mousex, int mousey) { - if (gui instanceof GuiScreenWrapper) { - IGuiElement hovered = ((GuiScreenWrapper) gui).getScreen().getContext().getHovered(); + if (gui instanceof GuiContainerWrapper) { + IGuiElement hovered = ((GuiContainerWrapper) gui).getScreen().getContext().getHovered(); if (hovered instanceof NEIIngredientProvider) { return ((NEIIngredientProvider) hovered).getStackForNEI(); } diff --git a/src/main/java/com/cleanroommc/modularui/integration/nei/ModularUINEIGuiHandler.java b/src/main/java/com/cleanroommc/modularui/integration/nei/ModularUINEIGuiHandler.java new file mode 100644 index 00000000..66ea3d1f --- /dev/null +++ b/src/main/java/com/cleanroommc/modularui/integration/nei/ModularUINEIGuiHandler.java @@ -0,0 +1,43 @@ +package com.cleanroommc.modularui.integration.nei; + +import codechicken.nei.NEIClientUtils; +import codechicken.nei.api.INEIGuiAdapter; + +import com.cleanroommc.modularui.api.widget.IGuiElement; +import com.cleanroommc.modularui.screen.GuiContainerWrapper; + +import com.cleanroommc.modularui.screen.ModularScreen; + +import net.minecraft.client.gui.inventory.GuiContainer; +import net.minecraft.item.ItemStack; + +import java.awt.*; + +public class ModularUINEIGuiHandler extends INEIGuiAdapter { + + @Override + public boolean handleDragNDrop(GuiContainer gui, int mousex, int mousey, ItemStack draggedStack, int button) { + if (!(gui instanceof GuiContainerWrapper guiContainer) || NEIClientUtils.getHeldItem() != null) { + return false; + } + IGuiElement hovered = guiContainer.getScreen().getContext().getHovered(); + if (hovered instanceof NEIDragAndDropHandler) { + return ((NEIDragAndDropHandler) hovered).handleDragAndDrop(draggedStack, button); + } + return false; + } + + @Override + public boolean hideItemPanelSlot(GuiContainer gui, int x, int y, int w, int h) { + if (!(gui instanceof GuiContainerWrapper guiContainer)) { + return false; + } + ModularScreen screen = guiContainer.getScreen(); + if (!screen.getContext().getNEISettings().isNEIEnabled(screen)) { + return false; + } + return screen.getContext().getNEISettings().getAllNEIExclusionAreas().stream().anyMatch( + a -> a.intersects(new Rectangle(x, y, w, h)) + ); + } +} diff --git a/src/main/java/com/cleanroommc/modularui/integration/nei/NEIModularUIConfig.java b/src/main/java/com/cleanroommc/modularui/integration/nei/NEIModularUIConfig.java new file mode 100644 index 00000000..aa4491c4 --- /dev/null +++ b/src/main/java/com/cleanroommc/modularui/integration/nei/NEIModularUIConfig.java @@ -0,0 +1,26 @@ +package com.cleanroommc.modularui.integration.nei; + +import codechicken.nei.api.API; +import codechicken.nei.api.IConfigureNEI; +import codechicken.nei.guihook.GuiContainerManager; + +@SuppressWarnings("unused") +public class NEIModularUIConfig implements IConfigureNEI { + + @Override + public void loadConfig() { + GuiContainerManager.addInputHandler(new ModularUIContainerInputHandler()); + GuiContainerManager.addObjectHandler(new ModularUIContainerObjectHandler()); + API.registerNEIGuiHandler(new ModularUINEIGuiHandler()); + } + + @Override + public String getName() { + return "ModularUI NEI integration"; + } + + @Override + public String getVersion() { + return ""; + } +} diff --git a/src/main/java/com/cleanroommc/modularui/integration/nei/NEIState.java b/src/main/java/com/cleanroommc/modularui/integration/nei/NEIState.java index 7f2e77f6..c904f679 100644 --- a/src/main/java/com/cleanroommc/modularui/integration/nei/NEIState.java +++ b/src/main/java/com/cleanroommc/modularui/integration/nei/NEIState.java @@ -21,7 +21,7 @@ public boolean test(ModularScreen screen) { DEFAULT { @Override public boolean test(ModularScreen screen) { - return !screen.getContainer().isClientOnly(); + return !screen.isClientOnly(); } } } diff --git a/src/main/java/com/cleanroommc/modularui/mixinplugin/Mixins.java b/src/main/java/com/cleanroommc/modularui/mixinplugin/Mixins.java index 3bb07aca..47abfcfb 100644 --- a/src/main/java/com/cleanroommc/modularui/mixinplugin/Mixins.java +++ b/src/main/java/com/cleanroommc/modularui/mixinplugin/Mixins.java @@ -14,8 +14,12 @@ public enum Mixins { // Vanilla client EntityAccessor("minecraft.EntityAccessor", Phase.EARLY, Side.CLIENT, VANILLA), ForgeHooksClientMixin("forge.ForgeHooksClientMixin", Phase.EARLY, Side.CLIENT, VANILLA), + GuiAccessor("minecraft.GuiAccessor", Phase.EARLY, Side.CLIENT, VANILLA), + GuiButtonMixin("minecraft.GuiButtonMixin", Phase.EARLY, Side.CLIENT, VANILLA), GuiContainerAccessor("minecraft.GuiContainerAccessor", Phase.EARLY, Side.CLIENT, VANILLA), GuiContainerMixin("minecraft.GuiContainerMixin", Phase.EARLY, Side.CLIENT, VANILLA), + GuiScreenAccessor("minecraft.GuiScreenAccessor", Phase.EARLY, Side.CLIENT, VANILLA), + GuiScreenMixin("minecraft.GuiScreenMixin", Phase.EARLY, Side.CLIENT, VANILLA), MinecraftMixin("minecraft.MinecraftMixin", Phase.EARLY, Side.CLIENT, VANILLA), SimpleResourceAccessor("minecraft.SimpleResourceAccessor", Phase.EARLY, Side.CLIENT, VANILLA), diff --git a/src/main/java/com/cleanroommc/modularui/mixins/early/minecraft/GuiAccessor.java b/src/main/java/com/cleanroommc/modularui/mixins/early/minecraft/GuiAccessor.java new file mode 100644 index 00000000..24c3b60d --- /dev/null +++ b/src/main/java/com/cleanroommc/modularui/mixins/early/minecraft/GuiAccessor.java @@ -0,0 +1,16 @@ +package com.cleanroommc.modularui.mixins.early.minecraft; + +import net.minecraft.client.gui.Gui; + +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +@Mixin(Gui.class) +public interface GuiAccessor { + + @Accessor + float getZLevel(); + + @Accessor + void setZLevel(float z); +} diff --git a/src/main/java/com/cleanroommc/modularui/mixins/early/minecraft/GuiButtonMixin.java b/src/main/java/com/cleanroommc/modularui/mixins/early/minecraft/GuiButtonMixin.java new file mode 100644 index 00000000..cb5466c1 --- /dev/null +++ b/src/main/java/com/cleanroommc/modularui/mixins/early/minecraft/GuiButtonMixin.java @@ -0,0 +1,29 @@ +package com.cleanroommc.modularui.mixins.early.minecraft; + +import com.cleanroommc.modularui.overlay.OverlayStack; + +import net.minecraft.client.gui.GuiButton; + +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Redirect; + +/** + * This mixin fixes some visual bugs that can happen with overlays. + */ +@Mixin(GuiButton.class) +public abstract class GuiButtonMixin { + + @Shadow protected boolean field_146123_n; // hovered + + @Shadow + public abstract int getHoverState(boolean mouseOver); + + @Redirect(method = "drawButton", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/GuiButton;getHoverState(Z)I")) + public int draw(GuiButton instance, boolean mouseOver) { + // fixes buttons being hovered when an overlay element is already hovered + if (this.field_146123_n) this.field_146123_n = !OverlayStack.isHoveringOverlay(); + return getHoverState(this.field_146123_n); + } +} diff --git a/src/main/java/com/cleanroommc/modularui/mixins/early/minecraft/GuiContainerAccessor.java b/src/main/java/com/cleanroommc/modularui/mixins/early/minecraft/GuiContainerAccessor.java index 771aa884..ed6baec2 100644 --- a/src/main/java/com/cleanroommc/modularui/mixins/early/minecraft/GuiContainerAccessor.java +++ b/src/main/java/com/cleanroommc/modularui/mixins/early/minecraft/GuiContainerAccessor.java @@ -12,6 +12,30 @@ @Mixin(GuiContainer.class) public interface GuiContainerAccessor { + @Accessor + void setXSize(int v); + + @Accessor + int getXSize(); + + @Accessor + void setYSize(int v); + + @Accessor + int getYSize(); + + @Accessor + void setGuiLeft(int v); + + @Accessor + int getGuiLeft(); + + @Accessor + void setGuiTop(int v); + + @Accessor + int getGuiTop(); + @Accessor("theSlot") void setHoveredSlot(Slot slot); @@ -27,15 +51,18 @@ public interface GuiContainerAccessor { @Accessor boolean getIsRightMouseClick(); + @Accessor("field_147007_t") + boolean getDragSplitting(); + + @Accessor("field_147008_s") + Set getDragSplittingSlots(); + @Accessor("field_146987_F") int getDragSplittingLimit(); @Invoker("func_146980_g") void invokeUpdateDragSplitting(); - @Accessor("field_147008_s") - Set getDragSplittingSlots(); - @Accessor("field_147007_t") boolean isDragSplittingInternal(); @@ -59,4 +86,10 @@ public interface GuiContainerAccessor { @Accessor long getReturningStackTime(); + + @Invoker + void invokeDrawGuiContainerForegroundLayer(int mouseX, int mouseY); + + @Invoker + void invokeDrawGuiContainerBackgroundLayer(float partialTicks, int mouseX, int mouseY); } diff --git a/src/main/java/com/cleanroommc/modularui/mixins/early/minecraft/GuiContainerMixin.java b/src/main/java/com/cleanroommc/modularui/mixins/early/minecraft/GuiContainerMixin.java index 4d0194dc..6e248475 100644 --- a/src/main/java/com/cleanroommc/modularui/mixins/early/minecraft/GuiContainerMixin.java +++ b/src/main/java/com/cleanroommc/modularui/mixins/early/minecraft/GuiContainerMixin.java @@ -1,12 +1,17 @@ package com.cleanroommc.modularui.mixins.early.minecraft; -import com.cleanroommc.modularui.screen.GuiScreenWrapper; +import com.cleanroommc.modularui.screen.GuiContainerWrapper; + +import com.llamalad7.mixinextras.sugar.Local; + import net.minecraft.client.gui.inventory.GuiContainer; import net.minecraft.inventory.Slot; + import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.ModifyVariable; import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; @Mixin(GuiContainer.class) @@ -22,9 +27,24 @@ public class GuiContainerMixin { */ @Inject(method = "getSlotAtPosition", at = @At("HEAD"), cancellable = true) public void modularui$injectGetSlotAtPosition(int x, int y, CallbackInfoReturnable cir) { - //noinspection ConstantValue - if (((Object) this).getClass() == GuiScreenWrapper.class) { + if (((Object) this).getClass() == GuiContainerWrapper.class) { cir.setReturnValue(this.theSlot); } } + + // https://github.com/MinecraftForge/MinecraftForge/pull/2378 + + @ModifyVariable(method = "mouseClicked", + at = @At(value = "STORE"), + name = "flag1") + private boolean modularui$fixSlotClickOutsideBoundaryOnMouseClick(boolean flag1, @Local(name = "slot") Slot slot) { + return flag1 && slot == null; + } + + @ModifyVariable(method = "mouseMovedOrUp", + at = @At(value = "STORE"), + name = "flag") + private boolean modularui$fixSlotClickOutsideBoundaryOnMouseRelease(boolean flag, @Local(name = "slot") Slot slot) { + return flag && slot == null; + } } diff --git a/src/main/java/com/cleanroommc/modularui/mixins/early/minecraft/GuiScreenAccessor.java b/src/main/java/com/cleanroommc/modularui/mixins/early/minecraft/GuiScreenAccessor.java new file mode 100644 index 00000000..3f09f3a1 --- /dev/null +++ b/src/main/java/com/cleanroommc/modularui/mixins/early/minecraft/GuiScreenAccessor.java @@ -0,0 +1,61 @@ +package com.cleanroommc.modularui.mixins.early.minecraft; + +import net.minecraft.client.gui.FontRenderer; +import net.minecraft.client.gui.GuiButton; +import net.minecraft.client.gui.GuiLabel; +import net.minecraft.client.gui.GuiScreen; + +import net.minecraft.client.renderer.entity.RenderItem; + +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; +import org.spongepowered.asm.mixin.gen.Invoker; + +import java.io.IOException; +import java.util.List; + +@Mixin(GuiScreen.class) +public interface GuiScreenAccessor { + + @Accessor("field_146298_h") + int getTouchValue(); + + @Accessor("field_146298_h") + void setTouchValue(int value); + + @Accessor + int getEventButton(); + + @Accessor + void setEventButton(int button); + + @Accessor + long getLastMouseEvent(); + + @Accessor + void setLastMouseEvent(long event); + + @Accessor + RenderItem getItemRender(); + + @Accessor("fontRendererObj") + FontRenderer getFontRenderer(); + + @Accessor + List getButtonList(); + + @Accessor + List getLabelList(); + + @Invoker + void invokeKeyTyped(char typedChar, int keyCode) throws IOException; + + @Invoker + void invokeMouseClicked(int mouseX, int mouseY, int mouseButton) throws IOException; + + @Invoker("mouseMovedOrUp") + void invokeMouseReleased(int mouseX, int mouseY, int state); + + @Invoker + void invokeMouseClickMove(int mouseX, int mouseY, int clickedMouseButton, long timeSinceLastClick); +} diff --git a/src/main/java/com/cleanroommc/modularui/mixins/early/minecraft/GuiScreenMixin.java b/src/main/java/com/cleanroommc/modularui/mixins/early/minecraft/GuiScreenMixin.java new file mode 100644 index 00000000..7c10ea64 --- /dev/null +++ b/src/main/java/com/cleanroommc/modularui/mixins/early/minecraft/GuiScreenMixin.java @@ -0,0 +1,57 @@ +package com.cleanroommc.modularui.mixins.early.minecraft; + +import com.cleanroommc.modularui.api.event.KeyboardInputEvent; +import com.cleanroommc.modularui.api.event.MouseInputEvent; + +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.GuiScreen; + +import net.minecraftforge.common.MinecraftForge; + +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.Unique; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Redirect; + +@Mixin(GuiScreen.class) +public abstract class GuiScreenMixin { + + @Shadow + public Minecraft mc; + + @Shadow + public abstract void handleMouseInput(); + + @Shadow + public abstract void handleKeyboardInput(); + + @Redirect(method = "handleInput", + at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/GuiScreen;handleMouseInput()V")) + public void modularui$injectMouseInputEvent(GuiScreen instance) { + if (MinecraftForge.EVENT_BUS.post(new MouseInputEvent.Pre(modularui$getThis()))) { + return; + } + this.handleMouseInput(); + if (modularui$getThis().equals(this.mc.currentScreen)) { + MinecraftForge.EVENT_BUS.post(new MouseInputEvent.Post(modularui$getThis())); + } + } + + @Redirect(method = "handleInput", + at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/GuiScreen;handleKeyboardInput()V")) + public void modularui$injectKeyboardInputEvent(GuiScreen instance) { + if (MinecraftForge.EVENT_BUS.post(new KeyboardInputEvent.Pre(modularui$getThis()))) { + return; + } + this.handleKeyboardInput(); + if (modularui$getThis().equals(this.mc.currentScreen)) { + MinecraftForge.EVENT_BUS.post(new KeyboardInputEvent.Post(modularui$getThis())); + } + } + + @Unique + private GuiScreen modularui$getThis() { + return (GuiScreen) (Object) this; + } +} diff --git a/src/main/java/com/cleanroommc/modularui/mixins/early/minecraft/MinecraftMixin.java b/src/main/java/com/cleanroommc/modularui/mixins/early/minecraft/MinecraftMixin.java index e35fd9a6..0a596397 100644 --- a/src/main/java/com/cleanroommc/modularui/mixins/early/minecraft/MinecraftMixin.java +++ b/src/main/java/com/cleanroommc/modularui/mixins/early/minecraft/MinecraftMixin.java @@ -1,8 +1,7 @@ package com.cleanroommc.modularui.mixins.early.minecraft; import com.cleanroommc.modularui.ModularUI; -import com.cleanroommc.modularui.screen.ModularScreen; -import com.cleanroommc.modularui.utils.Animator; +import com.cleanroommc.modularui.screen.ClientScreenHandler; import net.minecraft.client.Minecraft; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; @@ -22,12 +21,8 @@ public class MinecraftMixin { public void modularui$onRender(CallbackInfo ci) { if (ModularUI.proxy == null || ModularUI.proxy.getTimer60Fps() == null) return; ModularUI.proxy.getTimer60Fps().updateTimer(); - ModularScreen screen = ModularScreen.getCurrent(); - if (screen != null) { - for (int j = 0; j < Math.min(20, ModularUI.proxy.getTimer60Fps().elapsedTicks); ++j) { - screen.onFrameUpdate(); - Animator.advance(); - } + for (int j = 0; j < Math.min(20, ModularUI.proxy.getTimer60Fps().elapsedTicks); ++j) { + ClientScreenHandler.onFrameUpdate(); } } } diff --git a/src/main/java/com/cleanroommc/modularui/overlay/OverlayHandler.java b/src/main/java/com/cleanroommc/modularui/overlay/OverlayHandler.java new file mode 100644 index 00000000..0b988e74 --- /dev/null +++ b/src/main/java/com/cleanroommc/modularui/overlay/OverlayHandler.java @@ -0,0 +1,42 @@ +package com.cleanroommc.modularui.overlay; + +import com.cleanroommc.modularui.screen.ModularScreen; + +import net.minecraft.client.gui.GuiScreen; + +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.NotNull; + +import java.util.function.Function; +import java.util.function.Predicate; + +@ApiStatus.Experimental +public class OverlayHandler implements Comparable { + + private final Predicate test; + private final Function overlayFunction; + private final int priority; + + public OverlayHandler(Predicate test, Function overlayFunction) { + this(test, overlayFunction, 1000); + } + + public OverlayHandler(Predicate test, Function overlayFunction, int priority) { + this.test = test; + this.overlayFunction = overlayFunction; + this.priority = priority; + } + + public boolean isValidFor(GuiScreen screen) { + return this.test.test(screen); + } + + public ModularScreen createOverlay(GuiScreen screen) { + return this.overlayFunction.apply(screen); + } + + @Override + public int compareTo(@NotNull OverlayHandler o) { + return Integer.compare(this.priority, o.priority); + } +} diff --git a/src/main/java/com/cleanroommc/modularui/overlay/OverlayManager.java b/src/main/java/com/cleanroommc/modularui/overlay/OverlayManager.java new file mode 100644 index 00000000..3109da73 --- /dev/null +++ b/src/main/java/com/cleanroommc/modularui/overlay/OverlayManager.java @@ -0,0 +1,43 @@ +package com.cleanroommc.modularui.overlay; + +import com.cleanroommc.modularui.screen.ModularScreen; + +import cpw.mods.fml.common.eventhandler.EventPriority; +import cpw.mods.fml.common.eventhandler.SubscribeEvent; + +import net.minecraft.client.Minecraft; +import net.minecraftforge.client.event.GuiOpenEvent; + +import org.jetbrains.annotations.ApiStatus; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +@ApiStatus.Experimental +public class OverlayManager { + + public static final List overlays = new ArrayList<>(); + + public static void register(OverlayHandler handler) { + if (!overlays.contains(handler)) { + overlays.add(handler); + overlays.sort(OverlayHandler::compareTo); + } + } + + @SubscribeEvent(priority = EventPriority.LOWEST) + public void onGuiOpen(GuiOpenEvent event) { + if (event.gui != Minecraft.getMinecraft().currentScreen) { + OverlayStack.closeAll(); + if (event.gui == null) return; + for (OverlayHandler handler : overlays) { + if (handler.isValidFor(event.gui)) { + ModularScreen overlay = Objects.requireNonNull(handler.createOverlay(event.gui), "Overlays must not be null!"); + overlay.constructOverlay(event.gui); + OverlayStack.open(overlay); + } + } + } + } +} diff --git a/src/main/java/com/cleanroommc/modularui/overlay/OverlayStack.java b/src/main/java/com/cleanroommc/modularui/overlay/OverlayStack.java new file mode 100644 index 00000000..d1f07222 --- /dev/null +++ b/src/main/java/com/cleanroommc/modularui/overlay/OverlayStack.java @@ -0,0 +1,114 @@ +package com.cleanroommc.modularui.overlay; + +import com.cleanroommc.modularui.api.widget.IGuiElement; +import com.cleanroommc.modularui.screen.ClientScreenHandler; +import com.cleanroommc.modularui.screen.ModularScreen; + +import net.minecraft.client.renderer.RenderHelper; + +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.Nullable; +import org.lwjgl.opengl.GL11; +import org.lwjgl.opengl.GL12; + +import java.util.ArrayList; +import java.util.List; +import java.util.function.Consumer; +import java.util.function.Predicate; + +@ApiStatus.Experimental +public class OverlayStack { + + private static final List overlay = new ArrayList<>(); + + public static void foreach(Consumer function, boolean topToBottom) { + if (topToBottom) { + for (int i = overlay.size() - 1; i >= 0; i--) { + function.accept(overlay.get(i)); + } + } else { + for (ModularScreen screen : overlay) { + function.accept(screen); + } + } + } + + public static boolean interact(Predicate function, boolean topToBottom) { + if (topToBottom) { + for (int i = overlay.size() - 1; i >= 0; i--) { + if (function.test(overlay.get(i))) { + return true; + } + } + } else { + for (ModularScreen screen : overlay) { + if (function.test(screen)) { + return true; + } + } + } + return false; + } + + public static void draw(int mouseX, int mouseY, float partialTicks) { + ModularScreen hovered = null; + ModularScreen fallback = null; + for (ModularScreen screen : overlay) { + GL11.glDisable(GL11.GL_DEPTH_TEST); + GL11.glDisable(GL11.GL_LIGHTING); + GL11.glEnable(GL11.GL_BLEND); + GL11.glColor4f(1f, 1f, 1f, 1f); + screen.drawScreen(mouseX, mouseY, partialTicks); + GL11.glColor4f(1f, 1f, 1f, 1f); + screen.drawForeground(partialTicks); + if (screen.getContext().getHovered() != null) hovered = screen; + fallback = screen; + } + ClientScreenHandler.drawDebugScreen(hovered, fallback); + GL11.glEnable(GL11.GL_LIGHTING); + GL11.glEnable(GL11.GL_DEPTH_TEST); + GL11.glEnable(GL12.GL_RESCALE_NORMAL); + RenderHelper.enableStandardItemLighting(); + } + + public static void open(ModularScreen screen) { + int i = overlay.indexOf(screen); + if (i >= 0 && i < overlay.size() - 1) { + overlay.remove(i); + } + overlay.add(screen); + screen.onOpen(); + } + + public static void close(ModularScreen screen) { + if (overlay.remove(screen)) { + screen.onCloseParent(); + } + } + + static void closeAll() { + for (int i = overlay.size() - 1; i >= 0; i--) { + ModularScreen screen = overlay.remove(i); + screen.onCloseParent(); + } + } + + public static void onTick() { + foreach(ModularScreen::onUpdate, true); + } + + @Nullable + public static IGuiElement getHoveredElement() { + for (int i = overlay.size() - 1; i >= 0; i--) { + ModularScreen screen = overlay.get(i); + IGuiElement hovered = screen.getContext().getHovered(); + if (hovered == null) continue; + return hovered; + } + return null; + } + + public static boolean isHoveringOverlay() { + return getHoveredElement() != null; + } +} diff --git a/src/main/java/com/cleanroommc/modularui/overlay/ScreenWrapper.java b/src/main/java/com/cleanroommc/modularui/overlay/ScreenWrapper.java new file mode 100644 index 00000000..01ac4cbf --- /dev/null +++ b/src/main/java/com/cleanroommc/modularui/overlay/ScreenWrapper.java @@ -0,0 +1,41 @@ +package com.cleanroommc.modularui.overlay; + +import com.cleanroommc.modularui.api.IMuiScreen; +import com.cleanroommc.modularui.screen.ModularScreen; + +import net.minecraft.client.gui.GuiScreen; + +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.NotNull; + +import java.awt.*; + +/** + * Wraps the current gui screen and uses it for overlays. + */ +@ApiStatus.Experimental +public class ScreenWrapper implements IMuiScreen { + + private final GuiScreen guiScreen; + private final ModularScreen screen; + + public ScreenWrapper(GuiScreen guiScreen, ModularScreen screen) { + this.guiScreen = guiScreen; + this.screen = screen; + } + + @Override + public @NotNull ModularScreen getScreen() { + return screen; + } + + @Override + public GuiScreen getGuiScreen() { + return guiScreen; + } + + @Override + public void updateGuiArea(Rectangle area) { + // overlay should not modify screen + } +} diff --git a/src/main/java/com/cleanroommc/modularui/screen/ClientScreenHandler.java b/src/main/java/com/cleanroommc/modularui/screen/ClientScreenHandler.java new file mode 100644 index 00000000..43ca2b9b --- /dev/null +++ b/src/main/java/com/cleanroommc/modularui/screen/ClientScreenHandler.java @@ -0,0 +1,540 @@ +package com.cleanroommc.modularui.screen; + +import codechicken.nei.guihook.GuiContainerManager; +import codechicken.nei.guihook.IContainerDrawHandler; + +import com.cleanroommc.modularui.ModularUIConfig; +import com.cleanroommc.modularui.api.IMuiScreen; +import com.cleanroommc.modularui.api.event.KeyboardInputEvent; +import com.cleanroommc.modularui.api.event.MouseInputEvent; +import com.cleanroommc.modularui.api.widget.IGuiElement; +import com.cleanroommc.modularui.api.widget.IVanillaSlot; +import com.cleanroommc.modularui.api.widget.Interactable; +import com.cleanroommc.modularui.mixins.early.minecraft.GuiAccessor; +import com.cleanroommc.modularui.mixins.early.minecraft.GuiContainerAccessor; +import com.cleanroommc.modularui.mixins.early.minecraft.GuiScreenAccessor; +import com.cleanroommc.modularui.drawable.GuiDraw; +import com.cleanroommc.modularui.drawable.Stencil; +import com.cleanroommc.modularui.overlay.OverlayStack; +import com.cleanroommc.modularui.screen.viewport.GuiContext; +import com.cleanroommc.modularui.screen.viewport.LocatedWidget; +import com.cleanroommc.modularui.utils.Animator; +import com.cleanroommc.modularui.utils.Color; +import com.cleanroommc.modularui.utils.FpsCounter; +import com.cleanroommc.modularui.widget.sizer.Area; +import com.cleanroommc.modularui.widgets.ItemSlot; +import com.cleanroommc.modularui.widgets.slot.ModularSlot; +import com.cleanroommc.modularui.widgets.slot.SlotGroup; + +import cpw.mods.fml.common.eventhandler.EventPriority; +import cpw.mods.fml.common.eventhandler.SubscribeEvent; + +import cpw.mods.fml.common.gameevent.TickEvent; + +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.FontRenderer; +import net.minecraft.client.gui.GuiButton; +import net.minecraft.client.gui.GuiLabel; +import net.minecraft.client.gui.GuiScreen; +import net.minecraft.client.gui.inventory.GuiContainer; +import net.minecraft.client.renderer.OpenGlHelper; +import net.minecraft.client.renderer.RenderHelper; +import net.minecraft.client.settings.GameSettings; +import net.minecraft.entity.player.InventoryPlayer; +import net.minecraft.item.ItemStack; +import net.minecraft.util.EnumChatFormatting; +import net.minecraft.util.MathHelper; +import net.minecraftforge.client.event.GuiOpenEvent; +import net.minecraftforge.client.event.GuiScreenEvent; + +import org.jetbrains.annotations.Nullable; +import org.lwjgl.input.Keyboard; +import org.lwjgl.input.Mouse; +import org.lwjgl.opengl.GL11; +import org.lwjgl.opengl.GL12; + +import java.awt.*; +import java.io.IOException; +import java.util.Objects; +import java.util.function.Predicate; + +public class ClientScreenHandler { + + private static ModularScreen currentScreen = null; + private static Character lastChar = null; + private static final FpsCounter fpsCounter = new FpsCounter(); + + @SubscribeEvent + public void onGuiOpen(GuiOpenEvent event) { + if (event.gui instanceof IMuiScreen muiScreen) { + Objects.requireNonNull(muiScreen.getScreen(), "ModularScreen must not be null!"); + if (currentScreen != muiScreen.getScreen()) { + if (hasScreen()) { + currentScreen.onCloseParent(); + currentScreen = null; + lastChar = null; + } + currentScreen = muiScreen.getScreen(); + fpsCounter.reset(); + } + } else if (hasScreen() && getMCScreen() != null && event.gui != getMCScreen()) { + currentScreen.onCloseParent(); + currentScreen = null; + lastChar = null; + } + } + + @SubscribeEvent + public void onGuiInit(GuiScreenEvent.InitGuiEvent.Post event) { + if (checkGui(event.gui)) { + currentScreen.onResize(event.gui.width, event.gui.height); + } + OverlayStack.foreach(ms -> ms.onResize(event.gui.width, event.gui.height), false); + } + + @SubscribeEvent(priority = EventPriority.LOW) + public void onGuiInputLow(KeyboardInputEvent.Pre event) throws IOException { + checkGui(event.gui); + // 1.7.10: NEI wants to process keyboard input first e.g. search bar + if (!(event.gui instanceof GuiContainer) && handleKeyboardInput(currentScreen, event.gui)) { + event.setCanceled(true); + } + } + + @SubscribeEvent(priority = EventPriority.LOW) + public void onGuiInputLow(MouseInputEvent.Pre event) throws IOException { + if (checkGui(event.gui)) { + currentScreen.getContext().updateEventState(); + } + // 1.7.10: In contrast to keyboard, MUI should process click first, otherwise ItemSlot causes infinite recursion + if (handleMouseInput(Mouse.getEventButton(), currentScreen, event.gui)) { + event.setCanceled(true); + } + } + + @SubscribeEvent + public void onScroll(MouseInputEvent.Pre event) { + int w = Mouse.getEventDWheel(); + if (w == 0) return; + ModularScreen.UpOrDown upOrDown = w > 0 ? ModularScreen.UpOrDown.UP : ModularScreen.UpOrDown.DOWN; + checkGui(event.gui); + if (doAction(currentScreen, ms -> ms.onMouseScroll(upOrDown, Math.abs(w)))) { + event.setCanceled(true); + } + } + + @SubscribeEvent(priority = EventPriority.LOW) + public void onGuiDraw(GuiScreenEvent.DrawScreenEvent.Pre event) { + if (checkGui(event.gui)) { + drawScreen(currentScreen, currentScreen.getScreenWrapper().getGuiScreen(), event.mouseX, event.mouseY, event.renderPartialTicks); + event.setCanceled(true); + } + } + + @SubscribeEvent + public void onGuiDraw(GuiScreenEvent.DrawScreenEvent.Post event) { + OverlayStack.draw(event.mouseX, event.mouseY, event.renderPartialTicks); + } + + @SubscribeEvent + public void onTick(TickEvent.ClientTickEvent event) { + if (event.phase == TickEvent.Phase.START) { + OverlayStack.onTick(); + if (checkGui()) { + currentScreen.onUpdate(); + } + } + } + + public static void onFrameUpdate() { + OverlayStack.foreach(ModularScreen::onFrameUpdate, true); + if (currentScreen != null) currentScreen.onFrameUpdate(); + Animator.advance(); + } + + private static boolean doAction(@Nullable ModularScreen muiScreen, Predicate action) { + return OverlayStack.interact(action, true) || (muiScreen != null && action.test(muiScreen)); + } + + private static boolean handleMouseInput(int button, @Nullable ModularScreen muiScreen, GuiScreen mcScreen) throws IOException { + GameSettings gameSettings = Minecraft.getMinecraft().gameSettings; + GuiScreenAccessor acc = (GuiScreenAccessor) mcScreen; + if (Mouse.getEventButtonState()) { + if (gameSettings.touchscreen) { + int val = acc.getTouchValue(); + if (val > 0) { + // we will cancel the event now, so we have to set the value + // otherwise the screen will handle it + acc.setTouchValue(val + 1); + return true; + } + } + acc.setEventButton(button); + acc.setLastMouseEvent(Minecraft.getSystemTime()); + return doAction(muiScreen, ms -> ms.onMousePressed(button)); + } + if (button != -1) { + if (gameSettings.touchscreen) { + int val = acc.getTouchValue(); + if (val - 1 > 0) { + // we will cancel the event now, so we have to set the value + // otherwise the screen will handle it + acc.setTouchValue(val - 1); + return true; + } + } + acc.setEventButton(-1); + return doAction(muiScreen, ms -> ms.onMouseRelease(button)); + } + if (acc.getEventButton() != -1 && acc.getLastMouseEvent() > 0L) { + long l = Minecraft.getSystemTime() - acc.getLastMouseEvent(); + return doAction(muiScreen, ms -> ms.onMouseDrag(acc.getEventButton(), l)); + } + return false; + } + + /** + * This replicates vanilla behavior while also injecting custom behavior for consistency + */ + public static boolean handleKeyboardInput(@Nullable ModularScreen muiScreen, GuiScreen mcScreen) throws IOException { + char c0 = Keyboard.getEventCharacter(); + int key = Keyboard.getEventKey(); + boolean state = Keyboard.getEventKeyState(); + + if (state) { + lastChar = c0; + return doAction(muiScreen, ms -> ms.onKeyPressed(c0, key)) || keyTyped(mcScreen, c0, key); + } else { + // when the key is released, the event char is empty + if (doAction(muiScreen, ms -> ms.onKeyRelease(lastChar, key))) return true; + if (key == 0 && c0 >= ' ') { + return keyTyped(mcScreen, c0, key); + } + } + return false; + } + + private static boolean keyTyped(GuiScreen screen, char typedChar, int keyCode) throws IOException { + if (currentScreen == null) return false; + // debug mode C + CTRL + SHIFT + ALT + if (keyCode == 46 && GuiScreen.isCtrlKeyDown() && GuiScreen.isShiftKeyDown() && Interactable.hasAltDown()) { + ModularUIConfig.guiDebugMode = !ModularUIConfig.guiDebugMode; + return true; + } + if (keyCode == 1 || keyCode == Minecraft.getMinecraft().gameSettings.keyBindInventory.getKeyCode()) { + if (currentScreen.getContext().hasDraggable()) { + currentScreen.getContext().dropDraggable(); + } else { + currentScreen.getPanelManager().closeTopPanel(true); + } + return true; + } + return false; + } + + public static void dragSlot(long timeSinceLastClick) { + if (hasScreen() && getMCScreen() instanceof GuiScreenAccessor container) { + GuiContext ctx = currentScreen.getContext(); + container.invokeMouseClickMove(ctx.getAbsMouseX(), ctx.getAbsMouseY(), ctx.getMouseButton(), timeSinceLastClick); + } + } + + public static void clickSlot() { + if (hasScreen() && getMCScreen() instanceof GuiScreenAccessor screen) { + GuiContext ctx = currentScreen.getContext(); + try { + screen.invokeMouseClicked(ctx.getAbsMouseX(), ctx.getMouseY(), ctx.getMouseButton()); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + } + + public static void releaseSlot() { + if (hasScreen() && getMCScreen() instanceof GuiScreenAccessor screen) { + GuiContext ctx = currentScreen.getContext(); + screen.invokeMouseReleased(ctx.getAbsMouseX(), ctx.getAbsMouseY(), ctx.getMouseButton()); + } + } + + public static boolean shouldDrawWorldBackground() { + return Minecraft.getMinecraft().theWorld == null; + } + + public static void drawDarkBackground(GuiScreen screen, int tint) { + if (hasScreen()) { + float alpha = currentScreen.getMainPanel().getAlpha(); + // vanilla color values as hex + int color = 0x101010; + int startAlpha = 0xc0; + int endAlpha = 0xd0; + GuiDraw.drawVerticalGradientRect(0, 0, screen.width, screen.height, Color.withAlpha(color, (int) (startAlpha * alpha)), Color.withAlpha(color, (int) (endAlpha * alpha))); + } + } + + public static void drawScreen(ModularScreen muiScreen, GuiScreen mcScreen, int mouseX, int mouseY, float partialTicks) { + if (mcScreen instanceof GuiContainer container) { + drawContainer(muiScreen, container, mouseX, mouseY, partialTicks); + } else { + drawScreenInternal(muiScreen, mcScreen, mouseX, mouseY, partialTicks); + } + } + + public static void drawScreenInternal(ModularScreen muiScreen, GuiScreen mcScreen, int mouseX, int mouseY, float partialTicks) { + Stencil.reset(); + Stencil.apply(muiScreen.getScreenArea(), null); + muiScreen.drawScreen(mouseX, mouseY, partialTicks); + GL11.glDisable(GL11.GL_LIGHTING); + GL11.glDisable(GL11.GL_DEPTH_TEST); + drawVanillaElements(mcScreen, mouseX, mouseY, partialTicks); + GL11.glColor4f(1.0F, 1.0F, 1.0F, 1.0F); + GL11.glEnable(GL12.GL_RESCALE_NORMAL); + OpenGlHelper.setLightmapTextureCoords(OpenGlHelper.lightmapTexUnit, 240.0F, 240.0F); + RenderHelper.disableStandardItemLighting(); + muiScreen.drawForeground(partialTicks); + GL11.glEnable(GL11.GL_LIGHTING); + GL11.glEnable(GL11.GL_DEPTH_TEST); + GL11.glEnable(GL12.GL_RESCALE_NORMAL); + RenderHelper.enableStandardItemLighting(); + Stencil.remove(); + } + + public static void drawContainer(ModularScreen muiScreen, GuiContainer mcScreen, int mouseX, int mouseY, float partialTicks) { + fpsCounter.onDraw(); + GuiContainerAccessor acc = (GuiContainerAccessor) mcScreen; + + Stencil.reset(); + Stencil.apply(muiScreen.getScreenArea(), null); + mcScreen.drawDefaultBackground(); + int x = acc.getGuiLeft(); + int y = acc.getGuiTop(); + + acc.invokeDrawGuiContainerBackgroundLayer(partialTicks, mouseX, mouseY); + muiScreen.drawScreen(mouseX, mouseY, partialTicks); + + GL11.glDisable(GL11.GL_LIGHTING); + GL11.glDisable(GL11.GL_DEPTH_TEST); + // mainly for invtweaks compat + drawVanillaElements(mcScreen, mouseX, mouseY, partialTicks); + GL11.glPushMatrix(); + GL11.glColor4f(1.0F, 1.0F, 1.0F, 1.0F); + GL11.glEnable(GL12.GL_RESCALE_NORMAL); + acc.setHoveredSlot(null); + OpenGlHelper.setLightmapTextureCoords(OpenGlHelper.lightmapTexUnit, 240.0F, 240.0F); + GL11.glEnable(GL12.GL_RESCALE_NORMAL); + GL11.glColor4f(1.0F, 1.0F, 1.0F, 1.0F); + + RenderHelper.enableGUIStandardItemLighting(); + if (muiScreen.getContext().getNEISettings().isNEIEnabled(muiScreen)) { + // Copied from GuiContainerManager#renderObjects but without translation + for (IContainerDrawHandler drawHandler : GuiContainerManager.drawHandlers) { + drawHandler.renderObjects(mcScreen, mouseX, mouseY); + } + for (IContainerDrawHandler drawHandler : GuiContainerManager.drawHandlers) { + drawHandler.postRenderObjects(mcScreen, mouseX, mouseY); + } + +// if (!shouldRenderOurTooltip()) { + // nh todo? + if (true) { + GuiContainerManager.getManager().renderToolTips(mouseX, mouseY); + } + } + GL11.glDisable(GL11.GL_DEPTH_TEST); + GL11.glColor4f(1.0F, 1.0F, 1.0F, 1.0F); + + RenderHelper.disableStandardItemLighting(); + acc.invokeDrawGuiContainerForegroundLayer(mouseX, mouseY); + muiScreen.drawForeground(partialTicks); + RenderHelper.enableGUIStandardItemLighting(); + + acc.setHoveredSlot(null); + IGuiElement hovered = muiScreen.getContext().getHovered(); + if (hovered instanceof IVanillaSlot vanillaSlot) { + acc.setHoveredSlot(vanillaSlot.getVanillaSlot()); + } + + GL11.glColor4f(1.0F, 1.0F, 1.0F, 1.0F); + + InventoryPlayer inventoryplayer = Minecraft.getMinecraft().thePlayer.inventory; + ItemStack itemstack = acc.getDraggedStack() == null ? inventoryplayer.getItemStack() : acc.getDraggedStack(); + GL11.glTranslatef((float) x, (float) y, 0.0F); + if (itemstack != null) { + int k2 = acc.getDraggedStack() == null ? 8 : 16; + String s = null; + + if (acc.getDraggedStack() != null && acc.getIsRightMouseClick()) { + itemstack = itemstack.copy(); + itemstack.stackSize = MathHelper.ceiling_float_int((float) itemstack.stackSize / 2.0F); + } else if (acc.getDragSplitting() && acc.getDragSplittingSlots().size() > 1) { + itemstack = itemstack.copy(); + itemstack.stackSize = acc.getDragSplittingRemnant(); + + if (itemstack.stackSize < 1) { + s = EnumChatFormatting.YELLOW + "0"; + } + } + + drawItemStack(mcScreen, itemstack, mouseX - x - 8, mouseY - y - k2, s); + } + + if (acc.getReturningStack() != null) { + float f = (float) (Minecraft.getSystemTime() - acc.getReturningStackTime()) / 100.0F; + + if (f >= 1.0F) { + f = 1.0F; + acc.setReturningStack(null); + } + + int l2 = acc.getReturningStackDestSlot().xDisplayPosition - acc.getTouchUpX(); + int i3 = acc.getReturningStackDestSlot().yDisplayPosition - acc.getTouchUpY(); + int l1 = acc.getTouchUpX() + (int) ((float) l2 * f); + int i2 = acc.getTouchUpY() + (int) ((float) i3 * f); + drawItemStack(mcScreen, acc.getReturningStack(), l1, i2, null); + } + GL11.glPopMatrix(); + GL11.glEnable(GL11.GL_LIGHTING); + GL11.glEnable(GL11.GL_DEPTH_TEST); + GL11.glEnable(GL12.GL_RESCALE_NORMAL); + RenderHelper.enableStandardItemLighting(); + Stencil.remove(); + } + + private static void drawItemStack(GuiContainer mcScreen, ItemStack stack, int x, int y, String altText) { + GL11.glTranslatef(0.0F, 0.0F, 32.0F); + ((GuiAccessor) mcScreen).setZLevel(200f); + ((GuiScreenAccessor) mcScreen).getItemRender().zLevel = 200.0F; + FontRenderer font = stack.getItem().getFontRenderer(stack); + if (font == null) font = ((GuiScreenAccessor) mcScreen).getFontRenderer(); + GL11.glEnable(GL11.GL_DEPTH_TEST); + ((GuiScreenAccessor) mcScreen).getItemRender().renderItemAndEffectIntoGUI(font, Minecraft.getMinecraft().getTextureManager(), stack, x, y); + ((GuiScreenAccessor) mcScreen).getItemRender().renderItemOverlayIntoGUI(font, Minecraft.getMinecraft().getTextureManager(), stack, x, y - (((GuiContainerAccessor) mcScreen).getDraggedStack() == null ? 0 : 8), altText); + GL11.glDisable(GL11.GL_DEPTH_TEST); + ((GuiAccessor) mcScreen).setZLevel(0f); + ((GuiScreenAccessor) mcScreen).getItemRender().zLevel = 0.0F; + } + + private static void drawVanillaElements(GuiScreen mcScreen, int mouseX, int mouseY, float partialTicks) { + for (GuiButton guiButton : ((GuiScreenAccessor) mcScreen).getButtonList()) { + guiButton.drawButton(Minecraft.getMinecraft(), mouseX, mouseY); + } + for (GuiLabel guiLabel : ((GuiScreenAccessor) mcScreen).getLabelList()) { + guiLabel.func_146159_a(Minecraft.getMinecraft(), mouseX, mouseY); + } + } + + public static void drawDebugScreen(@Nullable ModularScreen muiScreen, @Nullable ModularScreen fallback) { + if (!ModularUIConfig.guiDebugMode) return; + if (muiScreen == null) { + if (checkGui()) { + muiScreen = currentScreen; + } else { + if (fallback == null) return; + muiScreen = fallback; + } + } + GL11.glDisable(GL11.GL_DEPTH_TEST); + GL11.glDisable(GL11.GL_LIGHTING); + GL11.glEnable(GL11.GL_BLEND); + + + GuiContext context = muiScreen.getContext(); + int mouseX = context.getAbsMouseX(), mouseY = context.getAbsMouseY(); + int screenH = muiScreen.getScreenArea().height; + int color = Color.rgb(180, 40, 115); + int lineY = screenH - 13 - (!context.getScreen().isOverlay() && context.getNEISettings().isNEIEnabled(muiScreen) ? 20 : 0); + Minecraft.getMinecraft().fontRenderer.drawStringWithShadow("Mouse Pos: " + mouseX + ", " + mouseY, 5, lineY, color); + lineY -= 11; + Minecraft.getMinecraft().fontRenderer.drawStringWithShadow("FPS: " + fpsCounter.getFps(), 5, screenH - 24, color); + LocatedWidget locatedHovered = muiScreen.getPanelManager().getTopWidgetLocated(true); + if (locatedHovered != null) { + drawSegmentLine(lineY -= 4, color); + lineY -= 10; + + IGuiElement hovered = locatedHovered.getElement(); + locatedHovered.applyMatrix(context); + GL11.glPushMatrix(); + context.applyToOpenGl(); + + Area area = hovered.getArea(); + IGuiElement parent = hovered.getParent(); + + GuiDraw.drawBorder(0, 0, area.width, area.height, color, 1f); + if (hovered.hasParent()) { + GuiDraw.drawBorder(-area.rx, -area.ry, parent.getArea().width, parent.getArea().height, Color.withAlpha(color, 0.3f), 1f); + } + GL11.glPopMatrix(); + locatedHovered.unapplyMatrix(context); + GuiDraw.drawText("Pos: " + area.x + ", " + area.y + " Rel: " + area.rx + ", " + area.ry, 5, lineY, 1, color, false); + lineY -= 11; + GuiDraw.drawText("Size: " + area.width + ", " + area.height, 5, lineY, 1, color, false); + lineY -= 11; + GuiDraw.drawText("Class: " + hovered, 5, lineY, 1, color, false); + if (hovered.hasParent()) { + drawSegmentLine(lineY -= 4, color); + lineY -= 10; + area = parent.getArea(); + GuiDraw.drawText("Parent size: " + area.width + ", " + area.height, 5, lineY, 1, color, false); + lineY -= 11; + GuiDraw.drawText("Parent: " + parent, 5, lineY, 1, color, false); + } + if (hovered instanceof ItemSlot slotWidget) { + drawSegmentLine(lineY -= 4, color); + lineY -= 10; + ModularSlot slot = slotWidget.getSlot(); + GuiDraw.drawText("Slot Index: " + slot.getSlotIndex(), 5, lineY, 1, color, false); + lineY -= 11; + GuiDraw.drawText("Slot Number: " + slot.slotNumber, 5, lineY, 1, color, false); + lineY -= 11; + if (slotWidget.isSynced()) { + SlotGroup slotGroup = slot.getSlotGroup(); + boolean allowShiftTransfer = slotGroup != null && slotGroup.allowShiftTransfer(); + GuiDraw.drawText("Shift-Click Priority: " + (allowShiftTransfer ? slotGroup.getShiftClickPriority() : "DISABLED"), 5, lineY, 1, color, false); + } + } + } + // dot at mouse pos + GuiDraw.drawRect(mouseX, mouseY, 1, 1, Color.withAlpha(Color.GREEN.main, 0.8f)); + GL11.glColor4f(1f, 1f, 1f, 1f); + } + + private static void drawSegmentLine(int y, int color) { + GuiDraw.drawRect(5, y, 140, 1, color); + } + + public static void updateGuiArea(GuiContainer container, Rectangle area) { + GuiContainerAccessor acc = (GuiContainerAccessor) container; + acc.setGuiLeft(area.x); + acc.setGuiTop(area.y); + acc.setXSize(area.width); + acc.setYSize(area.height); + } + + public static boolean hasScreen() { + return currentScreen != null; + } + + @Nullable + public static GuiScreen getMCScreen() { + return Minecraft.getMinecraft().currentScreen; + } + + @Nullable + public static ModularScreen getMuiScreen() { + return currentScreen; + } + + private static boolean checkGui() { + return checkGui(Minecraft.getMinecraft().currentScreen); + } + + private static boolean checkGui(GuiScreen screen) { + if (currentScreen == null || !(screen instanceof IMuiScreen muiScreen)) return false; + if (screen != Minecraft.getMinecraft().currentScreen || muiScreen.getScreen() != currentScreen) { + currentScreen = null; + lastChar = null; + return false; + } + return true; + } +} diff --git a/src/main/java/com/cleanroommc/modularui/screen/GuiContainerWrapper.java b/src/main/java/com/cleanroommc/modularui/screen/GuiContainerWrapper.java new file mode 100644 index 00000000..3b6ba7ec --- /dev/null +++ b/src/main/java/com/cleanroommc/modularui/screen/GuiContainerWrapper.java @@ -0,0 +1,36 @@ +package com.cleanroommc.modularui.screen; + +import com.cleanroommc.modularui.api.IMuiScreen; + +import cpw.mods.fml.relauncher.Side; +import cpw.mods.fml.relauncher.SideOnly; + +import net.minecraft.client.gui.inventory.GuiContainer; + +import org.jetbrains.annotations.NotNull; + +@SideOnly(Side.CLIENT) +public class GuiContainerWrapper extends GuiContainer implements IMuiScreen { + + private final ModularScreen screen; + + public GuiContainerWrapper(ModularContainer container, ModularScreen screen) { + super(container); + this.screen = screen; + this.screen.construct(this); + } + + @Override + public void drawWorldBackground(int tint) { + handleDrawBackground(tint, super::drawWorldBackground); + } + + @Override + protected void drawGuiContainerBackgroundLayer(float partialTicks, int mouseX, int mouseY) { + } + + @Override + public @NotNull ModularScreen getScreen() { + return this.screen; + } +} diff --git a/src/main/java/com/cleanroommc/modularui/screen/GuiScreenWrapper.java b/src/main/java/com/cleanroommc/modularui/screen/GuiScreenWrapper.java index ce5b0ec0..91119611 100644 --- a/src/main/java/com/cleanroommc/modularui/screen/GuiScreenWrapper.java +++ b/src/main/java/com/cleanroommc/modularui/screen/GuiScreenWrapper.java @@ -1,502 +1,31 @@ package com.cleanroommc.modularui.screen; -import codechicken.nei.NEIClientUtils; -import codechicken.nei.VisiblityData; -import codechicken.nei.api.INEIGuiHandler; -import codechicken.nei.api.TaggedInventoryArea; -import codechicken.nei.guihook.GuiContainerManager; -import codechicken.nei.guihook.IContainerDrawHandler; -import codechicken.nei.guihook.IContainerInputHandler; -import com.cleanroommc.modularui.GuiErrorHandler; -import com.cleanroommc.modularui.ModularUIConfig; -import com.cleanroommc.modularui.api.widget.IGuiElement; -import com.cleanroommc.modularui.api.widget.IVanillaSlot; -import com.cleanroommc.modularui.api.widget.Interactable; -import com.cleanroommc.modularui.drawable.GuiDraw; -import com.cleanroommc.modularui.drawable.Stencil; -import com.cleanroommc.modularui.integration.nei.NEIDragAndDropHandler; -import com.cleanroommc.modularui.mixins.early.minecraft.GuiContainerAccessor; -import com.cleanroommc.modularui.screen.viewport.GuiContext; -import com.cleanroommc.modularui.screen.viewport.LocatedWidget; -import com.cleanroommc.modularui.utils.Color; -import com.cleanroommc.modularui.widget.sizer.Area; -import com.cleanroommc.modularui.widgets.ItemSlot; -import com.cleanroommc.modularui.widgets.slot.ModularSlot; -import com.cleanroommc.modularui.widgets.slot.SlotGroup; +import com.cleanroommc.modularui.api.IMuiScreen; -import cpw.mods.fml.common.Optional; import cpw.mods.fml.relauncher.Side; import cpw.mods.fml.relauncher.SideOnly; -import net.minecraft.client.Minecraft; -import net.minecraft.client.gui.FontRenderer; -import net.minecraft.client.gui.GuiButton; -import net.minecraft.client.gui.GuiLabel; -import net.minecraft.client.gui.inventory.GuiContainer; -import net.minecraft.client.renderer.OpenGlHelper; -import net.minecraft.client.renderer.RenderHelper; -import net.minecraft.client.renderer.entity.RenderItem; -import net.minecraft.entity.player.InventoryPlayer; -import net.minecraft.inventory.Slot; -import net.minecraft.item.ItemStack; -import net.minecraft.util.EnumChatFormatting; -import net.minecraft.util.MathHelper; -import org.lwjgl.input.Keyboard; -import org.lwjgl.input.Mouse; -import org.lwjgl.opengl.GL11; -import org.lwjgl.opengl.GL12; -import java.awt.Rectangle; -import java.util.List; -import java.util.Set; +import net.minecraft.client.gui.GuiScreen; -import static com.cleanroommc.modularui.ModularUI.MODID_NEI; -import static com.cleanroommc.modularui.ModularUI.isNEILoaded; +import org.jetbrains.annotations.NotNull; -@Optional.Interface(iface = "codechicken.nei.api.INEIGuiHandler", modid = MODID_NEI) @SideOnly(Side.CLIENT) -public class GuiScreenWrapper extends GuiContainer implements INEIGuiHandler { +public class GuiScreenWrapper extends GuiScreen implements IMuiScreen { private final ModularScreen screen; - private boolean init = true; - private char lastChar; - private int fps, frameCount = 0; - private long timer = Minecraft.getSystemTime(); - - public GuiScreenWrapper(ModularContainer container, ModularScreen screen) { - super(container); + public GuiScreenWrapper(ModularScreen screen) { this.screen = screen; this.screen.construct(this); } - @Override - public void initGui() { - GuiErrorHandler.INSTANCE.clear(); - super.initGui(); - if (this.init) { - this.screen.onOpen(); - this.init = false; - } - this.screen.onResize(this.width, this.height); - } - - public void updateArea(Area mainViewport) { - this.guiLeft = mainViewport.x; - this.guiTop = mainViewport.y; - this.xSize = mainViewport.width; - this.ySize = mainViewport.height; - } - - public GuiContainerAccessor getAccessor() { - return (GuiContainerAccessor) this; - } - - @Override - public void drawScreen(int mouseX, int mouseY, float partialTicks) { - this.frameCount++; - long time = Minecraft.getSystemTime(); - if (time - this.timer >= 1000) { - this.fps = this.frameCount; - this.frameCount = 0; - this.timer += 1000; - } - - Stencil.reset(); - Stencil.apply(this.screen.getScreenArea(), null); - drawDefaultBackground(); - int i = this.guiLeft; - int j = this.guiTop; - - this.drawGuiContainerBackgroundLayer(partialTicks, mouseX, mouseY); - this.screen.drawScreen(mouseX, mouseY, partialTicks); - - GL11.glDisable(GL11.GL_LIGHTING); - GL11.glDisable(GL11.GL_DEPTH_TEST); - // mainly for invtweaks compat - drawVanillaElements(mouseX, mouseY, partialTicks); - GL11.glPushMatrix(); - GL11.glColor4f(1.0F, 1.0F, 1.0F, 1.0F); - GL11.glEnable(GL12.GL_RESCALE_NORMAL); - getAccessor().setHoveredSlot(null); - OpenGlHelper.setLightmapTextureCoords(OpenGlHelper.lightmapTexUnit, 240.0F, 240.0F); - GL11.glEnable(GL12.GL_RESCALE_NORMAL); - GL11.glColor4f(1.0F, 1.0F, 1.0F, 1.0F); - RenderHelper.enableGUIStandardItemLighting(); - if (this.screen.getContext().getNEISettings().isNEIEnabled(this.screen)) { - // Copied from GuiContainerManager#renderObjects but without translation - for (IContainerDrawHandler drawHandler : GuiContainerManager.drawHandlers) { - drawHandler.renderObjects(this, mouseX, mouseY); - } - for (IContainerDrawHandler drawHandler : GuiContainerManager.drawHandlers) { - drawHandler.postRenderObjects(this, mouseX, mouseY); - } - -// if (!shouldRenderOurTooltip()) { - // nh todo? - if (true) { - GuiContainerManager.getManager().renderToolTips(mouseX, mouseY); - } - } - GL11.glDisable(GL11.GL_DEPTH_TEST); - GL11.glColor4f(1.0F, 1.0F, 1.0F, 1.0F); - RenderHelper.disableStandardItemLighting(); - this.drawGuiContainerForegroundLayer(mouseX, mouseY); - this.screen.drawForeground(partialTicks); - RenderHelper.enableGUIStandardItemLighting(); - - getAccessor().setHoveredSlot(null); - IGuiElement hovered = this.screen.getContext().getHovered(); - if (hovered instanceof IVanillaSlot vanillaSlot) { - getAccessor().setHoveredSlot(vanillaSlot.getVanillaSlot()); - } - - GL11.glColor4f(1.0F, 1.0F, 1.0F, 1.0F); - GL11.glPushMatrix(); - GL11.glTranslatef(i, j, 0); - GL11.glPopMatrix(); - - InventoryPlayer inventoryplayer = this.mc.thePlayer.inventory; - ItemStack itemstack = getAccessor().getDraggedStack() == null ? inventoryplayer.getItemStack() : getAccessor().getDraggedStack(); - GL11.glTranslatef(i, j, 0.0F); - if (itemstack != null) { - int k2 = getAccessor().getDraggedStack() == null ? 8 : 16; - String s = null; - - if (getAccessor().getDraggedStack() != null && getAccessor().getIsRightMouseClick()) { - itemstack = itemstack.copy(); - itemstack.stackSize = MathHelper.ceiling_double_int((float) itemstack.stackSize / 2.0F); - } else if (this.isDragSplitting() && this.getDragSlots().size() > 1) { - itemstack = itemstack.copy(); - itemstack.stackSize = getAccessor().getDragSplittingRemnant(); - - if (itemstack.stackSize < 1) { - s = EnumChatFormatting.YELLOW + "0"; - } - } - - this.drawItemStack(itemstack, mouseX - i - 8, mouseY - j - k2, s); - } - - if (getAccessor().getReturningStack() != null) { - float f = (float) (Minecraft.getSystemTime() - getAccessor().getReturningStackTime()) / 100.0F; - - if (f >= 1.0F) { - f = 1.0F; - getAccessor().setReturningStack(null); - } - - int l2 = getAccessor().getReturningStackDestSlot().xDisplayPosition - getAccessor().getTouchUpX(); - int i3 = getAccessor().getReturningStackDestSlot().yDisplayPosition - getAccessor().getTouchUpY(); - int l1 = getAccessor().getTouchUpX() + (int) ((float) l2 * f); - int i2 = getAccessor().getTouchUpY() + (int) ((float) i3 * f); - this.drawItemStack(getAccessor().getReturningStack(), l1, i2, null); - } - - GL11.glPopMatrix(); - - if (ModularUIConfig.guiDebugMode) { - GL11.glDisable(GL11.GL_DEPTH_TEST); - GL11.glDisable(GL11.GL_LIGHTING); - GL11.glEnable(GL11.GL_BLEND); - drawDebugScreen(); - GL11.glColor4f(1f, 1f, 1f, 1f); - } - GuiErrorHandler.INSTANCE.drawErrors(0, 0); - - GL11.glEnable(GL11.GL_LIGHTING); - GL11.glEnable(GL11.GL_DEPTH_TEST); - GL11.glEnable(GL12.GL_RESCALE_NORMAL); - RenderHelper.enableStandardItemLighting(); - - Stencil.remove(); - } - @Override public void drawWorldBackground(int tint) { - if (this.mc.theWorld == null) { - super.drawWorldBackground(tint); - return; - } - float alpha = this.screen.getMainPanel().getAlpha(); - // vanilla color values as hex - int color = 0x101010; - int startAlpha = 0xc0; - int endAlpha = 0xd0; - this.drawGradientRect(0, 0, this.width, this.height, Color.withAlpha(color, (int) (startAlpha * alpha)), Color.withAlpha(color, (int) (endAlpha * alpha))); - } - - private void drawItemStack(ItemStack stack, int x, int y, String altText) { - GL11.glTranslatef(0.0F, 0.0F, 32.0F); - this.zLevel = 200.0F; - itemRender.zLevel = 200.0F; - FontRenderer font = stack.getItem().getFontRenderer(stack); - if (font == null) font = this.fontRendererObj; - GL11.glEnable(GL11.GL_DEPTH_TEST); - itemRender.renderItemAndEffectIntoGUI(font, mc.getTextureManager(), stack, x, y); - itemRender.renderItemOverlayIntoGUI(font, mc.getTextureManager(), stack, x, y - (getAccessor().getDraggedStack() == null ? 0 : 8), altText); - GL11.glDisable(GL11.GL_DEPTH_TEST); - GuiDraw.afterRenderItemAndEffectIntoGUI(stack); - this.zLevel = 0.0F; - itemRender.zLevel = 0.0F; - } - - protected void drawVanillaElements(int mouseX, int mouseY, float partialTicks) { - for (Object guiButton : this.buttonList) { - ((GuiButton) guiButton).drawButton(this.mc, mouseX, mouseY); - } - for (Object guiLabel : this.labelList) { - ((GuiLabel) guiLabel).func_146159_a(this.mc, mouseX, mouseY); - } - } - - public void drawDebugScreen() { - GuiContext context = this.screen.getContext(); - int mouseX = context.getAbsMouseX(), mouseY = context.getAbsMouseY(); - int screenH = this.screen.getScreenArea().height; - int color = Color.rgb(180, 40, 115); - int lineY = screenH - 13 - (this.screen.getContext().getNEISettings().isNEIEnabled(this.screen) ? 20 : 0); - drawString(this.fontRendererObj, "Mouse Pos: " + mouseX + ", " + mouseY, 5, lineY, color); - lineY -= 11; - drawString(this.fontRendererObj, "FPS: " + this.fps, 5, lineY, color); - LocatedWidget locatedHovered = this.screen.getPanelManager().getTopWidgetLocated(true); - if (locatedHovered != null) { - drawSegmentLine(lineY -= 4, color); - lineY -= 10; - - IGuiElement hovered = locatedHovered.getElement(); - locatedHovered.applyMatrix(context); - GL11.glPushMatrix(); - context.applyToOpenGl(); - - Area area = hovered.getArea(); - IGuiElement parent = hovered.getParent(); - - GuiDraw.drawBorder(0, 0, area.width, area.height, color, 1f); - if (hovered.hasParent()) { - GuiDraw.drawBorder(-area.rx, -area.ry, parent.getArea().width, parent.getArea().height, Color.withAlpha(color, 0.3f), 1f); - } - GL11.glPopMatrix(); - locatedHovered.unapplyMatrix(context); - GuiDraw.drawText("Pos: " + area.x + ", " + area.y + " Rel: " + area.rx + ", " + area.ry, 5, lineY, 1, color, false); - lineY -= 11; - GuiDraw.drawText("Size: " + area.width + ", " + area.height, 5, lineY, 1, color, false); - lineY -= 11; - GuiDraw.drawText("Class: " + hovered, 5, lineY, 1, color, false); - if (hovered.hasParent()) { - drawSegmentLine(lineY -= 4, color); - lineY -= 10; - area = parent.getArea(); - GuiDraw.drawText("Parent size: " + area.width + ", " + area.height, 5, lineY, 1, color, false); - lineY -= 11; - GuiDraw.drawText("Parent: " + parent, 5, lineY, 1, color, false); - } - if (hovered instanceof ItemSlot slotWidget) { - drawSegmentLine(lineY -= 4, color); - lineY -= 10; - ModularSlot slot = slotWidget.getSlot(); - GuiDraw.drawText("Slot Index: " + slot.getSlotIndex(), 5, lineY, 1, color, false); - lineY -= 11; - GuiDraw.drawText("Slot Number: " + slot.slotNumber, 5, lineY, 1, color, false); - lineY -= 11; - if (slotWidget.isSynced()) { - SlotGroup slotGroup = slot.getSlotGroup(); - boolean allowShiftTransfer = slotGroup != null && slotGroup.allowShiftTransfer(); - GuiDraw.drawText("Shift-Click Priority: " + (allowShiftTransfer ? slotGroup.getShiftClickPriority() : "DISABLED"), 5, lineY, 1, color, false); - } - } - } - // dot at mouse pos - drawRect(mouseX, mouseY, mouseX + 1, mouseY + 1, Color.withAlpha(Color.GREEN.main, 0.8f)); - } - - private void drawSegmentLine(int y, int color) { - GuiDraw.drawRect(5, y, 140, 1, color); + handleDrawBackground(tint, super::drawWorldBackground); } @Override - protected void drawGuiContainerBackgroundLayer(float partialTicks, int mouseX, int mouseY) { - } - - @Override - public void updateScreen() { - super.updateScreen(); - this.screen.onUpdate(); - } - - @Override - public void onGuiClosed() { - super.onGuiClosed(); - this.screen.onClose(); - this.init = true; - } - - public ModularScreen getScreen() { + public @NotNull ModularScreen getScreen() { return this.screen; } - - @Override - protected void mouseClicked(int mouseX, int mouseY, int mouseButton) { - if (this.screen.onMousePressed(mouseButton)) { - if (this.screen.getContext().getNEISettings().isNEIEnabled(this.screen)) { - for (IContainerInputHandler inputhander : GuiContainerManager.inputHandlers) { - inputhander.onMouseClicked(this, mouseX, mouseY, mouseButton); - } - } - return; - } - // NEI injects GuiContainerManager#mouseClicked there. - // Ideally we should call `onMouseClicked` before handling our click behaviors, - // but then they will be called twice if our onMousePressed returns false. - super.mouseClicked(mouseX, mouseY, mouseButton); - } - - public void clickSlot() { - super.mouseClicked(this.screen.getContext().getAbsMouseX(), this.screen.getContext().getAbsMouseY(), this.screen.getContext().getMouseButton()); - } - - @Override - protected void mouseMovedOrUp(int mouseX, int mouseY, int state) { - if (this.screen.onMouseRelease(state)) return; - super.mouseMovedOrUp(mouseX, mouseY, state); - } - - public void releaseSlot() { - super.mouseMovedOrUp(this.screen.getContext().getAbsMouseX(), this.screen.getContext().getAbsMouseY(), this.screen.getContext().getMouseButton()); - } - - @Override - protected void mouseClickMove(int mouseX, int mouseY, int clickedMouseButton, long timeSinceLastClick) { - if (this.screen.onMouseDrag(clickedMouseButton, timeSinceLastClick)) return; - super.mouseClickMove(mouseX, mouseY, clickedMouseButton, timeSinceLastClick); - } - - public void dragSlot(long timeSinceLastClick) { - super.mouseClickMove(this.screen.getContext().getAbsMouseX(), this.screen.getContext().getAbsMouseY(), this.screen.getContext().getMouseButton(), timeSinceLastClick); - } - - @Override - public void handleMouseInput() { - int scrolled = Mouse.getEventDWheel(); - if (scrolled != 0 && this.screen.onMouseScroll(scrolled > 0 ? ModularScreen.UpOrDown.UP : ModularScreen.UpOrDown.DOWN, Math.abs(scrolled))) { - return; - } - super.handleMouseInput(); - } - - /** - * This replicates vanilla behavior while also injecting custom behavior for consistency - */ - @Override - public void handleKeyboardInput() { - char c0 = Keyboard.getEventCharacter(); - int key = Keyboard.getEventKey(); - boolean state = Keyboard.getEventKeyState(); - - if (isNEILoaded && GuiContainerManager.getManager().firstKeyTyped(c0, key)) { - return; - } - - if (state) { - this.lastChar = c0; - if (this.screen.onKeyPressed(c0, key)) return; - keyTyped(c0, key); - } else { - // when the key is released, the event char is empty - if (this.screen.onKeyRelease(this.lastChar, key)) return; - if (key == 0 && c0 >= ' ') { - keyTyped(c0, key); - } - } - - this.mc.func_152348_aa(); - } - - @Override - protected void keyTyped(char typedChar, int keyCode) { - // debug mode C + CTRL + SHIFT + ALT - if (keyCode == Keyboard.KEY_C && isCtrlKeyDown() && isShiftKeyDown() && Interactable.hasAltDown()) { - ModularUIConfig.guiDebugMode = !ModularUIConfig.guiDebugMode; - return; - } - if (isNEILoaded && GuiContainerManager.getManager().lastKeyTyped(keyCode, typedChar)) { - return; - } - if (keyCode == Keyboard.KEY_ESCAPE || keyCode == this.mc.gameSettings.keyBindInventory.getKeyCode()) { - if (this.screen.getContext().hasDraggable()) { - this.screen.getContext().dropDraggable(); - } else { - this.screen.getPanelManager().closeTopPanel(true); - } - } - - this.checkHotbarKeys(keyCode); - Slot hoveredSlot = getAccessor().getHoveredSlot(); - if (hoveredSlot != null && hoveredSlot.getHasStack()) { - if (keyCode == this.mc.gameSettings.keyBindPickBlock.getKeyCode()) { - this.handleMouseClick(hoveredSlot, hoveredSlot.slotNumber, 0, 3); - } else if (keyCode == this.mc.gameSettings.keyBindDrop.getKeyCode()) { - this.handleMouseClick(hoveredSlot, hoveredSlot.slotNumber, isCtrlKeyDown() ? 1 : 0, 4); - } - } - } - - public boolean isDragSplitting() { - return getAccessor().isDragSplittingInternal(); - } - - public Set getDragSlots() { - return getAccessor().getDragSplittingSlots(); - } - - public static RenderItem getItemRenderer() { - return itemRender; - } - - public float getZ() { - return this.zLevel; - } - - public void setZ(float z) { - this.zLevel = z; - } - - public FontRenderer getFontRenderer() { - return this.fontRendererObj; - } - - // === NEI overrides === - - @Override - public VisiblityData modifyVisiblity(GuiContainer gui, VisiblityData currentVisibility) { - return null; - } - - @Override - public Iterable getItemSpawnSlots(GuiContainer gui, ItemStack item) { - return null; - } - - @Override - public List getInventoryAreas(GuiContainer gui) { - return null; - } - - @Override - public boolean handleDragNDrop(GuiContainer gui, int mousex, int mousey, ItemStack draggedStack, int button) { - if (!(gui instanceof GuiScreenWrapper) || NEIClientUtils.getHeldItem() != null) return false; - IGuiElement hovered = ((GuiScreenWrapper) gui).getScreen().getContext().getHovered(); - if (hovered instanceof NEIDragAndDropHandler) { - return ((NEIDragAndDropHandler) hovered).handleDragAndDrop(draggedStack, button); - } - return false; - } - - @Override - public boolean hideItemPanelSlot(GuiContainer gui, int x, int y, int w, int h) { - if (!(gui instanceof GuiScreenWrapper)) return false; - if (!this.screen.getContext().getNEISettings().isNEIEnabled(this.screen)) return false; - return this.screen.getContext().getNEISettings().getAllNEIExclusionAreas().stream().anyMatch( - a -> a.intersects(new Rectangle(x, y, w, h)) - ); - } } diff --git a/src/main/java/com/cleanroommc/modularui/screen/ModularPanel.java b/src/main/java/com/cleanroommc/modularui/screen/ModularPanel.java index 0dc132e2..06f47418 100644 --- a/src/main/java/com/cleanroommc/modularui/screen/ModularPanel.java +++ b/src/main/java/com/cleanroommc/modularui/screen/ModularPanel.java @@ -3,6 +3,7 @@ import codechicken.nei.ItemPanels; import com.cleanroommc.modularui.api.IPanelHandler; import com.cleanroommc.modularui.api.ITheme; +import com.cleanroommc.modularui.api.drawable.IDrawable; import com.cleanroommc.modularui.api.layout.IViewport; import com.cleanroommc.modularui.api.layout.IViewportStack; import com.cleanroommc.modularui.api.widget.IFocusedWidget; @@ -62,6 +63,7 @@ public static ModularPanel defaultPanel(@NotNull String name, int width, int hei private final Input keyboard = new Input(); private final Input mouse = new Input(); + private boolean invisible = false; private Animator animator; private float scale = 1f; private float alpha = 1f; @@ -181,6 +183,11 @@ private void findHoveredWidgets() { stack.popViewport(null); } + @Override + public boolean canHover() { + return !this.invisible && super.canHover(); + } + @MustBeInvokedByOverriders public void onOpen(ModularScreen screen) { this.screen = screen; @@ -375,7 +382,8 @@ private boolean shouldSkipClick(Interactable interactable) { } private boolean isNEIWantToHandleDragAndDrop() { - return getContext().getNEISettings().isNEIEnabled(this.screen) + return !getContext().getScreen().isOverlay() + && getContext().getNEISettings().isNEIEnabled(this.screen) && (ItemPanels.itemPanel.draggedStack != null || ItemPanels.bookmarkPanel.draggedStack != null); } @@ -586,7 +594,9 @@ public int getDefaultWidth() { final void setPanelGuiContext(@NotNull GuiContext context) { setContext(context); - context.getNEISettings().addNEIExclusionArea(this); + if (!context.getScreen().isOverlay()) { + context.getNEISettings().addNEIExclusionArea(this); + } } public boolean isOpening() { @@ -629,7 +639,7 @@ protected Animator getAnimator() { } public boolean shouldAnimate() { - return getScreen().getCurrentTheme().getOpenCloseAnimationOverride() > 0; + return !getScreen().isOverlay() && getScreen().getCurrentTheme().getOpenCloseAnimationOverride() > 0; } public ModularPanel bindPlayerInventory() { @@ -640,6 +650,11 @@ public ModularPanel bindPlayerInventory(int bottom) { return child(SlotGroupWidget.playerInventory(bottom)); } + public ModularPanel invisible() { + this.invisible = true; + return background(IDrawable.EMPTY); + } + @Override public String toString() { return super.toString() + "#" + getName(); diff --git a/src/main/java/com/cleanroommc/modularui/screen/ModularScreen.java b/src/main/java/com/cleanroommc/modularui/screen/ModularScreen.java index 68cdc90d..818b50b9 100644 --- a/src/main/java/com/cleanroommc/modularui/screen/ModularScreen.java +++ b/src/main/java/com/cleanroommc/modularui/screen/ModularScreen.java @@ -1,18 +1,23 @@ package com.cleanroommc.modularui.screen; import com.cleanroommc.modularui.ModularUI; +import com.cleanroommc.modularui.api.IMuiScreen; import com.cleanroommc.modularui.api.ITheme; import com.cleanroommc.modularui.api.IThemeApi; +import com.cleanroommc.modularui.api.MCHelper; import com.cleanroommc.modularui.api.widget.IGuiAction; import com.cleanroommc.modularui.api.widget.IWidget; import com.cleanroommc.modularui.drawable.GuiDraw; +import com.cleanroommc.modularui.overlay.ScreenWrapper; import com.cleanroommc.modularui.screen.viewport.GuiContext; import com.cleanroommc.modularui.utils.Color; import com.cleanroommc.modularui.value.sync.ModularSyncManager; import com.cleanroommc.modularui.widget.WidgetTree; import com.cleanroommc.modularui.widget.sizer.Area; + import cpw.mods.fml.relauncher.Side; import cpw.mods.fml.relauncher.SideOnly; + import it.unimi.dsi.fastutil.objects.Object2ObjectArrayMap; import it.unimi.dsi.fastutil.objects.Object2ObjectMap; import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; @@ -20,6 +25,7 @@ import net.minecraft.client.Minecraft; import net.minecraft.client.gui.GuiScreen; +import net.minecraft.client.gui.inventory.GuiContainer; import net.minecraft.client.renderer.RenderHelper; import net.minecraft.util.ResourceLocation; import org.jetbrains.annotations.ApiStatus; @@ -45,7 +51,7 @@ public class ModularScreen { public static boolean isScreen(@Nullable GuiScreen guiScreen, String owner, String name) { - if (guiScreen instanceof GuiScreenWrapper screenWrapper) { + if (guiScreen instanceof GuiContainerWrapper screenWrapper) { ModularScreen screen = screenWrapper.getScreen(); return screen.getOwner().equals(owner) && screen.getName().equals(name); } @@ -58,7 +64,7 @@ public static boolean isActive(String owner, String name) { @Nullable public static ModularScreen getCurrent() { - if (Minecraft.getMinecraft().currentScreen instanceof GuiScreenWrapper screenWrapper) { + if (MCHelper.getCurrentScreen() instanceof GuiContainerWrapper screenWrapper) { return screenWrapper.getScreen(); } return null; @@ -73,7 +79,8 @@ public static ModularScreen getCurrent() { private final Object2ObjectArrayMap frameUpdates = new Object2ObjectArrayMap<>(); private ITheme currentTheme; - private GuiScreenWrapper screenWrapper; + private IMuiScreen screenWrapper; + private boolean overlay = false; /** * Creates a new screen with a ModularUI as its owner and a given {@link ModularPanel}. @@ -129,14 +136,28 @@ ModularPanel buildUI(GuiContext context) { } @MustBeInvokedByOverriders - void construct(GuiScreenWrapper wrapper) { + public void construct(IMuiScreen wrapper) { if (this.screenWrapper != null) throw new IllegalStateException("ModularScreen is already constructed!"); if (wrapper == null) throw new NullPointerException("GuiScreenWrapper must not be null!"); this.screenWrapper = wrapper; - this.screenWrapper.updateArea(this.panelManager.getMainPanel().getArea()); + this.screenWrapper.updateGuiArea(this.panelManager.getMainPanel().getArea()); + this.overlay = false; } + @ApiStatus.Internal + @MustBeInvokedByOverriders + public void constructOverlay(GuiScreen screen) { + if (this.screenWrapper != null) throw new IllegalStateException("ModularScreen is already constructed!"); + if (screen == null) throw new NullPointerException("GuiScreenWrapper must not be null!"); + this.screenWrapper = new ScreenWrapper(screen, this); + this.overlay = true; + } + + @MustBeInvokedByOverriders public void onResize(int width, int height) { + if (this.panelManager.tryInit()) { + onOpen(); + } this.screenArea.set(0, 0, width, height); this.screenArea.z(0); @@ -146,16 +167,23 @@ public void onResize(int width, int height) { } this.context.popViewport(null); - this.screenWrapper.updateArea(this.panelManager.getMainPanel().getArea()); + if (!isOverlay()) { + this.screenWrapper.updateGuiArea(this.panelManager.getMainPanel().getArea()); + } } + public final void onCloseParent() { + if (this.panelManager.closeAll()) { + onClose(); + } + } + + @ApiStatus.OverrideOnly public void onOpen() { - this.panelManager.init(); } - @MustBeInvokedByOverriders + @ApiStatus.OverrideOnly public void onClose() { - this.panelManager.closeAll(); } public void close() { @@ -165,7 +193,7 @@ public void close() { public void close(boolean force) { if (isActive()) { if (force) { - this.context.mc.thePlayer.closeScreen(); + MCHelper.closeScreen(); return; } getMainPanel().closeIfOpen(true); @@ -388,6 +416,10 @@ public ResourceLocation getResourceLocation() { return new ResourceLocation(this.owner, this.name); } + public boolean isOverlay() { + return overlay; + } + public GuiContext getContext() { return this.context; } @@ -404,7 +436,7 @@ public ModularPanel getMainPanel() { return this.panelManager.getMainPanel(); } - public GuiScreenWrapper getScreenWrapper() { + public IMuiScreen getScreenWrapper() { return this.screenWrapper; } @@ -413,11 +445,17 @@ public Area getScreenArea() { } public boolean isClientOnly() { - return getContainer().isClientOnly(); + return isOverlay() || !this.screenWrapper.isGuiContainer() || getContainer().isClientOnly(); } public ModularContainer getContainer() { - return (ModularContainer) this.screenWrapper.inventorySlots; + if (isOverlay()) { + throw new IllegalStateException("Can't get ModularContainer for overlay"); + } + if (this.screenWrapper.getGuiScreen() instanceof GuiContainer container) { + return (ModularContainer) container.inventorySlots; + } + throw new IllegalStateException("Screen does not extend GuiContainer!"); } @SuppressWarnings("unchecked") diff --git a/src/main/java/com/cleanroommc/modularui/screen/PanelManager.java b/src/main/java/com/cleanroommc/modularui/screen/PanelManager.java index eb6b4988..7d43c123 100644 --- a/src/main/java/com/cleanroommc/modularui/screen/PanelManager.java +++ b/src/main/java/com/cleanroommc/modularui/screen/PanelManager.java @@ -44,13 +44,15 @@ public PanelManager(ModularScreen screen, ModularPanel panel) { this.mainPanel = Objects.requireNonNull(panel, "Main panel must not be null!"); } - void init() { + boolean tryInit() { if (this.state == State.CLOSED) throw new IllegalStateException("Can't init in closed state!"); if (this.state == State.INIT || this.state == State.DISPOSED) { setState(State.OPEN); openPanel(this.mainPanel, false); checkDirty(); + return true; } + return false; } public boolean isMainPanel(ModularPanel panel) { @@ -170,11 +172,13 @@ public void closeTopPanel(boolean animate) { getTopMostPanel().closeIfOpen(animate); } - public void closeAll() { + public boolean closeAll() { if (this.state.isOpen) { this.panels.forEach(this::finalizePanel); setState(State.CLOSED); + return true; } + return false; } private void finalizePanel(ModularPanel panel) { diff --git a/src/main/java/com/cleanroommc/modularui/screen/Tooltip.java b/src/main/java/com/cleanroommc/modularui/screen/Tooltip.java index 891676b3..5a50bd65 100644 --- a/src/main/java/com/cleanroommc/modularui/screen/Tooltip.java +++ b/src/main/java/com/cleanroommc/modularui/screen/Tooltip.java @@ -120,7 +120,7 @@ public Rectangle determineTooltipArea(GuiContext context, List lines, int height = (int) renderer.getLastHeight(); if (!this.customPos) { - this.pos = context.screen.getCurrentTheme().getTooltipPosOverride(); + this.pos = context.getScreen().getCurrentTheme().getTooltipPosOverride(); } if (this.pos == null) { @@ -198,7 +198,7 @@ public Rectangle determineTooltipArea(GuiContext context, List lines, if (this.pos == Pos.HORIZONTAL) { if (area.x > screenWidth - area.ex()) { pos = Pos.LEFT; - x = 0; + // x = 0; } else { pos = Pos.RIGHT; x = screenWidth - area.ex() + padding; diff --git a/src/main/java/com/cleanroommc/modularui/screen/viewport/GuiContext.java b/src/main/java/com/cleanroommc/modularui/screen/viewport/GuiContext.java index 7d0e7352..9b394c05 100644 --- a/src/main/java/com/cleanroommc/modularui/screen/viewport/GuiContext.java +++ b/src/main/java/com/cleanroommc/modularui/screen/viewport/GuiContext.java @@ -2,17 +2,13 @@ import com.cleanroommc.modularui.api.GuiAxis; import com.cleanroommc.modularui.api.ITheme; -import com.cleanroommc.modularui.api.widget.IDraggable; -import com.cleanroommc.modularui.api.widget.IFocusedWidget; -import com.cleanroommc.modularui.api.widget.IGuiElement; -import com.cleanroommc.modularui.api.widget.IVanillaSlot; -import com.cleanroommc.modularui.api.widget.IWidget; -import com.cleanroommc.modularui.mixins.early.minecraft.GuiContainerAccessor; +import com.cleanroommc.modularui.api.MCHelper; +import com.cleanroommc.modularui.api.widget.*; import com.cleanroommc.modularui.screen.*; import net.minecraft.client.Minecraft; -import net.minecraft.client.gui.FontRenderer; +import net.minecraft.client.entity.EntityPlayerSP; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -30,10 +26,8 @@ */ public class GuiContext extends GuiViewportStack { - public final Minecraft mc; - public final FontRenderer font; - /* GUI elements */ + @Deprecated public final ModularScreen screen; private LocatedWidget focusedWidget = LocatedWidget.EMPTY; @Nullable @@ -66,9 +60,11 @@ public class GuiContext extends GuiViewportStack { public GuiContext(ModularScreen screen) { this.screen = screen; - this.hoveredWidgets = new HoveredIterable(this.screen.getPanelManager()); - this.mc = Minecraft.getMinecraft(); - this.font = this.mc.fontRenderer; + this.hoveredWidgets = new HoveredIterable(this.screen.getPanelManager());; + } + + public ModularScreen getScreen() { + return screen; } /** @@ -259,7 +255,8 @@ public boolean hasDraggable() { } public boolean isMouseItemEmpty() { - return this.mc.thePlayer.inventory.getItemStack() == null; + EntityPlayerSP player = MCHelper.getPlayer(); + return player == null || player.inventory.getItemStack() == null; } @ApiStatus.Internal @@ -368,9 +365,9 @@ public void onFrameUpdate() { if (this.hovered != null) { this.hovered.onMouseStartHover(); if (this.hovered instanceof IVanillaSlot vanillaSlot) { - ((GuiContainerAccessor) this.screen.getScreenWrapper()).setHoveredSlot(vanillaSlot.getVanillaSlot()); + this.screen.getScreenWrapper().setHoveredSlot(vanillaSlot.getVanillaSlot()); } else { - ((GuiContainerAccessor) this.screen.getScreenWrapper()).setHoveredSlot(null); + this.screen.getScreenWrapper().setHoveredSlot(null); } } } else { @@ -453,6 +450,9 @@ public ITheme getTheme() { } public NEISettingsImpl getNEISettings() { + if (this.screen.isOverlay()) { + throw new IllegalStateException("Overlays don't have NEI settings!"); + } if (this.neiSettings == null) { throw new IllegalStateException("The screen is not yet initialised!"); } diff --git a/src/main/java/com/cleanroommc/modularui/screen/viewport/GuiViewportStack.java b/src/main/java/com/cleanroommc/modularui/screen/viewport/GuiViewportStack.java index 1ba8f783..e48d305f 100644 --- a/src/main/java/com/cleanroommc/modularui/screen/viewport/GuiViewportStack.java +++ b/src/main/java/com/cleanroommc/modularui/screen/viewport/GuiViewportStack.java @@ -3,13 +3,12 @@ import com.cleanroommc.modularui.api.layout.IViewport; import com.cleanroommc.modularui.api.layout.IViewportStack; import com.cleanroommc.modularui.utils.GuiUtils; +import com.cleanroommc.modularui.utils.Matrix4f; +import com.cleanroommc.modularui.utils.Vector3f; import com.cleanroommc.modularui.widget.sizer.Area; import it.unimi.dsi.fastutil.objects.ObjectArrayList; import org.jetbrains.annotations.Nullable; -import org.lwjgl.util.vector.Matrix4f; -import org.lwjgl.util.vector.Vector2f; -import org.lwjgl.util.vector.Vector3f; import java.util.ArrayList; import java.util.List; @@ -133,7 +132,7 @@ public void popUntilViewport(IViewport viewport) { public void translate(float x, float y) { checkViewport(); - this.top.getMatrix().translate(new Vector2f(x, y)); + this.top.getMatrix().translate(x, y); this.top.markDirty(); } diff --git a/src/main/java/com/cleanroommc/modularui/screen/viewport/TransformationMatrix.java b/src/main/java/com/cleanroommc/modularui/screen/viewport/TransformationMatrix.java index d7ce5611..cd316358 100644 --- a/src/main/java/com/cleanroommc/modularui/screen/viewport/TransformationMatrix.java +++ b/src/main/java/com/cleanroommc/modularui/screen/viewport/TransformationMatrix.java @@ -1,10 +1,10 @@ package com.cleanroommc.modularui.screen.viewport; import com.cleanroommc.modularui.api.layout.IViewport; +import com.cleanroommc.modularui.utils.Matrix4f; +import com.cleanroommc.modularui.utils.Vector3f; import com.cleanroommc.modularui.widget.sizer.Area; import org.jetbrains.annotations.Nullable; -import org.lwjgl.util.vector.Matrix4f; -import org.lwjgl.util.vector.Vector3f; /** * A single matrix in a matrix stack. Also has some other information. diff --git a/src/main/java/com/cleanroommc/modularui/test/OverlayTest.java b/src/main/java/com/cleanroommc/modularui/test/OverlayTest.java new file mode 100644 index 00000000..2b07c564 --- /dev/null +++ b/src/main/java/com/cleanroommc/modularui/test/OverlayTest.java @@ -0,0 +1,74 @@ +package com.cleanroommc.modularui.test; + +import com.cleanroommc.modularui.api.drawable.IDrawable; +import com.cleanroommc.modularui.api.drawable.IKey; +import com.cleanroommc.modularui.drawable.GuiTextures; +import com.cleanroommc.modularui.mixins.early.minecraft.GuiContainerAccessor; +import com.cleanroommc.modularui.overlay.OverlayHandler; +import com.cleanroommc.modularui.overlay.OverlayManager; +import com.cleanroommc.modularui.screen.CustomModularScreen; +import com.cleanroommc.modularui.screen.ModularPanel; +import com.cleanroommc.modularui.screen.ModularScreen; +import com.cleanroommc.modularui.screen.viewport.GuiContext; +import com.cleanroommc.modularui.utils.Color; +import com.cleanroommc.modularui.widgets.ButtonWidget; +import com.cleanroommc.modularui.widgets.TextWidget; + +import net.minecraft.client.gui.GuiMainMenu; +import net.minecraft.client.gui.inventory.GuiContainer; + +import org.jetbrains.annotations.NotNull; + +import java.util.concurrent.atomic.AtomicInteger; + +public class OverlayTest { + + public static void init() { + + OverlayManager.register(new OverlayHandler(screen -> screen instanceof GuiMainMenu, screen -> { + GuiMainMenu gui = (GuiMainMenu) screen; + TextWidget title = new TextWidget(IKey.str("ModularUI")); + int[] colors = {Color.WHITE.main, Color.AMBER.main, Color.BLUE.main, Color.GREEN.main, Color.DEEP_PURPLE.main, Color.RED.main}; + AtomicInteger k = new AtomicInteger(); + return new ModularScreen(ModularPanel.defaultPanel("overlay").sizeRel(1f) + .background(IDrawable.EMPTY) + .child(title.scale(5f) + .shadow(true) + .color(colors[k.get()]) + .leftRel(0.5f).topRel(0.07f)) + .child(new ButtonWidget<>() // test button overlapping + .topRel(0.25f, 59, 0f) + .leftRelOffset(0.5f, 91) + .size(44) + .overlay(IKey.str("Fun Button")) + .onMousePressed(mouseButton -> { + k.set((k.get() + 1) % colors.length); + title.color(colors[k.get()]); + return true; + }))); + })); + + OverlayManager.register(new OverlayHandler(screen -> screen instanceof GuiContainer, screen -> { + GuiContainerAccessor gui = (GuiContainerAccessor) screen; + return new CustomModularScreen() { + + @Override + public @NotNull ModularPanel buildUI(GuiContext context) { + return ModularPanel.defaultPanel("watermark_overlay", gui.getXSize(), gui.getYSize()) + .pos(gui.getGuiLeft(), gui.getGuiTop()) + .background(IDrawable.EMPTY) + .child(GuiTextures.MUI_LOGO.asIcon().asWidget() + .top(5).right(5) + .size(18)); + } + + @Override + public void onResize(int width, int height) { + getMainPanel().pos(gui.getGuiLeft(), gui.getGuiTop()) + .size(gui.getXSize(), gui.getYSize()); + super.onResize(width, height); + } + }; + })); + } +} diff --git a/src/main/java/com/cleanroommc/modularui/test/TestBlock.java b/src/main/java/com/cleanroommc/modularui/test/TestBlock.java index aa196d88..75aef4ee 100644 --- a/src/main/java/com/cleanroommc/modularui/test/TestBlock.java +++ b/src/main/java/com/cleanroommc/modularui/test/TestBlock.java @@ -1,6 +1,7 @@ package com.cleanroommc.modularui.test; import cpw.mods.fml.common.registry.GameRegistry; +import com.cleanroommc.modularui.factory.GuiFactories; import com.cleanroommc.modularui.factory.TileEntityGuiFactory; import net.minecraft.block.Block; import net.minecraft.block.ITileEntityProvider; @@ -38,7 +39,7 @@ public TileEntity createNewTileEntity(World worldIn, int meta) { @Override public boolean onBlockActivated(World worldIn, int x, int y, int z, EntityPlayer playerIn, int side, float hitX, float hitY, float hitZ) { if (!worldIn.isRemote) { - TileEntityGuiFactory.open(playerIn, x, y, z); + GuiFactories.tileEntity().open(playerIn, x, y, z); } return true; } diff --git a/src/main/java/com/cleanroommc/modularui/test/TestGui.java b/src/main/java/com/cleanroommc/modularui/test/TestGui.java index b7eaa1c4..2080c950 100644 --- a/src/main/java/com/cleanroommc/modularui/test/TestGui.java +++ b/src/main/java/com/cleanroommc/modularui/test/TestGui.java @@ -33,7 +33,6 @@ public class TestGui extends CustomModularScreen { @Override public void onClose() { - super.onClose(); ModularUI.LOGGER.info("New values: {}", this.configuredOptions); } diff --git a/src/main/java/com/cleanroommc/modularui/test/TestItem.java b/src/main/java/com/cleanroommc/modularui/test/TestItem.java index bc18f9de..4cf1aed8 100644 --- a/src/main/java/com/cleanroommc/modularui/test/TestItem.java +++ b/src/main/java/com/cleanroommc/modularui/test/TestItem.java @@ -3,6 +3,7 @@ import com.cleanroommc.modularui.api.IGuiHolder; import com.cleanroommc.modularui.utils.item.IItemHandlerModifiable; import com.cleanroommc.modularui.factory.GuiData; +import com.cleanroommc.modularui.factory.GuiFactories; import com.cleanroommc.modularui.factory.ItemGuiFactory; import com.cleanroommc.modularui.screen.ModularPanel; import com.cleanroommc.modularui.utils.Alignment; @@ -47,7 +48,7 @@ public ModularPanel buildUI(GuiData guiData, PanelSyncManager guiSyncManager) { @Override public ItemStack onItemRightClick(ItemStack itemStackIn, World worldIn, EntityPlayer player) { if (!worldIn.isRemote) { - ItemGuiFactory.open((EntityPlayerMP) player); + GuiFactories.item().open(player); } return super.onItemRightClick(itemStackIn, worldIn, player); } diff --git a/src/main/java/com/cleanroommc/modularui/theme/WidgetTheme.java b/src/main/java/com/cleanroommc/modularui/theme/WidgetTheme.java index bd4864b7..05e787f7 100644 --- a/src/main/java/com/cleanroommc/modularui/theme/WidgetTheme.java +++ b/src/main/java/com/cleanroommc/modularui/theme/WidgetTheme.java @@ -55,4 +55,9 @@ public int getTextColor() { public boolean getTextShadow() { return this.textShadow; } + + public WidgetTheme withColor(int color) { + // TODO it is currently somewhat difficult to color drawable with a custom color. This is a dirty solution. + return new WidgetTheme(this.background, this.hoverBackground, color, this.textColor, this.textShadow); + } } diff --git a/src/main/java/com/cleanroommc/modularui/utils/FpsCounter.java b/src/main/java/com/cleanroommc/modularui/utils/FpsCounter.java new file mode 100644 index 00000000..6fe929a2 --- /dev/null +++ b/src/main/java/com/cleanroommc/modularui/utils/FpsCounter.java @@ -0,0 +1,29 @@ +package com.cleanroommc.modularui.utils; + +import net.minecraft.client.Minecraft; + +public class FpsCounter { + + private int fps = 0, frameCount = 0; + private long timer = Minecraft.getSystemTime(); + + public void reset() { + this.fps = 0; + this.frameCount = 0; + this.timer = Minecraft.getSystemTime(); + } + + public void onDraw() { + frameCount++; + long time = Minecraft.getSystemTime(); + if (time - timer >= 1000) { + fps = frameCount; + frameCount = 0; + timer += 1000; + } + } + + public int getFps() { + return fps; + } +} diff --git a/src/main/java/com/cleanroommc/modularui/utils/GuiUtils.java b/src/main/java/com/cleanroommc/modularui/utils/GuiUtils.java index f600bf91..5d1ccf0d 100644 --- a/src/main/java/com/cleanroommc/modularui/utils/GuiUtils.java +++ b/src/main/java/com/cleanroommc/modularui/utils/GuiUtils.java @@ -2,7 +2,6 @@ import org.lwjgl.BufferUtils; import org.lwjgl.opengl.GL11; -import org.lwjgl.util.vector.Matrix4f; import java.nio.FloatBuffer; diff --git a/src/main/java/com/cleanroommc/modularui/utils/Matrix4f.java b/src/main/java/com/cleanroommc/modularui/utils/Matrix4f.java new file mode 100644 index 00000000..c30b42d3 --- /dev/null +++ b/src/main/java/com/cleanroommc/modularui/utils/Matrix4f.java @@ -0,0 +1,846 @@ +package com.cleanroommc.modularui.utils; + +import org.lwjgl.util.vector.Vector4f; + +import java.nio.FloatBuffer; + +public class Matrix4f { + + public float m00, m01, m02, m03, m10, m11, m12, m13, m20, m21, m22, m23, m30, m31, m32, m33; + + /** + * Construct a new matrix, initialized to the identity. + */ + public Matrix4f() { + setIdentity(); + } + + public Matrix4f(final Matrix4f src) { + load(src); + } + + /** + * Returns a string representation of this matrix + */ + public String toString() { + StringBuilder buf = new StringBuilder(); + buf.append(m00).append(' ').append(m10).append(' ').append(m20).append(' ').append(m30).append('\n'); + buf.append(m01).append(' ').append(m11).append(' ').append(m21).append(' ').append(m31).append('\n'); + buf.append(m02).append(' ').append(m12).append(' ').append(m22).append(' ').append(m32).append('\n'); + buf.append(m03).append(' ').append(m13).append(' ').append(m23).append(' ').append(m33).append('\n'); + return buf.toString(); + } + + /** + * Set this matrix to be the identity matrix. + * + * @return this + */ + public Matrix4f setIdentity() { + return setIdentity(this); + } + + /** + * Set the given matrix to be the identity matrix. + * + * @param m The matrix to set to the identity + * @return m + */ + public static Matrix4f setIdentity(Matrix4f m) { + m.m00 = 1.0f; + m.m01 = 0.0f; + m.m02 = 0.0f; + m.m03 = 0.0f; + m.m10 = 0.0f; + m.m11 = 1.0f; + m.m12 = 0.0f; + m.m13 = 0.0f; + m.m20 = 0.0f; + m.m21 = 0.0f; + m.m22 = 1.0f; + m.m23 = 0.0f; + m.m30 = 0.0f; + m.m31 = 0.0f; + m.m32 = 0.0f; + m.m33 = 1.0f; + + return m; + } + + /** + * Set this matrix to 0. + * + * @return this + */ + public Matrix4f setZero() { + return setZero(this); + } + + /** + * Set the given matrix to 0. + * + * @param m The matrix to set to 0 + * @return m + */ + public static Matrix4f setZero(Matrix4f m) { + m.m00 = 0.0f; + m.m01 = 0.0f; + m.m02 = 0.0f; + m.m03 = 0.0f; + m.m10 = 0.0f; + m.m11 = 0.0f; + m.m12 = 0.0f; + m.m13 = 0.0f; + m.m20 = 0.0f; + m.m21 = 0.0f; + m.m22 = 0.0f; + m.m23 = 0.0f; + m.m30 = 0.0f; + m.m31 = 0.0f; + m.m32 = 0.0f; + m.m33 = 0.0f; + + return m; + } + + /** + * Load from another matrix4f + * + * @param src The source matrix + * @return this + */ + public Matrix4f load(Matrix4f src) { + return load(src, this); + } + + /** + * Copy the source matrix to the destination matrix + * + * @param src The source matrix + * @param dest The destination matrix, or null of a new one is to be created + * @return The copied matrix + */ + public static Matrix4f load(Matrix4f src, Matrix4f dest) { + if (dest == null) + dest = new Matrix4f(); + dest.m00 = src.m00; + dest.m01 = src.m01; + dest.m02 = src.m02; + dest.m03 = src.m03; + dest.m10 = src.m10; + dest.m11 = src.m11; + dest.m12 = src.m12; + dest.m13 = src.m13; + dest.m20 = src.m20; + dest.m21 = src.m21; + dest.m22 = src.m22; + dest.m23 = src.m23; + dest.m30 = src.m30; + dest.m31 = src.m31; + dest.m32 = src.m32; + dest.m33 = src.m33; + + return dest; + } + + /** + * Load from a float buffer. The buffer stores the matrix in column major + * (OpenGL) order. + * + * @param buf A float buffer to read from + * @return this + */ + public Matrix4f load(FloatBuffer buf) { + + m00 = buf.get(); + m01 = buf.get(); + m02 = buf.get(); + m03 = buf.get(); + m10 = buf.get(); + m11 = buf.get(); + m12 = buf.get(); + m13 = buf.get(); + m20 = buf.get(); + m21 = buf.get(); + m22 = buf.get(); + m23 = buf.get(); + m30 = buf.get(); + m31 = buf.get(); + m32 = buf.get(); + m33 = buf.get(); + + return this; + } + + /** + * Load from a float buffer. The buffer stores the matrix in row major + * (maths) order. + * + * @param buf A float buffer to read from + * @return this + */ + public Matrix4f loadTranspose(FloatBuffer buf) { + + m00 = buf.get(); + m10 = buf.get(); + m20 = buf.get(); + m30 = buf.get(); + m01 = buf.get(); + m11 = buf.get(); + m21 = buf.get(); + m31 = buf.get(); + m02 = buf.get(); + m12 = buf.get(); + m22 = buf.get(); + m32 = buf.get(); + m03 = buf.get(); + m13 = buf.get(); + m23 = buf.get(); + m33 = buf.get(); + + return this; + } + + /** + * Store this matrix in a float buffer. The matrix is stored in column + * major (openGL) order. + * + * @param buf The buffer to store this matrix in + */ + public Matrix4f store(FloatBuffer buf) { + buf.put(m00); + buf.put(m01); + buf.put(m02); + buf.put(m03); + buf.put(m10); + buf.put(m11); + buf.put(m12); + buf.put(m13); + buf.put(m20); + buf.put(m21); + buf.put(m22); + buf.put(m23); + buf.put(m30); + buf.put(m31); + buf.put(m32); + buf.put(m33); + return this; + } + + /** + * Store this matrix in a float buffer. The matrix is stored in row + * major (maths) order. + * + * @param buf The buffer to store this matrix in + */ + public Matrix4f storeTranspose(FloatBuffer buf) { + buf.put(m00); + buf.put(m10); + buf.put(m20); + buf.put(m30); + buf.put(m01); + buf.put(m11); + buf.put(m21); + buf.put(m31); + buf.put(m02); + buf.put(m12); + buf.put(m22); + buf.put(m32); + buf.put(m03); + buf.put(m13); + buf.put(m23); + buf.put(m33); + return this; + } + + /** + * Store the rotation portion of this matrix in a float buffer. The matrix is stored in column + * major (openGL) order. + * + * @param buf The buffer to store this matrix in + */ + public Matrix4f store3f(FloatBuffer buf) { + buf.put(m00); + buf.put(m01); + buf.put(m02); + buf.put(m10); + buf.put(m11); + buf.put(m12); + buf.put(m20); + buf.put(m21); + buf.put(m22); + return this; + } + + /** + * Add two matrices together and place the result in a third matrix. + * + * @param left The left source matrix + * @param right The right source matrix + * @param dest The destination matrix, or null if a new one is to be created + * @return the destination matrix + */ + public static Matrix4f add(Matrix4f left, Matrix4f right, Matrix4f dest) { + if (dest == null) + dest = new Matrix4f(); + + dest.m00 = left.m00 + right.m00; + dest.m01 = left.m01 + right.m01; + dest.m02 = left.m02 + right.m02; + dest.m03 = left.m03 + right.m03; + dest.m10 = left.m10 + right.m10; + dest.m11 = left.m11 + right.m11; + dest.m12 = left.m12 + right.m12; + dest.m13 = left.m13 + right.m13; + dest.m20 = left.m20 + right.m20; + dest.m21 = left.m21 + right.m21; + dest.m22 = left.m22 + right.m22; + dest.m23 = left.m23 + right.m23; + dest.m30 = left.m30 + right.m30; + dest.m31 = left.m31 + right.m31; + dest.m32 = left.m32 + right.m32; + dest.m33 = left.m33 + right.m33; + + return dest; + } + + /** + * Subtract the right matrix from the left and place the result in a third matrix. + * + * @param left The left source matrix + * @param right The right source matrix + * @param dest The destination matrix, or null if a new one is to be created + * @return the destination matrix + */ + public static Matrix4f sub(Matrix4f left, Matrix4f right, Matrix4f dest) { + if (dest == null) + dest = new Matrix4f(); + + dest.m00 = left.m00 - right.m00; + dest.m01 = left.m01 - right.m01; + dest.m02 = left.m02 - right.m02; + dest.m03 = left.m03 - right.m03; + dest.m10 = left.m10 - right.m10; + dest.m11 = left.m11 - right.m11; + dest.m12 = left.m12 - right.m12; + dest.m13 = left.m13 - right.m13; + dest.m20 = left.m20 - right.m20; + dest.m21 = left.m21 - right.m21; + dest.m22 = left.m22 - right.m22; + dest.m23 = left.m23 - right.m23; + dest.m30 = left.m30 - right.m30; + dest.m31 = left.m31 - right.m31; + dest.m32 = left.m32 - right.m32; + dest.m33 = left.m33 - right.m33; + + return dest; + } + + /** + * Multiply the right matrix by the left and place the result in a third matrix. + * + * @param left The left source matrix + * @param right The right source matrix + * @param dest The destination matrix, or null if a new one is to be created + * @return the destination matrix + */ + public static Matrix4f mul(Matrix4f left, Matrix4f right, Matrix4f dest) { + if (dest == null) + dest = new Matrix4f(); + + float m00 = left.m00 * right.m00 + left.m10 * right.m01 + left.m20 * right.m02 + left.m30 * right.m03; + float m01 = left.m01 * right.m00 + left.m11 * right.m01 + left.m21 * right.m02 + left.m31 * right.m03; + float m02 = left.m02 * right.m00 + left.m12 * right.m01 + left.m22 * right.m02 + left.m32 * right.m03; + float m03 = left.m03 * right.m00 + left.m13 * right.m01 + left.m23 * right.m02 + left.m33 * right.m03; + float m10 = left.m00 * right.m10 + left.m10 * right.m11 + left.m20 * right.m12 + left.m30 * right.m13; + float m11 = left.m01 * right.m10 + left.m11 * right.m11 + left.m21 * right.m12 + left.m31 * right.m13; + float m12 = left.m02 * right.m10 + left.m12 * right.m11 + left.m22 * right.m12 + left.m32 * right.m13; + float m13 = left.m03 * right.m10 + left.m13 * right.m11 + left.m23 * right.m12 + left.m33 * right.m13; + float m20 = left.m00 * right.m20 + left.m10 * right.m21 + left.m20 * right.m22 + left.m30 * right.m23; + float m21 = left.m01 * right.m20 + left.m11 * right.m21 + left.m21 * right.m22 + left.m31 * right.m23; + float m22 = left.m02 * right.m20 + left.m12 * right.m21 + left.m22 * right.m22 + left.m32 * right.m23; + float m23 = left.m03 * right.m20 + left.m13 * right.m21 + left.m23 * right.m22 + left.m33 * right.m23; + float m30 = left.m00 * right.m30 + left.m10 * right.m31 + left.m20 * right.m32 + left.m30 * right.m33; + float m31 = left.m01 * right.m30 + left.m11 * right.m31 + left.m21 * right.m32 + left.m31 * right.m33; + float m32 = left.m02 * right.m30 + left.m12 * right.m31 + left.m22 * right.m32 + left.m32 * right.m33; + float m33 = left.m03 * right.m30 + left.m13 * right.m31 + left.m23 * right.m32 + left.m33 * right.m33; + + dest.m00 = m00; + dest.m01 = m01; + dest.m02 = m02; + dest.m03 = m03; + dest.m10 = m10; + dest.m11 = m11; + dest.m12 = m12; + dest.m13 = m13; + dest.m20 = m20; + dest.m21 = m21; + dest.m22 = m22; + dest.m23 = m23; + dest.m30 = m30; + dest.m31 = m31; + dest.m32 = m32; + dest.m33 = m33; + + return dest; + } + + /** + * Transform a Vector by a matrix and return the result in a destination + * vector. + * + * @param left The left matrix + * @param right The right vector + * @param dest The destination vector, or null if a new one is to be created + * @return the destination vector + */ + public static Vector4f transform(Matrix4f left, Vector4f right, Vector4f dest) { + if (dest == null) + dest = new Vector4f(); + + float x = left.m00 * right.x + left.m10 * right.y + left.m20 * right.z + left.m30 * right.w; + float y = left.m01 * right.x + left.m11 * right.y + left.m21 * right.z + left.m31 * right.w; + float z = left.m02 * right.x + left.m12 * right.y + left.m22 * right.z + left.m32 * right.w; + float w = left.m03 * right.x + left.m13 * right.y + left.m23 * right.z + left.m33 * right.w; + + dest.x = x; + dest.y = y; + dest.z = z; + dest.w = w; + + return dest; + } + + /** + * Transpose this matrix + * + * @return this + */ + public Matrix4f transpose() { + return transpose(this); + } + + /** + * Translate this matrix + * + * @param x x to translate by + * @param y y to translate by + * @return this + */ + public Matrix4f translate(float x, float y) { + return translate(x, y, this); + } + + /** + * Translate this matrix + * + * @param vec The vector to translate by + * @return this + */ + public Matrix4f translate(Vector3f vec) { + return translate(vec, this); + } + + /** + * Scales this matrix + * + * @param vec The vector to scale by + * @return this + */ + public Matrix4f scale(Vector3f vec) { + return scale(vec, this, this); + } + + /** + * Scales the source matrix and put the result in the destination matrix + * + * @param vec The vector to scale by + * @param src The source matrix + * @param dest The destination matrix, or null if a new matrix is to be created + * @return The scaled matrix + */ + public static Matrix4f scale(Vector3f vec, Matrix4f src, Matrix4f dest) { + if (dest == null) + dest = new Matrix4f(); + dest.m00 = src.m00 * vec.x; + dest.m01 = src.m01 * vec.x; + dest.m02 = src.m02 * vec.x; + dest.m03 = src.m03 * vec.x; + dest.m10 = src.m10 * vec.y; + dest.m11 = src.m11 * vec.y; + dest.m12 = src.m12 * vec.y; + dest.m13 = src.m13 * vec.y; + dest.m20 = src.m20 * vec.z; + dest.m21 = src.m21 * vec.z; + dest.m22 = src.m22 * vec.z; + dest.m23 = src.m23 * vec.z; + return dest; + } + + /** + * Rotates the matrix around the given axis the specified angle + * + * @param angle the angle, in radians. + * @param axis The vector representing the rotation axis. Must be normalized. + * @return this + */ + public Matrix4f rotate(float angle, Vector3f axis) { + return rotate(angle, axis, this); + } + + /** + * Rotates the matrix around the given axis the specified angle + * + * @param angle the angle, in radians. + * @param axis The vector representing the rotation axis. Must be normalized. + * @param dest The matrix to put the result, or null if a new matrix is to be created + * @return The rotated matrix + */ + public Matrix4f rotate(float angle, Vector3f axis, Matrix4f dest) { + return rotate(angle, axis, this, dest); + } + + /** + * Rotates the source matrix around the given axis the specified angle and + * put the result in the destination matrix. + * + * @param angle the angle, in radians. + * @param axis The vector representing the rotation axis. Must be normalized. + * @param src The matrix to rotate + * @param dest The matrix to put the result, or null if a new matrix is to be created + * @return The rotated matrix + */ + public static Matrix4f rotate(float angle, Vector3f axis, Matrix4f src, Matrix4f dest) { + if (dest == null) + dest = new Matrix4f(); + float c = (float) Math.cos(angle); + float s = (float) Math.sin(angle); + float oneminusc = 1.0f - c; + float xy = axis.x * axis.y; + float yz = axis.y * axis.z; + float xz = axis.x * axis.z; + float xs = axis.x * s; + float ys = axis.y * s; + float zs = axis.z * s; + + float f00 = axis.x * axis.x * oneminusc + c; + float f01 = xy * oneminusc + zs; + float f02 = xz * oneminusc - ys; + // n[3] not used + float f10 = xy * oneminusc - zs; + float f11 = axis.y * axis.y * oneminusc + c; + float f12 = yz * oneminusc + xs; + // n[7] not used + float f20 = xz * oneminusc + ys; + float f21 = yz * oneminusc - xs; + float f22 = axis.z * axis.z * oneminusc + c; + + float t00 = src.m00 * f00 + src.m10 * f01 + src.m20 * f02; + float t01 = src.m01 * f00 + src.m11 * f01 + src.m21 * f02; + float t02 = src.m02 * f00 + src.m12 * f01 + src.m22 * f02; + float t03 = src.m03 * f00 + src.m13 * f01 + src.m23 * f02; + float t10 = src.m00 * f10 + src.m10 * f11 + src.m20 * f12; + float t11 = src.m01 * f10 + src.m11 * f11 + src.m21 * f12; + float t12 = src.m02 * f10 + src.m12 * f11 + src.m22 * f12; + float t13 = src.m03 * f10 + src.m13 * f11 + src.m23 * f12; + dest.m20 = src.m00 * f20 + src.m10 * f21 + src.m20 * f22; + dest.m21 = src.m01 * f20 + src.m11 * f21 + src.m21 * f22; + dest.m22 = src.m02 * f20 + src.m12 * f21 + src.m22 * f22; + dest.m23 = src.m03 * f20 + src.m13 * f21 + src.m23 * f22; + dest.m00 = t00; + dest.m01 = t01; + dest.m02 = t02; + dest.m03 = t03; + dest.m10 = t10; + dest.m11 = t11; + dest.m12 = t12; + dest.m13 = t13; + return dest; + } + + /** + * Translate this matrix and stash the result in another matrix + * + * @param vec The vector to translate by + * @param dest The destination matrix or null if a new matrix is to be created + * @return the translated matrix + */ + public Matrix4f translate(Vector3f vec, Matrix4f dest) { + return translate(vec, this, dest); + } + + /** + * Translate the source matrix and stash the result in the destination matrix + * + * @param vec The vector to translate by + * @param src The source matrix + * @param dest The destination matrix or null if a new matrix is to be created + * @return The translated matrix + */ + public static Matrix4f translate(Vector3f vec, Matrix4f src, Matrix4f dest) { + if (dest == null) + dest = new Matrix4f(); + + dest.m30 += src.m00 * vec.x + src.m10 * vec.y + src.m20 * vec.z; + dest.m31 += src.m01 * vec.x + src.m11 * vec.y + src.m21 * vec.z; + dest.m32 += src.m02 * vec.x + src.m12 * vec.y + src.m22 * vec.z; + dest.m33 += src.m03 * vec.x + src.m13 * vec.y + src.m23 * vec.z; + + return dest; + } + + /** + * Translate this matrix and stash the result in another matrix + * + * @param x x to translate by + * @param y y to translate by + * @param dest The destination matrix or null if a new matrix is to be created + * @return the translated matrix + */ + public Matrix4f translate(float x, float y, Matrix4f dest) { + return translate(x, y, this, dest); + } + + /** + * Translate the source matrix and stash the result in the destination matrix + * + * @param x x to translate by + * @param y y to translate by + * @param src The source matrix + * @param dest The destination matrix or null if a new matrix is to be created + * @return The translated matrix + */ + public static Matrix4f translate(float x, float y, Matrix4f src, Matrix4f dest) { + if (dest == null) + dest = new Matrix4f(); + + dest.m30 += src.m00 * x + src.m10 * y; + dest.m31 += src.m01 * x + src.m11 * y; + dest.m32 += src.m02 * x + src.m12 * y; + dest.m33 += src.m03 * x + src.m13 * y; + + return dest; + } + + /** + * Transpose this matrix and place the result in another matrix + * + * @param dest The destination matrix or null if a new matrix is to be created + * @return the transposed matrix + */ + public Matrix4f transpose(Matrix4f dest) { + return transpose(this, dest); + } + + /** + * Transpose the source matrix and place the result in the destination matrix + * + * @param src The source matrix + * @param dest The destination matrix or null if a new matrix is to be created + * @return the transposed matrix + */ + public static Matrix4f transpose(Matrix4f src, Matrix4f dest) { + if (dest == null) + dest = new Matrix4f(); + float m00 = src.m00; + float m01 = src.m10; + float m02 = src.m20; + float m03 = src.m30; + float m10 = src.m01; + float m11 = src.m11; + float m12 = src.m21; + float m13 = src.m31; + float m20 = src.m02; + float m21 = src.m12; + float m22 = src.m22; + float m23 = src.m32; + float m30 = src.m03; + float m31 = src.m13; + float m32 = src.m23; + float m33 = src.m33; + + dest.m00 = m00; + dest.m01 = m01; + dest.m02 = m02; + dest.m03 = m03; + dest.m10 = m10; + dest.m11 = m11; + dest.m12 = m12; + dest.m13 = m13; + dest.m20 = m20; + dest.m21 = m21; + dest.m22 = m22; + dest.m23 = m23; + dest.m30 = m30; + dest.m31 = m31; + dest.m32 = m32; + dest.m33 = m33; + + return dest; + } + + /** + * @return the determinant of the matrix + */ + public float determinant() { + float f = + m00 + * ((m11 * m22 * m33 + m12 * m23 * m31 + m13 * m21 * m32) + - m13 * m22 * m31 + - m11 * m23 * m32 + - m12 * m21 * m33); + f -= m01 + * ((m10 * m22 * m33 + m12 * m23 * m30 + m13 * m20 * m32) + - m13 * m22 * m30 + - m10 * m23 * m32 + - m12 * m20 * m33); + f += m02 + * ((m10 * m21 * m33 + m11 * m23 * m30 + m13 * m20 * m31) + - m13 * m21 * m30 + - m10 * m23 * m31 + - m11 * m20 * m33); + f -= m03 + * ((m10 * m21 * m32 + m11 * m22 * m30 + m12 * m20 * m31) + - m12 * m21 * m30 + - m10 * m22 * m31 + - m11 * m20 * m32); + return f; + } + + /** + * Calculate the determinant of a 3x3 matrix + * + * @return result + */ + + private static float determinant3x3(float t00, float t01, float t02, + float t10, float t11, float t12, + float t20, float t21, float t22) { + return t00 * (t11 * t22 - t12 * t21) + + t01 * (t12 * t20 - t10 * t22) + + t02 * (t10 * t21 - t11 * t20); + } + + /** + * Invert this matrix + * + * @return this if successful, null otherwise + */ + public Matrix4f invert() { + return invert(this, this); + } + + /** + * Invert the source matrix and put the result in the destination + * + * @param src The source matrix + * @param dest The destination matrix, or null if a new matrix is to be created + * @return The inverted matrix if successful, null otherwise + */ + public static Matrix4f invert(Matrix4f src, Matrix4f dest) { + float determinant = src.determinant(); + + if (determinant != 0) { + /* + * m00 m01 m02 m03 + * m10 m11 m12 m13 + * m20 m21 m22 m23 + * m30 m31 m32 m33 + */ + if (dest == null) + dest = new Matrix4f(); + float determinant_inv = 1f / determinant; + + // first row + float t00 = determinant3x3(src.m11, src.m12, src.m13, src.m21, src.m22, src.m23, src.m31, src.m32, src.m33); + float t01 = -determinant3x3(src.m10, src.m12, src.m13, src.m20, src.m22, src.m23, src.m30, src.m32, src.m33); + float t02 = determinant3x3(src.m10, src.m11, src.m13, src.m20, src.m21, src.m23, src.m30, src.m31, src.m33); + float t03 = -determinant3x3(src.m10, src.m11, src.m12, src.m20, src.m21, src.m22, src.m30, src.m31, src.m32); + // second row + float t10 = -determinant3x3(src.m01, src.m02, src.m03, src.m21, src.m22, src.m23, src.m31, src.m32, src.m33); + float t11 = determinant3x3(src.m00, src.m02, src.m03, src.m20, src.m22, src.m23, src.m30, src.m32, src.m33); + float t12 = -determinant3x3(src.m00, src.m01, src.m03, src.m20, src.m21, src.m23, src.m30, src.m31, src.m33); + float t13 = determinant3x3(src.m00, src.m01, src.m02, src.m20, src.m21, src.m22, src.m30, src.m31, src.m32); + // third row + float t20 = determinant3x3(src.m01, src.m02, src.m03, src.m11, src.m12, src.m13, src.m31, src.m32, src.m33); + float t21 = -determinant3x3(src.m00, src.m02, src.m03, src.m10, src.m12, src.m13, src.m30, src.m32, src.m33); + float t22 = determinant3x3(src.m00, src.m01, src.m03, src.m10, src.m11, src.m13, src.m30, src.m31, src.m33); + float t23 = -determinant3x3(src.m00, src.m01, src.m02, src.m10, src.m11, src.m12, src.m30, src.m31, src.m32); + // fourth row + float t30 = -determinant3x3(src.m01, src.m02, src.m03, src.m11, src.m12, src.m13, src.m21, src.m22, src.m23); + float t31 = determinant3x3(src.m00, src.m02, src.m03, src.m10, src.m12, src.m13, src.m20, src.m22, src.m23); + float t32 = -determinant3x3(src.m00, src.m01, src.m03, src.m10, src.m11, src.m13, src.m20, src.m21, src.m23); + float t33 = determinant3x3(src.m00, src.m01, src.m02, src.m10, src.m11, src.m12, src.m20, src.m21, src.m22); + + // transpose and divide by the determinant + dest.m00 = t00 * determinant_inv; + dest.m11 = t11 * determinant_inv; + dest.m22 = t22 * determinant_inv; + dest.m33 = t33 * determinant_inv; + dest.m01 = t10 * determinant_inv; + dest.m10 = t01 * determinant_inv; + dest.m20 = t02 * determinant_inv; + dest.m02 = t20 * determinant_inv; + dest.m12 = t21 * determinant_inv; + dest.m21 = t12 * determinant_inv; + dest.m03 = t30 * determinant_inv; + dest.m30 = t03 * determinant_inv; + dest.m13 = t31 * determinant_inv; + dest.m31 = t13 * determinant_inv; + dest.m32 = t23 * determinant_inv; + dest.m23 = t32 * determinant_inv; + return dest; + } else + return null; + } + + /** + * Negate this matrix + * + * @return this + */ + public Matrix4f negate() { + return negate(this); + } + + /** + * Negate this matrix and place the result in a destination matrix. + * + * @param dest The destination matrix, or null if a new matrix is to be created + * @return the negated matrix + */ + public Matrix4f negate(Matrix4f dest) { + return negate(this, dest); + } + + /** + * Negate this matrix and place the result in a destination matrix. + * + * @param src The source matrix + * @param dest The destination matrix, or null if a new matrix is to be created + * @return The negated matrix + */ + public static Matrix4f negate(Matrix4f src, Matrix4f dest) { + if (dest == null) + dest = new Matrix4f(); + + dest.m00 = -src.m00; + dest.m01 = -src.m01; + dest.m02 = -src.m02; + dest.m03 = -src.m03; + dest.m10 = -src.m10; + dest.m11 = -src.m11; + dest.m12 = -src.m12; + dest.m13 = -src.m13; + dest.m20 = -src.m20; + dest.m21 = -src.m21; + dest.m22 = -src.m22; + dest.m23 = -src.m23; + dest.m30 = -src.m30; + dest.m31 = -src.m31; + dest.m32 = -src.m32; + dest.m33 = -src.m33; + + return dest; + } +} diff --git a/src/main/java/com/cleanroommc/modularui/utils/Vector3f.java b/src/main/java/com/cleanroommc/modularui/utils/Vector3f.java new file mode 100644 index 00000000..1153caeb --- /dev/null +++ b/src/main/java/com/cleanroommc/modularui/utils/Vector3f.java @@ -0,0 +1,321 @@ +package com.cleanroommc.modularui.utils; + +import java.nio.FloatBuffer; + +public class Vector3f { + + public float x, y, z; + + /** + * Constructor for Vec3f. + */ + public Vector3f() { + super(); + } + + /** + * Constructor + */ + public Vector3f(Vector3f src) { + set(src); + } + + /** + * Constructor + */ + public Vector3f(float x, float y, float z) { + set(x, y, z); + } + + /* (non-Javadoc) + * @see org.lwjgl.util.vector.WritableVec2f#set(float, float) + */ + public void set(float x, float y) { + this.x = x; + this.y = y; + } + + /* (non-Javadoc) + * @see org.lwjgl.util.vector.WritableVec3f#set(float, float, float) + */ + public void set(float x, float y, float z) { + this.x = x; + this.y = y; + this.z = z; + } + + /** + * Load from another Vec3f + * + * @param src The source vector + * @return this + */ + public Vector3f set(Vector3f src) { + x = src.getX(); + y = src.getY(); + z = src.getZ(); + return this; + } + + /** + * @return the length squared of the vector + */ + public float lengthSquared() { + return x * x + y * y + z * z; + } + + public float length() { + return (float) Math.sqrt(lengthSquared()); + } + + /** + * Translate a vector + * + * @param x The translation in x + * @param y the translation in y + * @return this + */ + public Vector3f translate(float x, float y, float z) { + this.x += x; + this.y += y; + this.z += z; + return this; + } + + /** + * Add a vector to another vector and place the result in a destination + * vector. + * + * @param left The LHS vector + * @param right The RHS vector + * @param dest The destination vector, or null if a new vector is to be created + * @return the sum of left and right in dest + */ + public static Vector3f add(Vector3f left, Vector3f right, Vector3f dest) { + if (dest == null) + return new Vector3f(left.x + right.x, left.y + right.y, left.z + right.z); + else { + dest.set(left.x + right.x, left.y + right.y, left.z + right.z); + return dest; + } + } + + /** + * Subtract a vector from another vector and place the result in a destination + * vector. + * + * @param left The LHS vector + * @param right The RHS vector + * @param dest The destination vector, or null if a new vector is to be created + * @return left minus right in dest + */ + public static Vector3f sub(Vector3f left, Vector3f right, Vector3f dest) { + if (dest == null) + return new Vector3f(left.x - right.x, left.y - right.y, left.z - right.z); + else { + dest.set(left.x - right.x, left.y - right.y, left.z - right.z); + return dest; + } + } + + /** + * The cross product of two vectors. + * + * @param left The LHS vector + * @param right The RHS vector + * @param dest The destination result, or null if a new vector is to be created + * @return left cross right + */ + public static Vector3f cross(Vector3f left, Vector3f right, Vector3f dest) { + if (dest == null) dest = new Vector3f(); + dest.set(left.y * right.z - left.z * right.y, + right.x * left.z - right.z * left.x, + left.x * right.y - left.y * right.x); + return dest; + } + + + /** + * Negate a vector + * + * @return this + */ + public Vector3f negate() { + x = -x; + y = -y; + z = -z; + return this; + } + + /** + * Negate a vector and place the result in a destination vector. + * + * @param dest The destination vector or null if a new vector is to be created + * @return the negated vector + */ + public Vector3f negate(Vector3f dest) { + if (dest == null) + dest = new Vector3f(); + dest.x = -x; + dest.y = -y; + dest.z = -z; + return dest; + } + + + /** + * Normalise this vector and place the result in another vector. + * + * @param dest The destination vector, or null if a new vector is to be created + * @return the normalised vector + */ + public Vector3f normalise(Vector3f dest) { + float l = length(); + + if (dest == null) + dest = new Vector3f(x / l, y / l, z / l); + else + dest.set(x / l, y / l, z / l); + + return dest; + } + + /** + * The dot product of two vectors is calculated as + * v1.x * v2.x + v1.y * v2.y + v1.z * v2.z + * + * @param left The LHS vector + * @param right The RHS vector + * @return left dot right + */ + public static float dot(Vector3f left, Vector3f right) { + return left.x * right.x + left.y * right.y + left.z * right.z; + } + + /** + * Calculate the angle between two vectors, in radians + * + * @param a A vector + * @param b The other vector + * @return the angle between the two vectors, in radians + */ + public static float angle(Vector3f a, Vector3f b) { + float dls = dot(a, b) / (a.length() * b.length()); + if (dls < -1f) + dls = -1f; + else if (dls > 1.0f) + dls = 1.0f; + return (float) Math.acos(dls); + } + + /* (non-Javadoc) + * @see org.lwjgl.vector.Vec#load(FloatBuffer) + */ + public Vector3f load(FloatBuffer buf) { + x = buf.get(); + y = buf.get(); + z = buf.get(); + return this; + } + + /* (non-Javadoc) + * @see org.lwjgl.vector.Vec#scale(float) + */ + public Vector3f scale(float scale) { + + x *= scale; + y *= scale; + z *= scale; + + return this; + + } + + /* (non-Javadoc) + * @see org.lwjgl.vector.Vec#store(FloatBuffer) + */ + public Vector3f store(FloatBuffer buf) { + + buf.put(x); + buf.put(y); + buf.put(z); + + return this; + } + + /* (non-Javadoc) + * @see java.lang.Object#toString() + */ + public String toString() { + StringBuilder sb = new StringBuilder(64); + + sb.append("Vec3f["); + sb.append(x); + sb.append(", "); + sb.append(y); + sb.append(", "); + sb.append(z); + sb.append(']'); + return sb.toString(); + } + + /** + * @return x + */ + public final float getX() { + return x; + } + + /** + * @return y + */ + public final float getY() { + return y; + } + + /** + * Set X + * + * @param x + */ + public final void setX(float x) { + this.x = x; + } + + /** + * Set Y + * + * @param y + */ + public final void setY(float y) { + this.y = y; + } + + /** + * Set Z + * + * @param z + */ + public void setZ(float z) { + this.z = z; + } + + /* (Overrides) + * @see org.lwjgl.vector.ReadableVec3f#getZ() + */ + public float getZ() { + return z; + } + + public boolean equals(Object obj) { + if (this == obj) return true; + if (obj == null) return false; + if (getClass() != obj.getClass()) return false; + Vector3f other = (Vector3f) obj; + + if (x == other.x && y == other.y && z == other.z) return true; + + return false; + } + +} diff --git a/src/main/java/com/cleanroommc/modularui/widget/ParentWidget.java b/src/main/java/com/cleanroommc/modularui/widget/ParentWidget.java index 329e5796..ab3c70a4 100644 --- a/src/main/java/com/cleanroommc/modularui/widget/ParentWidget.java +++ b/src/main/java/com/cleanroommc/modularui/widget/ParentWidget.java @@ -1,8 +1,8 @@ package com.cleanroommc.modularui.widget; +import com.cleanroommc.modularui.api.drawable.IDrawable; import com.cleanroommc.modularui.api.widget.IWidget; import com.cleanroommc.modularui.screen.ModularPanel; - import com.cleanroommc.modularui.theme.WidgetTheme; import org.jetbrains.annotations.NotNull; @@ -24,16 +24,13 @@ public List getChildren() { @Override public boolean canHover() { - return getBackground() != null || - getHoverBackground() != null || - getHoverOverlay() != null || - getTooltip() != null || - hasThemeBackground(); - } - - protected boolean hasThemeBackground() { + if (IDrawable.isVisible(getBackground()) || + IDrawable.isVisible(getHoverBackground()) || + IDrawable.isVisible(getHoverOverlay()) || + getTooltip() != null) return true; WidgetTheme widgetTheme = getWidgetTheme(getContext().getTheme()); - return widgetTheme.getBackground() != null || widgetTheme.getHoverBackground() != null; + if (getBackground() == null && IDrawable.isVisible(widgetTheme.getBackground())) return true; + return getHoverBackground() == null && IDrawable.isVisible(widgetTheme.getHoverBackground()); } public boolean addChild(IWidget child, int index) { diff --git a/src/main/java/com/cleanroommc/modularui/widget/Widget.java b/src/main/java/com/cleanroommc/modularui/widget/Widget.java index e595d440..60e71e94 100644 --- a/src/main/java/com/cleanroommc/modularui/widget/Widget.java +++ b/src/main/java/com/cleanroommc/modularui/widget/Widget.java @@ -76,7 +76,7 @@ public void initialise(@NotNull IWidget parent) { getArea().z(parent.getArea().z() + 1); if (this.guiActionListeners != null) { for (IGuiAction action : this.guiActionListeners) { - this.context.screen.registerGuiActionListener(action); + this.context.getScreen().registerGuiActionListener(action); } } } @@ -125,7 +125,7 @@ public void dispose() { if (isValid()) { if (this.guiActionListeners != null) { for (IGuiAction action : this.guiActionListeners) { - this.context.screen.removeGuiActionListener(action); + this.context.getScreen().removeGuiActionListener(action); } } @@ -303,7 +303,7 @@ public W listenGuiAction(IGuiAction action) { } this.guiActionListeners.add(action); if (isValid()) { - this.context.screen.registerGuiActionListener(action); + this.context.getScreen().registerGuiActionListener(action); } return getThis(); } diff --git a/src/main/java/com/cleanroommc/modularui/widgets/ItemSlot.java b/src/main/java/com/cleanroommc/modularui/widgets/ItemSlot.java index f3e416c9..43bbf58b 100644 --- a/src/main/java/com/cleanroommc/modularui/widgets/ItemSlot.java +++ b/src/main/java/com/cleanroommc/modularui/widgets/ItemSlot.java @@ -3,6 +3,7 @@ import codechicken.nei.guihook.GuiContainerManager; import codechicken.nei.guihook.IContainerTooltipHandler; +import com.cleanroommc.modularui.screen.ClientScreenHandler; import com.cleanroommc.modularui.api.ITheme; import com.cleanroommc.modularui.api.widget.IVanillaSlot; import com.cleanroommc.modularui.api.widget.Interactable; @@ -11,8 +12,9 @@ import com.cleanroommc.modularui.utils.item.IItemHandlerModifiable; import com.cleanroommc.modularui.integration.nei.NEIDragAndDropHandler; import com.cleanroommc.modularui.integration.nei.NEIIngredientProvider; +import com.cleanroommc.modularui.mixins.early.minecraft.GuiAccessor; import com.cleanroommc.modularui.mixins.early.minecraft.GuiContainerAccessor; -import com.cleanroommc.modularui.screen.GuiScreenWrapper; +import com.cleanroommc.modularui.mixins.early.minecraft.GuiScreenAccessor; import com.cleanroommc.modularui.screen.ModularScreen; import com.cleanroommc.modularui.screen.Tooltip; import com.cleanroommc.modularui.screen.viewport.GuiContext; @@ -29,7 +31,10 @@ import cpw.mods.fml.relauncher.Side; import cpw.mods.fml.relauncher.SideOnly; import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.GuiScreen; +import net.minecraft.client.gui.inventory.GuiContainer; import net.minecraft.client.renderer.RenderHelper; +import net.minecraft.client.renderer.entity.RenderItem; import net.minecraft.inventory.Container; import net.minecraft.inventory.Slot; import net.minecraft.item.ItemStack; @@ -65,6 +70,9 @@ protected void addToolTip(Tooltip tooltip) { @Override public void onInit() { + if (getScreen().isOverlay()) { + throw new IllegalStateException("Overlays can't have slots!"); + } size(SIZE, SIZE); } @@ -110,7 +118,8 @@ public WidgetSlotTheme getWidgetTheme(ITheme theme) { MouseData mouseData = MouseData.create(mouseButton); this.syncHandler.syncToServer(2, mouseData::writeToPacket); } else { - getScreen().getScreenWrapper().clickSlot(); + ClientScreenHandler.clickSlot(); + //getScreen().getScreenWrapper().clickSlot(); } return Result.SUCCESS; } @@ -118,7 +127,8 @@ public WidgetSlotTheme getWidgetTheme(ITheme theme) { @Override public boolean onMouseRelease(int mouseButton) { if (!this.syncHandler.isPhantom()) { - getScreen().getScreenWrapper().releaseSlot(); + ClientScreenHandler.releaseSlot(); + //getScreen().getScreenWrapper().releaseSlot(); } return true; } @@ -135,7 +145,8 @@ public boolean onMouseScroll(ModularScreen.UpOrDown scrollDirection, int amount) @Override public void onMouseDrag(int mouseButton, long timeSinceClick) { - getScreen().getScreenWrapper().dragSlot(timeSinceClick); + //getScreen().getScreenWrapper().dragSlot(timeSinceClick); + ClientScreenHandler.dragSlot(timeSinceClick); } public ModularSlot getSlot() { @@ -157,19 +168,19 @@ public Slot getVanillaSlot() { @SideOnly(Side.CLIENT) protected List getItemTooltip(ItemStack stack) { - if (!isNEILoaded) { + if (!isNEILoaded || !(getScreen().getScreenWrapper() instanceof GuiContainer guiContainer)) { return stack.getTooltip( Minecraft.getMinecraft().thePlayer, Minecraft.getMinecraft().gameSettings.advancedItemTooltips); } - List tooltips = GuiContainerManager.itemDisplayNameMultiline(stack, getScreen().getScreenWrapper(), true); + List tooltips = GuiContainerManager.itemDisplayNameMultiline(stack, guiContainer, true); GuiContainerManager.applyItemCountDetails(tooltips, stack); - if (GuiContainerManager.getManager() != null && GuiContainerManager.shouldShowTooltip(getScreen().getScreenWrapper())) { + if (GuiContainerManager.getManager() != null && GuiContainerManager.shouldShowTooltip(guiContainer)) { for (IContainerTooltipHandler handler : GuiContainerManager.getManager().instanceTooltipHandlers) - tooltips = handler.handleItemTooltip(getScreen().getScreenWrapper(), stack, getContext().getMouseX(), getContext().getMouseY(), tooltips); + tooltips = handler.handleItemTooltip(guiContainer, stack, getContext().getMouseX(), getContext().getMouseY(), tooltips); } return tooltips; @@ -187,29 +198,32 @@ public ItemSlot slot(IItemHandlerModifiable itemHandler, int index) { @SideOnly(Side.CLIENT) private void drawSlot(ModularSlot slotIn) { - GuiScreenWrapper guiScreen = getScreen().getScreenWrapper(); - GuiContainerAccessor accessor = guiScreen.getAccessor(); + GuiScreen guiScreen = getScreen().getScreenWrapper().getGuiScreen(); + if (!(guiScreen instanceof GuiContainer)) + throw new IllegalStateException("The gui must be an instance of GuiContainer if it contains slots!"); + GuiContainerAccessor acc = (GuiContainerAccessor) guiScreen; + RenderItem renderItem = ((GuiScreenAccessor) guiScreen).getItemRender(); ItemStack itemstack = slotIn.getStack(); boolean flag = false; - boolean flag1 = slotIn == accessor.getClickedSlot() && accessor.getDraggedStack() != null && !accessor.getIsRightMouseClick(); + boolean flag1 = slotIn == acc.getClickedSlot() && acc.getDraggedStack() != null && !acc.getIsRightMouseClick(); ItemStack itemstack1 = guiScreen.mc.thePlayer.inventory.getItemStack(); int amount = -1; String format = null; - if (slotIn == accessor.getClickedSlot() && accessor.getDraggedStack() != null && accessor.getIsRightMouseClick() && itemstack != null) { + if (slotIn == acc.getClickedSlot() && acc.getDraggedStack() != null && acc.getIsRightMouseClick() && itemstack != null) { itemstack = itemstack.copy(); itemstack.stackSize /= 2; - } else if (guiScreen.isDragSplitting() && guiScreen.getDragSlots().contains(slotIn) && itemstack1 != null) { - if (guiScreen.getDragSlots().size() == 1) { + } else if (acc.getDragSplitting() && acc.getDragSplittingSlots().contains(slotIn) && itemstack1 != null) { + if (acc.getDragSplittingSlots().size() == 1) { return; } // canAddItemToSlot - if (Container.func_94527_a(slotIn, itemstack1, true) && guiScreen.inventorySlots.canDragIntoSlot(slotIn)) { + if (Container.func_94527_a(slotIn, itemstack1, true) && getScreen().getContainer().canDragIntoSlot(slotIn)) { itemstack = itemstack1.copy(); flag = true; // computeStackSize - Container.func_94525_a(guiScreen.getDragSlots(), accessor.getDragSplittingLimit(), itemstack, slotIn.getStack() == null ? 0 : slotIn.getStack().stackSize); + Container.func_94525_a(acc.getDragSplittingSlots(), acc.getDragSplittingLimit(), itemstack, slotIn.getStack() == null ? 0 : slotIn.getStack().stackSize); int k = Math.min(itemstack.getMaxStackSize(), slotIn.getSlotStackLimit()); if (itemstack.stackSize > k) { @@ -218,13 +232,13 @@ private void drawSlot(ModularSlot slotIn) { itemstack.stackSize = k; } } else { - guiScreen.getDragSlots().remove(slotIn); - accessor.invokeUpdateDragSplitting(); + acc.getDragSplittingSlots().remove(slotIn); + acc.invokeUpdateDragSplitting(); } } - guiScreen.setZ(100f); - GuiScreenWrapper.getItemRenderer().zLevel = 100.0F; + ((GuiAccessor) guiScreen).setZLevel(100f); + renderItem.zLevel = 100.0F; if (!flag1) { if (flag) { @@ -238,7 +252,7 @@ private void drawSlot(ModularSlot slotIn) { GL11.glEnable(GL11.GL_DEPTH_TEST); GL11.glEnable(GL12.GL_RESCALE_NORMAL); // render the item itself - GuiScreenWrapper.getItemRenderer().renderItemAndEffectIntoGUI(Minecraft.getMinecraft().fontRenderer, Minecraft.getMinecraft().getTextureManager(), itemstack, 1, 1); + renderItem.renderItemAndEffectIntoGUI(Minecraft.getMinecraft().fontRenderer, Minecraft.getMinecraft().getTextureManager(), itemstack, 1, 1); GuiDraw.afterRenderItemAndEffectIntoGUI(itemstack); GL11.glDisable(GL12.GL_RESCALE_NORMAL); if (amount < 0) { @@ -275,14 +289,14 @@ private void drawSlot(ModularSlot slotIn) { int cachedCount = itemstack.stackSize; itemstack.stackSize = 1; // required to not render the amount overlay // render other overlays like durability bar - GuiScreenWrapper.getItemRenderer().renderItemOverlayIntoGUI(guiScreen.getFontRenderer(), Minecraft.getMinecraft().getTextureManager(), itemstack, 1, 1, null); + renderItem.renderItemOverlayIntoGUI(((GuiScreenAccessor) guiScreen).getFontRenderer(), Minecraft.getMinecraft().getTextureManager(), itemstack, 1, 1, null); itemstack.stackSize = cachedCount; GL11.glDisable(GL11.GL_DEPTH_TEST); } } - GuiScreenWrapper.getItemRenderer().zLevel = 0.0F; - guiScreen.setZ(0f); + ((GuiAccessor) guiScreen).setZLevel(0f); + renderItem.zLevel = 0f; } @Override diff --git a/src/main/java/com/cleanroommc/modularui/widgets/ItemSlotLong.java b/src/main/java/com/cleanroommc/modularui/widgets/ItemSlotLong.java index c0fa6d0c..a80a1d0b 100644 --- a/src/main/java/com/cleanroommc/modularui/widgets/ItemSlotLong.java +++ b/src/main/java/com/cleanroommc/modularui/widgets/ItemSlotLong.java @@ -8,12 +8,20 @@ import com.cleanroommc.modularui.api.widget.Interactable; import com.cleanroommc.modularui.integration.nei.NEIDragAndDropHandler; import com.cleanroommc.modularui.integration.nei.NEIIngredientProvider; +import com.cleanroommc.modularui.mixins.early.minecraft.GuiAccessor; +import com.cleanroommc.modularui.mixins.early.minecraft.GuiScreenAccessor; +import com.cleanroommc.modularui.screen.ClientScreenHandler; import com.cleanroommc.modularui.screen.ModularScreen; import com.cleanroommc.modularui.screen.Tooltip; import com.cleanroommc.modularui.theme.WidgetSlotTheme; import com.cleanroommc.modularui.utils.MouseData; import com.cleanroommc.modularui.widget.Widget; +import net.minecraft.client.gui.GuiScreen; +import net.minecraft.client.gui.inventory.GuiContainer; + +import net.minecraft.client.renderer.entity.RenderItem; + import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.lwjgl.opengl.GL11; @@ -24,7 +32,6 @@ import com.cleanroommc.modularui.drawable.TextRenderer; import com.cleanroommc.modularui.utils.item.IItemHandlerLong; import com.cleanroommc.modularui.mixins.early.minecraft.GuiContainerAccessor; -import com.cleanroommc.modularui.screen.GuiScreenWrapper; import com.cleanroommc.modularui.screen.viewport.GuiContext; import com.cleanroommc.modularui.theme.WidgetTheme; import com.cleanroommc.modularui.utils.Alignment; @@ -70,6 +77,9 @@ protected void addToolTip(Tooltip tooltip) { @Override public void onInit() { + if (getScreen().isOverlay()) { + throw new IllegalStateException("Overlays can't have slots!"); + } size(SIZE, SIZE); } @@ -115,7 +125,8 @@ public WidgetSlotTheme getWidgetTheme(ITheme theme) { MouseData mouseData = MouseData.create(mouseButton); this.syncHandler.syncToServer(2, mouseData::writeToPacket); } else { - getScreen().getScreenWrapper().clickSlot(); + ClientScreenHandler.clickSlot(); + //getScreen().getScreenWrapper().clickSlot(); } return Result.SUCCESS; } @@ -123,7 +134,8 @@ public WidgetSlotTheme getWidgetTheme(ITheme theme) { @Override public boolean onMouseRelease(int mouseButton) { if (!this.syncHandler.isPhantom()) { - getScreen().getScreenWrapper().releaseSlot(); + ClientScreenHandler.releaseSlot(); + //getScreen().getScreenWrapper().releaseSlot(); } return true; } @@ -140,7 +152,8 @@ public boolean onMouseScroll(ModularScreen.UpOrDown scrollDirection, int amount) @Override public void onMouseDrag(int mouseButton, long timeSinceClick) { - getScreen().getScreenWrapper().dragSlot(timeSinceClick); + //getScreen().getScreenWrapper().dragSlot(timeSinceClick); + ClientScreenHandler.dragSlot(timeSinceClick); } public ModularSlotLong getSlot() { @@ -162,19 +175,19 @@ public Slot getVanillaSlot() { @SideOnly(Side.CLIENT) protected List getItemTooltip(ItemStack stack) { - if (!isNEILoaded) { + if (!isNEILoaded || !(getScreen().getScreenWrapper() instanceof GuiContainer guiContainer)) { return stack.getTooltip( Minecraft.getMinecraft().thePlayer, Minecraft.getMinecraft().gameSettings.advancedItemTooltips); } - List tooltips = GuiContainerManager.itemDisplayNameMultiline(stack, getScreen().getScreenWrapper(), true); + List tooltips = GuiContainerManager.itemDisplayNameMultiline(stack, guiContainer, true); GuiContainerManager.applyItemCountDetails(tooltips, stack); - if (GuiContainerManager.getManager() != null && GuiContainerManager.shouldShowTooltip(getScreen().getScreenWrapper())) { + if (GuiContainerManager.getManager() != null && GuiContainerManager.shouldShowTooltip(guiContainer)) { for (IContainerTooltipHandler handler : GuiContainerManager.getManager().instanceTooltipHandlers) - tooltips = handler.handleItemTooltip(getScreen().getScreenWrapper(), stack, getContext().getMouseX(), getContext().getMouseY(), tooltips); + tooltips = handler.handleItemTooltip(guiContainer, stack, getContext().getMouseX(), getContext().getMouseY(), tooltips); } return tooltips; @@ -192,30 +205,33 @@ public ItemSlotLong slot(IItemHandlerLong itemHandler, int index) { @SideOnly(Side.CLIENT) private void drawSlot(ModularSlotLong slotIn) { - GuiScreenWrapper guiScreen = getScreen().getScreenWrapper(); - GuiContainerAccessor accessor = guiScreen.getAccessor(); + GuiScreen guiScreen = getScreen().getScreenWrapper().getGuiScreen(); + if (!(guiScreen instanceof GuiContainer)) + throw new IllegalStateException("The gui must be an instance of GuiContainer if it contains slots!"); + GuiContainerAccessor acc = (GuiContainerAccessor) guiScreen; + RenderItem renderItem = ((GuiScreenAccessor) guiScreen).getItemRender(); IItemStackLong itemstack = slotIn.getStackLong(); boolean flag = false; - boolean flag1 = slotIn == accessor.getClickedSlot() && accessor.getDraggedStack() != null && !accessor.getIsRightMouseClick(); + boolean flag1 = slotIn == acc.getClickedSlot() && acc.getDraggedStack() != null && !acc.getIsRightMouseClick(); ItemStack itemstack2 = guiScreen.mc.thePlayer.inventory.getItemStack(); IItemStackLong itemstack1 = itemstack2 == null ? null : new ItemStackLong(itemstack2); long amount = -1; String format = null; - if (slotIn == accessor.getClickedSlot() && accessor.getDraggedStack() != null && accessor.getIsRightMouseClick() && itemstack != null) { + if (slotIn == acc.getClickedSlot() && acc.getDraggedStack() != null && acc.getIsRightMouseClick() && itemstack != null) { itemstack = itemstack.copy(); itemstack.setStackSize(itemstack.getStackSize() / 2); - } else if (guiScreen.isDragSplitting() && guiScreen.getDragSlots().contains(slotIn) && itemstack1 != null) { - if (guiScreen.getDragSlots().size() == 1) { + } else if (acc.getDragSplitting() && acc.getDragSplittingSlots().contains(slotIn) && itemstack1 != null) { + if (acc.getDragSplittingSlots().size() == 1) { return; } // canAddItemToSlot - if (Container.func_94527_a(slotIn, itemstack2, true) && guiScreen.inventorySlots.canDragIntoSlot(slotIn)) { + if (Container.func_94527_a(slotIn, itemstack2, true) && getScreen().getContainer().canDragIntoSlot(slotIn)) { itemstack = itemstack1.copy(); flag = true; // computeStackSize - Container.func_94525_a(guiScreen.getDragSlots(), accessor.getDragSplittingLimit(), itemstack.getAsItemStack(), slotIn.getStack() == null ? 0 : slotIn.getStack().stackSize); + Container.func_94525_a(acc.getDragSplittingSlots(), acc.getDragSplittingLimit(), itemstack.getAsItemStack(), slotIn.getStack() == null ? 0 : slotIn.getStack().stackSize); long k = Math.min(itemstack.getMaxStackSize(), slotIn.getSlotStackLimit()); if (itemstack.getStackSize() > k) { @@ -224,13 +240,13 @@ private void drawSlot(ModularSlotLong slotIn) { itemstack.setStackSize(k); } } else { - guiScreen.getDragSlots().remove(slotIn); - accessor.invokeUpdateDragSplitting(); + acc.getDragSplittingSlots().remove(slotIn); + acc.invokeUpdateDragSplitting(); } } - guiScreen.setZ(100f); - GuiScreenWrapper.getItemRenderer().zLevel = 100.0F; + ((GuiAccessor) guiScreen).setZLevel(100f); + renderItem.zLevel = 100.0F; if (!flag1) { if (flag) { @@ -244,7 +260,7 @@ private void drawSlot(ModularSlotLong slotIn) { GL11.glEnable(GL11.GL_DEPTH_TEST); GL11.glEnable(GL12.GL_RESCALE_NORMAL); // render the item itself - GuiScreenWrapper.getItemRenderer().renderItemAndEffectIntoGUI(Minecraft.getMinecraft().fontRenderer, Minecraft.getMinecraft().getTextureManager(), itemstack.getAsItemStack(), 1, 1); + renderItem.renderItemAndEffectIntoGUI(Minecraft.getMinecraft().fontRenderer, Minecraft.getMinecraft().getTextureManager(), itemstack.getAsItemStack(), 1, 1); GuiDraw.afterRenderItemAndEffectIntoGUI(itemstack.getAsItemStack()); GL11.glDisable(GL12.GL_RESCALE_NORMAL); if (amount < 0) { @@ -281,14 +297,14 @@ private void drawSlot(ModularSlotLong slotIn) { long cachedCount = itemstack.getStackSize(); itemstack.setStackSize(1); // required to not render the amount overlay // render other overlays like durability bar - GuiScreenWrapper.getItemRenderer().renderItemOverlayIntoGUI(guiScreen.getFontRenderer(), Minecraft.getMinecraft().getTextureManager(), itemstack.getAsItemStack(), 1, 1, null); + renderItem.renderItemOverlayIntoGUI(((GuiScreenAccessor) guiScreen).getFontRenderer(), Minecraft.getMinecraft().getTextureManager(), itemstack.getAsItemStack(), 1, 1, null); itemstack.setStackSize(cachedCount); GL11.glDisable(GL11.GL_DEPTH_TEST); } } - GuiScreenWrapper.getItemRenderer().zLevel = 0.0F; - guiScreen.setZ(0f); + ((GuiAccessor) guiScreen).setZLevel(0.0F); + renderItem.zLevel = 0f; } @Override diff --git a/src/main/java/com/cleanroommc/modularui/widgets/TextWidget.java b/src/main/java/com/cleanroommc/modularui/widgets/TextWidget.java index 466a79e4..54e5fa40 100644 --- a/src/main/java/com/cleanroommc/modularui/widgets/TextWidget.java +++ b/src/main/java/com/cleanroommc/modularui/widgets/TextWidget.java @@ -34,7 +34,7 @@ public TextWidget(String key) { public void draw(GuiContext context, WidgetTheme widgetTheme) { TextRenderer renderer = TextRenderer.SHARED; renderer.setColor(this.colorChanged ? this.color : widgetTheme.getTextColor()); - renderer.setAlignment(this.alignment, getArea().w() + 1, getArea().h()); + renderer.setAlignment(this.alignment, getArea().w() + this.scale, getArea().h()); renderer.setShadow(this.shadowChanged ? this.shadow : widgetTheme.getTextShadow()); renderer.setPos(getArea().getPadding().left, getArea().getPadding().top); renderer.setScale(this.scale); @@ -60,11 +60,13 @@ private TextRenderer simulate(float maxWidth) { @Override public int getDefaultHeight() { - float maxWidth = getScreen().getScreenArea().width; + float maxWidth; if (resizer() != null && resizer().isWidthCalculated()) { - maxWidth = getArea().width + 1; + maxWidth = getArea().width + this.scale; } else if (getParent().resizer() != null && getParent().resizer().isWidthCalculated()) { - maxWidth = getParent().getArea().width + 1; + maxWidth = getParent().getArea().width + this.scale; + } else { + maxWidth = getScreen().getScreenArea().width; } TextRenderer renderer = simulate(maxWidth); Box padding = getArea().getPadding();