From 85063565b729500aebd44697820bc4a0094ec825 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johel=20Ernesto=20Guerrero=20Pe=C3=B1a?= Date: Sat, 3 Apr 2021 07:43:56 -0400 Subject: [PATCH] feat(qt): add custom screen layout editor --- src/citra/config.cpp | 1 - src/citra_qt/CMakeLists.txt | 6 + src/citra_qt/configuration/config.cpp | 2 - .../configuration/configure_enhancements.cpp | 51 +++- .../configuration/configure_enhancements.h | 2 + .../configuration/configure_enhancements.ui | 12 + src/citra_qt/configuration/custom_screen.cpp | 261 ++++++++++++++++++ src/citra_qt/configuration/custom_screen.h | 57 ++++ src/citra_qt/configuration/custom_screen.ui | 146 ++++++++++ .../custom_screen_layout_editor.cpp | 47 ++++ .../custom_screen_layout_editor.h | 38 +++ .../custom_screen_layout_editor.ui | 81 ++++++ src/citra_qt/main.cpp | 28 +- src/citra_qt/main.ui | 33 ++- src/common/diagnostic.h | 27 ++ src/core/frontend/emu_window.cpp | 47 ++-- src/core/frontend/framebuffer_layout.cpp | 125 +++++---- src/core/settings.h | 2 +- 18 files changed, 842 insertions(+), 124 deletions(-) create mode 100644 src/citra_qt/configuration/custom_screen.cpp create mode 100644 src/citra_qt/configuration/custom_screen.h create mode 100644 src/citra_qt/configuration/custom_screen.ui create mode 100644 src/citra_qt/configuration/custom_screen_layout_editor.cpp create mode 100644 src/citra_qt/configuration/custom_screen_layout_editor.h create mode 100644 src/citra_qt/configuration/custom_screen_layout_editor.ui create mode 100644 src/common/diagnostic.h diff --git a/src/citra/config.cpp b/src/citra/config.cpp index 88a36ed66..8e7a22f5b 100644 --- a/src/citra/config.cpp +++ b/src/citra/config.cpp @@ -160,7 +160,6 @@ void Config::ReadValues() { static_cast(sdl2_config->GetInteger("Layout", "layout_option", 0)); Settings::values.swap_screen = sdl2_config->GetBoolean("Layout", "swap_screen", false); Settings::values.upright_screen = sdl2_config->GetBoolean("Layout", "upright_screen", false); - Settings::values.custom_layout = sdl2_config->GetBoolean("Layout", "custom_layout", false); Settings::values.custom_top_left = static_cast(sdl2_config->GetInteger("Layout", "custom_top_left", 0)); Settings::values.custom_top_top = diff --git a/src/citra_qt/CMakeLists.txt b/src/citra_qt/CMakeLists.txt index 24c2ca601..f3bb5b2c1 100644 --- a/src/citra_qt/CMakeLists.txt +++ b/src/citra_qt/CMakeLists.txt @@ -79,6 +79,12 @@ add_executable(citra-qt configuration/configure_web.cpp configuration/configure_web.h configuration/configure_web.ui + configuration/custom_screen.cpp + configuration/custom_screen.h + configuration/custom_screen.ui + configuration/custom_screen_layout_editor.cpp + configuration/custom_screen_layout_editor.h + configuration/custom_screen_layout_editor.ui debugger/console.h debugger/console.cpp debugger/graphics/graphics.cpp diff --git a/src/citra_qt/configuration/config.cpp b/src/citra_qt/configuration/config.cpp index 8901c47c2..f9829a50e 100644 --- a/src/citra_qt/configuration/config.cpp +++ b/src/citra_qt/configuration/config.cpp @@ -343,7 +343,6 @@ void Config::ReadLayoutValues() { static_cast(ReadSetting(QStringLiteral("layout_option")).toInt()); Settings::values.swap_screen = ReadSetting(QStringLiteral("swap_screen"), false).toBool(); Settings::values.upright_screen = ReadSetting(QStringLiteral("upright_screen"), false).toBool(); - Settings::values.custom_layout = ReadSetting(QStringLiteral("custom_layout"), false).toBool(); Settings::values.custom_top_left = ReadSetting(QStringLiteral("custom_top_left"), 0).toInt(); Settings::values.custom_top_top = ReadSetting(QStringLiteral("custom_top_top"), 0).toInt(); Settings::values.custom_top_right = @@ -887,7 +886,6 @@ void Config::SaveLayoutValues() { WriteSetting(QStringLiteral("layout_option"), static_cast(Settings::values.layout_option)); WriteSetting(QStringLiteral("swap_screen"), Settings::values.swap_screen, false); WriteSetting(QStringLiteral("upright_screen"), Settings::values.upright_screen, false); - WriteSetting(QStringLiteral("custom_layout"), Settings::values.custom_layout, false); WriteSetting(QStringLiteral("custom_top_left"), Settings::values.custom_top_left, 0); WriteSetting(QStringLiteral("custom_top_top"), Settings::values.custom_top_top, 0); WriteSetting(QStringLiteral("custom_top_right"), Settings::values.custom_top_right, 400); diff --git a/src/citra_qt/configuration/configure_enhancements.cpp b/src/citra_qt/configuration/configure_enhancements.cpp index d93155847..585af8cd0 100644 --- a/src/citra_qt/configuration/configure_enhancements.cpp +++ b/src/citra_qt/configuration/configure_enhancements.cpp @@ -2,16 +2,41 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include #include +#include +#include #include "citra_qt/configuration/configure_enhancements.h" +#include "core/3ds.h" #include "core/core.h" #include "core/settings.h" #include "ui_configure_enhancements.h" #include "video_core/renderer_opengl/post_processing_opengl.h" #include "video_core/renderer_opengl/texture_filters/texture_filterer.h" +namespace { + +const QString g_top_screen_name{QObject::tr("Top")}; +const QString g_bottom_screen_name{QObject::tr("Bottom")}; + +void AddScreens(CustomScreenLayoutEditor& layout_editor) { + layout_editor.addScreen( + g_top_screen_name, {0, 0, Core::kScreenTopWidth, Core::kScreenTopHeight}, + {QPoint{Settings::values.custom_top_left, Settings::values.custom_top_top}, + QPoint{Settings::values.custom_top_right - 1, Settings::values.custom_top_bottom - 1}}); + layout_editor.addScreen( + g_bottom_screen_name, + {40, Core::kScreenTopHeight, Core::kScreenBottomWidth, Core::kScreenBottomHeight}, + {QPoint{Settings::values.custom_bottom_left, Settings::values.custom_bottom_top}, + QPoint{Settings::values.custom_bottom_right - 1, + Settings::values.custom_bottom_bottom - 1}}); +} + +} // namespace + ConfigureEnhancements::ConfigureEnhancements(QWidget* parent) - : QWidget(parent), ui(std::make_unique()) { + : QWidget(parent), ui(std::make_unique()), + layout_editor(std::make_unique(this)) { ui->setupUi(this); for (const auto& filter : OpenGL::TextureFilterer::GetFilterNames()) @@ -19,16 +44,19 @@ ConfigureEnhancements::ConfigureEnhancements(QWidget* parent) SetConfiguration(); - ui->layoutBox->setEnabled(!Settings::values.custom_layout); - ui->resolution_factor_combobox->setEnabled(Settings::values.use_hw_renderer); + AddScreens(*layout_editor); + connect(ui->render_3d_combobox, static_cast(&QComboBox::currentIndexChanged), this, [this](int currentIndex) { updateShaders(static_cast(currentIndex)); }); + connect(ui->editor_button, &QPushButton::clicked, this, + [this] { layout_editor->showMaximized(); }); + connect(ui->bg_button, &QPushButton::clicked, this, [this] { const QColor new_bg_color = QColorDialog::getColor(bg_color); if (!new_bg_color.isValid()) { @@ -113,6 +141,23 @@ void ConfigureEnhancements::ApplyConfiguration() { Settings::values.texture_filter_name = ui->texture_filter_combobox->currentText().toStdString(); Settings::values.layout_option = static_cast(ui->layout_combobox->currentIndex()); + { + const auto geometry = [this](const QString& screen_name) { + const auto* const screen{layout_editor->getScreen(screen_name)}; + assert(screen); + return screen->geometry(); + }; + const QRect top_screen{geometry(g_top_screen_name)}; + Settings::values.custom_top_left = top_screen.left(); + Settings::values.custom_top_top = top_screen.top(); + Settings::values.custom_top_right = top_screen.right() + 1; + Settings::values.custom_top_bottom = top_screen.bottom() + 1; + const QRect bottom_screen{geometry(g_bottom_screen_name)}; + Settings::values.custom_bottom_left = bottom_screen.left(); + Settings::values.custom_bottom_top = bottom_screen.top(); + Settings::values.custom_bottom_right = bottom_screen.right() + 1; + Settings::values.custom_bottom_bottom = bottom_screen.bottom() + 1; + } Settings::values.swap_screen = ui->swap_screen->isChecked(); Settings::values.upright_screen = ui->upright_screen->isChecked(); Settings::values.dump_textures = ui->toggle_dump_textures->isChecked(); diff --git a/src/citra_qt/configuration/configure_enhancements.h b/src/citra_qt/configuration/configure_enhancements.h index 3541ccf3d..5658f3468 100644 --- a/src/citra_qt/configuration/configure_enhancements.h +++ b/src/citra_qt/configuration/configure_enhancements.h @@ -6,6 +6,7 @@ #include #include +#include "citra_qt/configuration/custom_screen_layout_editor.h" namespace Settings { enum class StereoRenderOption; @@ -31,5 +32,6 @@ private: void updateTextureFilter(int index); std::unique_ptr ui; + std::unique_ptr layout_editor; QColor bg_color; }; diff --git a/src/citra_qt/configuration/configure_enhancements.ui b/src/citra_qt/configuration/configure_enhancements.ui index 99a2fdb7e..857af43a1 100644 --- a/src/citra_qt/configuration/configure_enhancements.ui +++ b/src/citra_qt/configuration/configure_enhancements.ui @@ -216,6 +216,13 @@ Layout + + + + Edit Custom Screen Layout + + + @@ -247,6 +254,11 @@ Side by Side + + + Custom + + diff --git a/src/citra_qt/configuration/custom_screen.cpp b/src/citra_qt/configuration/custom_screen.cpp new file mode 100644 index 000000000..7c73c23db --- /dev/null +++ b/src/citra_qt/configuration/custom_screen.cpp @@ -0,0 +1,261 @@ +// Copyright 2019 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "citra_qt/configuration/custom_screen.h" +#include "common/diagnostic.h" +#include "ui_custom_screen.h" + +CITRA_DIAGNOSTIC_IGNORE_SWITCH + +constexpr unsigned to_underlying(RectangleSides s) { + return static_cast(s); +} + +constexpr RectangleSides operator|(RectangleSides l, RectangleSides r) { + return RectangleSides{to_underlying(l) | to_underlying(r)}; +} + +constexpr RectangleSides operator&(RectangleSides l, RectangleSides r) { + return RectangleSides{to_underlying(l) & to_underlying(r)}; +} + +CustomScreen::CustomScreen(QWidget* parent) + : QFrame(parent), ui(std::make_unique()) { + ui->setupUi(this); + + const auto value_changed{QOverload::of(&QSpinBox::valueChanged)}; + + connect(ui->left, value_changed, this, [this](int x) { move(x, y()); }); + connect(ui->top, value_changed, this, [this](int y) { move(x(), y); }); + connect(ui->width, value_changed, this, [this](int width) { + resize(width, height()); + ui->width->setValue(this->width()); + }); + connect(ui->height, value_changed, this, [this](int height) { + resize(width(), height); + ui->height->setValue(this->height()); + }); +} + +QString CustomScreen::name() const { + return ui->name->text(); +} + +void CustomScreen::setName(const QString& name) { + ui->name->setText(name); +} + +namespace { + +bool IsClamped(int v, int lo, int hi) { + return lo <= v && v < hi; +} + +} // namespace + +auto CustomScreen::SidesAt(QPoint pt) -> RectangleSides { + return RectangleSides{ + (IsClamped(pt.x(), 0, lineWidth()) ? RectangleSides::left : RectangleSides{}) | + (IsClamped(pt.y(), 0, lineWidth()) ? RectangleSides::top : RectangleSides{}) | + (IsClamped(pt.x(), width() - lineWidth(), width()) ? RectangleSides::right + : RectangleSides{}) | + (IsClamped(pt.y(), height() - lineWidth(), height()) ? RectangleSides::bottom + : RectangleSides{})}; +} + +void CustomScreen::mousePressEvent(QMouseEvent* event) { + switch (event->buttons()) { + case Qt::MouseButton::LeftButton: + drag = {SidesAt(event->pos()), geometry(), event->globalPos()}; + if (drag.sides == RectangleSides{}) + setCursor(Qt::CursorShape::ClosedHandCursor); + return event->accept(); + default: + return event->ignore(); + } +} + +void CustomScreen::mouseMoveEvent(QMouseEvent* event) { + switch (event->buttons()) { + case Qt::MouseButton::NoButton: + switch (SidesAt(event->pos())) { + case RectangleSides::left: + case RectangleSides::right: + setCursor(Qt::CursorShape::SizeHorCursor); + break; + case RectangleSides::top: + case RectangleSides::bottom: + setCursor(Qt::CursorShape::SizeVerCursor); + break; + case RectangleSides::top | RectangleSides::left: + case RectangleSides::bottom | RectangleSides::right: + setCursor(Qt::CursorShape::SizeFDiagCursor); + break; + case RectangleSides::top | RectangleSides::right: + case RectangleSides::bottom | RectangleSides::left: + setCursor(Qt::CursorShape::SizeBDiagCursor); + break; + default: + setCursor(Qt::CursorShape::OpenHandCursor); + break; + } + break; + case Qt::MouseButton::LeftButton: + drag(*this, event->globalPos()); + break; + default: + return event->ignore(); + } + + event->accept(); +} + +namespace { + +bool HasSingleBit(unsigned x) { + return x != 0 && (x & (x - 1)) == 0; +} + +unsigned BitWidth(unsigned x) { + return x == 0 ? 0 : 1 + static_cast(std::log2(x)); +} + +struct RectangleSideOffsets { + int sides[4]{}; + + int& operator[](RectangleSides s) { + assert(HasSingleBit(to_underlying(s))); + return sides[BitWidth(to_underlying(s)) - 1]; + } + + int& left() { + return operator[](RectangleSides::left); + } + int& top() { + return operator[](RectangleSides::top); + } + int& right() { + return operator[](RectangleSides::right); + } + int& bottom() { + return operator[](RectangleSides::bottom); + } +}; + +RectangleSides prev(RectangleSides side) { + assert(HasSingleBit(to_underlying(side))); + return side == RectangleSides::left ? RectangleSides::bottom + : RectangleSides{to_underlying(side) >> 1}; +} + +RectangleSides next(RectangleSides side) { + assert(HasSingleBit(to_underlying(side))); + return side == RectangleSides::bottom ? RectangleSides::left + : RectangleSides{to_underlying(side) << 1}; +} + +bool HaveEqualOutwardsDirectionSign(RectangleSides l, RectangleSides r) { + assert(l != r); + return (l | r) == (RectangleSides::top | RectangleSides::left) || + (l | r) == (RectangleSides::bottom | RectangleSides::right); +} + +void GrowAdjacents(double aspect_ratio, RectangleSideOffsets& offsets, RectangleSides side) { + assert(HasSingleBit(to_underlying(side))); + const int adjacent_growth{static_cast(aspect_ratio * offsets[side])}; + RectangleSides adjacent_sides[2]{prev(side), next(side)}; + if (!HaveEqualOutwardsDirectionSign(side, adjacent_sides[0])) + std::swap(adjacent_sides[0], adjacent_sides[1]); + offsets[adjacent_sides[0]] = adjacent_growth / 2; + offsets[adjacent_sides[1]] = -adjacent_growth / 2; +} + +int FurthestOffset(RectangleSides side, int l, int r) { + assert(HasSingleBit(to_underlying(side))); + return (side & (RectangleSides::top | RectangleSides::left)) != RectangleSides{} + ? std::min(l, r) + : std::max(l, r); +} + +constexpr RectangleSides vertical_sides{RectangleSides::left | RectangleSides::right}; +constexpr RectangleSides horizontal_sides{RectangleSides::top | RectangleSides::bottom}; + +[[maybe_unused]] bool IsCorner(RectangleSides sides) { + return HasSingleBit(to_underlying(sides & vertical_sides)) && + HasSingleBit(to_underlying(sides & horizontal_sides)); +} + +void GrowCorner(double aspect_ratio, RectangleSideOffsets& offsets, RectangleSides corner) { + assert(IsCorner(corner)); + const RectangleSides vertical_side{corner & vertical_sides}; + const RectangleSides horizontal_side{corner & horizontal_sides}; + int& vertical_offset{offsets[vertical_side]}; + int& horizontal_offset{offsets[horizontal_side]}; + const int adjust{HaveEqualOutwardsDirectionSign(vertical_side, horizontal_side) ? 1 : -1}; + const int grown_vertical_offset{static_cast(horizontal_offset * aspect_ratio) * adjust}; + const int grown_horizontal_offset{static_cast(vertical_offset / aspect_ratio) * adjust}; + vertical_offset = FurthestOffset(vertical_side, vertical_offset, grown_vertical_offset); + horizontal_offset = FurthestOffset(horizontal_side, horizontal_offset, grown_horizontal_offset); +} + +} // namespace + +void CustomScreen::Drag::operator()(CustomScreen& screen, QPoint move_position) const { + const auto drag_offset{move_position - press_position}; + + if (sides == RectangleSides{}) + return screen.move(original_screen_geometry.topLeft() + drag_offset); + + RectangleSideOffsets offsets{ + (sides & RectangleSides::left) != RectangleSides{} ? drag_offset.x() : 0, + (sides & RectangleSides::top) != RectangleSides{} ? drag_offset.y() : 0, + (sides & RectangleSides::right) != RectangleSides{} ? drag_offset.x() : 0, + (sides & RectangleSides::bottom) != RectangleSides{} ? drag_offset.y() : 0}; + + if (screen.ui->keep_aspect_ratio->isChecked()) { + const double aspect_ratio{original_screen_geometry.width() * 1.0 / + original_screen_geometry.height()}; + (HasSingleBit(to_underlying(sides)) ? GrowAdjacents : GrowCorner)(aspect_ratio, offsets, + sides); + } + + screen.move(std::min(original_screen_geometry.x() + offsets.left(), + original_screen_geometry.right() - screen.minimumWidth() + 1), + std::min(original_screen_geometry.y() + offsets.top(), + original_screen_geometry.bottom() - screen.minimumHeight() + 1)); + screen.resize(original_screen_geometry.width() + offsets.right() - offsets.left(), + original_screen_geometry.height() + offsets.bottom() - offsets.top()); +} + +void CustomScreen::moveEvent(QMoveEvent*) { + move(std::max(0, x()), std::max(0, y())); + ui->left->setValue(x()); + ui->top->setValue(y()); +} + +void CustomScreen::resizeEvent(QResizeEvent*) { + moveEvent(nullptr); + ui->width->setValue(width()); + ui->height->setValue(height()); + +#if !defined(NDEBUG) + const int num{width()}; + const int den{height()}; + const int gcd{std::gcd(num, den)}; + ui->keep_aspect_ratio->setText( + tr("Keep aspect ratio (%1, %2:%3)").arg(1.0 * num / den).arg(num / gcd).arg(den / gcd)); +#endif +} + +CustomScreen::~CustomScreen() {} diff --git a/src/citra_qt/configuration/custom_screen.h b/src/citra_qt/configuration/custom_screen.h new file mode 100644 index 000000000..d930b7448 --- /dev/null +++ b/src/citra_qt/configuration/custom_screen.h @@ -0,0 +1,57 @@ +// Copyright 2019 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include +#include +#include +#include +#include + +enum class RectangleSides : unsigned { + left = (1 << 0), + top = (1 << 1), + right = (1 << 2), + bottom = (1 << 3) +}; + +namespace Ui { +class CustomScreen; +} + +class CustomScreen : public QFrame { + Q_OBJECT + +public: + explicit CustomScreen(QWidget* parent = nullptr); + ~CustomScreen(); + + QString name() const; + + void setName(const QString&); + +protected: + void mousePressEvent(QMouseEvent*) override; + void mouseMoveEvent(QMouseEvent*) override; + + void moveEvent(QMoveEvent*) override; + void resizeEvent(QResizeEvent*) override; + +private: + RectangleSides SidesAt(QPoint); + + struct Drag { + RectangleSides sides; + QRect original_screen_geometry; + QPoint press_position; + + void operator()(CustomScreen& screen, QPoint move_position) const; + }; + + friend Drag; + + std::unique_ptr ui; + Drag drag; +}; diff --git a/src/citra_qt/configuration/custom_screen.ui b/src/citra_qt/configuration/custom_screen.ui new file mode 100644 index 000000000..82caf0976 --- /dev/null +++ b/src/citra_qt/configuration/custom_screen.ui @@ -0,0 +1,146 @@ + + + CustomScreen + + + + 0 + 0 + 400 + 300 + + + + true + + + Form + + + QFrame::StyledPanel + + + 5 + + + + QLayout::SetMinimumSize + + + + + + 0 + 0 + + + + TextLabel + + + + + + + Keep aspect ratio + + + true + + + + + + + + 0 + 0 + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + + + + Left + + + + + + + 16777215 + + + + + + + + + + + Top + + + + + + + 16777215 + + + + + + + + + + + Width + + + + + + + 16777215 + + + + + + + + + + + Height + + + + + + + 16777215 + + + + + + + + + + + + + diff --git a/src/citra_qt/configuration/custom_screen_layout_editor.cpp b/src/citra_qt/configuration/custom_screen_layout_editor.cpp new file mode 100644 index 000000000..590162c68 --- /dev/null +++ b/src/citra_qt/configuration/custom_screen_layout_editor.cpp @@ -0,0 +1,47 @@ +// Copyright 2019 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include +#include +#include +#include +#include +#include "citra_qt/configuration/custom_screen_layout_editor.h" +#include "ui_custom_screen_layout_editor.h" + +CustomScreenLayoutEditor::CustomScreenLayoutEditor(QWidget* parent) + : QDialog(parent), ui(std::make_unique()) { + ui->setupUi(this); + + auto* const framebuffer = new QMainWindow; + ui->scroll_area->setWidget(framebuffer); + + const auto set = [this](QRect RestorableScreen::*geometry) { + return [this, geometry] { + for (auto& s : screens) + s.screen->setGeometry(s.*geometry); + }; + }; + + connect(ui->buttonBox->button(QDialogButtonBox::Reset), &QPushButton::clicked, this, + set(&RestorableScreen::reset_geometry)); + connect(ui->buttonBox->button(QDialogButtonBox::RestoreDefaults), &QPushButton::clicked, this, + set(&RestorableScreen::default_geometry)); +} + +void CustomScreenLayoutEditor::addScreen(QString name, QRect default_geometry, + QRect current_geometry) { + auto s{std::make_unique(ui->scroll_area->widget())}; + s->setGeometry(current_geometry); + s->setName(name); + screens.emplace_back(RestorableScreen{std::move(s), default_geometry}); +} + +CustomScreen* CustomScreenLayoutEditor::getScreen(QString name) const { + auto s{std::find_if(screens.begin(), screens.end(), + [&](auto& s) { return s.screen->name() == name; })}; + return s == screens.end() ? nullptr : s->screen.get(); +} + +CustomScreenLayoutEditor::~CustomScreenLayoutEditor() {} diff --git a/src/citra_qt/configuration/custom_screen_layout_editor.h b/src/citra_qt/configuration/custom_screen_layout_editor.h new file mode 100644 index 000000000..23bcc6f70 --- /dev/null +++ b/src/citra_qt/configuration/custom_screen_layout_editor.h @@ -0,0 +1,38 @@ +// Copyright 2019 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include +#include +#include +#include +#include +#include "citra_qt/configuration/custom_screen.h" + +namespace Ui { +class CustomScreenLayoutEditor; +} + +class CustomScreenLayoutEditor : public QDialog { + Q_OBJECT + +public: + explicit CustomScreenLayoutEditor(QWidget* parent = nullptr); + ~CustomScreenLayoutEditor(); + + void addScreen(QString name, QRect default_geometry, QRect current_geometry); + + CustomScreen* getScreen(QString name) const; + +private: + struct RestorableScreen { + std::unique_ptr screen; + QRect default_geometry; + QRect reset_geometry{screen->geometry()}; + }; + + std::unique_ptr ui; + std::vector screens; +}; diff --git a/src/citra_qt/configuration/custom_screen_layout_editor.ui b/src/citra_qt/configuration/custom_screen_layout_editor.ui new file mode 100644 index 000000000..b295dc230 --- /dev/null +++ b/src/citra_qt/configuration/custom_screen_layout_editor.ui @@ -0,0 +1,81 @@ + + + CustomScreenLayoutEditor + + + + 0 + 0 + 490 + 300 + + + + Custom Screen Layout Editor + + + + + + true + + + + + 0 + 0 + 470 + 250 + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok|QDialogButtonBox::Reset|QDialogButtonBox::RestoreDefaults + + + + + + + + + buttonBox + accepted() + CustomScreenLayoutEditor + accept() + + + 257 + 290 + + + 157 + 274 + + + + + buttonBox + rejected() + CustomScreenLayoutEditor + reject() + + + 325 + 290 + + + 286 + 274 + + + + + diff --git a/src/citra_qt/main.cpp b/src/citra_qt/main.cpp index c8639f616..8fa51bfbc 100644 --- a/src/citra_qt/main.cpp +++ b/src/citra_qt/main.cpp @@ -301,6 +301,7 @@ void GMainWindow::InitializeWidgets() { 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_Side_by_Side); + actionGroup_ScreenLayouts->addAction(ui->action_Screen_Layout_Custom); } void GMainWindow::InitializeDebugWidgets() { @@ -734,6 +735,8 @@ void GMainWindow::ConnectMenuEvents() { &GMainWindow::ChangeScreenLayout); connect(ui->action_Screen_Layout_Side_by_Side, &QAction::triggered, this, &GMainWindow::ChangeScreenLayout); + connect(ui->action_Screen_Layout_Custom, &QAction::triggered, this, + &GMainWindow::ChangeScreenLayout); connect(ui->action_Screen_Layout_Swap_Screens, &QAction::triggered, this, &GMainWindow::OnSwapScreens); connect(ui->action_Screen_Layout_Upright_Screens, &QAction::triggered, this, @@ -1664,6 +1667,8 @@ void GMainWindow::ChangeScreenLayout() { new_layout = Settings::LayoutOption::LargeScreen; } else if (ui->action_Screen_Layout_Side_by_Side->isChecked()) { new_layout = Settings::LayoutOption::SideScreen; + } else if (ui->action_Screen_Layout_Custom->isChecked()) { + new_layout = Settings::LayoutOption::Custom; } Settings::values.layout_option = new_layout; @@ -1671,24 +1676,9 @@ void GMainWindow::ChangeScreenLayout() { } void GMainWindow::ToggleScreenLayout() { - Settings::LayoutOption new_layout = Settings::LayoutOption::Default; - - switch (Settings::values.layout_option) { - case Settings::LayoutOption::Default: - new_layout = Settings::LayoutOption::SingleScreen; - break; - case Settings::LayoutOption::SingleScreen: - new_layout = Settings::LayoutOption::LargeScreen; - break; - case Settings::LayoutOption::LargeScreen: - new_layout = Settings::LayoutOption::SideScreen; - break; - case Settings::LayoutOption::SideScreen: - new_layout = Settings::LayoutOption::Default; - break; - } - - Settings::values.layout_option = new_layout; + Settings::values.layout_option = + static_cast((static_cast(Settings::values.layout_option) + 1) % + (static_cast(Settings::LayoutOption::Custom) + 1)), SyncMenuUISettings(); Settings::Apply(); } @@ -2387,6 +2377,8 @@ void GMainWindow::SyncMenuUISettings() { Settings::LayoutOption::LargeScreen); ui->action_Screen_Layout_Side_by_Side->setChecked(Settings::values.layout_option == Settings::LayoutOption::SideScreen); + ui->action_Screen_Layout_Custom->setChecked(Settings::values.layout_option == + Settings::LayoutOption::Custom); ui->action_Screen_Layout_Swap_Screens->setChecked(Settings::values.swap_screen); ui->action_Screen_Layout_Upright_Screens->setChecked(Settings::values.upright_screen); } diff --git a/src/citra_qt/main.ui b/src/citra_qt/main.ui index 2eff98083..b191a23a8 100644 --- a/src/citra_qt/main.ui +++ b/src/citra_qt/main.ui @@ -125,6 +125,7 @@ + @@ -235,20 +236,20 @@ - - false - - - Save - + + false + + + Save + - - false - - - Load - + + false + + + Load + @@ -461,6 +462,14 @@ Side by Side + + + true + + + Custom + + true diff --git a/src/common/diagnostic.h b/src/common/diagnostic.h new file mode 100644 index 000000000..a7f667ad9 --- /dev/null +++ b/src/common/diagnostic.h @@ -0,0 +1,27 @@ +// Copyright 2019 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +// Adapted from +// https://github.com/ericniebler/range-v3/blob/master/include/range/v3/detail/config.hpp#L185. +#define CITRA_PRAGMA(X) _Pragma(#X) +#if !CITRA_COMP_MSVC +#define CITRA_DIAGNOSTIC_PUSH CITRA_PRAGMA(GCC diagnostic push) +#define CITRA_DIAGNOSTIC_POP CITRA_PRAGMA(GCC diagnostic pop) +#define CITRA_DIAGNOSTIC_IGNORE_PRAGMAS CITRA_PRAGMA(GCC diagnostic ignored "-Wpragmas") +#define CITRA_DIAGNOSTIC_IGNORE(X) \ + CITRA_DIAGNOSTIC_IGNORE_PRAGMAS \ + CITRA_PRAGMA(GCC diagnostic ignored "-Wunknown-pragmas") \ + CITRA_PRAGMA(GCC diagnostic ignored "-Wunknown-warning-option") \ + CITRA_PRAGMA(GCC diagnostic ignored X) +#define CITRA_DIAGNOSTIC_IGNORE_SWITCH CITRA_DIAGNOSTIC_IGNORE("-Wswitch") +#else +#define CITRA_DIAGNOSTIC_PUSH CITRA_PRAGMA(warning(push)) +#define CITRA_DIAGNOSTIC_POP CITRA_PRAGMA(warning(pop)) +#define CITRA_DIAGNOSTIC_IGNORE_PRAGMAS CITRA_PRAGMA(warning(disable : 4068)) +#define CITRA_DIAGNOSTIC_IGNORE(X) \ + CITRA_DIAGNOSTIC_IGNORE_PRAGMAS CITRA_PRAGMA(warning(disable : X)) +#define CITRA_DIAGNOSTIC_IGNORE_SWITCH +#endif diff --git a/src/core/frontend/emu_window.cpp b/src/core/frontend/emu_window.cpp index 44502858d..d87d5dd7d 100644 --- a/src/core/frontend/emu_window.cpp +++ b/src/core/frontend/emu_window.cpp @@ -151,32 +151,31 @@ void EmuWindow::UpdateCurrentFramebufferLayout(unsigned width, unsigned height) const auto min_size = Layout::GetMinimumSizeFromLayout(layout_option, Settings::values.upright_screen); - if (Settings::values.custom_layout == true) { + width = std::max(width, min_size.first); + height = std::max(height, min_size.second); + switch (layout_option) { + case Settings::LayoutOption::Custom: layout = Layout::CustomFrameLayout(width, height); - } else { - width = std::max(width, min_size.first); - height = std::max(height, min_size.second); - switch (layout_option) { - case Settings::LayoutOption::SingleScreen: - layout = Layout::SingleFrameLayout(width, height, Settings::values.swap_screen, - Settings::values.upright_screen); - break; - case Settings::LayoutOption::LargeScreen: - layout = Layout::LargeFrameLayout(width, height, Settings::values.swap_screen, - Settings::values.upright_screen); - break; - case Settings::LayoutOption::SideScreen: - layout = Layout::SideFrameLayout(width, height, Settings::values.swap_screen, - Settings::values.upright_screen); - break; - case Settings::LayoutOption::Default: - default: - layout = Layout::DefaultFrameLayout(width, height, Settings::values.swap_screen, - Settings::values.upright_screen); - break; - } - UpdateMinimumWindowSize(min_size); + break; + case Settings::LayoutOption::SingleScreen: + layout = Layout::SingleFrameLayout(width, height, Settings::values.swap_screen, + Settings::values.upright_screen); + break; + case Settings::LayoutOption::LargeScreen: + layout = Layout::LargeFrameLayout(width, height, Settings::values.swap_screen, + Settings::values.upright_screen); + break; + case Settings::LayoutOption::SideScreen: + layout = Layout::SideFrameLayout(width, height, Settings::values.swap_screen, + Settings::values.upright_screen); + break; + case Settings::LayoutOption::Default: + default: + layout = Layout::DefaultFrameLayout(width, height, Settings::values.swap_screen, + Settings::values.upright_screen); + break; } + UpdateMinimumWindowSize(min_size); NotifyFramebufferLayoutChanged(layout); } diff --git a/src/core/frontend/framebuffer_layout.cpp b/src/core/frontend/framebuffer_layout.cpp index 71dce24be..9c7bc48d5 100644 --- a/src/core/frontend/framebuffer_layout.cpp +++ b/src/core/frontend/framebuffer_layout.cpp @@ -286,79 +286,78 @@ FramebufferLayout CustomFrameLayout(u32 width, u32 height) { FramebufferLayout FrameLayoutFromResolutionScale(u32 res_scale) { FramebufferLayout layout; - if (Settings::values.custom_layout == true) { + int width, height; + switch (Settings::values.layout_option) { + case Settings::LayoutOption::Custom: layout = CustomFrameLayout( std::max(Settings::values.custom_top_right, Settings::values.custom_bottom_right), std::max(Settings::values.custom_top_bottom, Settings::values.custom_bottom_bottom)); - } else { - int width, height; - switch (Settings::values.layout_option) { - case Settings::LayoutOption::SingleScreen: - if (Settings::values.upright_screen) { - if (Settings::values.swap_screen) { - width = Core::kScreenBottomHeight * res_scale; - height = Core::kScreenBottomWidth * res_scale; - } else { - width = Core::kScreenTopHeight * res_scale; - height = Core::kScreenTopWidth * res_scale; - } + break; + case Settings::LayoutOption::SingleScreen: + if (Settings::values.upright_screen) { + if (Settings::values.swap_screen) { + width = Core::kScreenBottomHeight * res_scale; + height = Core::kScreenBottomWidth * res_scale; } else { - if (Settings::values.swap_screen) { - width = Core::kScreenBottomWidth * res_scale; - height = Core::kScreenBottomHeight * res_scale; - } else { - width = Core::kScreenTopWidth * res_scale; - height = Core::kScreenTopHeight * res_scale; - } - } - layout = SingleFrameLayout(width, height, Settings::values.swap_screen, - Settings::values.upright_screen); - break; - case Settings::LayoutOption::LargeScreen: - if (Settings::values.upright_screen) { - if (Settings::values.swap_screen) { - width = Core::kScreenBottomHeight * res_scale; - height = (Core::kScreenBottomWidth + Core::kScreenTopWidth / 4) * res_scale; - } else { - width = Core::kScreenTopHeight * res_scale; - height = (Core::kScreenTopWidth + Core::kScreenBottomWidth / 4) * res_scale; - } - } else { - if (Settings::values.swap_screen) { - width = (Core::kScreenBottomWidth + Core::kScreenTopWidth / 4) * res_scale; - height = Core::kScreenBottomHeight * res_scale; - } else { - width = (Core::kScreenTopWidth + Core::kScreenBottomWidth / 4) * res_scale; - height = Core::kScreenTopHeight * res_scale; - } - } - layout = LargeFrameLayout(width, height, Settings::values.swap_screen, - Settings::values.upright_screen); - break; - case Settings::LayoutOption::SideScreen: - if (Settings::values.upright_screen) { width = Core::kScreenTopHeight * res_scale; - height = (Core::kScreenTopWidth + Core::kScreenBottomWidth) * res_scale; - } else { - width = (Core::kScreenTopWidth + Core::kScreenBottomWidth) * res_scale; - height = Core::kScreenTopHeight * res_scale; - } - layout = SideFrameLayout(width, height, Settings::values.swap_screen, - Settings::values.upright_screen); - break; - case Settings::LayoutOption::Default: - default: - if (Settings::values.upright_screen) { - width = (Core::kScreenTopHeight + Core::kScreenBottomHeight) * res_scale; height = Core::kScreenTopWidth * res_scale; + } + } else { + if (Settings::values.swap_screen) { + width = Core::kScreenBottomWidth * res_scale; + height = Core::kScreenBottomHeight * res_scale; } else { width = Core::kScreenTopWidth * res_scale; - height = (Core::kScreenTopHeight + Core::kScreenBottomHeight) * res_scale; + height = Core::kScreenTopHeight * res_scale; } - layout = DefaultFrameLayout(width, height, Settings::values.swap_screen, - Settings::values.upright_screen); - break; } + layout = SingleFrameLayout(width, height, Settings::values.swap_screen, + Settings::values.upright_screen); + break; + case Settings::LayoutOption::LargeScreen: + if (Settings::values.upright_screen) { + if (Settings::values.swap_screen) { + width = Core::kScreenBottomHeight * res_scale; + height = (Core::kScreenBottomWidth + Core::kScreenTopWidth / 4) * res_scale; + } else { + width = Core::kScreenTopHeight * res_scale; + height = (Core::kScreenTopWidth + Core::kScreenBottomWidth / 4) * res_scale; + } + } else { + if (Settings::values.swap_screen) { + width = (Core::kScreenBottomWidth + Core::kScreenTopWidth / 4) * res_scale; + height = Core::kScreenBottomHeight * res_scale; + } else { + width = (Core::kScreenTopWidth + Core::kScreenBottomWidth / 4) * res_scale; + height = Core::kScreenTopHeight * res_scale; + } + } + layout = LargeFrameLayout(width, height, Settings::values.swap_screen, + Settings::values.upright_screen); + break; + case Settings::LayoutOption::SideScreen: + if (Settings::values.upright_screen) { + width = Core::kScreenTopHeight * res_scale; + height = (Core::kScreenTopWidth + Core::kScreenBottomWidth) * res_scale; + } else { + width = (Core::kScreenTopWidth + Core::kScreenBottomWidth) * res_scale; + height = Core::kScreenTopHeight * res_scale; + } + layout = SideFrameLayout(width, height, Settings::values.swap_screen, + Settings::values.upright_screen); + break; + case Settings::LayoutOption::Default: + default: + if (Settings::values.upright_screen) { + width = (Core::kScreenTopHeight + Core::kScreenBottomHeight) * res_scale; + height = Core::kScreenTopWidth * res_scale; + } else { + width = Core::kScreenTopWidth * res_scale; + height = (Core::kScreenTopHeight + Core::kScreenBottomHeight) * res_scale; + } + layout = DefaultFrameLayout(width, height, Settings::values.swap_screen, + Settings::values.upright_screen); + break; } return layout; } diff --git a/src/core/settings.h b/src/core/settings.h index 0876d312f..f47f19fd9 100644 --- a/src/core/settings.h +++ b/src/core/settings.h @@ -24,6 +24,7 @@ enum class LayoutOption { SingleScreen, LargeScreen, SideScreen, + Custom, }; enum class MicInputType { @@ -163,7 +164,6 @@ struct Values { LayoutOption layout_option; bool swap_screen; bool upright_screen; - bool custom_layout; u16 custom_top_left; u16 custom_top_top; u16 custom_top_right;