File indexing completed on 2024-04-28 05:35:49

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