File indexing completed on 2024-04-28 16:49:27
0001 /* 0002 * Copyright 2018 Michail Vourlakos <mvourlakos@gmail.com> 0003 * 0004 * This file is part of Latte-Dock 0005 * 0006 * Latte-Dock is free software; you can redistribute it and/or 0007 * modify it under the terms of the GNU General Public License as 0008 * published by the Free Software Foundation; either version 2 of 0009 * the License, or (at your option) any later version. 0010 * 0011 * Latte-Dock is distributed in the hope that it will be useful, 0012 * but WITHOUT ANY WARRANTY; without even the implied warranty of 0013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 0014 * GNU General Public License for more details. 0015 * 0016 * You should have received a copy of the GNU General Public License 0017 * along with this program. If not, see <http://www.gnu.org/licenses/>. 0018 */ 0019 0020 #include "secondaryconfigview.h" 0021 0022 // local 0023 #include <config-latte.h> 0024 #include "primaryconfigview.h" 0025 #include "../panelshadows_p.h" 0026 #include "../view.h" 0027 #include "../../lattecorona.h" 0028 #include "../../wm/abstractwindowinterface.h" 0029 0030 // Qt 0031 #include <QQuickItem> 0032 #include <QQmlContext> 0033 #include <QQmlEngine> 0034 #include <QScreen> 0035 0036 // KDE 0037 #include <KLocalizedContext> 0038 #include <KDeclarative/KDeclarative> 0039 #include <KWayland/Client/plasmashell.h> 0040 #include <KWayland/Client/surface.h> 0041 #include <KWindowEffects> 0042 #include <KWindowSystem> 0043 0044 // Plasma 0045 #include <Plasma/Package> 0046 0047 namespace Latte { 0048 namespace ViewPart { 0049 0050 SecondaryConfigView::SecondaryConfigView(Latte::View *view, QWindow *parent) 0051 : QQuickView(nullptr), 0052 m_latteView(view) 0053 { 0054 m_parent = qobject_cast<PrimaryConfigView *>(parent); 0055 m_corona = qobject_cast<Latte::Corona *>(m_latteView->containment()->corona()); 0056 0057 setupWaylandIntegration(); 0058 0059 if (KWindowSystem::isPlatformX11()) { 0060 m_corona->wm()->registerIgnoredWindow(winId()); 0061 } else { 0062 connect(m_corona->wm(), &WindowSystem::AbstractWindowInterface::latteWindowAdded, this, [&]() { 0063 if (m_waylandWindowId.isNull()) { 0064 m_waylandWindowId = m_corona->wm()->winIdFor("latte-dock", geometry()); 0065 m_corona->wm()->registerIgnoredWindow(m_waylandWindowId); 0066 } 0067 }); 0068 } 0069 0070 setResizeMode(QQuickView::SizeViewToRootObject); 0071 setScreen(m_latteView->screen()); 0072 0073 if (m_latteView && m_latteView->containment()) { 0074 setIcon(qGuiApp->windowIcon()); 0075 } 0076 0077 m_screenSyncTimer.setSingleShot(true); 0078 m_screenSyncTimer.setInterval(100); 0079 0080 connect(this, &QQuickView::widthChanged, this, &SecondaryConfigView::updateEffects); 0081 connect(this, &QQuickView::heightChanged, this, &SecondaryConfigView::updateEffects); 0082 0083 connect(this, &QQuickView::statusChanged, [&](QQuickView::Status status) { 0084 if (status == QQuickView::Ready) { 0085 updateEffects(); 0086 } 0087 }); 0088 0089 connections << connect(m_parent, &PrimaryConfigView::availableScreenGeometryChanged, this, &SecondaryConfigView::syncGeometry); 0090 0091 connections << connect(&m_screenSyncTimer, &QTimer::timeout, this, [this]() { 0092 setScreen(m_latteView->screen()); 0093 setFlags(wFlags()); 0094 0095 if (KWindowSystem::isPlatformX11()) { 0096 #if KF5_VERSION_MINOR >= 45 0097 KWindowSystem::setState(winId(), NET::SkipTaskbar | NET::SkipPager | NET::SkipSwitcher); 0098 #else 0099 KWindowSystem::setState(winId(), NET::SkipTaskbar | NET::SkipPager); 0100 #endif 0101 0102 KWindowSystem::setOnAllDesktops(winId(), true); 0103 } 0104 0105 syncGeometry(); 0106 syncSlideEffect(); 0107 }); 0108 connections << connect(m_latteView->visibility(), &VisibilityManager::modeChanged, this, &SecondaryConfigView::syncGeometry); 0109 0110 m_thicknessSyncTimer.setSingleShot(true); 0111 m_thicknessSyncTimer.setInterval(200); 0112 connections << connect(&m_thicknessSyncTimer, &QTimer::timeout, this, [this]() { 0113 syncGeometry(); 0114 }); 0115 0116 connections << connect(m_latteView, &Latte::View::normalThicknessChanged, [&]() { 0117 m_thicknessSyncTimer.start(); 0118 }); 0119 } 0120 0121 SecondaryConfigView::~SecondaryConfigView() 0122 { 0123 qDebug() << "SecDockConfigView deleting ..."; 0124 0125 m_corona->dialogShadows()->removeWindow(this); 0126 0127 m_corona->wm()->unregisterIgnoredWindow(KWindowSystem::isPlatformX11() ? winId() : m_waylandWindowId); 0128 0129 for (const auto &var : connections) { 0130 QObject::disconnect(var); 0131 } 0132 } 0133 0134 void SecondaryConfigView::init() 0135 { 0136 qDebug() << "dock secondary config view : initialization started..."; 0137 0138 setDefaultAlphaBuffer(true); 0139 setColor(Qt::transparent); 0140 m_corona->dialogShadows()->addWindow(this); 0141 rootContext()->setContextProperty(QStringLiteral("latteView"), m_latteView); 0142 rootContext()->setContextProperty(QStringLiteral("viewConfig"), this); 0143 rootContext()->setContextProperty(QStringLiteral("plasmoid"), m_latteView->containment()->property("_plasma_graphicObject").value<QObject *>()); 0144 0145 KDeclarative::KDeclarative kdeclarative; 0146 kdeclarative.setDeclarativeEngine(engine()); 0147 kdeclarative.setTranslationDomain(QStringLiteral("latte-dock")); 0148 #if KF5_VERSION_MINOR >= 45 0149 kdeclarative.setupContext(); 0150 kdeclarative.setupEngine(engine()); 0151 #else 0152 kdeclarative.setupBindings(); 0153 #endif 0154 0155 QByteArray tempFilePath = "lattedocksecondaryconfigurationui"; 0156 0157 updateEnabledBorders(); 0158 0159 auto source = QUrl::fromLocalFile(m_latteView->containment()->corona()->kPackage().filePath(tempFilePath)); 0160 setSource(source); 0161 syncGeometry(); 0162 syncSlideEffect(); 0163 0164 if (m_parent && KWindowSystem::isPlatformX11()) { 0165 m_parent->requestActivate(); 0166 } 0167 0168 qDebug() << "dock secondary config view : initialization ended..."; 0169 } 0170 0171 inline Qt::WindowFlags SecondaryConfigView::wFlags() const 0172 { 0173 return (flags() | Qt::FramelessWindowHint /*| Qt::WindowStaysOnTopHint*/) & ~Qt::WindowDoesNotAcceptFocus; 0174 } 0175 0176 QRect SecondaryConfigView::geometryWhenVisible() const 0177 { 0178 return m_geometryWhenVisible; 0179 } 0180 0181 void SecondaryConfigView::requestActivate() 0182 { 0183 if (KWindowSystem::isPlatformWayland() && m_shellSurface) { 0184 if (m_waylandWindowId.isNull()) { 0185 m_waylandWindowId = m_corona->wm()->winIdFor("latte-dock", geometry()); 0186 } 0187 0188 m_corona->wm()->requestActivate(m_waylandWindowId); 0189 } else { 0190 QQuickView::requestActivate(); 0191 } 0192 } 0193 0194 void SecondaryConfigView::syncGeometry() 0195 { 0196 if (!m_latteView || !m_latteView->layout() || !m_latteView->containment() || !m_parent || !rootObject()) { 0197 return; 0198 } 0199 0200 const QSize size(rootObject()->width(), rootObject()->height()); 0201 setMaximumSize(size); 0202 setMinimumSize(size); 0203 resize(size); 0204 0205 const auto location = m_latteView->containment()->location(); 0206 const auto scrGeometry = m_latteView->screenGeometry(); 0207 const auto availGeometry = m_parent->availableScreenGeometry(); 0208 0209 int clearThickness = m_latteView->editThickness(); 0210 0211 int secondaryConfigSpacing = 2 * m_latteView->fontPixelSize(); 0212 0213 QPoint position{0, 0}; 0214 0215 int xPos{0}; 0216 int yPos{0}; 0217 0218 switch (m_latteView->containment()->formFactor()) { 0219 case Plasma::Types::Horizontal: { 0220 if (qApp->isLeftToRight()) { 0221 xPos = availGeometry.x() + secondaryConfigSpacing; 0222 } else { 0223 xPos = availGeometry.x() + availGeometry.width() - size.width() - secondaryConfigSpacing; 0224 } 0225 0226 if (location == Plasma::Types::TopEdge) { 0227 yPos = scrGeometry.y() + clearThickness; 0228 } else if (location == Plasma::Types::BottomEdge) { 0229 yPos = scrGeometry.y() + scrGeometry.height() - clearThickness - size.height(); 0230 } 0231 } 0232 break; 0233 0234 case Plasma::Types::Vertical: { 0235 yPos = availGeometry.y() + secondaryConfigSpacing; 0236 0237 if (location == Plasma::Types::LeftEdge) { 0238 xPos = scrGeometry.x() + clearThickness; 0239 } else if (location == Plasma::Types::RightEdge) { 0240 xPos = scrGeometry.x() + scrGeometry.width() - clearThickness - size.width(); 0241 } 0242 } 0243 break; 0244 0245 default: 0246 qWarning() << "no sync geometry, wrong formFactor"; 0247 break; 0248 } 0249 0250 position = {xPos, yPos}; 0251 0252 updateEnabledBorders(); 0253 0254 m_geometryWhenVisible = QRect(position.x(), position.y(), size.width(), size.height()); 0255 0256 setPosition(position); 0257 0258 if (m_shellSurface) { 0259 m_shellSurface->setPosition(position); 0260 } 0261 0262 //! after placement request to activate the main config window in order to avoid 0263 //! rare cases of closing settings window from secondaryConfigView->focusOutEvent 0264 if (m_parent && KWindowSystem::isPlatformX11()) { 0265 m_parent->requestActivate(); 0266 } 0267 } 0268 0269 void SecondaryConfigView::syncSlideEffect() 0270 { 0271 if (!m_latteView || !m_latteView->containment()) { 0272 return; 0273 } 0274 0275 auto slideLocation = WindowSystem::AbstractWindowInterface::Slide::None; 0276 0277 switch (m_latteView->containment()->location()) { 0278 case Plasma::Types::TopEdge: 0279 slideLocation = WindowSystem::AbstractWindowInterface::Slide::Top; 0280 break; 0281 0282 case Plasma::Types::RightEdge: 0283 slideLocation = WindowSystem::AbstractWindowInterface::Slide::Right; 0284 break; 0285 0286 case Plasma::Types::BottomEdge: 0287 slideLocation = WindowSystem::AbstractWindowInterface::Slide::Bottom; 0288 break; 0289 0290 case Plasma::Types::LeftEdge: 0291 slideLocation = WindowSystem::AbstractWindowInterface::Slide::Left; 0292 break; 0293 0294 default: 0295 qDebug() << staticMetaObject.className() << "wrong location"; 0296 break; 0297 } 0298 0299 m_corona->wm()->slideWindow(*this, slideLocation); 0300 } 0301 0302 void SecondaryConfigView::showEvent(QShowEvent *ev) 0303 { 0304 QQuickWindow::showEvent(ev); 0305 0306 if (!m_latteView) { 0307 return; 0308 } 0309 0310 m_corona->wm()->setViewExtraFlags(*this); 0311 setFlags(wFlags()); 0312 0313 syncGeometry(); 0314 syncSlideEffect(); 0315 0316 m_screenSyncTimer.start(); 0317 QTimer::singleShot(400, this, &SecondaryConfigView::syncGeometry); 0318 0319 emit showSignal(); 0320 } 0321 0322 void SecondaryConfigView::focusOutEvent(QFocusEvent *ev) 0323 { 0324 Q_UNUSED(ev); 0325 0326 const auto *focusWindow = qGuiApp->focusWindow(); 0327 0328 if ((focusWindow && (focusWindow->flags().testFlag(Qt::Popup) 0329 || focusWindow->flags().testFlag(Qt::ToolTip))) 0330 || m_latteView->alternativesIsShown()) { 0331 return; 0332 } 0333 0334 const auto parent = qobject_cast<PrimaryConfigView *>(m_parent); 0335 0336 if (!m_latteView->containsMouse() && parent && !parent->sticker() && !parent->isActive()) { 0337 parent->hideConfigWindow(); 0338 } 0339 } 0340 0341 void SecondaryConfigView::setupWaylandIntegration() 0342 { 0343 if (m_shellSurface || !KWindowSystem::isPlatformWayland() || !m_latteView || !m_latteView->containment()) { 0344 // already setup 0345 return; 0346 } 0347 0348 if (m_corona) { 0349 using namespace KWayland::Client; 0350 PlasmaShell *interface = m_corona->waylandCoronaInterface(); 0351 0352 if (!interface) { 0353 return; 0354 } 0355 0356 Surface *s = Surface::fromWindow(this); 0357 0358 if (!s) { 0359 return; 0360 } 0361 0362 qDebug() << "wayland secondary settings surface was created..."; 0363 0364 m_shellSurface = interface->createSurface(s, this); 0365 m_shellSurface->setSkipTaskbar(true); 0366 #if KF5_VERSION_MINOR >= 47 0367 m_shellSurface->setSkipSwitcher(true); 0368 #endif 0369 0370 syncGeometry(); 0371 } 0372 } 0373 0374 bool SecondaryConfigView::event(QEvent *e) 0375 { 0376 if (e->type() == QEvent::PlatformSurface) { 0377 if (auto pe = dynamic_cast<QPlatformSurfaceEvent *>(e)) { 0378 switch (pe->surfaceEventType()) { 0379 case QPlatformSurfaceEvent::SurfaceCreated: 0380 0381 if (m_shellSurface) { 0382 break; 0383 } 0384 0385 setupWaylandIntegration(); 0386 break; 0387 0388 case QPlatformSurfaceEvent::SurfaceAboutToBeDestroyed: 0389 if (m_shellSurface) { 0390 delete m_shellSurface; 0391 m_shellSurface = nullptr; 0392 qDebug() << "WAYLAND secondary config window surface was deleted..."; 0393 } 0394 0395 break; 0396 } 0397 } 0398 } 0399 0400 return QQuickView::event(e); 0401 } 0402 0403 void SecondaryConfigView::hideConfigWindow() 0404 { 0405 if (m_shellSurface) { 0406 //!NOTE: Avoid crash in wayland environment with qt5.9 0407 close(); 0408 } else { 0409 hide(); 0410 } 0411 } 0412 0413 void SecondaryConfigView::updateEffects() 0414 { 0415 //! Don't apply any effect before the wayland surface is created under wayland 0416 //! https://bugs.kde.org/show_bug.cgi?id=392890 0417 if (KWindowSystem::isPlatformWayland() && !m_shellSurface) { 0418 return; 0419 } 0420 0421 if (!m_background) { 0422 m_background = new Plasma::FrameSvg(this); 0423 } 0424 0425 if (m_background->imagePath() != "widgets/panel-background") { 0426 m_background->setImagePath(QStringLiteral("widgets/panel-background")); 0427 } 0428 0429 m_background->setEnabledBorders(m_enabledBorders); 0430 m_background->resizeFrame(size()); 0431 0432 QRegion mask = m_background->mask(); 0433 0434 QRegion fixedMask = mask.isNull() ? QRegion(QRect(0,0,width(),height())) : mask; 0435 0436 if (!fixedMask.isEmpty()) { 0437 setMask(fixedMask); 0438 } else { 0439 setMask(QRegion()); 0440 } 0441 0442 if (KWindowSystem::compositingActive()) { 0443 KWindowEffects::enableBlurBehind(winId(), true, fixedMask); 0444 } else { 0445 KWindowEffects::enableBlurBehind(winId(), false); 0446 } 0447 } 0448 0449 //!BEGIN borders 0450 Plasma::FrameSvg::EnabledBorders SecondaryConfigView::enabledBorders() const 0451 { 0452 return m_enabledBorders; 0453 } 0454 0455 void SecondaryConfigView::updateEnabledBorders() 0456 { 0457 if (!this->screen()) { 0458 return; 0459 } 0460 0461 Plasma::FrameSvg::EnabledBorders borders = Plasma::FrameSvg::AllBorders; 0462 0463 switch (m_latteView->location()) { 0464 case Plasma::Types::TopEdge: 0465 borders &= ~Plasma::FrameSvg::TopBorder; 0466 break; 0467 0468 case Plasma::Types::LeftEdge: 0469 borders &= ~Plasma::FrameSvg::LeftBorder; 0470 break; 0471 0472 case Plasma::Types::RightEdge: 0473 borders &= ~Plasma::FrameSvg::RightBorder; 0474 break; 0475 0476 case Plasma::Types::BottomEdge: 0477 borders &= ~Plasma::FrameSvg::BottomBorder; 0478 break; 0479 0480 default: 0481 break; 0482 } 0483 0484 if (m_enabledBorders != borders) { 0485 m_enabledBorders = borders; 0486 0487 m_corona->dialogShadows()->addWindow(this, m_enabledBorders); 0488 0489 emit enabledBordersChanged(); 0490 } 0491 } 0492 0493 //!END borders 0494 0495 } 0496 } 0497