瀏覽代碼

minimize to system tray (#1601)

jachin 4 年之前
父節點
當前提交
cb14461f58

+ 16 - 0
src/core/coreconfig.cpp

@@ -7,6 +7,7 @@ using namespace vnotex;
 
 #define READSTR(key) readString(appObj, userObj, (key))
 #define READINT(key) readInt(appObj, userObj, (key))
+#define READBOOL(key) readBool(appObj, userObj, (key))
 
 QStringList CoreConfig::s_availableLocales;
 
@@ -40,6 +41,10 @@ void CoreConfig::init(const QJsonObject &p_app,
     if (m_toolBarIconSize <= 0) {
         m_toolBarIconSize = 16;
     }
+
+    if (!isUndefinedKey(appObj, userObj, "minimize_to_system_tray")) {
+        m_minimizeToSystemTray = READBOOL(QStringLiteral("minimize_to_system_tray")) ? 1 : 0;
+    }
 }
 
 QJsonObject CoreConfig::toJson() const
@@ -49,6 +54,9 @@ QJsonObject CoreConfig::toJson() const
     obj[QStringLiteral("locale")] = m_locale;
     obj[QStringLiteral("shortcuts")] = saveShortcuts();
     obj[QStringLiteral("toolbar_icon_size")] = m_toolBarIconSize;
+    if (m_minimizeToSystemTray != -1) {
+        obj[QStringLiteral("minimize_to_system_tray")] = m_minimizeToSystemTray > 0;
+    }
     return obj;
 }
 
@@ -122,3 +130,11 @@ void CoreConfig::setToolBarIconSize(int p_size)
     Q_ASSERT(p_size > 0);
     updateConfig(m_toolBarIconSize, p_size, this);
 }
+
+int CoreConfig::getMinimizeToSystemTray() const {
+    return m_minimizeToSystemTray;
+}
+
+void CoreConfig::setMinimizeToSystemTray(bool state){
+    updateConfig(m_minimizeToSystemTray, int(state), this);
+}

+ 9 - 0
src/core/coreconfig.h

@@ -48,6 +48,9 @@ namespace vnotex
         int getToolBarIconSize() const;
         void setToolBarIconSize(int p_size);
 
+        int getMinimizeToSystemTray() const;
+        void setMinimizeToSystemTray(bool state);
+
         static const QStringList &getAvailableLocales();
 
     private:
@@ -67,6 +70,12 @@ namespace vnotex
         // Icon size of MainWindow tool bar.
         int m_toolBarIconSize = 16;
 
+        // Whether to minimize to tray.
+        // -1 for prompting for user;
+        // 0 for disabling minimizing to system tray;
+        // 1 for enabling minimizing to system tray;
+        int m_minimizeToSystemTray = -1;
+
         static QStringList s_availableLocales;
     };
 } // ns vnotex

+ 6 - 0
src/core/editorconfig.cpp

@@ -131,6 +131,12 @@ int EditorConfig::getToolBarIconSize() const
     return m_toolBarIconSize;
 }
 
+void EditorConfig::setToolBarIconSize(int p_size)
+{
+    Q_ASSERT(p_size > 0);
+    updateConfig(m_toolBarIconSize, p_size, this);
+}
+
 const QString &EditorConfig::getShortcut(Shortcut p_shortcut) const
 {
     Q_ASSERT(p_shortcut < Shortcut::MaxShortcut);

+ 1 - 0
src/core/editorconfig.h

@@ -75,6 +75,7 @@ namespace vnotex
         QJsonObject toJson() const Q_DECL_OVERRIDE;
 
         int getToolBarIconSize() const;
+        void setToolBarIconSize(int p_size);
 
         EditorConfig::AutoSavePolicy getAutoSavePolicy() const;
         void setAutoSavePolicy(EditorConfig::AutoSavePolicy p_policy);

+ 10 - 0
src/core/iconfig.h

@@ -126,6 +126,16 @@ namespace vnotex
             return read(p_default, p_user, p_key).toDouble();
         }
 
+        static bool isUndefinedKey(const QJsonObject &p_default,
+                                   const QJsonObject &p_user,
+                                   const QString &p_key)
+        {
+            if (p_user.find(p_key) == p_user.end() && p_default.find(p_key) == p_default.end()) {
+                return true;
+            }
+            return false;
+        }
+
         template <typename T>
         static void updateConfig(T &p_cur,
                                  const T &p_new,

+ 2 - 0
src/core/singleinstanceguard.cpp

@@ -23,6 +23,8 @@ bool SingleInstanceGuard::tryRun()
     // this will attach to the old segment, then exit, freeing the old segment.
     if (m_sharedMemory.attach()) {
         qInfo() << "another instance is running";
+        // So try to show it?
+        showInstance();
         return false;
     }
 

+ 3 - 3
src/core/vnotex.cpp

@@ -1,6 +1,7 @@
 #include "vnotex.h"
 
 #include <QDateTime>
+#include <QRandomGenerator>
 
 #include <widgets/mainwindow.h>
 #include "notebookmgr.h"
@@ -12,6 +13,7 @@
 
 #include <utils/docsutils.h>
 
+
 using namespace vnotex;
 
 VNoteX::VNoteX(QObject *p_parent)
@@ -19,9 +21,7 @@ VNoteX::VNoteX(QObject *p_parent)
       m_mainWindow(nullptr),
       m_notebookMgr(nullptr)
 {
-    qsrand(QDateTime::currentDateTime().toTime_t());
-
-    m_instanceId = qrand();
+    m_instanceId = QRandomGenerator::global()->generate64();
 
     initThemeMgr();
 

+ 9 - 1
src/main.cpp

@@ -21,6 +21,7 @@
 #include <core/exception.h>
 #include <widgets/messageboxhelper.h>
 
+#include <QProcess>
 
 using namespace vnotex;
 
@@ -119,7 +120,7 @@ int main(int argc, char *argv[])
 
     loadTranslators(app);
 
-    MainWindow window;
+    MainWindow window(nullptr);
 
     window.show();
     VNoteX::getInst().getThemeMgr().setBaseBackground(window.palette().color(QPalette::Base));
@@ -127,6 +128,13 @@ int main(int argc, char *argv[])
     window.kickOffOnStart();
 
     int ret = app.exec();
+    if (ret == RESTART_EXIT_CODE) {
+        // Ask to restart VNote.
+        guard.exit();
+        QProcess::startDetached(qApp->applicationFilePath(), QStringList());
+        return 0;
+    }
+
     return ret;
 }
 

+ 1 - 0
src/src.pro

@@ -5,6 +5,7 @@ equals(QT_MAJOR_VERSION, 5):lessThan(QT_MINOR_VERSION, 12): error("requires Qt 5
 QT += core gui widgets webenginewidgets webchannel network svg printsupport
 
 CONFIG -= qtquickcompiler
+unix:!macx:CONFIG += use_gold_linker
 
 # Enable message log in release build
 DEFINES += QT_MESSAGELOGCONTEXT

+ 20 - 0
src/widgets/dialogs/settings/editorpage.cpp

@@ -3,6 +3,7 @@
 #include <QComboBox>
 #include <QFormLayout>
 #include <QTimer>
+#include <QSpinBox>
 
 #include <widgets/widgetsfactory.h>
 #include <core/editorconfig.h>
@@ -34,6 +35,21 @@ void EditorPage::setupUI()
         connect(m_autoSavePolicyComboBox, QOverload<int>::of(&QComboBox::currentIndexChanged),
                 this, &EditorPage::pageIsChanged);
     }
+
+    {
+        m_toolBarIconSizeSpinBox = WidgetsFactory::createSpinBox(this);
+        m_toolBarIconSizeSpinBox->setToolTip(tr("Icon size of the editor tool bar"));
+
+        m_toolBarIconSizeSpinBox->setRange(1, 48);
+        m_toolBarIconSizeSpinBox->setSingleStep(1);
+
+        const QString label(tr("Toolbar icon size:"));
+        mainLayout->addRow(label, m_toolBarIconSizeSpinBox);
+        addSearchItem(label, m_toolBarIconSizeSpinBox->toolTip(), m_toolBarIconSizeSpinBox);
+        connect(m_toolBarIconSizeSpinBox, QOverload<int>::of(&QSpinBox::valueChanged),
+                this, &EditorPage::pageIsChanged);
+
+    }
 }
 
 void EditorPage::loadInternal()
@@ -45,6 +61,8 @@ void EditorPage::loadInternal()
         Q_ASSERT(idx != -1);
         m_autoSavePolicyComboBox->setCurrentIndex(idx);
     }
+
+    m_toolBarIconSizeSpinBox->setValue(editorConfig.getToolBarIconSize());
 }
 
 void EditorPage::saveInternal()
@@ -56,6 +74,8 @@ void EditorPage::saveInternal()
         editorConfig.setAutoSavePolicy(static_cast<EditorConfig::AutoSavePolicy>(policy));
     }
 
+    editorConfig.setToolBarIconSize(m_toolBarIconSizeSpinBox->value());
+
     notifyEditorConfigChange();
 }
 

+ 2 - 0
src/widgets/dialogs/settings/editorpage.h

@@ -4,6 +4,7 @@
 #include "settingspage.h"
 
 class QComboBox;
+class QSpinBox;
 
 namespace vnotex
 {
@@ -27,6 +28,7 @@ namespace vnotex
         void setupUI();
 
         QComboBox *m_autoSavePolicyComboBox = nullptr;
+        QSpinBox *m_toolBarIconSizeSpinBox = nullptr;
     };
 }
 

+ 22 - 0
src/widgets/dialogs/settings/generalpage.cpp

@@ -2,6 +2,7 @@
 
 #include <QComboBox>
 #include <QFormLayout>
+#include <QCheckBox>
 
 #include <widgets/widgetsfactory.h>
 #include <core/coreconfig.h>
@@ -55,6 +56,17 @@ void GeneralPage::setupUI()
                 this, &GeneralPage::pageIsChanged);
     }
 #endif
+
+#if not defined(Q_OS_MACOS)
+    {
+        m_systemTrayCheckBox = WidgetsFactory::createCheckBox("System tray");
+        mainLayout->addRow(m_systemTrayCheckBox);
+
+        connect(m_systemTrayCheckBox, &QCheckBox::stateChanged,
+                this, &GeneralPage::pageIsChanged);
+    }
+#endif
+
 }
 
 void GeneralPage::loadInternal()
@@ -73,6 +85,12 @@ void GeneralPage::loadInternal()
         Q_ASSERT(idx != -1);
         m_openGLComboBox->setCurrentIndex(idx);
     }
+
+    if(m_systemTrayCheckBox){
+        auto toTray = coreConfig.getMinimizeToSystemTray();
+        if(toTray)
+            m_systemTrayCheckBox->setChecked(true);
+    }
 }
 
 void GeneralPage::saveInternal()
@@ -89,6 +107,10 @@ void GeneralPage::saveInternal()
         int opt = m_openGLComboBox->currentData().toInt();
         sessionConfig.setOpenGL(static_cast<SessionConfig::OpenGL>(opt));
     }
+
+    if(m_systemTrayCheckBox) {
+        coreConfig.setMinimizeToSystemTray(m_systemTrayCheckBox->isChecked());
+    }
 }
 
 QString GeneralPage::title() const

+ 4 - 0
src/widgets/dialogs/settings/generalpage.h

@@ -4,6 +4,7 @@
 #include "settingspage.h"
 
 class QComboBox;
+class QCheckBox;
 
 namespace vnotex
 {
@@ -26,6 +27,9 @@ namespace vnotex
         QComboBox *m_localeComboBox = nullptr;
 
         QComboBox *m_openGLComboBox = nullptr;
+
+        QCheckBox *m_systemTrayCheckBox = nullptr;
+
     };
 }
 

+ 97 - 8
src/widgets/mainwindow.cpp

@@ -15,6 +15,9 @@
 #include <QCoreApplication>
 #include <QApplication>
 #include <QShortcut>
+#include <QSystemTrayIcon>
+#include <QMenu>
+#include <QIcon>
 
 #include "toolbox.h"
 #include "notebookexplorer.h"
@@ -32,6 +35,7 @@
 #include "outlineviewer.h"
 #include <utils/widgetutils.h>
 #include "navigationmodemgr.h"
+#include <widgets/messageboxhelper.h>
 
 #include <vtoolbar.h>
 
@@ -46,6 +50,8 @@ MainWindow::MainWindow(QWidget *p_parent)
 
     setupUI();
 
+    initSystemTrayIcon();
+
     setupShortcuts();
 
     loadStateAndGeometry();
@@ -54,6 +60,9 @@ MainWindow::MainWindow(QWidget *p_parent)
     QApplication::setQuitOnLastWindowClosed(false);
 #endif
 
+    // The signal is particularly useful if your application has
+    // to do some last-second cleanup.
+    // Note that no user interaction is possible in this state.
     connect(qApp, &QCoreApplication::aboutToQuit,
             this, &MainWindow::closeOnQuit);
 }
@@ -282,21 +291,61 @@ void MainWindow::closeEvent(QCloseEvent *p_event)
 {
     // TODO: support minimized to system tray.
 
+    auto toTray = ConfigMgr::getInst().getCoreConfig().getMinimizeToSystemTray();
+    bool isExit = m_requestQuit;
+    m_requestQuit = 0;
+
     if (isVisible()) {
         saveStateAndGeometry();
     }
 
-    // Signal out the close event.
-    auto event = QSharedPointer<Event>::create();
-    event->m_response = true;
-    emit mainWindowClosed(event);
-    if (!event->m_response.toBool()) {
-        // Stop the close.
-        p_event->ignore();
+#if defined(Q_OS_MACOS) || defined(Q_OS_MAC)
+    // Do not support minimized to tray on macOS.
+    if (!isExit) {
+        p_event->accept();
         return;
     }
+#endif
+
+    if(!isExit && toTray == -1){
+        int ret =  MessageBoxHelper::questionYesNo(MessageBoxHelper::Question,
+                                                   tr("Close VNote"),
+                                                   tr("Do you want to minimize VNote to system tray "
+                                                      "instead of quitting it when closing VNote?"),
+                                                   tr("You could change the option in Settings later."),
+                                                   this);
+        if (ret == QMessageBox::Yes) {
+            ConfigMgr::getInst().getCoreConfig().setMinimizeToSystemTray(true);
+            hide();
+        } else if (ret == QMessageBox::No) {
+            ConfigMgr::getInst().getCoreConfig().setMinimizeToSystemTray(false);
+            isExit = true;
+        } else {
+            p_event->ignore();
+            return;
+        }
+    }
+
+    if(isExit || toTray == 0 || !m_trayIcon->isVisible()){
+        // really to quit, process workspace
+        // TODO: process workspace
+
+        // Signal out the close event.
+        auto event = QSharedPointer<Event>::create();
+        event->m_response = true;
+        emit mainWindowClosed(event);
+        if (!event->m_response.toBool()) {
+            // Stop the close.
+            p_event->ignore();
+            return;
+        }
 
-    QMainWindow::closeEvent(p_event);
+        QMainWindow::closeEvent(p_event);
+        qApp->quit();
+    }else {
+        hide();
+        p_event->ignore();
+    }
 }
 
 void MainWindow::saveStateAndGeometry()
@@ -485,3 +534,43 @@ void MainWindow::setStayOnTop(bool p_enabled)
         show();
     }
 }
+
+void MainWindow::initSystemTrayIcon(){
+    QMenu *menu = new QMenu(this);
+    QAction *showMainWindowAct = menu->addAction(tr("Show VNote"));
+    connect(showMainWindowAct, &QAction::triggered,
+            this, &MainWindow::show);
+
+    QAction *exitAct = menu->addAction(tr("Quit"));
+    connect(exitAct, &QAction::triggered,
+            this, [this](){
+                this->m_requestQuit = 1;
+                this->close();
+                });
+
+    QIcon sysIcon(":/vnotex/data/core/logo/vnote.png");
+
+#if defined(Q_OS_MACOS) || defined(Q_OS_MAC)
+    sysIcon.setIsMask(true);
+#endif
+
+    m_trayIcon = new QSystemTrayIcon(sysIcon, this);
+    m_trayIcon->setToolTip(tr("VNote"));
+    m_trayIcon->setContextMenu(menu);
+
+    connect(m_trayIcon, &QSystemTrayIcon::activated,
+            this, [this](QSystemTrayIcon::ActivationReason p_reason){
+#if !defined(Q_OS_MACOS) && !defined(Q_OS_MAC)
+                if (p_reason == QSystemTrayIcon::Trigger) {
+                    this->show();
+                    this->activateWindow();
+                }
+#endif
+            });
+
+    m_trayIcon->show();
+}
+
+void MainWindow::restart(){
+    QCoreApplication::exit(RESTART_EXIT_CODE);
+}

+ 14 - 0
src/widgets/mainwindow.h

@@ -7,7 +7,10 @@
 #include "toolbarhelper.h"
 #include "statusbarhelper.h"
 
+#define RESTART_EXIT_CODE   1000
+
 class QDockWidget;
+class QSystemTrayIcon;
 
 namespace vnotex
 {
@@ -42,6 +45,8 @@ namespace vnotex
 
         void setStayOnTop(bool p_enabled);
 
+        void restart();
+
     signals:
         void mainWindowStarted();
 
@@ -100,6 +105,14 @@ namespace vnotex
 
         void setupShortcuts();
 
+        // Init system tray and correspondign context menu.
+        void initSystemTrayIcon();
+
+        // Tray icon.
+        QSystemTrayIcon *m_trayIcon;
+
+        bool m_requestQuit = false;
+
         ToolBarHelper m_toolBarHelper;
 
         StatusBarHelper m_statusBarHelper;
@@ -117,6 +130,7 @@ namespace vnotex
         QVector<QDockWidget *> m_docks;
 
         bool m_layoutReset = false;
+        
     };
 } // ns vnotex
 

+ 8 - 0
src/widgets/toolbarhelper.cpp

@@ -305,6 +305,14 @@ QToolBar *ToolBarHelper::setupSettingsToolBar(MainWindow *p_win, QToolBar *p_too
                         [p_win]() {
                             p_win->resetStateAndGeometry();
                         });
+
+        menu->addSeparator();
+
+        menu->addAction(MainWindow::tr("Restart"),
+                        menu,
+                        [p_win]() {
+                            p_win->restart();
+                        });
     }
 
     // Help.

+ 1 - 0
src/widgets/viewarea.h

@@ -13,6 +13,7 @@
 
 class QLayout;
 class QSplitter;
+class QTimer;
 
 namespace vnotex
 {

+ 1 - 1
src/widgets/viewwindow.cpp

@@ -410,7 +410,7 @@ QAction *ViewWindow::addAction(QToolBar *p_toolBar, ViewWindowToolBarHelper::Act
         act = ViewWindowToolBarHelper::addAction(p_toolBar, p_action);
         connect(act, &QAction::triggered,
                 this, [this]() {
-                    if (m_findAndReplace && m_findAndReplace->isVisible()) {
+                    if (findAndReplaceWidgetVisible()) {
                         hideFindAndReplaceWidget();
                     } else {
                         showFindAndReplaceWidget();