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"