From 2c0f1263a72b7b7b04b21ca172112025c33b42b0 Mon Sep 17 00:00:00 2001 From: Evan Debenham Date: Sun, 14 Sep 2025 11:58:11 -0400 Subject: [PATCH] v3.2.4: updates to improve handling of Android cutouts: - HP bar can now resize-reposition to prevent being cut off - menu pane can now move inward to prevent being cut off - increased buff bar cut off sensitivity - slightly increased 'large cutout' permissiveness --- .../android/AndroidPlatformSupport.java | 5 ++- .../main/assets/interfaces/status_pane.png | Bin 1061 -> 5284 bytes .../scenes/GameScene.java | 42 +++++++++++++++--- .../ui/DangerIndicator.java | 4 +- .../shatteredpixeldungeon/ui/MenuPane.java | 1 + .../shatteredpixeldungeon/ui/StatusPane.java | 35 ++++++++++++--- 6 files changed, 70 insertions(+), 17 deletions(-) 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 67c9e6964..f9cdc4ccb 100644 --- a/android/src/main/java/com/shatteredpixel/shatteredpixeldungeon/android/AndroidPlatformSupport.java +++ b/android/src/main/java/com/shatteredpixel/shatteredpixeldungeon/android/AndroidPlatformSupport.java @@ -121,9 +121,10 @@ public class AndroidPlatformSupport extends PlatformSupport { for (Rect r : cutout.getBoundingRects()){ //use abs as some cutouts can apparently be returned inverted int cutoutSize = Math.abs(r.height() * r.width()); - //display cutouts are considered large if they take up more than 0.605% of the screen + //display cutouts are considered large if they take up more than 0.667% of the screen //in reality we want less than about 0.5%, but some cutouts over-report their size - if (cutoutSize*165 >= screenSize){ + //Pixel devices especially =S + if (cutoutSize*150 >= screenSize){ largeCutout = true; } } diff --git a/core/src/main/assets/interfaces/status_pane.png b/core/src/main/assets/interfaces/status_pane.png index cbe1ff0000f7fed1175d9f999b01f5f6cf014921..b2f836b03d23e025acdfa5c1db8fbd44a5a6fa3a 100644 GIT binary patch literal 5284 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 C4yYIa delta 777 zcmV+k1NQu+DWwRIBL)ErP)t-svkMRl0e=l+PZ$6I00MMUPE-E?<+m_O0008VNkloD<=~Afp4Xg+nT-%VHwLliw zW(q#onFX95*TQi8o3Jm1c|z|54Z#REX$15<8V*4S6R90qO^q^uLI`V2?F5y^RDWI_ zFp@bT<7P=A%0IvIr~}-u2_ZjkEpez^RvvwTbvArt*{Nz>fCuyeqj12;axO7nfHuH5 z6FxEso`~828xF7rJOc-iB)}L#Ob*BaIUon*fK&nHfrJA}n>tjs;p1I{;RTsJKN;NA)G1LTrx zHjslefPG2x8nWi=d>fz~rwI@`nuck405F@T_zIw)V*yeKK#}Gv*rIEz!a4)sY>I&G ziZy0$39x4p1nhn%@D_|n#eHLqwMVQ}mdHd{XQ1VYO$O!|m^?!3GkucBD1UvTSMmg% zkMv8PqVj>B$uqRx(R|#3a~8hGPPISa%GCwpVxawj@C9PJ{ec7* zh>rGS?r1;v_!o!{rsX1hG(Aahf!OX3gv{Xh5CD#dMRw0Oxyi+ys%{^}6NMC>e-kSJ z%b2j=QZuG~jQ3kGnG<%qs@t=x$Cz)y146E*PkR=35J@3~6hfLoWOkhiA6b$NqITG< z*)yaWM8}7O9i%o00000NkvXX Hu0mjfdyh`d 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 e1e9865ab..401e2ee5b 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/scenes/GameScene.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/scenes/GameScene.java @@ -368,22 +368,39 @@ public class GameScene extends PixelScene { int uiSize = SPDSettings.interfaceSize(); - //Some more medium sized display cutouts can obstruct the buff bar, so we limit the length - // of the 1st row in some cases + //display cutouts can obstruct various UI elements, so we need to adjust for that sometimes + float menuBarMaxLeft = uiCamera.width-insets.right-MenuPane.WIDTH; + int hpBarMaxWidth = 50; //default max width float buffBarTopRowMaxWidth = 50; //default max width if (largeInsetTop != insets.top){ - //most notably iOS's Dynamic island, which must exist in this case + //iOS's Dynamic island badly obstructs the first buff bar row if (DeviceCompat.isiOS()){ //TODO bad to hardcode this atm, need to change this so platformsupport returns cutout dimensions buffBarTopRowMaxWidth = 15; } else if (DeviceCompat.isAndroid()) { - //some android hole punches can also be big too + //Android hole punches are of varying size and may obstruct the menu, HP bar, or buff bar RectF cutout = Game.platform.getDisplayCutout().scale(1f / defaultZoom); + //if the cutout is positioned to obstruct the menu bar + if (cutout.top < 20 + && cutout.left < menuBarMaxLeft + MenuPane.WIDTH + && cutout.right > menuBarMaxLeft) { + menuBarMaxLeft = Math.min(menuBarMaxLeft, cutout.left - MenuPane.WIDTH); + //make sure we have space to actually move it though + menuBarMaxLeft = Math.max(menuBarMaxLeft, PixelScene.MIN_WIDTH_P-MenuPane.WIDTH); + } + //if the cutout is positioned to obstruct the HP bar + if (cutout.left < 78 + && cutout.top < 4 + && cutout.right > 32) { + //subtract starting position, but add a bit back due to end of bar + hpBarMaxWidth = Math.round(cutout.left - 32 + 4); + hpBarMaxWidth = Math.max(hpBarMaxWidth, 21); //cannot go below 21 (30 effective) + } //if the cutout is positioned to obstruct the buff bar if (cutout.left < 80 && cutout.top < 10 && cutout.right > 32 - && cutout.bottom > 12) { + && cutout.bottom > 11) { buffBarTopRowMaxWidth = cutout.left - 32; //subtract starting position } } @@ -396,11 +413,24 @@ public class GameScene extends PixelScene { menu = new MenuPane(); menu.camera = uiCamera; - menu.setPos( uiCamera.width-MenuPane.WIDTH-insets.right, screentop); + menu.setPos( menuBarMaxLeft, screentop); add(menu); + float extraRight = uiCamera.width - (menuBarMaxLeft + MenuPane.WIDTH); + if (extraRight > 0){ + SkinnedBlock bar = new SkinnedBlock(extraRight, 20, TextureCache.createSolid(0x88000000)); + bar.x = uiCamera.width - extraRight; + bar.camera = uiCamera; + add(bar); + + PointerArea blocker = new PointerArea(uiCamera.width - extraRight, 0, extraRight, 20); + blocker.camera = uiCamera; + add(blocker); + } + status = new StatusPane( SPDSettings.interfaceSize() > 0 ); status.camera = uiCamera; + StatusPane.hpBarMaxWidth = hpBarMaxWidth; StatusPane.buffBarTopRowMaxWidth = buffBarTopRowMaxWidth; status.setRect(insets.left, uiSize > 0 ? uiCamera.height-39-insets.bottom : screentop, uiCamera.width - insets.left - insets.right, 0 ); add(status); diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/ui/DangerIndicator.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/ui/DangerIndicator.java index f0440efbf..1b5b4bf86 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/ui/DangerIndicator.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/ui/DangerIndicator.java @@ -73,14 +73,14 @@ public class DangerIndicator extends Tag { protected void layout() { super.layout(); - icon.x = right() - 10; + icon.x = left() + 14; icon.y = y + (height - icon.height) / 2; placeNumber(); } private void placeNumber() { - number.x = right() - 11 - number.width(); + number.x = left() + 13 - number.width(); number.y = y + (height - number.baseLine()) / 2f; PixelScene.align(number); } diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/ui/MenuPane.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/ui/MenuPane.java index 01c3b87d4..c0c9a5343 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/ui/MenuPane.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/ui/MenuPane.java @@ -208,6 +208,7 @@ public class MenuPane extends Component { } danger.setPos( x + WIDTH - danger.width(), y + bg.height + 1 ); + danger.setSize( camera.width - danger.width(), danger.height()); } public void pickup(Item item, int cell) { diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/ui/StatusPane.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/ui/StatusPane.java index 05778f7dd..0dea1410a 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/ui/StatusPane.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/ui/StatusPane.java @@ -78,8 +78,10 @@ public class StatusPane extends Component { private boolean large; - //lower the buff indicator to avoid larger cutouts (e.g. iPhone dynamic island) - public static float cutoutOffset; + //potentially shrinks and/or repositions the hp bar to avoid some cutouts + public static int hpBarMaxWidth = 50; + private Image hpCutout; + //potentially cuts off the top row of the the buff indicator to avoid some cutouts public static float buffBarTopRowMaxWidth = 50; public StatusPane( boolean large ){ @@ -90,10 +92,13 @@ public class StatusPane extends Component { this.large = large; if (large) bg = new NinePatch( asset, 0, 64, 41, 39, 33, 0, 4, 0 ); - //right part is transparent now so Ninepatching doesn't actually do anything - else bg = new NinePatch( asset, 0, 0, 128, 38, 85, 0, 45, 0 ); + else bg = new NinePatch( asset, 0, 0, 82, 38, 32, 0, 5, 0 ); add( bg ); + hpCutout = new Image(asset, 90, 0, 12, 9); + hpCutout.visible = false; + add(hpCutout); + heroInfo = new Button(){ @Override protected void onClick () { @@ -179,7 +184,7 @@ public class StatusPane extends Component { bg.x = x; bg.y = y; if (large) bg.size( 160, bg.height ); //HP bars must be 128px wide atm - else bg.size( width, bg.height ); + else bg.size(hpBarMaxWidth+32, bg.height ); //default max right is 50px health bar + 32 avatar.x = bg.x - avatar.width / 2f + 15; avatar.y = bg.y - avatar.height / 2f + 16; @@ -216,7 +221,23 @@ public class StatusPane extends Component { exp.x = x+2; exp.y = y+30; - hp.x = shieldedHP.x = rawShielding.x = x + 30; + float hpleft = x + 30; + if (hpBarMaxWidth < 82){ + //the class variable assumes the left of the bar can't move, but we can inset it 9px + int hpWidth = (int)hpBarMaxWidth; + if (hpWidth <= 41){ + hpleft -= 9; + hpWidth += 9; + hpCutout.visible = true; + hpCutout.x = hpleft - 2; + hpCutout.y = y; + } + hp.frame(50-hpWidth, 40, 50, 4); + shieldedHP.frame(50-hpWidth, 44, 50, 4); + rawShielding.frame(50-hpWidth, 44, 50, 4); + } + + hp.x = shieldedHP.x = rawShielding.x = hpleft; hp.y = shieldedHP.y = rawShielding.y = y + 2; hpText.scale.set(PixelScene.align(0.5f)); @@ -234,7 +255,7 @@ public class StatusPane extends Component { heroInfoOnBar.setRect(heroInfo.right(), y, 50, 9); buffs.firstRowWidth = buffBarTopRowMaxWidth; - buffs.setRect( x + 31, y + 8 + cutoutOffset, 50, 15 ); + buffs.setRect( x + 31, y + 8, 50, 15 ); busy.x = x + 1; busy.y = y + 37;