save work

This commit is contained in:
Martin Rotter 2023-09-13 14:09:18 +02:00
parent 7e489dcbb9
commit 1cfcc0b952
9 changed files with 188 additions and 18 deletions

View File

@ -61,8 +61,8 @@
#define RELEASES_LIST "https://api.github.com/repos/martinrotter/rssguard/releases"
#define MSG_FILTERING_HELP APP_URL_DOCUMENTATION "#fltr"
#define URL_REGEXP \
"^(http|https|feed|ftp):\\/\\/[\\w\\-_]+(\\.[\\w\\-_]+)+([\\w\\-\\.,@?^=%&:/~\\+#]*[\\w\\-\\@?^=%&/" \
#define URL_REGEXP \
"^(http|https|feed|ftp):\\/\\/[\\w\\-_]+(\\.[\\w\\-_]+)+([\\w\\-\\.,@?^=%&:/~\\+#]*[\\w\\-\\@?^=%&/" \
"~\\+#])?$"
#define SCRIPT_SOURCE_TYPE_REGEXP "^.+#.*$"
#define TEXT_TITLE_LIMIT 30
@ -99,6 +99,8 @@
#define DEFAULT_NOTIFICATION_VOLUME 50
#define MAX_THREADPOOL_THREADS 32
#define WEB_BROWSER_SCROLL_STEP 50.0
#define NOTIFICATIONS_MARGIN 16
#define NOTIFICATIONS_WIDTH 256
#define GOOGLE_SEARCH_URL "https://www.google.com/search?q=%1&ie=utf-8&oe=utf-8"
#define GOOGLE_SUGGEST_URL "http://suggestqueries.google.com/complete/search?output=toolbar&hl=en&q=%1"
@ -178,13 +180,13 @@
#define ZOOM_FACTOR_STEP 0.05f
#if defined(USE_WEBENGINE)
#define HTTP_COMPLETE_USERAGENT \
(qApp->web()->engineProfile()->httpUserAgent().toLocal8Bit() + QByteArrayLiteral(" ") + \
#define HTTP_COMPLETE_USERAGENT \
(qApp->web()->engineProfile()->httpUserAgent().toLocal8Bit() + QByteArrayLiteral(" ") + \
QByteArrayLiteral(APP_USERAGENT))
#else
#define HTTP_COMPLETE_USERAGENT \
(QByteArrayLiteral("Mozilla/5.0 (Windows NT 6.2; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) " \
"QtWebEngine/5.15.2 Chrome/83.0.4103.122 Safari/537.36 ") + \
#define HTTP_COMPLETE_USERAGENT \
(QByteArrayLiteral("Mozilla/5.0 (Windows NT 6.2; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) " \
"QtWebEngine/5.15.2 Chrome/83.0.4103.122 Safari/537.36 ") + \
QByteArrayLiteral(APP_USERAGENT))
#endif

View File

@ -1090,7 +1090,18 @@ void FormMain::showAddAccountDialog() {
}
void FormMain::reportABug() {
qApp->web()->openUrlInExternalBrowser(QSL(APP_URL_ISSUES_NEW));
qApp
->showGuiMessage(Notification::Event::GeneralEvent,
GuiMessage(QDateTime::currentDateTime().toString(),
"Quisque ullamcorper ut purus nec tempus. Vivamus eros dolor, sagittis ultrices augue "
"ut, posuere fringilla lorem. Donec posuere, enim sit amet fermentum dignissim, tellus "
"lectus laoreet lectus, vestibulum laoreet felis tortor eget nunc. Curabitur sagittis "
"quam in scelerisque placerat. Vivamus vel porta tortor. Vivamus nec volutpat sem",
QSystemTrayIcon::MessageIcon::Information),
GuiMessageDestination(),
GuiAction("test", []() {}));
// qApp->web()->openUrlInExternalBrowser(QSL(APP_URL_ISSUES_NEW));
}
void FormMain::donate() {

View File

@ -4,9 +4,12 @@
#include "miscellaneous/iconfactory.h"
#include <QCloseEvent>
BaseToastNotification::BaseToastNotification(QWidget* parent) : QDialog(parent) {
setAttribute(Qt::WidgetAttribute::WA_ShowWithoutActivating);
setAttribute(Qt::WidgetAttribute::WA_TranslucentBackground);
setFixedWidth(NOTIFICATIONS_WIDTH);
setFocusPolicy(Qt::FocusPolicy::NoFocus);
setWindowFlags(
#ifdef Q_OS_MAC
@ -15,10 +18,27 @@ BaseToastNotification::BaseToastNotification(QWidget* parent) : QDialog(parent)
Qt::WindowType::Tool |
#endif
Qt::WindowType::FramelessWindowHint | Qt::WindowType::WindowStaysOnTopHint | Qt::WindowType::WindowSystemMenuHint);
installEventFilter(this);
}
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);
}
bool BaseToastNotification::eventFilter(QObject* watched, QEvent* event) {
if (event->type() == QEvent::Type::KeyPress) {
return true;
}
else {
return QDialog::eventFilter(watched, event);
}
}
void BaseToastNotification::closeEvent(QCloseEvent* event) {
event->ignore();
}

View File

@ -14,8 +14,17 @@ class BaseToastNotification : public QDialog {
explicit BaseToastNotification(QWidget* parent = nullptr);
virtual ~BaseToastNotification();
// If true, then notification is always moved as close to top as possible.
virtual bool alwaysOnTop() const = 0;
protected:
virtual bool eventFilter(QObject* watched, QEvent* event);
virtual void closeEvent(QCloseEvent* event);
void setupCloseButton(QAbstractButton* btn);
signals:
void closeRequested();
};
#endif // BASETOASTNOTIFICATION_H

View File

@ -19,6 +19,10 @@ ToastNotification::ToastNotification(Notification::Event event,
loadNotification(event, msg, action);
}
bool ToastNotification::alwaysOnTop() const {
return false;
}
void ToastNotification::loadNotification(Notification::Event event, const GuiMessage& msg, const GuiAction& action) {
m_ui.m_lblTitle->setText(msg.m_title);
m_ui.m_lblBody->setText(msg.m_message);

View File

@ -18,6 +18,8 @@ class ToastNotification : public BaseToastNotification {
const GuiAction& action,
QWidget* parent = nullptr);
virtual bool alwaysOnTop() const;
private:
void loadNotification(Notification::Event event, const GuiMessage& msg, const GuiAction& action);

View File

@ -2,8 +2,14 @@
#include "gui/notifications/toastnotificationsmanager.h"
#include "3rd-party/boolinq/boolinq.h"
#include "gui/notifications/basetoastnotification.h"
#include "gui/notifications/toastnotification.h"
#include <QRect>
#include <QScreen>
#include <QWindow>
ToastNotificationsManager::ToastNotificationsManager(QObject* parent)
: QObject(parent), m_position(NotificationPosition::BottomRight), m_screen(-1) {}
@ -43,17 +49,115 @@ void ToastNotificationsManager::clear() {
void ToastNotificationsManager::showNotification(Notification::Event event,
const GuiMessage& msg,
const GuiAction& action) {
// Remove top existing notifications as long as their combined height with height of this
// new notification extends.
ToastNotification* notif = new ToastNotification(event, msg, action, qApp->mainFormWidget());
auto aa = notif->height();
auto* screen = moveToProperScreen(notif);
// Insert new notification into free space.
notif->show();
auto bb = notif->height();
auto cc = notif->height();
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();
// Remove out-of-bounds old notifications and shift existing
// ones to make space for new notifications.
removeOutOfBoundsNotifications(notif->height());
makeSpaceForNotification(notif->height());
m_activeNotifications.prepend(notif);
}
void ToastNotificationsManager::showNotification(const QList<Message>& new_messages) {}
QScreen* ToastNotificationsManager::activeScreen() const {
if (m_screen >= 0) {
auto all_screens = QGuiApplication::screens();
if (m_screen < all_screens.size()) {
return all_screens.at(m_screen);
}
}
return QGuiApplication::primaryScreen();
}
QPoint ToastNotificationsManager::cornerForNewNotification(QRect rect) {
switch (m_position) {
case ToastNotificationsManager::TopLeft:
return rect.topLeft() + QPoint(NOTIFICATIONS_MARGIN, NOTIFICATIONS_MARGIN);
case ToastNotificationsManager::TopRight:
return rect.topRight() - QPoint(NOTIFICATIONS_WIDTH + NOTIFICATIONS_MARGIN, -NOTIFICATIONS_MARGIN);
case ToastNotificationsManager::BottomLeft:
return rect.bottomLeft() - QPoint(-NOTIFICATIONS_MARGIN, NOTIFICATIONS_MARGIN);
case ToastNotificationsManager::BottomRight:
return rect.bottomRight() - QPoint(NOTIFICATIONS_MARGIN, NOTIFICATIONS_MARGIN);
}
}
void ToastNotificationsManager::moveNotificationToCorner(BaseToastNotification* notif, const QPoint& corner) {
switch (m_position) {
case ToastNotificationsManager::TopLeft:
notif->move(corner);
break;
case ToastNotificationsManager::TopRight:
notif->move(corner);
break;
case ToastNotificationsManager::BottomLeft:
notif->move(corner);
break;
case ToastNotificationsManager::BottomRight:
notif->move(corner);
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::removeOutOfBoundsNotifications(int height_to_reserve) {
auto* screen = activeScreen();
int available_height = screen->availableSize().height();
while (boolinq::from(m_activeNotifications).sum([](BaseToastNotification* notif) {
return notif->height() + NOTIFICATIONS_MARGIN;
}) + height_to_reserve >
available_height) {
if (!m_activeNotifications.isEmpty()) {
m_activeNotifications.takeLast()->deleteLater();
}
else {
break;
}
}
}
QScreen* ToastNotificationsManager::moveToProperScreen(BaseToastNotification* notif) {
if (m_screen >= 0) {
auto all_screens = QGuiApplication::screens();
if (m_screen < all_screens.size()) {
notif->windowHandle()->setScreen(all_screens.at(m_screen));
return all_screens.at(m_screen);
}
}
return QGuiApplication::primaryScreen();
}

View File

@ -9,6 +9,7 @@
class BaseToastNotification;
class ToastNotification;
class QScreen;
class ToastNotificationsManager : public QObject {
Q_OBJECT
@ -39,9 +40,20 @@ class ToastNotificationsManager : public QObject {
void showNotification(Notification::Event event, const GuiMessage& msg, const GuiAction& action);
void showNotification(const QList<Message>& new_messages);
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);
private:
NotificationPosition m_position;
int m_screen;
// List of all displayed notifications, newest notifications are in the beginning of the list
// and oldest at the end.
QList<BaseToastNotification*> m_activeNotifications;
};

View File

@ -177,9 +177,11 @@ Application::Application(const QString& id, int& argc, char** argv, const QStrin
#if defined(USE_WEBENGINE)
m_webFactory->urlIinterceptor()->load();
m_webFactory->engineProfile()->setCachePath(cacheFolder() + QDir::separator() + QSL("web") + QDir::separator() + QSL("cache"));
m_webFactory->engineProfile()->setCachePath(cacheFolder() + QDir::separator() + QSL("web") + QDir::separator() +
QSL("cache"));
m_webFactory->engineProfile()->setHttpCacheType(QWebEngineProfile::HttpCacheType::DiskHttpCache);
m_webFactory->engineProfile()->setPersistentStoragePath(userDataFolder() + QDir::separator() + QSL("web") + QDir::separator() + QSL("storage"));
m_webFactory->engineProfile()->setPersistentStoragePath(userDataFolder() + QDir::separator() + QSL("web") +
QDir::separator() + QSL("storage"));
m_webFactory->loadCustomCss(userDataFolder() + QDir::separator() + QSL("web") + QDir::separator() +
QSL("user-styles.css"));
@ -833,7 +835,10 @@ void Application::showMessagesNumber(int unread_messages, bool any_feed_has_new_
m_trayIcon->setNumber(unread_messages, any_feed_has_new_unread_messages);
}
// Set task bar overlay with number of unread articles.
// Use Qt function to set "badge" number directly in some cases.
#if defined(Q_OS_MACOS) && QT_VERSION >= 0x060500 // Qt >= 6.5.0
qApp->setBadgeNumber(unread_messages);
#else
#if defined(Q_OS_UNIX) && !defined(Q_OS_MACOS)
// Use D-Bus "LauncherEntry" service on Linux.
bool task_bar_count_enabled = settings()->value(GROUP(GUI), SETTING(GUI::UnreadNumbersOnTaskBar)).toBool();
@ -882,6 +887,7 @@ void Application::showMessagesNumber(int unread_messages, bool any_feed_has_new_
else {
qCriticalNN << LOGSEC_GUI << "Main form not set for setting numbers.";
}
#endif
#endif
if (m_mainForm != nullptr) {