mirror of https://github.com/cutefishos/core
Add notification window
parent
d5b02178fd
commit
4b627ab25b
@ -0,0 +1,118 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2021 CutefishOS Team.
|
||||||
|
*
|
||||||
|
* Author: Reion Wong <reion@cutefishos.com>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "application.h"
|
||||||
|
#include "notificationsmodel.h"
|
||||||
|
#include "screenhelper.h"
|
||||||
|
#include "notificationadaptor.h"
|
||||||
|
#include "historymodel.h"
|
||||||
|
|
||||||
|
#include <QCommandLineParser>
|
||||||
|
#include <QQmlApplicationEngine>
|
||||||
|
#include <QQmlContext>
|
||||||
|
|
||||||
|
#include <QDBusInterface>
|
||||||
|
#include <QDBusPendingCall>
|
||||||
|
#include <QDBusConnection>
|
||||||
|
#include <QPixmapCache>
|
||||||
|
#include <QTranslator>
|
||||||
|
#include <QFileInfo>
|
||||||
|
#include <QIcon>
|
||||||
|
#include <QDir>
|
||||||
|
|
||||||
|
#include <QDebug>
|
||||||
|
|
||||||
|
Application::Application(int& argc, char** argv)
|
||||||
|
: QApplication(argc, argv)
|
||||||
|
, m_notificationServer(NotificationServer::self())
|
||||||
|
, m_model(NotificationsModel::self())
|
||||||
|
, m_window(nullptr)
|
||||||
|
, m_instance(false)
|
||||||
|
{
|
||||||
|
if (QDBusConnection::sessionBus().registerService("com.cutefish.Notification")) {
|
||||||
|
setOrganizationName("cutefishos");
|
||||||
|
|
||||||
|
// Translations
|
||||||
|
QLocale locale;
|
||||||
|
QString qmFilePath = QString("%1/%2.qm").arg("/usr/share/cutefish-notificationd/translations/").arg(locale.name());
|
||||||
|
if (QFile::exists(qmFilePath)) {
|
||||||
|
QTranslator *translator = new QTranslator(this);
|
||||||
|
if (translator->load(qmFilePath)) {
|
||||||
|
installTranslator(translator);
|
||||||
|
} else {
|
||||||
|
translator->deleteLater();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
new NotificationAdaptor(this);
|
||||||
|
QDBusConnection::sessionBus().registerObject("/Notification", this);
|
||||||
|
|
||||||
|
qmlRegisterType<NotificationsModel>("Cutefish.Notification", 1, 0, "NotificationsModel");
|
||||||
|
qmlRegisterType<HistoryModel>("Cutefish.Notification", 1, 0, "HistoryModel");
|
||||||
|
qmlRegisterType<ScreenHelper>("Cutefish.Notification", 1, 0, "ScreenHelper");
|
||||||
|
|
||||||
|
m_instance = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Application::showWindow()
|
||||||
|
{
|
||||||
|
if (m_window)
|
||||||
|
m_window->open();
|
||||||
|
}
|
||||||
|
|
||||||
|
int Application::run()
|
||||||
|
{
|
||||||
|
if (!parseCommandLineArgs())
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
QQmlApplicationEngine engine;
|
||||||
|
engine.rootContext()->setContextProperty("notificationsModel", m_model);
|
||||||
|
engine.load(QUrl("qrc:/qml/main.qml"));
|
||||||
|
|
||||||
|
m_window = new NotificationWindow;
|
||||||
|
|
||||||
|
return QApplication::exec();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Application::parseCommandLineArgs()
|
||||||
|
{
|
||||||
|
QCommandLineParser parser;
|
||||||
|
parser.setApplicationDescription(QStringLiteral("Notification"));
|
||||||
|
parser.addHelpOption();
|
||||||
|
|
||||||
|
QCommandLineOption showOption(QStringList() << "s" << "show" << "Show dialog");
|
||||||
|
parser.addOption(showOption);
|
||||||
|
|
||||||
|
parser.process(arguments());
|
||||||
|
|
||||||
|
if (m_instance) {
|
||||||
|
QPixmapCache::setCacheLimit(2048);
|
||||||
|
} else {
|
||||||
|
QDBusInterface iface("com.cutefish.Notification",
|
||||||
|
"/Notification",
|
||||||
|
"com.cutefish.Notification",
|
||||||
|
QDBusConnection::sessionBus(), this);
|
||||||
|
if (iface.isValid() && parser.isSet(showOption)) {
|
||||||
|
iface.call("showWindow");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return m_instance;
|
||||||
|
}
|
||||||
@ -0,0 +1,47 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2021 CutefishOS Team.
|
||||||
|
*
|
||||||
|
* Author: Reion Wong <reion@cutefishos.com>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef APPLICATION_H
|
||||||
|
#define APPLICATION_H
|
||||||
|
|
||||||
|
#include <QApplication>
|
||||||
|
#include "notificationwindow.h"
|
||||||
|
|
||||||
|
class NotificationServer;
|
||||||
|
class NotificationsModel;
|
||||||
|
class Application : public QApplication
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit Application(int& argc, char** argv);
|
||||||
|
|
||||||
|
void showWindow();
|
||||||
|
|
||||||
|
int run();
|
||||||
|
bool parseCommandLineArgs();
|
||||||
|
|
||||||
|
private:
|
||||||
|
NotificationServer *m_notificationServer;
|
||||||
|
NotificationsModel *m_model;
|
||||||
|
NotificationWindow *m_window;
|
||||||
|
bool m_instance;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // APPLICATION_H
|
||||||
@ -0,0 +1,6 @@
|
|||||||
|
<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
|
||||||
|
<node>
|
||||||
|
<interface name="com.cutefish.Notification">
|
||||||
|
<method name="showWindow"></method>
|
||||||
|
</interface>
|
||||||
|
</node>
|
||||||
@ -0,0 +1,130 @@
|
|||||||
|
#include "historymodel.h"
|
||||||
|
|
||||||
|
#include <QSettings>
|
||||||
|
#include <QDataStream>
|
||||||
|
#include <QMetaEnum>
|
||||||
|
|
||||||
|
static HistoryModel *s_historyModel = nullptr;
|
||||||
|
|
||||||
|
HistoryModel *HistoryModel::self()
|
||||||
|
{
|
||||||
|
if (!s_historyModel)
|
||||||
|
s_historyModel = new HistoryModel;
|
||||||
|
|
||||||
|
return s_historyModel;
|
||||||
|
}
|
||||||
|
|
||||||
|
HistoryModel::HistoryModel(QObject *parent)
|
||||||
|
: QAbstractListModel(parent)
|
||||||
|
{
|
||||||
|
initDatas();
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariant HistoryModel::data(const QModelIndex &index, int role) const
|
||||||
|
{
|
||||||
|
const Notification ¬ification = m_notifications.at(index.row());
|
||||||
|
|
||||||
|
switch (role) {
|
||||||
|
case HistoryModel::IdRole:
|
||||||
|
return notification.id;
|
||||||
|
case HistoryModel::SummaryRole:
|
||||||
|
return notification.summary;
|
||||||
|
case HistoryModel::ImageRole:
|
||||||
|
return "";
|
||||||
|
case HistoryModel::CreatedRole:
|
||||||
|
return notification.created;
|
||||||
|
case HistoryModel::BodyRole:
|
||||||
|
return notification.body;
|
||||||
|
case HistoryModel::IconNameRole:
|
||||||
|
return notification.appIcon;
|
||||||
|
case HistoryModel::HasDefaultActionRole:
|
||||||
|
return notification.actions.contains("default");
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return QVariant();
|
||||||
|
}
|
||||||
|
|
||||||
|
int HistoryModel::rowCount(const QModelIndex &parent) const
|
||||||
|
{
|
||||||
|
Q_UNUSED(parent);
|
||||||
|
|
||||||
|
return m_notifications.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
QHash<int, QByteArray> HistoryModel::roleNames() const
|
||||||
|
{
|
||||||
|
static QHash<int, QByteArray> s_roles;
|
||||||
|
|
||||||
|
if (s_roles.isEmpty()) {
|
||||||
|
// This generates role names from the Roles enum in the form of: FooRole -> foo
|
||||||
|
const QMetaEnum e = QMetaEnum::fromType<HistoryModel::Roles>();
|
||||||
|
|
||||||
|
// Qt built-in roles we use
|
||||||
|
s_roles.insert(Qt::DisplayRole, QByteArrayLiteral("display"));
|
||||||
|
s_roles.insert(Qt::DecorationRole, QByteArrayLiteral("decoration"));
|
||||||
|
|
||||||
|
for (int i = 0; i < e.keyCount(); ++i) {
|
||||||
|
const int value = e.value(i);
|
||||||
|
|
||||||
|
QByteArray key(e.key(i));
|
||||||
|
key[0] = key[0] + 32; // lower case first letter
|
||||||
|
key.chop(4); // strip "Role" suffix
|
||||||
|
|
||||||
|
s_roles.insert(value, key);
|
||||||
|
}
|
||||||
|
|
||||||
|
s_roles.insert(HistoryModel::IdRole, QByteArrayLiteral("notificationId")); // id is QML-reserved
|
||||||
|
}
|
||||||
|
|
||||||
|
return s_roles;
|
||||||
|
}
|
||||||
|
|
||||||
|
void HistoryModel::add(const Notification ¬ification)
|
||||||
|
{
|
||||||
|
beginInsertRows(QModelIndex(), m_notifications.size(), m_notifications.size());
|
||||||
|
m_notifications.append(std::move(notification));
|
||||||
|
endInsertRows();
|
||||||
|
save();
|
||||||
|
}
|
||||||
|
|
||||||
|
void HistoryModel::remove(int index)
|
||||||
|
{
|
||||||
|
if (index > m_notifications.size() && index < 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
beginRemoveRows(QModelIndex(), index, index);
|
||||||
|
m_notifications.removeAt(index);
|
||||||
|
endRemoveRows();
|
||||||
|
save();
|
||||||
|
}
|
||||||
|
|
||||||
|
void HistoryModel::clearAll()
|
||||||
|
{
|
||||||
|
beginResetModel();
|
||||||
|
m_notifications.clear();
|
||||||
|
endResetModel();
|
||||||
|
save();
|
||||||
|
}
|
||||||
|
|
||||||
|
void HistoryModel::save()
|
||||||
|
{
|
||||||
|
QSettings settings(QSettings::UserScope, "cutefishos", "notifications");
|
||||||
|
settings.clear();
|
||||||
|
|
||||||
|
QByteArray datas;
|
||||||
|
QDataStream out(&datas, QIODevice::WriteOnly);
|
||||||
|
|
||||||
|
out << m_notifications;
|
||||||
|
|
||||||
|
settings.setValue("datas", datas);
|
||||||
|
}
|
||||||
|
|
||||||
|
void HistoryModel::initDatas()
|
||||||
|
{
|
||||||
|
QSettings settings(QSettings::UserScope, "cutefishos", "notifications");
|
||||||
|
QByteArray listByteArray = settings.value("datas").toByteArray();
|
||||||
|
QDataStream in(&listByteArray, QIODevice::ReadOnly);
|
||||||
|
in >> m_notifications;
|
||||||
|
}
|
||||||
@ -0,0 +1,42 @@
|
|||||||
|
#ifndef HISTORYMODEL_H
|
||||||
|
#define HISTORYMODEL_H
|
||||||
|
|
||||||
|
#include <QAbstractListModel>
|
||||||
|
#include "notification.h"
|
||||||
|
|
||||||
|
class HistoryModel : public QAbstractListModel
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
enum Roles {
|
||||||
|
IdRole = Qt::UserRole + 1,
|
||||||
|
SummaryRole = Qt::DisplayRole,
|
||||||
|
ImageRole = Qt::DecorationRole,
|
||||||
|
CreatedRole,
|
||||||
|
UpdatedRole,
|
||||||
|
BodyRole,
|
||||||
|
IconNameRole,
|
||||||
|
HasDefaultActionRole
|
||||||
|
};
|
||||||
|
Q_ENUM(Roles)
|
||||||
|
|
||||||
|
static HistoryModel* self();
|
||||||
|
explicit HistoryModel(QObject *parent = nullptr);
|
||||||
|
|
||||||
|
QVariant data(const QModelIndex &index, int role) const override;
|
||||||
|
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
|
||||||
|
QHash<int, QByteArray> roleNames() const override;
|
||||||
|
|
||||||
|
Q_INVOKABLE void add(const Notification ¬ification);
|
||||||
|
Q_INVOKABLE void remove(int index);
|
||||||
|
Q_INVOKABLE void clearAll();
|
||||||
|
Q_INVOKABLE void save();
|
||||||
|
|
||||||
|
void initDatas();
|
||||||
|
|
||||||
|
private:
|
||||||
|
QVector<Notification> m_notifications;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // HISTORYMODEL_H
|
||||||
@ -0,0 +1,18 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<svg version="1.1" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
|
||||||
|
<metadata>
|
||||||
|
<rdf:RDF>
|
||||||
|
<cc:Work rdf:about="">
|
||||||
|
<dc:format>image/svg+xml</dc:format>
|
||||||
|
<dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/>
|
||||||
|
<dc:title/>
|
||||||
|
</cc:Work>
|
||||||
|
</rdf:RDF>
|
||||||
|
</metadata>
|
||||||
|
<defs>
|
||||||
|
<style id="current-color-scheme" type="text/css">.ColorScheme-Text {
|
||||||
|
color:#363636;
|
||||||
|
}</style>
|
||||||
|
</defs>
|
||||||
|
<path class="ColorScheme-Text" d="m18.564 3.7559-3.2656 5.8184-2.416-1.4355-1.0215 1.7188 5.6504 3.3594 1.0215-1.7207-2.375-1.4121 3.2773-5.8398-0.87109-0.48828zm-10.564 3.2441a1 1 0 0 0-1 1 1 1 0 0 0 1 1 1 1 0 0 0 1-1 1 1 0 0 0-1-1zm-3.5 1a0.5 0.5 0 0 0-0.5 0.5 0.5 0.5 0 0 0 0.5 0.5 0.5 0.5 0 0 0 0.5-0.5 0.5 0.5 0 0 0-0.5-0.5zm1.5 2a1 1 0 0 0-1 1 1 1 0 0 0 1 1 1 1 0 0 0 1-1 1 1 0 0 0-1-1zm5 1c-1.1185 1.5643-3.1645 2.8109-7 3v1c2.1137 3.3717 5 5 9 5h1c1.3112-1.5872 2.3575-3.0324 3-5v-1l-5-3h-1zm0.5293 1.1016 4.3281 2.5977c-0.57283 1.5466-1.472 2.9036-2.584 4.2695-3.2734 0.03125-6.2207-1.4282-7.9473-4.1387 3.1182-0.31062 5.0195-1.2848 6.2031-2.7285z" fill="#fff"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.2 KiB |
@ -0,0 +1,13 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||||
|
<defs id="defs3051">
|
||||||
|
<style type="text/css" id="current-color-scheme">
|
||||||
|
.ColorScheme-Text {
|
||||||
|
color:#363636;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</defs>
|
||||||
|
<path style="fill:currentColor;fill-opacity:1;stroke:none"
|
||||||
|
d="M 18.564453 3.7558594 L 15.298828 9.5742188 L 12.882812 8.1386719 L 11.861328 9.8574219 L 17.511719 13.216797 L 18.533203 11.496094 L 16.158203 10.083984 L 19.435547 4.2441406 L 18.564453 3.7558594 z M 8 7 A 1 1 0 0 0 7 8 A 1 1 0 0 0 8 9 A 1 1 0 0 0 9 8 A 1 1 0 0 0 8 7 z M 4.5 8 A 0.5 0.5 0 0 0 4 8.5 A 0.5 0.5 0 0 0 4.5 9 A 0.5 0.5 0 0 0 5 8.5 A 0.5 0.5 0 0 0 4.5 8 z M 6 10 A 1 1 0 0 0 5 11 A 1 1 0 0 0 6 12 A 1 1 0 0 0 7 11 A 1 1 0 0 0 6 10 z M 11 11 C 9.8815125 12.564268 7.8355055 13.810894 4 14 L 4 15 C 6.1137135 18.371692 9 20 13 20 L 14 20 C 15.311249 18.412805 16.357513 16.967619 17 15 L 17 14 L 12 11 L 11 11 z M 11.529297 12.101562 L 15.857422 14.699219 C 15.284594 16.24586 14.385404 17.602833 13.273438 18.96875 C 9.999999 19 7.052768 17.540538 5.3261719 14.830078 C 8.4443879 14.51946 10.345716 13.545307 11.529297 12.101562 z "
|
||||||
|
class="ColorScheme-Text"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.2 KiB |
@ -0,0 +1,53 @@
|
|||||||
|
#include "notificationwindow.h"
|
||||||
|
#include "notificationsmodel.h"
|
||||||
|
#include "historymodel.h"
|
||||||
|
|
||||||
|
#include <QQmlContext>
|
||||||
|
|
||||||
|
#include <KWindowSystem>
|
||||||
|
#include <KWindowEffects>
|
||||||
|
|
||||||
|
NotificationWindow::NotificationWindow(QQuickView *parent)
|
||||||
|
: QQuickView(parent)
|
||||||
|
{
|
||||||
|
installEventFilter(this);
|
||||||
|
|
||||||
|
setFlags(Qt::Popup);
|
||||||
|
setResizeMode(QQuickView::SizeRootObjectToView);
|
||||||
|
setColor(Qt::transparent);
|
||||||
|
|
||||||
|
rootContext()->setContextProperty("NotificationDialog", this);
|
||||||
|
rootContext()->setContextProperty("notificationsModel", NotificationsModel::self());
|
||||||
|
rootContext()->setContextProperty("historyModel", HistoryModel::self());
|
||||||
|
|
||||||
|
// KWindowEffects::slideWindow(winId(), KWindowEffects::RightEdge);
|
||||||
|
setSource(QUrl("qrc:/qml/NotificationWindow.qml"));
|
||||||
|
setVisible(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void NotificationWindow::open()
|
||||||
|
{
|
||||||
|
setVisible(true);
|
||||||
|
setMouseGrabEnabled(true);
|
||||||
|
setKeyboardGrabEnabled(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool NotificationWindow::eventFilter(QObject *object, QEvent *event)
|
||||||
|
{
|
||||||
|
if (event->type() == QEvent::MouseButtonPress) {
|
||||||
|
if (QWindow *w = qobject_cast<QWindow*>(object)) {
|
||||||
|
if (!w->geometry().contains(static_cast<QMouseEvent*>(event)->globalPos())) {
|
||||||
|
QQuickView::setVisible(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (event->type() == QEvent::KeyPress) {
|
||||||
|
QKeyEvent *keyEvent = static_cast<QKeyEvent*>(event);
|
||||||
|
if (keyEvent->key() == Qt::Key_Escape) {
|
||||||
|
QQuickView::setVisible(false);
|
||||||
|
}
|
||||||
|
} else if (event->type() == QEvent::Show) {
|
||||||
|
KWindowSystem::setState(winId(), NET::SkipTaskbar | NET::SkipPager | NET::SkipSwitcher);
|
||||||
|
}
|
||||||
|
|
||||||
|
return QObject::eventFilter(object, event);
|
||||||
|
}
|
||||||
@ -0,0 +1,18 @@
|
|||||||
|
#ifndef NOTIFICATIONWINDOW_H
|
||||||
|
#define NOTIFICATIONWINDOW_H
|
||||||
|
|
||||||
|
#include <QQuickView>
|
||||||
|
|
||||||
|
class NotificationWindow : public QQuickView
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit NotificationWindow(QQuickView *parent = nullptr);
|
||||||
|
|
||||||
|
void open();
|
||||||
|
|
||||||
|
bool eventFilter(QObject *object, QEvent *event) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // NOTIFICATIONWINDOW_H
|
||||||
@ -0,0 +1,79 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2021 CutefishOS Team.
|
||||||
|
*
|
||||||
|
* Author: Reion Wong <reion@cutefishos.com>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import QtQuick 2.12
|
||||||
|
import QtQuick.Controls 2.12
|
||||||
|
import QtQuick.Layouts 1.12
|
||||||
|
import FishUI 1.0 as FishUI
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: control
|
||||||
|
|
||||||
|
property url source
|
||||||
|
property real size: 30
|
||||||
|
property string popupText
|
||||||
|
|
||||||
|
signal leftButtonClicked
|
||||||
|
signal rightButtonClicked
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
id: mouseArea
|
||||||
|
anchors.fill: parent
|
||||||
|
acceptedButtons: Qt.LeftButton | Qt.RightButton
|
||||||
|
hoverEnabled: control.visible ? true : false
|
||||||
|
|
||||||
|
onClicked: {
|
||||||
|
if (mouse.button === Qt.LeftButton)
|
||||||
|
control.leftButtonClicked()
|
||||||
|
else if (mouse.button === Qt.RightButton)
|
||||||
|
control.rightButtonClicked()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.margins: 1
|
||||||
|
// radius: parent.height * 0.2
|
||||||
|
radius: parent.height / 2
|
||||||
|
|
||||||
|
color: {
|
||||||
|
if (mouseArea.containsMouse) {
|
||||||
|
if (mouseArea.containsPress)
|
||||||
|
return (FishUI.Theme.darkMode) ? Qt.rgba(255, 255, 255, 0.3) : Qt.rgba(0, 0, 0, 0.2)
|
||||||
|
else
|
||||||
|
return (FishUI.Theme.darkMode) ? Qt.rgba(255, 255, 255, 0.2) : Qt.rgba(0, 0, 0, 0.1)
|
||||||
|
}
|
||||||
|
|
||||||
|
return "transparent"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Image {
|
||||||
|
id: iconImage
|
||||||
|
anchors.centerIn: parent
|
||||||
|
width: parent.height * 0.8
|
||||||
|
height: width
|
||||||
|
sourceSize.width: width
|
||||||
|
sourceSize.height: height
|
||||||
|
source: control.source
|
||||||
|
asynchronous: true
|
||||||
|
antialiasing: true
|
||||||
|
smooth: false
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,236 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2021 CutefishOS Team.
|
||||||
|
*
|
||||||
|
* Author: Reion Wong <reion@cutefishos.com>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import QtQuick 2.12
|
||||||
|
import QtQml 2.12
|
||||||
|
import QtQuick.Controls 2.12
|
||||||
|
import QtQuick.Layouts 1.12
|
||||||
|
import QtQuick.Window 2.12
|
||||||
|
import QtGraphicalEffects 1.0
|
||||||
|
import FishUI 1.0 as FishUI
|
||||||
|
import Cutefish.Notification 1.0
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: control
|
||||||
|
|
||||||
|
visible: true
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: _background
|
||||||
|
anchors.fill: parent
|
||||||
|
color: FishUI.Theme.secondBackgroundColor
|
||||||
|
radius: NotificationDialog.width * 0.05 //FishUI.Theme.bigRadius
|
||||||
|
opacity: 0.7
|
||||||
|
|
||||||
|
border.width: 1
|
||||||
|
border.color: FishUI.Theme.darkMode ? Qt.rgba(255, 255, 255, 0.1)
|
||||||
|
: Qt.rgba(0, 0, 0, 0.05)
|
||||||
|
}
|
||||||
|
|
||||||
|
readonly property rect screenRect: {
|
||||||
|
let rect = Qt.rect(screen.screenGeometry.x + screen.availableScreenRect.x,
|
||||||
|
screen.screenGeometry.y + screen.availableScreenRect.y,
|
||||||
|
screen.availableScreenRect.width,
|
||||||
|
screen.availableScreenRect.height)
|
||||||
|
return rect
|
||||||
|
}
|
||||||
|
|
||||||
|
onScreenRectChanged: {
|
||||||
|
NotificationDialog.width = 350
|
||||||
|
NotificationDialog.height = screenRect.height - FishUI.Units.smallSpacing * 3
|
||||||
|
NotificationDialog.x = screenRect.x + screenRect.width - NotificationDialog.width - FishUI.Units.smallSpacing * 1.5
|
||||||
|
NotificationDialog.y = screenRect.y + FishUI.Units.smallSpacing * 1.5
|
||||||
|
}
|
||||||
|
|
||||||
|
ScreenHelper {
|
||||||
|
id: screen
|
||||||
|
}
|
||||||
|
|
||||||
|
FishUI.WindowHelper {
|
||||||
|
id: windowHelper
|
||||||
|
}
|
||||||
|
|
||||||
|
FishUI.WindowShadow {
|
||||||
|
view: NotificationDialog
|
||||||
|
radius: _background.radius
|
||||||
|
}
|
||||||
|
|
||||||
|
FishUI.WindowBlur {
|
||||||
|
view: NotificationDialog
|
||||||
|
geometry: Qt.rect(NotificationDialog.x,
|
||||||
|
NotificationDialog.y,
|
||||||
|
NotificationDialog.width,
|
||||||
|
NotificationDialog.height)
|
||||||
|
windowRadius: _background.radius
|
||||||
|
enabled: true
|
||||||
|
}
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.margins: FishUI.Units.largeSpacing
|
||||||
|
spacing: FishUI.Units.largeSpacing
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
Label {
|
||||||
|
text: qsTr("Notification Center")
|
||||||
|
Layout.fillWidth: true
|
||||||
|
elide: Text.ElideRight
|
||||||
|
leftPadding: FishUI.Units.smallSpacing
|
||||||
|
color: FishUI.Theme.textColor
|
||||||
|
font.pointSize: 15
|
||||||
|
}
|
||||||
|
|
||||||
|
IconButton {
|
||||||
|
visible: _view.count > 0
|
||||||
|
Layout.preferredHeight: 30
|
||||||
|
Layout.preferredWidth: 30
|
||||||
|
source: FishUI.Theme.darkMode ? "qrc:/images/dark/clear.svg"
|
||||||
|
: "qrc:/images/light/clear.svg"
|
||||||
|
onLeftButtonClicked: historyModel.clearAll()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.fillHeight: true
|
||||||
|
|
||||||
|
ListView {
|
||||||
|
id: _view
|
||||||
|
anchors.fill: parent
|
||||||
|
model: historyModel
|
||||||
|
spacing: FishUI.Units.largeSpacing
|
||||||
|
highlightFollowsCurrentItem: true
|
||||||
|
clip: true
|
||||||
|
|
||||||
|
Label {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
text: qsTr("No notifications")
|
||||||
|
color: FishUI.Theme.disabledTextColor
|
||||||
|
font.pointSize: 15
|
||||||
|
visible: _view.count === 0
|
||||||
|
}
|
||||||
|
|
||||||
|
addDisplaced: Transition {
|
||||||
|
NumberAnimation { properties: "x, y"; duration: 300 }
|
||||||
|
}
|
||||||
|
|
||||||
|
removeDisplaced: Transition {
|
||||||
|
NumberAnimation { properties: "x, y"; duration: 300 }
|
||||||
|
}
|
||||||
|
|
||||||
|
delegate: Item {
|
||||||
|
width: ListView.view.width
|
||||||
|
height: 70
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
anchors.fill: parent
|
||||||
|
color: FishUI.Theme.darkMode ? "white"
|
||||||
|
: "black"
|
||||||
|
radius: FishUI.Theme.bigRadius
|
||||||
|
opacity: FishUI.Theme.darkMode ? 0.1
|
||||||
|
: 0.03
|
||||||
|
}
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.margins: FishUI.Units.smallSpacing
|
||||||
|
spacing: FishUI.Units.smallSpacing
|
||||||
|
|
||||||
|
Image {
|
||||||
|
id: _icon
|
||||||
|
width: 48
|
||||||
|
height: width
|
||||||
|
source: "image://icontheme/%1".arg(model.iconName)
|
||||||
|
sourceSize: Qt.size(width, height)
|
||||||
|
Layout.alignment: Qt.AlignVCenter
|
||||||
|
antialiasing: true
|
||||||
|
smooth: true
|
||||||
|
}
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
spacing: 0
|
||||||
|
|
||||||
|
Item {
|
||||||
|
Layout.fillHeight: true
|
||||||
|
}
|
||||||
|
|
||||||
|
Label {
|
||||||
|
text: model.summary
|
||||||
|
visible: text
|
||||||
|
elide: Text.ElideRight
|
||||||
|
Layout.fillWidth: true
|
||||||
|
rightPadding: FishUI.Units.smallSpacing
|
||||||
|
}
|
||||||
|
|
||||||
|
Label {
|
||||||
|
text: model.body
|
||||||
|
visible: text
|
||||||
|
rightPadding: FishUI.Units.smallSpacing
|
||||||
|
maximumLineCount: 2
|
||||||
|
elide: Text.ElideRight
|
||||||
|
wrapMode: Text.Wrap
|
||||||
|
Layout.fillWidth: true
|
||||||
|
// Layout.fillHeight: true
|
||||||
|
Layout.alignment: Qt.AlignVCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
Layout.fillHeight: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Image {
|
||||||
|
anchors.top: parent.top
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.topMargin: FishUI.Units.smallSpacing / 2
|
||||||
|
anchors.rightMargin: FishUI.Units.smallSpacing / 2
|
||||||
|
width: 24
|
||||||
|
height: 24
|
||||||
|
source: "qrc:/images/" + (FishUI.Theme.darkMode ? "dark" : "light") + "/close.svg"
|
||||||
|
sourceSize: Qt.size(width, height)
|
||||||
|
visible: true
|
||||||
|
z: 9999
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
property color hoveredColor: FishUI.Theme.darkMode ? Qt.lighter(FishUI.Theme.backgroundColor, 2)
|
||||||
|
: Qt.darker(FishUI.Theme.backgroundColor, 1.2)
|
||||||
|
property color pressedColor: FishUI.Theme.darkMode ? Qt.lighter(FishUI.Theme.backgroundColor, 1.5)
|
||||||
|
: Qt.darker(FishUI.Theme.backgroundColor, 1.3)
|
||||||
|
|
||||||
|
z: -1
|
||||||
|
anchors.fill: parent
|
||||||
|
color: _closeBtnArea.pressed ? pressedColor : _closeBtnArea.containsMouse ? hoveredColor : "transparent"
|
||||||
|
radius: height / 2
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
id: _closeBtnArea
|
||||||
|
anchors.fill: parent
|
||||||
|
hoverEnabled: true
|
||||||
|
onClicked: {
|
||||||
|
historyModel.remove(index)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,17 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!DOCTYPE TS>
|
||||||
|
<TS version="2.1">
|
||||||
|
<context>
|
||||||
|
<name>NotificationWindow</name>
|
||||||
|
<message>
|
||||||
|
<location filename="../qml/NotificationWindow.qml" line="91"/>
|
||||||
|
<source>Notification Center</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../qml/NotificationWindow.qml" line="123"/>
|
||||||
|
<source>No notifications</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
</context>
|
||||||
|
</TS>
|
||||||
@ -0,0 +1,17 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!DOCTYPE TS>
|
||||||
|
<TS version="2.1">
|
||||||
|
<context>
|
||||||
|
<name>NotificationWindow</name>
|
||||||
|
<message>
|
||||||
|
<location filename="../qml/NotificationWindow.qml" line="91"/>
|
||||||
|
<source>Notification Center</source>
|
||||||
|
<translation>通知中心</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../qml/NotificationWindow.qml" line="123"/>
|
||||||
|
<source>No notifications</source>
|
||||||
|
<translation>无通知</translation>
|
||||||
|
</message>
|
||||||
|
</context>
|
||||||
|
</TS>
|
||||||
Loading…
Reference in New Issue