Skip to content

Commit

Permalink
Detach Mui from GuiScreenWrapper (CleanroomMC#64)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
brachy84 authored Sep 1, 2024
1 parent 3e1e9cc commit 47cf883
Show file tree
Hide file tree
Showing 65 changed files with 2,941 additions and 557 deletions.
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,7 @@ enableSpotless = false

# Enable JUnit testing platform used for testing your code.
# Uses JUnit 5. See guide and documentation here: https://junit.org/junit5/docs/current/user-guide/
enableJUnit = false
enableJUnit = true

# Deployment debug setting
# Uncomment this to test deployments to CurseForge and Modrinth
Expand Down
17 changes: 2 additions & 15 deletions src/main/java/com/cleanroommc/modularui/ClientEventHandler.java
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
package com.cleanroommc.modularui;

import com.cleanroommc.modularui.drawable.Stencil;
import com.cleanroommc.modularui.screen.GuiScreenWrapper;
import com.cleanroommc.modularui.screen.ModularScreen;
import com.cleanroommc.modularui.screen.GuiContainerWrapper;

import net.minecraftforge.client.event.GuiScreenEvent;
import net.minecraftforge.fml.common.eventhandler.EventPriority;
Expand All @@ -11,7 +10,6 @@
import net.minecraftforge.fml.relauncher.Side;
import net.minecraftforge.fml.relauncher.SideOnly;

import org.lwjgl.input.Mouse;
import org.lwjgl.opengl.GL11;

import java.io.IOException;
Expand All @@ -32,17 +30,6 @@ public static void onClientTick(TickEvent.ClientTickEvent event) {
}
}

@SubscribeEvent
public static void onScroll(GuiScreenEvent.MouseInputEvent.Pre event) {
ModularScreen screen = ModularScreen.getCurrent();
if (screen != null) {
int w = Mouse.getEventDWheel();
if (w != 0 && screen.onMouseScroll(w > 0 ? ModularScreen.UpOrDown.UP : ModularScreen.UpOrDown.DOWN, Math.abs(w))) {
event.setCanceled(true);
}
}
}

@SubscribeEvent
public static void preDraw(TickEvent.RenderTickEvent event) {
if (event.phase == TickEvent.Phase.START) {
Expand Down Expand Up @@ -78,6 +65,6 @@ public static void onGuiInput(GuiScreenEvent.KeyboardInputEvent.Pre event) {
}

private static boolean hasDraggable(GuiScreenEvent event) {
return event.getGui() instanceof GuiScreenWrapper screenWrapper && screenWrapper.getScreen().getContext().hasDraggable();
return event.getGui() instanceof GuiContainerWrapper screenWrapper && screenWrapper.getScreen().getContext().hasDraggable();
}
}
7 changes: 6 additions & 1 deletion src/main/java/com/cleanroommc/modularui/ClientProxy.java
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
package com.cleanroommc.modularui;

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.keybind.KeyBindHandler;
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;

Expand All @@ -31,10 +33,13 @@ void preInit(FMLPreInitializationEvent event) {
super.preInit(event);

MinecraftForge.EVENT_BUS.register(ClientEventHandler.class);
MinecraftForge.EVENT_BUS.register(ClientScreenHandler.class);
MinecraftForge.EVENT_BUS.register(OverlayManager.class);
MinecraftForge.EVENT_BUS.register(KeyBindHandler.class);

if (ModularUIConfig.enabledTestGuis) {
MinecraftForge.EVENT_BUS.register(EventHandler.class);
OverlayTest.init();
}

DrawableSerialization.init();
Expand Down
9 changes: 2 additions & 7 deletions src/main/java/com/cleanroommc/modularui/CommonProxy.java
Original file line number Diff line number Diff line change
@@ -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.screen.ModularContainer;
Expand Down Expand Up @@ -42,9 +39,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) {
Expand Down
4 changes: 0 additions & 4 deletions src/main/java/com/cleanroommc/modularui/ModularUI.java
Original file line number Diff line number Diff line change
Expand Up @@ -49,10 +49,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);
Expand Down
106 changes: 106 additions & 0 deletions src/main/java/com/cleanroommc/modularui/api/IMuiScreen.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
package com.cleanroommc.modularui.api;

import com.cleanroommc.modularui.core.mixin.GuiContainerAccessor;
import com.cleanroommc.modularui.screen.ClientScreenHandler;
import com.cleanroommc.modularui.screen.ModularScreen;

import net.minecraft.client.gui.GuiScreen;
import net.minecraft.client.gui.inventory.GuiContainer;
import net.minecraft.inventory.Slot;

import net.minecraftforge.fml.relauncher.Side;
import net.minecraftforge.fml.relauncher.SideOnly;

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();

/**
* {@link GuiScreen GuiScreens} need to be focused when a text field is focused, to prevent key input from
* behaving unexpectedly.
*
* @param focused if the screen should be focused
*/
default void setFocused(boolean focused) {
getGuiScreen().setFocused(focused);
}

/**
* 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;
}
}
48 changes: 48 additions & 0 deletions src/main/java/com/cleanroommc/modularui/api/MCHelper.java
Original file line number Diff line number Diff line change
@@ -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().player;
}
return null;
}

public static boolean closeScreen() {
if (!hasMc()) return false;
EntityPlayerSP player = Minecraft.getMinecraft().player;
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;
}
}
19 changes: 18 additions & 1 deletion src/main/java/com/cleanroommc/modularui/api/UIFactory.java
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
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;

import net.minecraft.entity.player.EntityPlayer;
Expand Down Expand Up @@ -51,6 +52,22 @@ public interface UIFactory<D extends GuiData> {
@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.
*
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
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;
Expand All @@ -10,6 +11,7 @@
import net.minecraftforge.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
Expand Down Expand Up @@ -138,6 +140,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.
*/
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
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
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.cleanroommc.modularui.core.mixin;

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);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package com.cleanroommc.modularui.core.mixin;

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 hovered;

@Shadow
protected 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.hovered) this.hovered = !OverlayStack.isHoveringOverlay();
return getHoverState(this.hovered);
}
}
Loading

0 comments on commit 47cf883

Please sign in to comment.