From 14a570559b33042b2fff0aa2129dc3ad04c59d5e Mon Sep 17 00:00:00 2001 From: Stenzek Date: Fri, 6 Sep 2024 20:43:43 +1000 Subject: [PATCH] Qt: Save/restore additional window positions Cheat Manager, Memory Scanner, CPU Debugger. --- src/duckstation-qt/cheatmanagerwindow.cpp | 3 + src/duckstation-qt/debuggerwindow.cpp | 3 + src/duckstation-qt/mainwindow.cpp | 47 ++------------ src/duckstation-qt/memoryscannerwindow.cpp | 2 + src/duckstation-qt/qtutils.cpp | 74 +++++++++++++++------- src/duckstation-qt/qtutils.h | 6 ++ 6 files changed, 72 insertions(+), 63 deletions(-) diff --git a/src/duckstation-qt/cheatmanagerwindow.cpp b/src/duckstation-qt/cheatmanagerwindow.cpp index 122c98b60..f32172b48 100644 --- a/src/duckstation-qt/cheatmanagerwindow.cpp +++ b/src/duckstation-qt/cheatmanagerwindow.cpp @@ -29,6 +29,8 @@ CheatManagerWindow::CheatManagerWindow() : QWidget() { m_ui.setupUi(this); + QtUtils::RestoreWindowGeometry("CheatManagerWindow", this); + connectUi(); updateCheatList(); @@ -63,6 +65,7 @@ void CheatManagerWindow::showEvent(QShowEvent* event) void CheatManagerWindow::closeEvent(QCloseEvent* event) { + QtUtils::SaveWindowGeometry("CheatManagerWindow", this); QWidget::closeEvent(event); emit closed(); } diff --git a/src/duckstation-qt/debuggerwindow.cpp b/src/duckstation-qt/debuggerwindow.cpp index 03d06d41f..681a861ff 100644 --- a/src/duckstation-qt/debuggerwindow.cpp +++ b/src/duckstation-qt/debuggerwindow.cpp @@ -410,6 +410,7 @@ void DebuggerWindow::onMemorySearchStringChanged(const QString&) void DebuggerWindow::closeEvent(QCloseEvent* event) { + QtUtils::SaveWindowGeometry("DebuggerWindow", this); g_emu_thread->disconnect(this); Host::RunOnCPUThread(&CPU::ClearBreakpoints); QMainWindow::closeEvent(event); @@ -439,6 +440,8 @@ void DebuggerWindow::setupAdditionalUi() setCentralWidget(nullptr); delete m_ui.centralwidget; + + QtUtils::RestoreWindowGeometry("DebuggerWindow", this); } void DebuggerWindow::connectSignals() diff --git a/src/duckstation-qt/mainwindow.cpp b/src/duckstation-qt/mainwindow.cpp index 3259d9c4e..17461ed10 100644 --- a/src/duckstation-qt/mainwindow.cpp +++ b/src/duckstation-qt/mainwindow.cpp @@ -2265,16 +2265,7 @@ void MainWindow::saveStateToConfig() if (!isVisible() || ((windowState() & Qt::WindowFullScreen) != Qt::WindowNoState)) return; - bool changed = false; - - const QByteArray geometry(saveGeometry()); - const QByteArray geometry_b64(geometry.toBase64()); - const std::string old_geometry_b64(Host::GetBaseStringSettingValue("UI", "MainWindowGeometry")); - if (old_geometry_b64 != geometry_b64.constData()) - { - Host::SetBaseStringSettingValue("UI", "MainWindowGeometry", geometry_b64.constData()); - changed = true; - } + bool changed = QtUtils::SaveWindowGeometry("MainWindow", this, false); const QByteArray state(saveState()); const QByteArray state_b64(state.toBase64()); @@ -2291,12 +2282,7 @@ void MainWindow::saveStateToConfig() void MainWindow::restoreStateFromConfig() { - { - const std::string geometry_b64 = Host::GetBaseStringSettingValue("UI", "MainWindowGeometry"); - const QByteArray geometry = QByteArray::fromBase64(QByteArray::fromStdString(geometry_b64)); - if (!geometry.isEmpty()) - restoreGeometry(geometry); - } + QtUtils::RestoreWindowGeometry("MainWindow", this); { const std::string state_b64 = Host::GetBaseStringSettingValue("UI", "MainWindowState"); @@ -2322,36 +2308,20 @@ void MainWindow::restoreStateFromConfig() void MainWindow::saveDisplayWindowGeometryToConfig() { - QWidget* container = getDisplayContainer(); + QWidget* const container = getDisplayContainer(); if (container->windowState() & Qt::WindowFullScreen) { // if we somehow ended up here, don't save the fullscreen state to the config return; } - const QByteArray geometry = container->saveGeometry(); - const QByteArray geometry_b64 = geometry.toBase64(); - const std::string old_geometry_b64 = Host::GetBaseStringSettingValue("UI", "DisplayWindowGeometry"); - if (old_geometry_b64 != geometry_b64.constData()) - { - Host::SetBaseStringSettingValue("UI", "DisplayWindowGeometry", geometry_b64.constData()); - Host::CommitBaseSettingChanges(); - } + QtUtils::SaveWindowGeometry("DisplayWindow", container); } void MainWindow::restoreDisplayWindowGeometryFromConfig() { - const std::string geometry_b64 = Host::GetBaseStringSettingValue("UI", "DisplayWindowGeometry"); - const QByteArray geometry = QByteArray::fromBase64(QByteArray::fromStdString(geometry_b64)); - QWidget* container = getDisplayContainer(); - if (!geometry.isEmpty()) - { - container->restoreGeometry(geometry); - - // make sure we're not loading a dodgy config which had fullscreen set... - container->setWindowState(container->windowState() & ~(Qt::WindowFullScreen | Qt::WindowActive)); - } - else + QWidget* const container = getDisplayContainer(); + if (!QtUtils::RestoreWindowGeometry("DisplayWindow", container)) { // default size container->resize(640, 480); @@ -2850,11 +2820,6 @@ void MainWindow::openCPUDebugger() m_debugger_window->deleteLater(); m_debugger_window = nullptr; }); - - // Position the debugger window to the right of the main/display window. - const QWidget* next_to_widget = - m_display_container ? static_cast(m_display_container) : static_cast(this); - m_debugger_window->move(next_to_widget->pos() + QPoint(next_to_widget->width() + 16, 0)); } QtUtils::ShowOrRaiseWindow(m_debugger_window); diff --git a/src/duckstation-qt/memoryscannerwindow.cpp b/src/duckstation-qt/memoryscannerwindow.cpp index ac74b0373..38e788ec2 100644 --- a/src/duckstation-qt/memoryscannerwindow.cpp +++ b/src/duckstation-qt/memoryscannerwindow.cpp @@ -94,6 +94,7 @@ static QString formatValue(u32 value, bool is_signed) MemoryScannerWindow::MemoryScannerWindow() : QWidget() { m_ui.setupUi(this); + QtUtils::RestoreWindowGeometry("MemoryScannerWindow", this); connectUi(); m_ui.cheatEngineAddress->setText(tr("Address of RAM for HxD Usage: 0x%1") @@ -217,6 +218,7 @@ void MemoryScannerWindow::showEvent(QShowEvent* event) void MemoryScannerWindow::closeEvent(QCloseEvent* event) { + QtUtils::SaveWindowGeometry("MemoryScannerWindow", this); QWidget::closeEvent(event); emit closed(); } diff --git a/src/duckstation-qt/qtutils.cpp b/src/duckstation-qt/qtutils.cpp index 9c23197b6..a6218013b 100644 --- a/src/duckstation-qt/qtutils.cpp +++ b/src/duckstation-qt/qtutils.cpp @@ -2,6 +2,7 @@ // SPDX-License-Identifier: CC-BY-NC-ND-4.0 #include "qtutils.h" +#include "qthost.h" #include "core/game_list.h" #include "core/system.h" @@ -40,9 +41,7 @@ Log_SetChannel(QtUtils); -namespace QtUtils { - -QFrame* CreateHorizontalLine(QWidget* parent) +QFrame* QtUtils::CreateHorizontalLine(QWidget* parent) { QFrame* line = new QFrame(parent); line->setFrameShape(QFrame::HLine); @@ -50,7 +49,7 @@ QFrame* CreateHorizontalLine(QWidget* parent) return line; } -QWidget* GetRootWidget(QWidget* widget, bool stop_at_window_or_dialog) +QWidget* QtUtils::GetRootWidget(QWidget* widget, bool stop_at_window_or_dialog) { QWidget* next_parent = widget->parentWidget(); while (next_parent) @@ -68,7 +67,7 @@ QWidget* GetRootWidget(QWidget* widget, bool stop_at_window_or_dialog) return widget; } -void ShowOrRaiseWindow(QWidget* window) +void QtUtils::ShowOrRaiseWindow(QWidget* window) { if (!window) return; @@ -135,17 +134,17 @@ ALWAYS_INLINE_RELEASE static void ResizeColumnsForView(T* view, const std::initi } } -void ResizeColumnsForTableView(QTableView* view, const std::initializer_list& widths) +void QtUtils::ResizeColumnsForTableView(QTableView* view, const std::initializer_list& widths) { ResizeColumnsForView(view, widths); } -void ResizeColumnsForTreeView(QTreeView* view, const std::initializer_list& widths) +void QtUtils::ResizeColumnsForTreeView(QTreeView* view, const std::initializer_list& widths) { ResizeColumnsForView(view, widths); } -void OpenURL(QWidget* parent, const QUrl& qurl) +void QtUtils::OpenURL(QWidget* parent, const QUrl& qurl) { if (!QDesktopServices::openUrl(qurl)) { @@ -154,12 +153,13 @@ void OpenURL(QWidget* parent, const QUrl& qurl) } } -void OpenURL(QWidget* parent, const char* url) +void QtUtils::OpenURL(QWidget* parent, const char* url) { return OpenURL(parent, QUrl::fromEncoded(QByteArray(url, static_cast(std::strlen(url))))); } -std::optional PromptForAddress(QWidget* parent, const QString& title, const QString& label, bool code) +std::optional QtUtils::PromptForAddress(QWidget* parent, const QString& title, const QString& label, + bool code) { const QString address_str( QInputDialog::getText(parent, title, qApp->translate("DebuggerWindow", "Enter memory address:"))); @@ -186,12 +186,12 @@ std::optional PromptForAddress(QWidget* parent, const QString& title, return address; } -QString StringViewToQString(std::string_view str) +QString QtUtils::StringViewToQString(std::string_view str) { return str.empty() ? QString() : QString::fromUtf8(str.data(), str.size()); } -void SetWidgetFontForInheritedSetting(QWidget* widget, bool inherited) +void QtUtils::SetWidgetFontForInheritedSetting(QWidget* widget, bool inherited) { if (widget->font().italic() != inherited) { @@ -201,7 +201,7 @@ void SetWidgetFontForInheritedSetting(QWidget* widget, bool inherited) } } -void BindLabelToSlider(QSlider* slider, QLabel* label, float range /*= 1.0f*/) +void QtUtils::BindLabelToSlider(QSlider* slider, QLabel* label, float range /*= 1.0f*/) { auto update_label = [label, range](int new_value) { label->setText(QString::number(static_cast(new_value) / range)); @@ -210,7 +210,7 @@ void BindLabelToSlider(QSlider* slider, QLabel* label, float range /*= 1.0f*/) QObject::connect(slider, &QSlider::valueChanged, label, std::move(update_label)); } -void SetWindowResizeable(QWidget* widget, bool resizeable) +void QtUtils::SetWindowResizeable(QWidget* widget, bool resizeable) { if (QMainWindow* window = qobject_cast(widget); window) { @@ -236,7 +236,7 @@ void SetWindowResizeable(QWidget* widget, bool resizeable) } } -void ResizePotentiallyFixedSizeWindow(QWidget* widget, int width, int height) +void QtUtils::ResizePotentiallyFixedSizeWindow(QWidget* widget, int width, int height) { width = std::max(width, 1); height = std::max(height, 1); @@ -246,7 +246,7 @@ void ResizePotentiallyFixedSizeWindow(QWidget* widget, int width, int height) widget->resize(width, height); } -QIcon GetIconForRegion(ConsoleRegion region) +QIcon QtUtils::GetIconForRegion(ConsoleRegion region) { switch (region) { @@ -261,7 +261,7 @@ QIcon GetIconForRegion(ConsoleRegion region) } } -QIcon GetIconForRegion(DiscRegion region) +QIcon QtUtils::GetIconForRegion(DiscRegion region) { switch (region) { @@ -278,7 +278,7 @@ QIcon GetIconForRegion(DiscRegion region) } } -QIcon GetIconForEntryType(GameList::EntryType type) +QIcon QtUtils::GetIconForEntryType(GameList::EntryType type) { switch (type) { @@ -295,12 +295,12 @@ QIcon GetIconForEntryType(GameList::EntryType type) } } -QIcon GetIconForCompatibility(GameDatabase::CompatibilityRating rating) +QIcon QtUtils::GetIconForCompatibility(GameDatabase::CompatibilityRating rating) { return QIcon(QStringLiteral(":/icons/star-%1.png").arg(static_cast(rating))); } -qreal GetDevicePixelRatioForWidget(const QWidget* widget) +qreal QtUtils::GetDevicePixelRatioForWidget(const QWidget* widget) { const QScreen* screen_for_ratio = widget->screen(); if (!screen_for_ratio) @@ -309,7 +309,7 @@ qreal GetDevicePixelRatioForWidget(const QWidget* widget) return screen_for_ratio ? screen_for_ratio->devicePixelRatio() : static_cast(1); } -std::optional GetWindowInfoForWidget(QWidget* widget) +std::optional QtUtils::GetWindowInfoForWidget(QWidget* widget) { WindowInfo wi; @@ -364,4 +364,34 @@ std::optional GetWindowInfoForWidget(QWidget* widget) return wi; } -} // namespace QtUtils +bool QtUtils::SaveWindowGeometry(std::string_view window_name, QWidget* widget, bool auto_commit_changes) +{ + const TinyString config_key = TinyString::from_format("{}Geometry", window_name); + + const QByteArray geometry = widget->saveGeometry(); + const QByteArray geometry_b64 = geometry.toBase64(); + const std::string old_geometry_b64 = Host::GetBaseStringSettingValue("UI", config_key); + if (old_geometry_b64 == geometry_b64.constData()) + return false; + + Host::SetBaseStringSettingValue("UI", config_key, geometry_b64.constData()); + if (auto_commit_changes) + Host::CommitBaseSettingChanges(); + + return true; +} + +bool QtUtils::RestoreWindowGeometry(std::string_view window_name, QWidget* widget) +{ + const TinyString config_key = TinyString::from_format("{}Geometry", window_name); + const std::string geometry_b64 = Host::GetBaseStringSettingValue("UI", config_key); + if (geometry_b64.empty()) + return false; + + const QByteArray geometry = QByteArray::fromBase64(QByteArray::fromStdString(geometry_b64)); + widget->restoreGeometry(geometry); + + // make sure we're not loading a dodgy config which had fullscreen set... + widget->setWindowState(widget->windowState() & ~(Qt::WindowFullScreen | Qt::WindowActive)); + return true; +} diff --git a/src/duckstation-qt/qtutils.h b/src/duckstation-qt/qtutils.h index 64cf85d94..a031ee500 100644 --- a/src/duckstation-qt/qtutils.h +++ b/src/duckstation-qt/qtutils.h @@ -115,4 +115,10 @@ qreal GetDevicePixelRatioForWidget(const QWidget* widget); /// Returns the common window info structure for a Qt widget. std::optional GetWindowInfoForWidget(QWidget* widget); +/// Saves a window's geometry to configuration. Returns false if the configuration was changed. +bool SaveWindowGeometry(std::string_view window_name, QWidget* widget, bool auto_commit_changes = true); + +/// Restores a window's geometry from configuration. Returns false if it was not found in the configuration. +bool RestoreWindowGeometry(std::string_view window_name, QWidget* widget); + } // namespace QtUtils