File indexing completed on 2024-12-01 05:02:25
0001 /* 0002 SPDX-FileCopyrightText: 2016 Smith AR <audoban@openmailbox.org> 0003 SPDX-FileCopyrightText: 2016 Michail Vourlakos <mvourlakos@gmail.com> 0004 0005 SPDX-License-Identifier: GPL-2.0-or-later 0006 */ 0007 0008 #include "primaryconfigview.h" 0009 0010 // local 0011 #include <config-latte.h> 0012 #include "canvasconfigview.h" 0013 #include "indicatoruimanager.h" 0014 #include "secondaryconfigview.h" 0015 #include "../effects.h" 0016 #include "../panelshadows_p.h" 0017 #include "../view.h" 0018 #include "../../lattecorona.h" 0019 #include "../../layouts/manager.h" 0020 #include "../../layout/genericlayout.h" 0021 #include "../../settings/universalsettings.h" 0022 #include "../../wm/abstractwindowinterface.h" 0023 0024 // Qt 0025 #include <QQuickItem> 0026 #include <QQmlContext> 0027 #include <QQmlEngine> 0028 #include <QScreen> 0029 0030 // KDE 0031 #include <KLocalizedContext> 0032 #include <KDeclarative/KDeclarative> 0033 #include <KWayland/Client/plasmashell.h> 0034 #include <KWayland/Client/surface.h> 0035 #include <KWindowEffects> 0036 #include <KWindowSystem> 0037 0038 // Plasma 0039 #include <Plasma/Package> 0040 0041 #define CANVASWINDOWINTERVAL 50 0042 #define PRIMARYWINDOWINTERVAL 250 0043 #define SECONDARYWINDOWINTERVAL 200 0044 #define SLIDEOUTINTERVAL 400 0045 0046 namespace Latte { 0047 namespace ViewPart { 0048 0049 PrimaryConfigView::PrimaryConfigView(Latte::View *view) 0050 : SubConfigView(view, QString("#primaryconfigview#")), 0051 m_indicatorUiManager(new Config::IndicatorUiManager(this)) 0052 { 0053 connect(this, &QQuickWindow::xChanged, this, &PrimaryConfigView::xChanged); 0054 connect(this, &QQuickWindow::yChanged, this, &PrimaryConfigView::yChanged); 0055 0056 connect(this, &QQuickView::widthChanged, this, &PrimaryConfigView::updateEffects); 0057 connect(this, &QQuickView::heightChanged, this, &PrimaryConfigView::updateEffects); 0058 0059 connect(this, &PrimaryConfigView::availableScreenGeometryChanged, this, &PrimaryConfigView::syncGeometry); 0060 0061 connect(this, &QQuickView::statusChanged, [&](QQuickView::Status status) { 0062 if (status == QQuickView::Ready) { 0063 updateEffects(); 0064 } 0065 }); 0066 0067 if (m_corona) { 0068 connections << connect(m_corona, &Latte::Corona::raiseViewsTemporaryChanged, this, &PrimaryConfigView::raiseDocksTemporaryChanged); 0069 connections << connect(m_corona, &Latte::Corona::availableScreenRectChangedFrom, this, &PrimaryConfigView::updateAvailableScreenGeometry); 0070 0071 connections << connect(m_corona->layoutsManager(), &Latte::Layouts::Manager::currentLayoutIsSwitching, this, [this]() { 0072 if (isVisible()) { 0073 hideConfigWindow(); 0074 } 0075 }); 0076 0077 connect(m_corona->universalSettings(), &Latte::UniversalSettings::inAdvancedModeForEditSettingsChanged, 0078 this, &PrimaryConfigView::updateShowInlineProperties); 0079 connect(m_corona->universalSettings(), &Latte::UniversalSettings::inAdvancedModeForEditSettingsChanged, 0080 this, &PrimaryConfigView::syncGeometry); 0081 } 0082 0083 m_availableScreemGeometryTimer.setSingleShot(true); 0084 m_availableScreemGeometryTimer.setInterval(250); 0085 0086 connections << connect(&m_availableScreemGeometryTimer, &QTimer::timeout, this, [this]() { 0087 instantUpdateAvailableScreenGeometry(); 0088 }); 0089 0090 setParentView(view); 0091 init(); 0092 } 0093 0094 PrimaryConfigView::~PrimaryConfigView() 0095 { 0096 if (m_canvasConfigView) { 0097 delete m_canvasConfigView; 0098 } 0099 0100 if (m_secConfigView) { 0101 delete m_secConfigView; 0102 } 0103 } 0104 0105 void PrimaryConfigView::init() 0106 { 0107 SubConfigView::init(); 0108 0109 QByteArray tempFilePath = "lattedockconfigurationui"; 0110 0111 auto source = QUrl::fromLocalFile(m_latteView->containment()->corona()->kPackage().filePath(tempFilePath)); 0112 setSource(source); 0113 syncGeometry(); 0114 } 0115 0116 Config::IndicatorUiManager *PrimaryConfigView::indicatorUiManager() 0117 { 0118 return m_indicatorUiManager; 0119 } 0120 0121 void PrimaryConfigView::setOnActivities(QStringList activities) 0122 { 0123 m_corona->wm()->setWindowOnActivities(trackedWindowId(), activities); 0124 0125 if (m_secConfigView) { 0126 m_corona->wm()->setWindowOnActivities(m_secConfigView->trackedWindowId(), activities); 0127 } 0128 0129 if (m_canvasConfigView) { 0130 m_corona->wm()->setWindowOnActivities(m_canvasConfigView->trackedWindowId(), activities); 0131 } 0132 } 0133 0134 void PrimaryConfigView::requestActivate() 0135 { 0136 if (m_latteView && m_latteView->visibility()) { 0137 if (KWindowSystem::isPlatformX11()) { 0138 m_latteView->visibility()->setViewOnFrontLayer(); 0139 } else if (m_shellSurface) { 0140 m_corona->wm()->requestActivate(m_latteView->positioner()->trackedWindowId()); 0141 } 0142 } 0143 0144 if (m_secConfigView) { 0145 m_secConfigView->requestActivate(); 0146 } 0147 0148 SubConfigView::requestActivate(); 0149 } 0150 0151 void PrimaryConfigView::showConfigWindow() 0152 { 0153 if (isVisible()) { 0154 return; 0155 } 0156 0157 if (m_latteView && m_latteView->containment()) { 0158 m_latteView->containment()->setUserConfiguring(true); 0159 } 0160 0161 showAfter(PRIMARYWINDOWINTERVAL); 0162 showCanvasWindow(); 0163 showSecondaryWindow(); 0164 } 0165 0166 void PrimaryConfigView::hideConfigWindow() 0167 { 0168 if (m_shellSurface) { 0169 //!NOTE: Avoid crash in wayland environment with qt5.9 0170 close(); 0171 } else { 0172 hide(); 0173 } 0174 0175 hideCanvasWindow(); 0176 hideSecondaryWindow(); 0177 } 0178 0179 void PrimaryConfigView::showCanvasWindow() 0180 { 0181 if (!m_canvasConfigView) { 0182 m_canvasConfigView = new CanvasConfigView(m_latteView, this); 0183 } 0184 0185 if (m_canvasConfigView && !m_canvasConfigView->isVisible()){ 0186 m_canvasConfigView->showAfter(CANVASWINDOWINTERVAL); 0187 } 0188 } 0189 0190 void PrimaryConfigView::hideCanvasWindow() 0191 { 0192 if (m_canvasConfigView) { 0193 m_canvasConfigView->hideConfigWindow(); 0194 } 0195 } 0196 0197 void PrimaryConfigView::showSecondaryWindow() 0198 { 0199 bool isValidShowing{m_latteView->formFactor() == Plasma::Types::Horizontal && inAdvancedMode()}; 0200 0201 if (!isValidShowing) { 0202 return; 0203 } 0204 0205 if (!m_secConfigView) { 0206 m_secConfigView = new SecondaryConfigView(m_latteView, this); 0207 } 0208 0209 if (m_secConfigView && !m_secConfigView->isVisible()){ 0210 m_secConfigView->showAfter(SECONDARYWINDOWINTERVAL); 0211 } 0212 } 0213 0214 void PrimaryConfigView::hideSecondaryWindow() 0215 { 0216 if (m_secConfigView) { 0217 m_secConfigView->hideConfigWindow(); 0218 } 0219 } 0220 0221 void PrimaryConfigView::setParentView(Latte::View *view, const bool &immediate) 0222 { 0223 if (m_latteView == view) { 0224 return; 0225 } 0226 0227 if (m_latteView && !immediate) { 0228 hideConfigWindow(); 0229 0230 //!slide-out delay 0231 QTimer::singleShot(SLIDEOUTINTERVAL, [this, view]() { 0232 initParentView(view); 0233 showConfigWindow(); 0234 }); 0235 } else { 0236 initParentView(view); 0237 showConfigWindow(); 0238 } 0239 } 0240 0241 void PrimaryConfigView::initParentView(Latte::View *view) 0242 { 0243 setIsReady(false); 0244 0245 SubConfigView::initParentView(view); 0246 0247 viewconnections << connect(m_latteView, &Latte::View::layoutChanged, this, [this]() { 0248 if (m_latteView->layout()) { 0249 updateAvailableScreenGeometry(); 0250 } 0251 }); 0252 0253 viewconnections << connect(m_latteView, &Latte::View::editThicknessChanged, this, [this]() { 0254 updateAvailableScreenGeometry(); 0255 }); 0256 0257 viewconnections << connect(m_latteView, &Latte::View::maxNormalThicknessChanged, this, [this]() { 0258 updateAvailableScreenGeometry(); 0259 }); 0260 0261 viewconnections << connect(m_latteView, &Latte::View::locationChanged, this, [this]() { 0262 updateAvailableScreenGeometry(); 0263 }); 0264 0265 viewconnections << connect(m_latteView->positioner(), &Latte::ViewPart::Positioner::currentScreenChanged, this, [this]() { 0266 updateAvailableScreenGeometry(); 0267 }); 0268 0269 viewconnections << connect(m_corona->universalSettings(), &Latte::UniversalSettings::inAdvancedModeForEditSettingsChanged, m_latteView, &Latte::View::inSettingsAdvancedModeChanged); 0270 viewconnections << connect(m_latteView->containment(), &Plasma::Containment::immutabilityChanged, this, &PrimaryConfigView::immutabilityChanged); 0271 0272 m_originalByPassWM = m_latteView->byPassWM(); 0273 m_originalMode = m_latteView->visibility()->mode(); 0274 0275 updateEnabledBorders(); 0276 updateAvailableScreenGeometry(); 0277 syncGeometry(); 0278 0279 setIsReady(true); 0280 0281 if (m_canvasConfigView) { 0282 m_canvasConfigView->setParentView(view); 0283 } 0284 0285 if (m_secConfigView) { 0286 m_secConfigView->setParentView(view); 0287 } 0288 0289 //! inform view about the current settings level 0290 emit m_latteView->inSettingsAdvancedModeChanged(); 0291 } 0292 0293 void PrimaryConfigView::instantUpdateAvailableScreenGeometry() 0294 { 0295 if (!m_latteView || !m_latteView->positioner()) { 0296 return; 0297 } 0298 0299 int currentScrId = m_latteView->positioner()->currentScreenId(); 0300 0301 QList<Latte::Types::Visibility> ignoreModes{Latte::Types::SidebarOnDemand,Latte::Types::SidebarAutoHide}; 0302 0303 if (m_latteView->visibility() && m_latteView->visibility()->isSidebar()) { 0304 ignoreModes.removeAll(Latte::Types::SidebarOnDemand); 0305 ignoreModes.removeAll(Latte::Types::SidebarAutoHide); 0306 } 0307 0308 QString activityid = m_latteView->layout()->lastUsedActivity(); 0309 0310 m_availableScreenGeometry = m_corona->availableScreenRectWithCriteria(currentScrId, activityid, ignoreModes, {}, false, true); 0311 emit availableScreenGeometryChanged(); 0312 } 0313 0314 void PrimaryConfigView::updateAvailableScreenGeometry(View *origin) 0315 { 0316 if (!m_latteView || !m_latteView->layout() || m_latteView == origin) { 0317 return; 0318 } 0319 0320 if (!m_availableScreemGeometryTimer.isActive()) { 0321 m_availableScreemGeometryTimer.start(); 0322 } 0323 } 0324 0325 QRect PrimaryConfigView::availableScreenGeometry() const 0326 { 0327 return m_availableScreenGeometry; 0328 } 0329 0330 QRect PrimaryConfigView::geometryWhenVisible() const 0331 { 0332 return m_geometryWhenVisible; 0333 } 0334 0335 void PrimaryConfigView::syncGeometry() 0336 { 0337 if (!m_latteView || !m_latteView->layout() || !m_latteView->containment() || !rootObject()) { 0338 return; 0339 } 0340 0341 const QSize size(rootObject()->width(), rootObject()->height()); 0342 const auto location = m_latteView->containment()->location(); 0343 const auto scrGeometry = m_latteView->screenGeometry(); 0344 const auto availGeometry = m_availableScreenGeometry; 0345 const auto canvasGeometry = m_latteView->positioner()->canvasGeometry(); 0346 0347 int canvasThickness = m_latteView->formFactor() == Plasma::Types::Vertical ? canvasGeometry.width() : canvasGeometry.height(); 0348 0349 QPoint position{0, 0}; 0350 0351 int xPos{0}; 0352 int yPos{0}; 0353 0354 switch (m_latteView->formFactor()) { 0355 case Plasma::Types::Horizontal: { 0356 if (inAdvancedMode()) { 0357 if (qApp->isLeftToRight()) { 0358 xPos = availGeometry.x() + availGeometry.width() - size.width(); 0359 } else { 0360 xPos = availGeometry.x(); 0361 } 0362 } else { 0363 xPos = scrGeometry.center().x() - size.width() / 2; 0364 } 0365 0366 if (location == Plasma::Types::TopEdge) { 0367 yPos = scrGeometry.y() + canvasThickness; 0368 } else if (location == Plasma::Types::BottomEdge) { 0369 yPos = scrGeometry.y() + scrGeometry.height() - canvasThickness - size.height(); 0370 } 0371 } 0372 break; 0373 0374 case Plasma::Types::Vertical: { 0375 if (location == Plasma::Types::LeftEdge) { 0376 xPos = scrGeometry.x() + canvasThickness; 0377 yPos = availGeometry.y() + (availGeometry.height() - size.height())/2; 0378 } else if (location == Plasma::Types::RightEdge) { 0379 xPos = scrGeometry.x() + scrGeometry.width() - canvasThickness - size.width(); 0380 yPos = availGeometry.y() + (availGeometry.height() - size.height())/2; 0381 } 0382 } 0383 break; 0384 0385 default: 0386 qWarning() << "no sync geometry, wrong formFactor"; 0387 break; 0388 } 0389 0390 position = {xPos, yPos}; 0391 0392 updateEnabledBorders(); 0393 0394 auto geometry = QRect(position.x(), position.y(), size.width(), size.height()); 0395 0396 QRect winGeometry(x(), y(), width(), height()); 0397 0398 if (m_geometryWhenVisible == geometry && winGeometry == geometry) { 0399 return; 0400 } 0401 0402 m_geometryWhenVisible = geometry; 0403 0404 setPosition(position); 0405 0406 if (m_shellSurface) { 0407 m_shellSurface->setPosition(position); 0408 } 0409 0410 setMaximumSize(size); 0411 setMinimumSize(size); 0412 resize(size); 0413 0414 emit m_latteView->configWindowGeometryChanged(); 0415 } 0416 0417 void PrimaryConfigView::showEvent(QShowEvent *ev) 0418 { 0419 updateAvailableScreenGeometry(); 0420 0421 if (m_shellSurface) { 0422 //! under wayland it needs to be set again after its hiding 0423 m_shellSurface->setPosition(m_geometryWhenVisible.topLeft()); 0424 } 0425 0426 SubConfigView::showEvent(ev); 0427 0428 if (!m_latteView) { 0429 return; 0430 } 0431 0432 setFlags(wFlags()); 0433 m_corona->wm()->setViewExtraFlags(this, false, Latte::Types::NormalWindow); 0434 0435 syncGeometry(); 0436 0437 m_screenSyncTimer.start(); 0438 QTimer::singleShot(400, this, &PrimaryConfigView::syncGeometry); 0439 0440 updateShowInlineProperties(); 0441 0442 showCanvasWindow(); 0443 0444 emit showSignal(); 0445 0446 if (m_latteView && m_latteView->layout()) { 0447 m_latteView->layout()->setLastConfigViewFor(m_latteView); 0448 } 0449 } 0450 0451 void PrimaryConfigView::hideEvent(QHideEvent *ev) 0452 { 0453 if (!m_latteView) { 0454 return; 0455 } 0456 0457 if (m_latteView->containment()) { 0458 m_latteView->containment()->setUserConfiguring(false); 0459 } 0460 0461 const auto mode = m_latteView->visibility()->mode(); 0462 0463 if ((mode == Types::AlwaysVisible || mode == Types::WindowsGoBelow) 0464 && !(m_originalMode == Types::AlwaysVisible || m_originalMode == Types::WindowsGoBelow)) { 0465 //! mode changed to AlwaysVisible OR WindowsGoBelow FROM Dodge mode 0466 if (m_originalByPassWM) { 0467 //! if original by pass is active 0468 m_latteView->layout()->recreateView(m_latteView->containment()); 0469 } 0470 } else if (m_latteView->byPassWM() != m_originalByPassWM) { 0471 m_latteView->layout()->recreateView(m_latteView->containment()); 0472 } 0473 0474 setVisible(false); 0475 } 0476 0477 bool PrimaryConfigView::hasFocus() const 0478 { 0479 bool primaryHasHocus{isActive()}; 0480 bool secHasFocus{m_secConfigView && m_secConfigView->isActive()}; 0481 bool canvasHasFocus{m_canvasConfigView && m_canvasConfigView->isActive()}; 0482 bool viewHasFocus{m_latteView && (m_latteView->containsMouse() || m_latteView->alternativesIsShown())}; 0483 0484 return (m_blockFocusLost || viewHasFocus || primaryHasHocus || secHasFocus || canvasHasFocus); 0485 } 0486 0487 void PrimaryConfigView::focusOutEvent(QFocusEvent *ev) 0488 { 0489 Q_UNUSED(ev); 0490 0491 if (!m_latteView) { 0492 return; 0493 } 0494 0495 const auto *focusWindow = qGuiApp->focusWindow(); 0496 0497 if (focusWindow && (focusWindow->flags().testFlag(Qt::Popup) 0498 || focusWindow->flags().testFlag(Qt::ToolTip))) { 0499 return; 0500 } 0501 0502 if (!hasFocus()) { 0503 hideConfigWindow(); 0504 } 0505 } 0506 0507 void PrimaryConfigView::immutabilityChanged(Plasma::Types::ImmutabilityType type) 0508 { 0509 if (type != Plasma::Types::Mutable && isVisible()) { 0510 hideConfigWindow(); 0511 } 0512 } 0513 0514 bool PrimaryConfigView::isReady() const 0515 { 0516 return m_isReady; 0517 } 0518 0519 void PrimaryConfigView::setIsReady(bool ready) 0520 { 0521 if (m_isReady == ready) { 0522 return; 0523 } 0524 0525 m_isReady = ready; 0526 emit isReadyChanged(); 0527 } 0528 0529 0530 bool PrimaryConfigView::sticker() const 0531 { 0532 return m_blockFocusLost; 0533 } 0534 0535 void PrimaryConfigView::setSticker(bool blockFocusLost) 0536 { 0537 if (m_blockFocusLost == blockFocusLost) 0538 return; 0539 0540 m_blockFocusLost = blockFocusLost; 0541 } 0542 0543 bool PrimaryConfigView::showInlineProperties() const 0544 { 0545 return m_showInlineProperties; 0546 } 0547 void PrimaryConfigView::setShowInlineProperties(bool show) 0548 { 0549 if (m_showInlineProperties == show) { 0550 return; 0551 } 0552 0553 m_showInlineProperties = show; 0554 emit showInlinePropertiesChanged(); 0555 } 0556 0557 void PrimaryConfigView::updateShowInlineProperties() 0558 { 0559 if (!m_latteView) { 0560 return; 0561 } 0562 0563 bool showSecWindow{false}; 0564 bool advancedApprovedSecWindow{false}; 0565 0566 if (inAdvancedMode() && m_latteView->formFactor() != Plasma::Types::Vertical) { 0567 showSecWindow = true; 0568 advancedApprovedSecWindow = true; 0569 } 0570 0571 //! consider screen geometry for showing or not the secondary window 0572 if (showSecWindow && !geometryWhenVisible().isNull()) { 0573 if (m_secConfigView && m_secConfigView->geometryWhenVisible().intersects(geometryWhenVisible())) { 0574 showSecWindow = false; 0575 } else if (advancedApprovedSecWindow) { 0576 showSecWindow = true; 0577 } 0578 } 0579 0580 if (showSecWindow) { 0581 showSecondaryWindow(); 0582 0583 // QTimer::singleShot(150, m_secConfigView, SLOT(show())); 0584 setShowInlineProperties(false); 0585 } else { 0586 hideSecondaryWindow(); 0587 setShowInlineProperties(true); 0588 } 0589 0590 // qDebug() << " showSecWindow:" << showSecWindow << " _ " << " inline:"<< !showSecWindow; 0591 } 0592 0593 bool PrimaryConfigView::inAdvancedMode() const 0594 { 0595 return m_corona->universalSettings()->inAdvancedModeForEditSettings(); 0596 } 0597 0598 //!BEGIN borders 0599 void PrimaryConfigView::updateEnabledBorders() 0600 { 0601 if (!this->screen()) { 0602 return; 0603 } 0604 0605 Plasma::FrameSvg::EnabledBorders borders = Plasma::FrameSvg::AllBorders; 0606 0607 switch (m_latteView->location()) { 0608 case Plasma::Types::TopEdge: 0609 borders &= m_inReverse ? ~Plasma::FrameSvg::BottomBorder : ~Plasma::FrameSvg::TopBorder; 0610 break; 0611 0612 case Plasma::Types::LeftEdge: 0613 borders &= ~Plasma::FrameSvg::LeftBorder; 0614 break; 0615 0616 case Plasma::Types::RightEdge: 0617 borders &= ~Plasma::FrameSvg::RightBorder; 0618 break; 0619 0620 case Plasma::Types::BottomEdge: 0621 borders &= m_inReverse ? ~Plasma::FrameSvg::TopBorder : ~Plasma::FrameSvg::BottomBorder; 0622 break; 0623 0624 default: 0625 break; 0626 } 0627 0628 if (m_enabledBorders != borders) { 0629 m_enabledBorders = borders; 0630 0631 m_corona->dialogShadows()->addWindow(this, m_enabledBorders); 0632 0633 emit enabledBordersChanged(); 0634 } 0635 } 0636 //!END borders 0637 0638 void PrimaryConfigView::updateEffects() 0639 { 0640 //! Don't apply any effect before the wayland surface is created under wayland 0641 //! https://bugs.kde.org/show_bug.cgi?id=392890 0642 if (KWindowSystem::isPlatformWayland() && !m_shellSurface) { 0643 return; 0644 } 0645 0646 if (!m_background) { 0647 m_background = new Plasma::FrameSvg(this); 0648 } 0649 0650 if (m_background->imagePath() != "dialogs/background") { 0651 m_background->setImagePath(QStringLiteral("dialogs/background")); 0652 } 0653 0654 m_background->setEnabledBorders(m_enabledBorders); 0655 m_background->resizeFrame(size()); 0656 0657 QRegion mask = m_background->mask(); 0658 0659 QRegion fixedMask = mask.isNull() ? QRegion(QRect(0,0,width(),height())) : mask; 0660 0661 if (!fixedMask.isEmpty()) { 0662 setMask(fixedMask); 0663 } else { 0664 setMask(QRegion()); 0665 } 0666 0667 if (KWindowSystem::compositingActive()) { 0668 KWindowEffects::enableBlurBehind(winId(), true, fixedMask); 0669 } else { 0670 KWindowEffects::enableBlurBehind(winId(), false); 0671 } 0672 } 0673 0674 } 0675 } 0676