File indexing completed on 2024-07-07 05:13:50

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