diff --git a/src/duckstation-qt/mainwindow.cpp b/src/duckstation-qt/mainwindow.cpp index b122d8c7d..2d74a731b 100644 --- a/src/duckstation-qt/mainwindow.cpp +++ b/src/duckstation-qt/mainwindow.cpp @@ -120,23 +120,36 @@ QtDisplayWidget* MainWindow::createDisplay(QThread* worker_thread, bool fullscre const std::string fullscreen_mode = m_host_interface->GetStringSettingValue("GPU", "FullscreenMode", ""); const bool is_exclusive_fullscreen = (fullscreen && !fullscreen_mode.empty() && m_host_display->SupportsFullscreen()); - m_display_widget = new QtDisplayWidget((!fullscreen && render_to_main) ? m_ui.mainContainer : nullptr); - m_display_widget->setWindowTitle(windowTitle()); - m_display_widget->setWindowIcon(windowIcon()); + QWidget* container; + if (QtDisplayContainer::IsNeeded(fullscreen, render_to_main)) + { + m_display_container = new QtDisplayContainer(); + m_display_widget = new QtDisplayWidget(m_display_container); + m_display_container->setDisplayWidget(m_display_widget); + container = m_display_container; + } + else + { + m_display_widget = new QtDisplayWidget((!fullscreen && render_to_main) ? m_ui.mainContainer : nullptr); + container = m_display_widget; + } + + container->setWindowTitle(windowTitle()); + container->setWindowIcon(windowIcon()); if (fullscreen) { if (!is_exclusive_fullscreen) - m_display_widget->showFullScreen(); + container->showFullScreen(); else - m_display_widget->showNormal(); + container->showNormal(); updateMouseMode(System::IsPaused()); } else if (!render_to_main) { restoreDisplayWindowGeometryFromConfig(); - m_display_widget->showNormal(); + container->showNormal(); } else { @@ -182,7 +195,9 @@ QtDisplayWidget* MainWindow::updateDisplay(QThread* worker_thread, bool fullscre return m_display_widget; // Skip recreating the surface if we're just transitioning between fullscreen and windowed with render-to-main off. - if (!is_rendering_to_main && !render_to_main && !is_exclusive_fullscreen) + const bool has_container = (m_display_container != nullptr); + const bool needs_container = QtDisplayContainer::IsNeeded(fullscreen, render_to_main); + if (!is_rendering_to_main && !render_to_main && !is_exclusive_fullscreen && has_container == needs_container) { qDebug() << "Toggling to" << (fullscreen ? "fullscreen" : "windowed") << "without recreating surface"; if (m_host_display && m_host_display->IsFullscreen()) @@ -206,23 +221,37 @@ QtDisplayWidget* MainWindow::updateDisplay(QThread* worker_thread, bool fullscre m_host_display->DestroyRenderSurface(); destroyDisplayWidget(); - m_display_widget = new QtDisplayWidget((!fullscreen && render_to_main) ? m_ui.mainContainer : nullptr); - m_display_widget->setWindowTitle(windowTitle()); - m_display_widget->setWindowIcon(windowIcon()); + + QWidget* container; + if (QtDisplayContainer::IsNeeded(fullscreen, render_to_main)) + { + m_display_container = new QtDisplayContainer(); + m_display_widget = new QtDisplayWidget(m_display_container); + m_display_container->setDisplayWidget(m_display_widget); + container = m_display_container; + } + else + { + m_display_widget = new QtDisplayWidget((!fullscreen && render_to_main) ? m_ui.mainContainer : nullptr); + container = m_display_widget; + } + + container->setWindowTitle(windowTitle()); + container->setWindowIcon(windowIcon()); if (fullscreen) { if (!is_exclusive_fullscreen) - m_display_widget->showFullScreen(); + container->showFullScreen(); else - m_display_widget->showNormal(); + container->showNormal(); updateMouseMode(System::IsPaused()); } else if (!render_to_main) { restoreDisplayWindowGeometryFromConfig(); - m_display_widget->showNormal(); + container->showNormal(); } else { @@ -281,10 +310,10 @@ void MainWindow::displaySizeRequested(qint32 width, qint32 height) if (!m_display_widget) return; - if (!m_display_widget->parent()) + if (m_display_container || !m_display_widget->parent()) { // no parent - rendering to separate window. easy. - m_display_widget->resize(QSize(std::max(width, 1), std::max(height, 1))); + getDisplayContainer()->resize(QSize(std::max(width, 1), std::max(height, 1))); return; } @@ -308,18 +337,23 @@ void MainWindow::destroyDisplayWidget() if (!m_display_widget) return; + if (m_display_container || (!m_display_widget->parent() && !m_display_widget->isFullScreen())) + saveDisplayWindowGeometryToConfig(); + + if (m_display_container) + m_display_container->removeDisplayWidget(); + if (m_display_widget->parent()) { switchToGameListView(); m_ui.mainContainer->removeWidget(m_display_widget); } - else if (!m_display_widget->isFullScreen()) - { - saveDisplayWindowGeometryToConfig(); - } delete m_display_widget; m_display_widget = nullptr; + + delete m_display_container; + m_display_container = nullptr; } void MainWindow::focusDisplayWidget() @@ -1367,7 +1401,7 @@ void MainWindow::restoreStateFromConfig() void MainWindow::saveDisplayWindowGeometryToConfig() { - const QByteArray geometry = m_display_widget->saveGeometry(); + const QByteArray geometry = getDisplayContainer()->saveGeometry(); const QByteArray geometry_b64 = geometry.toBase64(); const std::string old_geometry_b64 = m_host_interface->GetStringSettingValue("UI", "DisplayWindowGeometry"); if (old_geometry_b64 != geometry_b64.constData()) @@ -1378,8 +1412,11 @@ void MainWindow::restoreDisplayWindowGeometryFromConfig() { const std::string geometry_b64 = m_host_interface->GetStringSettingValue("UI", "DisplayWindowGeometry"); const QByteArray geometry = QByteArray::fromBase64(QByteArray::fromStdString(geometry_b64)); + QWidget* container = getDisplayContainer(); if (!geometry.isEmpty()) - m_display_widget->restoreGeometry(geometry); + container->restoreGeometry(geometry); + else + container->resize(640, 480); } SettingsDialog* MainWindow::getSettingsDialog() diff --git a/src/duckstation-qt/mainwindow.h b/src/duckstation-qt/mainwindow.h index 87a2bd5ed..6aa21eda6 100644 --- a/src/duckstation-qt/mainwindow.h +++ b/src/duckstation-qt/mainwindow.h @@ -2,9 +2,11 @@ #include #include #include +#include #include #include "core/types.h" +#include "qtdisplaywidget.h" #include "settingsdialog.h" #include "ui_mainwindow.h" @@ -13,7 +15,6 @@ class QThread; class GameListWidget; class QtHostInterface; -class QtDisplayWidget; class AutoUpdaterDialog; class MemoryCardEditorDialog; class CheatManagerDialog; @@ -119,6 +120,11 @@ protected: void dropEvent(QDropEvent* event) override; private: + ALWAYS_INLINE QWidget* getDisplayContainer() const + { + return (m_display_container ? static_cast(m_display_container) : static_cast(m_display_widget)); + } + void setupAdditionalUi(); void connectSignals(); void addThemeToMenu(const QString& name, const QString& key); @@ -150,6 +156,7 @@ private: HostDisplay* m_host_display = nullptr; QtDisplayWidget* m_display_widget = nullptr; + QtDisplayContainer* m_display_container = nullptr; QLabel* m_status_speed_widget = nullptr; QLabel* m_status_fps_widget = nullptr; diff --git a/src/duckstation-qt/qtdisplaywidget.cpp b/src/duckstation-qt/qtdisplaywidget.cpp index d92f8a317..69a6a8a3d 100644 --- a/src/duckstation-qt/qtdisplaywidget.cpp +++ b/src/duckstation-qt/qtdisplaywidget.cpp @@ -233,3 +233,78 @@ bool QtDisplayWidget::event(QEvent* event) return QWidget::event(event); } } + +QtDisplayContainer::QtDisplayContainer() : QStackedWidget(nullptr) {} + +QtDisplayContainer::~QtDisplayContainer() = default; + +bool QtDisplayContainer::IsNeeded(bool fullscreen, bool render_to_main) +{ +#if defined(_WIN32) || defined(__APPLE__) + return false; +#else + if (fullscreen || render_to_main) + return false; + + // We only need this on Wayland because of client-side decorations... + const QString platform_name = QGuiApplication::platformName(); + return (platform_name == QStringLiteral("wayland")); +#endif +} + +void QtDisplayContainer::setDisplayWidget(QtDisplayWidget* widget) +{ + Assert(!m_display_widget); + m_display_widget = widget; + addWidget(widget); +} + +QtDisplayWidget* QtDisplayContainer::removeDisplayWidget() +{ + QtDisplayWidget* widget = m_display_widget; + Assert(widget); + m_display_widget = nullptr; + removeWidget(widget); + return widget; +} + +bool QtDisplayContainer::event(QEvent* event) +{ + const bool res = QStackedWidget::event(event); + if (!m_display_widget) + return res; + + switch (event->type()) + { + case QEvent::Close: + { + emit m_display_widget->windowClosedEvent(); + } + break; + + case QEvent::WindowStateChange: + { + if (static_cast(event)->oldState() & Qt::WindowMinimized) + emit m_display_widget->windowRestoredEvent(); + } + break; + + case QEvent::FocusIn: + { + emit m_display_widget->windowFocusEvent(); + } + break; + + case QEvent::ActivationChange: + { + if (isActiveWindow()) + emit m_display_widget->windowFocusEvent(); + } + break; + + default: + break; + } + + return res; +} diff --git a/src/duckstation-qt/qtdisplaywidget.h b/src/duckstation-qt/qtdisplaywidget.h index ce19c4515..c56f61f83 100644 --- a/src/duckstation-qt/qtdisplaywidget.h +++ b/src/duckstation-qt/qtdisplaywidget.h @@ -1,6 +1,7 @@ #pragma once #include "common/types.h" #include "common/window_info.h" +#include #include #include @@ -40,3 +41,23 @@ private: QPoint m_relative_mouse_last_position{}; bool m_relative_mouse_enabled = false; }; + +class QtDisplayContainer final : public QStackedWidget +{ + Q_OBJECT + +public: + QtDisplayContainer(); + ~QtDisplayContainer(); + + static bool IsNeeded(bool fullscreen, bool render_to_main); + + void setDisplayWidget(QtDisplayWidget* widget); + QtDisplayWidget* removeDisplayWidget(); + +protected: + bool event(QEvent* event) override; + +private: + QtDisplayWidget* m_display_widget = nullptr; +};