diff --git a/applets/notifications/package/contents/ui/NotificationItem.qml b/applets/notifications/package/contents/ui/NotificationItem.qml index 4da475c96..42f20bc36 100644 --- a/applets/notifications/package/contents/ui/NotificationItem.qml +++ b/applets/notifications/package/contents/ui/NotificationItem.qml @@ -1,357 +1,357 @@ /* * Copyright 2011 Marco Martin * Copyright 2014 Kai Uwe Broulik * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library General Public License as * published by the Free Software Foundation; either version 2, or * (at your option) 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 Library General Public License for more details * * You should have received a copy of the GNU Library General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ import QtQuick 2.0 import QtQuick.Layouts 1.1 import QtQuick.Controls.Private 1.0 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 import org.kde.kquickcontrolsaddons 2.0 MouseArea { id: notificationItem width: parent.width - implicitHeight: Math.max(appIconItem.visible || imageItem.visible ? units.iconSizes.large : 0, mainLayout.height) + implicitHeight: Math.max(appIconItem.valid || imageItem.nativeWidth > 0 ? units.iconSizes.large : 0, mainLayout.height) // We need to clip here because we support displaying images through // and if we don't clip, they will be painted over the borders of the dialog/item clip: true signal close signal configure signal action(string actionId) signal openUrl(url url) property bool compact: false property alias icon: appIconItem.source property alias image: imageItem.image property alias summary: summaryLabel.text property alias body: bodyText.text property alias configurable: settingsButton.visible property var created property var urls: [] property int maximumTextHeight: -1 property ListModel actions: ListModel { } property bool hasDefaultAction: false property bool hasConfigureAction: false readonly property bool dragging: thumbnailStripLoader.item ? thumbnailStripLoader.item.dragging : false onClicked: { // the MEL would close the notification before the action button // onClicked handler would fire effectively breaking notification actions if (pressedAction()) { return } if (hasDefaultAction) { // the notifications was clicked, trigger the default action if set action("default") } } function pressedAction() { for (var i = 0, count = actionRepeater.count; i < count; ++i) { var item = actionRepeater.itemAt(i) if (item.pressed) { return item } } if (thumbnailStripLoader.item) { var item = thumbnailStripLoader.item.pressedAction() if (item) { return item } } if (settingsButton.pressed) { return settingsButton } if (closeButton.pressed) { return closeButton } return null } function updateTimeLabel() { if (!created || created.getTime() <= 0) { timeLabel.text = "" return } var currentTime = new Date().getTime() var createdTime = created.getTime() var d = (currentTime - createdTime) / 1000 if (d < 10) { timeLabel.text = i18nc("notification was just added, keep short", "Just now") } else if (d < 20) { timeLabel.text = i18nc("10 seconds ago, keep short", "10 s ago"); } else if (d < 40) { timeLabel.text = i18nc("30 seconds ago, keep short", "30 s ago"); } else if (d < 60 * 60) { timeLabel.text = i18ncp("minutes ago, keep short", "%1 min ago", "%1 min ago", Math.round(d / 60)) } else if (d <= 60 * 60 * 23) { timeLabel.text = Qt.formatTime(created, Qt.locale().timeFormat(Locale.ShortFormat).replace(/.ss?/i, "")) } else { var yesterday = new Date() yesterday.setDate(yesterday.getDate() - 1) // this will wrap yesterday.setHours(0) yesterday.setMinutes(0) yesterday.setSeconds(0) if (createdTime > yesterday.getTime()) { timeLabel.text = i18nc("notification was added yesterday, keep short", "Yesterday"); } else { timeLabel.text = i18ncp("notification was added n days ago, keep short", "%1 day ago", "%1 days ago", Math.round((currentTime - yesterday.getTime()) / 1000 / 3600 / 24)); } } } Timer { interval: 15000 running: plasmoid.expanded repeat: true triggeredOnStart: true onTriggered: updateTimeLabel() } PlasmaCore.IconItem { id: appIconItem width: units.iconSizes.large height: units.iconSizes.large anchors { top: parent.top left: parent.left } - visible: !imageItem.visible && valid + visible: imageItem.nativeWidth == 0 && valid animated: false } QImageItem { id: imageItem anchors.fill: appIconItem smooth: true visible: nativeWidth > 0 } ColumnLayout { id: mainLayout anchors { top: parent.top - left: appIconItem.visible || imageItem.visible ? appIconItem.right : parent.left + left: appIconItem.valid || imageItem.nativeWidth > 0 ? appIconItem.right : parent.left right: parent.right leftMargin: units.smallSpacing } spacing: Math.round(units.smallSpacing / 2) RowLayout { id: titleBar spacing: units.smallSpacing PlasmaExtras.Heading { id: summaryLabel Layout.fillWidth: true Layout.fillHeight: true verticalAlignment: Text.AlignVCenter level: 4 elide: Text.ElideRight wrapMode: Text.NoWrap } PlasmaExtras.Heading { id: timeLabel Layout.fillHeight: true level: 5 visible: text !== "" verticalAlignment: Text.AlignVCenter PlasmaCore.ToolTipArea { anchors.fill: parent subText: Qt.formatDateTime(created, Qt.DefaultLocaleLongDate) } } PlasmaComponents.ToolButton { id: settingsButton width: units.iconSizes.smallMedium height: width visible: false iconSource: "configure" onClicked: { if (notificationItem.hasConfigureAction) { notificationItem.action("settings"); } else { configure() } } } PlasmaComponents.ToolButton { id: closeButton width: units.iconSizes.smallMedium height: width flat: compact iconSource: "window-close" onClicked: close() } } RowLayout { id: bottomPart Layout.alignment: Qt.AlignTop spacing: units.smallSpacing // Force the whole thing to collapse if the children are invisible // If there is a big notification followed by a small one, the height // of the popup does not always shrink back, so this forces it to // height=0 when those are invisible. -1 means "default to implicitHeight" - Layout.maximumHeight: bodyText.visible || actionsColumn.visible ? -1 : 0 + Layout.maximumHeight: bodyText.length > 0 || notificationItem.actions.count > 0 ? -1 : 0 PlasmaExtras.ScrollArea { id: bodyTextScrollArea Layout.alignment: Qt.AlignTop Layout.fillWidth: true implicitHeight: maximumTextHeight > 0 ? Math.min(maximumTextHeight, bodyText.paintedHeight) : bodyText.paintedHeight visible: bodyText.length > 0 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: Qt.openUrlExternally(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 { anchors.fill: parent acceptedButtons: Qt.RightButton | Qt.LeftButton onClicked: { if (mouse.button == Qt.RightButton) contextMenu.open(mouse.x, mouse.y) else { notificationItem.clicked(mouse) } } PlasmaComponents.ContextMenu { id: contextMenu visualParent: parent PlasmaComponents.MenuItem { text: i18n("Copy") onClicked: { bodyText.selectAll() bodyText.copy() } } } } } } ColumnLayout { id: actionsColumn Layout.alignment: Qt.AlignTop Layout.maximumWidth: theme.mSize(theme.defaultFont).width * (compact ? 10 : 16) // this is so it never collapses but always follows what the Buttons below want // but also don't let the buttons get too narrow (e.g. "View" or "Open" button) Layout.minimumWidth: Math.max(units.gridUnit * 4, implicitWidth) spacing: units.smallSpacing visible: notificationItem.actions && notificationItem.actions.count > 0 Repeater { id: actionRepeater model: notificationItem.actions PlasmaComponents.Button { Layout.fillWidth: true Layout.preferredWidth: minimumWidth Layout.maximumWidth: actionsColumn.Layout.maximumWidth text: model.text onClicked: notificationItem.action(model.id) } } } } Loader { id: thumbnailStripLoader Layout.fillWidth: true Layout.preferredHeight: item ? item.implicitHeight : 0 source: "ThumbnailStrip.qml" active: notificationItem.urls.length > 0 } } } diff --git a/applets/notifications/package/contents/ui/NotificationPopup.qml b/applets/notifications/package/contents/ui/NotificationPopup.qml index 3108655f2..2a9672a7c 100644 --- a/applets/notifications/package/contents/ui/NotificationPopup.qml +++ b/applets/notifications/package/contents/ui/NotificationPopup.qml @@ -1,138 +1,139 @@ /* * Copyright 2014 Martin Klapetek * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library General Public License as * published by the Free Software Foundation; either version 2, or * (at your option) 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 Library General Public License for more details * * You should have received a copy of the GNU Library General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ import QtQuick 2.0 import QtQuick.Controls.Private 1.0 import QtQuick.Layouts 1.1 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 import org.kde.kquickcontrolsaddons 2.0 import org.kde.kquickcontrolsaddons 2.0 as KQuickControlsAddons PlasmaCore.Dialog { id: notificationPopup location: PlasmaCore.Types.Floating type: PlasmaCore.Dialog.Notification flags: Qt.WindowDoesNotAcceptFocus property var notificationProperties: ({}) signal notificationTimeout() onVisibleChanged: { if (!visible) { notificationTimer.stop(); } } onYChanged: { if (visible) { notificationTimer.restart(); } } function populatePopup(notification) { notificationProperties = notification notificationTimer.interval = notification.expireTimeout - notificationTimer.restart() - + notificationTimer.restart(); + //temporarly disable height binding, avoids an useless window resize when removing the old actions + heightBinding.when = false; // notification.actions is a JS array, but we can easily append that to our model notificationItem.actions.clear() notificationItem.actions.append(notificationProperties.actions) + //enable height binding again, finally do the resize + heightBinding.when = true; } function clearPopup() { notificationProperties = {} notificationItem.actions.clear() } - Behavior on y { - NumberAnimation { - duration: units.longDuration - easing.type: Easing.OutQuad - } - } - mainItem: NotificationItem { id: notificationItem hoverEnabled: true LayoutMirroring.enabled: Qt.application.layoutDirection === Qt.RightToLeft LayoutMirroring.childrenInherit: true - height: implicitHeight + //the binding needs to be disabled when re-populating actions, to minimize resizes + Binding on height { + id: heightBinding + value: notificationItem.implicitHeight + when: true + } Timer { id: notificationTimer onTriggered: { if (!notificationProperties.isPersistent) { expireNotification(notificationProperties.source) } notificationPopup.notificationTimeout(); } } onContainsMouseChanged: { if (containsMouse) { notificationTimer.stop() } else if (!containsMouse && !dragging && visible) { notificationTimer.restart() } } onDraggingChanged: { if (dragging) { notificationTimer.stop() } else if (!containsMouse && !dragging && visible) { notificationTimer.restart() } } summary: notificationProperties.summary || "" body: notificationProperties.body || "" icon: notificationProperties.appIcon || "" image: notificationProperties.image // explicit true/false or else it complains about assigning undefined to bool configurable: notificationProperties.configurable && !Settings.isMobile ? true : false urls: notificationProperties.urls || [] hasDefaultAction: notificationProperties.hasDefaultAction || false hasConfigureAction: notificationProperties.hasConfigureAction || false width: Math.round(23 * units.gridUnit) maximumTextHeight: theme.mSize(theme.defaultFont).height * 10 onClose: { closeNotification(notificationProperties.source) // the popup will be closed in response to sourceRemoved } onConfigure: { configureNotification(notificationProperties.appRealName, notificationProperties.eventId) notificationPositioner.closePopup(notificationProperties.source); } onAction: { executeAction(notificationProperties.source, actionId) actions.clear() } onOpenUrl: { Qt.openUrlExternally(url) notificationPositioner.closePopup(notificationProperties.source); } } } diff --git a/applets/notifications/plugin/notificationshelper.cpp b/applets/notifications/plugin/notificationshelper.cpp index 187f40f36..3f861b32f 100644 --- a/applets/notifications/plugin/notificationshelper.cpp +++ b/applets/notifications/plugin/notificationshelper.cpp @@ -1,358 +1,329 @@ /* Copyright (C) 2014 Martin Klapetek This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "notificationshelper.h" #include #include #include #include #include #include #include #include NotificationsHelper::NotificationsHelper(QObject *parent) : QObject(parent), m_popupLocation(NotificationsHelper::BottomRight), m_busy(false) { m_mutex = new QReadWriteLock(QReadWriteLock::Recursive); m_offset = QFontMetrics(QGuiApplication::font()).boundingRect(QStringLiteral("M")).height(); m_dispatchTimer = new QTimer(this); m_dispatchTimer->setInterval(500); m_dispatchTimer->setSingleShot(true); connect(m_dispatchTimer, &QTimer::timeout, [this](){m_busy = false; processQueues();}); } NotificationsHelper::~NotificationsHelper() { qDeleteAll(m_availablePopups); qDeleteAll(m_popupsOnScreen); delete m_mutex; } void NotificationsHelper::setPopupLocation(PositionOnScreen popupLocation) { if (m_popupLocation != popupLocation) { m_popupLocation = popupLocation; emit popupLocationChanged(); repositionPopups(); } } void NotificationsHelper::setPlasmoidScreenGeometry(const QRect &plasmoidScreenGeometry) { m_plasmoidScreen = plasmoidScreenGeometry; repositionPopups(); } void NotificationsHelper::addNotificationPopup(QObject *win) { QQuickWindow *popup = qobject_cast(win); m_availablePopups.append(popup); // Don't let QML ever delete this component QQmlEngine::setObjectOwnership(win, QQmlEngine::CppOwnership); connect(win, SIGNAL(notificationTimeout()), this, SLOT(onPopupClosed())); connect(popup, &QWindow::heightChanged, this, &NotificationsHelper::repositionPopups, Qt::UniqueConnection); - connect(popup, &QWindow::visibleChanged, this, &NotificationsHelper::onPopupShown, Qt::UniqueConnection); - popup->setProperty("initialPositionSet", false); -} - -void NotificationsHelper::onPopupShown() -{ - QWindow *popup = qobject_cast(sender()); - if (!popup || !popup->isVisible()) { - return; - } - - // Make sure Dialog lays everything out and gets proper geometry - QMetaObject::invokeMethod(popup, "updateVisibility", Qt::DirectConnection, Q_ARG(bool, true)); - - // Now we can position the popups properly as the geometry is now known - repositionPopups(); + //We are sure that after visibleChanged the size is final + //and the first expose event didn't arrive yet + connect(popup, &QQuickWindow::visibleChanged, this, &NotificationsHelper::repositionPopups); } void NotificationsHelper::processQueues() { if (m_busy) { return; } m_mutex->lockForRead(); bool shouldProcessShow = !m_showQueue.isEmpty() && !m_availablePopups.isEmpty(); m_mutex->unlock(); if (shouldProcessShow) { m_busy = true; processShow(); // Return here, makes the movement more clear and easier to follow return; } m_mutex->lockForRead(); bool shouldProcessHide = !m_hideQueue.isEmpty(); m_mutex->unlock(); if (shouldProcessHide) { m_busy = true; processHide(); } } void NotificationsHelper::processShow() { m_mutex->lockForWrite(); const QVariantMap notificationData = m_showQueue.takeFirst(); m_mutex->unlock(); QString sourceName = notificationData.value(QStringLiteral("source")).toString(); // Try getting existing popup for the given source // (case of notification being just updated) QQuickWindow *popup = m_sourceMap.value(sourceName); if (!popup) { // No existing notification for the given source, // take one from the available popups m_mutex->lockForWrite(); popup = m_availablePopups.takeFirst(); m_popupsOnScreen << popup; m_sourceMap.insert(sourceName, popup); m_mutex->unlock(); // Set the source name directly on the popup object too // to avoid looking up the notificationProperties map as above popup->setProperty("sourceName", sourceName); } // Populate the popup with data, this is the component's own QML method QMetaObject::invokeMethod(popup, "populatePopup", Qt::DirectConnection, Q_ARG(QVariant, notificationData)); Q_EMIT popupShown(popup); - QTimer::singleShot(300, popup, &QWindow::show); + //use setproperty so the Dialog reimplementation will be used + popup->setProperty("visible", true); if (!m_dispatchTimer->isActive()) { m_dispatchTimer->start(); } } void NotificationsHelper::processHide() { m_mutex->lockForWrite(); QQuickWindow *popup = m_hideQueue.takeFirst(); m_mutex->unlock(); if (popup) { m_mutex->lockForWrite(); // Remove the popup from the active list and return it into the available list m_popupsOnScreen.removeAll(popup); m_sourceMap.remove(popup->property("sourceName").toString()); if (!m_availablePopups.contains(popup)) { // make extra sure that pointers in here aren't doubled m_availablePopups.append(popup); } m_mutex->unlock(); popup->hide(); - // Make sure the popup gets placed correctly - // next time it's put on screen - popup->setProperty("initialPositionSet", false); - QMetaObject::invokeMethod(popup, "clearPopup", Qt::DirectConnection); } m_mutex->lockForRead(); bool shouldReposition = !m_popupsOnScreen.isEmpty();// && m_showQueue.isEmpty(); m_mutex->unlock(); if (shouldReposition) { repositionPopups(); } if (!m_dispatchTimer->isActive()) { m_dispatchTimer->start(); } } void NotificationsHelper::displayNotification(const QVariantMap ¬ificationData) { if (notificationData.isEmpty()) { return; } QVariant sourceName = notificationData.value(QStringLiteral("source")); // first check if we don't already have data for the same source // which would mean that the notification was just updated // so remove the old one and append the newest data only QMutableListIterator i(m_showQueue); while (i.hasNext()) { if (i.next().value(QStringLiteral("source")) == sourceName) { m_mutex->lockForWrite(); i.remove(); m_mutex->unlock(); } } // ...also look into the hide queue, if it's already queued // for hiding, we need to remove it from there otherwise // it will get closed too soon QMutableListIterator j(m_hideQueue); while (j.hasNext()) { if (j.next()->property("sourceName") == sourceName) { m_mutex->lockForWrite(); j.remove(); m_mutex->unlock(); } } m_mutex->lockForWrite(); m_showQueue.append(notificationData); m_mutex->unlock(); if (!m_dispatchTimer->isActive()) { // If the dispatch timer is not already running, process // the queues directly, that should cut the time between // notification emitting the event and popup displaying processQueues(); } } void NotificationsHelper::closePopup(const QString &sourceName) { QQuickWindow *popup = m_sourceMap.value(sourceName); m_mutex->lockForRead(); bool shouldQueue = popup && !m_hideQueue.contains(popup); m_mutex->unlock(); // Make sure the notification that was closed (programatically) // is not in the show queue. This is important otherwise that // notification will be shown and then never closed (because // the close event arrives here, before it's even shown) QMutableListIterator i(m_showQueue); while (i.hasNext()) { if (i.next().value(QStringLiteral("source")) == sourceName) { m_mutex->lockForWrite(); i.remove(); m_mutex->unlock(); } } if (shouldQueue) { m_mutex->lockForWrite(); m_hideQueue.append(popup); m_mutex->unlock(); if (!m_dispatchTimer->isActive()) { processQueues(); } } } void NotificationsHelper::onPopupClosed() { QQuickWindow *popup = qobject_cast(sender()); m_mutex->lockForRead(); bool shouldQueue = popup && !m_hideQueue.contains(popup); m_mutex->unlock(); if (shouldQueue) { m_mutex->lockForWrite(); m_hideQueue << popup; m_mutex->unlock(); if (!m_dispatchTimer->isActive()) { processQueues(); } } } void NotificationsHelper::repositionPopups() { int cumulativeHeight = m_offset; m_mutex->lockForWrite(); + QPoint pos; + for (int i = 0; i < m_popupsOnScreen.size(); ++i) { if (m_popupLocation == NotificationsHelper::TopLeft || m_popupLocation == NotificationsHelper::TopCenter || m_popupLocation == NotificationsHelper::TopRight) { - int posY = m_plasmoidScreen.top() + cumulativeHeight; + pos.setY(m_plasmoidScreen.top() + cumulativeHeight); - if (m_popupsOnScreen[i]->isVisible() && m_popupsOnScreen[i]->property("initialPositionSet").toBool() == true && m_popupsOnScreen[i]->y() != 0) { - //if it's visible, go through setProperty which animates it - m_popupsOnScreen[i]->setProperty("y", posY); - } else { - // ...otherwise just set it directly - m_popupsOnScreen[i]->setY(posY); - m_popupsOnScreen[i]->setProperty("initialPositionSet", true); - } } else { - int posY = m_plasmoidScreen.bottom() - cumulativeHeight - m_popupsOnScreen[i]->contentItem()->height(); - - if (m_popupsOnScreen[i]->isVisible() && m_popupsOnScreen[i]->property("initialPositionSet").toBool() == true && m_popupsOnScreen[i]->y() != 0) { - m_popupsOnScreen[i]->setProperty("y", posY); - } else { - m_popupsOnScreen[i]->setY(posY); - m_popupsOnScreen[i]->setProperty("initialPositionSet", true); - } + pos.setY(m_plasmoidScreen.bottom() - cumulativeHeight - m_popupsOnScreen[i]->height()); } switch (m_popupLocation) { case Default: //This should not happen as the defualt handling is in NotificationApplet::onScreenPositionChanged Q_ASSERT(false); qWarning("Notication popupLocation is still \"default\". This should not happen"); //fall through to top right case TopRight: case BottomRight: - m_popupsOnScreen[i]->setX(m_plasmoidScreen.right() - m_popupsOnScreen[i]->contentItem()->width() - m_offset); + pos.setX(m_plasmoidScreen.right() - m_popupsOnScreen[i]->width() - m_offset); break; case TopCenter: case BottomCenter: - m_popupsOnScreen[i]->setX(m_plasmoidScreen.x() + (m_plasmoidScreen.width() / 2) - (m_popupsOnScreen[i]->contentItem()->width() / 2)); + pos.setX(m_plasmoidScreen.x() + (m_plasmoidScreen.width() / 2) - (m_popupsOnScreen[i]->width() / 2)); break; case TopLeft: case BottomLeft: - m_popupsOnScreen[i]->setX(m_plasmoidScreen.left() + m_offset); + pos.setX(m_plasmoidScreen.left() + m_offset); break; case Left: case Center: case Right: // Fall-through to make the compiler happy break; } - - cumulativeHeight += (m_popupsOnScreen[i]->contentItem()->height() + m_offset); + m_popupsOnScreen[i]->setPosition(pos); + cumulativeHeight += (m_popupsOnScreen[i]->height() + m_offset); } m_mutex->unlock(); } diff --git a/applets/notifications/plugin/notificationshelper.h b/applets/notifications/plugin/notificationshelper.h index 78cf16595..2fe6e01a4 100644 --- a/applets/notifications/plugin/notificationshelper.h +++ b/applets/notifications/plugin/notificationshelper.h @@ -1,96 +1,95 @@ /* Copyright (C) 2014 Martin Klapetek This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef NOTIFICATIONSHELPER_H #define NOTIFICATIONSHELPER_H #include #include #include #include class QQuickWindow; class QTimer; class QReadWriteLock; class NotificationsHelper : public QObject { Q_OBJECT Q_PROPERTY(PositionOnScreen popupLocation MEMBER m_popupLocation WRITE setPopupLocation NOTIFY popupLocationChanged) public: enum PositionOnScreen { Default, // Follows the panel TopLeft, TopCenter, TopRight, Left, Center, Right, BottomLeft, BottomCenter, BottomRight }; Q_ENUM(PositionOnScreen) NotificationsHelper(QObject *parent = 0); ~NotificationsHelper() override; Q_INVOKABLE void addNotificationPopup(QObject *win); Q_INVOKABLE void closePopup(const QString &sourceName); Q_INVOKABLE void setPlasmoidScreenGeometry(const QRect &geometry); void setPopupLocation(PositionOnScreen popupLocation); /** * Fills the popup with data from notificationData * and puts the popup on proper place on screen. * If there's no space on screen for the notification, * it's queued and displayed as soon as there's space for it */ Q_INVOKABLE void displayNotification(const QVariantMap ¬ificationData); Q_SIGNALS: void popupLocationChanged(); void popupShown(QQuickWindow* popup); // void plasmoidScreenChanged(); private Q_SLOTS: - void onPopupShown(); void onPopupClosed(); void processQueues(); void processShow(); void processHide(); private: void repositionPopups(); QList m_popupsOnScreen; QList m_availablePopups; QHash m_sourceMap; QRect m_plasmoidScreen; PositionOnScreen m_popupLocation; int m_offset; bool m_busy; QList m_hideQueue; QList m_showQueue; QReadWriteLock *m_mutex; QTimer *m_dispatchTimer; }; #endif // NOTIFICATIONSHELPER_H