File indexing completed on 2024-04-28 13:25:47

0001 /*
0002     SPDX-FileCopyrightText: 2018 Michail Vourlakos <mvourlakos@gmail.com>
0003     SPDX-License-Identifier: GPL-2.0-or-later
0004 */
0005 
0006 #include "secondaryconfigview.h"
0007 
0008 // local
0009 #include <config-latte.h>
0010 #include "primaryconfigview.h"
0011 #include "../panelshadows_p.h"
0012 #include "../view.h"
0013 #include "../../lattecorona.h"
0014 #include "../../wm/abstractwindowinterface.h"
0015 
0016 // Qt
0017 #include <QQuickItem>
0018 #include <QQmlContext>
0019 #include <QQmlEngine>
0020 #include <QScreen>
0021 
0022 // KDE
0023 #include <KLocalizedContext>
0024 #include <KDeclarative/KDeclarative>
0025 #include <KWayland/Client/plasmashell.h>
0026 #include <KWayland/Client/surface.h>
0027 #include <KWindowEffects>
0028 #include <KWindowSystem>
0029 
0030 // Plasma
0031 #include <Plasma/Package>
0032 
0033 namespace Latte {
0034 namespace ViewPart {
0035 
0036 SecondaryConfigView::SecondaryConfigView(Latte::View *view, PrimaryConfigView *parent)
0037     : SubConfigView(view, QString("#secondaryconfigview#")),
0038       m_parent(parent)
0039 {
0040     connect(this, &QQuickView::widthChanged, this, &SecondaryConfigView::updateEffects);
0041     connect(this, &QQuickView::heightChanged, this, &SecondaryConfigView::updateEffects);
0042 
0043     connect(this, &QQuickView::statusChanged, [&](QQuickView::Status status) {
0044         if (status == QQuickView::Ready) {
0045             updateEffects();
0046         }
0047     });
0048 
0049     connections << connect(m_parent, &PrimaryConfigView::availableScreenGeometryChanged, this, &SecondaryConfigView::syncGeometry);
0050 
0051     setParentView(view);
0052     init();
0053 }
0054 
0055 void SecondaryConfigView::init()
0056 {
0057     SubConfigView::init();
0058 
0059     QByteArray tempFilePath = "lattedocksecondaryconfigurationui";
0060 
0061     updateEnabledBorders();
0062 
0063     auto source = QUrl::fromLocalFile(m_latteView->containment()->corona()->kPackage().filePath(tempFilePath));
0064     setSource(source);
0065     syncGeometry();
0066 
0067     if (m_parent && KWindowSystem::isPlatformX11()) {
0068         m_parent->requestActivate();
0069     }
0070 }
0071 
0072 QRect SecondaryConfigView::geometryWhenVisible() const
0073 {
0074     return m_geometryWhenVisible;
0075 }
0076 
0077 void SecondaryConfigView::initParentView(Latte::View *view)
0078 {   
0079     SubConfigView::initParentView(view);
0080 
0081     rootContext()->setContextProperty(QStringLiteral("primaryConfigView"), m_parent);
0082 
0083     updateEnabledBorders();
0084     syncGeometry();
0085 }
0086 
0087 void SecondaryConfigView::syncGeometry()
0088 {
0089     if (!m_latteView || !m_latteView->layout() || !m_latteView->containment() || !m_parent || !rootObject()) {
0090         return;
0091     }
0092 
0093     const QSize size(rootObject()->width(), rootObject()->height());
0094     const auto location = m_latteView->containment()->location();
0095     const auto scrGeometry = m_latteView->screenGeometry();
0096     const auto availGeometry = m_parent->availableScreenGeometry();
0097     const auto canvasGeometry = m_latteView->positioner()->canvasGeometry();
0098 
0099     int canvasThickness = m_latteView->formFactor() == Plasma::Types::Vertical ? canvasGeometry.width() : canvasGeometry.height();
0100 
0101     int secondaryConfigSpacing = 2 * m_latteView->fontPixelSize();
0102 
0103     QPoint position{0, 0};
0104 
0105     int xPos{0};
0106     int yPos{0};
0107 
0108     switch (m_latteView->containment()->formFactor()) {
0109     case Plasma::Types::Horizontal: {
0110         if (qApp->isLeftToRight()) {
0111             xPos = availGeometry.x() + secondaryConfigSpacing;
0112         } else {
0113             xPos = availGeometry.x() + availGeometry.width() - size.width() - secondaryConfigSpacing;
0114         }
0115 
0116         if (location == Plasma::Types::TopEdge) {
0117             yPos = scrGeometry.y() + canvasThickness;
0118         } else if (location == Plasma::Types::BottomEdge) {
0119             yPos = scrGeometry.y() + scrGeometry.height() - canvasThickness - size.height();
0120         }
0121     }
0122         break;
0123 
0124     case Plasma::Types::Vertical: {
0125         yPos = availGeometry.y() + secondaryConfigSpacing;
0126 
0127         if (location == Plasma::Types::LeftEdge) {
0128             xPos = scrGeometry.x() + canvasThickness;
0129         } else if (location == Plasma::Types::RightEdge) {
0130             xPos = scrGeometry.x() + scrGeometry.width() - canvasThickness - size.width();
0131         }
0132     }
0133         break;
0134 
0135     default:
0136         qWarning() << "no sync geometry, wrong formFactor";
0137         break;
0138     }
0139 
0140     position = {xPos, yPos};
0141 
0142     updateEnabledBorders();
0143 
0144     auto geometry = QRect(position.x(), position.y(), size.width(), size.height());
0145 
0146     if (m_geometryWhenVisible == geometry) {
0147         return;
0148     }
0149 
0150     m_geometryWhenVisible = geometry;
0151 
0152     setPosition(position);
0153 
0154     if (m_shellSurface) {
0155         m_shellSurface->setPosition(position);
0156     }
0157 
0158     setMaximumSize(size);
0159     setMinimumSize(size);
0160     resize(size);
0161 
0162     //! after placement request to activate the main config window in order to avoid
0163     //! rare cases of closing settings window from secondaryConfigView->focusOutEvent
0164     if (m_parent && KWindowSystem::isPlatformX11()) {
0165         m_parent->requestActivate();
0166     }
0167 }
0168 
0169 void SecondaryConfigView::showEvent(QShowEvent *ev)
0170 {
0171     if (m_shellSurface) {
0172         //! under wayland it needs to be set again after its hiding
0173         m_shellSurface->setPosition(m_geometryWhenVisible.topLeft());
0174     }
0175 
0176     SubConfigView::showEvent(ev);
0177 
0178     if (!m_latteView) {
0179         return;
0180     }
0181 
0182     setFlags(wFlags());
0183     m_corona->wm()->setViewExtraFlags(this, false, Latte::Types::NormalWindow);
0184 
0185     syncGeometry();
0186 
0187     m_screenSyncTimer.start();
0188     QTimer::singleShot(400, this, &SecondaryConfigView::syncGeometry);
0189 
0190     emit showSignal();
0191 }
0192 
0193 void SecondaryConfigView::focusOutEvent(QFocusEvent *ev)
0194 {
0195     Q_UNUSED(ev);
0196 
0197     if (!m_latteView) {
0198         return;
0199     }
0200 
0201     const auto *focusWindow = qGuiApp->focusWindow();
0202 
0203     if (focusWindow && (focusWindow->flags().testFlag(Qt::Popup)
0204                          || focusWindow->flags().testFlag(Qt::ToolTip))) {
0205         return;
0206     }
0207 
0208     const auto parent = qobject_cast<PrimaryConfigView *>(m_parent);
0209 
0210     if (!parent->hasFocus()) {
0211         parent->hideConfigWindow();
0212     }
0213 }
0214 
0215 void SecondaryConfigView::hideConfigWindow()
0216 {
0217     if (m_shellSurface) {
0218         //!NOTE: Avoid crash in wayland environment with qt5.9
0219         close();
0220     } else {
0221         hide();
0222     }
0223 }
0224 
0225 void SecondaryConfigView::updateEffects()
0226 {
0227     //! Don't apply any effect before the wayland surface is created under wayland
0228     //! https://bugs.kde.org/show_bug.cgi?id=392890
0229     if (KWindowSystem::isPlatformWayland() && !m_shellSurface) {
0230         return;
0231     }
0232 
0233     //! Don't apply any effect before the wayland surface is created under wayland
0234     //! https://bugs.kde.org/show_bug.cgi?id=392890
0235     if (KWindowSystem::isPlatformWayland() && !m_shellSurface) {
0236         return;
0237     }
0238 
0239     if (!m_background) {
0240         m_background = new Plasma::FrameSvg(this);
0241     }
0242 
0243     if (m_background->imagePath() != "dialogs/background") {
0244         m_background->setImagePath(QStringLiteral("dialogs/background"));
0245     }
0246 
0247     m_background->setEnabledBorders(m_enabledBorders);
0248     m_background->resizeFrame(size());
0249 
0250     QRegion mask = m_background->mask();
0251 
0252     QRegion fixedMask = mask.isNull() ? QRegion(QRect(0,0,width(),height())) : mask;
0253 
0254     if (!fixedMask.isEmpty()) {
0255         setMask(fixedMask);
0256     } else {
0257         setMask(QRegion());
0258     }
0259 
0260     if (KWindowSystem::compositingActive()) {
0261         KWindowEffects::enableBlurBehind(winId(), true, fixedMask);
0262     } else {
0263         KWindowEffects::enableBlurBehind(winId(), false);
0264     }
0265 }
0266 
0267 //!BEGIN borders
0268 void SecondaryConfigView::updateEnabledBorders()
0269 {
0270     if (!this->screen()) {
0271         return;
0272     }
0273 
0274     Plasma::FrameSvg::EnabledBorders borders = Plasma::FrameSvg::AllBorders;
0275 
0276     switch (m_latteView->location()) {
0277     case Plasma::Types::TopEdge:
0278         borders &= ~Plasma::FrameSvg::TopBorder;
0279         break;
0280 
0281     case Plasma::Types::LeftEdge:
0282         borders &= ~Plasma::FrameSvg::LeftBorder;
0283         break;
0284 
0285     case Plasma::Types::RightEdge:
0286         borders &= ~Plasma::FrameSvg::RightBorder;
0287         break;
0288 
0289     case Plasma::Types::BottomEdge:
0290         borders &=  ~Plasma::FrameSvg::BottomBorder;
0291         break;
0292 
0293     default:
0294         break;
0295     }
0296 
0297     if (m_enabledBorders != borders) {
0298         m_enabledBorders = borders;
0299 
0300         m_corona->dialogShadows()->addWindow(this, m_enabledBorders);
0301 
0302         emit enabledBordersChanged();
0303     }
0304 }
0305 
0306 //!END borders
0307 
0308 }
0309 }
0310