diff --git a/src/dialog/vsettingsdialog.cpp b/src/dialog/vsettingsdialog.cpp index dd03eea5..275abb19 100644 --- a/src/dialog/vsettingsdialog.cpp +++ b/src/dialog/vsettingsdialog.cpp @@ -108,17 +108,28 @@ const QVector VGeneralTab::c_availableLangs = { "System", "English", "C VGeneralTab::VGeneralTab(QWidget *p_parent) : QWidget(p_parent) { - QLabel *langLabel = new QLabel(tr("&Language:")); + // Language combo. m_langCombo = new QComboBox(this); + m_langCombo->setToolTip(tr("Choose the language of VNote interface")); m_langCombo->addItem(tr("System"), "System"); auto langs = VUtils::getAvailableLanguages(); for (auto const &lang : langs) { m_langCombo->addItem(lang.second, lang.first); } - langLabel->setBuddy(m_langCombo); + + QLabel *langLabel = new QLabel(tr("Language:"), this); + langLabel->setToolTip(m_langCombo->toolTip()); + + // System tray checkbox. + m_systemTray = new QCheckBox(this); + m_systemTray->setToolTip(tr("Minimized to the system tray after closing VNote")); + + QLabel *trayLabel = new QLabel(tr("System tray:"), this); + trayLabel->setToolTip(m_systemTray->toolTip()); QFormLayout *optionLayout = new QFormLayout(); optionLayout->addRow(langLabel, m_langCombo); + optionLayout->addRow(trayLabel, m_systemTray); QVBoxLayout *mainLayout = new QVBoxLayout(); mainLayout->addLayout(optionLayout); @@ -131,6 +142,11 @@ bool VGeneralTab::loadConfiguration() if (!loadLanguage()) { return false; } + + if (!loadSystemTray()) { + return false; + } + return true; } @@ -139,6 +155,11 @@ bool VGeneralTab::saveConfiguration() if (!saveLanguage()) { return false; } + + if (!saveSystemTray()) { + return false; + } + return true; } @@ -174,6 +195,18 @@ bool VGeneralTab::saveLanguage() return true; } +bool VGeneralTab::loadSystemTray() +{ + m_systemTray->setChecked(vconfig.getMinimizeToStystemTray() != 0); + return true; +} + +bool VGeneralTab::saveSystemTray() +{ + vconfig.setMinimizeToSystemTray(m_systemTray->isChecked() ? 1 : 0); + return true; +} + VReadEditTab::VReadEditTab(QWidget *p_parent) : QWidget(p_parent) { diff --git a/src/dialog/vsettingsdialog.h b/src/dialog/vsettingsdialog.h index 9b195738..83536e21 100644 --- a/src/dialog/vsettingsdialog.h +++ b/src/dialog/vsettingsdialog.h @@ -25,9 +25,15 @@ private: bool loadLanguage(); bool saveLanguage(); + bool loadSystemTray(); + bool saveSystemTray(); + // Language QComboBox *m_langCombo; + // System tray + QCheckBox *m_systemTray; + static const QVector c_availableLangs; }; diff --git a/src/resources/vnote.ini b/src/resources/vnote.ini index a8a95226..a1d1fe53 100644 --- a/src/resources/vnote.ini +++ b/src/resources/vnote.ini @@ -60,6 +60,12 @@ enable_smart_im_in_vim_mode=true ; 0 - None, 1 - Absolute, 2 - Relative editor_line_number=0 +; Whether minimize to system tray when closing the app +; -1: uninitialized, prompt user for the behavior +; 0: do not minimize to system tray +; 1: minimize to system tray +minimize_to_system_tray=-1 + [session] tools_dock_checked=true diff --git a/src/vconfigmanager.cpp b/src/vconfigmanager.cpp index e4f9d508..b826a855 100644 --- a/src/vconfigmanager.cpp +++ b/src/vconfigmanager.cpp @@ -157,6 +157,12 @@ void VConfigManager::initialize() m_editorLineNumber = getConfigFromSettings("global", "editor_line_number").toInt(); + m_minimizeToSystemTray = getConfigFromSettings("global", + "minimize_to_system_tray").toInt(); + if (m_minimizeToSystemTray > 1 || m_minimizeToSystemTray < -1) { + setMinimizeToSystemTray(0); + } + readShortcutsFromSettings(); } diff --git a/src/vconfigmanager.h b/src/vconfigmanager.h index aceed6e3..6f2a2320 100644 --- a/src/vconfigmanager.h +++ b/src/vconfigmanager.h @@ -214,6 +214,9 @@ public: const QString &getEditorLineNumberBg() const; const QString &getEditorLineNumberFg() const; + int getMinimizeToStystemTray() const; + void setMinimizeToSystemTray(int p_val); + // Return the configured key sequence of @p_operation. // Return empty if there is no corresponding config. QString getShortcutKeySequence(const QString &p_operation) const; @@ -429,6 +432,12 @@ private: // Operation -> KeySequence. QHash m_shortcuts; + // Whether minimize to system tray icon when closing the app. + // -1: uninitialized; + // 0: do not minimize to the tay; + // 1: minimize to the tray. + int m_minimizeToSystemTray; + // The name of the config file in each directory, obsolete. // Use c_dirConfigFile instead. static const QString c_obsoleteDirConfigFile; @@ -1101,4 +1110,20 @@ inline const QString &VConfigManager::getEditorLineNumberFg() const return m_editorLineNumberFg; } +inline int VConfigManager::getMinimizeToStystemTray() const +{ + return m_minimizeToSystemTray; +} + +inline void VConfigManager::setMinimizeToSystemTray(int p_val) +{ + if (m_minimizeToSystemTray == p_val) { + return; + } + + m_minimizeToSystemTray = p_val; + setConfigToSettings("global", "minimize_to_system_tray", + m_minimizeToSystemTray); +} + #endif // VCONFIGMANAGER_H diff --git a/src/vmainwindow.cpp b/src/vmainwindow.cpp index 7d876880..be617715 100644 --- a/src/vmainwindow.cpp +++ b/src/vmainwindow.cpp @@ -38,7 +38,8 @@ extern QFile g_logFile; #endif VMainWindow::VMainWindow(VSingleInstanceGuard *p_guard, QWidget *p_parent) - : QMainWindow(p_parent), m_onePanel(false), m_guard(p_guard) + : QMainWindow(p_parent), m_onePanel(false), m_guard(p_guard), + m_windowOldState(Qt::WindowNoState), m_requestQuit(false) { setWindowIcon(QIcon(":/resources/icons/vnote.ico")); vnote = new VNote(this); @@ -148,6 +149,8 @@ void VMainWindow::setupUI() // Create and show the status bar statusBar()->addPermanentWidget(m_vimIndicator); statusBar()->addPermanentWidget(m_tabIndicator); + + initTrayIcon(); } QWidget *VMainWindow::setupDirectoryPanel() @@ -723,16 +726,22 @@ void VMainWindow::initFileMenu() fileMenu->addSeparator(); // Exit. - QAction *exitAct = new QAction(tr("&Exit"), this); - exitAct->setToolTip(tr("Exit VNote")); + QAction *exitAct = new QAction(tr("&Quit"), this); + exitAct->setToolTip(tr("Quit VNote")); exitAct->setShortcut(QKeySequence("Ctrl+Q")); exitAct->setMenuRole(QAction::QuitRole); connect(exitAct, &QAction::triggered, - this, &VMainWindow::close); + this, &VMainWindow::quitApp); fileMenu->addAction(exitAct); } +void VMainWindow::quitApp() +{ + m_requestQuit = true; + close(); +} + void VMainWindow::initEditMenu() { QMenu *editMenu = menuBar()->addMenu(tr("&Edit")); @@ -1527,13 +1536,42 @@ void VMainWindow::deleteCurNote() void VMainWindow::closeEvent(QCloseEvent *event) { - if (!editArea->closeAllFiles(false)) { - // Fail to close all the opened files, cancel closing app - event->ignore(); - return; + bool isExit = m_requestQuit || !vconfig.getMinimizeToStystemTray(); + m_requestQuit = false; + + if (!isExit && vconfig.getMinimizeToStystemTray() == -1) { + // Not initialized yet. Prompt for user. + int ret = VUtils::showMessage(QMessageBox::Information, + 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."), + QMessageBox::Ok | QMessageBox::No | QMessageBox::Cancel, + QMessageBox::Ok, + this); + if (ret == QMessageBox::Ok) { + vconfig.setMinimizeToSystemTray(1); + } else if (ret == QMessageBox::No) { + vconfig.setMinimizeToSystemTray(0); + isExit = true; + } else { + return; + } + } + + if (isExit) { + if (!editArea->closeAllFiles(false)) { + // Fail to close all the opened files, cancel closing app + event->ignore(); + return; + } + + saveStateAndGeometry(); + QMainWindow::closeEvent(event); + } else { + hide(); + event->ignore(); } - saveStateAndGeometry(); - QMainWindow::closeEvent(event); } void VMainWindow::saveStateAndGeometry() @@ -1868,8 +1906,58 @@ void VMainWindow::checkSharedMemory() qDebug() << "shared memory fetch files" << files; openExternalFiles(files); - show(); - activateWindow(); - QApplication::alert(this, 5000); + showMainWindow(); } } + +void VMainWindow::initTrayIcon() +{ + QMenu *menu = new QMenu(this); + QAction *showMainWindowAct = menu->addAction(tr("Show VNote")); + connect(showMainWindowAct, &QAction::triggered, + this, &VMainWindow::showMainWindow); + + QAction *exitAct = menu->addAction(tr("Quit")); + connect(exitAct, &QAction::triggered, + this, &VMainWindow::quitApp); + + m_trayIcon = new QSystemTrayIcon(QIcon(":/resources/icons/32x32/vnote.png"), this); + m_trayIcon->setToolTip(tr("VNote")); + m_trayIcon->setContextMenu(menu); + + connect(m_trayIcon, &QSystemTrayIcon::activated, + this, [this](QSystemTrayIcon::ActivationReason p_reason){ + if (p_reason == QSystemTrayIcon::Trigger) { + this->showMainWindow(); + } + }); + + m_trayIcon->show(); +} + +void VMainWindow::changeEvent(QEvent *p_event) +{ + if (p_event->type() == QEvent::WindowStateChange) { + QWindowStateChangeEvent *eve = dynamic_cast(p_event); + m_windowOldState = eve->oldState(); + } + + QMainWindow::changeEvent(p_event); +} + +void VMainWindow::showMainWindow() +{ + if (this->isMinimized()) { + if (m_windowOldState & Qt::WindowMaximized) { + this->showMaximized(); + } else if (m_windowOldState & Qt::WindowFullScreen) { + this->showFullScreen(); + } else { + this->showNormal(); + } + } else { + this->show(); + } + + this->activateWindow(); +} diff --git a/src/vmainwindow.h b/src/vmainwindow.h index 958bf1a6..23ba1934 100644 --- a/src/vmainwindow.h +++ b/src/vmainwindow.h @@ -33,6 +33,7 @@ class VVimIndicator; class VTabIndicator; class VSingleInstanceGuard; class QTimer; +class QSystemTrayIcon; class VMainWindow : public QMainWindow { @@ -109,11 +110,18 @@ private slots: // files to open. void checkSharedMemory(); + void quitApp(); + + // Restore main window. + void showMainWindow(); + protected: void closeEvent(QCloseEvent *event) Q_DECL_OVERRIDE; void resizeEvent(QResizeEvent *event) Q_DECL_OVERRIDE; void keyPressEvent(QKeyEvent *event) Q_DECL_OVERRIDE; + void changeEvent(QEvent *p_event) Q_DECL_OVERRIDE; + private: void setupUI(); QWidget *setupDirectoryPanel(); @@ -167,6 +175,9 @@ private: // VNote. void initSharedMemoryWatcher(); + // Init system tray icon and correspondign context menu. + void initTrayIcon(); + VNote *vnote; QPointer m_curFile; QPointer m_curTab; @@ -233,6 +244,15 @@ private: // Timer to check the shared memory between instances of VNote. QTimer *m_sharedMemTimer; + // Tray icon. + QSystemTrayIcon *m_trayIcon; + + // The old state of window. + Qt::WindowStates m_windowOldState; + + // Whether user request VNote to quit. + bool m_requestQuit; + // Interval of the shared memory timer in ms. static const int c_sharedMemTimerInterval; }; diff --git a/src/vnote.qrc b/src/vnote.qrc index c0075ff6..1d0d8a67 100644 --- a/src/vnote.qrc +++ b/src/vnote.qrc @@ -116,5 +116,6 @@ resources/icons/strikethrough.svg resources/icons/inline_code.svg resources/icons/close_note_tb.svg + resources/icons/32x32/vnote.png