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 }