File indexing completed on 2024-04-28 05:35:58
0001 /* 0002 SPDX-FileCopyrightText: 2013 Marco Martin <mart@kde.org> 0003 0004 SPDX-License-Identifier: LGPL-2.0-or-later 0005 */ 0006 0007 #include "panelconfigview.h" 0008 #include "config-X11.h" 0009 #include "panelshadows_p.h" 0010 #include "panelview.h" 0011 #include "shellcorona.h" 0012 0013 #include <LayerShellQt/Window> 0014 0015 #include <QAction> 0016 #include <QDebug> 0017 #include <QDir> 0018 #include <QQmlComponent> 0019 #include <QQmlContext> 0020 #include <QQmlEngine> 0021 #include <QScreen> 0022 0023 #include <KWindowSystem> 0024 #include <plasmaquick/popupplasmawindow.h> 0025 #include <qnamespace.h> 0026 #if HAVE_X11 0027 #include <KX11Extras> 0028 #endif 0029 #include <klocalizedstring.h> 0030 #include <kwindoweffects.h> 0031 0032 #include <Plasma/Containment> 0033 #include <Plasma/PluginLoader> 0034 0035 #include <KWayland/Client/plasmashell.h> 0036 #include <KWayland/Client/surface.h> 0037 0038 #include <chrono> 0039 0040 using namespace std::chrono_literals; 0041 using namespace Qt::StringLiterals; 0042 0043 PanelRulerView::PanelRulerView(Plasma::Containment *containment, PanelView *panelView, PanelConfigView *mainConfigView) 0044 : PlasmaWindow() 0045 , m_containment(containment) 0046 , m_panelView(panelView) 0047 , m_mainConfigView(mainConfigView) 0048 { 0049 if (KWindowSystem::isPlatformWayland()) { 0050 m_layerWindow = LayerShellQt::Window::get(this); 0051 m_layerWindow->setLayer(LayerShellQt::Window::LayerTop); 0052 m_layerWindow->setKeyboardInteractivity(LayerShellQt::Window::KeyboardInteractivityOnDemand); 0053 m_layerWindow->setScope(QStringLiteral("dock")); 0054 m_layerWindow->setCloseOnDismissed(false); 0055 } 0056 setScreen(m_panelView->screen()); 0057 0058 connect(this, &PanelRulerView::mainItemChanged, this, &PanelRulerView::syncPanelLocation); 0059 } 0060 0061 PanelRulerView::~PanelRulerView() 0062 { 0063 } 0064 0065 void PanelRulerView::syncPanelLocation() 0066 { 0067 if (!mainItem()) { 0068 return; 0069 } 0070 const QRect available = m_containment->corona()->availableScreenRect(m_containment->screen()); 0071 0072 switch (m_containment->location()) { 0073 case Plasma::Types::TopEdge: 0074 setBorders(Qt::BottomEdge); 0075 break; 0076 case Plasma::Types::LeftEdge: 0077 setBorders(Qt::RightEdge); 0078 break; 0079 case Plasma::Types::RightEdge: 0080 setBorders(Qt::LeftEdge); 0081 break; 0082 case Plasma::Types::BottomEdge: 0083 default: 0084 setBorders(Qt::TopEdge); 0085 } 0086 0087 switch (m_containment->location()) { 0088 case Plasma::Types::LeftEdge: 0089 case Plasma::Types::RightEdge: 0090 setMaximumWidth(mainItem()->implicitWidth()); 0091 setWidth(mainItem()->implicitWidth()); 0092 setMaximumHeight(available.height()); 0093 setHeight(available.height()); 0094 break; 0095 case Plasma::Types::TopEdge: 0096 case Plasma::Types::BottomEdge: 0097 default: 0098 setMaximumWidth(available.width()); 0099 setWidth(available.width()); 0100 setMaximumHeight(mainItem()->implicitHeight()); 0101 setHeight(mainItem()->implicitHeight()); 0102 break; 0103 } 0104 0105 if (KWindowSystem::isPlatformX11()) { 0106 KX11Extras::setType(winId(), NET::Dock); 0107 KX11Extras::setState(winId(), NET::KeepAbove); 0108 switch (m_containment->location()) { 0109 case Plasma::Types::TopEdge: 0110 setPosition(available.topLeft() + screen()->geometry().topLeft()); 0111 break; 0112 case Plasma::Types::LeftEdge: 0113 setPosition(available.topLeft() + screen()->geometry().topLeft()); 0114 break; 0115 case Plasma::Types::RightEdge: 0116 setPosition(available.topLeft() + screen()->geometry().topRight() - QPoint(width(), 0)); 0117 break; 0118 case Plasma::Types::BottomEdge: 0119 default: 0120 setPosition(available.bottomLeft() + screen()->geometry().topLeft() - QPoint(0, height())); 0121 } 0122 } else if (m_layerWindow) { 0123 m_layerWindow->setKeyboardInteractivity(LayerShellQt::Window::KeyboardInteractivityOnDemand); 0124 LayerShellQt::Window::Anchors anchors; 0125 0126 switch (m_containment->location()) { 0127 case Plasma::Types::TopEdge: 0128 anchors.setFlag(LayerShellQt::Window::AnchorTop); 0129 break; 0130 case Plasma::Types::LeftEdge: 0131 anchors.setFlag(LayerShellQt::Window::AnchorLeft); 0132 break; 0133 case Plasma::Types::RightEdge: 0134 anchors.setFlag(LayerShellQt::Window::AnchorRight); 0135 break; 0136 case Plasma::Types::BottomEdge: 0137 default: 0138 anchors.setFlag(LayerShellQt::Window::AnchorBottom); 0139 break; 0140 } 0141 0142 if (m_containment->formFactor() == Plasma::Types::Horizontal) { 0143 switch (m_panelView->alignment()) { 0144 case Qt::AlignLeft: 0145 anchors.setFlag(LayerShellQt::Window::AnchorLeft); 0146 break; 0147 case Qt::AlignCenter: 0148 break; 0149 case Qt::AlignRight: 0150 anchors.setFlag(LayerShellQt::Window::AnchorRight); 0151 break; 0152 } 0153 } else { 0154 switch (m_panelView->alignment()) { 0155 case Qt::AlignLeft: 0156 anchors.setFlag(LayerShellQt::Window::AnchorTop); 0157 break; 0158 case Qt::AlignCenter: 0159 break; 0160 case Qt::AlignRight: 0161 anchors.setFlag(LayerShellQt::Window::AnchorBottom); 0162 break; 0163 } 0164 } 0165 0166 // m_layerWindow->setMargins(margins); 0167 m_layerWindow->setAnchors(anchors); 0168 0169 requestUpdate(); 0170 } 0171 } 0172 0173 void PanelRulerView::showEvent(QShowEvent *ev) 0174 { 0175 syncPanelLocation(); 0176 PlasmaWindow::showEvent(ev); 0177 } 0178 0179 void PanelRulerView::focusOutEvent(QFocusEvent *ev) 0180 { 0181 QWindow *focusWindow = QGuiApplication::focusWindow(); 0182 0183 if (focusWindow 0184 && ((focusWindow->flags().testFlag(Qt::Popup)) || focusWindow->objectName() == QLatin1String("QMenuClassWindow") || focusWindow == m_mainConfigView)) { 0185 return; 0186 } 0187 0188 m_mainConfigView->focusVisibilityCheck(focusWindow); 0189 } 0190 0191 //////////////////////////////PanelConfigView 0192 PanelConfigView::PanelConfigView(Plasma::Containment *containment, PanelView *panelView) 0193 : PlasmaQuick::PopupPlasmaWindow() 0194 , m_containment(containment) 0195 , m_panelView(panelView) 0196 , m_sharedQmlEngine(std::make_unique<PlasmaQuick::SharedQmlEngine>(this)) 0197 { 0198 connect(panelView, &QObject::destroyed, this, &QObject::deleteLater); 0199 0200 setScreen(panelView->screen()); 0201 0202 connect(panelView, &QWindow::screenChanged, &m_screenSyncTimer, QOverload<>::of(&QTimer::start)); 0203 m_screenSyncTimer.setSingleShot(true); 0204 m_screenSyncTimer.setInterval(150ms); 0205 connect(&m_screenSyncTimer, &QTimer::timeout, [this, panelView]() { 0206 setScreen(panelView->screen()); 0207 syncGeometry(); 0208 }); 0209 0210 m_sharedQmlEngine->rootContext()->setContextProperties({ 0211 QQmlContext::PropertyPair{u"plasmoid"_s, QVariant::fromValue(containment)}, 0212 QQmlContext::PropertyPair{u"panel"_s, QVariant::fromValue(panelView)}, 0213 QQmlContext::PropertyPair{u"configDialog"_s, QVariant::fromValue(this)}, 0214 }); 0215 connect(containment, &Plasma::Containment::destroyedChanged, this, &QObject::deleteLater); 0216 connect(containment, &Plasma::Containment::formFactorChanged, this, &PanelConfigView::syncGeometry); 0217 connect(containment, &Plasma::Containment::locationChanged, this, &PanelConfigView::syncGeometry); 0218 0219 connect(panelView, &PanelView::lengthChanged, this, &PanelConfigView::syncGeometry); 0220 connect(panelView, &PanelView::geometryChanged, this, &PanelConfigView::syncGeometry); 0221 connect(panelView, &PanelView::thicknessChanged, this, &PanelConfigView::syncGeometry); 0222 connect(m_containment->corona(), &Plasma::Corona::editModeChanged, this, [this](bool edit) { 0223 if (!edit) { 0224 hide(); 0225 } 0226 }); 0227 0228 setMargin(4); 0229 m_focusWindow = qApp->focusWindow(); 0230 } 0231 0232 PanelConfigView::~PanelConfigView() 0233 { 0234 } 0235 0236 void PanelConfigView::init() 0237 { 0238 m_sharedQmlEngine->setInitializationDelayed(true); 0239 m_sharedQmlEngine->setSource(m_containment->corona()->kPackage().fileUrl("panelconfigurationui")); 0240 m_sharedQmlEngine->completeInitialization({{QStringLiteral("panelConfiguration"), QVariant::fromValue(this)}}); 0241 setMainItem(qobject_cast<QQuickItem *>(m_sharedQmlEngine->rootObject())); 0242 if (mainItem()) { 0243 if (m_panelRulerView) { 0244 QQuickItem *ruler = mainItem()->property("panelRuler").value<QQuickItem *>(); 0245 m_panelRulerView->setMainItem(ruler); 0246 m_panelRulerView->syncPanelLocation(); 0247 } 0248 auto syncSize = [this] { 0249 resize(mainItem()->implicitWidth() + leftPadding() + rightPadding(), mainItem()->implicitHeight() + topPadding() + bottomPadding()); 0250 }; 0251 connect(mainItem(), &QQuickItem::implicitWidthChanged, this, syncSize); 0252 syncSize(); 0253 mainItem()->setVisible(true); 0254 } 0255 syncGeometry(); 0256 } 0257 0258 void PanelConfigView::showAddWidgetDialog() 0259 { 0260 QAction *addWidgetAction = m_containment->internalAction(QStringLiteral("add widgets")); 0261 if (addWidgetAction) { 0262 addWidgetAction->trigger(); 0263 } 0264 } 0265 0266 void PanelConfigView::addPanelSpacer() 0267 { 0268 ShellCorona *c = qobject_cast<ShellCorona *>(m_containment->corona()); 0269 if (!c) { 0270 return; 0271 } 0272 // Add a spacer at the end *except* if there is exactly one spacer already 0273 // this to trigger the panel centering mode of the spacer in a slightly more discoverable way 0274 c->evaluateScript(QStringLiteral("panel = panelById(") + QString::number(m_containment->id()) 0275 + QStringLiteral(");" 0276 "var spacers = panel.widgets(\"org.kde.plasma.panelspacer\");" 0277 "if (spacers.length === 1) {" 0278 " panel.addWidget(\"org.kde.plasma.panelspacer\", 0,0,1,1);" 0279 "} else {" 0280 " panel.addWidget(\"org.kde.plasma.panelspacer\");" 0281 "}")); 0282 } 0283 0284 void PanelConfigView::syncGeometry() 0285 { 0286 switch (m_containment->location()) { 0287 case Plasma::Types::TopEdge: 0288 setPopupDirection(Qt::BottomEdge); 0289 break; 0290 case Plasma::Types::LeftEdge: 0291 setPopupDirection(Qt::RightEdge); 0292 break; 0293 case Plasma::Types::RightEdge: 0294 setPopupDirection(Qt::LeftEdge); 0295 break; 0296 case Plasma::Types::BottomEdge: 0297 default: 0298 setPopupDirection(Qt::TopEdge); 0299 } 0300 queuePositionUpdate(); 0301 update(); 0302 if (m_panelRulerView) { 0303 m_panelRulerView->syncPanelLocation(); 0304 } 0305 } 0306 0307 void PanelConfigView::keyPressEvent(QKeyEvent *ev) 0308 { 0309 QQuickWindow::keyPressEvent(ev); 0310 if (ev->isAccepted()) { 0311 return; 0312 } 0313 0314 if (ev->matches(QKeySequence::Cancel)) { 0315 ev->accept(); 0316 hide(); 0317 } 0318 } 0319 0320 void PanelConfigView::showEvent(QShowEvent *ev) 0321 { 0322 if (m_containment) { 0323 m_containment->setUserConfiguring(true); 0324 } 0325 PopupPlasmaWindow::showEvent(ev); 0326 } 0327 0328 void PanelConfigView::hideEvent(QHideEvent *ev) 0329 { 0330 PopupPlasmaWindow::hideEvent(ev); 0331 0332 if (m_containment) { 0333 m_containment->setUserConfiguring(false); 0334 } 0335 deleteLater(); 0336 } 0337 0338 void PanelConfigView::focusVisibilityCheck(QWindow *focusWindow) 0339 { 0340 QWindow *oldFocusWindow = m_focusWindow; 0341 m_focusWindow = focusWindow; 0342 0343 if (!focusWindow) { 0344 hide(); 0345 return; 0346 } 0347 if (focusWindow == this || (m_panelRulerView && focusWindow == m_panelRulerView.get())) { 0348 return; 0349 } 0350 if (auto popup = qobject_cast<const PopupPlasmaWindow *>(focusWindow)) { 0351 if (auto parent = popup->visualParent(); parent && parent->window() == m_panelView) { 0352 return; 0353 } 0354 } 0355 if (auto popup = qobject_cast<const PopupPlasmaWindow *>(oldFocusWindow); popup && oldFocusWindow != this) { 0356 requestActivate(); 0357 return; 0358 } 0359 // Don't close if the user is directly manipulating the panel 0360 if (m_panelView->mouseGrabberItem()) { 0361 return; 0362 } 0363 hide(); 0364 } 0365 0366 void PanelConfigView::focusInEvent(QFocusEvent *ev) 0367 { 0368 connect(qApp, &QGuiApplication::focusWindowChanged, this, &PanelConfigView::focusVisibilityCheck, Qt::UniqueConnection); 0369 PopupPlasmaWindow::focusInEvent(ev); 0370 } 0371 0372 void PanelConfigView::setVisibilityMode(PanelView::VisibilityMode mode) 0373 { 0374 m_panelView->setVisibilityMode(mode); 0375 Q_EMIT visibilityModeChanged(); 0376 } 0377 0378 PanelView::VisibilityMode PanelConfigView::visibilityMode() const 0379 { 0380 return m_panelView->visibilityMode(); 0381 } 0382 0383 void PanelConfigView::setOpacityMode(PanelView::OpacityMode mode) 0384 { 0385 m_panelView->setOpacityMode(mode); 0386 Q_EMIT opacityModeChanged(); 0387 } 0388 0389 PanelView::OpacityMode PanelConfigView::opacityMode() const 0390 { 0391 return m_panelView->opacityMode(); 0392 } 0393 0394 KSvg::FrameSvg::EnabledBorders PanelConfigView::enabledBorders() const 0395 { 0396 return m_enabledBorders; 0397 } 0398 0399 PanelRulerView *PanelConfigView::panelRulerView() 0400 { 0401 if (!m_panelRulerView) { 0402 m_panelRulerView = std::make_unique<PanelRulerView>(m_containment, m_panelView, this); 0403 // It's a queued connection because m_panelRulerView needs a bit to have the proper size after visibleChanged is emitted 0404 connect( 0405 m_panelRulerView.get(), 0406 &PanelRulerView::visibleChanged, 0407 this, 0408 [this](bool visible) { 0409 if (visible) { 0410 setMargin(std::min(m_panelRulerView->width(), m_panelRulerView->height()) + 4); 0411 } else { 0412 setMargin(4); 0413 } 0414 }, 0415 Qt::QueuedConnection); 0416 } 0417 0418 if (mainItem()) { 0419 QQuickItem *ruler = mainItem()->property("panelRuler").value<QQuickItem *>(); 0420 m_panelRulerView->setMainItem(ruler); 0421 m_panelRulerView->syncPanelLocation(); 0422 } 0423 return m_panelRulerView.get(); 0424 } 0425 0426 #include "moc_panelconfigview.cpp"