File indexing completed on 2024-11-10 04:57:51
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 = std::make_unique<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 = std::make_unique<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 = std::make_unique<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 0233 0234 #include "moc_onscreennotification.cpp"