PC: Implement Layout settings tab and support

Co-Authored-By: Reg Tiangha <8913195+rtiangha@users.noreply.github.com>
Co-Authored-By: OpenSauce <48618519+opensauce04@users.noreply.github.com>
This commit is contained in:
BlakDulz 2024-07-26 03:52:34 +02:00 committed by Gamer64ytb
parent 2a6b968fef
commit d865fce783
23 changed files with 1030 additions and 312 deletions

View File

@ -185,14 +185,14 @@ void Config::ReadValues() {
Settings::values.layout_option = static_cast<Settings::LayoutOption>(sdl2_config->GetInteger(
"Layout", "layout_option", static_cast<int>(Settings::LayoutOption::MobileLandscape)));
ReadSetting("Layout", Settings::values.custom_layout);
ReadSetting("Layout", Settings::values.custom_top_left);
ReadSetting("Layout", Settings::values.custom_top_top);
ReadSetting("Layout", Settings::values.custom_top_right);
ReadSetting("Layout", Settings::values.custom_top_bottom);
ReadSetting("Layout", Settings::values.custom_bottom_left);
ReadSetting("Layout", Settings::values.custom_bottom_top);
ReadSetting("Layout", Settings::values.custom_bottom_right);
ReadSetting("Layout", Settings::values.custom_bottom_bottom);
ReadSetting("Layout", Settings::values.custom_top_x);
ReadSetting("Layout", Settings::values.custom_top_y);
ReadSetting("Layout", Settings::values.custom_top_width);
ReadSetting("Layout", Settings::values.custom_top_height);
ReadSetting("Layout", Settings::values.custom_bottom_x);
ReadSetting("Layout", Settings::values.custom_bottom_y);
ReadSetting("Layout", Settings::values.custom_bottom_width);
ReadSetting("Layout", Settings::values.custom_bottom_height);
ReadSetting("Layout", Settings::values.cardboard_screen_size);
ReadSetting("Layout", Settings::values.cardboard_x_shift);
ReadSetting("Layout", Settings::values.cardboard_y_shift);

View File

@ -225,14 +225,14 @@ custom_layout =
# Screen placement when using Custom layout option
# 0x, 0y is the top left corner of the render window.
custom_top_left =
custom_top_top =
custom_top_right =
custom_top_bottom =
custom_bottom_left =
custom_bottom_top =
custom_bottom_right =
custom_bottom_bottom =
custom_top_x =
custom_top_y =
custom_top_width =
custom_top_height =
custom_bottom_x =
custom_bottom_y =
custom_bottom_width =
custom_bottom_height =
# Swaps the prominent screen with the other screen.
# For example, if Single Screen is chosen, setting this to 1 will display the bottom screen instead of the top screen.

View File

@ -174,17 +174,25 @@ void Config::ReadValues() {
ReadSetting("Layout", Settings::values.swap_screen);
ReadSetting("Layout", Settings::values.upright_screen);
ReadSetting("Layout", Settings::values.large_screen_proportion);
ReadSetting("Layout", Settings::values.custom_layout);
ReadSetting("Layout", Settings::values.custom_top_left);
ReadSetting("Layout", Settings::values.custom_top_top);
ReadSetting("Layout", Settings::values.custom_top_right);
ReadSetting("Layout", Settings::values.custom_top_bottom);
ReadSetting("Layout", Settings::values.custom_bottom_left);
ReadSetting("Layout", Settings::values.custom_bottom_top);
ReadSetting("Layout", Settings::values.custom_bottom_right);
ReadSetting("Layout", Settings::values.custom_bottom_bottom);
ReadSetting("Layout", Settings::values.custom_top_x);
ReadSetting("Layout", Settings::values.custom_top_y);
ReadSetting("Layout", Settings::values.custom_top_width);
ReadSetting("Layout", Settings::values.custom_top_height);
ReadSetting("Layout", Settings::values.custom_bottom_x);
ReadSetting("Layout", Settings::values.custom_bottom_y);
ReadSetting("Layout", Settings::values.custom_bottom_width);
ReadSetting("Layout", Settings::values.custom_bottom_height);
ReadSetting("Layout", Settings::values.custom_second_layer_opacity);
ReadSetting("Layout", Settings::values.screen_top_stretch);
ReadSetting("Layout", Settings::values.screen_top_leftright_padding);
ReadSetting("Layout", Settings::values.screen_top_topbottom_padding);
ReadSetting("Layout", Settings::values.screen_bottom_stretch);
ReadSetting("Layout", Settings::values.screen_bottom_leftright_padding);
ReadSetting("Layout", Settings::values.screen_bottom_topbottom_padding);
// Utility
ReadSetting("Utility", Settings::values.dump_textures);
ReadSetting("Utility", Settings::values.custom_textures);

View File

@ -220,6 +220,7 @@ filter_mode =
# 3: Side by Side
# 4: Separate Windows
# 5: Hybrid Screen
# 6: Custom Layout
layout_option =
# Toggle custom layout (using the settings below) on or off.
@ -228,14 +229,14 @@ custom_layout =
# Screen placement when using Custom layout option
# 0x, 0y is the top left corner of the render window.
custom_top_left =
custom_top_top =
custom_top_right =
custom_top_bottom =
custom_bottom_left =
custom_bottom_top =
custom_bottom_right =
custom_bottom_bottom =
custom_top_x =
custom_top_y =
custom_top_width =
custom_top_height =
custom_bottom_x =
custom_bottom_y =
custom_bottom_width =
custom_bottom_height =
# Opacity of second layer when using custom layout option (bottom screen unless swapped)
custom_second_layer_opacity =

View File

@ -48,6 +48,9 @@ add_executable(citra-qt
configuration/configure_enhancements.cpp
configuration/configure_enhancements.h
configuration/configure_enhancements.ui
configuration/configure_layout.cpp
configuration/configure_layout.h
configuration/configure_layout.ui
configuration/configure_dialog.cpp
configuration/configure_dialog.h
configuration/configure_general.cpp

View File

@ -526,16 +526,24 @@ void Config::ReadLayoutValues() {
if (global) {
ReadBasicSetting(Settings::values.mono_render_option);
ReadBasicSetting(Settings::values.custom_layout);
ReadBasicSetting(Settings::values.custom_top_left);
ReadBasicSetting(Settings::values.custom_top_top);
ReadBasicSetting(Settings::values.custom_top_right);
ReadBasicSetting(Settings::values.custom_top_bottom);
ReadBasicSetting(Settings::values.custom_bottom_left);
ReadBasicSetting(Settings::values.custom_bottom_top);
ReadBasicSetting(Settings::values.custom_bottom_right);
ReadBasicSetting(Settings::values.custom_bottom_bottom);
ReadBasicSetting(Settings::values.custom_top_x);
ReadBasicSetting(Settings::values.custom_top_y);
ReadBasicSetting(Settings::values.custom_top_width);
ReadBasicSetting(Settings::values.custom_top_height);
ReadBasicSetting(Settings::values.custom_bottom_x);
ReadBasicSetting(Settings::values.custom_bottom_y);
ReadBasicSetting(Settings::values.custom_bottom_width);
ReadBasicSetting(Settings::values.custom_bottom_height);
ReadBasicSetting(Settings::values.custom_second_layer_opacity);
ReadBasicSetting(Settings::values.screen_top_stretch);
ReadBasicSetting(Settings::values.screen_top_leftright_padding);
ReadBasicSetting(Settings::values.screen_top_topbottom_padding);
ReadBasicSetting(Settings::values.screen_bottom_stretch);
ReadBasicSetting(Settings::values.screen_bottom_leftright_padding);
ReadBasicSetting(Settings::values.screen_bottom_topbottom_padding);
}
qt_config->endGroup();
@ -1080,16 +1088,24 @@ void Config::SaveLayoutValues() {
if (global) {
WriteBasicSetting(Settings::values.mono_render_option);
WriteBasicSetting(Settings::values.custom_layout);
WriteBasicSetting(Settings::values.custom_top_left);
WriteBasicSetting(Settings::values.custom_top_top);
WriteBasicSetting(Settings::values.custom_top_right);
WriteBasicSetting(Settings::values.custom_top_bottom);
WriteBasicSetting(Settings::values.custom_bottom_left);
WriteBasicSetting(Settings::values.custom_bottom_top);
WriteBasicSetting(Settings::values.custom_bottom_right);
WriteBasicSetting(Settings::values.custom_bottom_bottom);
WriteBasicSetting(Settings::values.custom_top_x);
WriteBasicSetting(Settings::values.custom_top_y);
WriteBasicSetting(Settings::values.custom_top_width);
WriteBasicSetting(Settings::values.custom_top_height);
WriteBasicSetting(Settings::values.custom_bottom_x);
WriteBasicSetting(Settings::values.custom_bottom_y);
WriteBasicSetting(Settings::values.custom_bottom_width);
WriteBasicSetting(Settings::values.custom_bottom_height);
WriteBasicSetting(Settings::values.custom_second_layer_opacity);
WriteBasicSetting(Settings::values.screen_top_stretch);
WriteBasicSetting(Settings::values.screen_top_leftright_padding);
WriteBasicSetting(Settings::values.screen_top_topbottom_padding);
WriteBasicSetting(Settings::values.screen_bottom_stretch);
WriteBasicSetting(Settings::values.screen_bottom_leftright_padding);
WriteBasicSetting(Settings::values.screen_bottom_topbottom_padding);
}
qt_config->endGroup();

View File

@ -97,6 +97,12 @@
<header>configuration/configure_enhancements.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>ConfigureLayout</class>
<extends>QWidget</extends>
<header>configuration/configure_layout.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>ConfigureWeb</class>
<extends>QWidget</extends>

View File

@ -13,6 +13,7 @@
#include "citra_qt/configuration/configure_graphics.h"
#include "citra_qt/configuration/configure_hotkeys.h"
#include "citra_qt/configuration/configure_input.h"
#include "citra_qt/configuration/configure_layout.h"
#include "citra_qt/configuration/configure_storage.h"
#include "citra_qt/configuration/configure_system.h"
#include "citra_qt/configuration/configure_ui.h"
@ -35,6 +36,7 @@ ConfigureDialog::ConfigureDialog(QWidget* parent, HotkeyRegistry& registry_, Cor
graphics_tab{
std::make_unique<ConfigureGraphics>(gl_renderer, physical_devices, is_powered_on, this)},
enhancements_tab{std::make_unique<ConfigureEnhancements>(this)},
layout_tab{std::make_unique<ConfigureLayout>(this)},
audio_tab{std::make_unique<ConfigureAudio>(is_powered_on, this)},
camera_tab{std::make_unique<ConfigureCamera>(this)},
debug_tab{std::make_unique<ConfigureDebug>(is_powered_on, this)},
@ -50,6 +52,7 @@ ConfigureDialog::ConfigureDialog(QWidget* parent, HotkeyRegistry& registry_, Cor
ui->tabWidget->addTab(hotkeys_tab.get(), tr("Hotkeys"));
ui->tabWidget->addTab(graphics_tab.get(), tr("Graphics"));
ui->tabWidget->addTab(enhancements_tab.get(), tr("Enhancements"));
ui->tabWidget->addTab(layout_tab.get(), tr("Layout"));
ui->tabWidget->addTab(audio_tab.get(), tr("Audio"));
ui->tabWidget->addTab(camera_tab.get(), tr("Camera"));
ui->tabWidget->addTab(debug_tab.get(), tr("Debug"));
@ -96,6 +99,7 @@ void ConfigureDialog::SetConfiguration() {
input_tab->LoadConfiguration();
graphics_tab->SetConfiguration();
enhancements_tab->SetConfiguration();
layout_tab->SetConfiguration();
audio_tab->SetConfiguration();
camera_tab->SetConfiguration();
debug_tab->SetConfiguration();
@ -112,6 +116,7 @@ void ConfigureDialog::ApplyConfiguration() {
hotkeys_tab->ApplyConfiguration(registry);
graphics_tab->ApplyConfiguration();
enhancements_tab->ApplyConfiguration();
layout_tab->ApplyConfiguration();
audio_tab->ApplyConfiguration();
camera_tab->ApplyConfiguration();
debug_tab->ApplyConfiguration();
@ -130,7 +135,7 @@ void ConfigureDialog::PopulateSelectionList() {
const std::array<std::pair<QString, QList<QWidget*>>, 5> items{
{{tr("General"), {general_tab.get(), web_tab.get(), debug_tab.get(), ui_tab.get()}},
{tr("System"), {system_tab.get(), camera_tab.get(), storage_tab.get()}},
{tr("Graphics"), {enhancements_tab.get(), graphics_tab.get()}},
{tr("Graphics"), {enhancements_tab.get(), layout_tab.get(), graphics_tab.get()}},
{tr("Audio"), {audio_tab.get()}},
{tr("Controls"), {input_tab.get(), hotkeys_tab.get()}}}};
@ -165,6 +170,7 @@ void ConfigureDialog::RetranslateUI() {
hotkeys_tab->RetranslateUI();
graphics_tab->RetranslateUI();
enhancements_tab->RetranslateUI();
layout_tab->RetranslateUI();
audio_tab->RetranslateUI();
camera_tab->RetranslateUI();
debug_tab->RetranslateUI();
@ -183,6 +189,7 @@ void ConfigureDialog::UpdateVisibleTabs() {
{input_tab.get(), tr("Input")},
{hotkeys_tab.get(), tr("Hotkeys")},
{enhancements_tab.get(), tr("Enhancements")},
{layout_tab.get(), tr("Layout")},
{graphics_tab.get(), tr("Advanced")},
{audio_tab.get(), tr("Audio")},
{camera_tab.get(), tr("Camera")},

View File

@ -24,6 +24,7 @@ class ConfigureSystem;
class ConfigureInput;
class ConfigureHotkeys;
class ConfigureGraphics;
class ConfigureLayout;
class ConfigureEnhancements;
class ConfigureAudio;
class ConfigureCamera;
@ -69,6 +70,7 @@ private:
std::unique_ptr<ConfigureHotkeys> hotkeys_tab;
std::unique_ptr<ConfigureGraphics> graphics_tab;
std::unique_ptr<ConfigureEnhancements> enhancements_tab;
std::unique_ptr<ConfigureLayout> layout_tab;
std::unique_ptr<ConfigureAudio> audio_tab;
std::unique_ptr<ConfigureCamera> camera_tab;
std::unique_ptr<ConfigureDebug> debug_tab;

View File

@ -18,8 +18,6 @@ ConfigureEnhancements::ConfigureEnhancements(QWidget* parent)
SetupPerGameUI();
SetConfiguration();
ui->layout_group->setEnabled(!Settings::values.custom_layout);
const auto graphics_api = Settings::values.graphics_api.GetValue();
const bool res_scale_enabled = graphics_api != Settings::GraphicsAPI::Software;
ui->resolution_factor_combobox->setEnabled(res_scale_enabled);
@ -29,18 +27,6 @@ ConfigureEnhancements::ConfigureEnhancements(QWidget* parent)
updateShaders(static_cast<Settings::StereoRenderOption>(currentIndex));
});
connect(ui->bg_button, &QPushButton::clicked, this, [this] {
const QColor new_bg_color = QColorDialog::getColor(bg_color);
if (!new_bg_color.isValid()) {
return;
}
bg_color = new_bg_color;
QPixmap pixmap(ui->bg_button->size());
pixmap.fill(bg_color);
const QIcon color_icon(pixmap);
ui->bg_button->setIcon(color_icon);
});
ui->toggle_preload_textures->setEnabled(ui->toggle_custom_textures->isChecked());
ui->toggle_async_custom_loading->setEnabled(ui->toggle_custom_textures->isChecked());
connect(ui->toggle_custom_textures, &QCheckBox::toggled, this, [this] {
@ -62,13 +48,9 @@ void ConfigureEnhancements::SetConfiguration() {
&Settings::values.texture_filter);
ConfigurationShared::SetHighlight(ui->widget_texture_filter,
!Settings::values.texture_filter.UsingGlobal());
ConfigurationShared::SetPerGameSetting(ui->layout_combobox,
&Settings::values.layout_option);
} else {
ui->resolution_factor_combobox->setCurrentIndex(
Settings::values.resolution_factor.GetValue());
ui->layout_combobox->setCurrentIndex(
static_cast<int>(Settings::values.layout_option.GetValue()));
ui->texture_filter_combobox->setCurrentIndex(
static_cast<int>(Settings::values.texture_filter.GetValue()));
}
@ -80,20 +62,10 @@ void ConfigureEnhancements::SetConfiguration() {
static_cast<int>(Settings::values.mono_render_option.GetValue()));
updateShaders(Settings::values.render_3d.GetValue());
ui->toggle_linear_filter->setChecked(Settings::values.filter_mode.GetValue());
ui->toggle_swap_screen->setChecked(Settings::values.swap_screen.GetValue());
ui->toggle_upright_screen->setChecked(Settings::values.upright_screen.GetValue());
ui->large_screen_proportion->setValue(Settings::values.large_screen_proportion.GetValue());
ui->toggle_dump_textures->setChecked(Settings::values.dump_textures.GetValue());
ui->toggle_custom_textures->setChecked(Settings::values.custom_textures.GetValue());
ui->toggle_preload_textures->setChecked(Settings::values.preload_textures.GetValue());
ui->toggle_async_custom_loading->setChecked(Settings::values.async_custom_loading.GetValue());
bg_color =
QColor::fromRgbF(Settings::values.bg_red.GetValue(), Settings::values.bg_green.GetValue(),
Settings::values.bg_blue.GetValue());
QPixmap pixmap(ui->bg_button->size());
pixmap.fill(bg_color);
const QIcon color_icon(pixmap);
ui->bg_button->setIcon(color_icon);
}
void ConfigureEnhancements::updateShaders(Settings::StereoRenderOption stereo_option) {
@ -148,17 +120,11 @@ void ConfigureEnhancements::ApplyConfiguration() {
Settings::values.pp_shader_name =
ui->shader_combobox->itemText(ui->shader_combobox->currentIndex()).toStdString();
}
Settings::values.large_screen_proportion = ui->large_screen_proportion->value();
ConfigurationShared::ApplyPerGameSetting(&Settings::values.filter_mode,
ui->toggle_linear_filter, linear_filter);
ConfigurationShared::ApplyPerGameSetting(&Settings::values.texture_filter,
ui->texture_filter_combobox);
ConfigurationShared::ApplyPerGameSetting(&Settings::values.layout_option, ui->layout_combobox);
ConfigurationShared::ApplyPerGameSetting(&Settings::values.swap_screen, ui->toggle_swap_screen,
swap_screen);
ConfigurationShared::ApplyPerGameSetting(&Settings::values.upright_screen,
ui->toggle_upright_screen, upright_screen);
ConfigurationShared::ApplyPerGameSetting(&Settings::values.dump_textures,
ui->toggle_dump_textures, dump_textures);
ConfigurationShared::ApplyPerGameSetting(&Settings::values.custom_textures,
@ -167,10 +133,6 @@ void ConfigureEnhancements::ApplyConfiguration() {
ui->toggle_preload_textures, preload_textures);
ConfigurationShared::ApplyPerGameSetting(&Settings::values.async_custom_loading,
ui->toggle_async_custom_loading, async_custom_loading);
Settings::values.bg_red = static_cast<float>(bg_color.redF());
Settings::values.bg_green = static_cast<float>(bg_color.greenF());
Settings::values.bg_blue = static_cast<float>(bg_color.blueF());
}
void ConfigureEnhancements::SetupPerGameUI() {
@ -179,8 +141,6 @@ void ConfigureEnhancements::SetupPerGameUI() {
ui->widget_resolution->setEnabled(Settings::values.resolution_factor.UsingGlobal());
ui->widget_texture_filter->setEnabled(Settings::values.texture_filter.UsingGlobal());
ui->toggle_linear_filter->setEnabled(Settings::values.filter_mode.UsingGlobal());
ui->toggle_swap_screen->setEnabled(Settings::values.swap_screen.UsingGlobal());
ui->toggle_upright_screen->setEnabled(Settings::values.upright_screen.UsingGlobal());
ui->toggle_dump_textures->setEnabled(Settings::values.dump_textures.UsingGlobal());
ui->toggle_custom_textures->setEnabled(Settings::values.custom_textures.UsingGlobal());
ui->toggle_preload_textures->setEnabled(Settings::values.preload_textures.UsingGlobal());
@ -191,14 +151,9 @@ void ConfigureEnhancements::SetupPerGameUI() {
ui->stereo_group->setVisible(false);
ui->widget_shader->setVisible(false);
ui->bg_color_group->setVisible(false);
ConfigurationShared::SetColoredTristate(ui->toggle_linear_filter, Settings::values.filter_mode,
linear_filter);
ConfigurationShared::SetColoredTristate(ui->toggle_swap_screen, Settings::values.swap_screen,
swap_screen);
ConfigurationShared::SetColoredTristate(ui->toggle_upright_screen,
Settings::values.upright_screen, upright_screen);
ConfigurationShared::SetColoredTristate(ui->toggle_dump_textures,
Settings::values.dump_textures, dump_textures);
ConfigurationShared::SetColoredTristate(ui->toggle_custom_textures,
@ -216,8 +171,4 @@ void ConfigureEnhancements::SetupPerGameUI() {
ConfigurationShared::SetColoredComboBox(
ui->texture_filter_combobox, ui->widget_texture_filter,
static_cast<int>(Settings::values.texture_filter.GetValue(true)));
ConfigurationShared::SetColoredComboBox(
ui->layout_combobox, ui->widget_layout,
static_cast<int>(Settings::values.layout_option.GetValue(true)));
}

View File

@ -39,8 +39,6 @@ private:
std::unique_ptr<Ui::ConfigureEnhancements> ui;
ConfigurationShared::CheckState linear_filter;
ConfigurationShared::CheckState swap_screen;
ConfigurationShared::CheckState upright_screen;
ConfigurationShared::CheckState dump_textures;
ConfigurationShared::CheckState custom_textures;
ConfigurationShared::CheckState preload_textures;

View File

@ -7,7 +7,7 @@
<x>0</x>
<y>0</y>
<width>440</width>
<height>748</height>
<height>950</height>
</rect>
</property>
<property name="minimumSize">
@ -19,16 +19,16 @@
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QGroupBox" name="rendererBox">
<property name="title">
<string>Renderer</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_6">
<layout class="QVBoxLayout" name="verticalLayout_3">
<item>
<widget class="QWidget" name="widget_resolution" native="true">
<layout class="QHBoxLayout" name="horizontalLayout">
<layout class="QHBoxLayout" name="horizontalLayout_1">
<property name="leftMargin">
<number>0</number>
</property>
@ -119,7 +119,7 @@
</item>
<item>
<widget class="QWidget" name="widget_shader" native="true">
<layout class="QHBoxLayout" name="horizontalLayout_11">
<layout class="QHBoxLayout" name="horizontalLayout_2">
<property name="leftMargin">
<number>0</number>
</property>
@ -184,6 +184,11 @@
<string>Bicubic</string>
</property>
</item>
<item>
<property name="text">
<string>Nearest Neighbor</string>
</property>
</item>
<item>
<property name="text">
<string>ScaleForce</string>
@ -194,11 +199,6 @@
<string>xBRZ</string>
</property>
</item>
<item>
<property name="text">
<string>MMPX</string>
</property>
</item>
</widget>
</item>
</layout>
@ -214,7 +214,7 @@
</property>
<layout class="QVBoxLayout" name="verticalLayout_4">
<item>
<layout class="QHBoxLayout" name="horizontalLayout_5">
<layout class="QHBoxLayout" name="horizontalLayout_4">
<item>
<widget class="QLabel" name="label_4">
<property name="text">
@ -254,7 +254,7 @@
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_7">
<layout class="QHBoxLayout" name="horizontalLayout_5">
<item>
<widget class="QLabel" name="label_3">
<property name="text">
@ -281,7 +281,7 @@
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_9">
<layout class="QHBoxLayout" name="horizontalLayout_6">
<item>
<widget class="QLabel" name="label_6">
<property name="text">
@ -308,168 +308,13 @@
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="layout_group">
<property name="title">
<string>Layout</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_3">
<item>
<widget class="QWidget" name="widget_layout" native="true">
<layout class="QHBoxLayout" name="screen_layout_group">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QLabel" name="layout_label">
<property name="text">
<string>Screen Layout:</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="layout_combobox">
<item>
<property name="text">
<string>Default</string>
</property>
</item>
<item>
<property name="text">
<string>Single Screen</string>
</property>
</item>
<item>
<property name="text">
<string>Large Screen</string>
</property>
</item>
<item>
<property name="text">
<string>Side by Side</string>
</property>
</item>
<item>
<property name="text">
<string>Separate Windows</string>
</property>
</item>
<item>
<property name="text">
<string>Hybrid Screen</string>
</property>
</item>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QCheckBox" name="toggle_swap_screen">
<property name="text">
<string>Swap Screens</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="toggle_upright_screen">
<property name="text">
<string>Rotate Screens Upright</string>
</property>
</widget>
</item>
<item>
<widget class="QWidget" name="widget" native="true">
<layout class="QHBoxLayout" name="proportion_layout">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QLabel" name="label_31">
<property name="text">
<string>Large Screen Proportion:</string>
</property>
</widget>
</item>
<item>
<widget class="QDoubleSpinBox" name="large_screen_proportion">
<property name="minimum">
<double>1.000000000000000</double>
</property>
<property name="maximum">
<double>16.000000000000000</double>
</property>
<property name="value">
<double>4.000000000000000</double>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QWidget" name="bg_color_group" native="true">
<layout class="QHBoxLayout" name="bg_color_group_2">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QLabel" name="bg_label">
<property name="text">
<string>Background Color:</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="bg_button">
<property name="maximumSize">
<size>
<width>40</width>
<height>16777215</height>
</size>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="utilityBox">
<property name="title">
<string>Utility</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_8">
<item>
<layout class="QGridLayout" name="gridLayout_1">
<item row="1" column="0">
<widget class="QCheckBox" name="toggle_custom_textures">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Replace textures with PNG files.&lt;/p&gt;&lt;p&gt;Textures are loaded from load/textures/[Title ID]/.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
@ -479,7 +324,7 @@
</property>
</widget>
</item>
<item>
<item row="2" column="0">
<widget class="QCheckBox" name="toggle_dump_textures">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Dump textures to PNG files.&lt;/p&gt;&lt;p&gt;Textures are dumped to dump/textures/[Title ID]/.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
@ -489,7 +334,7 @@
</property>
</widget>
</item>
<item>
<item row="1" column="1">
<widget class="QCheckBox" name="toggle_preload_textures">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Load all custom textures into memory on boot, instead of loading them when the game requires them.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
@ -499,7 +344,7 @@
</property>
</widget>
</item>
<item>
<item row="2" column="1">
<widget class="QCheckBox" name="toggle_async_custom_loading">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Load custom textures asynchronously with background threads to reduce loading stutter&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
@ -520,7 +365,7 @@
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>165</height>
<height>270</height>
</size>
</property>
</spacer>
@ -535,11 +380,6 @@
<tabstop>render_3d_combobox</tabstop>
<tabstop>factor_3d</tabstop>
<tabstop>mono_rendering_eye</tabstop>
<tabstop>layout_combobox</tabstop>
<tabstop>toggle_swap_screen</tabstop>
<tabstop>toggle_upright_screen</tabstop>
<tabstop>large_screen_proportion</tabstop>
<tabstop>bg_button</tabstop>
<tabstop>toggle_custom_textures</tabstop>
<tabstop>toggle_dump_textures</tabstop>
<tabstop>toggle_preload_textures</tabstop>

View File

@ -0,0 +1,183 @@
// Copyright 2019 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <QColorDialog>
#include "citra_qt/configuration/configuration_shared.h"
#include "citra_qt/configuration/configure_layout.h"
#include "common/settings.h"
#include "ui_configure_layout.h"
#ifdef ENABLE_OPENGL
#include "video_core/renderer_opengl/post_processing_opengl.h"
#endif
ConfigureLayout::ConfigureLayout(QWidget* parent)
: QWidget(parent), ui(std::make_unique<Ui::ConfigureLayout>()) {
ui->setupUi(this);
SetupPerGameUI();
SetConfiguration();
ui->large_screen_proportion->setEnabled(
(Settings::values.layout_option.GetValue() == Settings::LayoutOption::LargeScreen));
connect(ui->layout_combobox,
static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this,
[this](int currentIndex) {
ui->large_screen_proportion->setEnabled(
currentIndex == (uint)(Settings::LayoutOption::LargeScreen));
});
ui->single_screen_layout_config_group->setEnabled(
(Settings::values.layout_option.GetValue() == Settings::LayoutOption::SingleScreen) ||
(Settings::values.layout_option.GetValue() == Settings::LayoutOption::SeparateWindows));
connect(ui->layout_combobox,
static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this,
[this](int currentIndex) {
ui->single_screen_layout_config_group->setEnabled(
(currentIndex == (uint)(Settings::LayoutOption::SingleScreen)) ||
(currentIndex == (uint)(Settings::LayoutOption::SeparateWindows)));
});
ui->custom_layout_group->setEnabled(
(Settings::values.layout_option.GetValue() == Settings::LayoutOption::CustomLayout));
connect(ui->layout_combobox,
static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this,
[this](int currentIndex) {
ui->custom_layout_group->setEnabled(currentIndex ==
(uint)(Settings::LayoutOption::CustomLayout));
});
ui->screen_top_leftright_padding->setEnabled(Settings::values.screen_top_stretch.GetValue());
connect(ui->screen_top_stretch, static_cast<void (QCheckBox::*)(int)>(&QCheckBox::stateChanged),
this,
[this](bool checkState) { ui->screen_top_leftright_padding->setEnabled(checkState); });
ui->screen_top_topbottom_padding->setEnabled(Settings::values.screen_top_stretch.GetValue());
connect(ui->screen_top_stretch, static_cast<void (QCheckBox::*)(int)>(&QCheckBox::stateChanged),
this,
[this](bool checkState) { ui->screen_top_topbottom_padding->setEnabled(checkState); });
ui->screen_bottom_leftright_padding->setEnabled(
Settings::values.screen_bottom_topbottom_padding.GetValue());
connect(
ui->screen_bottom_stretch, static_cast<void (QCheckBox::*)(int)>(&QCheckBox::stateChanged),
this,
[this](bool checkState) { ui->screen_bottom_leftright_padding->setEnabled(checkState); });
ui->screen_bottom_topbottom_padding->setEnabled(
Settings::values.screen_bottom_topbottom_padding.GetValue());
connect(
ui->screen_bottom_stretch, static_cast<void (QCheckBox::*)(int)>(&QCheckBox::stateChanged),
this,
[this](bool checkState) { ui->screen_bottom_topbottom_padding->setEnabled(checkState); });
connect(ui->bg_button, &QPushButton::clicked, this, [this] {
const QColor new_bg_color = QColorDialog::getColor(bg_color);
if (!new_bg_color.isValid()) {
return;
}
bg_color = new_bg_color;
QPixmap pixmap(ui->bg_button->size());
pixmap.fill(bg_color);
const QIcon color_icon(pixmap);
ui->bg_button->setIcon(color_icon);
});
}
ConfigureLayout::~ConfigureLayout() = default;
void ConfigureLayout::SetConfiguration() {
if (!Settings::IsConfiguringGlobal()) {
ConfigurationShared::SetPerGameSetting(ui->layout_combobox,
&Settings::values.layout_option);
} else {
ui->layout_combobox->setCurrentIndex(
static_cast<int>(Settings::values.layout_option.GetValue()));
}
ui->toggle_swap_screen->setChecked(Settings::values.swap_screen.GetValue());
ui->toggle_upright_screen->setChecked(Settings::values.upright_screen.GetValue());
ui->large_screen_proportion->setValue(Settings::values.large_screen_proportion.GetValue());
ui->custom_top_x->setValue(Settings::values.custom_top_x.GetValue());
ui->custom_top_y->setValue(Settings::values.custom_top_y.GetValue());
ui->custom_top_width->setValue(Settings::values.custom_top_width.GetValue());
ui->custom_top_height->setValue(Settings::values.custom_top_height.GetValue());
ui->custom_bottom_x->setValue(Settings::values.custom_bottom_x.GetValue());
ui->custom_bottom_y->setValue(Settings::values.custom_bottom_y.GetValue());
ui->custom_bottom_width->setValue(Settings::values.custom_bottom_width.GetValue());
ui->custom_bottom_height->setValue(Settings::values.custom_bottom_height.GetValue());
ui->custom_second_layer_opacity->setValue(
Settings::values.custom_second_layer_opacity.GetValue());
ui->screen_top_stretch->setChecked(Settings::values.screen_top_stretch.GetValue());
ui->screen_top_leftright_padding->setValue(
Settings::values.screen_top_leftright_padding.GetValue());
ui->screen_top_topbottom_padding->setValue(
Settings::values.screen_top_topbottom_padding.GetValue());
ui->screen_bottom_stretch->setChecked(Settings::values.screen_bottom_stretch.GetValue());
ui->screen_bottom_leftright_padding->setValue(
Settings::values.screen_bottom_leftright_padding.GetValue());
ui->screen_bottom_topbottom_padding->setValue(
Settings::values.screen_bottom_topbottom_padding.GetValue());
bg_color =
QColor::fromRgbF(Settings::values.bg_red.GetValue(), Settings::values.bg_green.GetValue(),
Settings::values.bg_blue.GetValue());
QPixmap pixmap(ui->bg_button->size());
pixmap.fill(bg_color);
const QIcon color_icon(pixmap);
ui->bg_button->setIcon(color_icon);
}
void ConfigureLayout::RetranslateUI() {
ui->retranslateUi(this);
}
void ConfigureLayout::ApplyConfiguration() {
Settings::values.large_screen_proportion = ui->large_screen_proportion->value();
Settings::values.custom_top_x = ui->custom_top_x->value();
Settings::values.custom_top_y = ui->custom_top_y->value();
Settings::values.custom_top_width = ui->custom_top_width->value();
Settings::values.custom_top_height = ui->custom_top_height->value();
Settings::values.custom_bottom_x = ui->custom_bottom_x->value();
Settings::values.custom_bottom_y = ui->custom_bottom_y->value();
Settings::values.custom_bottom_width = ui->custom_bottom_width->value();
Settings::values.custom_bottom_height = ui->custom_bottom_height->value();
Settings::values.custom_second_layer_opacity = ui->custom_second_layer_opacity->value();
Settings::values.screen_top_stretch = ui->screen_top_stretch->checkState();
Settings::values.screen_top_leftright_padding = ui->screen_top_leftright_padding->value();
Settings::values.screen_top_topbottom_padding = ui->screen_top_topbottom_padding->value();
Settings::values.screen_bottom_stretch = ui->screen_bottom_stretch->checkState();
Settings::values.screen_bottom_leftright_padding = ui->screen_bottom_leftright_padding->value();
Settings::values.screen_bottom_topbottom_padding = ui->screen_bottom_topbottom_padding->value();
ConfigurationShared::ApplyPerGameSetting(&Settings::values.layout_option, ui->layout_combobox);
ConfigurationShared::ApplyPerGameSetting(&Settings::values.swap_screen, ui->toggle_swap_screen,
swap_screen);
ConfigurationShared::ApplyPerGameSetting(&Settings::values.upright_screen,
ui->toggle_upright_screen, upright_screen);
Settings::values.bg_red = static_cast<float>(bg_color.redF());
Settings::values.bg_green = static_cast<float>(bg_color.greenF());
Settings::values.bg_blue = static_cast<float>(bg_color.blueF());
}
void ConfigureLayout::SetupPerGameUI() {
// Block the global settings if a game is currently running that overrides them
if (Settings::IsConfiguringGlobal()) {
ui->toggle_swap_screen->setEnabled(Settings::values.swap_screen.UsingGlobal());
ui->toggle_upright_screen->setEnabled(Settings::values.upright_screen.UsingGlobal());
return;
}
ui->bg_color_group->setVisible(false);
ConfigurationShared::SetColoredTristate(ui->toggle_swap_screen, Settings::values.swap_screen,
swap_screen);
ConfigurationShared::SetColoredTristate(ui->toggle_upright_screen,
Settings::values.upright_screen, upright_screen);
ConfigurationShared::SetColoredComboBox(
ui->layout_combobox, ui->widget_layout,
static_cast<int>(Settings::values.layout_option.GetValue(true)));
}

View File

@ -0,0 +1,44 @@
// Copyright 2019 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <memory>
#include <QWidget>
#include "common/common_types.h"
namespace Settings {
enum class StereoRenderOption : u32;
}
namespace ConfigurationShared {
enum class CheckState;
}
namespace Ui {
class ConfigureLayout;
}
class ConfigureLayout : public QWidget {
Q_OBJECT
public:
explicit ConfigureLayout(QWidget* parent = nullptr);
~ConfigureLayout();
void ApplyConfiguration();
void RetranslateUI();
void SetConfiguration();
void SetupPerGameUI();
private:
void updateShaders(Settings::StereoRenderOption stereo_option);
void updateTextureFilter(int index);
std::unique_ptr<Ui::ConfigureLayout> ui;
ConfigurationShared::CheckState swap_screen;
ConfigurationShared::CheckState upright_screen;
QColor bg_color;
};

View File

@ -0,0 +1,599 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>ConfigureLayout</class>
<widget class="QWidget" name="ConfigureLayout">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>705</width>
<height>950</height>
</rect>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QGroupBox" name="layout_group">
<property name="enabled">
<bool>true</bool>
</property>
<property name="title">
<string>Screens</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_3">
<item>
<widget class="QWidget" name="widget_layout" native="true">
<layout class="QHBoxLayout" name="screen_layout_group">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QLabel" name="layout_label">
<property name="text">
<string>Screen Layout</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="layout_combobox">
<item>
<property name="text">
<string>Default</string>
</property>
</item>
<item>
<property name="text">
<string>Single Screen</string>
</property>
</item>
<item>
<property name="text">
<string>Large Screen</string>
</property>
</item>
<item>
<property name="text">
<string>Side by Side</string>
</property>
</item>
<item>
<property name="text">
<string>Separate Windows</string>
</property>
</item>
<item>
<property name="text">
<string>Hybrid Screen</string>
</property>
</item>
<item>
<property name="text">
<string>Custom Layout</string>
</property>
</item>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QCheckBox" name="toggle_swap_screen">
<property name="text">
<string>Swap Screens</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="toggle_upright_screen">
<property name="text">
<string>Rotate Screens Upright</string>
</property>
</widget>
</item>
<item>
<widget class="QWidget" name="widget" native="true">
<layout class="QHBoxLayout" name="proportion_layout">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QLabel" name="label_3">
<property name="text">
<string>Large Screen Proportion</string>
</property>
</widget>
</item>
<item>
<widget class="QDoubleSpinBox" name="large_screen_proportion">
<property name="minimum">
<double>1.000000000000000</double>
</property>
<property name="maximum">
<double>16.000000000000000</double>
</property>
<property name="value">
<double>4.000000000000000</double>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QWidget" name="bg_color_group" native="true">
<layout class="QHBoxLayout" name="bg_color_group_2">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QLabel" name="bg_label">
<property name="text">
<string>Background Color</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="bg_button">
<property name="maximumSize">
<size>
<width>40</width>
<height>16777215</height>
</size>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="custom_layout_group">
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="title">
<string>Custom Layout</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_5">
<item>
<layout class="QHBoxLayout" name="horizontalLayout_4">
<item>
<widget class="QGroupBox" name="gb_top_screen">
<property name="title">
<string>Top Screen</string>
</property>
<layout class="QGridLayout" name="gridLayout_2">
<item row="0" column="0">
<widget class="QLabel" name="lb_top_x">
<property name="text">
<string>X Position</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QSpinBox" name="custom_top_x">
<property name="buttonSymbols">
<enum>QAbstractSpinBox::NoButtons</enum>
</property>
<property name="suffix">
<string>px</string>
</property>
<property name="maximum">
<number>2147483647</number>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="lb_top_y">
<property name="text">
<string>Y Position</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QSpinBox" name="custom_top_y">
<property name="buttonSymbols">
<enum>QAbstractSpinBox::NoButtons</enum>
</property>
<property name="suffix">
<string>px</string>
</property>
<property name="maximum">
<number>2147483647</number>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="lb_top_width">
<property name="text">
<string>Width</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QSpinBox" name="custom_top_width">
<property name="buttonSymbols">
<enum>QAbstractSpinBox::NoButtons</enum>
</property>
<property name="suffix">
<string>px</string>
</property>
<property name="maximum">
<number>2147483647</number>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="lb_top_height">
<property name="text">
<string>Height</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QSpinBox" name="custom_top_height">
<property name="buttonSymbols">
<enum>QAbstractSpinBox::NoButtons</enum>
</property>
<property name="suffix">
<string>px</string>
</property>
<property name="maximum">
<number>2147483647</number>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="gb_bottom_screen">
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="title">
<string>Bottom Screen</string>
</property>
<layout class="QGridLayout" name="gridLayout_1">
<item row="0" column="0">
<widget class="QLabel" name="lb_bottom_x">
<property name="text">
<string>X Position</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QSpinBox" name="custom_bottom_x">
<property name="buttonSymbols">
<enum>QAbstractSpinBox::NoButtons</enum>
</property>
<property name="suffix">
<string>px</string>
</property>
<property name="maximum">
<number>2147483647</number>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="lb_bottom_y">
<property name="text">
<string>Y Position</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QSpinBox" name="custom_bottom_y">
<property name="buttonSymbols">
<enum>QAbstractSpinBox::NoButtons</enum>
</property>
<property name="suffix">
<string>px</string>
</property>
<property name="maximum">
<number>2147483647</number>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="lb_bottom_width">
<property name="text">
<string>Width</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QSpinBox" name="custom_bottom_width">
<property name="buttonSymbols">
<enum>QAbstractSpinBox::NoButtons</enum>
</property>
<property name="suffix">
<string>px</string>
</property>
<property name="maximum">
<number>2147483647</number>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="lb_bottom_height">
<property name="text">
<string>Height</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QSpinBox" name="custom_bottom_height">
<property name="buttonSymbols">
<enum>QAbstractSpinBox::NoButtons</enum>
</property>
<property name="suffix">
<string>px</string>
</property>
<property name="maximum">
<number>2147483647</number>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<widget class="QLabel" name="lb_opacity_second_layer">
<property name="text">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Bottom Screen Opacity % (OpenGL Only)&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="custom_second_layer_opacity">
<property name="buttonSymbols">
<enum>QAbstractSpinBox::PlusMinus</enum>
</property>
<property name="minimum">
<number>10</number>
</property>
<property name="maximum">
<number>100</number>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="single_screen_layout_config_group">
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="title">
<string>Single Screen Layout</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_6">
<item>
<layout class="QHBoxLayout" name="horizontalLayout_5">
<item>
<widget class="QGroupBox" name="gb_top_screen_2">
<property name="title">
<string>Top Screen</string>
</property>
<layout class="QGridLayout" name="gridLayout_3">
<item row="0" column="0">
<widget class="QLabel" name="lb_top_stretch">
<property name="text">
<string>Stretch</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QSpinBox" name="screen_top_leftright_padding">
<property name="buttonSymbols">
<enum>QAbstractSpinBox::NoButtons</enum>
</property>
<property name="suffix">
<string>px</string>
</property>
<property name="maximum">
<number>2147483647</number>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="lb_top_leftright_padding">
<property name="text">
<string>Left/Right Padding</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="lb_top_topbottom_padding">
<property name="text">
<string>Top/Bottom Padding</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QSpinBox" name="screen_top_topbottom_padding">
<property name="buttonSymbols">
<enum>QAbstractSpinBox::NoButtons</enum>
</property>
<property name="suffix">
<string>px</string>
</property>
<property name="maximum">
<number>2147483647</number>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QCheckBox" name="screen_top_stretch">
<property name="text">
<string notr="true"/>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="gb_bottom_screen_2">
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="title">
<string>Bottom Screen</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="1" column="0">
<widget class="QLabel" name="lb_bottom_leftright_padding">
<property name="text">
<string>Left/Right Padding</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="lb_bottom_topbottom_padding">
<property name="text">
<string>Top/Bottom Padding</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QSpinBox" name="screen_bottom_leftright_padding">
<property name="buttonSymbols">
<enum>QAbstractSpinBox::NoButtons</enum>
</property>
<property name="suffix">
<string>px</string>
</property>
<property name="maximum">
<number>2147483647</number>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QSpinBox" name="screen_bottom_topbottom_padding">
<property name="buttonSymbols">
<enum>QAbstractSpinBox::NoButtons</enum>
</property>
<property name="suffix">
<string>px</string>
</property>
<property name="maximum">
<number>2147483647</number>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="lb_bottom_stretch">
<property name="text">
<string>Stretch</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QCheckBox" name="screen_bottom_stretch">
<property name="text">
<string notr="true"/>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>Note: These settings affect the Single Screen and Separate Windows layouts</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>270</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
<tabstops>
<tabstop>layout_combobox</tabstop>
<tabstop>toggle_swap_screen</tabstop>
<tabstop>toggle_upright_screen</tabstop>
<tabstop>large_screen_proportion</tabstop>
<tabstop>bg_button</tabstop>
</tabstops>
<resources/>
<connections/>
</ui>

View File

@ -14,6 +14,7 @@
#include "citra_qt/configuration/configure_enhancements.h"
#include "citra_qt/configuration/configure_general.h"
#include "citra_qt/configuration/configure_graphics.h"
#include "citra_qt/configuration/configure_layout.h"
#include "citra_qt/configuration/configure_per_game.h"
#include "citra_qt/configuration/configure_system.h"
#include "citra_qt/util/util.h"
@ -37,6 +38,7 @@ ConfigurePerGame::ConfigurePerGame(QWidget* parent, u64 title_id_, const QString
audio_tab = std::make_unique<ConfigureAudio>(is_powered_on, this);
general_tab = std::make_unique<ConfigureGeneral>(this);
enhancements_tab = std::make_unique<ConfigureEnhancements>(this);
layout_tab = std::make_unique<ConfigureLayout>(this);
graphics_tab =
std::make_unique<ConfigureGraphics>(gl_renderer, physical_devices, is_powered_on, this);
system_tab = std::make_unique<ConfigureSystem>(system, this);
@ -48,6 +50,7 @@ ConfigurePerGame::ConfigurePerGame(QWidget* parent, u64 title_id_, const QString
ui->tabWidget->addTab(general_tab.get(), tr("General"));
ui->tabWidget->addTab(system_tab.get(), tr("System"));
ui->tabWidget->addTab(enhancements_tab.get(), tr("Enhancements"));
ui->tabWidget->addTab(layout_tab.get(), tr("Layout"));
ui->tabWidget->addTab(graphics_tab.get(), tr("Graphics"));
ui->tabWidget->addTab(audio_tab.get(), tr("Audio"));
ui->tabWidget->addTab(debug_tab.get(), tr("Debug"));
@ -112,6 +115,7 @@ void ConfigurePerGame::ApplyConfiguration() {
general_tab->ApplyConfiguration();
system_tab->ApplyConfiguration();
enhancements_tab->ApplyConfiguration();
layout_tab->ApplyConfiguration();
graphics_tab->ApplyConfiguration();
audio_tab->ApplyConfiguration();
debug_tab->ApplyConfiguration();

View File

@ -18,6 +18,7 @@ class System;
class ConfigureAudio;
class ConfigureGeneral;
class ConfigureEnhancements;
class ConfigureLayout;
class ConfigureGraphics;
class ConfigureSystem;
class ConfigureDebug;
@ -74,6 +75,7 @@ private:
std::unique_ptr<ConfigureAudio> audio_tab;
std::unique_ptr<ConfigureGeneral> general_tab;
std::unique_ptr<ConfigureEnhancements> enhancements_tab;
std::unique_ptr<ConfigureLayout> layout_tab;
std::unique_ptr<ConfigureGraphics> graphics_tab;
std::unique_ptr<ConfigureSystem> system_tab;
std::unique_ptr<ConfigureDebug> debug_tab;

View File

@ -476,9 +476,10 @@ void GMainWindow::InitializeWidgets() {
actionGroup_ScreenLayouts->addAction(ui->action_Screen_Layout_Default);
actionGroup_ScreenLayouts->addAction(ui->action_Screen_Layout_Single_Screen);
actionGroup_ScreenLayouts->addAction(ui->action_Screen_Layout_Large_Screen);
actionGroup_ScreenLayouts->addAction(ui->action_Screen_Layout_Hybrid_Screen);
actionGroup_ScreenLayouts->addAction(ui->action_Screen_Layout_Side_by_Side);
actionGroup_ScreenLayouts->addAction(ui->action_Screen_Layout_Separate_Windows);
actionGroup_ScreenLayouts->addAction(ui->action_Screen_Layout_Hybrid_Screen);
actionGroup_ScreenLayouts->addAction(ui->action_Screen_Layout_Custom_Layout);
}
void GMainWindow::InitializeDebugWidgets() {
@ -929,6 +930,7 @@ void GMainWindow::ConnectMenuEvents() {
connect_menu(ui->action_Screen_Layout_Hybrid_Screen, &GMainWindow::ChangeScreenLayout);
connect_menu(ui->action_Screen_Layout_Side_by_Side, &GMainWindow::ChangeScreenLayout);
connect_menu(ui->action_Screen_Layout_Separate_Windows, &GMainWindow::ChangeScreenLayout);
connect_menu(ui->action_Screen_Layout_Custom_Layout, &GMainWindow::ChangeScreenLayout);
connect_menu(ui->action_Screen_Layout_Swap_Screens, &GMainWindow::OnSwapScreens);
connect_menu(ui->action_Screen_Layout_Upright_Screens, &GMainWindow::OnRotateScreens);
@ -2418,6 +2420,8 @@ void GMainWindow::ChangeScreenLayout() {
new_layout = Settings::LayoutOption::SideScreen;
} else if (ui->action_Screen_Layout_Separate_Windows->isChecked()) {
new_layout = Settings::LayoutOption::SeparateWindows;
} else if (ui->action_Screen_Layout_Custom_Layout->isChecked()) {
new_layout = Settings::LayoutOption::CustomLayout;
}
Settings::values.layout_option = new_layout;
@ -2439,6 +2443,8 @@ void GMainWindow::ToggleScreenLayout() {
case Settings::LayoutOption::SideScreen:
return Settings::LayoutOption::SeparateWindows;
case Settings::LayoutOption::SeparateWindows:
return Settings::LayoutOption::CustomLayout;
case Settings::LayoutOption::CustomLayout:
return Settings::LayoutOption::Default;
default:
LOG_ERROR(Frontend, "Unknown layout option {}",
@ -3498,6 +3504,8 @@ void GMainWindow::SyncMenuUISettings() {
Settings::LayoutOption::SideScreen);
ui->action_Screen_Layout_Separate_Windows->setChecked(
Settings::values.layout_option.GetValue() == Settings::LayoutOption::SeparateWindows);
ui->action_Screen_Layout_Custom_Layout->setChecked(Settings::values.layout_option.GetValue() ==
Settings::LayoutOption::CustomLayout);
ui->action_Screen_Layout_Swap_Screens->setChecked(Settings::values.swap_screen.GetValue());
ui->action_Screen_Layout_Upright_Screens->setChecked(
Settings::values.upright_screen.GetValue());

View File

@ -135,9 +135,10 @@
<addaction name="action_Screen_Layout_Default"/>
<addaction name="action_Screen_Layout_Single_Screen"/>
<addaction name="action_Screen_Layout_Large_Screen"/>
<addaction name="action_Screen_Layout_Hybrid_Screen"/>
<addaction name="action_Screen_Layout_Side_by_Side"/>
<addaction name="action_Screen_Layout_Separate_Windows"/>
<addaction name="action_Screen_Layout_Hybrid_Screen"/>
<addaction name="action_Screen_Layout_Custom_Layout"/>
<addaction name="separator"/>
<addaction name="action_Screen_Layout_Upright_Screens"/>
<addaction name="action_Screen_Layout_Swap_Screens"/>
@ -520,14 +521,6 @@
<string>Large Screen</string>
</property>
</action>
<action name="action_Screen_Layout_Hybrid_Screen">
<property name="checkable">
<bool>true</bool>
</property>
<property name="text">
<string>Hybrid Screen</string>
</property>
</action>
<action name="action_Screen_Layout_Side_by_Side">
<property name="checkable">
<bool>true</bool>
@ -544,6 +537,22 @@
<string>Separate Windows</string>
</property>
</action>
<action name="action_Screen_Layout_Hybrid_Screen">
<property name="checkable">
<bool>true</bool>
</property>
<property name="text">
<string>Hybrid Screen</string>
</property>
</action>
<action name="action_Screen_Layout_Custom_Layout">
<property name="checkable">
<bool>true</bool>
</property>
<property name="text">
<string>Custom Layout</string>
</property>
</action>
<action name="action_Screen_Layout_Swap_Screens">
<property name="checkable">
<bool>true</bool>

View File

@ -42,6 +42,9 @@ enum class LayoutOption : u32 {
SeparateWindows,
#endif
HybridScreen,
#ifndef ANDROID // TODO: Implement custom layouts on Android
CustomLayout,
#endif
// Similiar to default, but better for mobile devices in portrait mode. Top screen in clamped to
// the top of the frame, and the bottom screen is enlarged to match the top screen.
MobilePortrait,
@ -489,16 +492,23 @@ struct Values {
SwitchableSetting<float, true> large_screen_proportion{4.f, 1.f, 16.f,
"large_screen_proportion"};
Setting<bool> custom_layout{false, "custom_layout"};
Setting<u16> custom_top_left{0, "custom_top_left"};
Setting<u16> custom_top_top{0, "custom_top_top"};
Setting<u16> custom_top_right{400, "custom_top_right"};
Setting<u16> custom_top_bottom{240, "custom_top_bottom"};
Setting<u16> custom_bottom_left{40, "custom_bottom_left"};
Setting<u16> custom_bottom_top{240, "custom_bottom_top"};
Setting<u16> custom_bottom_right{360, "custom_bottom_right"};
Setting<u16> custom_bottom_bottom{480, "custom_bottom_bottom"};
Setting<u16> custom_top_x{0, "custom_top_x"};
Setting<u16> custom_top_y{0, "custom_top_y"};
Setting<u16> custom_top_width{400, "custom_top_width"};
Setting<u16> custom_top_height{240, "custom_top_height"};
Setting<u16> custom_bottom_x{40, "custom_bottom_x"};
Setting<u16> custom_bottom_y{240, "custom_bottom_y"};
Setting<u16> custom_bottom_width{320, "custom_bottom_width"};
Setting<u16> custom_bottom_height{240, "custom_bottom_height"};
Setting<u16> custom_second_layer_opacity{100, "custom_second_layer_opacity"};
SwitchableSetting<bool> screen_top_stretch{false, "screen_top_stretch"};
Setting<u16> screen_top_leftright_padding{0, "screen_top_leftright_padding"};
Setting<u16> screen_top_topbottom_padding{0, "screen_top_topbottom_padding"};
SwitchableSetting<bool> screen_bottom_stretch{false, "screen_bottom_stretch"};
Setting<u16> screen_bottom_leftright_padding{0, "screen_bottom_leftright_padding"};
Setting<u16> screen_bottom_topbottom_padding{0, "screen_bottom_topbottom_padding"};
SwitchableSetting<float> bg_red{0.f, "bg_red"};
SwitchableSetting<float> bg_green{0.f, "bg_green"};
SwitchableSetting<float> bg_blue{0.f, "bg_blue"};

View File

@ -228,6 +228,12 @@ void EmuWindow::UpdateCurrentFramebufferLayout(u32 width, u32 height, bool is_po
Layout::LargeFrameLayout(width, height, Settings::values.swap_screen.GetValue(),
false, 2.25f, Layout::VerticalAlignment::Top);
break;
#ifndef ANDROID // TODO: Implement custom layouts on Android
case Settings::LayoutOption::CustomLayout:
layout =
Layout::CustomFrameLayout(width, height, Settings::values.swap_screen.GetValue());
break;
#endif
case Settings::LayoutOption::Default:
default:
layout =

View File

@ -173,9 +173,20 @@ FramebufferLayout SingleFrameLayout(u32 width, u32 height, bool swapped, bool up
emulation_aspect_ratio = (swapped) ? BOT_SCREEN_ASPECT_RATIO : TOP_SCREEN_ASPECT_RATIO;
}
const bool stretched = (Settings::values.screen_top_stretch.GetValue() && !swapped) ||
(Settings::values.screen_bottom_stretch.GetValue() && swapped);
float window_aspect_ratio = static_cast<float>(height) / width;
if (window_aspect_ratio < emulation_aspect_ratio) {
if (stretched) {
top_screen = {Settings::values.screen_top_leftright_padding.GetValue(),
Settings::values.screen_top_topbottom_padding.GetValue(),
width - Settings::values.screen_top_leftright_padding.GetValue(),
height - Settings::values.screen_top_topbottom_padding.GetValue()};
bot_screen = {Settings::values.screen_bottom_leftright_padding.GetValue(),
Settings::values.screen_bottom_topbottom_padding.GetValue(),
width - Settings::values.screen_bottom_leftright_padding.GetValue(),
height - Settings::values.screen_bottom_topbottom_padding.GetValue()};
} else if (window_aspect_ratio < emulation_aspect_ratio) {
top_screen =
top_screen.TranslateX((screen_window_area.GetWidth() - top_screen.GetWidth()) / 2);
bot_screen =
@ -377,14 +388,18 @@ FramebufferLayout CustomFrameLayout(u32 width, u32 height, bool is_swapped) {
FramebufferLayout res{width, height, true, true, {}, {}, !Settings::values.upright_screen};
Common::Rectangle<u32> top_screen{Settings::values.custom_top_left.GetValue(),
Settings::values.custom_top_top.GetValue(),
Settings::values.custom_top_right.GetValue(),
Settings::values.custom_top_bottom.GetValue()};
Common::Rectangle<u32> bot_screen{Settings::values.custom_bottom_left.GetValue(),
Settings::values.custom_bottom_top.GetValue(),
Settings::values.custom_bottom_right.GetValue(),
Settings::values.custom_bottom_bottom.GetValue()};
Common::Rectangle<u32> top_screen{Settings::values.custom_top_x.GetValue(),
Settings::values.custom_top_y.GetValue(),
(u32)(Settings::values.custom_top_x.GetValue() +
Settings::values.custom_top_width.GetValue()),
(u32)(Settings::values.custom_top_y.GetValue() +
Settings::values.custom_top_height.GetValue())};
Common::Rectangle<u32> bot_screen{Settings::values.custom_bottom_x.GetValue(),
Settings::values.custom_bottom_y.GetValue(),
(u32)(Settings::values.custom_bottom_x.GetValue() +
Settings::values.custom_bottom_width.GetValue()),
(u32)(Settings::values.custom_bottom_y.GetValue() +
Settings::values.custom_bottom_height.GetValue())};
if (is_swapped) {
res.top_screen = bot_screen;
@ -398,10 +413,10 @@ FramebufferLayout CustomFrameLayout(u32 width, u32 height, bool is_swapped) {
FramebufferLayout FrameLayoutFromResolutionScale(u32 res_scale, bool is_secondary) {
if (Settings::values.custom_layout.GetValue() == true) {
return CustomFrameLayout(std::max(Settings::values.custom_top_right.GetValue(),
Settings::values.custom_bottom_right.GetValue()),
std::max(Settings::values.custom_top_bottom.GetValue(),
Settings::values.custom_bottom_bottom.GetValue()),
return CustomFrameLayout(std::max(Settings::values.custom_top_width.GetValue(),
Settings::values.custom_bottom_width.GetValue()),
std::max(Settings::values.custom_top_height.GetValue(),
Settings::values.custom_bottom_height.GetValue()),
Settings::values.swap_screen.GetValue());
}

View File

@ -694,7 +694,9 @@ void RendererOpenGL::DrawScreens(const Layout::FramebufferLayout& layout, bool f
}
void RendererOpenGL::ApplySecondLayerOpacity() {
if (Settings::values.custom_layout &&
#ifndef ANDROID // TODO: Implement custom layouts on Android
if ((Settings::values.layout_option.GetValue() == Settings::LayoutOption::CustomLayout ||
Settings::values.custom_layout) &&
Settings::values.custom_second_layer_opacity.GetValue() < 100) {
state.blend.src_rgb_func = GL_CONSTANT_ALPHA;
state.blend.src_a_func = GL_CONSTANT_ALPHA;
@ -702,10 +704,13 @@ void RendererOpenGL::ApplySecondLayerOpacity() {
state.blend.dst_rgb_func = GL_ONE_MINUS_CONSTANT_ALPHA;
state.blend.color.alpha = Settings::values.custom_second_layer_opacity.GetValue() / 100.0f;
}
#endif
}
void RendererOpenGL::ResetSecondLayerOpacity() {
if (Settings::values.custom_layout &&
#ifndef ANDROID // TODO: Implement custom layouts on Android
if ((Settings::values.layout_option.GetValue() == Settings::LayoutOption::CustomLayout ||
Settings::values.custom_layout) &&
Settings::values.custom_second_layer_opacity.GetValue() < 100) {
state.blend.src_rgb_func = GL_ONE;
state.blend.dst_rgb_func = GL_ZERO;
@ -713,6 +718,7 @@ void RendererOpenGL::ResetSecondLayerOpacity() {
state.blend.dst_a_func = GL_ZERO;
state.blend.color.alpha = 0.0f;
}
#endif
}
void RendererOpenGL::DrawTopScreen(const Layout::FramebufferLayout& layout,