Add notification window

pull/15/head
reionwong 5 years ago
parent d5b02178fd
commit 4b627ab25b

@ -1,8 +1,11 @@
set(SRCS main.cpp
application.cpp
screenhelper.cpp
notificationsmodel.cpp
notificationserver.cpp
notification.cpp
notificationwindow.cpp
historymodel.cpp
utils.cpp
dbus/notificationsadaptor.cpp
resources.qrc
@ -11,16 +14,29 @@ set(SRCS main.cpp
# for Ubuntu
# qt_add_dbus_adaptor(SRCS org.freedesktop.Notifications.xml notificationserver.h NotificationServer)
add_executable(cutefish-notificationd ${SRCS})
qt5_add_dbus_adaptor(DBUS_SOURCES com.cutefish.Notification.xml application.h Application)
set_source_files_properties(${DBUS_SOURCES} PROPERTIES SKIP_AUTOGEN ON)
find_package(KF5WindowSystem)
add_executable(cutefish-notificationd ${SRCS} ${DBUS_SOURCES})
target_link_libraries(cutefish-notificationd
Qt5::Core
Qt5::DBus
Qt5::Quick
Qt5::Widgets
KF5::WindowSystem
)
install(TARGETS cutefish-notificationd
DESTINATION /usr/bin
COMPONENT Runtime
)
file(GLOB TS_FILES translations/*.ts)
qt5_create_translation(QM_FILES ${TS_FILES})
add_custom_target(notificationd_translations DEPENDS ${QM_FILES} SOURCES ${TS_FILES})
add_dependencies(cutefish-notificationd notificationd_translations)
install(FILES ${QM_FILES} DESTINATION /usr/share/cutefish-notificationd/translations)

@ -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 &notification = 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 &notification)
{
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 &notification);
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

@ -17,26 +17,17 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <QApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>
#include "application.h"
#include "view.h"
#include <QQmlApplicationEngine>
#include "notificationsmodel.h"
#include "screenhelper.h"
int main(int argc, char *argv[])
{
// QCoreApplication::setAttribute(Qt::AA_UseHighDpiPixmaps, true);
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling, true);
QApplication a(argc, argv);
qmlRegisterType<NotificationsModel>("Cutefish.Notification", 1, 0, "NotificationsModel");
qmlRegisterType<ScreenHelper>("Cutefish.Notification", 1, 0, "ScreenHelper");
QQmlApplicationEngine engine;
engine.load(QUrl("qrc:/qml/main.qml"));
// QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling, true);
return a.exec();
Application a(argc, argv);
return a.run();
}

@ -18,3 +18,21 @@
*/
#include "notification.h"
QDataStream &operator<<(QDataStream &argument, const Notification &info)
{
argument << info.service << info.summary
<< info.body << info.appName << info.appIcon
<< info.created << info.updated;
return argument;
}
const QDataStream &operator>>(QDataStream &argument, Notification &info)
{
argument >> info.service >> info.summary;
argument >> info.body >> info.appName >> info.appIcon;
argument >> info.created >> info.updated;
return argument;
}

@ -22,6 +22,7 @@
#include <QObject>
#include <QDateTime>
#include <QMetaType>
class Notification
{
@ -37,6 +38,20 @@ public:
QDateTime created;
QDateTime updated;
inline bool operator==(const Notification &other) const {
return service == other.service &&
summary == other.summary &&
body == other.body &&
appName == other.appName &&
appIcon == other.appIcon &&
created == other.created &&
updated == other.updated;
}
friend QDataStream &operator<<(QDataStream &argument, const Notification &info);
friend const QDataStream &operator>>(QDataStream &argument, Notification &info);
};
Q_DECLARE_METATYPE(Notification)
#endif // NOTIFICATION_H

@ -5,12 +5,22 @@
*/
#include "notificationsmodel.h"
#include "historymodel.h"
#include "notification.h"
#include <QMetaEnum>
#include <QDebug>
static const int s_notificationsLimit = 1000;
static NotificationsModel *NOTIFICATIONS_MODEL = nullptr;
NotificationsModel *NotificationsModel::self()
{
if (!NOTIFICATIONS_MODEL)
NOTIFICATIONS_MODEL = new NotificationsModel;
return NOTIFICATIONS_MODEL;
}
NotificationsModel::NotificationsModel(QObject *parent)
: QAbstractListModel(parent)
@ -106,6 +116,15 @@ QHash<int, QByteArray> NotificationsModel::roleNames() const
return s_roles;
}
void NotificationsModel::expired(uint id)
{
int row = rowOfNotification(id);
if (row > -1) {
onNotificationRemoved(id, NotificationServer::CloseReason::Expired);
}
}
void NotificationsModel::close(uint id)
{
if (rowOfNotification(id) > -1) {
@ -221,7 +240,10 @@ void NotificationsModel::onNotificationRemoved(uint removedId, NotificationServe
// notification.setExpired(true);
notification.actions.clear();
emit dataChanged(idx, idx);
return;
HistoryModel::self()->add(notification);
// return;
}
// Otherwise if explicitly closed by either user or app, mark it for removal

@ -31,6 +31,7 @@ public:
};
Q_ENUM(Roles)
static NotificationsModel *self();
explicit NotificationsModel(QObject *parent = nullptr);
QVariant data(const QModelIndex &index, int role) const override;
@ -38,6 +39,8 @@ public:
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
QHash<int, QByteArray> roleNames() const override;
Q_INVOKABLE void expired(uint id);
Q_INVOKABLE void close(uint id);
Q_INVOKABLE void invokeDefaultAction(uint id);

@ -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
}
}

@ -166,7 +166,7 @@ Window {
interval: control.defaultTimeout
onTriggered: {
notificationsModel.close(model.notificationId)
notificationsModel.expired(model.notificationId)
}
}
}

@ -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)
}
}
}
}
}
}
}
}

@ -51,10 +51,6 @@ Item {
id: screen
}
NotificationsModel {
id: notificationsModel
}
function positionPopups() {
const screenRect = root.screenRect

@ -4,5 +4,9 @@
<file>qml/NotificationPopup.qml</file>
<file>images/dark/close.svg</file>
<file>images/light/close.svg</file>
<file>qml/NotificationWindow.qml</file>
<file>images/dark/clear.svg</file>
<file>images/light/clear.svg</file>
<file>qml/IconButton.qml</file>
</qresource>
</RCC>

@ -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>

@ -1,3 +1,22 @@
/*
* 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 "utils.h"
#include <QFile>

@ -1,3 +1,22 @@
/*
* 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 UTILS_H
#define UTILS_H

Loading…
Cancel
Save