File indexing completed on 2024-04-28 16:54: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 "panelshadows_p.h"
0009 #include "shellcorona.h"
0010 
0011 #include <QAction>
0012 #include <QDebug>
0013 #include <QDir>
0014 #include <QQmlComponent>
0015 #include <QQmlContext>
0016 #include <QQmlEngine>
0017 #include <QScreen>
0018 
0019 #include <KActionCollection>
0020 #include <KWindowSystem>
0021 #include <KX11Extras>
0022 #include <klocalizedstring.h>
0023 #include <kwindoweffects.h>
0024 
0025 #include <Plasma/Containment>
0026 #include <Plasma/PluginLoader>
0027 
0028 #include <KWayland/Client/plasmashell.h>
0029 #include <KWayland/Client/surface.h>
0030 
0031 #include <chrono>
0032 
0033 using namespace std::chrono_literals;
0034 
0035 //////////////////////////////PanelConfigView
0036 PanelConfigView::PanelConfigView(Plasma::Containment *containment, PanelView *panelView, QWindow *parent)
0037     : ConfigView(containment, parent)
0038     , m_containment(containment)
0039     , m_panelView(panelView)
0040 {
0041     connect(panelView, &QObject::destroyed, this, &QObject::deleteLater);
0042 
0043     setScreen(panelView->screen());
0044 
0045     connect(panelView, &QWindow::screenChanged, &m_screenSyncTimer, QOverload<>::of(&QTimer::start));
0046     m_screenSyncTimer.setSingleShot(true);
0047     m_screenSyncTimer.setInterval(150ms);
0048     connect(&m_screenSyncTimer, &QTimer::timeout, [=]() {
0049         setScreen(panelView->screen());
0050         KWindowSystem::setType(winId(), NET::Dock);
0051         KWindowSystem::setState(winId(), NET::KeepAbove);
0052         syncGeometry();
0053         syncLocation();
0054     });
0055 
0056     KWindowSystem::setType(winId(), NET::Dock);
0057     KWindowSystem::setState(winId(), NET::KeepAbove);
0058     KX11Extras::forceActiveWindow(winId());
0059 
0060     updateBlurBehindAndContrast();
0061     connect(&m_theme, &Plasma::Theme::themeChanged, this, &PanelConfigView::updateBlurBehindAndContrast);
0062 
0063     rootContext()->setContextProperty(QStringLiteral("panel"), panelView);
0064     rootContext()->setContextProperty(QStringLiteral("configDialog"), this);
0065     connect(containment, &Plasma::Containment::formFactorChanged, this, &PanelConfigView::syncGeometry);
0066     connect(containment, &Plasma::Containment::locationChanged, this, &PanelConfigView::syncLocation);
0067 }
0068 
0069 PanelConfigView::~PanelConfigView()
0070 {
0071 }
0072 
0073 void PanelConfigView::init()
0074 {
0075     setSource(m_containment->corona()->kPackage().fileUrl("panelconfigurationui"));
0076     syncGeometry();
0077     syncLocation();
0078 }
0079 
0080 void PanelConfigView::updateBlurBehindAndContrast()
0081 {
0082     KWindowEffects::enableBlurBehind(this, m_theme.blurBehindEnabled());
0083     KWindowEffects::enableBackgroundContrast(this,
0084                                              m_theme.backgroundContrastEnabled(),
0085                                              m_theme.backgroundContrast(),
0086                                              m_theme.backgroundIntensity(),
0087                                              m_theme.backgroundSaturation());
0088 }
0089 
0090 void PanelConfigView::showAddWidgetDialog()
0091 {
0092     QAction *addWidgetAction = m_containment->actions()->action(QStringLiteral("add widgets"));
0093     if (addWidgetAction) {
0094         addWidgetAction->trigger();
0095     }
0096 }
0097 
0098 void PanelConfigView::addPanelSpacer()
0099 {
0100     ShellCorona *c = qobject_cast<ShellCorona *>(m_containment->corona());
0101     if (!c) {
0102         return;
0103     }
0104     // Add a spacer at the end *except* if there is exactly one spacer already
0105     // this to trigger the panel centering mode of the spacer in a slightly more discoverable way
0106     c->evaluateScript(QStringLiteral("panel = panelById(") + QString::number(m_containment->id())
0107                       + QStringLiteral(");"
0108                                        "var spacers = panel.widgets(\"org.kde.plasma.panelspacer\");"
0109                                        "if (spacers.length === 1) {"
0110                                        "    panel.addWidget(\"org.kde.plasma.panelspacer\", 0,0,1,1);"
0111                                        "} else {"
0112                                        "    panel.addWidget(\"org.kde.plasma.panelspacer\");"
0113                                        "}"));
0114 }
0115 
0116 void PanelConfigView::syncGeometry()
0117 {
0118     if (!m_containment || !rootObject()) {
0119         return;
0120     }
0121 
0122     if (m_containment->formFactor() == Plasma::Types::Vertical) {
0123         QSize s(rootObject()->implicitWidth(), screen()->size().height());
0124         resize(s);
0125         setMinimumSize(s);
0126         setMaximumSize(s);
0127 
0128         if (m_containment->location() == Plasma::Types::LeftEdge) {
0129             setPosition(m_panelView->geometry().right(), screen()->geometry().top());
0130         } else if (m_containment->location() == Plasma::Types::RightEdge) {
0131             setPosition(m_panelView->geometry().left() - width(), screen()->geometry().top());
0132         }
0133 
0134     } else {
0135         QSize s(screen()->size().width(), rootObject()->implicitHeight());
0136         resize(s);
0137         setMinimumSize(s);
0138         setMaximumSize(s);
0139 
0140         if (m_containment->location() == Plasma::Types::TopEdge) {
0141             setPosition(screen()->geometry().left(), m_panelView->geometry().bottom());
0142         } else if (m_containment->location() == Plasma::Types::BottomEdge) {
0143             setPosition(screen()->geometry().left(), m_panelView->geometry().top() - height());
0144         }
0145     }
0146 }
0147 
0148 void PanelConfigView::syncLocation()
0149 {
0150     if (!m_containment) {
0151         return;
0152     }
0153 
0154     KWindowEffects::SlideFromLocation slideLocation = KWindowEffects::NoEdge;
0155     Plasma::FrameSvg::EnabledBorders enabledBorders = Plasma::FrameSvg::AllBorders;
0156 
0157     switch (m_containment->location()) {
0158     case Plasma::Types::TopEdge:
0159         slideLocation = KWindowEffects::TopEdge;
0160         enabledBorders = Plasma::FrameSvg::BottomBorder;
0161         break;
0162     case Plasma::Types::RightEdge:
0163         slideLocation = KWindowEffects::RightEdge;
0164         enabledBorders = Plasma::FrameSvg::LeftBorder;
0165         break;
0166     case Plasma::Types::BottomEdge:
0167         slideLocation = KWindowEffects::BottomEdge;
0168         enabledBorders = Plasma::FrameSvg::TopBorder;
0169         break;
0170     case Plasma::Types::LeftEdge:
0171         slideLocation = KWindowEffects::LeftEdge;
0172         enabledBorders = Plasma::FrameSvg::RightBorder;
0173         break;
0174     default:
0175         break;
0176     }
0177 
0178     KWindowEffects::slideWindow(this, slideLocation, -1);
0179 
0180     if (m_enabledBorders != enabledBorders) {
0181         m_enabledBorders = enabledBorders;
0182 
0183         PanelShadows::self()->setEnabledBorders(this, enabledBorders);
0184 
0185         Q_EMIT enabledBordersChanged();
0186     }
0187 }
0188 
0189 void PanelConfigView::showEvent(QShowEvent *ev)
0190 {
0191     QQuickWindow::showEvent(ev);
0192 
0193     KWindowSystem::setType(winId(), NET::Dock);
0194     setFlags(Qt::WindowFlags((flags() | Qt::FramelessWindowHint) & (~Qt::WindowDoesNotAcceptFocus)) | Qt::X11BypassWindowManagerHint
0195              | Qt::WindowStaysOnTopHint);
0196     KWindowSystem::setState(winId(), NET::KeepAbove);
0197     KX11Extras::forceActiveWindow(winId());
0198     updateBlurBehindAndContrast();
0199     syncGeometry();
0200     syncLocation();
0201 
0202     // this because due to Qt xcb implementation the actual flags gets set only after a while after the window is actually visible
0203     m_screenSyncTimer.start();
0204 
0205     if (m_containment) {
0206         m_containment->setUserConfiguring(true);
0207     }
0208 
0209     PanelShadows::self()->addWindow(this, m_enabledBorders);
0210 }
0211 
0212 void PanelConfigView::hideEvent(QHideEvent *ev)
0213 {
0214     QQuickWindow::hideEvent(ev);
0215 
0216     if (m_containment) {
0217         m_containment->setUserConfiguring(false);
0218     }
0219     deleteLater();
0220 }
0221 
0222 void PanelConfigView::focusOutEvent(QFocusEvent *ev)
0223 {
0224     const QWindow *focusWindow = QGuiApplication::focusWindow();
0225 
0226     if (focusWindow && ((focusWindow->flags().testFlag(Qt::Popup)) || focusWindow->objectName() == QLatin1String("QMenuClassWindow"))) {
0227         return;
0228     }
0229     Q_UNUSED(ev)
0230     close();
0231 }
0232 
0233 void PanelConfigView::moveEvent(QMoveEvent *ev)
0234 {
0235     if (m_shellSurface) {
0236         m_shellSurface->setPosition(ev->pos());
0237     }
0238 }
0239 
0240 bool PanelConfigView::event(QEvent *e)
0241 {
0242     if (e->type() == QEvent::PlatformSurface) {
0243         switch (static_cast<QPlatformSurfaceEvent *>(e)->surfaceEventType()) {
0244         case QPlatformSurfaceEvent::SurfaceCreated:
0245             KWindowSystem::setState(winId(), NET::SkipTaskbar | NET::SkipPager);
0246 
0247             if (m_shellSurface) {
0248                 break;
0249             }
0250             if (ShellCorona *c = qobject_cast<ShellCorona *>(m_containment->corona())) {
0251                 using namespace KWayland::Client;
0252                 PlasmaShell *interface = c->waylandPlasmaShellInterface();
0253                 if (!interface) {
0254                     break;
0255                 }
0256                 Surface *s = Surface::fromWindow(this);
0257                 if (!s) {
0258                     break;
0259                 }
0260                 m_shellSurface = interface->createSurface(s, this);
0261                 m_shellSurface->setPanelTakesFocus(true);
0262             }
0263             break;
0264         case QPlatformSurfaceEvent::SurfaceAboutToBeDestroyed:
0265             delete m_shellSurface;
0266             m_shellSurface = nullptr;
0267             PanelShadows::self()->removeWindow(this);
0268             break;
0269         }
0270     }
0271 
0272     return PlasmaQuick::ConfigView::event(e);
0273 }
0274 
0275 void PanelConfigView::setVisibilityMode(PanelView::VisibilityMode mode)
0276 {
0277     m_panelView->setVisibilityMode(mode);
0278     Q_EMIT visibilityModeChanged();
0279 }
0280 
0281 PanelView::VisibilityMode PanelConfigView::visibilityMode() const
0282 {
0283     return m_panelView->visibilityMode();
0284 }
0285 
0286 void PanelConfigView::setOpacityMode(PanelView::OpacityMode mode)
0287 {
0288     m_panelView->setOpacityMode(mode);
0289     Q_EMIT opacityModeChanged();
0290 }
0291 
0292 PanelView::OpacityMode PanelConfigView::opacityMode() const
0293 {
0294     return m_panelView->opacityMode();
0295 }
0296 
0297 Plasma::FrameSvg::EnabledBorders PanelConfigView::enabledBorders() const
0298 {
0299     return m_enabledBorders;
0300 }
0301 
0302 #include "moc_panelconfigview.cpp"