File indexing completed on 2024-04-28 16:54:48
0001 /* 0002 SPDX-FileCopyrightText: 2014 Bhushan Shah <bhush94@gmail.com> 0003 SPDX-FileCopyrightText: 2014 Marco Martin <notmart@gmail.com> 0004 0005 SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL 0006 */ 0007 0008 #include "plasmawindowedview.h" 0009 0010 #include <QMenu> 0011 #include <QQmlContext> 0012 #include <QQmlEngine> 0013 #include <QQmlExpression> 0014 #include <QQmlProperty> 0015 #include <QQuickItem> 0016 #include <QResizeEvent> 0017 0018 #include <KActionCollection> 0019 #include <KIconLoader> 0020 #include <KLocalizedString> 0021 #include <KStatusNotifierItem> 0022 0023 PlasmaWindowedView::PlasmaWindowedView(QWindow *parent) 0024 : QQuickView(parent) 0025 , m_applet(nullptr) 0026 , m_statusNotifier(nullptr) 0027 , m_withStatusNotifier(false) 0028 { 0029 engine()->rootContext()->setContextProperty(QStringLiteral("root"), contentItem()); 0030 // access appletInterface.Layout.minimumWidth, to create the Layout attached object for appletInterface as a sideeffect 0031 QQmlExpression *expr = new QQmlExpression( 0032 engine()->rootContext(), 0033 contentItem(), 0034 QStringLiteral("Qt.createQmlObject('import QtQuick 2.0; import QtQuick.Layouts 1.1; import org.kde.plasma.core 2.0 as PlasmaCore; " 0035 "Rectangle {color: PlasmaCore.Theme.backgroundColor; anchors.fill:parent; " 0036 "property Item appletInterface; onAppletInterfaceChanged: print(appletInterface.Layout.minimumWidth)}', root, \"\");")); 0037 m_rootObject = expr->evaluate().value<QQuickItem *>(); 0038 } 0039 0040 PlasmaWindowedView::~PlasmaWindowedView() 0041 { 0042 } 0043 0044 void PlasmaWindowedView::setHasStatusNotifier(bool stay) 0045 { 0046 Q_ASSERT(!m_statusNotifier); 0047 m_withStatusNotifier = stay; 0048 } 0049 0050 void PlasmaWindowedView::setApplet(Plasma::Applet *applet) 0051 { 0052 m_applet = applet; 0053 if (!applet) { 0054 return; 0055 } 0056 0057 m_appletInterface = applet->property("_plasma_graphicObject").value<QQuickItem *>(); 0058 0059 if (!m_appletInterface) { 0060 return; 0061 } 0062 0063 m_appletInterface->setParentItem(m_rootObject); 0064 m_rootObject->setProperty("appletInterface", QVariant::fromValue(m_appletInterface.data())); 0065 m_appletInterface->setVisible(true); 0066 setTitle(applet->title()); 0067 setIcon(QIcon::fromTheme(applet->icon())); 0068 0069 const QSize switchSize(m_appletInterface->property("switchWidth").toInt(), m_appletInterface->property("switchHeight").toInt()); 0070 QRect geom = m_applet->config().readEntry("geometry", QRect()); 0071 0072 if (geom.isValid()) { 0073 geom.setWidth(qMax(geom.width(), switchSize.width() + 1)); 0074 geom.setHeight(qMax(geom.height(), switchSize.height() + 1)); 0075 setGeometry(geom); 0076 } 0077 setMinimumSize(QSize(qMax((int)KIconLoader::SizeEnormous, switchSize.width() + 1), qMax((int)KIconLoader::SizeEnormous, switchSize.height() + 1))); 0078 0079 foreach (QObject *child, m_appletInterface->children()) { 0080 // find for the needed property of Layout: minimum/maximum/preferred sizes and fillWidth/fillHeight 0081 if (child->property("minimumWidth").isValid() && child->property("minimumHeight").isValid() && child->property("preferredWidth").isValid() 0082 && child->property("preferredHeight").isValid() && child->property("maximumWidth").isValid() && child->property("maximumHeight").isValid() 0083 && child->property("fillWidth").isValid() && child->property("fillHeight").isValid()) { 0084 m_layout = child; 0085 } 0086 } 0087 0088 if (m_layout) { 0089 connect(m_layout, SIGNAL(minimumWidthChanged()), this, SLOT(minimumWidthChanged())); 0090 connect(m_layout, SIGNAL(minimumHeightChanged()), this, SLOT(minimumHeightChanged())); 0091 } 0092 minimumWidthChanged(); 0093 minimumHeightChanged(); 0094 QObject::connect(applet->containment(), &Plasma::Containment::configureRequested, this, &PlasmaWindowedView::showConfigurationInterface); 0095 0096 Q_ASSERT(!m_statusNotifier); 0097 if (m_withStatusNotifier) { 0098 m_statusNotifier = new KStatusNotifierItem(applet->pluginMetaData().pluginId(), this); 0099 m_statusNotifier->setStandardActionsEnabled(false); // we add our own "Close" entry manually below 0100 0101 updateSniIcon(); 0102 connect(applet, &Plasma::Applet::iconChanged, this, &PlasmaWindowedView::updateSniIcon); 0103 0104 updateSniTitle(); 0105 connect(applet, &Plasma::Applet::titleChanged, this, &PlasmaWindowedView::updateSniTitle); 0106 0107 updateSniStatus(); 0108 connect(applet, &Plasma::Applet::statusChanged, this, &PlasmaWindowedView::updateSniStatus); 0109 0110 // set up actions 0111 for (auto a : applet->contextualActions()) { 0112 m_statusNotifier->contextMenu()->addAction(a); 0113 } 0114 m_statusNotifier->contextMenu()->addSeparator(); 0115 QAction *closeAction = new QAction(QIcon::fromTheme(QStringLiteral("window-close")), i18n("Close %1", applet->title()), this); 0116 connect(closeAction, &QAction::triggered, this, [this]() { 0117 m_statusNotifier->deleteLater(); 0118 close(); 0119 }); 0120 m_statusNotifier->contextMenu()->addAction(closeAction); 0121 0122 connect(m_statusNotifier.data(), &KStatusNotifierItem::activateRequested, this, [this](bool /*active*/, const QPoint & /*pos*/) { 0123 if (isVisible() && isActive()) { 0124 hide(); 0125 } else { 0126 show(); 0127 raise(); 0128 } 0129 }); 0130 auto syncStatus = [this]() { 0131 switch (m_applet->status()) { 0132 case Plasma::Types::AcceptingInputStatus: 0133 case Plasma::Types::RequiresAttentionStatus: 0134 case Plasma::Types::NeedsAttentionStatus: 0135 m_statusNotifier->setStatus(KStatusNotifierItem::NeedsAttention); 0136 break; 0137 case Plasma::Types::ActiveStatus: 0138 m_statusNotifier->setStatus(KStatusNotifierItem::Active); 0139 break; 0140 default: 0141 m_statusNotifier->setStatus(KStatusNotifierItem::Passive); 0142 } 0143 }; 0144 connect(applet, &Plasma::Applet::statusChanged, this, syncStatus); 0145 syncStatus(); 0146 } 0147 } 0148 0149 void PlasmaWindowedView::resizeEvent(QResizeEvent *ev) 0150 { 0151 if (!m_applet) { 0152 return; 0153 } 0154 0155 QQuickItem *i = m_applet->property("_plasma_graphicObject").value<QQuickItem *>(); 0156 if (!i) { 0157 return; 0158 } 0159 0160 minimumWidthChanged(); 0161 minimumHeightChanged(); 0162 0163 i->setSize(ev->size()); 0164 contentItem()->setSize(ev->size()); 0165 0166 m_applet->config().writeEntry("geometry", QRect(position(), ev->size())); 0167 } 0168 0169 void PlasmaWindowedView::mouseReleaseEvent(QMouseEvent *ev) 0170 { 0171 QQuickWindow::mouseReleaseEvent(ev); 0172 0173 if ((!(ev->buttons() & Qt::RightButton) && ev->button() != Qt::RightButton) || ev->isAccepted()) { 0174 return; 0175 } 0176 0177 Q_EMIT m_applet->contextualActionsAboutToShow(); 0178 0179 QMenu menu; 0180 0181 foreach (QAction *action, m_applet->contextualActions()) { 0182 if (action) { 0183 menu.addAction(action); 0184 } 0185 } 0186 0187 if (!m_applet->failedToLaunch()) { 0188 QAction *runAssociatedApplication = m_applet->actions()->action(QStringLiteral("run associated application")); 0189 if (runAssociatedApplication && runAssociatedApplication->isEnabled()) { 0190 menu.addAction(runAssociatedApplication); 0191 } 0192 0193 QAction *configureApplet = m_applet->actions()->action(QStringLiteral("configure")); 0194 if (configureApplet && configureApplet->isEnabled()) { 0195 menu.addAction(configureApplet); 0196 } 0197 } 0198 0199 menu.exec(ev->globalPos()); 0200 ev->setAccepted(true); 0201 } 0202 0203 void PlasmaWindowedView::keyPressEvent(QKeyEvent *ev) 0204 { 0205 if (ev->matches(QKeySequence::Quit)) { 0206 m_statusNotifier->deleteLater(); 0207 close(); 0208 } 0209 QQuickView::keyReleaseEvent(ev); 0210 } 0211 0212 void PlasmaWindowedView::moveEvent(QMoveEvent *ev) 0213 { 0214 Q_UNUSED(ev) 0215 m_applet->config().writeEntry("geometry", QRect(position(), size())); 0216 } 0217 0218 void PlasmaWindowedView::hideEvent(QHideEvent *ev) 0219 { 0220 Q_UNUSED(ev) 0221 m_applet->config().sync(); 0222 if (!m_withStatusNotifier) { 0223 m_applet->deleteLater(); 0224 deleteLater(); 0225 } 0226 } 0227 0228 void PlasmaWindowedView::showConfigurationInterface(Plasma::Applet *applet) 0229 { 0230 if (m_configView) { 0231 m_configView->hide(); 0232 m_configView->deleteLater(); 0233 } 0234 0235 if (!applet || !applet->containment()) { 0236 return; 0237 } 0238 0239 m_configView = new PlasmaQuick::ConfigView(applet); 0240 0241 m_configView->init(); 0242 m_configView->show(); 0243 } 0244 0245 void PlasmaWindowedView::minimumWidthChanged() 0246 { 0247 if (!m_layout || !m_appletInterface) { 0248 return; 0249 } 0250 0251 setMinimumWidth( 0252 qMax(m_appletInterface->property("switchWidth").toInt() + 1, qMax((int)KIconLoader::SizeEnormous, m_layout->property("minimumWidth").toInt()))); 0253 } 0254 0255 void PlasmaWindowedView::minimumHeightChanged() 0256 { 0257 if (!m_layout || !m_appletInterface) { 0258 return; 0259 } 0260 0261 setMinimumHeight( 0262 qMax(m_appletInterface->property("switchHeight").toInt() + 1, qMax((int)KIconLoader::SizeEnormous, m_layout->property("minimumHeight").toInt()))); 0263 } 0264 0265 void PlasmaWindowedView::maximumWidthChanged() 0266 { 0267 if (!m_layout) { 0268 return; 0269 } 0270 0271 setMaximumWidth(m_layout->property("maximumWidth").toInt()); 0272 } 0273 0274 void PlasmaWindowedView::maximumHeightChanged() 0275 { 0276 if (!m_layout) { 0277 return; 0278 } 0279 0280 setMaximumHeight(m_layout->property("maximumHeight").toInt()); 0281 } 0282 0283 void PlasmaWindowedView::updateSniIcon() 0284 { 0285 m_statusNotifier->setIconByName(m_applet->icon()); 0286 } 0287 0288 void PlasmaWindowedView::updateSniTitle() 0289 { 0290 m_statusNotifier->setTitle(m_applet->title()); 0291 m_statusNotifier->setToolTipTitle(m_applet->title()); 0292 } 0293 0294 void PlasmaWindowedView::updateSniStatus() 0295 { 0296 switch (m_applet->status()) { 0297 case Plasma::Types::UnknownStatus: 0298 case Plasma::Types::PassiveStatus: 0299 case Plasma::Types::HiddenStatus: 0300 m_statusNotifier->setStatus(KStatusNotifierItem::Passive); 0301 break; 0302 case Plasma::Types::ActiveStatus: 0303 case Plasma::Types::AcceptingInputStatus: 0304 m_statusNotifier->setStatus(KStatusNotifierItem::Active); 0305 break; 0306 case Plasma::Types::NeedsAttentionStatus: 0307 case Plasma::Types::RequiresAttentionStatus: 0308 m_statusNotifier->setStatus(KStatusNotifierItem::NeedsAttention); 0309 break; 0310 } 0311 }