5454import net .minecraftforge .fml .relauncher .Side ;
5555import net .minecraftforge .fml .relauncher .SideOnly ;
5656
57+ import it .unimi .dsi .fastutil .objects .ObjectArrayList ;
5758import org .jetbrains .annotations .ApiStatus ;
5859import org .jetbrains .annotations .Nullable ;
60+ import org .jetbrains .annotations .UnmodifiableView ;
5961import org .lwjgl .input .Keyboard ;
6062import org .lwjgl .input .Mouse ;
6163import org .lwjgl .opengl .GL11 ;
@@ -78,49 +80,18 @@ public class ClientScreenHandler {
7880 private static long ticks = 0L ;
7981
8082 private static IMuiScreen lastMui ;
83+ private static final ObjectArrayList <IMuiScreen > muiStack = new ObjectArrayList <>(8 );
8184
8285 // we need to know the actual gui and not some fake screen some other mod overwrites
8386 @ SubscribeEvent (priority = EventPriority .LOWEST )
84- public void onGuiOpen (GuiOpenEvent event ) {
87+ public void onGuiChange (GuiOpenEvent event ) {
8588 onGuiChanged (getMCScreen (), event .getGui ());
8689 }
8790
88- private static void onGuiChanged (GuiScreen oldScreen , GuiScreen newScreen ) {
89- if (oldScreen == newScreen ) return ;
90- defaultContext .reset ();
91-
92- GuiScreen lastParent = null ;
93- if (lastMui != null ) {
94- if (newScreen == lastMui ) {
95- // reopen
96- return ;
97- }
98- lastParent = lastMui .getScreen ().getContext ().getParentScreen ();
99- lastMui .getScreen ().onCloseParent ();
100- lastMui = null ;
101- currentScreen = null ;
102- lastChar = null ;
103- }
104-
105- if (newScreen instanceof IMuiScreen muiScreen ) {
106- lastMui = muiScreen ;
107- currentScreen = muiScreen .getScreen ();
108- GuiScreen parent = oldScreen ;
109- if (lastParent == parent && parent instanceof IMuiScreen oldMuiScreen ) {
110- parent = oldMuiScreen .getScreen ().getContext ().getParentScreen ();
111- }
112- currentScreen .getContext ().setParentScreen (parent );
113- fpsCounter .reset ();
114- }
115-
116- GuiErrorHandler .INSTANCE .clear ();
117- OverlayManager .onGuiOpen (newScreen );
118- }
119-
12091 @ SubscribeEvent
12192 public void onGuiInit (GuiScreenEvent .InitGuiEvent .Post event ) {
12293 defaultContext .updateScreenArea (event .getGui ().width , event .getGui ().height );
123- if (checkGui (event .getGui ())) {
94+ if (validateGui (event .getGui ())) {
12495 currentScreen .onResize (event .getGui ().width , event .getGui ().height );
12596 }
12697 OverlayStack .foreach (ms -> ms .onResize (event .getGui ().width , event .getGui ().height ), false );
@@ -140,7 +111,7 @@ public void onGuiInputLow(GuiScreenEvent.KeyboardInputEvent.Pre event) throws IO
140111 }
141112
142113 private static void inputEvent (GuiScreenEvent .KeyboardInputEvent .Pre event , InputPhase phase ) throws IOException {
143- if (checkGui (event .getGui ())) currentScreen .getContext ().updateEventState ();
114+ if (validateGui (event .getGui ())) currentScreen .getContext ().updateEventState ();
144115 if (handleKeyboardInput (currentScreen , event .getGui (), phase )) {
145116 event .setCanceled (true );
146117 }
@@ -150,7 +121,7 @@ private static void inputEvent(GuiScreenEvent.KeyboardInputEvent.Pre event, Inpu
150121 @ SubscribeEvent (priority = EventPriority .HIGH )
151122 public void onGuiInputHigh (GuiScreenEvent .MouseInputEvent .Pre event ) throws IOException {
152123 defaultContext .updateEventState ();
153- if (checkGui (event .getGui ())) currentScreen .getContext ().updateEventState ();
124+ if (validateGui (event .getGui ())) currentScreen .getContext ().updateEventState ();
154125 if (handleMouseInput (Mouse .getEventButton (), currentScreen , event .getGui ())) {
155126 Platform .unFocusRecipeViewer ();
156127 event .setCanceled (true );
@@ -159,7 +130,7 @@ public void onGuiInputHigh(GuiScreenEvent.MouseInputEvent.Pre event) throws IOEx
159130 int w = Mouse .getEventDWheel ();
160131 if (w == 0 ) return ;
161132 UpOrDown upOrDown = w > 0 ? UpOrDown .UP : UpOrDown .DOWN ;
162- checkGui (event .getGui ());
133+ validateGui (event .getGui ());
163134 if (doAction (currentScreen , ms -> ms .onMouseScroll (upOrDown , Math .abs (w )))) {
164135 event .setCanceled (true );
165136 }
@@ -171,7 +142,7 @@ public void onGuiDraw(GuiScreenEvent.DrawScreenEvent.Pre event) {
171142 float pt = event .getRenderPartialTicks ();
172143 defaultContext .updateState (mx , my , pt );
173144 defaultContext .reset ();
174- if (checkGui (event .getGui ())) {
145+ if (validateGui (event .getGui ())) {
175146 currentScreen .getContext ().updateState (mx , my , pt );
176147 drawScreen (currentScreen , currentScreen .getScreenWrapper ().getGuiScreen (), mx , my , pt );
177148 event .setCanceled (true );
@@ -190,7 +161,7 @@ public void onTick(TickEvent.ClientTickEvent event) {
190161 if (event .phase == TickEvent .Phase .END ) {
191162 OverlayStack .onTick ();
192163 defaultContext .tick ();
193- if (checkGui ()) {
164+ if (validateGui ()) {
194165 currentScreen .onUpdate ();
195166 }
196167 ticks ++;
@@ -214,6 +185,59 @@ public static void onFrameUpdate() {
214185 if (currentScreen != null ) currentScreen .onFrameUpdate ();
215186 }
216187
188+ private static void onGuiChanged (GuiScreen oldScreen , GuiScreen newScreen ) {
189+ if (oldScreen == newScreen ) return ;
190+ defaultContext .reset ();
191+ fpsCounter .reset ();
192+ GuiErrorHandler .INSTANCE .clear ();
193+
194+ IMuiScreen lastLastMui = lastMui ;
195+ if (lastMui != null ) {
196+ // called on open and close
197+ // invalidate last mui screen, but keep it in stack
198+ invalidateCurrentScreen ();
199+ }
200+
201+ if (newScreen instanceof IMuiScreen muiScreen ) {
202+ lastMui = muiScreen ;
203+ currentScreen = muiScreen .getScreen ();
204+ muiStack .remove (muiScreen );
205+ muiStack .add (muiScreen ); // put screen to the top of the stack
206+ GuiScreen lastParent = lastLastMui != null ? lastLastMui .getScreen ().getContext ().getParentScreen () : null ;
207+ if (lastParent != muiScreen ) {
208+ // new screen in the stack
209+ currentScreen .getContext ().setParentScreen (oldScreen );
210+ } else {
211+ // last parent is equal to new screen -> effectively popping the current screen from the stack
212+ // the current screen will disconnect from the stack and therefore need to dispose it
213+ muiStack .remove (lastLastMui );
214+ lastLastMui .getScreen ().getPanelManager ().dispose ();
215+ }
216+ } else if (newScreen == null ) {
217+ // closing -> clear stack and dispose every screen
218+ invalidateMuiStack ();
219+ }
220+
221+ OverlayManager .onGuiOpen (newScreen );
222+ }
223+
224+ private static void invalidateCurrentScreen () {
225+ // reset mouse inputs, relevant when screen gets reopened
226+ if (lastMui != null ) {
227+ ((GuiScreenAccessor ) lastMui .getGuiScreen ()).setEventButton (-1 );
228+ ((GuiScreenAccessor ) lastMui .getGuiScreen ()).setLastMouseEvent (-1 );
229+ lastMui .getScreen ().getPanelManager ().closeAll ();
230+ lastMui = null ;
231+ }
232+ currentScreen = null ;
233+ lastChar = null ;
234+ }
235+
236+ private static void invalidateMuiStack () {
237+ muiStack .forEach (muiScreen -> muiScreen .getScreen ().getPanelManager ().dispose ());
238+ muiStack .clear ();
239+ }
240+
217241 private static boolean doAction (@ Nullable ModularScreen muiScreen , Predicate <ModularScreen > action ) {
218242 return OverlayStack .interact (action , true ) || (muiScreen != null && action .test (muiScreen ));
219243 }
@@ -312,7 +336,7 @@ public static void dragSlot(long timeSinceLastClick) {
312336
313337 public static void clickSlot (ModularScreen ms , Slot slot ) {
314338 GuiScreen screen = ms .getScreenWrapper ().getGuiScreen ();
315- if (screen instanceof GuiScreenAccessor acc && screen instanceof IClickableGuiContainer clickableGuiContainer && checkGui (screen )) {
339+ if (screen instanceof GuiScreenAccessor acc && screen instanceof IClickableGuiContainer clickableGuiContainer && validateGui (screen )) {
316340 ModularGuiContext ctx = ms .getContext ();
317341 List <GuiButton > buttonList = acc .getButtonList ();
318342 try {
@@ -502,7 +526,7 @@ public static void drawDebugScreen(@Nullable ModularScreen muiScreen, @Nullable
502526 fpsCounter .onDraw ();
503527 if (!ModularUIConfig .guiDebugMode ) return ;
504528 if (muiScreen == null ) {
505- if (checkGui ()) {
529+ if (validateGui ()) {
506530 muiScreen = currentScreen ;
507531 } else {
508532 if (fallback == null ) return ;
@@ -614,16 +638,27 @@ public static ModularScreen getMuiScreen() {
614638 return currentScreen ;
615639 }
616640
617- private static boolean checkGui () {
618- return MCHelper .hasMc () && checkGui (Minecraft .getMinecraft ().currentScreen );
641+ @ UnmodifiableView
642+ public static List <IMuiScreen > getMuiStack () {
643+ return Collections .unmodifiableList (muiStack );
644+ }
645+
646+ private static boolean validateGui () {
647+ return MCHelper .hasMc () && validateGui (Minecraft .getMinecraft ().currentScreen );
619648 }
620649
621- private static boolean checkGui (GuiScreen screen ) {
622- if (!MCHelper .hasMc () || currentScreen == null || !(screen instanceof IMuiScreen muiScreen )) return false ;
650+ private static boolean validateGui (GuiScreen screen ) {
651+ if (!MCHelper .hasMc () || currentScreen == null || !(screen instanceof IMuiScreen muiScreen )) {
652+ // no mui screen currently open
653+ return false ;
654+ }
623655 if (screen != Minecraft .getMinecraft ().currentScreen || muiScreen .getScreen () != currentScreen ) {
656+ // mui screen doesn't match the events screen -> invalidate
624657 defaultContext .reset ();
625- currentScreen = null ;
626- lastChar = null ;
658+ invalidateCurrentScreen ();
659+ if (MCHelper .getCurrentScreen () == null ) {
660+ invalidateMuiStack ();
661+ }
627662 return false ;
628663 }
629664 return true ;
@@ -634,7 +669,7 @@ public static GuiContext getDefaultContext() {
634669 }
635670
636671 public static GuiContext getBestContext () {
637- if (checkGui ()) {
672+ if (validateGui ()) {
638673 return currentScreen .getContext ();
639674 }
640675 return defaultContext ;
0 commit comments