Commit ff9ca8e4 authored by Kai Uwe Broulik's avatar Kai Uwe Broulik 🍇

Begin settings stuff and further cleanup

- Introduces a Settings item that can be used to query and write settings
  Used from both the applet and the KCM and in the future Task Manager and others
- Wire up delegate to make FullRepresentation somewhat work
- Fix text selection in NotificationItem (workaround QQC1 ScrollView bug)
- Make model writable where it makes sense (rather than having a dedicated setter)
- Let JobsModel also notice jobs that were present at the time of its creation
parent 79427e3d
......@@ -19,6 +19,7 @@
*/
import QtQuick 2.8
import QtQuick.Layouts 1.1
import org.kde.plasma.core 2.0 as PlasmaCore
import org.kde.plasma.components 2.0 as PlasmaComponents
......@@ -26,6 +27,20 @@ import org.kde.plasma.components 2.0 as PlasmaComponents
MouseArea {
id: compactRoot
// FIXME figure out a way how to let the compact icon not grow beond iconSizeHints
// but still let it expand eventually for a sidebar
/*readonly property bool inPanel: (plasmoid.location === PlasmaCore.Types.TopEdge
|| plasmoid.location === PlasmaCore.Types.RightEdge
|| plasmoid.location === PlasmaCore.Types.BottomEdge
|| plasmoid.location === PlasmaCore.Types.LeftEdge)
Layout.minimumWidth: plasmoid.formFactor === PlasmaCore.Types.Horizontal ? height : units.iconSizes.small
Layout.minimumHeight: plasmoid.formFactor === PlasmaCore.Types.Vertical ? width : (units.iconSizes.small + 2 * theme.mSize(theme.defaultFont).height)
Layout.maximumWidth: -1//inPanel ? units.iconSizeHints.panel : -1
Layout.maximumHeight: inPanel ? units.iconSizeHints.panel : -1*/
property int activeCount: 0
property int expiredCount: 0
......
......@@ -29,8 +29,8 @@ import org.kde.plasma.extras 2.0 as PlasmaExtras
import org.kde.notificationmanager 1.0 as NotificationManager
ColumnLayout {
Layout.preferredWidth: units.gridUnit * 22
Layout.preferredHeight: units.gridUnit * 28
Layout.preferredWidth: units.gridUnit * 18
Layout.preferredHeight: units.gridUnit * 24
Layout.fillHeight: plasmoid.formFactor === PlasmaCore.Types.Vertical
RowLayout {
......@@ -113,15 +113,17 @@ ColumnLayout {
ListView {
id: list
model: historyModel
// FIXME
/*section {
property: "applicationName"
criteria: ViewSection.FullString
delegate: NotificationDelegate {
width: list.width
applicationName: section
remove: Transition {
ParallelAnimation {
NumberAnimation { property: "opacity"; to: 0; duration: units.longDuration }
NumberAnimation { property: "x"; to: list.width; duration: units.longDuration }
}
}*/
}
removeDisplaced: Transition {
PauseAnimation { duration: units.longDuration }
NumberAnimation { properties: "y"; duration: units.longDuration }
}
delegate: Loader {
sourceComponent: model.isGroup ? groupDelegate : notificationDelegate
......@@ -144,6 +146,8 @@ ColumnLayout {
NotificationDelegate {
width: list.width
notificationType: model.type
applicationName: model.applicationName
applicatonIconSource: model.applicationIconName
......@@ -151,15 +155,45 @@ ColumnLayout {
configurable: model.configurable
// FIXME make the dismiss button a undismiss button
dismissable: model.type === NotificationManager.Notifications.JobType
&& model.jobState !== NotificationManager.Notifications.JobStateStopped
closable: model.type === NotificationManager.Notifications.NotificationType
|| model.jobState === NotificationManager.Notifications.JobStateStopped
summary: model.summary
body: model.body || "" // TODO
icon: model.image || model.iconName
// TODO everything else
urls: model.urls || []
jobState: model.jobState || 0
percentage: model.percentage || 0
error: model.error || 0
errorText: model.errorText || ""
suspendable: !!model.suspendable
killable: !!model.killable
jobDetails: model.jobDetails || null
configureActionLabel: model.configureActionLabel || ""
actionNames: model.actionNames
actionLabels: model.actionLabels
onCloseClicked: historyModel.close(historyModel.index(index, 0))
onDismissClicked: model.dismissed = false
onConfigureClicked: historyModel.configure(historyModel.index(index, 0))
onActionInvoked: historyModel.invokeAction(historyModel.index(index, 0), actionName)
onOpenUrl: {
Qt.openUrlExternally(url);
//historyModel.close(historyModel.index(index, 0))
}
onSuspendJobClicked: historyModel.suspendJob(historyModel.index(index, 0))
onResumeJobClicked: historyModel.resumeJob(historyModel.index(index, 0))
onKillJobClicked: historyModel.killJob(historyModel.index(index, 0))
// FIXME
svg: lineSvg
}
}
......
......@@ -27,6 +27,8 @@ import org.kde.plasma.core 2.0 as PlasmaCore
ColumnLayout {
id: delegate
property alias notificationType: notificationItem.notificationType
property alias applicationName: notificationItem.applicationName
property alias applicatonIconSource: notificationItem.applicationIconSource
......@@ -35,17 +37,35 @@ ColumnLayout {
property alias summary: notificationItem.summary
property alias body: notificationItem.body
property alias icon: notificationItem.icon
property alias urls: notificationItem.urls
property alias jobState: notificationItem.jobState
property alias percentage: notificationItem.percentage
property alias error: notificationItem.error
property alias errorText: notificationItem.errorText
property alias suspendable: notificationItem.suspendable
property alias killable: notificationItem.killable
property alias jobDetails: notificationItem.jobDetails
property alias configureActionLabel: notificationItem.configureActionLabel
property alias configurable: notificationItem.configurable
//property bool hasDefaultAction
//property alias actionNames: notificationItem.actionNames
//property alias actionLabels: notificationItem.actionLabels
property alias dismissable: notificationItem.dismissable
property alias closable: notificationItem.closable
property alias actionNames: notificationItem.actionNames
property alias actionLabels: notificationItem.actionLabels
signal closeClicked
signal configureClicked
signal dismissClicked
signal closeClicked
//signal defaultActionInvoked
//signal actionInvoked(string actionName)
//signal expired
signal actionInvoked(string actionName)
signal openUrl(string url)
signal suspendJobClicked
signal resumeJobClicked
signal killJobClicked
// FIXME
property alias svg: lineSvgItem.svg
......@@ -59,7 +79,15 @@ ColumnLayout {
closable: true
onCloseClicked: delegate.closeClicked()
onDismissClicked: delegate.dismissClicked()
onConfigureClicked: delegate.configureClicked()
onActionInvoked: delegate.actionInvoked(actionName)
onOpenUrl: delegate.openUrl(url)
onSuspendJobClicked: delegate.suspendJobClicked()
onResumeJobClicked: delegate.resumeJobClicked()
onKillJobClicked: delegate.killJobClicked()
}
PlasmaCore.SvgItem {
......
......@@ -26,9 +26,11 @@ import org.kde.plasma.core 2.0 as PlasmaCore
import org.kde.plasma.components 2.0 as PlasmaComponents
import org.kde.plasma.extras 2.0 as PlasmaExtras
// FIXME FIXME this thing doesn't work, doesn't scroll, etc :(
PlasmaExtras.ScrollArea {
id: bodyTextScrollArea
// NOTE This wrapper item is needed for QQC ScrollView to work
// In NotificationItem we just do SelectableLabel {} and then it gets confused
// as to which is the "contentItem"
Item {
id: bodyTextContainer
property alias text: bodyText.text
property alias font: bodyText.font
......@@ -38,104 +40,113 @@ PlasmaExtras.ScrollArea {
signal clicked(var mouse)
signal linkActivated(string link)
implicitWidth: bodyText.paintedWidth
implicitHeight: bodyText.paintedHeight
flickableItem.boundsBehavior: Flickable.StopAtBounds
flickableItem.flickableDirection: Flickable.VerticalFlick
horizontalScrollBarPolicy: Qt.ScrollBarAlwaysOff
TextEdit {
id: bodyText
width: bodyTextScrollArea.width
//enabled: !Settings.isMobile
color: PlasmaCore.ColorScope.textColor
selectedTextColor: theme.viewBackgroundColor
selectionColor: theme.viewFocusColor
font.capitalization: theme.defaultFont.capitalization
font.family: theme.defaultFont.family
font.italic: theme.defaultFont.italic
font.letterSpacing: theme.defaultFont.letterSpacing
font.pointSize: theme.defaultFont.pointSize
font.strikeout: theme.defaultFont.strikeout
font.underline: theme.defaultFont.underline
font.weight: theme.defaultFont.weight
font.wordSpacing: theme.defaultFont.wordSpacing
renderType: Text.NativeRendering
selectByMouse: true
readOnly: true
wrapMode: Text.Wrap
textFormat: TextEdit.RichText
onLinkActivated: bodyTextScrollArea.linkActivated(link)
// ensure selecting text scrolls the view as needed...
onCursorRectangleChanged: {
var flick = bodyTextScrollArea.flickableItem
if (flick.contentY >= cursorRectangle.y) {
flick.contentY = cursorRectangle.y
} else if (flick.contentY + flick.height <= cursorRectangle.y + cursorRectangle.height) {
flick.contentY = cursorRectangle.y + cursorRectangle.height - flick.height
}
}
MouseArea {
property int selectionStart
property point mouseDownPos: Qt.point(-999, -999);
anchors.fill: parent
acceptedButtons: Qt.RightButton | Qt.LeftButton
cursorShape: {
if (bodyText.hoveredLink) {
return Qt.PointingHandCursor;
} else if (bodyText.selectionStart !== bodyText.selectionEnd) {
return Qt.IBeamCursor;
} else {
return bodyTextScrollArea.cursorShape || Qt.IBeamCursor;
PlasmaExtras.ScrollArea {
id: bodyTextScrollArea
anchors.fill: parent
flickableItem.boundsBehavior: Flickable.StopAtBounds
flickableItem.flickableDirection: Flickable.VerticalFlick
horizontalScrollBarPolicy: Qt.ScrollBarAlwaysOff
TextEdit {
id: bodyText
width: bodyTextScrollArea.width
// TODO check that this doesn't causes infinite loops when it starts adding and removing the scrollbar
//width: bodyTextScrollArea.viewport.width
//enabled: !Settings.isMobile
color: PlasmaCore.ColorScope.textColor
selectedTextColor: theme.viewBackgroundColor
selectionColor: theme.viewFocusColor
font.capitalization: theme.defaultFont.capitalization
font.family: theme.defaultFont.family
font.italic: theme.defaultFont.italic
font.letterSpacing: theme.defaultFont.letterSpacing
font.pointSize: theme.defaultFont.pointSize
font.strikeout: theme.defaultFont.strikeout
font.underline: theme.defaultFont.underline
font.weight: theme.defaultFont.weight
font.wordSpacing: theme.defaultFont.wordSpacing
renderType: Text.NativeRendering
selectByMouse: true
readOnly: true
wrapMode: Text.Wrap
textFormat: TextEdit.RichText
onLinkActivated: bodyTextContainer.linkActivated(link)
// ensure selecting text scrolls the view as needed...
onCursorRectangleChanged: {
var flick = bodyTextScrollArea.flickableItem
if (flick.contentY >= cursorRectangle.y) {
flick.contentY = cursorRectangle.y
} else if (flick.contentY + flick.height <= cursorRectangle.y + cursorRectangle.height) {
flick.contentY = cursorRectangle.y + cursorRectangle.height - flick.height
}
}
preventStealing: true // don't let us accidentally drag the Flickable
onPressed: {
if (mouse.button === Qt.RightButton) {
var contextMenu = contextMenuComponent.createObject(bodyText);
contextMenu.link = bodyText.linkAt(mouse.x, mouse.y);
contextMenu.closed.connect(function() {
contextMenu.destroy();
});
contextMenu.open(mouse.x, mouse.y);
return;
MouseArea {
property int selectionStart
property point mouseDownPos: Qt.point(-999, -999);
anchors.fill: parent
acceptedButtons: Qt.RightButton | Qt.LeftButton
cursorShape: {
if (bodyText.hoveredLink) {
return Qt.PointingHandCursor;
} else if (bodyText.selectionStart !== bodyText.selectionEnd) {
return Qt.IBeamCursor;
} else {
return bodyTextContainer.cursorShape || Qt.IBeamCursor;
}
}
preventStealing: true // don't let us accidentally drag the Flickable
onPressed: {
if (mouse.button === Qt.RightButton) {
var contextMenu = contextMenuComponent.createObject(bodyText);
contextMenu.link = bodyText.linkAt(mouse.x, mouse.y);
contextMenu.closed.connect(function() {
contextMenu.destroy();
});
contextMenu.open(mouse.x, mouse.y);
return;
}
mouseDownPos = Qt.point(mouse.x, mouse.y);
selectionStart = bodyText.positionAt(mouse.x, mouse.y);
var pos = bodyText.positionAt(mouse.x, mouse.y);
// deselect() would scroll to the end which we don't want
bodyText.select(pos, pos);
}
mouseDownPos = Qt.point(mouse.x, mouse.y);
selectionStart = bodyText.positionAt(mouse.x, mouse.y);
var pos = bodyText.positionAt(mouse.x, mouse.y);
// deselect() would scroll to the end which we don't want
bodyText.select(pos, pos);
}
onReleased: {
// emulate "onClicked"
var manhattanLength = Math.abs(mouseDownPos.x - mouse.x) + Math.abs(mouseDownPos.y - mouse.y);
if (manhattanLength <= Qt.styleHints.startDragDistance) {
var link = bodyText.linkAt(mouse.x, mouse.y);
if (link) {
Qt.openUrlExternally(link);
} else {
bodyTextScrollArea.clicked(mouse);
onReleased: {
// emulate "onClicked"
var manhattanLength = Math.abs(mouseDownPos.x - mouse.x) + Math.abs(mouseDownPos.y - mouse.y);
if (manhattanLength <= Qt.styleHints.startDragDistance) {
var link = bodyText.linkAt(mouse.x, mouse.y);
if (link) {
Qt.openUrlExternally(link);
} else {
bodyTextContainer.clicked(mouse);
}
}
mouseDownPos = Qt.point(-999, -999);
}
mouseDownPos = Qt.point(-999, -999);
}
// HACK to be able to select text whilst still getting all mouse events to the MouseArea
onPositionChanged: {
if (pressed) {
var pos = bodyText.positionAt(mouseX, mouseY);
if (selectionStart < pos) {
bodyText.select(selectionStart, pos);
} else {
bodyText.select(pos, selectionStart);
// HACK to be able to select text whilst still getting all mouse events to the MouseArea
onPositionChanged: {
if (pressed) {
var pos = bodyText.positionAt(mouseX, mouseY);
if (selectionStart < pos) {
bodyText.select(selectionStart, pos);
} else {
bodyText.select(pos, selectionStart);
}
}
}
}
......
......@@ -67,23 +67,19 @@ Item {
}
NotificationManager.Settings {
id: notificationSettings
}
NotificationManager.Notifications {
id: historyModel
showExpired: true
showDismissed: true
showJobs: true
showJobs: notificationSettings.jobsInNotifications
groupMode: NotificationManager.Notifications.GroupApplicationsFlat
}
function action_notificationskcm() {
KCMShell.open("kcmnotify");
}
Component.onCompleted: {
if (KCMShell.authorize("kcmnotify.desktop").length > 0) {
plasmoid.setAction("notificationskcm", i18n("&Configure Event Notifications and Actions..."), "preferences-desktop-notification")
}
Popups.PopupHandler.adopt(plasmoid)
}
}
......@@ -32,8 +32,8 @@ PlasmaCore.Dialog {
id: notificationPopup
property alias notificationType: notificationItem.notificationType
readonly property bool isNotification: notificationType === NotificationManager.Notifications.NotificationType
readonly property bool isJob: notificationType === NotificationManager.Notifications.JobType
//readonly property bool isNotification: notificationType === NotificationManager.Notifications.NotificationType
//readonly property bool isJob: notificationType === NotificationManager.Notifications.JobType
property alias applicationName: notificationItem.applicationName
property alias applicatonIconSource: notificationItem.applicationIconSource
......@@ -44,7 +44,6 @@ PlasmaCore.Dialog {
property alias body: notificationItem.body
property alias icon: notificationItem.icon
property alias urls: notificationItem.urls
property alias deviceName: notificationItem.deviceName
property int urgency
property int timeout
......@@ -79,7 +78,6 @@ PlasmaCore.Dialog {
signal resumeJobClicked
signal killJobClicked
// TODO configurable and/or based on notification size
property int defaultTimeout: 5000
readonly property int effectiveTimeout: timeout === -1 ? defaultTimeout : timeout
......
......@@ -78,11 +78,13 @@ QtObject {
limit: Math.ceil(popupHandler.screenRect.height / (theme.mSize(theme.defaultFont).height * 4))
showExpired: false
showDismissed: false
showJobs: true
showJobs: notificationSettings.jobsInNotifications
groupMode: NotificationManager.Notifications.GroupDisabled
urgencies: NotificationManager.Notifications.NormalUrgency | NotificationManager.Notifications.CriticalUrgency
}
property QtObject notificationSettings: NotificationManager.Settings { }
function adopt(plasmoid) {
var newPlasmoids = plasmoids;
newPlasmoids.push(plasmoid);
......@@ -156,8 +158,6 @@ QtObject {
property Instantiator popupInstantiator: Instantiator {
model: popupNotificationsModel
delegate: NotificationPopup {
// TODO defaultTimeout configurable
notificationType: model.type
applicationName: model.applicationName
......@@ -178,10 +178,10 @@ QtObject {
icon: model.image || model.iconName
hasDefaultAction: model.hasDefaultAction || false
timeout: model.timeout
defaultTimeout: notificationSettings.popupTimeout
urls: model.urls || []
urgency: model.urgency
deviceName: model.deviceName || ""
jobState: model.jobState || 0
percentage: model.percentage || 0
......@@ -197,7 +197,7 @@ QtObject {
onExpired: popupNotificationsModel.expire(popupNotificationsModel.index(index, 0))
onCloseClicked: popupNotificationsModel.close(popupNotificationsModel.index(index, 0))
onDismissClicked: popupNotificationsModel.dismiss(popupNotificationsModel.index(index, 0))
onDismissClicked: model.dismissed = true
onConfigureClicked: popupNotificationsModel.configure(popupNotificationsModel.index(index, 0))
onDefaultActionInvoked: popupNotificationsModel.invokeDefaultAction(popupNotificationsModel.index(index, 0))
onActionInvoked: popupNotificationsModel.invokeAction(popupNotificationsModel.index(index, 0), actionName)
......
......@@ -23,3 +23,4 @@ X-KDE-Library=plasma_applet_newnotifications
X-Plasma-MainScript=ui/main.qml
X-Plasma-NotificationArea=true
X-Plasma-NotificationAreaCategory=ApplicationStatus
X-Plasma-ConfigPlugins=kcm_notifications
......@@ -29,11 +29,15 @@ ecm_qt_declare_logging_category(notificationmanager_LIB_SRCS
IDENTIFIER NOTIFICATIONMANAGER
CATEGORY_NAME org.kde.plasma.notifications)
kconfig_add_kcfg_files(notificationmanager_LIB_SRCS notificationsettings.kcfgc)
# Settings
kconfig_add_kcfg_files(notificationmanager_LIB_SRCS kcfg/donotdisturbsettings.kcfgc)
kconfig_add_kcfg_files(notificationmanager_LIB_SRCS kcfg/notificationsettings.kcfgc)
kconfig_add_kcfg_files(notificationmanager_LIB_SRCS kcfg/jobsettings.kcfgc)
kconfig_add_kcfg_files(notificationmanager_LIB_SRCS kcfg/badgesettings.kcfgc)
qt5_add_dbus_adaptor(notificationmanager_LIB_SRCS org.freedesktop.Notifications.xml notificationserver_p.h NotificationManager::NotificationServerPrivate fdonotificationsadaptor)
qt5_add_dbus_adaptor(notificationmanager_LIB_SRCS org.kde.Notifications.xml inhibitionserver.h NotificationManager::InhibitionServer kdenotificationsadaptor InhibitionAdaptor)
# DBus
qt5_add_dbus_adaptor(notificationmanager_LIB_SRCS dbus/org.freedesktop.Notifications.xml notificationserver_p.h NotificationManager::NotificationServerPrivate fdonotificationsadaptor)
qt5_add_dbus_adaptor(notificationmanager_LIB_SRCS dbus/org.kde.Notifications.xml inhibitionserver.h NotificationManager::InhibitionServer kdenotificationsadaptor InhibitionAdaptor)
add_library(notificationmanager ${notificationmanager_LIB_SRCS})
add_library(PW::LibNotificationManager ALIAS notificationmanager)
......@@ -49,10 +53,10 @@ target_link_libraries(notificationmanager
Qt5::Core
Qt5::Gui
Qt5::Quick
KF5::ConfigCore
KF5::ItemModels
PRIVATE
Qt5::DBus
KF5::ConfigCore
KF5::ConfigGui
KF5::Service
KF5::Plasma
......@@ -70,6 +74,7 @@ install(TARGETS notificationmanager EXPORT notificationmanagerLibraryTargets ${K
install(FILES
notificationserver.h
notification.h
settings.h
${CMAKE_CURRENT_BINARY_DIR}/notificationmanager_export.h
DESTINATION ${KDE_INSTALL_INCLUDEDIR}/notificationmanager COMPONENT Devel
)
......@@ -90,5 +95,3 @@ install(EXPORT notificationmanagerLibraryTargets
NAMESPACE PW::
DESTINATION ${CMAKECONFIG_INSTALL_DIR}
FILE LibNotificationManagerLibraryTargets.cmake )
#install(FILES notificationsettings.kcfg DESTINATION ${KCFG_INSTALL_DIR} RENAME plasmanotify.kcfg)
......@@ -6,4 +6,4 @@ find_dependency(Qt5Gui "@QT_MIN_VERSION@")
find_dependency(Qt5Quick "@QT_MIN_VERSION@")
find_dependency(KF5ItemModels "@KF5_MIN_VERSION@")
include("${CMAKE_CURRENT_LIST_DIR}/LibTaskManagerLibraryTargets.cmake")
include("${CMAKE_CURRENT_LIST_DIR}/LibNotificationManagerLibraryTargets.cmake")
......@@ -22,6 +22,7 @@
#include "notifications.h"
#include "jobdetails.h"
#include "settings.h"
#include <QQmlEngine>
......@@ -33,4 +34,5 @@ void NotificationManagerPlugin::registerTypes(const char *uri)
qmlRegisterType<Notifications>(uri, 1, 0, "Notifications");
qmlRegisterType<JobDetails>();
qmlRegisterType<Settings>(uri, 1, 0, "Settings");
}
......@@ -23,7 +23,6 @@
#include "notifications.h"
#include <QDebug>
#include <QScopedPointer>
#include <KJob>
......@@ -43,6 +42,8 @@ public:
explicit Private(JobsModel *q);
~Private();
void onSourceAdded(const QString &source);
void onSourceRemoved(const QString &source);
void operationCall(const QString &source, const QString &operation);
JobsModel *q;
......@@ -51,9 +52,11 @@ public:
Plasma::DataEngine *dataEngine;
QStringList sources;
// FIXME why is this a pointer?
// Try making it not anymore and make sure jobdetails has
// a copy constructor that copies its private stuff
QHash<QString, Job *> jobs;