From 452f9446783ffa19ff21632616793029b56590b6 Mon Sep 17 00:00:00 2001 From: Evan Debenham Date: Sat, 30 Aug 2025 17:32:57 -0400 Subject: [PATCH] v3.2.3: implemented mostly complete new iOS inset support --- .../src/main/java/com/watabou/noosa/Game.java | 4 -- .../java/com/watabou/noosa/NoosaScript.java | 2 +- .../java/com/watabou/noosa/TextInput.java | 2 +- .../java/com/watabou/utils/DeviceCompat.java | 2 +- .../scenes/GameScene.java | 8 +++ .../ios/IOSLauncher.java | 11 +++- .../ios/IOSPlatformSupport.java | 59 ++++++++++++++----- 7 files changed, 64 insertions(+), 24 deletions(-) diff --git a/SPD-classes/src/main/java/com/watabou/noosa/Game.java b/SPD-classes/src/main/java/com/watabou/noosa/Game.java index 884199aec..0f73ea0e3 100644 --- a/SPD-classes/src/main/java/com/watabou/noosa/Game.java +++ b/SPD-classes/src/main/java/com/watabou/noosa/Game.java @@ -50,9 +50,6 @@ public class Game implements ApplicationListener { public static int width; public static int height; - //number of pixels from bottom of view before rendering starts - public static int bottomInset; - // Density: mdpi=1, hdpi=1.5, xhdpi=2... public static float density = 1; @@ -136,7 +133,6 @@ public class Game implements ApplicationListener { Vertexbuffer.reload(); } - height -= bottomInset; if (height != Game.height || width != Game.width) { Game.width = width; diff --git a/SPD-classes/src/main/java/com/watabou/noosa/NoosaScript.java b/SPD-classes/src/main/java/com/watabou/noosa/NoosaScript.java index 09a2622a8..182966aba 100644 --- a/SPD-classes/src/main/java/com/watabou/noosa/NoosaScript.java +++ b/SPD-classes/src/main/java/com/watabou/noosa/NoosaScript.java @@ -172,7 +172,7 @@ public class NoosaScript extends Script { Gdx.gl20.glScissor( Math.round(camera.x * xScale), - Math.round((Game.height - camera.screenHeight - camera.y) * yScale) + Game.bottomInset, + Math.round((Game.height - camera.screenHeight - camera.y) * yScale), Math.round(camera.screenWidth * xScale), Math.round(camera.screenHeight * yScale)); } else { diff --git a/SPD-classes/src/main/java/com/watabou/noosa/TextInput.java b/SPD-classes/src/main/java/com/watabou/noosa/TextInput.java index 464876c8e..5d5673394 100644 --- a/SPD-classes/src/main/java/com/watabou/noosa/TextInput.java +++ b/SPD-classes/src/main/java/com/watabou/noosa/TextInput.java @@ -64,7 +64,7 @@ public class TextInput extends Component { //use a custom viewport here to ensure stage camera matches game camera Viewport viewport = new Viewport() {}; viewport.setWorldSize(Game.width, Game.height); - viewport.setScreenBounds(0, Game.bottomInset, Game.width, Game.height); + viewport.setScreenBounds(0, 0, Game.width, Game.height); viewport.setCamera(new OrthographicCamera()); //TODO this is needed for the moment as Spritebatch switched to using VAOs in libGDX v1.13.1 // This results in HARD crashes atm, whereas old vertex arrays work fine diff --git a/SPD-classes/src/main/java/com/watabou/utils/DeviceCompat.java b/SPD-classes/src/main/java/com/watabou/utils/DeviceCompat.java index 5e29a15e1..da5d7a3b7 100644 --- a/SPD-classes/src/main/java/com/watabou/utils/DeviceCompat.java +++ b/SPD-classes/src/main/java/com/watabou/utils/DeviceCompat.java @@ -66,7 +66,7 @@ public class DeviceCompat { //...and in the Y dimension public static float getRealPixelScaleY(){ - return ((Gdx.graphics.getBackBufferHeight()-Game.bottomInset) / (float)Game.height ); + return (Gdx.graphics.getBackBufferHeight() / (float)Game.height ); } } diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/scenes/GameScene.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/scenes/GameScene.java index 9730167ea..bbcf0b408 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/scenes/GameScene.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/scenes/GameScene.java @@ -427,6 +427,14 @@ public class GameScene extends PixelScene { toolbar.setRect( insets.left, uiCamera.height - toolbar.height() - insets.bottom, uiCamera.width - insets.right, toolbar.height() ); } + //TODO this is pretty barebones, could be minimized or avoided perhaps? + if (insets.bottom > 0){ + SkinnedBlock blackBar = new SkinnedBlock(uiCamera.width, insets.bottom, TextureCache.createSolid(0xFF000000)); + blackBar.camera = uiCamera; + blackBar.y = uiCamera.height - insets.bottom; + add(blackBar); + } + layoutTags(); switch (InterlevelScene.mode) { diff --git a/ios/src/main/java/com/shatteredpixel/shatteredpixeldungeon/ios/IOSLauncher.java b/ios/src/main/java/com/shatteredpixel/shatteredpixeldungeon/ios/IOSLauncher.java index 36006772a..85aa3711d 100644 --- a/ios/src/main/java/com/shatteredpixel/shatteredpixeldungeon/ios/IOSLauncher.java +++ b/ios/src/main/java/com/shatteredpixel/shatteredpixeldungeon/ios/IOSLauncher.java @@ -45,6 +45,7 @@ import org.robovm.apple.foundation.NSMutableDictionary; import org.robovm.apple.foundation.NSObject; import org.robovm.apple.foundation.NSString; import org.robovm.apple.uikit.UIApplication; +import org.robovm.apple.uikit.UIInterfaceOrientation; import java.io.File; @@ -106,9 +107,9 @@ public class IOSLauncher extends IOSApplication.Delegate { //if the application has a short status bar (no notch), then hide it //TODO we do this check elsewhere now, can this be removed? - if (statusBarHeight <= 24) { + //if (statusBarHeight <= 24) { UIApplication.getSharedApplication().setStatusBarHidden(true); - } + //} config.useHaptics = true; config.useAccelerometer = false; @@ -167,6 +168,12 @@ public class IOSLauncher extends IOSApplication.Delegate { return new IOSApplication(new ShatteredPixelDungeon(new IOSPlatformSupport()), config); } + @Override + public void didChangStatusBarOrientation(UIApplication application, UIInterfaceOrientation oldStatusBarOrientation) { + super.didChangStatusBarOrientation(application, oldStatusBarOrientation); + ShatteredPixelDungeon.seamlessResetScene(); + } + public static void main(String[] argv) { NSAutoreleasePool pool = new NSAutoreleasePool(); UIApplication.main(argv, null, IOSLauncher.class); diff --git a/ios/src/main/java/com/shatteredpixel/shatteredpixeldungeon/ios/IOSPlatformSupport.java b/ios/src/main/java/com/shatteredpixel/shatteredpixeldungeon/ios/IOSPlatformSupport.java index 007eb6a61..860140ab8 100644 --- a/ios/src/main/java/com/shatteredpixel/shatteredpixeldungeon/ios/IOSPlatformSupport.java +++ b/ios/src/main/java/com/shatteredpixel/shatteredpixeldungeon/ios/IOSPlatformSupport.java @@ -29,17 +29,18 @@ import com.badlogic.gdx.backends.iosrobovm.objectal.OALSimpleAudio; import com.badlogic.gdx.graphics.Pixmap; import com.badlogic.gdx.graphics.g2d.PixmapPacker; import com.badlogic.gdx.graphics.g2d.freetype.FreeTypeFontGenerator; -import com.shatteredpixel.shatteredpixeldungeon.ShatteredPixelDungeon; +import com.shatteredpixel.shatteredpixeldungeon.SPDSettings; import com.watabou.input.ControllerHandler; import com.watabou.noosa.Game; import com.watabou.utils.PlatformSupport; import com.watabou.utils.RectF; import org.robovm.apple.audiotoolbox.AudioServices; +import org.robovm.apple.coregraphics.CGRect; import org.robovm.apple.systemconfiguration.SCNetworkReachability; import org.robovm.apple.systemconfiguration.SCNetworkReachabilityFlags; import org.robovm.apple.uikit.UIApplication; - +import org.robovm.apple.uikit.UIInterfaceOrientation; import java.util.HashMap; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -49,10 +50,11 @@ public class IOSPlatformSupport extends PlatformSupport { @Override public void updateDisplaySize() { //non-zero safe insets on left/top/right means device has a notch, show status bar + //TODO turn this into a setting instead? if (Gdx.graphics.getSafeInsetTop() != 0 || Gdx.graphics.getSafeInsetLeft() != 0 || Gdx.graphics.getSafeInsetRight() != 0){ - UIApplication.getSharedApplication().setStatusBarHidden(false); + //UIApplication.getSharedApplication().setStatusBarHidden(false); } else { UIApplication.getSharedApplication().setStatusBarHidden(true); } @@ -60,27 +62,54 @@ public class IOSPlatformSupport extends PlatformSupport { @Override public boolean supportsFullScreen() { - //fullscreen is always enabled on iOS - return false; + //iOS supports drawing into the gesture safe area + //TODO do we want this to control status bar visibility as well, or make that separate? + return Gdx.graphics.getSafeInsetBottom() > 0; } @Override public RectF getSafeInsets(int level) { - //TODO currently returns all insets all the time. Needs testing based on particular iOS quirks - // we roughly want: - // ignore bottom home indicator insets in fullsceen - // ignore side insets in landscape for side that isn't notch - // older notch is large, compact dynamic island is not (maybe? Probably need UI adjustments then) - return super.getSafeInsets(level); + RectF insets = super.getSafeInsets(INSET_ALL); + + //iOS gives us ALL insets by default, and so we need to filter from there: + + //ignore the home indicator if we're in fullscreen + if (!supportsFullScreen() || SPDSettings.fullscreen()){ + insets.bottom = 0; + } else { + //otherwise bottom inset is pretty big, halve it + insets.bottom /= 2; + } + + //only cutouts can be on top/left/right, which are never blocking + if (level == INSET_BLK){ + insets.left = insets.top = insets.right = 0; + } else if (level == INSET_LRG){ + //Dynamic Island counts as a 'small cutout', we have to use status bar height to get it =I + CGRect statusBarFrame = UIApplication.getSharedApplication().getStatusBarFrame(); + double statusBarHeight = Math.min(statusBarFrame.getWidth(), statusBarFrame.getHeight()); + if (statusBarHeight >= 51){ //magic number BS for larger status bar caused by island + insets.left = insets.top = insets.right = 0; + } + } + + //if we are in landscape, the display cutout is only actually on one side, so cancel the other + if (Game.width > Game.height){ + if (UIApplication.getSharedApplication().getStatusBarOrientation().equals(UIInterfaceOrientation.LandscapeLeft)){ + insets.left = 0; + } else { + insets.right = 0; + } + } + + //TODO if we want to support status bar on-off, then we need a check here to set top inset to 0 + + return insets; } @Override public void updateSystemUI() { - int prevInset = Game.bottomInset; updateDisplaySize(); - if (prevInset != Game.bottomInset) { - ShatteredPixelDungeon.seamlessResetScene(); - } } @Override