diff --git a/YACReaderLibrary/YACReaderLibrary.pro b/YACReaderLibrary/YACReaderLibrary.pro index f8460faf..b39293fc 100644 --- a/YACReaderLibrary/YACReaderLibrary.pro +++ b/YACReaderLibrary/YACReaderLibrary.pro @@ -115,6 +115,7 @@ HEADERS += comic_flow.h \ ../common/pdf_comic.h \ no_libraries_widget.h \ import_widget.h \ + trayicon_controller.h \ yacreader_local_server.h \ yacreader_main_toolbar.h \ comics_remover.h \ @@ -184,6 +185,7 @@ SOURCES += comic_flow.cpp \ ../common/onstart_flow_selection_dialog.cpp \ no_libraries_widget.cpp \ import_widget.cpp \ + trayicon_controller.cpp \ yacreader_local_server.cpp \ yacreader_main_toolbar.cpp \ comics_remover.cpp \ @@ -221,6 +223,11 @@ SOURCES += comic_flow.cpp \ SOURCES += ../common/gl/yacreader_flow_gl.cpp } +macx { + HEADERS += trayhandler.h + OBJECTIVE_SOURCES += trayhandler.mm +} + include(./server/server.pri) include(../custom_widgets/custom_widgets_yacreaderlibrary.pri) diff --git a/YACReaderLibrary/images_osx.qrc b/YACReaderLibrary/images_osx.qrc index 14398e66..60489bfb 100644 --- a/YACReaderLibrary/images_osx.qrc +++ b/YACReaderLibrary/images_osx.qrc @@ -71,5 +71,6 @@ ../images/sidebar/delete_sidebar_osx@2x.png ../images/sidebar/addLabelIcon_osx@2x.png ../images/sidebar/renameListIcon_osx@2x.png + macostrayicon.svg diff --git a/YACReaderLibrary/images_win.qrc b/YACReaderLibrary/images_win.qrc index fdb1ea0b..cb87e0cf 100644 --- a/YACReaderLibrary/images_win.qrc +++ b/YACReaderLibrary/images_win.qrc @@ -41,5 +41,6 @@ ../images/lists/label_yellow.png ../images/lists/list.png ../images/empty_reading_list.png + icon.ico diff --git a/YACReaderLibrary/library_window.cpp b/YACReaderLibrary/library_window.cpp index eae3f19e..7d6c8967 100644 --- a/YACReaderLibrary/library_window.cpp +++ b/YACReaderLibrary/library_window.cpp @@ -81,12 +81,16 @@ #include "yacreader_comics_views_manager.h" +#include "trayicon_controller.h" + #include "QsLog.h" #ifdef Q_OS_WIN #include #endif +using namespace YACReader; + LibraryWindow::LibraryWindow() : QMainWindow(), fullscreen(false), previousFilter(""), fetching(false), status(LibraryWindow::Normal), removeError(false) { @@ -136,37 +140,26 @@ void LibraryWindow::setupUI() else //if(settings->value(USE_OPEN_GL).toBool() == false) showMaximized(); -} -/* //disabled until icons are ready and macos native code is done - trayIcon.setIcon(QApplication::windowIcon()); - trayIcon.show(); - connect(&trayIcon, SIGNAL(activated(QSystemTrayIcon::ActivationReason)), - this, SLOT(trayActivation(QSystemTrayIcon::ActivationReason))); - } + + trayIconController = new TrayIconController(settings, this); } -void LibraryWindow::trayActivation(QSystemTrayIcon::ActivationReason reason) +/*void LibraryWindow::changeEvent(QEvent *event) { - if (reason == QSystemTrayIcon::Trigger) - { - setWindowState((windowState() & ~Qt::WindowMinimized)); - show(); - activateWindow(); - raise(); - } -} - - -void LibraryWindow::changeEvent(QEvent *event) -{ - if (event->type() == QEvent::WindowStateChange && isMinimized()) - { - hide(); - } - else - { QMainWindow::changeEvent(event); - } + + if (event->type() == QEvent::WindowStateChange && isMinimized() && + trayIcon.isVisible()) { +#ifdef Q_OS_MACOS + OSXHideDockIcon(); +#endif + hide(); + } else if (event->type() == QEvent::WindowStateChange) { +#ifdef Q_OS_MACOS + OSXShowDockIcon(); +#endif + show(); + } }*/ void LibraryWindow::doLayout() @@ -2281,8 +2274,9 @@ void LibraryWindow::importLibrary(QString clc, QString destPath, QString name) void LibraryWindow::reloadOptions() { - //comicFlow->setFlowType(flowType); comicsViewsManager->comicsView->updateConfig(settings); + + trayIconController->updateIconVisibility(); } QString LibraryWindow::currentPath() @@ -2318,6 +2312,14 @@ void LibraryWindow::showImportComicsInfo() #include "startup.h" extern Startup *s; void LibraryWindow::closeEvent(QCloseEvent *event) +{ + if (!trayIconController->handleCloseToTrayIcon(event)) { + event->accept(); + closeApp(); + } +} + +void LibraryWindow::prepareToCloseApp() { s->stop(); settings->setValue(MAIN_WINDOW_GEOMETRY, saveGeometry()); @@ -2326,8 +2328,13 @@ void LibraryWindow::closeEvent(QCloseEvent *event) sideBar->close(); QApplication::instance()->processEvents(); - event->accept(); - QMainWindow::closeEvent(event); +} + +void LibraryWindow::closeApp() +{ + prepareToCloseApp(); + + qApp->exit(0); } void LibraryWindow::showNoLibrariesWidget() @@ -2506,14 +2513,14 @@ void LibraryWindow::showFoldersContextMenu(const QPoint &point) /* void LibraryWindow::showSocial() { - socialDialog->move(this->mapToGlobal(QPoint(width()-socialDialog->width()-10, centralWidget()->pos().y()+10))); + socialDialog->move(this->mapToGlobal(QPoint(width()-socialDialog->width()-10, centralWidget()->pos().y()+10))); - QModelIndexList indexList = getSelectedComics(); + QModelIndexList indexList = getSelectedComics(); - ComicDB comic = dmCV->getComic(indexList.at(0)); + ComicDB comic = dmCV->getComic(indexList.at(0)); - socialDialog->setComic(comic,currentPath()); - socialDialog->setHidden(false); + socialDialog->setComic(comic,currentPath()); + socialDialog->setHidden(false); }*/ void LibraryWindow::libraryAlreadyExists(const QString &name) diff --git a/YACReaderLibrary/library_window.h b/YACReaderLibrary/library_window.h index 1f2bc896..993b58b9 100644 --- a/YACReaderLibrary/library_window.h +++ b/YACReaderLibrary/library_window.h @@ -5,7 +5,6 @@ #include #include #include -#include #include "yacreader_global_gui.h" #include "yacreader_libraries.h" @@ -77,6 +76,10 @@ class EmptySpecialListWidget; class EmptyReadingListWidget; class YACReaderComicsViewsManager; +namespace YACReader { +class TrayIconController; +} + #include "comic_db.h" using namespace YACReader; @@ -385,7 +388,8 @@ public slots: void checkMaxNumLibraries(); void showErrorUpgradingLibrary(const QString &path); - //void changeEvent(QEvent *event); + void prepareToCloseApp(); + void closeApp(); private: //fullscreen mode in Windows for preventing this bug: QTBUG-41309 https://bugreports.qt.io/browse/QTBUG-41309 @@ -393,10 +397,8 @@ private: QPoint previousPos; QSize previousSize; std::future upgradeLibraryFuture; - QSystemTrayIcon trayIcon; -private slots: - //void trayActivation(QSystemTrayIcon::ActivationReason reason); + TrayIconController *trayIconController; }; #endif diff --git a/YACReaderLibrary/macostrayicon.svg b/YACReaderLibrary/macostrayicon.svg new file mode 100644 index 00000000..03d3446f --- /dev/null +++ b/YACReaderLibrary/macostrayicon.svg @@ -0,0 +1 @@ +macostrayicon \ No newline at end of file diff --git a/YACReaderLibrary/main.cpp b/YACReaderLibrary/main.cpp index 8501179d..62f68584 100644 --- a/YACReaderLibrary/main.cpp +++ b/YACReaderLibrary/main.cpp @@ -20,6 +20,9 @@ #include "yacreader_libraries.h" #include "exit_check.h" #include "opengl_checker.h" +#ifdef Q_OS_MACOS +#include "trayhandler.h" +#endif #include "QsLog.h" #include "QsLogDest.h" @@ -127,9 +130,12 @@ int main(int argc, char **argv) app.setApplicationVersion(VERSION); app.setAttribute(Qt::AA_UseHighDpiPixmaps); + // Set window icon according to Freedesktop icon specification + // This is mostly relevant for Linux and other Unix systems if (QIcon::hasThemeIcon("YACReaderLibrary")) { app.setWindowIcon(QIcon::fromTheme("YACReaderLibrary")); } + // TODO: We might want to add a fallback icon here. QString destLog = YACReader::getSettingsPath() + "/yacreaderlibrary.log"; QDir().mkpath(YACReader::getSettingsPath()); @@ -239,7 +245,15 @@ int main(int argc, char **argv) //connections to localServer - mw->show(); + // start as tray + if (!settings->value(START_TO_TRAY, false).toBool() || !settings->value(CLOSE_TO_TRAY, false).toBool()) { + mw->show(); + } +#ifdef Q_OS_MACOS + else { + OSXHideDockIcon(); + } +#endif int ret = app.exec(); diff --git a/YACReaderLibrary/options_dialog.cpp b/YACReaderLibrary/options_dialog.cpp index 5667ff73..432273a8 100644 --- a/YACReaderLibrary/options_dialog.cpp +++ b/YACReaderLibrary/options_dialog.cpp @@ -43,6 +43,27 @@ OptionsDialog::OptionsDialog(QWidget *parent) #ifndef NO_OPENGL sw->hide(); #endif + // Tray icon settings + QGroupBox *trayIconBox = new QGroupBox(tr("Tray icon settings (experimental)")); + QVBoxLayout *trayLayout = new QVBoxLayout(); + + trayIconCheckbox = new QCheckBox(tr("Close to tray")); + startToTrayCheckbox = new QCheckBox(tr("Start into the system tray")); + + connect(trayIconCheckbox, &QCheckBox::clicked, + [=](bool checked) { + settings->setValue(CLOSE_TO_TRAY, checked); + startToTrayCheckbox->setEnabled(checked); + emit optionsChanged(); + }); + connect(startToTrayCheckbox, &QCheckBox::clicked, + [=](bool checked) { + settings->setValue(START_TO_TRAY, checked); + }); + + trayLayout->addWidget(trayIconCheckbox); + trayLayout->addWidget(startToTrayCheckbox); + trayIconBox->setLayout(trayLayout); auto apiKeyLayout = new QVBoxLayout(); auto apiKeyButton = new QPushButton(tr("Edit Comic Vine API key")); @@ -115,6 +136,7 @@ OptionsDialog::OptionsDialog(QWidget *parent) auto generalW = new QWidget; generalW->setLayout(generalLayout); + generalLayout->addWidget(trayIconBox); generalLayout->addWidget(shortcutsBox); generalLayout->addWidget(apiKeyBox); generalLayout->addStretch(); @@ -146,6 +168,10 @@ void OptionsDialog::restoreOptions(QSettings *settings) { YACReaderOptionsDialog::restoreOptions(settings); + trayIconCheckbox->setChecked(settings->value(CLOSE_TO_TRAY, false).toBool()); + startToTrayCheckbox->setChecked(settings->value(START_TO_TRAY, false).toBool()); + startToTrayCheckbox->setEnabled(trayIconCheckbox->isChecked()); + bool useBackgroundImage = settings->value(USE_BACKGROUND_IMAGE_IN_GRID_VIEW, true).toBool(); useBackgroundImageCheck->setChecked(useBackgroundImage); diff --git a/YACReaderLibrary/options_dialog.h b/YACReaderLibrary/options_dialog.h index 20c542a1..7ecc8511 100644 --- a/YACReaderLibrary/options_dialog.h +++ b/YACReaderLibrary/options_dialog.h @@ -34,8 +34,9 @@ private: QLabel *opacityLabel; QLabel *blurLabel; QPushButton *resetButton; - QCheckBox *displayContinueReadingBannerCheck; + QCheckBox *trayIconCheckbox; + QCheckBox *startToTrayCheckbox; }; #endif diff --git a/YACReaderLibrary/trayhandler.h b/YACReaderLibrary/trayhandler.h new file mode 100644 index 00000000..ce898a0e --- /dev/null +++ b/YACReaderLibrary/trayhandler.h @@ -0,0 +1,7 @@ +#ifndef TRAY_HANDLER +#define TRAY_HANDLER + +void OSXShowDockIcon(); +void OSXHideDockIcon(); + +#endif \ No newline at end of file diff --git a/YACReaderLibrary/trayhandler.mm b/YACReaderLibrary/trayhandler.mm new file mode 100644 index 00000000..9c3cce37 --- /dev/null +++ b/YACReaderLibrary/trayhandler.mm @@ -0,0 +1,11 @@ +#import +#include "trayhandler.h" + +void OSXShowDockIcon() +{ + [NSApp setActivationPolicy:NSApplicationActivationPolicyRegular]; +} +void OSXHideDockIcon() +{ + [NSApp setActivationPolicy:NSApplicationActivationPolicyAccessory]; +} \ No newline at end of file diff --git a/YACReaderLibrary/trayicon_controller.cpp b/YACReaderLibrary/trayicon_controller.cpp new file mode 100644 index 00000000..b2098d9c --- /dev/null +++ b/YACReaderLibrary/trayicon_controller.cpp @@ -0,0 +1,102 @@ +#include "trayicon_controller.h" + +#include "yacreader_global_gui.h" + +#include "library_window.h" + +#include +#include + +#ifdef Q_OS_MACOS +#include "trayhandler.h" +#endif + +using namespace YACReader; + +TrayIconController::TrayIconController(QSettings *settings, LibraryWindow *window) + : QObject(nullptr), settings(settings), window(window) +{ + + // If a window icon was set in main() we reuse it for the tray too. + // This allows support for third party icon themes on Freedesktop(Linux/Unix) + // systems. + if (!QApplication::windowIcon().isNull()) { + trayIcon.setIcon(QApplication::windowIcon()); + } else { +#ifdef Q_OS_WIN + trayIcon.setIcon(QIcon(":/icon.ico")); +#else +#ifdef Q_OS_MACOS + auto icon = QIcon(":/macostrayicon.svg"); + icon.setIsMask(true); + trayIcon.setIcon(icon); +#else + trayIcon.setIcon(QIcon(":/images/iconLibrary.png")); +#endif +#endif + } + + connect(&trayIcon, &QSystemTrayIcon::activated, + [=](QSystemTrayIcon::ActivationReason reason) { + if (reason == QSystemTrayIcon::DoubleClick) { + showWindow(); + } + }); + + auto restoreAction = new QAction(tr("&Restore"), this); + connect(restoreAction, &QAction::triggered, this, &TrayIconController::showWindow); + + auto quitAction = new QAction(tr("&Quit"), this); + connect(quitAction, &QAction::triggered, window, &LibraryWindow::closeApp); + + trayIconMenu = new QMenu(this->window); + trayIconMenu->addAction(restoreAction); + trayIconMenu->addSeparator(); + trayIconMenu->addAction(quitAction); + + trayIcon.setContextMenu(trayIconMenu); + + updateIconVisibility(); +} + +void TrayIconController::updateIconVisibility() +{ + trayIcon.setVisible(settings->value(CLOSE_TO_TRAY, false).toBool()); +} + +bool TrayIconController::handleCloseToTrayIcon(QCloseEvent *event) +{ +#ifdef Q_OS_OSX + if (!event->spontaneous() || !window->isVisible()) { + return false; + } +#endif + if (trayIcon.isVisible()) { + if (!settings->value(CLOSE_TO_TRAY_NOTIFIED, false).toBool()) { + QMessageBox::information(window, tr("Systray"), + tr("YACReaderLibrary will keep running in the " + "system tray. To terminate the program, " + "choose Quit in the context menu " + "of the system tray icon.")); + settings->setValue(CLOSE_TO_TRAY_NOTIFIED, true); + } +#ifdef Q_OS_OSX + OSXHideDockIcon(); +#endif + window->hide(); + event->ignore(); + return true; + } else { + return false; + } +} + +void TrayIconController::showWindow() +{ +#ifdef Q_OS_MACOS + OSXShowDockIcon(); +#endif + window->showNormal(); + window->raise(); // for MacOS + window->activateWindow(); // for Windows +} diff --git a/YACReaderLibrary/trayicon_controller.h b/YACReaderLibrary/trayicon_controller.h new file mode 100644 index 00000000..1ff9d407 --- /dev/null +++ b/YACReaderLibrary/trayicon_controller.h @@ -0,0 +1,36 @@ +#ifndef TRAYICON_CONTROLLER_H +#define TRAYICON_CONTROLLER_H + +#include +#include + +class LibraryWindow; + +namespace YACReader { + +class TrayIconController : public QObject +{ + Q_OBJECT +public: + TrayIconController(QSettings *settings, + LibraryWindow *window); + + void updateIconVisibility(); + + bool handleCloseToTrayIcon(QCloseEvent *event); + + QSystemTrayIcon trayIcon; + +public slots: + void showWindow(); + +private: + QSettings *settings; + LibraryWindow *window; + + QMenu *trayIconMenu; +}; + +} + +#endif // TRAYICON_CONTROLLER_H diff --git a/common/yacreader_global_gui.h b/common/yacreader_global_gui.h index 0ec4ab1b..94a876ea 100644 --- a/common/yacreader_global_gui.h +++ b/common/yacreader_global_gui.h @@ -17,6 +17,9 @@ #define FULLSCREEN "FULLSCREEN" #define Y_WINDOW_GEOMETRY "GEOMETRY" #define MAXIMIZED "MAXIMIZED" +#define CLOSE_TO_TRAY "CLOSE_TO_TRAY" +#define CLOSE_TO_TRAY_NOTIFIED "CLOSE_TO_TRAY_NOTIFIED" +#define START_TO_TRAY "START_TO_TRAY" #define DOUBLE_PAGE "DOUBLE_PAGE" #define DOUBLE_MANGA_PAGE "DOUBLE_MANGA_PAGE" #define BACKGROUND_COLOR "BACKGROUND_COLOR"