From 1e1b757963f69203d437fc5973c109d68876f830 Mon Sep 17 00:00:00 2001 From: "J.D. Purcell" Date: Sat, 7 Dec 2024 19:35:50 -0500 Subject: [PATCH] Add "Window On Top" item in View menu --- i18n/qview_de.ts | 10 ++++++++++ src/actionmanager.cpp | 8 ++++++++ src/mainwindow.cpp | 37 +++++++++++++++++++++++++++++++++++-- src/mainwindow.h | 5 +++++ src/qvapplication.cpp | 14 +++++++++++--- src/qvapplication.h | 8 +++++--- src/qvoptionsdialog.cpp | 31 ++++++++++++++++++++----------- src/qvoptionsdialog.h | 2 ++ src/shortcutmanager.cpp | 1 + 9 files changed, 97 insertions(+), 19 deletions(-) diff --git a/i18n/qview_de.ts b/i18n/qview_de.ts index 2bd7a65d..f0db647e 100644 --- a/i18n/qview_de.ts +++ b/i18n/qview_de.ts @@ -198,6 +198,11 @@ Reset &Transformation &Transformation zurücksetzen + + + Window On To&p + Fenster im Vordergrund + Hide Title&bar @@ -1881,6 +1886,11 @@ Keine Schreibberechtigung oder Datei ist schreibgeschützt. Reset Transformation Transformation zurücksetzen + + + Window On Top + Fenster im Vordergrund + Toggle Titlebar Hidden diff --git a/src/actionmanager.cpp b/src/actionmanager.cpp index 02c48afb..53194622 100644 --- a/src/actionmanager.cpp +++ b/src/actionmanager.cpp @@ -281,6 +281,7 @@ QMenu *ActionManager::buildViewMenu(bool addIcon, QWidget *parent) addCloneOfAction(viewMenu, "flip"); addCloneOfAction(viewMenu, "resettransformation"); viewMenu->addSeparator(); + addCloneOfAction(viewMenu, "windowontop"); addCloneOfAction(viewMenu, "toggletitlebar"); addCloneOfAction(viewMenu, "fullscreen"); @@ -665,6 +666,8 @@ void ActionManager::actionTriggered(QAction *triggeredAction, MainWindow *releva relevantWindow->flip(); } else if (key == "resettransformation") { relevantWindow->resetTransformation(); + } else if (key == "windowontop") { + relevantWindow->toggleWindowOnTop(); } else if (key == "toggletitlebar") { relevantWindow->toggleTitlebarHidden(); } else if (key == "fullscreen") { @@ -823,6 +826,11 @@ void ActionManager::initializeActionLibrary() resetTransformationAction->setData({"disable"}); actionLibrary.insert("resettransformation", resetTransformationAction); + auto *windowOnTopAction = new QAction(tr("Window On To&p")); + windowOnTopAction->setData({"windowdisable"}); + windowOnTopAction->setCheckable(true); + actionLibrary.insert("windowontop", windowOnTopAction); + auto *toggleTitlebarAction = new QAction(tr("Hide Title&bar")); toggleTitlebarAction->setData({"windowdisable"}); actionLibrary.insert("toggletitlebar", toggleTitlebarAction); diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index d5584d77..00d9809c 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -639,6 +639,12 @@ void MainWindow::updateMenuBarVisible() menuBar()->setVisible(alwaysVisible || (menuBarEnabled && !(hideWhenImmersive && isImmersive()))); } +bool MainWindow::getWindowOnTop() const +{ + const QWindow *winHandle = windowHandle(); + return winHandle && winHandle->flags().testFlag(Qt::WindowStaysOnTopHint); +} + bool MainWindow::getTitlebarHidden() const { if (!windowHandle()) @@ -806,6 +812,8 @@ const QJsonObject MainWindow::getSessionState() const state["geometry"] = QString(saveGeometry().toBase64()); + state["windowOnTop"] = getWindowOnTop(); + state["titlebarHidden"] = getTitlebarHidden(); if (getCurrentFileDetails().isPixmapLoaded) @@ -827,6 +835,9 @@ void MainWindow::loadSessionState(const QJsonObject &state, const bool isInitial return; } + if (state["windowOnTop"].toBool() != getWindowOnTop()) + toggleWindowOnTop(); + if (state["titlebarHidden"].toBool() != getTitlebarHidden()) toggleTitlebarHidden(); @@ -1298,8 +1309,17 @@ void MainWindow::toggleSlideshow() slideshowAction->setText(isStarting ? tr("Stop S&lideshow") : tr("Start S&lideshow")); slideshowAction->setIcon(QIcon::fromTheme(isStarting ? "media-playback-stop" : "media-playback-start")); } - if (qvApp->getSlideshowKeepsWindowOnTop()) - windowHandle()->setFlag(Qt::WindowStaysOnTopHint, isStarting); + if (isStarting) + { + slideshowSetOnTopFlag = qvApp->getSettingsManager().getBoolean("slideshowkeepswindowontop") && !getWindowOnTop(); + if (slideshowSetOnTopFlag) + toggleWindowOnTop(); + } + else + { + if (slideshowSetOnTopFlag && getWindowOnTop()) + toggleWindowOnTop(); + } } void MainWindow::cancelSlideshow() @@ -1376,6 +1396,19 @@ void MainWindow::toggleFullScreen() setUpdatesEnabled(true); } +void MainWindow::toggleWindowOnTop() +{ + if (!windowHandle()) + return; + + const bool targetValue = !getWindowOnTop(); + windowHandle()->setFlag(Qt::WindowStaysOnTopHint, targetValue); + for (const auto &action : qvApp->getActionManager().getAllClonesOfAction("windowontop", this)) + action->setChecked(targetValue); + + emit qvApp->windowOnTopChanged(); +} + void MainWindow::toggleTitlebarHidden() { if (windowState().testFlag(Qt::WindowFullScreen)) diff --git a/src/mainwindow.h b/src/mainwindow.h index 8281b7c0..1731097b 100644 --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -41,6 +41,8 @@ class MainWindow : public QMainWindow void updateMenuBarVisible(); + bool getWindowOnTop() const; + bool getTitlebarHidden() const; void setTitlebarHidden(const bool shouldHide); @@ -129,6 +131,8 @@ class MainWindow : public QMainWindow void toggleFullScreen(); + void toggleWindowOnTop(); + void toggleTitlebarHidden(); int getTitlebarOverlap() const; @@ -201,6 +205,7 @@ protected slots: Qt::WindowStates storedWindowState {Qt::WindowNoState}; bool storedTitlebarHidden {false}; + bool slideshowSetOnTopFlag {false}; QNetworkAccessManager networkAccessManager; diff --git a/src/qvapplication.cpp b/src/qvapplication.cpp index 1cdb8b3a..27d5bb72 100644 --- a/src/qvapplication.cpp +++ b/src/qvapplication.cpp @@ -35,7 +35,6 @@ QVApplication::QVApplication(int &argc, char **argv) : QApplication(argc, argv) updateChecker.check(); showSubmenuIcons = getSettingsManager().getBoolean("submenuicons"); - slideshowKeepsWindowOnTop = getSettingsManager().getBoolean("slideshowkeepswindowontop"); // Block any erroneous icons from showing up on mac and windows // (this is overridden in some cases) @@ -225,7 +224,7 @@ void QVApplication::deleteFromActiveWindows(MainWindow *window) bool QVApplication::foundLoadedImage() const { - for (MainWindow *window : std::as_const(activeWindows)) + for (const MainWindow *window : activeWindows) { if (window->getIsPixmapLoaded()) return true; @@ -233,6 +232,16 @@ bool QVApplication::foundLoadedImage() const return false; } +bool QVApplication::foundOnTopWindow() const +{ + for (const MainWindow *window : activeWindows) + { + if (window->getWindowOnTop()) + return true; + } + return false; +} + void QVApplication::openOptionsDialog(QWidget *parent) { #ifdef Q_OS_MACOS @@ -240,7 +249,6 @@ void QVApplication::openOptionsDialog(QWidget *parent) parent = nullptr; #endif - if (optionsDialog) { optionsDialog->raise(); diff --git a/src/qvapplication.h b/src/qvapplication.h index 47de0299..59233418 100644 --- a/src/qvapplication.h +++ b/src/qvapplication.h @@ -57,6 +57,8 @@ class QVApplication : public QApplication bool foundLoadedImage() const; + bool foundOnTopWindow() const; + void openOptionsDialog(QWidget *parent = nullptr); void openWelcomeDialog(QWidget *parent = nullptr); @@ -92,8 +94,6 @@ class QVApplication : public QApplication bool getShowSubmenuIcons() const { return showSubmenuIcons; } - bool getSlideshowKeepsWindowOnTop() const { return slideshowKeepsWindowOnTop; } - void ensureFontLoaded(const QString &path); static QIcon iconFromFont(const QString &fontFamily, const QChar &codePoint, const int pixelSize, const qreal pixelRatio); @@ -116,6 +116,9 @@ class QVApplication : public QApplication void addClosedWindowSessionState(const QJsonObject &state, const qint64 lastActivatedTimestamp); +signals: + void windowOnTopChanged(); + protected slots: void onCommitDataRequest(QSessionManager &manager); @@ -145,7 +148,6 @@ protected slots: QPointer aboutDialog; bool showSubmenuIcons {true}; - bool slideshowKeepsWindowOnTop {false}; UpdateChecker updateChecker; diff --git a/src/qvoptionsdialog.cpp b/src/qvoptionsdialog.cpp index 207165de..0230f031 100644 --- a/src/qvoptionsdialog.cpp +++ b/src/qvoptionsdialog.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include @@ -27,7 +28,6 @@ QVOptionsDialog::QVOptionsDialog(QWidget *parent) : connect(ui->shortcutsTable, &QTableWidget::cellDoubleClicked, this, &QVOptionsDialog::shortcutCellDoubleClicked); connect(ui->bgColorCheckbox, &QCheckBox::stateChanged, this, &QVOptionsDialog::bgColorCheckboxStateChanged); connect(ui->submenuIconsCheckbox, &QCheckBox::stateChanged, this, [this](int state) { restartNotifyForCheckbox("submenuicons", state); }); - connect(ui->slideshowKeepsWindowOnTopCheckbox, &QCheckBox::stateChanged, this, [this](int state) { restartNotifyForCheckbox("slideshowkeepswindowontop", state); }); connect(ui->smoothScalingLimitCheckbox, &QCheckBox::stateChanged, this, &QVOptionsDialog::smoothScalingLimitCheckboxStateChanged); connect(ui->fitZoomLimitCheckbox, &QCheckBox::stateChanged, this, &QVOptionsDialog::fitZoomLimitCheckboxStateChanged); connect(ui->constrainImagePositionCheckbox, &QCheckBox::stateChanged, this, &QVOptionsDialog::constrainImagePositionCheckboxStateChanged); @@ -47,22 +47,17 @@ QVOptionsDialog::QVOptionsDialog(QWidget *parent) : populateComboBoxes(); populateLanguages(); + // Platform specific behaviors #ifdef Q_OS_MACOS - if (qvApp->getSlideshowKeepsWindowOnTop()) - setWindowModality(Qt::ApplicationModal); -#else - setWindowModality(Qt::WindowModal); -#endif - -#ifdef Q_OS_MACOS - // Load window geometry restoreGeometry(settings.value("optionsgeometry").toByteArray()); -#endif if (QOperatingSystemVersion::current() < QOperatingSystemVersion(QOperatingSystemVersion::MacOS, 13)) setWindowTitle(tr("Preferences")); +#else + setWindowModality(Qt::WindowModal); +#endif -// Platform specific settings + // Platform specific settings #ifdef Q_OS_MACOS ui->menubarCheckbox->hide(); #else @@ -105,6 +100,20 @@ void QVOptionsDialog::done(int r) QDialog::done(r); } +void QVOptionsDialog::showEvent(QShowEvent *event) +{ +#ifdef Q_OS_MACOS + // On macOS, we don't make this dialog modal, so make sure it doesn't get covered by on top windows + const auto updateWindowOnTop = [this]() { + windowHandle()->setFlag(Qt::WindowStaysOnTopHint, qvApp->foundOnTopWindow()); + }; + updateWindowOnTop(); + connect(qvApp, &QVApplication::windowOnTopChanged, this, updateWindowOnTop); +#endif + + QDialog::showEvent(event); +} + void QVOptionsDialog::changeEvent(QEvent *event) { if (event->type() == QEvent::PaletteChange) diff --git a/src/qvoptionsdialog.h b/src/qvoptionsdialog.h index dfe399cf..0701f355 100644 --- a/src/qvoptionsdialog.h +++ b/src/qvoptionsdialog.h @@ -30,6 +30,8 @@ class QVOptionsDialog : public QDialog protected: void done(int r) override; + void showEvent(QShowEvent *event) override; + void changeEvent(QEvent *event) override; void modifySetting(QString key, QVariant value); diff --git a/src/shortcutmanager.cpp b/src/shortcutmanager.cpp index 74435a39..f99e2622 100644 --- a/src/shortcutmanager.cpp +++ b/src/shortcutmanager.cpp @@ -94,6 +94,7 @@ void ShortcutManager::initializeShortcutsList() shortcutsList.append({tr("Mirror"), "mirror", QStringList(QKeySequence(Qt::Key_F).toString()), {}}); shortcutsList.append({tr("Flip"), "flip", QStringList(QKeySequence(Qt::CTRL | Qt::Key_F).toString()), {}}); shortcutsList.append({tr("Reset Transformation"), "resettransformation", QStringList(QKeySequence(Qt::Key_T).toString()), {}}); + shortcutsList.append({tr("Window On Top"), "windowontop", {}, {}}); shortcutsList.append({tr("Toggle Titlebar Hidden"), "toggletitlebar", {}, {}}); shortcutsList.append({tr("Full Screen"), "fullscreen", keyBindingsToStringList(QKeySequence::FullScreen), {}}); //Fixes alt+enter only working with numpad enter when using qt's standard keybinds