File indexing completed on 2024-02-18 16:20:08

0001 /*
0002     SPDX-FileCopyrightText: 2016 Martin Graesslin <mgraesslin@kde.org>
0003 
0004     SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
0005 
0006 */
0007 #include "onscreennotification.h"
0008 
0009 #include <config-kwin.h>
0010 
0011 #include "input.h"
0012 #include "input_event.h"
0013 #include "input_event_spy.h"
0014 
0015 #include <QPropertyAnimation>
0016 #include <QQmlComponent>
0017 #include <QQmlContext>
0018 #include <QQmlEngine>
0019 #include <QQuickWindow>
0020 #include <QStandardPaths>
0021 #include <QTimer>
0022 
0023 #include <KConfigGroup>
0024 
0025 #include <functional>
0026 
0027 namespace KWin
0028 {
0029 
0030 class OnScreenNotificationInputEventSpy : public InputEventSpy
0031 {
0032 public:
0033     explicit OnScreenNotificationInputEventSpy(OnScreenNotification *parent);
0034 
0035     void pointerEvent(MouseEvent *event) override;
0036 
0037 private:
0038     OnScreenNotification *m_parent;
0039 };
0040 
0041 OnScreenNotificationInputEventSpy::OnScreenNotificationInputEventSpy(OnScreenNotification *parent)
0042     : m_parent(parent)
0043 {
0044 }
0045 
0046 void OnScreenNotificationInputEventSpy::pointerEvent(MouseEvent *event)
0047 {
0048     if (event->type() != QEvent::MouseMove) {
0049         return;
0050     }
0051 
0052     m_parent->setContainsPointer(m_parent->geometry().contains(event->globalPos()));
0053 }
0054 
0055 OnScreenNotification::OnScreenNotification(QObject *parent)
0056     : QObject(parent)
0057     , m_timer(new QTimer(this))
0058 {
0059     m_timer->setSingleShot(true);
0060     connect(m_timer, &QTimer::timeout, this, std::bind(&OnScreenNotification::setVisible, this, false));
0061     connect(this, &OnScreenNotification::visibleChanged, this, [this]() {
0062         if (m_visible) {
0063             show();
0064         } else {
0065             m_timer->stop();
0066             m_spy.reset();
0067             m_containsPointer = false;
0068         }
0069     });
0070 }
0071 
0072 OnScreenNotification::~OnScreenNotification()
0073 {
0074     if (QQuickWindow *w = qobject_cast<QQuickWindow *>(m_mainItem.get())) {
0075         w->hide();
0076         w->destroy();
0077     }
0078 }
0079 
0080 void OnScreenNotification::setConfig(KSharedConfigPtr config)
0081 {
0082     m_config = config;
0083 }
0084 
0085 void OnScreenNotification::setEngine(QQmlEngine *engine)
0086 {
0087     m_qmlEngine = engine;
0088 }
0089 
0090 bool OnScreenNotification::isVisible() const
0091 {
0092     return m_visible;
0093 }
0094 
0095 void OnScreenNotification::setVisible(bool visible)
0096 {
0097     if (m_visible == visible) {
0098         return;
0099     }
0100     m_visible = visible;
0101     Q_EMIT visibleChanged();
0102 }
0103 
0104 QString OnScreenNotification::message() const
0105 {
0106     return m_message;
0107 }
0108 
0109 void OnScreenNotification::setMessage(const QString &message)
0110 {
0111     if (m_message == message) {
0112         return;
0113     }
0114     m_message = message;
0115     Q_EMIT messageChanged();
0116 }
0117 
0118 QString OnScreenNotification::iconName() const
0119 {
0120     return m_iconName;
0121 }
0122 
0123 void OnScreenNotification::setIconName(const QString &iconName)
0124 {
0125     if (m_iconName == iconName) {
0126         return;
0127     }
0128     m_iconName = iconName;
0129     Q_EMIT iconNameChanged();
0130 }
0131 
0132 int OnScreenNotification::timeout() const
0133 {
0134     return m_timer->interval();
0135 }
0136 
0137 void OnScreenNotification::setTimeout(int timeout)
0138 {
0139     if (m_timer->interval() == timeout) {
0140         return;
0141     }
0142     m_timer->setInterval(timeout);
0143     Q_EMIT timeoutChanged();
0144 }
0145 
0146 void OnScreenNotification::show()
0147 {
0148     Q_ASSERT(m_visible);
0149     ensureQmlContext();
0150     ensureQmlComponent();
0151     createInputSpy();
0152     if (m_timer->interval() != 0) {
0153         m_timer->start();
0154     }
0155 }
0156 
0157 void OnScreenNotification::ensureQmlContext()
0158 {
0159     Q_ASSERT(m_qmlEngine);
0160     if (m_qmlContext) {
0161         return;
0162     }
0163     m_qmlContext.reset(new QQmlContext(m_qmlEngine));
0164     m_qmlContext->setContextProperty(QStringLiteral("osd"), this);
0165 }
0166 
0167 void OnScreenNotification::ensureQmlComponent()
0168 {
0169     Q_ASSERT(m_config);
0170     Q_ASSERT(m_qmlEngine);
0171     if (m_qmlComponent) {
0172         return;
0173     }
0174     m_qmlComponent.reset(new QQmlComponent(m_qmlEngine));
0175     const QString fileName = QStandardPaths::locate(QStandardPaths::GenericDataLocation,
0176                                                     m_config->group(QStringLiteral("OnScreenNotification")).readEntry("QmlPath", QStringLiteral("kwin/onscreennotification/plasma/main.qml")));
0177     if (fileName.isEmpty()) {
0178         return;
0179     }
0180     m_qmlComponent->loadUrl(QUrl::fromLocalFile(fileName));
0181     if (!m_qmlComponent->isError()) {
0182         m_mainItem.reset(m_qmlComponent->create(m_qmlContext.get()));
0183     } else {
0184         m_qmlComponent.reset();
0185     }
0186 }
0187 
0188 void OnScreenNotification::createInputSpy()
0189 {
0190     Q_ASSERT(!m_spy);
0191     if (auto w = qobject_cast<QQuickWindow *>(m_mainItem.get())) {
0192         m_spy.reset(new OnScreenNotificationInputEventSpy(this));
0193         input()->installInputEventSpy(m_spy.get());
0194         if (!m_animation) {
0195             m_animation = new QPropertyAnimation(w, "opacity", this);
0196             m_animation->setStartValue(1.0);
0197             m_animation->setEndValue(0.0);
0198             m_animation->setDuration(250);
0199             m_animation->setEasingCurve(QEasingCurve::InOutCubic);
0200         }
0201     }
0202 }
0203 
0204 QRect OnScreenNotification::geometry() const
0205 {
0206     if (QQuickWindow *w = qobject_cast<QQuickWindow *>(m_mainItem.get())) {
0207         return w->geometry();
0208     }
0209     return QRect();
0210 }
0211 
0212 void OnScreenNotification::setContainsPointer(bool contains)
0213 {
0214     if (m_containsPointer == contains) {
0215         return;
0216     }
0217     m_containsPointer = contains;
0218     if (!m_animation) {
0219         return;
0220     }
0221     m_animation->setDirection(m_containsPointer ? QAbstractAnimation::Forward : QAbstractAnimation::Backward);
0222     m_animation->start();
0223 }
0224 
0225 void OnScreenNotification::setSkipCloseAnimation(bool skip)
0226 {
0227     if (QQuickWindow *w = qobject_cast<QQuickWindow *>(m_mainItem.get())) {
0228         w->setProperty("KWIN_SKIP_CLOSE_ANIMATION", skip);
0229     }
0230 }
0231 
0232 } // namespace KWin