From 09f5948aa253428728a566d1e0eca66a7d7d64bd Mon Sep 17 00:00:00 2001 From: Evan Debenham Date: Fri, 29 Aug 2025 14:36:24 -0400 Subject: [PATCH] v3.2.3: initial overhaul to inset functionality for edge-to-edge --- .../java/com/watabou/utils/DeviceCompat.java | 9 --- .../com/watabou/utils/PlatformSupport.java | 18 ++++++ .../android/AndroidPlatformSupport.java | 48 +++++++++++++- android/src/main/res/values/style.xml | 4 +- build.gradle | 3 - .../scenes/AboutScene.java | 9 ++- .../scenes/AlchemyScene.java | 60 ++++++++++-------- .../scenes/AmuletScene.java | 25 +++++--- .../scenes/ChangesScene.java | 26 +++++--- .../scenes/GameScene.java | 23 +++++-- .../scenes/HeroSelectScene.java | 63 +++++++++++-------- .../scenes/InterlevelScene.java | 51 +++++++++------ .../scenes/JournalScene.java | 24 ++++--- .../scenes/NewsScene.java | 23 ++++--- .../scenes/PixelScene.java | 22 ++++++- .../scenes/RankingsScene.java | 31 +++++---- .../scenes/StartScene.java | 17 +++-- .../scenes/SupporterScene.java | 16 +++-- .../scenes/SurfaceScene.java | 10 ++- .../scenes/TitleScene.java | 23 ++++--- .../scenes/WelcomeScene.java | 15 +++-- .../shatteredpixeldungeon/ui/Window.java | 20 ++++-- .../ios/IOSPlatformSupport.java | 11 ++++ 23 files changed, 376 insertions(+), 175 deletions(-) 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 ecc8a0bb6..5e29a15e1 100644 --- a/SPD-classes/src/main/java/com/watabou/utils/DeviceCompat.java +++ b/SPD-classes/src/main/java/com/watabou/utils/DeviceCompat.java @@ -58,15 +58,6 @@ public class DeviceCompat { Gdx.app.log( tag, message ); } - public static RectF getSafeInsets(){ - RectF result = new RectF(); - result.left = Gdx.graphics.getSafeInsetLeft(); - result.top = Gdx.graphics.getSafeInsetTop(); - result.right = Gdx.graphics.getSafeInsetRight(); - result.bottom = Gdx.graphics.getSafeInsetBottom(); - return result; - } - //some devices (macOS mainly) report virtual pixels to Shattered, but sometimes we want real pixel precision //this returns the number of real pixels per virtual pixel in the X dimension... public static float getRealPixelScaleX(){ diff --git a/SPD-classes/src/main/java/com/watabou/utils/PlatformSupport.java b/SPD-classes/src/main/java/com/watabou/utils/PlatformSupport.java index d0921532e..2d016643f 100644 --- a/SPD-classes/src/main/java/com/watabou/utils/PlatformSupport.java +++ b/SPD-classes/src/main/java/com/watabou/utils/PlatformSupport.java @@ -39,6 +39,24 @@ public abstract class PlatformSupport { public boolean supportsFullScreen(){ return true; //default } + + public static final int INSET_ALL = 3; //All insets, from hole punches to nav bars + public static final int INSET_LRG = 2; //Only big insets, full size notches and nav bars + public static final int INSET_BLK = 1; //only complete blocker assets like navbars + + public RectF getSafeInsets( int level ){ + return new RectF( + Gdx.graphics.getSafeInsetLeft(), + Gdx.graphics.getSafeInsetTop(), + Gdx.graphics.getSafeInsetRight(), + Gdx.graphics.getSafeInsetBottom() + ); + } + + //returns a display cutout (if one is present) in device pixels, or null is none is present + public RectF getDisplayCutout(){ + return null; + } public abstract void updateSystemUI(); diff --git a/android/src/main/java/com/shatteredpixel/shatteredpixeldungeon/android/AndroidPlatformSupport.java b/android/src/main/java/com/shatteredpixel/shatteredpixeldungeon/android/AndroidPlatformSupport.java index 7d98405f0..a23185fdc 100644 --- a/android/src/main/java/com/shatteredpixel/shatteredpixeldungeon/android/AndroidPlatformSupport.java +++ b/android/src/main/java/com/shatteredpixel/shatteredpixeldungeon/android/AndroidPlatformSupport.java @@ -25,6 +25,7 @@ import android.annotation.SuppressLint; import android.content.Context; import android.net.ConnectivityManager; import android.os.Build; +import android.view.DisplayCutout; import android.view.View; import android.view.WindowInsets; import android.view.WindowManager; @@ -35,6 +36,7 @@ import com.badlogic.gdx.graphics.g2d.PixmapPacker; import com.badlogic.gdx.graphics.g2d.freetype.FreeTypeFontGenerator; import com.shatteredpixel.shatteredpixeldungeon.SPDSettings; import com.watabou.utils.PlatformSupport; +import com.watabou.utils.RectF; import java.util.HashMap; import java.util.regex.Matcher; @@ -45,6 +47,8 @@ public class AndroidPlatformSupport extends PlatformSupport { public void updateDisplaySize(){ //TODO seem to be existing bugs with handling split screen here, should look into that + + //TODO display isn't refreshing when fullscreen toggled on/off, or on 180 degree rotate } public boolean supportsFullScreen(){ @@ -56,7 +60,46 @@ public class AndroidPlatformSupport extends PlatformSupport { return true; } } - + + @Override + public RectF getSafeInsets( int level ) { + RectF insets = new RectF(); + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + WindowInsets rootInsets = AndroidLauncher.instance.getApplicationWindow().getDecorView().getRootWindowInsets(); + if (rootInsets != null) { + + //Navigation bar (never on the top) + //Android 14 and below do this for us + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.VANILLA_ICE_CREAM && supportsFullScreen() && !SPDSettings.fullscreen()) { + insets.left = Math.max(insets.left, rootInsets.getStableInsetLeft()); + insets.right = Math.max(insets.right, rootInsets.getStableInsetRight()); + insets.bottom = Math.max(insets.bottom, rootInsets.getStableInsetBottom()); + } + + //display cutout + if (level > INSET_BLK && Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { + DisplayCutout cutout = rootInsets.getDisplayCutout(); + //TODO determine if a cutout is large or not based on its size in pixels: + // on my OP7P, dev mode not simulations are: + //top-left cutout is 0,0,136,136 (136x136 = 18.5k total) + //center is 552,0,888,168 (336*168 = 56k total) + //top-right corner is 1272,0,1440,168 (x168 = 28k total) + //overall screen is 1440x3120 = 4400k pixels + // 0.5% of 4400k is 22k + //maybe judge a cutout to be large if it's bigger than 0.5% of the display? + if (cutout != null) { + insets.left = Math.max(insets.left, cutout.getSafeInsetLeft()); + insets.top = Math.max(insets.top, cutout.getSafeInsetTop()); + insets.right = Math.max(insets.right, cutout.getSafeInsetRight()); + insets.bottom = Math.max(insets.bottom, cutout.getSafeInsetBottom()); + } + } + } + } + return insets; + } + public void updateSystemUI() { AndroidLauncher.instance.runOnUiThread(new Runnable() { @@ -80,8 +123,9 @@ public class AndroidPlatformSupport extends PlatformSupport { | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_FULLSCREEN | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY ); } else { + //still want to hide the status bar and cutout void AndroidLauncher.instance.getWindow().getDecorView().setSystemUiVisibility( - View.SYSTEM_UI_FLAG_LAYOUT_STABLE ); + View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN ); } } }); diff --git a/android/src/main/res/values/style.xml b/android/src/main/res/values/style.xml index 59e8ec20e..195bf28c8 100644 --- a/android/src/main/res/values/style.xml +++ b/android/src/main/res/values/style.xml @@ -1,7 +1,9 @@ diff --git a/build.gradle b/build.gradle index b9858b63b..b850c3244 100644 --- a/build.gradle +++ b/build.gradle @@ -21,9 +21,6 @@ allprojects { appAndroidCompileSDK = 35 //Android 15 appAndroidMinSDK = 21 //Android 5.0 - // TODO currently we've opted-out of edge-to-edge but it will be required for Android 16 - // Need to figure out how the game will handle drawing into notches (and unify with iOS) - // See: https://developer.android.com/about/versions/15/behavior-changes-15#ux appAndroidTargetSDK = 35 //Android 15 gdxVersion = '1.13.6-SNAPSHOT' //using snapshot as 1.13.5 has issues with Android R8 diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/scenes/AboutScene.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/scenes/AboutScene.java index aad4414d1..42db5c1da 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/scenes/AboutScene.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/scenes/AboutScene.java @@ -36,6 +36,7 @@ import com.watabou.noosa.Group; import com.watabou.noosa.Image; import com.watabou.noosa.PointerArea; import com.watabou.noosa.ui.Component; +import com.watabou.utils.RectF; public class AboutScene extends PixelScene { @@ -49,6 +50,8 @@ public class AboutScene extends PixelScene { int w = Camera.main.width; int h = Camera.main.height; + RectF insets = getCommonInsets(); + Archs archs = new Archs(); archs.setSize( w, h ); add( archs ); @@ -71,9 +74,9 @@ public class AboutScene extends PixelScene { "ShatteredPixel.com", "https://ShatteredPixel.com"); if (landscape()){ - shpx.setRect((w - fullWidth)/2f - 6, 10, 120, 0); + shpx.setRect((w - fullWidth)/2f - 6, insets.top + 10, 120, 0); } else { - shpx.setRect((w - fullWidth)/2f, 6, 120, 0); + shpx.setRect((w - fullWidth)/2f, insets.top + 6, 120, 0); } content.add(shpx); @@ -232,7 +235,7 @@ public class AboutScene extends PixelScene { freesound.setRect(transifex.left()-10, transifex.bottom() + 8, colWidth+20, 0); content.add(freesound); - content.setSize( fullWidth, freesound.bottom()+10 ); + content.setSize( fullWidth, freesound.bottom()+10 + insets.bottom ); list.setRect( 0, 0, w, h ); list.scrollTo(0, 0); diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/scenes/AlchemyScene.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/scenes/AlchemyScene.java index b109b0006..92384366d 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/scenes/AlchemyScene.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/scenes/AlchemyScene.java @@ -79,6 +79,7 @@ import com.watabou.noosa.SkinnedBlock; import com.watabou.noosa.audio.Sample; import com.watabou.noosa.particles.Emitter; import com.watabou.noosa.ui.Component; +import com.watabou.utils.RectF; import java.io.IOException; import java.util.ArrayList; @@ -120,9 +121,13 @@ public class AlchemyScene extends PixelScene { @Override public void create() { super.create(); - + + int w = Camera.main.width; + int h = Camera.main.height; + RectF insets = getCommonInsets(); + water = new SkinnedBlock( - Camera.main.width, Camera.main.height, + w, h, Dungeon.level.waterTex() ){ @Override @@ -143,18 +148,21 @@ public class AlchemyScene extends PixelScene { Image im = new Image(TextureCache.createGradient(0x66000000, 0x88000000, 0xAA000000, 0xCC000000, 0xFF000000)); im.angle = 90; - im.x = Camera.main.width; - im.scale.x = Camera.main.height/5f; - im.scale.y = Camera.main.width; + im.x = w; + im.scale.x = h/5f; + im.scale.y = w; add(im); + w -= insets.left + insets.right; + h -= insets.top + insets.bottom; + ExitButton btnExit = new ExitButton(){ @Override protected void onClick() { Game.switchScene(GameScene.class); } }; - btnExit.setPos( Camera.main.width - btnExit.width(), 0 ); + btnExit.setPos( insets.left + w - btnExit.width(), insets.top ); add( btnExit ); bubbleEmitter = new Emitter(); @@ -166,30 +174,30 @@ public class AlchemyScene extends PixelScene { IconTitle title = new IconTitle(Icons.ALCHEMY.get(), Messages.get(this, "title") ); title.setSize(200, 0); title.setPos( - (Camera.main.width - title.reqWidth()) / 2f, - (20 - title.height()) / 2f + insets.left + (w - title.reqWidth()) / 2f, + insets.top + (20 - title.height()) / 2f ); align(title); add(title); - int w = Math.min(50 + Camera.main.width/2, 150); - int left = (Camera.main.width - w)/2; + int pw = Math.min(50 + w/2, 150); + int left = (int)(insets.left) + (w - pw)/2; - centerW = left + w/2; + centerW = left + pw/2; - int pos = (Camera.main.height - 120)/2; + int pos = (int)(insets.top) + (h - 120)/2; if (splitAlchGuide && - Camera.main.width >= 300 && - Camera.main.height >= PixelScene.MIN_HEIGHT_FULL){ - w = Math.min(150, Camera.main.width/2); - left = (Camera.main.width/2 - w); - centerW = left + w/2; + w >= 300 && + h >= PixelScene.MIN_HEIGHT_FULL){ + pw = Math.min(150, w/2); + left = (w/2 - pw); + centerW = left + pw/2; NinePatch guideBG = Chrome.get(Chrome.Type.TOAST); guideBG.size(126 + guideBG.marginHor(), Math.min(Camera.main.height - 18, 191 + guideBG.marginVer())); - guideBG.y = Math.max(17, (Camera.main.height - guideBG.height())/2f); - guideBG.x = Camera.main.width - left - guideBG.width(); + guideBG.y = Math.max(17, insets.top + (h - guideBG.height())/2f); + guideBG.x = insets.left + w - left - guideBG.width(); add(guideBG); alchGuide = new WndJournal.AlchemyTab(); @@ -204,9 +212,9 @@ public class AlchemyScene extends PixelScene { } RenderedTextBlock desc = PixelScene.renderTextBlock(6); - desc.maxWidth(w); + desc.maxWidth(pw); desc.text( Messages.get(AlchemyScene.class, "text") ); - desc.setPos(left + (w - desc.width())/2, pos); + desc.setPos(left + (pw - desc.width())/2, pos); add(desc); pos += desc.height() + 6; @@ -383,8 +391,8 @@ public class AlchemyScene extends PixelScene { if (i == 0){ //first ones are always visible - combines[i].setRect(left + (w-30)/2f, inputs[1].top()+5, 30, inputs[1].height()-10); - outputs[i].setRect(left + w - BTN_SIZE - 10, inputs[1].top(), BTN_SIZE, BTN_SIZE); + combines[i].setRect(left + (pw-30)/2f, inputs[1].top()+5, 30, inputs[1].height()-10); + outputs[i].setRect(left + pw - BTN_SIZE - 10, inputs[1].top(), BTN_SIZE, BTN_SIZE); } else { combines[i].visible = false; outputs[i].visible = false; @@ -403,7 +411,7 @@ public class AlchemyScene extends PixelScene { if (Camera.main.height >= 280){ //last elements get centered even with a split alch guide UI, as long as there's enough height - centerW = Camera.main.width/2; + centerW = (int)(insets.left) + w/2; } bubbleEmitter.pos(0, @@ -415,7 +423,7 @@ public class AlchemyScene extends PixelScene { lowerBubbles.pos(0, pos, 2*centerW, - Math.max(0, Camera.main.height-pos)); + Math.max(0, h-pos)); lowerBubbles.pour(Speck.factory( Speck.BUBBLE ), 0.1f ); String energyText = Messages.get(AlchemyScene.class, "energy") + " " + Dungeon.energy; @@ -426,7 +434,7 @@ public class AlchemyScene extends PixelScene { energyLeft = PixelScene.renderTextBlock(energyText, 9); energyLeft.setPos( centerW - energyLeft.width()/2, - Camera.main.height - 8 - energyLeft.height() + insets.top + h - 8 - energyLeft.height() ); energyLeft.hardlight(0x44CCFF); add(energyLeft); diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/scenes/AmuletScene.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/scenes/AmuletScene.java index 6917751c1..12b304fa3 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/scenes/AmuletScene.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/scenes/AmuletScene.java @@ -42,6 +42,7 @@ import com.watabou.noosa.Image; import com.watabou.noosa.audio.Music; import com.watabou.noosa.tweeners.Delayer; import com.watabou.utils.Random; +import com.watabou.utils.RectF; public class AmuletScene extends PixelScene { @@ -119,30 +120,34 @@ public class AmuletScene extends PixelScene { btnStay.icon(Icons.CLOSE.get()); btnStay.setSize( WIDTH, BTN_HEIGHT ); add( btnStay ); - + + RectF insets = getCommonInsets(); + int w = (int) (Camera.main.width - insets.left + insets.right); + int h = (int) (Camera.main.height - insets.top + insets.bottom); + float height; if (noText) { height = amulet.height + LARGE_GAP + btnExit.height() + SMALL_GAP + btnStay.height(); - amulet.x = (Camera.main.width - amulet.width) / 2; - amulet.y = (Camera.main.height - height) / 2; + amulet.x = insets.left + (w - amulet.width) / 2; + amulet.y = insets.top + (h - height) / 2; align(amulet); - btnExit.setPos( (Camera.main.width - btnExit.width()) / 2, amulet.y + amulet.height + LARGE_GAP ); + btnExit.setPos( insets.left + (w - btnExit.width()) / 2, amulet.y + amulet.height + LARGE_GAP ); btnStay.setPos( btnExit.left(), btnExit.bottom() + SMALL_GAP ); } else { height = amulet.height + LARGE_GAP + text.height() + LARGE_GAP + btnExit.height() + SMALL_GAP + btnStay.height(); - - amulet.x = (Camera.main.width - amulet.width) / 2; - amulet.y = (Camera.main.height - height) / 2; + + amulet.x = insets.left + (w - amulet.width) / 2; + amulet.y = insets.top + (h - height) / 2; align(amulet); - text.setPos((Camera.main.width - text.width()) / 2, amulet.y + amulet.height + LARGE_GAP); + text.setPos(insets.left + (w - text.width()) / 2, amulet.y + amulet.height + LARGE_GAP); align(text); add(text); - - btnExit.setPos( (Camera.main.width - btnExit.width()) / 2, text.top() + text.height() + LARGE_GAP ); + + btnExit.setPos( insets.left + (w - btnExit.width()) / 2, amulet.y + amulet.height + LARGE_GAP ); btnStay.setPos( btnExit.left(), btnExit.bottom() + SMALL_GAP ); } diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/scenes/ChangesScene.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/scenes/ChangesScene.java index d40a27b8d..ab762c1b2 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/scenes/ChangesScene.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/scenes/ChangesScene.java @@ -55,6 +55,7 @@ import com.watabou.noosa.NinePatch; import com.watabou.noosa.Scene; import com.watabou.noosa.audio.Music; import com.watabou.noosa.ui.Component; +import com.watabou.utils.RectF; import java.util.ArrayList; @@ -79,17 +80,26 @@ public class ChangesScene extends PixelScene { int w = Camera.main.width; int h = Camera.main.height; + RectF insets = getCommonInsets(); + + Archs archs = new Archs(); + archs.setSize(w, h); + //archs added later + + w -= insets.left + insets.right; + h -= insets.top + insets.bottom; + IconTitle title = new IconTitle(Icons.CHANGES.get(), Messages.get(this, "title")); title.setSize(200, 0); title.setPos( - (w - title.reqWidth()) / 2f, - (20 - title.height()) / 2f + insets.left + (w - title.reqWidth()) / 2f, + insets.top + (20 - title.height()) / 2f ); align(title); add(title); ExitButton btnExit = new ExitButton(); - btnExit.setPos( Camera.main.width - btnExit.width(), 0 ); + btnExit.setPos( insets.left + w - btnExit.width(), insets.top ); add( btnExit ); NinePatch panel = Chrome.get(Chrome.Type.TOAST); @@ -99,8 +109,8 @@ public class ChangesScene extends PixelScene { if (h >= PixelScene.MIN_HEIGHT_FULL && w >= 300) { panel.size( pw, ph ); - panel.x = (w - pw) / 2f - pw/2 - 1; - panel.y = 20; + panel.x = insets.left + (w - pw) / 2f - pw/2 - 1; + panel.y = insets.top + 20; rightPanel = Chrome.get(Chrome.Type.TOAST); rightPanel.size( pw, ph ); @@ -131,8 +141,8 @@ public class ChangesScene extends PixelScene { } else { panel.size( pw, ph ); - panel.x = (w - pw) / 2f; - panel.y = 20; + panel.x = insets.left + (w - pw) / 2f; + panel.y = insets.top + 20; } align( panel ); add( panel ); @@ -340,8 +350,6 @@ public class ChangesScene extends PixelScene { btnOld.setRect(btn0_6.right()-2, btn0_8.top(), 22, changesSelected == 7 ? 19 : 15); addToBack(btnOld); - Archs archs = new Archs(); - archs.setSize( Camera.main.width, Camera.main.height ); addToBack( archs ); fadeIn(); 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 288fb49b4..9730167ea 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/scenes/GameScene.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/scenes/GameScene.java @@ -121,6 +121,7 @@ import com.shatteredpixel.shatteredpixeldungeon.windows.WndKeyBindings; import com.shatteredpixel.shatteredpixeldungeon.windows.WndMessage; import com.shatteredpixel.shatteredpixeldungeon.windows.WndOptions; import com.shatteredpixel.shatteredpixeldungeon.windows.WndResurrect; +import com.watabou.gltextures.TextureCache; import com.watabou.glwrap.Blending; import com.watabou.input.ControllerHandler; import com.watabou.input.KeyBindings; @@ -138,8 +139,8 @@ import com.watabou.noosa.audio.Sample; import com.watabou.noosa.particles.Emitter; import com.watabou.noosa.tweeners.Tweener; import com.watabou.utils.Callback; -import com.watabou.utils.DeviceCompat; import com.watabou.utils.GameMath; +import com.watabou.utils.PlatformSupport; import com.watabou.utils.Point; import com.watabou.utils.PointF; import com.watabou.utils.Random; @@ -231,6 +232,8 @@ public class GameScene extends PixelScene { case 1: Camera.main.setFollowDeadzone(0.9f); break; } + RectF insets = Game.platform.getSafeInsets(PlatformSupport.INSET_ALL).scale(1f/defaultZoom); + scene = this; terrain = new Group(); @@ -361,16 +364,24 @@ public class GameScene extends PixelScene { int uiSize = SPDSettings.interfaceSize(); + //TODO this is a good start but just emulating black bars is boring. There must be more to do here. + menu = new MenuPane(); menu.camera = uiCamera; - menu.setPos( uiCamera.width-MenuPane.WIDTH, uiSize > 0 ? 0 : 1); + menu.setPos( uiCamera.width-MenuPane.WIDTH-insets.right, insets.top + (uiSize > 0 ? 0 : 1)); add(menu); status = new StatusPane( SPDSettings.interfaceSize() > 0 ); status.camera = uiCamera; - status.setRect(0, uiSize > 0 ? uiCamera.height-39 : 0, uiCamera.width, 0 ); + status.setRect(insets.left, uiSize > 0 ? uiCamera.height-39-insets.bottom : insets.top, uiCamera.width - insets.left - insets.right, 0 ); add(status); + if (uiSize < 2 && insets.top > 0) { + SkinnedBlock blackBar = new SkinnedBlock(uiCamera.width, insets.top, TextureCache.createSolid(0xFF000000)); + blackBar.camera = uiCamera; + add(blackBar); + } + boss = new BossHealthBar(); boss.camera = uiCamera; boss.setPos( 6 + (uiCamera.width - boss.width())/2, 20); @@ -411,9 +422,9 @@ public class GameScene extends PixelScene { inventory.setPos(uiCamera.width - inventory.width(), uiCamera.height - inventory.height()); add(inventory); - toolbar.setRect( 0, uiCamera.height - toolbar.height() - inventory.height(), uiCamera.width, toolbar.height() ); + toolbar.setRect( insets.left, uiCamera.height - toolbar.height() - inventory.height() - insets.bottom, uiCamera.width - insets.right, toolbar.height() ); } else { - toolbar.setRect( 0, uiCamera.height - toolbar.height(), uiCamera.width, toolbar.height() ); + toolbar.setRect( insets.left, uiCamera.height - toolbar.height() - insets.bottom, uiCamera.width - insets.right, toolbar.height() ); } layoutTags(); @@ -840,7 +851,7 @@ public class GameScene extends PixelScene { //primarily for phones displays with notches //TODO Android never draws into notch atm, perhaps allow it for center notches? - RectF insets = DeviceCompat.getSafeInsets(); + RectF insets = Game.platform.getSafeInsets( PlatformSupport.INSET_ALL ); insets = insets.scale(1f / uiCamera.zoom); boolean tagsOnLeft = SPDSettings.flipTags(); diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/scenes/HeroSelectScene.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/scenes/HeroSelectScene.java index 462bf32e5..1181a003a 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/scenes/HeroSelectScene.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/scenes/HeroSelectScene.java @@ -59,7 +59,9 @@ import com.watabou.noosa.tweeners.Tweener; import com.watabou.noosa.ui.Component; import com.watabou.utils.DeviceCompat; import com.watabou.utils.GameMath; +import com.watabou.utils.PlatformSupport; import com.watabou.utils.PointF; +import com.watabou.utils.RectF; import java.text.SimpleDateFormat; import java.util.ArrayList; @@ -84,6 +86,8 @@ public class HeroSelectScene extends PixelScene { private GameOptions optionsPane; private IconButton btnExit; + private RectF insets; + @Override public void create() { super.create(); @@ -93,6 +97,11 @@ public class HeroSelectScene extends PixelScene { Badges.loadGlobal(); Journal.loadGlobal(); + insets = Game.platform.getSafeInsets(PlatformSupport.INSET_BLK).scale(1f/defaultZoom); + + int w = (int)(Camera.main.width - insets.left - insets.right); + int h = (int)(Camera.main.height - insets.top - insets.bottom); + background = new Image(TextureCache.createSolid(0xFF2d2f31), 0, 0, 800, 450){ @Override public void update() { @@ -106,10 +115,10 @@ public class HeroSelectScene extends PixelScene { } } }; - background.scale.set(Camera.main.height/background.height); + background.scale.set(h/background.height); - background.x = (Camera.main.width - background.width())/2f; - background.y = (Camera.main.height - background.height())/2f; + background.x = insets.left + (w - background.width())/2f; + background.y = insets.top + (h - background.height())/2f; PixelScene.align(background); add(background); @@ -157,11 +166,11 @@ public class HeroSelectScene extends PixelScene { super.onClick(); HeroClass cls = GamesInProgress.selectedClass; if (cls != null) { - Window w = new WndHeroInfo(GamesInProgress.selectedClass); + Window info = new WndHeroInfo(GamesInProgress.selectedClass); if (landscape()) { - w.offset(Camera.main.width / 6, 0); + info.offset(w / 6, 0); } - ShatteredPixelDungeon.scene().addToFront(w); + ShatteredPixelDungeon.scene().addToFront(info); } } @@ -222,19 +231,19 @@ public class HeroSelectScene extends PixelScene { } if (landscape()){ - float leftArea = Math.max(100, Camera.main.width/3f); - float uiHeight = Math.min(Camera.main.height-20, 300); + float leftArea = Math.max(100, w/3f); + float uiHeight = Math.min(h-20, 300); float uiSpacing = (uiHeight-120)/2f; if (uiHeight >= 160) uiSpacing -= 5; if (uiHeight >= 180) uiSpacing -= 6; - background.x += leftArea/6f; + background.x += insets.left + leftArea/6f; float fadeLeftScale = 47 * (leftArea - background.x)/leftArea; fadeLeft.scale = new PointF(3 + Math.max(0, fadeLeftScale), background.height()); - title.setPos( (leftArea - title.width())/2f, (Camera.main.height-uiHeight)/2f); + title.setPos(insets.left + (leftArea - title.width())/2f, (h-uiHeight)/2f); align(title); int btnWidth = HeroBtn.MIN_WIDTH + 15; @@ -244,7 +253,7 @@ public class HeroSelectScene extends PixelScene { } int cols = (int)Math.ceil(heroBtns.size()/2f); - float curX = (leftArea - btnWidth * cols + (cols-1))/2f; + float curX = insets.left + (leftArea - btnWidth * cols + (cols-1))/2f; float curY = title.bottom() + uiSpacing; int count = 0; @@ -264,7 +273,7 @@ public class HeroSelectScene extends PixelScene { } heroName = renderTextBlock(9); - heroName.setPos(0, heroBtns.get(heroBtns.size()-1).bottom()+5); + heroName.setPos(insets.left, heroBtns.get(heroBtns.size()-1).bottom()+5); add(heroName); if (uiHeight >= 160){ @@ -273,12 +282,12 @@ public class HeroSelectScene extends PixelScene { heroDesc = renderTextBlock(5); } heroDesc.align(RenderedTextBlock.CENTER_ALIGN); - heroDesc.setPos(0, heroName.bottom()+5); + heroDesc.setPos(insets.left, heroName.bottom()+5); add(heroDesc); startBtn.text(Messages.titleCase(Messages.get(this, "start"))); startBtn.setSize(startBtn.reqWidth()+8, 21); - startBtn.setPos((leftArea - startBtn.width())/2f, title.top() + uiHeight - startBtn.height()); + startBtn.setPos(insets.left + (leftArea - startBtn.width())/2f, title.top() + uiHeight - startBtn.height()); align(startBtn); btnFade = new IconButton(Icons.CHEVRON.get()){ @@ -309,19 +318,19 @@ public class HeroSelectScene extends PixelScene { int btnWidth = HeroBtn.MIN_WIDTH; - float curX = (Camera.main.width - btnWidth * heroBtns.size()) / 2f; + float curX = insets.left + (w - btnWidth * heroBtns.size()) / 2f; if (curX > 0) { btnWidth += Math.min(curX / (heroBtns.size() / 2f), 15); - curX = (Camera.main.width - btnWidth * heroBtns.size()) / 2f; + curX = insets.left + (w - btnWidth * heroBtns.size()) / 2f; } - float curY = Camera.main.height - HeroBtn.HEIGHT + 3; + float curY = insets.top + h - HeroBtn.HEIGHT + 3; for (StyledButton button : heroBtns) { button.setRect(curX, curY, btnWidth, HeroBtn.HEIGHT); curX += btnWidth; } - title.setPos((Camera.main.width - title.width()) / 2f, (Camera.main.height - HeroBtn.HEIGHT - title.height() - 4)); + title.setPos(insets.left + (w - title.width()) / 2f, insets.top + (h - HeroBtn.HEIGHT - title.height() - 4)); btnOptions.setRect(heroBtns.get(0).left() + 16, Camera.main.height-HeroBtn.HEIGHT-16, 20, 21); optionsPane.setPos(heroBtns.get(0).left(), 0); @@ -396,23 +405,23 @@ public class HeroSelectScene extends PixelScene { background.visible = true; background.hardlight(1.5f,1.5f,1.5f); - float leftPortion = Math.max(100, Camera.main.width/3f); + float leftPortion = Math.max(100, (Camera.main.width - insets.left - insets.right)/3f); if (landscape()) { heroName.text(Messages.titleCase(cl.title())); heroName.hardlight(Window.TITLE_COLOR); - heroName.setPos((leftPortion - heroName.width() - 20)/2f, heroName.top()); + heroName.setPos(insets.left + (leftPortion - heroName.width() - 20)/2f, heroName.top()); align(heroName); heroDesc.text(cl.shortDesc()); heroDesc.maxWidth(80); - heroDesc.setPos((leftPortion - heroDesc.width())/2f, heroName.bottom() + 5); + heroDesc.setPos(insets.left +(leftPortion - heroDesc.width())/2f, heroName.bottom() + 5); align(heroDesc); while(startBtn.top() < heroDesc.bottom()){ heroDesc.maxWidth(heroDesc.maxWidth()+10); - heroDesc.setPos(Math.max(0, (leftPortion - heroDesc.width())/2f), heroName.bottom() + 5); + heroDesc.setPos(Math.max(insets.left, (leftPortion - heroDesc.width())/2f), heroName.bottom() + 5); align(heroDesc); } @@ -433,7 +442,7 @@ public class HeroSelectScene extends PixelScene { startBtn.text(Messages.titleCase(cl.title())); startBtn.setSize(startBtn.reqWidth() + 8, 21); - startBtn.setPos((Camera.main.width - startBtn.width())/2f, (Camera.main.height - HeroBtn.HEIGHT + 2 - startBtn.height())); + startBtn.setPos((Camera.main.width - startBtn.width())/2f, (Camera.main.height - insets.bottom - HeroBtn.HEIGHT + 2 - startBtn.height())); PixelScene.align(startBtn); infoButton.visible = infoButton.active = true; @@ -496,13 +505,15 @@ public class HeroSelectScene extends PixelScene { if (landscape()){ - background.x = (Camera.main.width - background.width())/2f; + int w = (int)(Camera.main.width - insets.left - insets.right); - float leftPortion = Math.max(100, Camera.main.width/3f); + background.x = insets.left + (w - background.width())/2f; + + float leftPortion = Math.max(100, w/3f); background.x += (leftPortion/2f)*alpha; - float fadeLeftScale = 47 * (leftPortion - background.x)/leftPortion; + float fadeLeftScale = 47 * (leftPortion - (background.x - insets.left))/leftPortion; fadeLeft.scale.x = 3 + Math.max(fadeLeftScale, 0)*alpha; fadeLeft.x = background.x-4; fadeRight.x = background.x + background.width() + 4; diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/scenes/InterlevelScene.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/scenes/InterlevelScene.java index 5c993e7ac..ddc595a41 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/scenes/InterlevelScene.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/scenes/InterlevelScene.java @@ -56,7 +56,9 @@ import com.watabou.noosa.tweeners.Tweener; import com.watabou.utils.BArray; import com.watabou.utils.DeviceCompat; import com.watabou.utils.GameMath; +import com.watabou.utils.PlatformSupport; import com.watabou.utils.Random; +import com.watabou.utils.RectF; import com.watabou.utils.Signal; import java.io.FileNotFoundException; @@ -107,6 +109,8 @@ public class InterlevelScene extends PixelScene { public static int lastRegion = -1; + private RectF insets; + { inGameScene = true; } @@ -210,16 +214,21 @@ public class InterlevelScene extends PixelScene { fadeTime = 0f; } - background = new Image(loadingAsset); - background.scale.set(Camera.main.height/background.height); + insets = Game.platform.getSafeInsets(PlatformSupport.INSET_BLK).scale(1f/defaultZoom); - if (Camera.main.width >= background.width()){ - background.x = (Camera.main.width - background.width())/2f; + int w = (int)(Camera.main.width - insets.left - insets.right); + int h = (int)(Camera.main.height - insets.top - insets.bottom); + + background = new Image(loadingAsset); + background.scale.set(h/background.height); + + if (w >= background.width()){ + background.x = insets.left + (w - background.width())/2f; } else { - background.x = Camera.main.width/2f - loadingCenter*background.scale.x; - background.x = GameMath.gate(Camera.main.width - background.width(), background.x, 0); + background.x = insets.left + w/2f - loadingCenter*background.scale.x; + background.x = GameMath.gate(w - background.width(), background.x, 0); } - background.y = (Camera.main.height - background.height())/2f; + background.y = insets.top + (h - background.height())/2f; PixelScene.align(background); add(background); @@ -248,17 +257,17 @@ public class InterlevelScene extends PixelScene { } }; im.angle = 90; - im.x = Camera.main.width; - im.scale.x = Camera.main.height/5f; - im.scale.y = Camera.main.width; + im.x = insets.left + w; + im.scale.x = h/5f; + im.scale.y = w; add(im); String text = Messages.get(Mode.class, mode.name()); loadingText = PixelScene.renderTextBlock( text, 9 ); loadingText.setPos( - (Camera.main.width - loadingText.width() - 8), - (Camera.main.height - loadingText.height() - 6) + insets.left + w - loadingText.width() - 8, + insets.top + h - loadingText.height() - 6 ); align(loadingText); add(loadingText); @@ -267,7 +276,7 @@ public class InterlevelScene extends PixelScene { if (Dungeon.hero == null || (loadingDepth > Statistics.deepestFloor && loadingDepth % 5 == 1)){ storyMessage = PixelScene.renderTextBlock(Document.INTROS.pageBody(region), 6); storyMessage.maxWidth( PixelScene.landscape() ? 180 : 125); - storyMessage.setPos((Camera.main.width-storyMessage.width())/2f, (Camera.main.height-storyMessage.height())/2f); + storyMessage.setPos(insets.left+(w-storyMessage.width())/2f, insets.top+(h-storyMessage.height())/2f); storyBG = new ShadowBox(); storyBG.boxRect(storyMessage.left()-10, storyMessage.top()-10, storyMessage.width()+20, storyMessage.height()+20); @@ -322,7 +331,7 @@ public class InterlevelScene extends PixelScene { } }); - btnContinue.setPos((Camera.main.width - btnContinue.width())/2f, storyMessage.bottom()+10); + btnContinue.setPos(insets.left + (w - btnContinue.width())/2f, storyMessage.bottom()+10); add(btnContinue); btnHideStory = new IconButton(Icons.CHEVRON.get()){ @@ -474,6 +483,9 @@ public class InterlevelScene extends PixelScene { break; } } + + int w = (int)(Camera.main.width - insets.left - insets.right); + int h = (int)(Camera.main.height - insets.top - insets.bottom); switch (phase) { @@ -528,12 +540,13 @@ public class InterlevelScene extends PixelScene { //slowly pan the background side to side in portait mode, if story text is displayed if (btnContinue != null && !textFadingIn && Game.width < Game.height){ if (background.speed.isZero() && background.acc.isZero()){ - background.acc.x = background.center().x >= Camera.main.width ? -1f : 1f; + background.acc.x = background.center().x >= (w+ insets.left) ? -1f : 1f; } else { + float margin = 25 - insets.left; background.speed.x = GameMath.gate(-10, background.speed.x, 10); - if (background.acc.x > 0 && background.x >= -25){ + if (background.acc.x > 0 && background.x >= -margin){ background.acc.x = -2.5f; - } else if (background.acc.x < 0 && background.x + background.width() <= Camera.main.width+25){ + } else if (background.acc.x < 0 && background.x + background.width() <= w+margin){ background.acc.x = 2.5f; } } @@ -581,8 +594,8 @@ public class InterlevelScene extends PixelScene { //the randomization is effectively -2 to +2 // we don't use the generator stack as levelgen may be occurring // and we don't want to accidentally use a seeded generator - (Camera.main.width - loadingText.width() - 4) + 4*(Random.Float(false)-0.5f), - (Camera.main.height - loadingText.height() - 6) + 4*(Random.Float(false)-0.5f) + (w + insets.left - loadingText.width() - 4) + 4*(Random.Float(false)-0.5f), + (h + insets.top - loadingText.height() - 6) + 4*(Random.Float(false)-0.5f) ); align(loadingText); } diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/scenes/JournalScene.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/scenes/JournalScene.java index 53fb82f76..361374e07 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/scenes/JournalScene.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/scenes/JournalScene.java @@ -44,6 +44,7 @@ import com.shatteredpixel.shatteredpixeldungeon.windows.WndJournal; import com.watabou.noosa.Camera; import com.watabou.noosa.NinePatch; import com.watabou.noosa.audio.Music; +import com.watabou.utils.RectF; import com.watabou.utils.SparseArray; public class JournalScene extends PixelScene { @@ -79,13 +80,22 @@ public class JournalScene extends PixelScene { int w = Camera.main.width; int h = Camera.main.height; + RectF insets = getCommonInsets(); + + Archs archs = new Archs(); + archs.setSize( w, h ); + //archs added later + + w -= insets.left + insets.right; + h -= insets.top + insets.bottom; + float top = 20; IconTitle title = new IconTitle( Icons.JOURNAL.get(), Messages.get(this, "title") ); title.setSize(200, 0); title.setPos( - (w - title.reqWidth()) / 2f, - (top - title.height()) / 2f + insets.left + (w - title.reqWidth()) / 2f, + insets.top + (top - title.height()) / 2f ); align(title); add(title); @@ -96,8 +106,8 @@ public class JournalScene extends PixelScene { int ph = h - 50 + panel.marginVer(); panel.size(pw, ph); - panel.x = (w - pw) / 2f; - panel.y = top; + panel.x = insets.left + (w - pw) / 2f; + panel.y = insets.top + top; add(panel); switch (lastIDX){ @@ -218,12 +228,10 @@ public class JournalScene extends PixelScene { if (lastIDX != 3) btnAlchemy.icon().brightness(0.6f); addToBack(btnAlchemy); - Archs archs = new Archs(); - archs.setSize( w, h ); - addToBack( archs ); + addToBack(archs); ExitButton btnExit = new ExitButton(); - btnExit.setPos( Camera.main.width - btnExit.width(), 0 ); + btnExit.setPos( insets.left + w - btnExit.width(), insets.top ); add( btnExit ); fadeIn(); diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/scenes/NewsScene.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/scenes/NewsScene.java index 5de715152..5fabd82a0 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/scenes/NewsScene.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/scenes/NewsScene.java @@ -43,6 +43,7 @@ import com.watabou.noosa.Camera; import com.watabou.noosa.Game; import com.watabou.noosa.NinePatch; import com.watabou.noosa.ui.Component; +import com.watabou.utils.RectF; import java.util.ArrayList; @@ -61,34 +62,38 @@ public class NewsScene extends PixelScene { int w = Camera.main.width; int h = Camera.main.height; - - int fullWidth = PixelScene.landscape() ? 202 : 100; - int left = (w - fullWidth)/2; + RectF insets = getCommonInsets(); Archs archs = new Archs(); archs.setSize(w, h); add(archs); + w -= insets.left + insets.right; + h -= insets.top + insets.bottom; + + int fullWidth = PixelScene.landscape() ? 202 : 100; + int left = (int)insets.left + (w - fullWidth)/2; + ExitButton btnExit = new ExitButton(); - btnExit.setPos(w - btnExit.width(), 0); + btnExit.setPos(insets.left + w - btnExit.width(), insets.top); add(btnExit); IconTitle title = new IconTitle( Icons.NEWS.get(), Messages.get(this, "title")); title.setSize(200, 0); title.setPos( - (w - title.reqWidth()) / 2f, - (20 - title.height()) / 2f + insets.left + (w - title.reqWidth()) / 2f, + insets.top + (20 - title.height()) / 2f ); align(title); add(title); - float top = 18; + float top = 18 + insets.top; displayingNoArticles = !News.articlesAvailable(); if (displayingNoArticles || Messages.lang() != Languages.ENGLISH) { Component newsInfo = new NewsInfo(); - newsInfo.setRect(left, 20, fullWidth, 0); + newsInfo.setRect(left, top, fullWidth, 0); add(newsInfo); top = newsInfo.bottom(); @@ -98,7 +103,7 @@ public class NewsScene extends PixelScene { if (!displayingNoArticles) { ArrayList articles = News.articles(); - float articleSpace = h - top - 2; + float articleSpace = h - top - 2 + insets.top; int rows = articles.size(); if (PixelScene.landscape()){ rows /= 2; diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/scenes/PixelScene.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/scenes/PixelScene.java index 46b6cfdb3..f29552080 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/scenes/PixelScene.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/scenes/PixelScene.java @@ -52,7 +52,9 @@ import com.watabou.noosa.ui.Cursor; import com.watabou.utils.Callback; import com.watabou.utils.DeviceCompat; import com.watabou.utils.GameMath; +import com.watabou.utils.PlatformSupport; import com.watabou.utils.PointF; +import com.watabou.utils.RectF; import com.watabou.utils.Reflection; import com.watabou.utils.Signal; @@ -119,7 +121,13 @@ public class PixelScene extends Scene { scaleFactor = 2.5f; } - maxDefaultZoom = (int)Math.min(Game.width/minWidth, Game.height/minHeight); + //TODO all insets? or just blockers? + RectF insets = Game.platform.getSafeInsets(PlatformSupport.INSET_ALL); + + float w = Game.width - insets.left - insets.right; + float h = Game.height - insets.top - insets.bottom; + + maxDefaultZoom = (int)Math.min(w/minWidth, h/minHeight); maxDefaultZoom = Math.max(2, maxDefaultZoom); defaultZoom = SPDSettings.scale(); @@ -397,6 +405,18 @@ public class PixelScene extends Scene { magnitude *= SPDSettings.screenShake(); Camera.main.shake(magnitude, duration); } + + //returns insets for the common case of all on top/bottom and only blocking on left/right + //plus scaled to pixel zoom + public RectF getCommonInsets(){ + RectF all = Game.platform.getSafeInsets(PlatformSupport.INSET_ALL); + RectF blocking = Game.platform.getSafeInsets(PlatformSupport.INSET_BLK); + + all.left = blocking.left; + all.right = blocking.right; + + return all.scale(1f/defaultZoom); + } protected static class Fader extends ColorBlock { diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/scenes/RankingsScene.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/scenes/RankingsScene.java index 553834840..19363ae0c 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/scenes/RankingsScene.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/scenes/RankingsScene.java @@ -48,6 +48,7 @@ import com.watabou.noosa.Camera; import com.watabou.noosa.Image; import com.watabou.noosa.audio.Music; import com.watabou.utils.GameMath; +import com.watabou.utils.RectF; public class RankingsScene extends PixelScene { @@ -74,18 +75,22 @@ public class RankingsScene extends PixelScene { int w = Camera.main.width; int h = Camera.main.height; - + RectF insets = getCommonInsets(); + archs = new Archs(); archs.setSize( w, h ); add( archs ); - + + w -= insets.left + insets.right; + h -= insets.top + insets.bottom; + Rankings.INSTANCE.load(); IconTitle title = new IconTitle( Icons.RANKINGS.get(), Messages.get(this, "title")); title.setSize(200, 0); title.setPos( - (w - title.reqWidth()) / 2f, - (20 - title.height()) / 2f + insets.left + (w - title.reqWidth()) / 2f, + insets.top + (20 - title.height()) / 2f ); align(title); add(title); @@ -93,7 +98,7 @@ public class RankingsScene extends PixelScene { if (Rankings.INSTANCE.records.size() > 0) { //attempts to give each record as much space as possible, ideally as much space as portrait mode - float rowHeight = GameMath.gate(ROW_HEIGHT_MIN, (uiCamera.height - 26)/Rankings.INSTANCE.records.size(), ROW_HEIGHT_MAX); + float rowHeight = GameMath.gate(ROW_HEIGHT_MIN, (h - 26)/Rankings.INSTANCE.records.size(), ROW_HEIGHT_MAX); float left = (w - Math.min( MAX_ROW_WIDTH, w )) / 2 + GAP; float top = (h - rowHeight * Rankings.INSTANCE.records.size()) / 2; @@ -106,7 +111,7 @@ public class RankingsScene extends PixelScene { if (rowHeight <= 14){ offset = (pos % 2 == 1) ? 5 : -5; } - row.setRect( left+offset, top + pos * rowHeight, w - left * 2, rowHeight ); + row.setRect( insets.left + left+offset, insets.top + top + pos * rowHeight, w - left * 2, rowHeight ); add(row); pos++; @@ -121,8 +126,8 @@ public class RankingsScene extends PixelScene { add( label ); label.setPos( - (w - label.width()) / 2, - h - label.height() - 2*GAP + insets.left + (w - label.width()) / 2, + insets.top + h - label.height() - 2*GAP ); align(label); @@ -133,8 +138,8 @@ public class RankingsScene extends PixelScene { RenderedTextBlock noRec = PixelScene.renderTextBlock(Messages.get(this, "no_games"), 8); noRec.hardlight( 0xCCCCCC ); noRec.setPos( - (w - noRec.width()) / 2, - (h - noRec.height()) / 2 + insets.left + (w - noRec.width()) / 2, + insets.top + (h - noRec.height()) / 2 ); align(noRec); add(noRec); @@ -142,10 +147,10 @@ public class RankingsScene extends PixelScene { } ExitButton btnExit = new ExitButton(); - btnExit.setPos( Camera.main.width - btnExit.width(), 0 ); + btnExit.setPos( Camera.main.width - btnExit.width() - insets.right, insets.top ); add( btnExit ); - int left = 0; + float left = insets.left; if (Rankings.INSTANCE.latestDaily != null) { IconButton btnDailies = new IconButton(Icons.CALENDAR.get()) { @@ -160,7 +165,7 @@ public class RankingsScene extends PixelScene { } }; btnDailies.icon().hardlight(0.5f, 1f, 2f); - btnDailies.setRect( left, 0, 16, 20 ); + btnDailies.setRect( left, insets.top, 16, 20 ); left += 16; add(btnDailies); } diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/scenes/StartScene.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/scenes/StartScene.java index 36da38485..9086f0dd7 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/scenes/StartScene.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/scenes/StartScene.java @@ -43,6 +43,7 @@ import com.watabou.noosa.Camera; import com.watabou.noosa.Game; import com.watabou.noosa.Image; import com.watabou.noosa.NinePatch; +import com.watabou.utils.RectF; import java.util.ArrayList; @@ -59,23 +60,27 @@ public class StartScene extends PixelScene { Journal.loadGlobal(); uiCamera.visible = false; - + int w = Camera.main.width; int h = Camera.main.height; + RectF insets = getCommonInsets(); Archs archs = new Archs(); archs.setSize( w, h ); add( archs ); + + w -= insets.left + insets.right; + h -= insets.top + insets.bottom; ExitButton btnExit = new ExitButton(); - btnExit.setPos( w - btnExit.width(), 0 ); + btnExit.setPos( insets.left + w - btnExit.width(), insets.top ); add( btnExit ); IconTitle title = new IconTitle( Icons.ENTER.get(), Messages.get(this, "title")); title.setSize(200, 0); title.setPos( - (w - title.reqWidth()) / 2f, - (20 - title.height()) / 2f + insets.left + (w - title.reqWidth()) / 2f, + insets.top + (20 - title.height()) / 2f ); align(title); add(title); @@ -92,9 +97,9 @@ public class StartScene extends PixelScene { slotsHeight -= slotCount-1; } - float yPos = (h - slotsHeight + title.bottom() + 2)/2f - 4; + float yPos = insets.top + (h - slotsHeight + title.bottom() + 2)/2f - 4; yPos = Math.max(yPos, title.bottom()+2); - float slotLeft = (w - SLOT_WIDTH) / 2f; + float slotLeft = insets.left + (w - SLOT_WIDTH) / 2f; for (GamesInProgress.Info game : games) { SaveSlotButton existingGame = new SaveSlotButton(); diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/scenes/SupporterScene.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/scenes/SupporterScene.java index a55493bdb..f7f9213f6 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/scenes/SupporterScene.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/scenes/SupporterScene.java @@ -36,6 +36,8 @@ import com.watabou.noosa.Camera; import com.watabou.noosa.Image; import com.watabou.noosa.NinePatch; import com.watabou.noosa.ui.Component; +import com.watabou.utils.Callback; +import com.watabou.utils.RectF; public class SupporterScene extends PixelScene { @@ -50,6 +52,7 @@ public class SupporterScene extends PixelScene { int w = Camera.main.width; int h = Camera.main.height; + RectF insets = getCommonInsets(); int elementWidth = PixelScene.landscape() ? 202 : 120; @@ -57,15 +60,18 @@ public class SupporterScene extends PixelScene { archs.setSize(w, h); add(archs); + w -= insets.right + insets.left; + h -= insets.top + insets.bottom; + ExitButton btnExit = new ExitButton(); - btnExit.setPos(w - btnExit.width(), 0); + btnExit.setPos(insets.left + w - btnExit.width(), insets.top); add(btnExit); IconTitle title = new IconTitle(Icons.GOLD.get(), Messages.get(this, "title")); title.setSize(200, 0); title.setPos( - (w - title.reqWidth()) / 2f, - (20 - title.height()) / 2f + insets.left + (w - title.reqWidth()) / 2f, + insets.top + (20 - title.height()) / 2f ); align(title); add(title); @@ -93,8 +99,8 @@ public class SupporterScene extends PixelScene { float elementHeight = msg.height() + BTN_HEIGHT + GAP; - float top = 16 + (h - 16 - elementHeight)/2f; - float left = (w-elementWidth)/2f; + float top = insets.top + 16 + (h - 16 - elementHeight)/2f; + float left = insets.left + (w-elementWidth)/2f; msg.setPos(left, top); align(msg); diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/scenes/SurfaceScene.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/scenes/SurfaceScene.java index 7bbad82f9..04097f055 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/scenes/SurfaceScene.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/scenes/SurfaceScene.java @@ -56,6 +56,7 @@ import com.watabou.noosa.audio.Music; import com.watabou.utils.Point; import com.watabou.utils.PointF; import com.watabou.utils.Random; +import com.watabou.utils.RectF; import java.nio.Buffer; import java.nio.FloatBuffer; @@ -95,14 +96,19 @@ public class SurfaceScene extends PixelScene { int w = Camera.main.width; int h = Camera.main.height; + + RectF insets = getCommonInsets(); Archs archs = new Archs(); archs.reversed = true; archs.setSize( w, h ); add( archs ); - float vx = align((w - SKY_WIDTH) / 2f); - float vy = align((h - SKY_HEIGHT - BUTTON_HEIGHT) / 2f); + w -= insets.left + insets.right; + h -= insets.top + insets.bottom; + + float vx = align(insets.left + (w - SKY_WIDTH) / 2f); + float vy = align(insets.top + (h - SKY_HEIGHT - BUTTON_HEIGHT) / 2f); Point s = Camera.main.cameraToScreen( vx, vy ); viewport = new Camera( s.x, s.y, SKY_WIDTH, SKY_HEIGHT, defaultZoom ); diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/scenes/TitleScene.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/scenes/TitleScene.java index bb5d5d5be..8788f6d92 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/scenes/TitleScene.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/scenes/TitleScene.java @@ -52,6 +52,7 @@ import com.watabou.noosa.Image; import com.watabou.noosa.audio.Music; import com.watabou.utils.ColorMath; import com.watabou.utils.DeviceCompat; +import com.watabou.utils.RectF; import java.util.Date; @@ -71,18 +72,23 @@ public class TitleScene extends PixelScene { int w = Camera.main.width; int h = Camera.main.height; - + + RectF insets = getCommonInsets(); + Archs archs = new Archs(); archs.setSize( w, h ); add( archs ); + w -= insets.left + insets.right; + h -= insets.top + insets.bottom; + Image title = BannerSprites.get( landscape() ? BannerSprites.Type.TITLE_LAND : BannerSprites.Type.TITLE_PORT); add( title ); float topRegion = Math.max(title.height - 6, h*0.45f); - title.x = (w - title.width()) / 2f; - title.y = 2 + (topRegion - title.height()) / 2f; + title.x = insets.left + (w - title.width()) / 2f; + title.y = insets.top + 2 + (topRegion - title.height()) / 2f; align(title); @@ -190,9 +196,9 @@ public class TitleScene extends PixelScene { GAP = Math.max(GAP, 2); float buttonAreaWidth = landscape() ? PixelScene.MIN_WIDTH_L-6 : PixelScene.MIN_WIDTH_P-2; - float btnAreaLeft = (Camera.main.width - buttonAreaWidth) / 2f; + float btnAreaLeft = insets.left + (w - buttonAreaWidth) / 2f; if (landscape()) { - btnPlay.setRect(btnAreaLeft, topRegion+GAP, (buttonAreaWidth/2)-1, BTN_HEIGHT); + btnPlay.setRect(btnAreaLeft, insets.top + topRegion+GAP, (buttonAreaWidth/2)-1, BTN_HEIGHT); align(btnPlay); btnSupport.setRect(btnPlay.right()+2, btnPlay.top(), btnPlay.width(), BTN_HEIGHT); btnRankings.setRect(btnPlay.left(), btnPlay.bottom()+ GAP, (float) (Math.floor(buttonAreaWidth/3f)-1), BTN_HEIGHT); @@ -202,7 +208,7 @@ public class TitleScene extends PixelScene { btnChanges.setRect(btnSettings.right()+2, btnSettings.top(), btnRankings.width(), BTN_HEIGHT); btnAbout.setRect(btnChanges.right()+2, btnSettings.top(), btnRankings.width(), BTN_HEIGHT); } else { - btnPlay.setRect(btnAreaLeft, topRegion+GAP, buttonAreaWidth, BTN_HEIGHT); + btnPlay.setRect(btnAreaLeft, insets.top + topRegion+GAP, buttonAreaWidth, BTN_HEIGHT); align(btnPlay); btnSupport.setRect(btnPlay.left(), btnPlay.bottom()+ GAP, btnPlay.width(), BTN_HEIGHT); btnRankings.setRect(btnPlay.left(), btnSupport.bottom()+ GAP, (btnPlay.width()/2)-1, BTN_HEIGHT); @@ -216,8 +222,9 @@ public class TitleScene extends PixelScene { BitmapText version = new BitmapText( "v" + Game.version, pixelFont); version.measure(); version.hardlight( 0x888888 ); - version.x = w - version.width() - 4; - version.y = h - version.height() - 2; + //TODO perhaps extra check for Android top-right / top-left notches? + version.x = insets.left + w - version.width() - 4; + version.y = insets.top + h - version.height() - 2; add( version ); if (DeviceCompat.isDesktop()) { diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/scenes/WelcomeScene.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/scenes/WelcomeScene.java index b51daf135..429af253b 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/scenes/WelcomeScene.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/scenes/WelcomeScene.java @@ -48,6 +48,7 @@ import com.watabou.noosa.Game; import com.watabou.noosa.Image; import com.watabou.noosa.audio.Music; import com.watabou.utils.FileUtils; +import com.watabou.utils.RectF; import java.util.Collections; @@ -94,6 +95,7 @@ public class WelcomeScene extends PixelScene { int w = Camera.main.width; int h = Camera.main.height; + RectF insets = getCommonInsets(); Archs archs = new Archs(); archs.setSize( w, h ); @@ -102,13 +104,16 @@ public class WelcomeScene extends PixelScene { //darkens the arches add(new ColorBlock(w, h, 0x88000000)); + w -= insets.left + insets.right; + h -= insets.top + insets.bottom; + Image title = BannerSprites.get( landscape() ? BannerSprites.Type.TITLE_LAND : BannerSprites.Type.TITLE_PORT); add( title ); float topRegion = Math.max(title.height - 6, h*0.45f); - title.x = (w - title.width()) / 2f; - title.y = 2 + (topRegion - title.height()) / 2f; + title.x = insets.left + (w - title.width()) / 2f; + title.y = insets.top + 2 + (topRegion - title.height()) / 2f; align(title); @@ -165,10 +170,10 @@ public class WelcomeScene extends PixelScene { } }; - float buttonY = Math.min(topRegion + (PixelScene.landscape() ? 60 : 120), h - 24); + float buttonY = insets.top + Math.min(topRegion + (PixelScene.landscape() ? 60 : 120), h - 24); float buttonAreaWidth = landscape() ? PixelScene.MIN_WIDTH_L-6 : PixelScene.MIN_WIDTH_P-2; - float btnAreaLeft = (Camera.main.width - buttonAreaWidth) / 2f; + float btnAreaLeft = insets.left + (w - buttonAreaWidth) / 2f; if (previousVersion != 0 && !SPDSettings.intro()){ StyledButton changes = new StyledButton(Chrome.Type.GREY_BUTTON_TR, Messages.get(TitleScene.class, "changes")){ @Override @@ -216,7 +221,7 @@ public class WelcomeScene extends PixelScene { text.text(message, Math.min(w-20, 300)); float titleBottom = title.y + title.height(); float textSpace = okay.top() - titleBottom - 4; - text.setPos((w - text.width()) / 2f, (titleBottom + 2) + (textSpace - text.height())/2); + text.setPos(insets.left + (w - text.width()) / 2f, (titleBottom + 2) + (textSpace - text.height())/2); add(text); if (SPDSettings.intro() && ControllerHandler.isControllerConnected()){ diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/ui/Window.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/ui/Window.java index 2f3ceb36a..94694f1f8 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/ui/Window.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/ui/Window.java @@ -33,7 +33,9 @@ import com.watabou.noosa.Game; import com.watabou.noosa.Group; import com.watabou.noosa.NinePatch; import com.watabou.noosa.PointerArea; +import com.watabou.utils.PlatformSupport; import com.watabou.utils.Point; +import com.watabou.utils.RectF; import com.watabou.utils.Signal; public class Window extends Group implements Signal.Listener { @@ -94,13 +96,17 @@ public class Window extends Group implements Signal.Listener { width - chrome.x + chrome.marginRight(), height - chrome.y + chrome.marginBottom() ); add( chrome ); + + RectF insets = Game.platform.getSafeInsets(PlatformSupport.INSET_BLK); + int screenW = (int)(Game.width - insets.left - insets.right); + int screenH = (int)(Game.height - insets.top - insets.bottom); camera = new Camera( 0, 0, (int)chrome.width, (int)chrome.height, PixelScene.defaultZoom ); - camera.x = (int)(Game.width - camera.width * camera.zoom) / 2; - camera.y = (int)(Game.height - camera.height * camera.zoom) / 2; + camera.x = (int)(insets.left + (screenW - camera.width * camera.zoom) / 2); + camera.y = (int)(insets.top + (screenH - camera.height * camera.zoom) / 2); camera.y -= yOffset * camera.zoom; camera.scroll.set( chrome.x, chrome.y ); Camera.add( camera ); @@ -123,10 +129,16 @@ public class Window extends Group implements Signal.Listener { camera.resize( (int)chrome.width, (int)chrome.height ); - camera.x = (int)(Game.width - camera.screenWidth()) / 2; + RectF insets = Game.platform.getSafeInsets(PlatformSupport.INSET_BLK); + int screenW = (int)(Game.width - insets.left - insets.right); + int screenH = (int)(Game.height - insets.top - insets.bottom); + + camera.x = (int)(screenW - camera.screenWidth()) / 2; + camera.x += insets.left; camera.x += xOffset * camera.zoom; - camera.y = (int)(Game.height - camera.screenHeight()) / 2; + camera.y = (int)(screenH - camera.screenHeight()) / 2; + camera.y += insets.top; camera.y += yOffset * camera.zoom; shadow.boxRect( camera.x / camera.zoom, camera.y / camera.zoom, chrome.width(), chrome.height ); 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 904cb7a27..007eb6a61 100644 --- a/ios/src/main/java/com/shatteredpixel/shatteredpixeldungeon/ios/IOSPlatformSupport.java +++ b/ios/src/main/java/com/shatteredpixel/shatteredpixeldungeon/ios/IOSPlatformSupport.java @@ -33,6 +33,7 @@ import com.shatteredpixel.shatteredpixeldungeon.ShatteredPixelDungeon; 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.systemconfiguration.SCNetworkReachability; @@ -63,6 +64,16 @@ public class IOSPlatformSupport extends PlatformSupport { return false; } + @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); + } + @Override public void updateSystemUI() { int prevInset = Game.bottomInset;