File indexing completed on 2025-02-09 06:41:26

0001 /*
0002     SPDX-FileCopyrightText: 2013 Marco Martin <mart@kde.org>
0003 
0004     SPDX-License-Identifier: GPL-2.0-or-later
0005 */
0006 
0007 #include "containmentview.h"
0008 #include "configview.h"
0009 #include "plasmoid/containmentitem.h"
0010 
0011 #include <KPackage/Package>
0012 #include <QDebug>
0013 #include <QQmlContext>
0014 #include <QQmlEngine>
0015 #include <QQuickItem>
0016 #include <QScreen>
0017 #include <QTimer>
0018 
0019 namespace PlasmaQuick
0020 {
0021 class ContainmentViewPrivate
0022 {
0023 public:
0024     ContainmentViewPrivate(Plasma::Corona *corona, ContainmentView *view);
0025     ~ContainmentViewPrivate();
0026 
0027     void setContainment(Plasma::Containment *cont);
0028     Plasma::Types::FormFactor formFactor() const;
0029     Plasma::Types::Location location() const;
0030     void showConfigurationInterface(Plasma::Applet *applet);
0031     void updateDestroyed(bool destroyed);
0032     /**
0033      * Reconnects the relevant signals after a screen change
0034      **/
0035     void reactToScreenChange();
0036 
0037     ContainmentView *q;
0038     friend class ContainmentView;
0039     Plasma::Corona *corona;
0040     QScreen *lastScreen;
0041     QPointer<Plasma::Containment> containment;
0042     QPointer<ConfigView> configContainmentView;
0043 };
0044 
0045 ContainmentViewPrivate::ContainmentViewPrivate(Plasma::Corona *cor, ContainmentView *view)
0046     : q(view)
0047     , corona(cor)
0048 {
0049 }
0050 
0051 ContainmentViewPrivate::~ContainmentViewPrivate()
0052 {
0053 }
0054 
0055 void ContainmentViewPrivate::setContainment(Plasma::Containment *cont)
0056 {
0057     if (containment == cont) {
0058         return;
0059     }
0060 
0061     Plasma::Types::Location oldLoc = location();
0062     Plasma::Types::FormFactor oldForm = formFactor();
0063 
0064     if (containment) {
0065         QObject::disconnect(containment, nullptr, q, nullptr);
0066         QObject *oldGraphicObject = AppletQuickItem::itemForApplet(containment);
0067         if (auto item = qobject_cast<QQuickItem *>(oldGraphicObject)) {
0068             // TODO: delete the item when needed instead of just hiding, but there are quite a lot of cornercases to manage beforehand
0069             item->setVisible(false);
0070         }
0071         containment->reactToScreenChange();
0072     }
0073 
0074     containment = cont;
0075 
0076     if (oldLoc != location()) {
0077         Q_EMIT q->locationChanged(location());
0078     }
0079     if (oldForm != formFactor()) {
0080         Q_EMIT q->formFactorChanged(formFactor());
0081     }
0082 
0083     Q_EMIT q->containmentChanged();
0084 
0085     // we are QuickViewSharedEngine::SizeRootObjectToView, but that's not enough, as
0086     // the root object isn't immediately resized (done at the resizeEvent handler).
0087     // by resizing it just before restoring the containment, it removes a chain of resizes at startup
0088     if (q->rootObject()) {
0089         q->rootObject()->setSize(q->size());
0090     }
0091     if (cont) {
0092         cont->reactToScreenChange();
0093         QObject::connect(cont, &Plasma::Containment::locationChanged, q, &ContainmentView::locationChanged);
0094         QObject::connect(cont, &Plasma::Containment::formFactorChanged, q, &ContainmentView::formFactorChanged);
0095         QObject::connect(cont, &Plasma::Containment::configureRequested, q, &ContainmentView::showConfigurationInterface);
0096         QObject::connect(cont, SIGNAL(destroyedChanged(bool)), q, SLOT(updateDestroyed(bool)));
0097 
0098         // Panels are created invisible and the code below ensures they are only
0099         // shown once their contents have settled to avoid visual glitches on startup
0100         if (cont->containmentType() == Plasma::Containment::Type::Panel || cont->containmentType() == Plasma::Containment::Type::CustomPanel) {
0101             QObject::connect(
0102                 cont,
0103                 &Plasma::Containment::uiReadyChanged,
0104                 q,
0105                 [this, cont](bool ready) {
0106                     if (ready && !cont->destroyed()) {
0107                         q->setVisible(true);
0108                     }
0109                 },
0110                 Qt::QueuedConnection);
0111 
0112             q->setVisible(!cont->destroyed() && cont->isUiReady());
0113         }
0114     } else {
0115         return;
0116     }
0117 
0118     QQuickItem *graphicObject = AppletQuickItem::itemForApplet(containment);
0119 
0120     if (graphicObject) {
0121         //         qDebug() << "using as graphic containment" << graphicObject << containment.data();
0122 
0123         graphicObject->setFocus(true);
0124         // by resizing before adding, it will avoid some resizes in most cases
0125         graphicObject->setSize(q->size());
0126         graphicObject->setParentItem(q->rootObject());
0127         if (q->rootObject()) {
0128             q->rootObject()->setProperty("containment", QVariant::fromValue(graphicObject));
0129             QObject *wpGraphicObject = containment->property("wallpaperGraphicsObject").value<QObject *>();
0130             if (wpGraphicObject) {
0131                 q->rootObject()->setProperty("wallpaper", QVariant::fromValue(wpGraphicObject));
0132             }
0133         } else {
0134             qWarning() << "Could not set containment property on rootObject";
0135         }
0136     } else {
0137         qWarning() << "Containment graphic object not valid";
0138     }
0139 }
0140 
0141 Plasma::Types::Location ContainmentViewPrivate::location() const
0142 {
0143     if (!containment) {
0144         return Plasma::Types::Desktop;
0145     }
0146     return containment->location();
0147 }
0148 
0149 Plasma::Types::FormFactor ContainmentViewPrivate::formFactor() const
0150 {
0151     if (!containment) {
0152         return Plasma::Types::Planar;
0153     }
0154     return containment->formFactor();
0155 }
0156 
0157 void ContainmentViewPrivate::showConfigurationInterface(Plasma::Applet *applet)
0158 {
0159     if (configContainmentView) {
0160         if (configContainmentView->applet() != applet) {
0161             configContainmentView->hide();
0162             configContainmentView->deleteLater();
0163         } else {
0164             configContainmentView->raise();
0165             configContainmentView->requestActivate();
0166             return;
0167         }
0168     }
0169 
0170     if (!applet || !applet->containment()) {
0171         return;
0172     }
0173 
0174     configContainmentView = new ConfigView(applet);
0175 
0176     configContainmentView->init();
0177     configContainmentView->show();
0178 }
0179 
0180 void ContainmentViewPrivate::updateDestroyed(bool destroyed)
0181 {
0182     q->setVisible(!destroyed);
0183 }
0184 
0185 void ContainmentViewPrivate::reactToScreenChange()
0186 {
0187     QScreen *newScreen = q->screen();
0188 
0189     if (newScreen == lastScreen) {
0190         return;
0191     }
0192 
0193     QObject::disconnect(lastScreen, nullptr, q, nullptr);
0194     lastScreen = newScreen;
0195     QObject::connect(newScreen, &QScreen::geometryChanged, q,
0196                      &ContainmentView::screenGeometryChanged);
0197     Q_EMIT q->screenGeometryChanged();
0198 }
0199 
0200 ContainmentView::ContainmentView(Plasma::Corona *corona, QWindow *parent)
0201     : PlasmaQuick::QuickViewSharedEngine(parent)
0202     , d(new ContainmentViewPrivate(corona, this))
0203 {
0204     setColor(Qt::transparent);
0205 
0206     d->lastScreen = screen();
0207     QObject::connect(d->lastScreen, &QScreen::geometryChanged, this,
0208                      &ContainmentView::screenGeometryChanged);
0209     QObject::connect(this, &ContainmentView::screenChanged, this,
0210                      [this]() {
0211                          d->reactToScreenChange();
0212                      });
0213 
0214     if (corona->kPackage().isValid()) {
0215         const auto info = corona->kPackage().metadata();
0216         if (info.isValid()) {
0217             setTranslationDomain(QStringLiteral("plasma_shell_") + info.pluginId());
0218         } else {
0219             qWarning() << "Invalid corona package metadata";
0220         }
0221     } else {
0222         qWarning() << "Invalid home screen package";
0223     }
0224 
0225     setResizeMode(ContainmentView::SizeRootObjectToView);
0226 }
0227 
0228 ContainmentView::~ContainmentView()
0229 {
0230     delete d;
0231 }
0232 
0233 void ContainmentView::destroy()
0234 {
0235     // it will hide and deallocate the window so that no visibility or geometry
0236     // changes will be emitted during the destructor, avoiding potential crash
0237     // situations
0238     QWindow::destroy();
0239 
0240     // TODO: do we need a version which does not create?
0241     QQuickItem *graphicObject = AppletQuickItem::itemForApplet(d->containment);
0242     if (auto item = qobject_cast<QQuickItem *>(graphicObject)) {
0243         item->setVisible(false);
0244         item->setParentItem(nullptr); // First, remove the item from the view
0245     }
0246     deleteLater(); // delete the view
0247 }
0248 
0249 Plasma::Corona *ContainmentView::corona() const
0250 {
0251     return d->corona;
0252 }
0253 
0254 KConfigGroup ContainmentView::config() const
0255 {
0256     if (!containment()) {
0257         return KConfigGroup();
0258     }
0259     KConfigGroup views(KSharedConfig::openConfig(), QStringLiteral("PlasmaContainmentViews"));
0260     return KConfigGroup(&views, QString::number(containment()->screen()));
0261 }
0262 
0263 void ContainmentView::setContainment(Plasma::Containment *cont)
0264 {
0265     d->setContainment(cont);
0266 }
0267 
0268 Plasma::Containment *ContainmentView::containment() const
0269 {
0270     return d->containment;
0271 }
0272 
0273 void ContainmentView::setLocation(Plasma::Types::Location location)
0274 {
0275     d->containment->setLocation(location);
0276 }
0277 
0278 Plasma::Types::Location ContainmentView::location() const
0279 {
0280     return d->location();
0281 }
0282 
0283 Plasma::Types::FormFactor ContainmentView::formFactor() const
0284 {
0285     return d->formFactor();
0286 }
0287 
0288 QRectF ContainmentView::screenGeometry()
0289 {
0290     return screen()->geometry();
0291 }
0292 
0293 void ContainmentView::showConfigurationInterface(Plasma::Applet *applet)
0294 {
0295     d->showConfigurationInterface(applet);
0296 }
0297 
0298 }
0299 
0300 #include "moc_containmentview.cpp"