From 75453632f979353766bd229384535259bdbe29b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Sat, 14 Mar 2026 17:06:23 +0100 Subject: [PATCH] Handle inset adjustements on the C++ side, handle them better in the UI. --- Common/UI/Context.cpp | 40 +++++++++++++++++-- Common/UI/Context.h | 4 +- Common/UI/PopupScreens.cpp | 32 ++++++++++----- Common/UI/Root.cpp | 4 +- Common/UI/Root.h | 2 +- Common/UI/Screen.h | 6 +++ Common/UI/ScrollView.cpp | 4 +- Common/UI/UIScreen.cpp | 6 +-- Common/UI/UIScreen.h | 6 ++- GPU/Common/PresentationCommon.cpp | 35 ++++++++-------- UI/BaseScreens.cpp | 28 +++++++++++++ UI/BaseScreens.h | 5 +++ UI/ControlMappingScreen.cpp | 2 +- UI/CustomButtonMappingScreen.cpp | 2 +- UI/DebugOverlay.cpp | 7 ++-- UI/DevScreens.h | 4 ++ UI/DisplayLayoutScreen.cpp | 12 ++++-- UI/EmuScreen.cpp | 7 +++- UI/EmuScreen.h | 1 + UI/GPUDriverTestScreen.cpp | 1 - UI/GameSettingsScreen.cpp | 1 - UI/MainScreen.cpp | 1 - UI/MainScreen.h | 2 + UI/MiscScreens.cpp | 2 - UI/MiscScreens.h | 5 +++ UI/OnScreenDisplay.cpp | 5 +++ UI/OnScreenDisplay.h | 5 +-- UI/SimpleDialogScreen.cpp | 26 ++++++++---- UI/SimpleDialogScreen.h | 6 +++ UI/TabbedDialogScreen.h | 4 +- Windows/main.cpp | 16 ++++++++ .../src/org/ppsspp/ppsspp/PpssppActivity.java | 13 ------ 32 files changed, 210 insertions(+), 84 deletions(-) diff --git a/Common/UI/Context.cpp b/Common/UI/Context.cpp index 8f1a2b38f9..00b835b416 100644 --- a/Common/UI/Context.cpp +++ b/Common/UI/Context.cpp @@ -141,15 +141,46 @@ Bounds UIContext::GetScissorBounds() { return bounds_; } -Bounds UIContext::GetLayoutBounds(bool ignoreBottomInset) const { - Bounds bounds = GetBounds(); - +Bounds UIContext::GetLayoutBounds(ViewLayoutMode layoutMode, bool immersiveMode) const { float left = System_GetPropertyFloat(SYSPROP_DISPLAY_SAFE_INSET_LEFT); float right = System_GetPropertyFloat(SYSPROP_DISPLAY_SAFE_INSET_RIGHT); float top = System_GetPropertyFloat(SYSPROP_DISPLAY_SAFE_INSET_TOP); float bottom = System_GetPropertyFloat(SYSPROP_DISPLAY_SAFE_INSET_BOTTOM); - if (ignoreBottomInset) { + + switch (layoutMode) { + case ViewLayoutMode::ApplyInsets: + // Nothing to do + break; + case ViewLayoutMode::IgnoreInsets: + left = 0.0f; + top = 0.0f; + right = 0.0f; bottom = 0.0f; + break; + case ViewLayoutMode::IgnoreBottomInset: + bottom = 0.0f; + break; + } + + if (immersiveMode) { + if (left > 0 && right > 0) { + // Navigation bar is available, so insets leave space for it + // even if it's hidden. + int smallestNonZero = std::min(right, left); + left = smallestNonZero; + right = smallestNonZero; + } else { + int sideWidth = std::max(left, right); + left = sideWidth; + right = sideWidth; + } + } else { + if ((left > 0) != (right > 0)) { + // Only one side has a cutout (surely the camera, which is usually smaller than the nav bar), so use the larger of the two for both sides. + int sideWidth = std::max(left, right); + left = sideWidth; + right = sideWidth; + } } // Note that we ignore bottom here, to let lists etc. extend to the bottom of the screen. @@ -157,6 +188,7 @@ Bounds UIContext::GetLayoutBounds(bool ignoreBottomInset) const { // touch things below the safe inset. // Adjust left edge to compensate for cutouts (notches) if any. + Bounds bounds = GetBounds(); bounds.x += left; bounds.w -= (left + right); bounds.y += top; diff --git a/Common/UI/Context.h b/Common/UI/Context.h index bf2f8aa902..cb308f28d5 100644 --- a/Common/UI/Context.h +++ b/Common/UI/Context.h @@ -56,6 +56,8 @@ struct AtlasData { typedef std::function UIAtlasProviderFunc; +enum class ViewLayoutMode; + class UIContext { public: UIContext(); @@ -113,7 +115,7 @@ public: // in dps, like dp_xres and dp_yres void SetBounds(const Bounds &b) { bounds_ = b; } const Bounds &GetBounds() const { return bounds_; } - Bounds GetLayoutBounds(bool ignoreBottomInset) const; + Bounds GetLayoutBounds(ViewLayoutMode layoutMode, bool immersiveMode) const; Draw::DrawContext *GetDrawContext() const { return draw_; } const UI::Theme &GetTheme() const { return *theme; diff --git a/Common/UI/PopupScreens.cpp b/Common/UI/PopupScreens.cpp index 8adffc00e6..ec40e788c1 100644 --- a/Common/UI/PopupScreens.cpp +++ b/Common/UI/PopupScreens.cpp @@ -17,21 +17,31 @@ namespace UI { +static void TextToImage(std::string_view buttonText, ImageID *image) { + auto di = GetI18NCategory(I18NCat::DIALOG); + if (buttonText == di->T("Delete") || buttonText == di->T("Move to trash")) { + *image = ImageID("I_TRASHCAN"); + } else if (buttonText == di->T("Back")) { + *image = ImageID("I_NAVIGATE_BACK"); + } else if (buttonText == di->T("Add")) { + *image = ImageID("I_PLUS"); + } else if (buttonText == di->T("OK")) { + *image = ImageID("I_CHECKMARK"); + } else if (buttonText == di->T("Cancel")) { + *image = ImageID("I_NAVIGATE_BACK"); + } else if (buttonText == di->T("Exit")) { + *image = ImageID("I_EXIT"); + } +} + PopupScreen::PopupScreen(std::string_view title, std::string_view button1, std::string_view button2) : title_(title), button1_(button1), button2_(button2) { - auto di = GetI18NCategory(I18NCat::DIALOG); // Auto-assign images. A bit hack to have this here. - if (button1 == di->T("Delete") || button1 == di->T("Move to trash")) { - button1Image_ = ImageID("I_TRASHCAN"); - } else if (button1 == di->T("Back")) { - button1Image_ = ImageID("I_NAVIGATE_BACK"); - } else if (button1 == di->T("Add")) { - button1Image_ = ImageID("I_PLUS"); - } else if (button1 == di->T("OK")) { - button1Image_ = ImageID("I_CHECKMARK"); + if (!button1.empty()) { + TextToImage(button1, &button1Image_); } - if (button2 == di->T("Cancel")) { - button2Image_ = ImageID("I_NAVIGATE_BACK"); + if (!button2.empty()) { + TextToImage(button2, &button2Image_); } alpha_ = 0.0f; // inherited diff --git a/Common/UI/Root.cpp b/Common/UI/Root.cpp index 12c81db67c..9a0da21e50 100644 --- a/Common/UI/Root.cpp +++ b/Common/UI/Root.cpp @@ -131,8 +131,8 @@ bool IsFocusMovementEnabled() { return focusMovementEnabled; } -void LayoutViewHierarchy(const UIContext &dc, const UI::Margins &rootMargins, ViewGroup *root, bool ignoreInsets, bool ignoreBottomInset) { - Bounds rootBounds = ignoreInsets ? dc.GetBounds() : dc.GetLayoutBounds(ignoreBottomInset); +void LayoutViewHierarchy(const UIContext &dc, const UI::Margins &rootMargins, ViewGroup *root, ViewLayoutMode layoutMode, bool immersiveMode) { + Bounds rootBounds = dc.GetLayoutBounds(layoutMode, immersiveMode); MeasureSpec horiz(EXACTLY, rootBounds.w - (rootMargins.left + rootMargins.right)); MeasureSpec vert(EXACTLY, rootBounds.h - (rootMargins.top + rootMargins.bottom)); diff --git a/Common/UI/Root.h b/Common/UI/Root.h index 9a0c7e1184..0063af72d1 100644 --- a/Common/UI/Root.h +++ b/Common/UI/Root.h @@ -24,7 +24,7 @@ DialogResult DispatchEvents(); class ViewGroup; -void LayoutViewHierarchy(const UIContext &dc, const UI::Margins &rootMargins, UI::ViewGroup *root, bool ignoreInsets, bool ignoreBottomInset); +void LayoutViewHierarchy(const UIContext &dc, const UI::Margins &rootMargins, UI::ViewGroup *root, ViewLayoutMode layoutMode, bool immersiveMode); DialogResult UpdateViewHierarchy(ViewGroup *root); enum class KeyEventResult { diff --git a/Common/UI/Screen.h b/Common/UI/Screen.h index 9f9b7fd886..20d25ca069 100644 --- a/Common/UI/Screen.h +++ b/Common/UI/Screen.h @@ -70,6 +70,12 @@ enum class ScreenRenderRole { }; ENUM_CLASS_BITOPS(ScreenRenderRole); +enum class ViewLayoutMode { + ApplyInsets = 0, // For gameplay, option #1 + IgnoreInsets, // For gameplay, option #2 + IgnoreBottomInset, +}; + class Screen { public: Screen() = default; diff --git a/Common/UI/ScrollView.cpp b/Common/UI/ScrollView.cpp index f060651d11..e3b2398cb6 100644 --- a/Common/UI/ScrollView.cpp +++ b/Common/UI/ScrollView.cpp @@ -294,8 +294,8 @@ void ScrollView::Draw(UIContext &dc) { dc.DrawRectDropShadow(shadowBounds, radius, fade); } - // Same at the bottom. - const float y2 = dc.GetLayoutBounds(false).y2(); + // Same at the bottom. (we check against the common UI layout mode) + const float y2 = dc.GetLayoutBounds(ViewLayoutMode::IgnoreBottomInset, false).y2(); if (shadows_ && bounds_.y2() < y2 && orientation_ == ORIENT_VERTICAL) { float radius = 20.0f; diff --git a/Common/UI/UIScreen.cpp b/Common/UI/UIScreen.cpp index 3bff6316cf..8566822af6 100644 --- a/Common/UI/UIScreen.cpp +++ b/Common/UI/UIScreen.cpp @@ -55,7 +55,7 @@ void UIScreen::DoRecreateViews() { // Update layout and refocus so things scroll into view. // This is for resizing down, when focused on something now offscreen. - UI::LayoutViewHierarchy(*screenManager()->getUIContext(), RootMargins(), root_, ignoreInsets_, ignoreBottomInset_); + UI::LayoutViewHierarchy(*screenManager()->getUIContext(), RootMargins(), root_, LayoutMode(), UseImmersiveMode()); UI::View *focused = UI::GetFocusedView(); if (focused) { root_->SubviewFocused(focused); @@ -201,7 +201,7 @@ void UIScreen::deviceRestored(Draw::DrawContext *draw) { } Bounds UIScreen::GetLayoutBounds(UIContext &dc) const { - return dc.GetLayoutBounds(ignoreBottomInset_); + return dc.GetLayoutBounds(LayoutMode(), UseImmersiveMode()); } ScreenRenderFlags UIScreen::render(ScreenRenderMode mode) { @@ -209,7 +209,7 @@ ScreenRenderFlags UIScreen::render(ScreenRenderMode mode) { UIContext &uiContext = *screenManager()->getUIContext(); if (root_) { - UI::LayoutViewHierarchy(uiContext, RootMargins(), root_, ignoreInsets_, ignoreBottomInset_); + UI::LayoutViewHierarchy(uiContext, RootMargins(), root_, LayoutMode(), UseImmersiveMode()); } uiContext.PushTransform({translation_, scale_, alpha_}); diff --git a/Common/UI/UIScreen.h b/Common/UI/UIScreen.h index 7d5302c449..e319b097d2 100644 --- a/Common/UI/UIScreen.h +++ b/Common/UI/UIScreen.h @@ -10,6 +10,8 @@ using namespace Lin; +enum class ViewLayoutMode; + class I18NCategory; namespace Draw { class DrawContext; @@ -67,13 +69,13 @@ protected: void RecreateViews() override { recreateViews_ = true; } DeviceOrientation GetDeviceOrientation() const; bool IsOnTop() const; + virtual ViewLayoutMode LayoutMode() const { return ViewLayoutMode::ApplyInsets; } + virtual bool UseImmersiveMode() const { return false; } UI::ViewGroup *root_ = nullptr; Vec3 translation_ = Vec3(0.0f); Vec3 scale_ = Vec3(1.0f); float alpha_ = 1.0f; - bool ignoreInsets_ = false; - bool ignoreBottomInset_ = false; bool ignoreInput_ = false; protected: diff --git a/GPU/Common/PresentationCommon.cpp b/GPU/Common/PresentationCommon.cpp index 045d8dbd23..37bf677416 100644 --- a/GPU/Common/PresentationCommon.cpp +++ b/GPU/Common/PresentationCommon.cpp @@ -54,7 +54,7 @@ void SetOverrideScreenFrame(const Bounds *bounds) { } } -FRect GetScreenFrame(bool ignoreInsets, float pixelWidth, float pixelHeight) { +FRect GetScreenFrame(bool ignoreScreenInsets, float pixelWidth, float pixelHeight) { FRect rc = FRect{ 0.0f, 0.0f, @@ -62,21 +62,7 @@ FRect GetScreenFrame(bool ignoreInsets, float pixelWidth, float pixelHeight) { pixelHeight, }; - const bool applyInset = !ignoreInsets; - - if (applyInset) { - // Remove the DPI scale to get back to pixels. - float left = System_GetPropertyFloat(SYSPROP_DISPLAY_SAFE_INSET_LEFT) / g_display.dpi_scale_x; - float right = System_GetPropertyFloat(SYSPROP_DISPLAY_SAFE_INSET_RIGHT) / g_display.dpi_scale_x; - float top = System_GetPropertyFloat(SYSPROP_DISPLAY_SAFE_INSET_TOP) / g_display.dpi_scale_y; - float bottom = System_GetPropertyFloat(SYSPROP_DISPLAY_SAFE_INSET_BOTTOM) / g_display.dpi_scale_y; - - // Adjust left edge to compensate for cutouts (notches) if any. - rc.x += left; - rc.w -= (left + right); - rc.y += top; - rc.h -= (top + bottom); - } + const bool applyInset = !ignoreScreenInsets; if (g_overrideScreenBounds) { // Set rectangle to match central node. Here we ignore bIgnoreScreenInsets. @@ -84,6 +70,23 @@ FRect GetScreenFrame(bool ignoreInsets, float pixelWidth, float pixelHeight) { rc.y = g_screenBounds.y; rc.w = g_screenBounds.w; rc.h = g_screenBounds.h; + } else if (applyInset) { + // Remove the DPI scale to get back to pixels. + float left = System_GetPropertyFloat(SYSPROP_DISPLAY_SAFE_INSET_LEFT) / g_display.dpi_scale_x; + float right = System_GetPropertyFloat(SYSPROP_DISPLAY_SAFE_INSET_RIGHT) / g_display.dpi_scale_x; + float top = System_GetPropertyFloat(SYSPROP_DISPLAY_SAFE_INSET_TOP) / g_display.dpi_scale_y; + float bottom = System_GetPropertyFloat(SYSPROP_DISPLAY_SAFE_INSET_BOTTOM) / g_display.dpi_scale_y; + + // NOTE: Similarly to what we do in UI, we disregard any bottom inset when in landscape mode. + if (g_display.GetDeviceOrientation() == DeviceOrientation::Landscape) { + bottom = 0.0f; + } + + // Adjust left edge to compensate for cutouts (notches) if any. + rc.x += left; + rc.w -= (left + right); + rc.y += top; + rc.h -= (top + bottom); } return rc; diff --git a/UI/BaseScreens.cpp b/UI/BaseScreens.cpp index e69de29bb2..b7bc505624 100644 --- a/UI/BaseScreens.cpp +++ b/UI/BaseScreens.cpp @@ -0,0 +1,28 @@ +#include "UI/BaseScreens.h" +#include "Core/Config.h" + +ViewLayoutMode UIBaseScreen::LayoutMode() const { + return ViewLayoutMode::IgnoreBottomInset; +} + +bool UIBaseScreen::UseImmersiveMode() const { + DisplayLayoutConfig &config = g_Config.GetDisplayLayoutConfig(GetDeviceOrientation()); + bool portrait = GetDeviceOrientation() == DeviceOrientation::Portrait; + if (!portrait && config.bImmersiveMode) { + return true; + } + return false; +} + +ViewLayoutMode UIBaseDialogScreen::LayoutMode() const { + return ViewLayoutMode::IgnoreBottomInset; +} + +bool UIBaseDialogScreen::UseImmersiveMode() const { + DisplayLayoutConfig &config = g_Config.GetDisplayLayoutConfig(GetDeviceOrientation()); + bool portrait = GetDeviceOrientation() == DeviceOrientation::Portrait; + if (!portrait && config.bImmersiveMode) { + return true; + } + return false; +} diff --git a/UI/BaseScreens.h b/UI/BaseScreens.h index 5ed87b6c69..f185b574be 100644 --- a/UI/BaseScreens.h +++ b/UI/BaseScreens.h @@ -8,6 +8,8 @@ class UIBaseScreen : public UIScreen { public: UIBaseScreen() : UIScreen() {} + ViewLayoutMode LayoutMode() const override; + bool UseImmersiveMode() const override; protected: void sendMessage(UIMessage message, const char *value) override; }; @@ -17,6 +19,9 @@ public: UIBaseDialogScreen() : UIDialogScreen(), gamePath_() {} explicit UIBaseDialogScreen(const Path &gamePath) : UIDialogScreen(), gamePath_(gamePath) {} protected: + ViewLayoutMode LayoutMode() const override; + bool UseImmersiveMode() const override; + void sendMessage(UIMessage message, const char *value) override; void AddStandardBack(UI::ViewGroup *parent); Path gamePath_; diff --git a/UI/ControlMappingScreen.cpp b/UI/ControlMappingScreen.cpp index 7459b9f928..3185fcb034 100644 --- a/UI/ControlMappingScreen.cpp +++ b/UI/ControlMappingScreen.cpp @@ -1025,7 +1025,7 @@ void VisualMappingScreen::MapNext(bool successive) { HandleKeyMapping(mapping); }, I18NCat::KEYMAPPING); - Bounds bounds = screenManager()->getUIContext()->GetLayoutBounds(false); + Bounds bounds = screenManager()->getUIContext()->GetLayoutBounds(LayoutMode(), false); dialog->SetPopupOffset(psp_->GetPopupOffset() * bounds.h); dialog->SetDelay(successive ? 0.5f : 0.1f); screenManager()->push(dialog); diff --git a/UI/CustomButtonMappingScreen.cpp b/UI/CustomButtonMappingScreen.cpp index dd1a77a832..0cfc94d570 100644 --- a/UI/CustomButtonMappingScreen.cpp +++ b/UI/CustomButtonMappingScreen.cpp @@ -142,7 +142,7 @@ void CustomButtonMappingScreen::CreateDialogViews(UI::ViewGroup *parent) { array[i] = (0x01 == ((g_Config.CustomButton[id_].key >> i) & 0x01)); // TODO: Less hacky layout work - const Bounds layoutBounds = screenManager()->getUIContext()->GetLayoutBounds(false); + const Bounds layoutBounds = GetLayoutBounds(*screenManager()->getUIContext()); leftColumn->Add(new ButtonPreview(g_Config.iTouchButtonStyle == 0 ? customKeyShapes[cfg->shape].i : customKeyShapes[cfg->shape].l, customKeyImages[cfg->image].i, customKeyImages[cfg->image].r, customKeyShapes[cfg->shape].f, customKeyShapes[cfg->shape].r, layoutBounds.x + 62, layoutBounds.y + 102)); diff --git a/UI/DebugOverlay.cpp b/UI/DebugOverlay.cpp index 7d92fb54fc..b8c65ab45b 100644 --- a/UI/DebugOverlay.cpp +++ b/UI/DebugOverlay.cpp @@ -220,13 +220,14 @@ void DrawFramebufferList(UIContext *ctx, GPUDebugInterface *gpu, const Bounds &b } void DrawControlMapperOverlay(UIContext *ctx, const Bounds &bounds, const ControlMapper &controlMapper) { - DrawControlDebug(ctx, controlMapper, ctx->GetLayoutBounds(false)); + DrawControlDebug(ctx, controlMapper, ctx->GetLayoutBounds(ViewLayoutMode::ApplyInsets, false)); } void DrawDebugOverlay(UIContext *ctx, const Bounds &bounds, DebugOverlay overlay) { bool inGame = GetUIState() == UISTATE_INGAME; - const Bounds layoutBounds = ctx->GetLayoutBounds(false); + const Bounds layoutBounds = ctx->GetLayoutBounds(ViewLayoutMode::ApplyInsets, false); + switch (overlay) { case DebugOverlay::DEBUG_STATS: if (inGame) @@ -256,7 +257,7 @@ void DrawDebugOverlay(UIContext *ctx, const Bounds &bounds, DebugOverlay overlay #endif case DebugOverlay::FRAMEBUFFER_LIST: if (inGame) - DrawFramebufferList(ctx, gpu, bounds); + DrawFramebufferList(ctx, gpu, layoutBounds); break; default: break; diff --git a/UI/DevScreens.h b/UI/DevScreens.h index e5bf262170..f7d72f8f5c 100644 --- a/UI/DevScreens.h +++ b/UI/DevScreens.h @@ -179,6 +179,10 @@ public: const char *tag() const override { return "TouchTest"; } protected: + ViewLayoutMode LayoutMode() const override { + return ViewLayoutMode::ApplyInsets; + } + struct TrackedTouch { int id; float x; diff --git a/UI/DisplayLayoutScreen.cpp b/UI/DisplayLayoutScreen.cpp index 6f9bb09b54..bbd1263832 100644 --- a/UI/DisplayLayoutScreen.cpp +++ b/UI/DisplayLayoutScreen.cpp @@ -282,11 +282,15 @@ void DisplayLayoutScreen::CreateViews() { supportsInsets = true; #endif // Hide insets option if no insets, or OS too old. + float insetLeft = System_GetPropertyFloat(SYSPROP_DISPLAY_SAFE_INSET_LEFT); + float insetRight = System_GetPropertyFloat(SYSPROP_DISPLAY_SAFE_INSET_RIGHT); + float insetTop = System_GetPropertyFloat(SYSPROP_DISPLAY_SAFE_INSET_TOP); + float insetBottom = System_GetPropertyFloat(SYSPROP_DISPLAY_SAFE_INSET_BOTTOM); if (supportsInsets && ( - System_GetPropertyFloat(SYSPROP_DISPLAY_SAFE_INSET_LEFT) != 0.0f || - System_GetPropertyFloat(SYSPROP_DISPLAY_SAFE_INSET_TOP) != 0.0f || - System_GetPropertyFloat(SYSPROP_DISPLAY_SAFE_INSET_RIGHT) != 0.0f || - System_GetPropertyFloat(SYSPROP_DISPLAY_SAFE_INSET_BOTTOM) != 0.0f)) { + insetLeft != 0.0f || + insetTop != 0.0f || + insetRight != 0.0f || + insetBottom != 0.0f) && (insetLeft != insetTop || insetRight != insetBottom)) { rightColumn->Add(new CheckBox(&config.bIgnoreScreenInsets, gr->T("Ignore camera notch when centering"))); } diff --git a/UI/EmuScreen.cpp b/UI/EmuScreen.cpp index a36ef6ad86..067fb70916 100644 --- a/UI/EmuScreen.cpp +++ b/UI/EmuScreen.cpp @@ -1268,6 +1268,7 @@ void EmuScreen::CreateViews() { TouchControlConfig &touch = g_Config.GetTouchControlsConfig(deviceOrientation); const Bounds &bounds = GetLayoutBounds(*screenManager()->getUIContext()); + InitPadLayout(&touch, deviceOrientation, bounds.w, bounds.h); root_ = CreatePadLayout(touch, bounds.w, bounds.h, &pauseTrigger_, &g_controlMapper); @@ -1429,6 +1430,10 @@ void EmuScreen::OnChat(UI::EventParams ¶ms) { // To avoid including proAdhoc.h, which includes a lot of stuff. int GetChatMessageCount(); +ViewLayoutMode EmuScreen::LayoutMode() const { + return ViewLayoutMode::ApplyInsets; +} + void EmuScreen::update() { using namespace UI; @@ -1963,7 +1968,7 @@ void EmuScreen::renderUI() { ctx->Begin(); if (root_) { - UI::LayoutViewHierarchy(*ctx, RootMargins(), root_, false, false); + UI::LayoutViewHierarchy(*ctx, RootMargins(), root_, LayoutMode(), UseImmersiveMode()); root_->Draw(*ctx); } diff --git a/UI/EmuScreen.h b/UI/EmuScreen.h index 6b195ffb40..fd59a9b35c 100644 --- a/UI/EmuScreen.h +++ b/UI/EmuScreen.h @@ -77,6 +77,7 @@ protected: void OnVKeyAnalog(VirtKey virtualKeyCode, float value) override; void UpdatePSPButtons(uint32_t buttonMask, uint32_t changedMask) override; void SetPSPAnalog(int rotation, int stick, float x, float y) override; + ViewLayoutMode LayoutMode() const override; private: void CreateViews() override; diff --git a/UI/GPUDriverTestScreen.cpp b/UI/GPUDriverTestScreen.cpp index 52274bc0c2..04f968d66f 100644 --- a/UI/GPUDriverTestScreen.cpp +++ b/UI/GPUDriverTestScreen.cpp @@ -235,7 +235,6 @@ static_assert(Draw::SEM_TEXCOORD0 == 3, "Semantic shader hardcoded in glsl above GPUDriverTestScreen::GPUDriverTestScreen() { using namespace Draw; - ignoreBottomInset_ = false; } GPUDriverTestScreen::~GPUDriverTestScreen() { diff --git a/UI/GameSettingsScreen.cpp b/UI/GameSettingsScreen.cpp index 81dbecb3bb..a774dbc036 100644 --- a/UI/GameSettingsScreen.cpp +++ b/UI/GameSettingsScreen.cpp @@ -1217,7 +1217,6 @@ void GameSettingsScreen::CreateSystemSettings(UI::ViewGroup *systemSettings) { // Hide Immersive Mode on pre-kitkat Android if (System_GetPropertyInt(SYSPROP_SYSTEMVERSION) >= 19) { DisplayLayoutConfig &config = g_Config.GetDisplayLayoutConfig(GetDeviceOrientation()); - // Let's reuse the Fullscreen translation string from desktop. systemSettings->Add(new CheckBox(&config.bImmersiveMode, sy->T("Hide navigation bar")))->OnClick.Handle(this, &GameSettingsScreen::OnImmersiveModeChange); } #endif diff --git a/UI/MainScreen.cpp b/UI/MainScreen.cpp index e9fed96d57..7ff131dfd8 100644 --- a/UI/MainScreen.cpp +++ b/UI/MainScreen.cpp @@ -1150,7 +1150,6 @@ void GameBrowser::OnHomebrewStore(UI::EventParams &e) { MainScreen::MainScreen() { g_BackgroundAudio.SetGame(Path()); - ignoreBottomInset_ = true; } MainScreen::~MainScreen() { diff --git a/UI/MainScreen.h b/UI/MainScreen.h index f16a221488..14ea47f68a 100644 --- a/UI/MainScreen.h +++ b/UI/MainScreen.h @@ -137,6 +137,8 @@ public: bool key(const KeyInput &touch) override; protected: + ViewLayoutMode LayoutMode() const override { return ViewLayoutMode::IgnoreBottomInset; } + void CreateViews() override; void CreateRecentTab(); GameBrowser *CreateBrowserTab(const Path &path, std::string_view title, std::string_view howToTitle, std::string_view howToUri, BrowseFlags browseFlags, bool *bGridView, float *scrollPos); diff --git a/UI/MiscScreens.cpp b/UI/MiscScreens.cpp index a2d3d95255..1dedbefa2f 100644 --- a/UI/MiscScreens.cpp +++ b/UI/MiscScreens.cpp @@ -506,8 +506,6 @@ std::string_view CreditsScreen::GetTitle() const { void CreditsScreen::CreateDialogViews(UI::ViewGroup *parent) { using namespace UI; - ignoreBottomInset_ = false; - auto di = GetI18NCategory(I18NCat::DIALOG); auto cr = GetI18NCategory(I18NCat::PSPCREDITS); auto mm = GetI18NCategory(I18NCat::MAINMENU); diff --git a/UI/MiscScreens.h b/UI/MiscScreens.h index 407d7b279e..14b8e4a3b1 100644 --- a/UI/MiscScreens.h +++ b/UI/MiscScreens.h @@ -109,6 +109,10 @@ public: const char *tag() const override { return "Logo"; } +protected: + ViewLayoutMode LayoutMode() const override { return ViewLayoutMode::ApplyInsets; } + bool UseImmersiveMode() const override { return true; } + private: void Next(); int frames_ = 0; @@ -123,6 +127,7 @@ public: void update() override; protected: + ViewLayoutMode LayoutMode() const override { return ViewLayoutMode::ApplyInsets; } std::string_view GetTitle() const override; void CreateDialogViews(UI::ViewGroup *parent) override; diff --git a/UI/OnScreenDisplay.cpp b/UI/OnScreenDisplay.cpp index 762e640ca2..1cc1923eca 100644 --- a/UI/OnScreenDisplay.cpp +++ b/UI/OnScreenDisplay.cpp @@ -425,6 +425,11 @@ void OSDOverlayScreen::update() { DoRecreateViews(); } +ViewLayoutMode OSDOverlayScreen::LayoutMode() const { + // Need the full insets to avoid notifications appearing out of any bounds. + return ViewLayoutMode::ApplyInsets; +} + void NoticeView::GetContentDimensionsBySpec(const UIContext &dc, UI::MeasureSpec horiz, UI::MeasureSpec vert, float &w, float &h) const { float layoutWidth = layoutParams_->width; if (layoutWidth < 0) { diff --git a/UI/OnScreenDisplay.h b/UI/OnScreenDisplay.h index 976cbc2188..7ed2f78d91 100644 --- a/UI/OnScreenDisplay.h +++ b/UI/OnScreenDisplay.h @@ -36,9 +36,6 @@ private: class OSDOverlayScreen : public UIScreen { public: - OSDOverlayScreen() { - ignoreInsets_ = false; - } const char *tag() const override { return "OSDOverlayScreen"; } bool UnsyncTouch(const TouchInput &touch) override; @@ -47,6 +44,8 @@ public: void DrawForeground(UIContext &ui) override; void update() override; +protected: + ViewLayoutMode LayoutMode() const override; private: OnScreenMessagesView *osmView_ = nullptr; }; diff --git a/UI/SimpleDialogScreen.cpp b/UI/SimpleDialogScreen.cpp index 39b9d2f0da..9eb1122824 100644 --- a/UI/SimpleDialogScreen.cpp +++ b/UI/SimpleDialogScreen.cpp @@ -4,12 +4,13 @@ #include "Common/UI/PopupScreens.h" #include "UI/MiscViews.h" +ViewLayoutMode UISimpleBaseDialogScreen::LayoutMode() const { + return (flags_ & SimpleDialogFlags::ContentsCanScroll) ? ViewLayoutMode::IgnoreBottomInset : ViewLayoutMode::ApplyInsets; +} + void UISimpleBaseDialogScreen::CreateViews() { using namespace UI; - const bool canScroll = flags_ & SimpleDialogFlags::ContentsCanScroll; - ignoreBottomInset_ = canScroll; - const bool portrait = GetDeviceOrientation() == DeviceOrientation::Portrait; root_ = new LinearLayout(ORIENT_VERTICAL, new LayoutParams(FILL_PARENT, FILL_PARENT)); @@ -29,7 +30,7 @@ void UISimpleBaseDialogScreen::CreateViews() { }); } - if (canScroll) { + if (flags_ & SimpleDialogFlags::ContentsCanScroll) { ScrollView *scroll = new ScrollView(ORIENT_VERTICAL, new LinearLayoutParams(FILL_PARENT, FILL_PARENT, 1.0f)); LinearLayout *contents = new LinearLayoutList(ORIENT_VERTICAL, new LinearLayoutParams(Margins(0, 0, 8, 0))); contents->SetSpacing(0); @@ -41,6 +42,19 @@ void UISimpleBaseDialogScreen::CreateViews() { } } +ViewLayoutMode UITwoPaneBaseDialogScreen::LayoutMode() const { + const bool portrait = GetDeviceOrientation() == DeviceOrientation::Portrait; + if (portrait) { + if (flags_ & TwoPaneFlags::SettingsCanScroll) { + return ViewLayoutMode::IgnoreBottomInset; + } else { + return ViewLayoutMode::ApplyInsets; + } + } else { + return ViewLayoutMode::ApplyInsets; + } +} + void UITwoPaneBaseDialogScreen::CreateViews() { using namespace UI; @@ -72,9 +86,6 @@ void UITwoPaneBaseDialogScreen::CreateViews() { if (portrait) { // Portrait layout is just a vertical stack. - if (flags_ & TwoPaneFlags::SettingsCanScroll) { - ignoreBottomInset_ = true; - } LinearLayout *root = new LinearLayout(ORIENT_VERTICAL, new LayoutParams(FILL_PARENT, FILL_PARENT)); TopBarFlags topBarFlags = TopBarFlags::Portrait; @@ -114,7 +125,6 @@ void UITwoPaneBaseDialogScreen::CreateViews() { } root_ = root; } else { - ignoreBottomInset_ = false; LinearLayout *root = new LinearLayout(ORIENT_VERTICAL, new LayoutParams(FILL_PARENT, FILL_PARENT)); std::string title(GetTitle()); TopBarFlags topBarFlags = portrait ? TopBarFlags::Portrait : TopBarFlags::Default; diff --git a/UI/SimpleDialogScreen.h b/UI/SimpleDialogScreen.h index 404689993d..892c70c918 100644 --- a/UI/SimpleDialogScreen.h +++ b/UI/SimpleDialogScreen.h @@ -26,6 +26,9 @@ public: virtual void CreateContextMenu(UI::ViewGroup *parent) {} // only called if CustomContextMenu is set in flags. virtual std::string_view GetTitle() const { return ""; } +protected: + ViewLayoutMode LayoutMode() const override; + private: void CreateViews() override; SimpleDialogFlags flags_; @@ -61,6 +64,9 @@ public: virtual std::string_view GetTitle() const { return ""; } virtual float SettingsWidth() const { return 350.0f; } +protected: + ViewLayoutMode LayoutMode() const override; + private: void CreateViews() override; TwoPaneFlags flags_ = TwoPaneFlags::Default; diff --git a/UI/TabbedDialogScreen.h b/UI/TabbedDialogScreen.h index 91f57cf741..c908a0961e 100644 --- a/UI/TabbedDialogScreen.h +++ b/UI/TabbedDialogScreen.h @@ -30,9 +30,7 @@ ENUM_CLASS_BITOPS(TabFlags); class UITabbedBaseDialogScreen : public UIBaseDialogScreen { public: - UITabbedBaseDialogScreen(const Path &gamePath, TabDialogFlags flags = TabDialogFlags::Default) : UIBaseDialogScreen(gamePath), flags_(flags) { - ignoreBottomInset_ = true; - } + UITabbedBaseDialogScreen(const Path &gamePath, TabDialogFlags flags = TabDialogFlags::Default) : UIBaseDialogScreen(gamePath), flags_(flags) {} void AddTab(const char *tag, std::string_view title, ImageID imageId, std::function createCallback, TabFlags flags = TabFlags::Default); void AddTab(const char *tag, std::string_view title, std::function createCallback, TabFlags flags = TabFlags::Default) { diff --git a/Windows/main.cpp b/Windows/main.cpp index 01478b0665..c7ad0989c3 100644 --- a/Windows/main.cpp +++ b/Windows/main.cpp @@ -406,11 +406,27 @@ float System_GetPropertyFloat(SystemProperty prop) { return ScreenRefreshRateHz(); case SYSPROP_DISPLAY_DPI: return ScreenDPI(); +#if 0 + // Simulate something like Android landscape mode for testing case SYSPROP_DISPLAY_SAFE_INSET_LEFT: + return 45.0f; case SYSPROP_DISPLAY_SAFE_INSET_RIGHT: + return 100.0f; case SYSPROP_DISPLAY_SAFE_INSET_TOP: + return 0.0f; + case SYSPROP_DISPLAY_SAFE_INSET_BOTTOM: + return 80.0f; +#else + case SYSPROP_DISPLAY_SAFE_INSET_LEFT: + return 0.0f; + case SYSPROP_DISPLAY_SAFE_INSET_RIGHT: + return 0.0f; + case SYSPROP_DISPLAY_SAFE_INSET_TOP: + return 0.0f; case SYSPROP_DISPLAY_SAFE_INSET_BOTTOM: return 0.0f; + +#endif default: return -1; } diff --git a/android/src/org/ppsspp/ppsspp/PpssppActivity.java b/android/src/org/ppsspp/ppsspp/PpssppActivity.java index fb79a283cd..930a9fc182 100644 --- a/android/src/org/ppsspp/ppsspp/PpssppActivity.java +++ b/android/src/org/ppsspp/ppsspp/PpssppActivity.java @@ -919,19 +919,6 @@ public class PpssppActivity extends AppCompatActivity implements SensorEventList int right = insets.right; int top = insets.top; int bottom = insets.bottom; - - // Log.w(TAG, "updateInsets: " + left + ", " + right + ", " + top + ", " + bottom); - - // Hack to make things symmetrical in landscape. Needed on Poco F1, for example. - if (orientation == Configuration.ORIENTATION_LANDSCAPE && useImmersive()) { - if (left > 0 && right > 0) { - int smallestNonZero = Math.min(right, left); - // Log.i(TAG, "Both left and right insets but not equal: " + left + " != " + right + " : Equalizing to " + smallest); - left = smallestNonZero; - right = smallestNonZero; - } - } - NativeApp.sendMessageFromJava("safe_insets", left + ":" + right + ":" + top + ":" + bottom); }