From 6e0bcdb872dcae7d510872ad5e8585b2a902f278 Mon Sep 17 00:00:00 2001 From: Martin Rotter Date: Thu, 14 Sep 2023 15:42:45 +0200 Subject: [PATCH] work on toasts --- .../notifications/basetoastnotification.cpp | 15 +++- .../gui/notifications/basetoastnotification.h | 9 ++- .../gui/notifications/toastnotification.cpp | 1 + .../toastnotificationsmanager.cpp | 74 ++++++++++++++----- .../notifications/toastnotificationsmanager.h | 13 +++- src/librssguard/miscellaneous/application.cpp | 3 +- 6 files changed, 90 insertions(+), 25 deletions(-) diff --git a/src/librssguard/gui/notifications/basetoastnotification.cpp b/src/librssguard/gui/notifications/basetoastnotification.cpp index eef31be48..c0dc3075b 100644 --- a/src/librssguard/gui/notifications/basetoastnotification.cpp +++ b/src/librssguard/gui/notifications/basetoastnotification.cpp @@ -5,12 +5,15 @@ #include "miscellaneous/iconfactory.h" #include +#include BaseToastNotification::BaseToastNotification(QWidget* parent) : QDialog(parent) { setAttribute(Qt::WidgetAttribute::WA_ShowWithoutActivating); setFixedWidth(NOTIFICATIONS_WIDTH); setFocusPolicy(Qt::FocusPolicy::NoFocus); + setAttribute(Qt::WidgetAttribute::WA_DeleteOnClose, false); + setWindowFlags( #ifdef Q_OS_MAC Qt::WindowType::SubWindow | @@ -19,6 +22,8 @@ BaseToastNotification::BaseToastNotification(QWidget* parent) : QDialog(parent) #endif Qt::WindowType::FramelessWindowHint | Qt::WindowType::WindowStaysOnTopHint | Qt::WindowType::WindowSystemMenuHint); + setStyleSheet(QSL("BaseToastNotification { border: 1px solid black; }")); + installEventFilter(this); } @@ -27,7 +32,11 @@ BaseToastNotification::~BaseToastNotification() {} void BaseToastNotification::setupCloseButton(QAbstractButton* btn) { btn->setIcon(qApp->icons()->fromTheme(QSL("dialog-close"), QSL("gtk-close"))); - connect(btn, &QAbstractButton::clicked, this, &BaseToastNotification::closeRequested); + connect(btn, &QAbstractButton::clicked, this, &BaseToastNotification::close); +} + +void BaseToastNotification::setupTimedClosing() { + QTimer::singleShot(15000, this, &BaseToastNotification::close); } bool BaseToastNotification::eventFilter(QObject* watched, QEvent* event) { @@ -40,5 +49,7 @@ bool BaseToastNotification::eventFilter(QObject* watched, QEvent* event) { } void BaseToastNotification::closeEvent(QCloseEvent* event) { - event->ignore(); + emit closeRequested(this); } + +void BaseToastNotification::reject() {} diff --git a/src/librssguard/gui/notifications/basetoastnotification.h b/src/librssguard/gui/notifications/basetoastnotification.h index 6d2248e8a..34704c917 100644 --- a/src/librssguard/gui/notifications/basetoastnotification.h +++ b/src/librssguard/gui/notifications/basetoastnotification.h @@ -17,14 +17,21 @@ class BaseToastNotification : public QDialog { // If true, then notification is always moved as close to top as possible. virtual bool alwaysOnTop() const = 0; + public slots: + virtual void reject(); + protected: virtual bool eventFilter(QObject* watched, QEvent* event); virtual void closeEvent(QCloseEvent* event); void setupCloseButton(QAbstractButton* btn); + void setupTimedClosing(); signals: - void closeRequested(); + void closeRequested(BaseToastNotification* notif); + + private: + int m_timerId; }; #endif // BASETOASTNOTIFICATION_H diff --git a/src/librssguard/gui/notifications/toastnotification.cpp b/src/librssguard/gui/notifications/toastnotification.cpp index 7669135aa..5312e409f 100644 --- a/src/librssguard/gui/notifications/toastnotification.cpp +++ b/src/librssguard/gui/notifications/toastnotification.cpp @@ -16,6 +16,7 @@ ToastNotification::ToastNotification(Notification::Event event, m_ui.setupUi(this); setupCloseButton(m_ui.m_btnClose); + setupTimedClosing(); loadNotification(event, msg, action); } diff --git a/src/librssguard/gui/notifications/toastnotificationsmanager.cpp b/src/librssguard/gui/notifications/toastnotificationsmanager.cpp index e983af8bd..c2267a630 100644 --- a/src/librssguard/gui/notifications/toastnotificationsmanager.cpp +++ b/src/librssguard/gui/notifications/toastnotificationsmanager.cpp @@ -38,9 +38,8 @@ void ToastNotificationsManager::setPosition(NotificationPosition position) { } void ToastNotificationsManager::clear() { - for (BaseToastNotification* nt : m_activeNotifications) { - nt->close(); - nt->deleteLater(); + for (BaseToastNotification* notif : m_activeNotifications) { + closeNotification(notif); } m_activeNotifications.clear(); @@ -51,6 +50,8 @@ void ToastNotificationsManager::showNotification(Notification::Event event, const GuiAction& action) { ToastNotification* notif = new ToastNotification(event, msg, action, qApp->mainFormWidget()); + hookNotification(notif); + auto* screen = moveToProperScreen(notif); // Insert new notification into free space. @@ -58,14 +59,13 @@ void ToastNotificationsManager::showNotification(Notification::Event event, auto notif_new_pos = cornerForNewNotification(screen->availableGeometry()); - moveNotificationToCorner(notif, notif_new_pos); - - notif->move(notif_new_pos); - // Make sure notification is finally resized. notif->adjustSize(); qApp->processEvents(); + // Move notification, at this point we already need to know its precise size. + moveNotificationToCorner(notif, notif_new_pos); + // Remove out-of-bounds old notifications and shift existing // ones to make space for new notifications. removeOutOfBoundsNotifications(notif->height()); @@ -76,6 +76,21 @@ void ToastNotificationsManager::showNotification(Notification::Event event, void ToastNotificationsManager::showNotification(const QList& new_messages) {} +void ToastNotificationsManager::closeNotification(BaseToastNotification* notif) { + auto notif_idx = m_activeNotifications.indexOf(notif); + + notif->deleteLater(); + + m_activeNotifications.removeAll(notif); + + // Shift all notifications. + if (notif_idx < 0) { + return; + } + + makeSpaceForNotification(notif->height(), true, notif_idx); +} + QScreen* ToastNotificationsManager::activeScreen() const { if (m_screen >= 0) { auto all_screens = QGuiApplication::screens(); @@ -88,23 +103,28 @@ QScreen* ToastNotificationsManager::activeScreen() const { return QGuiApplication::primaryScreen(); } -QPoint ToastNotificationsManager::cornerForNewNotification(QRect rect) { +QPoint ToastNotificationsManager::cornerForNewNotification(QRect screen_rect) { switch (m_position) { case ToastNotificationsManager::TopLeft: - return rect.topLeft() + QPoint(NOTIFICATIONS_MARGIN, NOTIFICATIONS_MARGIN); + return screen_rect.topLeft() + QPoint(NOTIFICATIONS_MARGIN, NOTIFICATIONS_MARGIN); case ToastNotificationsManager::TopRight: - return rect.topRight() - QPoint(NOTIFICATIONS_WIDTH + NOTIFICATIONS_MARGIN, -NOTIFICATIONS_MARGIN); + return screen_rect.topRight() - QPoint(NOTIFICATIONS_WIDTH + NOTIFICATIONS_MARGIN, -NOTIFICATIONS_MARGIN); case ToastNotificationsManager::BottomLeft: - return rect.bottomLeft() - QPoint(-NOTIFICATIONS_MARGIN, NOTIFICATIONS_MARGIN); + return screen_rect.bottomLeft() - QPoint(-NOTIFICATIONS_MARGIN, NOTIFICATIONS_MARGIN); case ToastNotificationsManager::BottomRight: - return rect.bottomRight() - QPoint(NOTIFICATIONS_MARGIN, NOTIFICATIONS_MARGIN); + default: + return screen_rect.bottomRight() - QPoint(NOTIFICATIONS_MARGIN, NOTIFICATIONS_MARGIN); } } -void ToastNotificationsManager::moveNotificationToCorner(BaseToastNotification* notif, const QPoint& corner) { +void ToastNotificationsManager::hookNotification(BaseToastNotification* notif) { + connect(notif, &BaseToastNotification::closeRequested, this, &ToastNotificationsManager::closeNotification); +} + +void ToastNotificationsManager::moveNotificationToCorner(BaseToastNotification* notif, QPoint corner) { switch (m_position) { case ToastNotificationsManager::TopLeft: notif->move(corner); @@ -119,14 +139,34 @@ void ToastNotificationsManager::moveNotificationToCorner(BaseToastNotification* break; case ToastNotificationsManager::BottomRight: - notif->move(corner); + notif->move(corner.x() - notif->frameGeometry().width(), corner.y() - notif->frameGeometry().height()); break; } } -void ToastNotificationsManager::makeSpaceForNotification(int height_to_make_space) { - for (BaseToastNotification* notif : m_activeNotifications) { - notif->move(notif->pos().x(), notif->pos().y() + height_to_make_space + NOTIFICATIONS_MARGIN); +void ToastNotificationsManager::makeSpaceForNotification(int height_to_make_space, bool reverse, int stard_idx) { + for (int i = stard_idx; i < m_activeNotifications.size(); i++) { + BaseToastNotification* notif = m_activeNotifications.at(i); + + switch (m_position) { + case ToastNotificationsManager::TopLeft: + case ToastNotificationsManager::TopRight: { + auto shift_down = reverse ? [](int x, int y) {return x - y;} : [](int x, int y) {return x + y;}; + + // Move it all down. + notif->move(notif->pos().x(), shift_down(notif->pos().y(), (height_to_make_space + NOTIFICATIONS_MARGIN))); + break; + } + + case ToastNotificationsManager::BottomLeft: + case ToastNotificationsManager::BottomRight: { + auto shift_up = reverse ? [](int x, int y) {return x + y;} : [](int x, int y) {return x - y;}; + + // Move it all up. + notif->move(notif->pos().x(), shift_up(notif->pos().y(), height_to_make_space + NOTIFICATIONS_MARGIN)); + break; + } + } } } diff --git a/src/librssguard/gui/notifications/toastnotificationsmanager.h b/src/librssguard/gui/notifications/toastnotificationsmanager.h index cac9f3501..9b8bedb8e 100644 --- a/src/librssguard/gui/notifications/toastnotificationsmanager.h +++ b/src/librssguard/gui/notifications/toastnotificationsmanager.h @@ -40,13 +40,18 @@ class ToastNotificationsManager : public QObject { void showNotification(Notification::Event event, const GuiMessage& msg, const GuiAction& action); void showNotification(const QList& new_messages); + private slots: + void closeNotification(BaseToastNotification* notif); + private: QScreen* activeScreen() const; - QPoint cornerForNewNotification(QRect rect); - void moveNotificationToCorner(BaseToastNotification* notif, const QPoint& corner); - void makeSpaceForNotification(int height_to_make_space); - void removeOutOfBoundsNotifications(int height_to_reserve); QScreen* moveToProperScreen(BaseToastNotification* notif); + QPoint cornerForNewNotification(QRect screen_rect); + + void hookNotification(BaseToastNotification* notif); + void moveNotificationToCorner(BaseToastNotification* notif, QPoint corner); + void makeSpaceForNotification(int height_to_make_space, bool reverse = false, int stard_idx = 0); + void removeOutOfBoundsNotifications(int height_to_reserve); private: NotificationPosition m_position; diff --git a/src/librssguard/miscellaneous/application.cpp b/src/librssguard/miscellaneous/application.cpp index 060858132..d60af157e 100644 --- a/src/librssguard/miscellaneous/application.cpp +++ b/src/librssguard/miscellaneous/application.cpp @@ -690,6 +690,7 @@ void Application::showGuiMessageCore(Notification::Event event, const GuiAction& action, QWidget* parent) { m_toastNotifications->showNotification(event, msg, action); + return; if (SystemTrayIcon::areNotificationsEnabled()) { auto notification = m_notifications->notificationForEvent(event); @@ -785,7 +786,7 @@ void Application::onAboutToQuit() { // Make sure that we obtain close lock BEFORE even trying to quit the application. const bool locked_safely = feedUpdateLock()->tryLock(4 * CLOSE_LOCK_TIMEOUT); - processEvents(); + QCoreApplication::processEvents(); qDebugNN << LOGSEC_CORE << "Cleaning up resources and saving application state."; if (locked_safely) {