Changeset View
Changeset View
Standalone View
Standalone View
applets/notifications/package/contents/ui/main.qml
1 | /*************************************************************************** | 1 | /* | ||
---|---|---|---|---|---|
2 | * Copyright 2011 Davide Bettio <davide.bettio@kdemail.net> * | 2 | * Copyright 2018-2019 Kai Uwe Broulik <kde@privat.broulik.de> | ||
3 | * Copyright 2011 Marco Martin <mart@kde.org> * | 3 | * | ||
4 | * * | 4 | * This program is free software; you can redistribute it and/or | ||
5 | * This program is free software; you can redistribute it and/or modify * | 5 | * modify it under the terms of the GNU General Public License as | ||
6 | * it under the terms of the GNU Library General Public License as published by * | 6 | * published by the Free Software Foundation; either version 2 of | ||
7 | * the Free Software Foundation; either version 2 of the License, or * | 7 | * the License or (at your option) version 3 or any later version | ||
8 | * (at your option) any later version. * | 8 | * accepted by the membership of KDE e.V. (or its successor approved | ||
9 | * * | 9 | * by the membership of KDE e.V.), which shall act as a proxy | ||
10 | * This program is distributed in the hope that it will be useful, * | 10 | * defined in Section 14 of version 3 of the license. | ||
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of * | 11 | * | ||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * | 12 | * This program is distributed in the hope that it will be useful, | ||
13 | * GNU Library General Public License for more details. * | 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
14 | * * | 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
15 | * You should have received a copy of the GNU Library General Public License * | 15 | * GNU General Public License for more details. | ||
16 | * along with this program; if not, write to the * | 16 | * | ||
17 | * Free Software Foundation, Inc., * | 17 | * You should have received a copy of the GNU General Public License | ||
18 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . * | 18 | * along with this program. If not, see <http://www.gnu.org/licenses/> | ||
19 | ***************************************************************************/ | 19 | */ | ||
20 | | ||||
21 | import QtQuick 2.8 | ||||
20 | 22 | | |||
21 | import QtQuick 2.0 | | |||
22 | import QtQuick.Layouts 1.1 | | |||
23 | import org.kde.plasma.plasmoid 2.0 | 23 | import org.kde.plasma.plasmoid 2.0 | ||
24 | import org.kde.plasma.core 2.0 as PlasmaCore | 24 | import org.kde.plasma.core 2.0 as PlasmaCore | ||
25 | import org.kde.plasma.components 2.0 as PlasmaComponents | | |||
26 | import org.kde.kquickcontrolsaddons 2.0 | 25 | import org.kde.kquickcontrolsaddons 2.0 | ||
27 | import org.kde.plasma.extras 2.0 as PlasmaExtras | | |||
28 | | ||||
29 | import org.kde.plasma.private.notifications 1.0 | | |||
30 | | ||||
31 | import "uiproperties.js" as UiProperties | | |||
32 | 26 | | |||
33 | MouseEventListener { | 27 | import org.kde.kcoreaddons 1.0 as KCoreAddons | ||
34 | id: notificationsApplet | 28 | import org.kde.kquickcontrolsaddons 2.0 as KQCAddons | ||
35 | //width: units.gridUnit.width * 10 | | |||
36 | //height: units.gridUnit.width * 15 | | |||
37 | 29 | | |||
38 | //Layout.minimumWidth: mainScrollArea.implicitWidth | 30 | import org.kde.notificationmanager 1.0 as NotificationManager | ||
39 | //Layout.minimumHeight: mainScrollArea.implicitHeight | | |||
40 | Layout.minimumWidth: 256 // FIXME: use above | | |||
41 | Layout.minimumHeight: 256 | | |||
42 | 31 | | |||
43 | LayoutMirroring.enabled: Qt.application.layoutDirection === Qt.RightToLeft | 32 | import "global" | ||
44 | LayoutMirroring.childrenInherit: true | | |||
45 | 33 | | |||
46 | property int layoutSpacing: UiProperties.layoutSpacing | 34 | Item { | ||
35 | id: root | ||||
47 | 36 | | |||
48 | property real globalProgress: 0 | 37 | Plasmoid.status: historyModel.activeJobsCount > 0 | ||
38 | || historyModel.unreadNotificationsCount > 0 | ||||
39 | || Globals.inhibited ? PlasmaCore.Types.ActiveStatus | ||||
40 | : PlasmaCore.Types.PassiveStatus | ||||
49 | 41 | | |||
50 | property Item notifications: historyList.headerItem ? historyList.headerItem.notifications : null | 42 | Plasmoid.toolTipSubText: { | ||
51 | property Item jobs: historyList.headerItem ? historyList.headerItem.jobs : null | 43 | var lines = []; | ||
52 | 44 | | |||
53 | //notifications + jobs | 45 | if (historyModel.activeJobsCount > 0) { | ||
54 | property int activeItemsCount: (notifications ? notifications.count : 0) + (jobs ? jobs.count : 0) | 46 | lines.push(i18np("%1 running job", "%1 running jobs", historyModel.activeJobsCount)); | ||
55 | property int totalCount: activeItemsCount + (notifications ? notifications.historyCount : 0) | 47 | } | ||
56 | 48 | | |||
57 | Plasmoid.switchWidth: units.gridUnit * 20 | 49 | // Any notification that is newer than "lastRead" is "unread" | ||
58 | Plasmoid.switchHeight: units.gridUnit * 30 | 50 | // since it doesn't know the popup is on screen which makes the user see it | ||
51 | var actualUnread = historyModel.unreadNotificationsCount - Globals.popupNotificationsModel.activeNotificationsCount; | ||||
52 | if (actualUnread > 0) { | ||||
53 | lines.push(i18np("%1 unread notification", "%1 unread notifications", actualUnread)); | ||||
54 | } | ||||
59 | 55 | | |||
60 | Plasmoid.status: activeItemsCount > 0 ? PlasmaCore.Types.ActiveStatus : PlasmaCore.Types.PassiveStatus | 56 | if (Globals.inhibited) { | ||
57 | var inhibitedUntil = notificationSettings.notificationsInhibitedUntil | ||||
58 | var inhibitedUntilValid = !isNaN(inhibitedUntil.getTime()); | ||||
61 | 59 | | |||
62 | Plasmoid.toolTipSubText: { | 60 | // TODO check app inhibition, too | ||
63 | if (activeItemsCount == 0) { | 61 | if (inhibitedUntilValid) { | ||
64 | return i18n("No notifications or jobs") | 62 | lines.push(i18n("Do not disturb until %1", | ||
65 | } else if (!notifications || !notifications.count) { | 63 | KCoreAddons.Format.formatRelativeDateTime(inhibitedUntil, Locale.ShortFormat))); | ||
66 | return i18np("%1 running job", "%1 running jobs", jobs.count) | | |||
67 | } else if (!jobs || !jobs.count) { | | |||
68 | return i18np("%1 notification", "%1 notifications", notifications.count) | | |||
69 | } else { | | |||
70 | return i18np("%1 running job", "%1 running jobs", jobs.count) + "\n" + i18np("%1 notification", "%1 notifications", notifications.count) | | |||
71 | } | 64 | } | ||
65 | } else if (lines.length === 0) { | ||||
66 | lines.push("No unread notificatons"); | ||||
72 | } | 67 | } | ||
73 | 68 | | |||
74 | Plasmoid.compactRepresentation: NotificationIcon { } | 69 | return lines.join("\n"); | ||
75 | | ||||
76 | // Always scroll to the top when opening as that's where the important stuff goes on | | |||
77 | Plasmoid.onExpandedChanged: { | | |||
78 | if (Plasmoid.expanded) { | | |||
79 | // contentY doesn't really work with ListView (creates and destroys delegates on demand and positions them randomly) | | |||
80 | // so first use its "move to the top" method and then move it further up to reveal all of its "header" contents | | |||
81 | historyList.positionViewAtBeginning(); | | |||
82 | historyList.contentY = historyList.originY; | | |||
83 | } | | |||
84 | } | 70 | } | ||
85 | 71 | | |||
86 | hoverEnabled: !UiProperties.touchInput | 72 | Plasmoid.switchWidth: units.gridUnit * 14 | ||
73 | Plasmoid.switchHeight: units.gridUnit * 10 | ||||
87 | 74 | | |||
88 | onActiveItemsCountChanged: { | 75 | Plasmoid.onExpandedChanged: { | ||
89 | if (!activeItemsCount) { | 76 | if (!plasmoid.expanded) { | ||
90 | plasmoid.expanded = false; | 77 | // FIXME Qt.callLater because system tray gets confused when an applet becomes passive when clicking to hide it | ||
91 | } | 78 | Qt.callLater(function() { | ||
79 | historyModel.lastRead = undefined; // reset to now | ||||
80 | historyModel.collapseAllGroups(); | ||||
81 | }); | ||||
92 | } | 82 | } | ||
93 | | ||||
94 | PlasmaExtras.Heading { | | |||
95 | width: parent.width | | |||
96 | level: 3 | | |||
97 | opacity: 0.6 | | |||
98 | visible: notificationsApplet.totalCount == 0 | | |||
99 | text: i18n("No new notifications.") | | |||
100 | } | 83 | } | ||
101 | 84 | | |||
102 | PlasmaExtras.ScrollArea { | 85 | Plasmoid.compactRepresentation: CompactRepresentation { | ||
103 | id: mainScrollArea | 86 | activeCount: Globals.popupNotificationsModel.activeNotificationsCount | ||
104 | anchors.fill: parent | 87 | unreadCount: Math.min(99, historyModel.unreadNotificationsCount) | ||
105 | 88 | | |||
106 | // HACK The history of notifications can become quite large. In order to avoid a memory leak | 89 | jobsCount: historyModel.activeJobsCount | ||
107 | // show them in a ListView which creates delegate instances only on demand. | 90 | jobsPercentage: historyModel.jobsPercentage | ||
108 | // The ListView's header functionality is abused to provide the jobs and regular notifications | | |||
109 | // which are few and might store some state inside the delegate (e.g. expanded state) and | | |||
110 | // thus are created all at once by a Repeater. | | |||
111 | ListView { | | |||
112 | id: historyList | | |||
113 | 91 | | |||
114 | // The history stuff is quite entangled with regular notifications, so | 92 | inhibited: Globals.inhibited | ||
115 | // model and delegate are set by Bindings {} inside Notifications.qml | 93 | } | ||
116 | | ||||
117 | header: Column { | | |||
118 | property alias jobs: jobsLoader.item | | |||
119 | property alias notifications: notificationsLoader.item | | |||
120 | 94 | | |||
121 | width: historyList.width | 95 | Plasmoid.fullRepresentation: FullRepresentation { | ||
122 | 96 | | |||
123 | Loader { | | |||
124 | id: jobsLoader | | |||
125 | width: parent.width | | |||
126 | source: "Jobs.qml" | | |||
127 | active: plasmoid.configuration.showJobs | | |||
128 | } | 97 | } | ||
129 | 98 | | |||
130 | Loader { | 99 | NotificationManager.Settings { | ||
131 | id: notificationsLoader | 100 | id: notificationSettings | ||
132 | width: parent.width | | |||
133 | source: "Notifications.qml" | | |||
134 | active: plasmoid.configuration.showNotifications | | |||
135 | } | 101 | } | ||
102 | | ||||
103 | NotificationManager.Notifications { | ||||
104 | id: historyModel | ||||
105 | showExpired: true | ||||
106 | showDismissed: true | ||||
107 | showJobs: notificationSettings.jobsInNotifications | ||||
108 | sortMode: NotificationManager.Notifications.SortByTypeAndUrgency | ||||
109 | groupMode: NotificationManager.Notifications.GroupApplicationsFlat | ||||
110 | groupLimit: 2 | ||||
111 | expandUnread: true | ||||
112 | blacklistedDesktopEntries: notificationSettings.historyBlacklistedApplications | ||||
113 | blacklistedNotifyRcNames: notificationSettings.historyBlacklistedServices | ||||
114 | urgencies: { | ||||
115 | var urgencies = NotificationManager.Notifications.CriticalUrgency | ||||
116 | | NotificationManager.Notifications.NormalUrgency; | ||||
117 | if (notificationSettings.lowPriorityHistory) { | ||||
118 | urgencies |= NotificationManager.Notifications.LowUrgency; | ||||
136 | } | 119 | } | ||
120 | return urgencies; | ||||
137 | } | 121 | } | ||
138 | } | 122 | } | ||
139 | 123 | | |||
140 | function action_clearNotifications() { | 124 | function action_clearHistory() { | ||
141 | notifications.clearNotifications(); | 125 | historyModel.clear(NotificationManager.Notifications.ClearExpired); | ||
142 | notifications.clearHistory(); | 126 | if (historyModel.count === 0) { | ||
127 | plasmoid.expanded = false; | ||||
128 | } | ||||
143 | } | 129 | } | ||
144 | 130 | | |||
145 | function action_notificationskcm() { | 131 | function action_openKcm() { | ||
146 | KCMShell.open("kcmnotify"); | 132 | KQCAddons.KCMShell.open("kcm_notifications"); | ||
147 | } | 133 | } | ||
148 | 134 | | |||
149 | Component.onCompleted: { | 135 | Component.onCompleted: { | ||
150 | plasmoid.setAction("clearNotifications", i18n("Clear Notifications"), "edit-clear-history") | 136 | Globals.adopt(plasmoid); | ||
151 | var clearAction = plasmoid.action("clearNotifications"); | 137 | | ||
138 | plasmoid.setAction("clearHistory", i18n("Clear History"), "edit-clear-history"); | ||||
139 | var clearAction = plasmoid.action("clearHistory"); | ||||
152 | clearAction.visible = Qt.binding(function() { | 140 | clearAction.visible = Qt.binding(function() { | ||
153 | return notificationsApplet.notifications && (notificationsApplet.notifications.count > 0 || notificationsApplet.notifications.historyCount > 0); | 141 | return historyModel.expiredNotificationsCount > 0; | ||
154 | }) | 142 | }); | ||
155 | 143 | | |||
156 | if (KCMShell.authorize("kcmnotify.desktop").length > 0) { | 144 | // FIXME only while Multi-page KCMs are broken when embedded in plasmoid config | ||
157 | plasmoid.setAction("notificationskcm", i18n("&Configure Event Notifications and Actions..."), "preferences-desktop-notification-bell") | 145 | plasmoid.setAction("openKcm", i18n("&Configure Event Notifications and Actions..."), "preferences-desktop-notification-bell"); | ||
158 | } | 146 | plasmoid.action("openKcm").visible = (KQCAddons.KCMShell.authorize("kcm_notifications.desktop").length > 0); | ||
159 | } | 147 | } | ||
160 | } | 148 | } |