work on toasts

This commit is contained in:
Martin Rotter 2023-09-14 15:42:45 +02:00
parent 5f2dab4362
commit 6e0bcdb872
6 changed files with 90 additions and 25 deletions

View File

@ -5,12 +5,15 @@
#include "miscellaneous/iconfactory.h" #include "miscellaneous/iconfactory.h"
#include <QCloseEvent> #include <QCloseEvent>
#include <QTimer>
BaseToastNotification::BaseToastNotification(QWidget* parent) : QDialog(parent) { BaseToastNotification::BaseToastNotification(QWidget* parent) : QDialog(parent) {
setAttribute(Qt::WidgetAttribute::WA_ShowWithoutActivating); setAttribute(Qt::WidgetAttribute::WA_ShowWithoutActivating);
setFixedWidth(NOTIFICATIONS_WIDTH); setFixedWidth(NOTIFICATIONS_WIDTH);
setFocusPolicy(Qt::FocusPolicy::NoFocus); setFocusPolicy(Qt::FocusPolicy::NoFocus);
setAttribute(Qt::WidgetAttribute::WA_DeleteOnClose, false);
setWindowFlags( setWindowFlags(
#ifdef Q_OS_MAC #ifdef Q_OS_MAC
Qt::WindowType::SubWindow | Qt::WindowType::SubWindow |
@ -19,6 +22,8 @@ BaseToastNotification::BaseToastNotification(QWidget* parent) : QDialog(parent)
#endif #endif
Qt::WindowType::FramelessWindowHint | Qt::WindowType::WindowStaysOnTopHint | Qt::WindowType::WindowSystemMenuHint); Qt::WindowType::FramelessWindowHint | Qt::WindowType::WindowStaysOnTopHint | Qt::WindowType::WindowSystemMenuHint);
setStyleSheet(QSL("BaseToastNotification { border: 1px solid black; }"));
installEventFilter(this); installEventFilter(this);
} }
@ -27,7 +32,11 @@ BaseToastNotification::~BaseToastNotification() {}
void BaseToastNotification::setupCloseButton(QAbstractButton* btn) { void BaseToastNotification::setupCloseButton(QAbstractButton* btn) {
btn->setIcon(qApp->icons()->fromTheme(QSL("dialog-close"), QSL("gtk-close"))); 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) { bool BaseToastNotification::eventFilter(QObject* watched, QEvent* event) {
@ -40,5 +49,7 @@ bool BaseToastNotification::eventFilter(QObject* watched, QEvent* event) {
} }
void BaseToastNotification::closeEvent(QCloseEvent* event) { void BaseToastNotification::closeEvent(QCloseEvent* event) {
event->ignore(); emit closeRequested(this);
} }
void BaseToastNotification::reject() {}

View File

@ -17,14 +17,21 @@ class BaseToastNotification : public QDialog {
// If true, then notification is always moved as close to top as possible. // If true, then notification is always moved as close to top as possible.
virtual bool alwaysOnTop() const = 0; virtual bool alwaysOnTop() const = 0;
public slots:
virtual void reject();
protected: protected:
virtual bool eventFilter(QObject* watched, QEvent* event); virtual bool eventFilter(QObject* watched, QEvent* event);
virtual void closeEvent(QCloseEvent* event); virtual void closeEvent(QCloseEvent* event);
void setupCloseButton(QAbstractButton* btn); void setupCloseButton(QAbstractButton* btn);
void setupTimedClosing();
signals: signals:
void closeRequested(); void closeRequested(BaseToastNotification* notif);
private:
int m_timerId;
}; };
#endif // BASETOASTNOTIFICATION_H #endif // BASETOASTNOTIFICATION_H

View File

@ -16,6 +16,7 @@ ToastNotification::ToastNotification(Notification::Event event,
m_ui.setupUi(this); m_ui.setupUi(this);
setupCloseButton(m_ui.m_btnClose); setupCloseButton(m_ui.m_btnClose);
setupTimedClosing();
loadNotification(event, msg, action); loadNotification(event, msg, action);
} }

View File

@ -38,9 +38,8 @@ void ToastNotificationsManager::setPosition(NotificationPosition position) {
} }
void ToastNotificationsManager::clear() { void ToastNotificationsManager::clear() {
for (BaseToastNotification* nt : m_activeNotifications) { for (BaseToastNotification* notif : m_activeNotifications) {
nt->close(); closeNotification(notif);
nt->deleteLater();
} }
m_activeNotifications.clear(); m_activeNotifications.clear();
@ -51,6 +50,8 @@ void ToastNotificationsManager::showNotification(Notification::Event event,
const GuiAction& action) { const GuiAction& action) {
ToastNotification* notif = new ToastNotification(event, msg, action, qApp->mainFormWidget()); ToastNotification* notif = new ToastNotification(event, msg, action, qApp->mainFormWidget());
hookNotification(notif);
auto* screen = moveToProperScreen(notif); auto* screen = moveToProperScreen(notif);
// Insert new notification into free space. // Insert new notification into free space.
@ -58,14 +59,13 @@ void ToastNotificationsManager::showNotification(Notification::Event event,
auto notif_new_pos = cornerForNewNotification(screen->availableGeometry()); auto notif_new_pos = cornerForNewNotification(screen->availableGeometry());
moveNotificationToCorner(notif, notif_new_pos);
notif->move(notif_new_pos);
// Make sure notification is finally resized. // Make sure notification is finally resized.
notif->adjustSize(); notif->adjustSize();
qApp->processEvents(); 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 // Remove out-of-bounds old notifications and shift existing
// ones to make space for new notifications. // ones to make space for new notifications.
removeOutOfBoundsNotifications(notif->height()); removeOutOfBoundsNotifications(notif->height());
@ -76,6 +76,21 @@ void ToastNotificationsManager::showNotification(Notification::Event event,
void ToastNotificationsManager::showNotification(const QList<Message>& new_messages) {} void ToastNotificationsManager::showNotification(const QList<Message>& 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 { QScreen* ToastNotificationsManager::activeScreen() const {
if (m_screen >= 0) { if (m_screen >= 0) {
auto all_screens = QGuiApplication::screens(); auto all_screens = QGuiApplication::screens();
@ -88,23 +103,28 @@ QScreen* ToastNotificationsManager::activeScreen() const {
return QGuiApplication::primaryScreen(); return QGuiApplication::primaryScreen();
} }
QPoint ToastNotificationsManager::cornerForNewNotification(QRect rect) { QPoint ToastNotificationsManager::cornerForNewNotification(QRect screen_rect) {
switch (m_position) { switch (m_position) {
case ToastNotificationsManager::TopLeft: case ToastNotificationsManager::TopLeft:
return rect.topLeft() + QPoint(NOTIFICATIONS_MARGIN, NOTIFICATIONS_MARGIN); return screen_rect.topLeft() + QPoint(NOTIFICATIONS_MARGIN, NOTIFICATIONS_MARGIN);
case ToastNotificationsManager::TopRight: 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: case ToastNotificationsManager::BottomLeft:
return rect.bottomLeft() - QPoint(-NOTIFICATIONS_MARGIN, NOTIFICATIONS_MARGIN); return screen_rect.bottomLeft() - QPoint(-NOTIFICATIONS_MARGIN, NOTIFICATIONS_MARGIN);
case ToastNotificationsManager::BottomRight: 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) { switch (m_position) {
case ToastNotificationsManager::TopLeft: case ToastNotificationsManager::TopLeft:
notif->move(corner); notif->move(corner);
@ -119,14 +139,34 @@ void ToastNotificationsManager::moveNotificationToCorner(BaseToastNotification*
break; break;
case ToastNotificationsManager::BottomRight: case ToastNotificationsManager::BottomRight:
notif->move(corner); notif->move(corner.x() - notif->frameGeometry().width(), corner.y() - notif->frameGeometry().height());
break; break;
} }
} }
void ToastNotificationsManager::makeSpaceForNotification(int height_to_make_space) { void ToastNotificationsManager::makeSpaceForNotification(int height_to_make_space, bool reverse, int stard_idx) {
for (BaseToastNotification* notif : m_activeNotifications) { for (int i = stard_idx; i < m_activeNotifications.size(); i++) {
notif->move(notif->pos().x(), notif->pos().y() + height_to_make_space + NOTIFICATIONS_MARGIN); 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;
}
}
} }
} }

View File

@ -40,13 +40,18 @@ class ToastNotificationsManager : public QObject {
void showNotification(Notification::Event event, const GuiMessage& msg, const GuiAction& action); void showNotification(Notification::Event event, const GuiMessage& msg, const GuiAction& action);
void showNotification(const QList<Message>& new_messages); void showNotification(const QList<Message>& new_messages);
private slots:
void closeNotification(BaseToastNotification* notif);
private: private:
QScreen* activeScreen() const; 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); 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: private:
NotificationPosition m_position; NotificationPosition m_position;

View File

@ -690,6 +690,7 @@ void Application::showGuiMessageCore(Notification::Event event,
const GuiAction& action, const GuiAction& action,
QWidget* parent) { QWidget* parent) {
m_toastNotifications->showNotification(event, msg, action); m_toastNotifications->showNotification(event, msg, action);
return;
if (SystemTrayIcon::areNotificationsEnabled()) { if (SystemTrayIcon::areNotificationsEnabled()) {
auto notification = m_notifications->notificationForEvent(event); 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. // Make sure that we obtain close lock BEFORE even trying to quit the application.
const bool locked_safely = feedUpdateLock()->tryLock(4 * CLOSE_LOCK_TIMEOUT); const bool locked_safely = feedUpdateLock()->tryLock(4 * CLOSE_LOCK_TIMEOUT);
processEvents(); QCoreApplication::processEvents();
qDebugNN << LOGSEC_CORE << "Cleaning up resources and saving application state."; qDebugNN << LOGSEC_CORE << "Cleaning up resources and saving application state.";
if (locked_safely) { if (locked_safely) {