From 967263fc8041659829cd0ef7f357b6dad8b7f518 Mon Sep 17 00:00:00 2001 From: David Griswold Date: Wed, 16 Apr 2025 15:05:46 -0300 Subject: [PATCH] Implement screen gap setting (#622) * implement screen gap * type conversion fix for windows * int setting --- .../features/settings/model/IntSetting.kt | 1 + .../settings/ui/SettingsFragmentPresenter.kt | 12 ++++++ src/android/app/src/main/jni/config.cpp | 2 + src/android/app/src/main/jni/default_ini.h | 6 +++ .../app/src/main/res/values/strings.xml | 2 + src/citra_qt/configuration/config.cpp | 2 + .../configuration/configure_layout.cpp | 2 + .../configuration/configure_layout.ui | 38 ++++++++++++++++++ src/common/settings.cpp | 2 + src/common/settings.h | 1 + src/core/frontend/framebuffer_layout.cpp | 40 ++++++++++--------- 11 files changed, 90 insertions(+), 18 deletions(-) diff --git a/src/android/app/src/main/java/org/citra/citra_emu/features/settings/model/IntSetting.kt b/src/android/app/src/main/java/org/citra/citra_emu/features/settings/model/IntSetting.kt index c3ae61941..8539db6e2 100644 --- a/src/android/app/src/main/java/org/citra/citra_emu/features/settings/model/IntSetting.kt +++ b/src/android/app/src/main/java/org/citra/citra_emu/features/settings/model/IntSetting.kt @@ -33,6 +33,7 @@ enum class IntSetting( LANDSCAPE_BOTTOM_Y("custom_bottom_y",Settings.SECTION_LAYOUT,480), LANDSCAPE_BOTTOM_WIDTH("custom_bottom_width",Settings.SECTION_LAYOUT,640), LANDSCAPE_BOTTOM_HEIGHT("custom_bottom_height",Settings.SECTION_LAYOUT,480), + SCREEN_GAP("screen_gap",Settings.SECTION_LAYOUT,0), PORTRAIT_SCREEN_LAYOUT("portrait_layout_option",Settings.SECTION_LAYOUT,0), PORTRAIT_TOP_X("custom_portrait_top_x",Settings.SECTION_LAYOUT,0), PORTRAIT_TOP_Y("custom_portrait_top_y",Settings.SECTION_LAYOUT,0), diff --git a/src/android/app/src/main/java/org/citra/citra_emu/features/settings/ui/SettingsFragmentPresenter.kt b/src/android/app/src/main/java/org/citra/citra_emu/features/settings/ui/SettingsFragmentPresenter.kt index 267d18f42..5a8442a6c 100644 --- a/src/android/app/src/main/java/org/citra/citra_emu/features/settings/ui/SettingsFragmentPresenter.kt +++ b/src/android/app/src/main/java/org/citra/citra_emu/features/settings/ui/SettingsFragmentPresenter.kt @@ -1092,6 +1092,18 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView) IntSetting.SMALL_SCREEN_POSITION.defaultValue ) ) + add( + SliderSetting( + IntSetting.SCREEN_GAP, + R.string.screen_gap, + R.string.screen_gap_description, + 0, + 480, + "px", + IntSetting.SCREEN_GAP.key, + IntSetting.SCREEN_GAP.defaultValue.toFloat() + ) + ) add( SliderSetting( FloatSetting.LARGE_SCREEN_PROPORTION, diff --git a/src/android/app/src/main/jni/config.cpp b/src/android/app/src/main/jni/config.cpp index 902ba9bb5..a104b541d 100644 --- a/src/android/app/src/main/jni/config.cpp +++ b/src/android/app/src/main/jni/config.cpp @@ -183,11 +183,13 @@ void Config::ReadValues() { layoutInt = static_cast(Settings::LayoutOption::LargeScreen); } Settings::values.layout_option = static_cast(layoutInt); + Settings::values.screen_gap = static_cast(sdl2_config->GetReal("Layout", "screen_gap", 0)); Settings::values.large_screen_proportion = static_cast(sdl2_config->GetReal("Layout", "large_screen_proportion", 2.25)); Settings::values.small_screen_position = static_cast( sdl2_config->GetInteger("Layout", "small_screen_position", static_cast(Settings::SmallScreenPosition::TopRight))); + ReadSetting("Layout", Settings::values.screen_gap); ReadSetting("Layout", Settings::values.custom_top_x); ReadSetting("Layout", Settings::values.custom_top_y); ReadSetting("Layout", Settings::values.custom_top_width); diff --git a/src/android/app/src/main/jni/default_ini.h b/src/android/app/src/main/jni/default_ini.h index bc7f8d894..ce3e00b41 100644 --- a/src/android/app/src/main/jni/default_ini.h +++ b/src/android/app/src/main/jni/default_ini.h @@ -202,6 +202,12 @@ disable_right_eye_render = # 5: Custom Layout layout_option = +# Screen Gap - adds a gap between screens in all two-screen modes +# Measured in pixels relative to the 240px default height of the screens +# Scales with the larger screen (so 24 is 10% of the larger screen height) +# Default value is 0.0 +screen_gap = + # Large Screen Proportion - Relative size of large:small in large screen mode # Default value is 2.25 large_screen_proportion = diff --git a/src/android/app/src/main/res/values/strings.xml b/src/android/app/src/main/res/values/strings.xml index c7ca8d331..23d5829e2 100644 --- a/src/android/app/src/main/res/values/strings.xml +++ b/src/android/app/src/main/res/values/strings.xml @@ -408,6 +408,8 @@ Bottom Left Above Below + Screen Gap + Gap between screens in all two-screen modes. Measured in px relative to the 240px height of the larger screen. Large Screen Proportion How many times larger is the large screen than the small screen in Large Screen layout? Adjust Custom Layout in Settings diff --git a/src/citra_qt/configuration/config.cpp b/src/citra_qt/configuration/config.cpp index 478ca8e02..b70e34248 100644 --- a/src/citra_qt/configuration/config.cpp +++ b/src/citra_qt/configuration/config.cpp @@ -522,6 +522,7 @@ void QtConfig::ReadLayoutValues() { ReadGlobalSetting(Settings::values.swap_screen); ReadGlobalSetting(Settings::values.upright_screen); ReadGlobalSetting(Settings::values.large_screen_proportion); + ReadGlobalSetting(Settings::values.screen_gap); ReadGlobalSetting(Settings::values.small_screen_position); if (global) { @@ -1082,6 +1083,7 @@ void QtConfig::SaveLayoutValues() { WriteGlobalSetting(Settings::values.swap_screen); WriteGlobalSetting(Settings::values.upright_screen); WriteGlobalSetting(Settings::values.large_screen_proportion); + WriteGlobalSetting(Settings::values.screen_gap); WriteGlobalSetting(Settings::values.small_screen_position); if (global) { WriteBasicSetting(Settings::values.mono_render_option); diff --git a/src/citra_qt/configuration/configure_layout.cpp b/src/citra_qt/configuration/configure_layout.cpp index 3f61d6044..58a0e66da 100644 --- a/src/citra_qt/configuration/configure_layout.cpp +++ b/src/citra_qt/configuration/configure_layout.cpp @@ -127,6 +127,7 @@ void ConfigureLayout::SetConfiguration() { ui->toggle_swap_screen->setChecked(Settings::values.swap_screen.GetValue()); ui->toggle_upright_screen->setChecked(Settings::values.upright_screen.GetValue()); + ui->screen_gap->setValue(Settings::values.screen_gap.GetValue()); ui->large_screen_proportion->setValue(Settings::values.large_screen_proportion.GetValue()); ui->small_screen_position_combobox->setCurrentIndex( static_cast(Settings::values.small_screen_position.GetValue())); @@ -166,6 +167,7 @@ void ConfigureLayout::RetranslateUI() { void ConfigureLayout::ApplyConfiguration() { Settings::values.large_screen_proportion = ui->large_screen_proportion->value(); + Settings::values.screen_gap = ui->screen_gap->value(); Settings::values.small_screen_position = static_cast( ui->small_screen_position_combobox->currentIndex()); Settings::values.custom_top_x = ui->custom_top_x->value(); diff --git a/src/citra_qt/configuration/configure_layout.ui b/src/citra_qt/configuration/configure_layout.ui index 3b564515b..33ef4fc68 100644 --- a/src/citra_qt/configuration/configure_layout.ui +++ b/src/citra_qt/configuration/configure_layout.ui @@ -107,6 +107,44 @@ + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Screen Gap + + + + + + + 0.0 + + + 720.0 + + + 0.0 + + + + + + diff --git a/src/common/settings.cpp b/src/common/settings.cpp index c26771018..08af05e88 100644 --- a/src/common/settings.cpp +++ b/src/common/settings.cpp @@ -115,6 +115,7 @@ void LogSettings() { log_setting("Layout_PortraitLayoutOption", values.portrait_layout_option.GetValue()); log_setting("Layout_SwapScreen", values.swap_screen.GetValue()); log_setting("Layout_UprightScreen", values.upright_screen.GetValue()); + log_setting("Layout_ScreenGap", values.screen_gap.GetValue()); log_setting("Layout_LargeScreenProportion", values.large_screen_proportion.GetValue()); log_setting("Layout_SmallScreenPosition", values.small_screen_position.GetValue()); log_setting("Utility_DumpTextures", values.dump_textures.GetValue()); @@ -208,6 +209,7 @@ void RestoreGlobalState(bool is_powered_on) { values.swap_screen.SetGlobal(true); values.upright_screen.SetGlobal(true); values.large_screen_proportion.SetGlobal(true); + values.screen_gap.SetGlobal(true); values.small_screen_position.SetGlobal(true); values.bg_red.SetGlobal(true); values.bg_green.SetGlobal(true); diff --git a/src/common/settings.h b/src/common/settings.h index 51b8d4e96..8d148bbd7 100644 --- a/src/common/settings.h +++ b/src/common/settings.h @@ -510,6 +510,7 @@ struct Values { SwitchableSetting upright_screen{false, "upright_screen"}; SwitchableSetting large_screen_proportion{4.f, 1.f, 16.f, "large_screen_proportion"}; + SwitchableSetting screen_gap{0, "screen_gap"}; SwitchableSetting small_screen_position{SmallScreenPosition::BottomRight, "small_screen_position"}; Setting custom_top_x{0, "custom_top_x"}; diff --git a/src/core/frontend/framebuffer_layout.cpp b/src/core/frontend/framebuffer_layout.cpp index e95e68c91..8b284b30c 100644 --- a/src/core/frontend/framebuffer_layout.cpp +++ b/src/core/frontend/framebuffer_layout.cpp @@ -127,7 +127,7 @@ FramebufferLayout LargeFrameLayout(u32 width, u32 height, bool swapped, bool upr // To do that, find the total emulation box and maximize that based on window size const float window_aspect_ratio = static_cast(height) / width; float emulation_aspect_ratio; - + u32 gap = (u32)(Settings::values.screen_gap.GetValue() * scale_factor); float large_height = swapped ? Core::kScreenBottomHeight * scale_factor : Core::kScreenTopHeight * scale_factor; float small_height = @@ -141,9 +141,9 @@ FramebufferLayout LargeFrameLayout(u32 width, u32 height, bool swapped, bool upr if (vertical) { // width is just the larger size at this point emulation_width = std::max(large_width, small_width); - emulation_height = large_height + small_height; + emulation_height = large_height + small_height + gap; } else { - emulation_width = large_width + small_width; + emulation_width = large_width + small_width + gap; emulation_height = std::max(large_height, small_height); } @@ -168,47 +168,48 @@ FramebufferLayout LargeFrameLayout(u32 width, u32 height, bool swapped, bool upr // shift the large screen so it is at the top position of the bounding rectangle large_screen = large_screen.TranslateY((height - total_rect.GetHeight()) / 2); } + gap = static_cast(static_cast(gap) * scale_amount); switch (small_screen_position) { case Settings::SmallScreenPosition::TopRight: // Shift the small screen to the top right corner - small_screen = small_screen.TranslateX(large_screen.right); + small_screen = small_screen.TranslateX(large_screen.right + gap); small_screen = small_screen.TranslateY(large_screen.top); break; case Settings::SmallScreenPosition::MiddleRight: // Shift the small screen to the center right - small_screen = small_screen.TranslateX(large_screen.right); + small_screen = small_screen.TranslateX(large_screen.right + gap); small_screen = small_screen.TranslateY( ((large_screen.GetHeight() - small_screen.GetHeight()) / 2) + large_screen.top); break; case Settings::SmallScreenPosition::BottomRight: // Shift the small screen to the bottom right corner - small_screen = small_screen.TranslateX(large_screen.right); + small_screen = small_screen.TranslateX(large_screen.right + gap); small_screen = small_screen.TranslateY(large_screen.bottom - small_screen.GetHeight()); break; case Settings::SmallScreenPosition::TopLeft: // shift the small screen to the upper left then shift the large screen to its right small_screen = small_screen.TranslateX(large_screen.left); - large_screen = large_screen.TranslateX(small_screen.GetWidth()); + large_screen = large_screen.TranslateX(small_screen.GetWidth() + gap); small_screen = small_screen.TranslateY(large_screen.top); break; case Settings::SmallScreenPosition::MiddleLeft: // shift the small screen to the middle left and shift the large screen to its right small_screen = small_screen.TranslateX(large_screen.left); - large_screen = large_screen.TranslateX(small_screen.GetWidth()); + large_screen = large_screen.TranslateX(small_screen.GetWidth() + gap); small_screen = small_screen.TranslateY( ((large_screen.GetHeight() - small_screen.GetHeight()) / 2) + large_screen.top); break; case Settings::SmallScreenPosition::BottomLeft: // shift the small screen to the bottom left and shift the large screen to its right small_screen = small_screen.TranslateX(large_screen.left); - large_screen = large_screen.TranslateX(small_screen.GetWidth()); + large_screen = large_screen.TranslateX(small_screen.GetWidth() + gap); small_screen = small_screen.TranslateY(large_screen.bottom - small_screen.GetHeight()); break; case Settings::SmallScreenPosition::AboveLarge: // shift the large screen down and the bottom screen above it small_screen = small_screen.TranslateY(large_screen.top); - large_screen = large_screen.TranslateY(small_screen.GetHeight()); + large_screen = large_screen.TranslateY(small_screen.GetHeight() + gap); // If the "large screen" is actually smaller, center it if (large_screen.GetWidth() < total_rect.GetWidth()) { large_screen = @@ -224,7 +225,7 @@ FramebufferLayout LargeFrameLayout(u32 width, u32 height, bool swapped, bool upr large_screen = large_screen.TranslateX((total_rect.GetWidth() - large_screen.GetWidth()) / 2); } - small_screen = small_screen.TranslateY(large_screen.bottom); + small_screen = small_screen.TranslateY(large_screen.bottom + gap); small_screen = small_screen.TranslateX(large_screen.left + large_screen.GetWidth() / 2 - small_screen.GetWidth() / 2); break; @@ -358,7 +359,8 @@ FramebufferLayout CustomFrameLayout(u32 width, u32 height, bool is_swapped, bool FramebufferLayout FrameLayoutFromResolutionScale(u32 res_scale, bool is_secondary, bool is_portrait) { - u32 width, height; + u32 width, height, gap; + gap = (int)(Settings::values.screen_gap.GetValue()) * res_scale; if (is_portrait) { auto layout_option = Settings::values.portrait_layout_option.GetValue(); switch (layout_option) { @@ -375,8 +377,10 @@ FramebufferLayout FrameLayoutFromResolutionScale(u32 res_scale, bool is_secondar Settings::values.swap_screen.GetValue(), is_portrait); case Settings::PortraitLayoutOption::PortraitTopFullWidth: width = Core::kScreenTopWidth * res_scale; - height = static_cast(Core::kScreenTopHeight + Core::kScreenBottomHeight * 1.25) * - res_scale; + // clang-format off + height = (static_cast(Core::kScreenTopHeight + Core::kScreenBottomHeight * 1.25) * + res_scale) + gap; + // clang-format on return PortraitTopFullFrameLayout(width, height, Settings::values.swap_screen.GetValue()); case Settings::PortraitLayoutOption::PortraitOriginal: @@ -435,9 +439,9 @@ FramebufferLayout FrameLayoutFromResolutionScale(u32 res_scale, bool is_secondar Settings::SmallScreenPosition::BelowLarge) { // vertical, so height is sum of heights, width is larger of widths width = std::max(largeWidth, smallWidth) * res_scale; - height = (largeHeight + smallHeight) * res_scale; + height = (largeHeight + smallHeight) * res_scale + gap; } else { - width = (largeWidth + smallWidth) * res_scale; + width = (largeWidth + smallWidth) * res_scale + gap; height = std::max(largeHeight, smallHeight) * res_scale; } @@ -450,7 +454,7 @@ FramebufferLayout FrameLayoutFromResolutionScale(u32 res_scale, bool is_secondar Settings::values.small_screen_position.GetValue()); } case Settings::LayoutOption::SideScreen: - width = (Core::kScreenTopWidth + Core::kScreenBottomWidth) * res_scale; + width = (Core::kScreenTopWidth + Core::kScreenBottomWidth) * res_scale + gap; height = Core::kScreenTopHeight * res_scale; if (Settings::values.upright_screen.GetValue()) { @@ -481,7 +485,7 @@ FramebufferLayout FrameLayoutFromResolutionScale(u32 res_scale, bool is_secondar case Settings::LayoutOption::Default: default: width = Core::kScreenTopWidth * res_scale; - height = (Core::kScreenTopHeight + Core::kScreenBottomHeight) * res_scale; + height = (Core::kScreenTopHeight + Core::kScreenBottomHeight) * res_scale + gap; if (Settings::values.upright_screen.GetValue()) { std::swap(width, height);