File indexing completed on 2025-03-09 05:02:43

0001 /*
0002     SPDX-FileCopyrightText: 2023 David Edmundson <davidedmundson@kde.org>
0003     SPDX-License-Identifier: LGPL-2.0-or-later
0004 */
0005 
0006 #include "plasmawindow.h"
0007 
0008 #include "dialogshadows_p.h"
0009 #include "private/dialogbackground_p.h"
0010 
0011 #include <QMarginsF>
0012 #include <QQuickItem>
0013 
0014 #include <KWindowEffects>
0015 #include <KWindowSystem>
0016 #include <KX11Extras>
0017 
0018 #include <Plasma/Theme>
0019 
0020 using namespace Plasma;
0021 
0022 namespace PlasmaQuick
0023 {
0024 class PlasmaWindowPrivate
0025 {
0026 public:
0027     PlasmaWindowPrivate(PlasmaWindow *_q)
0028         : q(_q)
0029     {
0030     }
0031     void handleFrameChanged();
0032     void updateMainItemGeometry();
0033     PlasmaWindow *q;
0034     DialogShadows *shadows;
0035     QPointer<QQuickItem> mainItem;
0036     DialogBackground *dialogBackground;
0037     PlasmaWindow::BackgroundHints backgroundHints = PlasmaWindow::StandardBackground;
0038 };
0039 
0040 PlasmaWindow::PlasmaWindow(const QString &svgPrefix)
0041     : QQuickWindow()
0042     , d(new PlasmaWindowPrivate(this))
0043 {
0044     setColor(QColor(Qt::transparent));
0045     setFlags(Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint);
0046 
0047     d->shadows = DialogShadows::instance(svgPrefix);
0048     d->dialogBackground = new DialogBackground(contentItem());
0049     d->dialogBackground->setImagePath(svgPrefix);
0050     connect(d->dialogBackground, &DialogBackground::fixedMarginsChanged, this, [this]() {
0051         d->updateMainItemGeometry();
0052         Q_EMIT paddingChanged();
0053     });
0054     connect(d->dialogBackground, &DialogBackground::maskChanged, this, [this]() {
0055         d->handleFrameChanged();
0056     });
0057 
0058     d->shadows->addWindow(this, d->dialogBackground->enabledBorders());
0059 }
0060 
0061 PlasmaWindow::~PlasmaWindow()
0062 {
0063 }
0064 
0065 void PlasmaWindow::setMainItem(QQuickItem *mainItem)
0066 {
0067     if (d->mainItem == mainItem)
0068         return;
0069 
0070     if (d->mainItem) {
0071         d->mainItem->setParentItem(nullptr);
0072     }
0073 
0074     d->mainItem = mainItem;
0075     Q_EMIT mainItemChanged();
0076 
0077     if (d->mainItem) {
0078         mainItem->setParentItem(contentItem());
0079         d->updateMainItemGeometry();
0080     }
0081 }
0082 
0083 QQuickItem *PlasmaWindow::mainItem() const
0084 {
0085     return d->mainItem;
0086 }
0087 
0088 static KSvg::FrameSvg::EnabledBorders edgeToBorder(Qt::Edges edges)
0089 {
0090     KSvg::FrameSvg::EnabledBorders borders = KSvg::FrameSvg::EnabledBorders(0);
0091 
0092     if (edges & Qt::Edge::TopEdge) {
0093         borders |= KSvg::FrameSvg::EnabledBorder::TopBorder;
0094     }
0095     if (edges & Qt::Edge::BottomEdge) {
0096         borders |= KSvg::FrameSvg::EnabledBorder::BottomBorder;
0097     }
0098     if (edges & Qt::Edge::LeftEdge) {
0099         borders |= KSvg::FrameSvg::EnabledBorder::LeftBorder;
0100     }
0101     if (edges & Qt::Edge::RightEdge) {
0102         borders |= KSvg::FrameSvg::EnabledBorder::RightBorder;
0103     }
0104 
0105     return borders;
0106 }
0107 
0108 static Qt::Edges bordersToEdge(KSvg::FrameSvg::EnabledBorders borders)
0109 {
0110     Qt::Edges edges;
0111     if (borders & KSvg::FrameSvg::EnabledBorder::TopBorder) {
0112         edges |= Qt::Edge::TopEdge;
0113     }
0114     if (borders & KSvg::FrameSvg::EnabledBorder::BottomBorder) {
0115         edges |= Qt::Edge::BottomEdge;
0116     }
0117     if (borders & KSvg::FrameSvg::EnabledBorder::LeftBorder) {
0118         edges |= Qt::Edge::LeftEdge;
0119     }
0120     if (borders & KSvg::FrameSvg::EnabledBorder::RightBorder) {
0121         edges |= Qt::Edge::RightEdge;
0122     }
0123     return edges;
0124 }
0125 
0126 void PlasmaWindow::setBorders(Qt::Edges bordersToShow)
0127 {
0128     d->dialogBackground->setEnabledBorders(edgeToBorder(bordersToShow));
0129     d->shadows->setEnabledBorders(this, d->dialogBackground->enabledBorders());
0130     Q_EMIT bordersChanged();
0131 }
0132 
0133 Qt::Edges PlasmaWindow::borders()
0134 {
0135     return bordersToEdge(d->dialogBackground->enabledBorders());
0136 }
0137 
0138 void PlasmaWindow::showEvent(QShowEvent *e)
0139 {
0140     // EWMH states that the state is reset every hide
0141     // Qt supports external factors setting state before the next show
0142     if (KWindowSystem::isPlatformX11()) {
0143         KX11Extras::setState(winId(), NET::SkipTaskbar | NET::SkipPager | NET::SkipSwitcher);
0144     }
0145     QQuickWindow::showEvent(e);
0146 }
0147 
0148 void PlasmaWindow::resizeEvent(QResizeEvent *e)
0149 {
0150     QQuickWindow::resizeEvent(e);
0151     const QSize windowSize = e->size();
0152     d->dialogBackground->setSize(windowSize);
0153     if (d->mainItem) {
0154         const QSize contentSize = windowSize.shrunkBy(padding());
0155         d->mainItem->setSize(contentSize);
0156     }
0157 }
0158 
0159 void PlasmaWindowPrivate::handleFrameChanged()
0160 {
0161     auto theme = Plasma::Theme();
0162     const QRegion mask = dialogBackground->mask();
0163     KWindowEffects::enableBlurBehind(q, theme.blurBehindEnabled(), mask);
0164     KWindowEffects::enableBackgroundContrast(q,
0165                                              theme.backgroundContrastEnabled(),
0166                                              theme.backgroundContrast(),
0167                                              theme.backgroundIntensity(),
0168                                              theme.backgroundSaturation(),
0169                                              mask);
0170 
0171     if (!KWindowSystem::isPlatformX11() || KX11Extras::compositingActive()) {
0172         q->setMask(QRegion());
0173     } else {
0174         q->setMask(mask);
0175     }
0176 }
0177 
0178 void PlasmaWindowPrivate::updateMainItemGeometry()
0179 {
0180     if (!mainItem) {
0181         return;
0182     }
0183     const QMargins frameMargins = q->padding();
0184     const QSize windowSize = q->size();
0185 
0186     mainItem->setX(frameMargins.left());
0187     mainItem->setY(frameMargins.top());
0188 
0189     const QSize contentSize = windowSize.shrunkBy(frameMargins);
0190     mainItem->setSize(contentSize);
0191 }
0192 
0193 QMargins PlasmaWindow::padding() const
0194 {
0195     return QMargins(d->dialogBackground->leftMargin(),
0196                     d->dialogBackground->topMargin(),
0197                     d->dialogBackground->rightMargin(),
0198                     d->dialogBackground->bottomMargin());
0199 }
0200 
0201 PlasmaWindow::BackgroundHints PlasmaWindow::backgroundHints() const
0202 {
0203     return d->backgroundHints;
0204 }
0205 
0206 void PlasmaWindow::setBackgroundHints(BackgroundHints hints)
0207 {
0208     if (d->backgroundHints == hints) {
0209         return;
0210     }
0211     d->backgroundHints = hints;
0212 
0213     auto prefix = QStringLiteral("");
0214     if (d->backgroundHints == PlasmaWindow::SolidBackground) {
0215         prefix = QStringLiteral("solid/");
0216     }
0217     d->dialogBackground->setImagePath(prefix + QStringLiteral("dialogs/background"));
0218 
0219     Q_EMIT backgroundHintsChanged();
0220 }
0221 
0222 qreal PlasmaWindow::topPadding() const
0223 {
0224     return d->dialogBackground->topMargin();
0225 }
0226 
0227 qreal PlasmaWindow::bottomPadding() const
0228 {
0229     return d->dialogBackground->bottomMargin();
0230 }
0231 
0232 qreal PlasmaWindow::leftPadding() const
0233 {
0234     return d->dialogBackground->leftMargin();
0235 }
0236 
0237 qreal PlasmaWindow::rightPadding() const
0238 {
0239     return d->dialogBackground->rightMargin();
0240 }
0241 }
0242 
0243 #include "moc_plasmawindow.cpp"