File indexing completed on 2024-05-12 09:31:34

0001 /*
0002  * SPDX-FileCopyrightText: 2014 Martin Gräßlin <mgraesslin@kde.org>
0003  *
0004  * SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
0005  */
0006 #include "decoration.h"
0007 #include "decoratedclient.h"
0008 #include "decoration_p.h"
0009 #include "decorationbutton.h"
0010 #include "decorationsettings.h"
0011 #include "private/decoratedclientprivate.h"
0012 #include "private/decorationbridge.h"
0013 
0014 #include <QCoreApplication>
0015 #include <QHoverEvent>
0016 
0017 #include <cmath>
0018 
0019 namespace KDecoration2
0020 {
0021 namespace
0022 {
0023 DecorationBridge *findBridge(const QVariantList &args)
0024 {
0025     for (const auto &arg : args) {
0026         if (auto bridge = arg.toMap().value(QStringLiteral("bridge")).value<DecorationBridge *>()) {
0027             return bridge;
0028         }
0029     }
0030     Q_UNREACHABLE();
0031 }
0032 }
0033 
0034 Decoration::Private::Private(Decoration *deco, const QVariantList &args)
0035     : sectionUnderMouse(Qt::NoSection)
0036     , bridge(findBridge(args))
0037     , client(std::shared_ptr<DecoratedClient>(new DecoratedClient(deco, bridge)))
0038     , opaque(false)
0039     , q(deco)
0040 {
0041 }
0042 
0043 void Decoration::Private::setSectionUnderMouse(Qt::WindowFrameSection section)
0044 {
0045     if (sectionUnderMouse == section) {
0046         return;
0047     }
0048     sectionUnderMouse = section;
0049     Q_EMIT q->sectionUnderMouseChanged(sectionUnderMouse);
0050 }
0051 
0052 void Decoration::Private::updateSectionUnderMouse(const QPoint &mousePosition)
0053 {
0054     if (titleBar.contains(mousePosition)) {
0055         setSectionUnderMouse(Qt::TitleBarArea);
0056         return;
0057     }
0058     const QSize size = q->size();
0059     const int corner = 2 * settings->largeSpacing();
0060     const bool left = mousePosition.x() < borders.left();
0061     const bool top = mousePosition.y() < borders.top();
0062     const bool bottom = size.height() - mousePosition.y() <= borders.bottom();
0063     const bool right = size.width() - mousePosition.x() <= borders.right();
0064     if (left) {
0065         if (top && mousePosition.y() < titleBar.top() + corner) {
0066             setSectionUnderMouse(Qt::TopLeftSection);
0067         } else if (size.height() - mousePosition.y() <= borders.bottom() + corner && mousePosition.y() > titleBar.bottom()) {
0068             setSectionUnderMouse(Qt::BottomLeftSection);
0069         } else {
0070             setSectionUnderMouse(Qt::LeftSection);
0071         }
0072         return;
0073     }
0074     if (right) {
0075         if (top && mousePosition.y() < titleBar.top() + corner) {
0076             setSectionUnderMouse(Qt::TopRightSection);
0077         } else if (size.height() - mousePosition.y() <= borders.bottom() + corner && mousePosition.y() > titleBar.bottom()) {
0078             setSectionUnderMouse(Qt::BottomRightSection);
0079         } else {
0080             setSectionUnderMouse(Qt::RightSection);
0081         }
0082         return;
0083     }
0084     if (bottom) {
0085         if (mousePosition.y() > titleBar.bottom()) {
0086             if (mousePosition.x() < borders.left() + corner) {
0087                 setSectionUnderMouse(Qt::BottomLeftSection);
0088             } else if (size.width() - mousePosition.x() <= borders.right() + corner) {
0089                 setSectionUnderMouse(Qt::BottomRightSection);
0090             } else {
0091                 setSectionUnderMouse(Qt::BottomSection);
0092             }
0093         } else {
0094             setSectionUnderMouse(Qt::TitleBarArea);
0095         }
0096         return;
0097     }
0098     if (top) {
0099         if (mousePosition.y() < titleBar.top()) {
0100             if (mousePosition.x() < borders.left() + corner) {
0101                 setSectionUnderMouse(Qt::TopLeftSection);
0102             } else if (size.width() - mousePosition.x() <= borders.right() + corner) {
0103                 setSectionUnderMouse(Qt::TopRightSection);
0104             } else {
0105                 setSectionUnderMouse(Qt::TopSection);
0106             }
0107         } else {
0108             setSectionUnderMouse(Qt::TitleBarArea);
0109         }
0110         return;
0111     }
0112     setSectionUnderMouse(Qt::NoSection);
0113 }
0114 
0115 void Decoration::Private::addButton(DecorationButton *button)
0116 {
0117     Q_ASSERT(!buttons.contains(button));
0118     buttons << button;
0119     QObject::connect(button, &QObject::destroyed, q, [this](QObject *o) {
0120         auto it = buttons.begin();
0121         while (it != buttons.end()) {
0122             if (*it == static_cast<DecorationButton *>(o)) {
0123                 it = buttons.erase(it);
0124             } else {
0125                 it++;
0126             }
0127         }
0128     });
0129 }
0130 
0131 Decoration::Decoration(QObject *parent, const QVariantList &args)
0132     : QObject(parent)
0133     , d(new Private(this, args))
0134 {
0135     connect(this, &Decoration::bordersChanged, this, [this] {
0136         update();
0137     });
0138 }
0139 
0140 Decoration::~Decoration() = default;
0141 
0142 DecoratedClient *Decoration::client() const
0143 {
0144     return d->client.get();
0145 }
0146 
0147 #define DELEGATE(name)                                                                                                                                         \
0148     void Decoration::name()                                                                                                                                    \
0149     {                                                                                                                                                          \
0150         d->client->d->name();                                                                                                                                  \
0151     }
0152 
0153 DELEGATE(requestClose)
0154 DELEGATE(requestContextHelp)
0155 DELEGATE(requestMinimize)
0156 DELEGATE(requestToggleOnAllDesktops)
0157 DELEGATE(requestToggleShade)
0158 DELEGATE(requestToggleKeepAbove)
0159 DELEGATE(requestToggleKeepBelow)
0160 
0161 #undef DELEGATE
0162 
0163 #if KDECORATIONS2_ENABLE_DEPRECATED_SINCE(5, 21)
0164 void Decoration::requestShowWindowMenu()
0165 {
0166     requestShowWindowMenu(QRect());
0167 }
0168 #endif
0169 
0170 void Decoration::requestShowWindowMenu(const QRect &rect)
0171 {
0172     d->client->d->requestShowWindowMenu(rect);
0173 }
0174 
0175 void Decoration::requestShowToolTip(const QString &text)
0176 {
0177     d->client->d->requestShowToolTip(text);
0178 }
0179 
0180 void Decoration::requestHideToolTip()
0181 {
0182     d->client->d->requestHideToolTip();
0183 }
0184 
0185 void Decoration::requestToggleMaximization(Qt::MouseButtons buttons)
0186 {
0187     d->client->d->requestToggleMaximization(buttons);
0188 }
0189 
0190 void Decoration::showApplicationMenu(int actionId)
0191 {
0192     auto it = std::find_if(d->buttons.constBegin(), d->buttons.constEnd(), [](DecorationButton *button) {
0193         return button->type() == DecorationButtonType::ApplicationMenu;
0194     });
0195 
0196     if (it != d->buttons.constEnd()) {
0197         requestShowApplicationMenu((*it)->geometry().toRect(), actionId);
0198     }
0199 }
0200 
0201 void Decoration::requestShowApplicationMenu(const QRect &rect, int actionId)
0202 {
0203     if (auto *appMenuEnabledPrivate = dynamic_cast<ApplicationMenuEnabledDecoratedClientPrivate *>(d->client->d.get())) {
0204         appMenuEnabledPrivate->requestShowApplicationMenu(rect, actionId);
0205     }
0206 }
0207 
0208 #define DELEGATE(name, variableName, type, emitValue)                                                                                                          \
0209     void Decoration::name(type a)                                                                                                                              \
0210     {                                                                                                                                                          \
0211         if (d->variableName == a) {                                                                                                                            \
0212             return;                                                                                                                                            \
0213         }                                                                                                                                                      \
0214         d->variableName = a;                                                                                                                                   \
0215         Q_EMIT variableName##Changed(emitValue);                                                                                                               \
0216     }
0217 
0218 DELEGATE(setBlurRegion, blurRegion, const QRegion &, )
0219 DELEGATE(setBorders, borders, const QMargins &, )
0220 DELEGATE(setResizeOnlyBorders, resizeOnlyBorders, const QMargins &, )
0221 DELEGATE(setTitleBar, titleBar, const QRect &, )
0222 DELEGATE(setOpaque, opaque, bool, d->opaque)
0223 DELEGATE(setShadow, shadow, const std::shared_ptr<DecorationShadow> &, d->shadow)
0224 
0225 #undef DELEGATE
0226 
0227 #define DELEGATE(name, type)                                                                                                                                   \
0228     type Decoration::name() const                                                                                                                              \
0229     {                                                                                                                                                          \
0230         return d->name;                                                                                                                                        \
0231     }
0232 
0233 DELEGATE(blurRegion, QRegion)
0234 DELEGATE(borders, QMargins)
0235 DELEGATE(resizeOnlyBorders, QMargins)
0236 DELEGATE(titleBar, QRect)
0237 DELEGATE(sectionUnderMouse, Qt::WindowFrameSection)
0238 DELEGATE(shadow, std::shared_ptr<DecorationShadow>)
0239 
0240 #undef DELEGATE
0241 
0242 bool Decoration::isOpaque() const
0243 {
0244     return d->opaque;
0245 }
0246 
0247 #define BORDER(name, Name)                                                                                                                                     \
0248     int Decoration::border##Name() const                                                                                                                       \
0249     {                                                                                                                                                          \
0250         return d->borders.name();                                                                                                                              \
0251     }                                                                                                                                                          \
0252     int Decoration::resizeOnlyBorder##Name() const                                                                                                             \
0253     {                                                                                                                                                          \
0254         return d->resizeOnlyBorders.name();                                                                                                                    \
0255     }
0256 
0257 BORDER(left, Left)
0258 BORDER(right, Right)
0259 BORDER(top, Top)
0260 BORDER(bottom, Bottom)
0261 #undef BORDER
0262 
0263 QSize Decoration::size() const
0264 {
0265     const QMargins &b = d->borders;
0266     return QSize(d->client->width() + b.left() + b.right(), //
0267                  (d->client->isShaded() ? 0 : d->client->height()) + b.top() + b.bottom());
0268 }
0269 
0270 QRect Decoration::rect() const
0271 {
0272     return QRect(QPoint(0, 0), size());
0273 }
0274 
0275 bool Decoration::event(QEvent *event)
0276 {
0277     switch (event->type()) {
0278     case QEvent::HoverEnter:
0279         hoverEnterEvent(static_cast<QHoverEvent *>(event));
0280         return true;
0281     case QEvent::HoverLeave:
0282         hoverLeaveEvent(static_cast<QHoverEvent *>(event));
0283         return true;
0284     case QEvent::HoverMove:
0285         hoverMoveEvent(static_cast<QHoverEvent *>(event));
0286         return true;
0287     case QEvent::MouseButtonPress:
0288         mousePressEvent(static_cast<QMouseEvent *>(event));
0289         return true;
0290     case QEvent::MouseButtonRelease:
0291         mouseReleaseEvent(static_cast<QMouseEvent *>(event));
0292         return true;
0293     case QEvent::MouseMove:
0294         mouseMoveEvent(static_cast<QMouseEvent *>(event));
0295         return true;
0296     case QEvent::Wheel:
0297         wheelEvent(static_cast<QWheelEvent *>(event));
0298         return true;
0299     default:
0300         return QObject::event(event);
0301     }
0302 }
0303 
0304 void Decoration::hoverEnterEvent(QHoverEvent *event)
0305 {
0306     for (DecorationButton *button : d->buttons) {
0307         QCoreApplication::instance()->sendEvent(button, event);
0308     }
0309     auto flooredPos = QPoint(std::floor(event->posF().x()), std::floor(event->posF().y()));
0310     d->updateSectionUnderMouse(flooredPos);
0311 }
0312 
0313 void Decoration::hoverLeaveEvent(QHoverEvent *event)
0314 {
0315     for (DecorationButton *button : d->buttons) {
0316         QCoreApplication::instance()->sendEvent(button, event);
0317     }
0318     d->setSectionUnderMouse(Qt::NoSection);
0319 }
0320 
0321 void Decoration::hoverMoveEvent(QHoverEvent *event)
0322 {
0323     for (DecorationButton *button : d->buttons) {
0324         if (!button->isEnabled() || !button->isVisible()) {
0325             continue;
0326         }
0327         const bool hovered = button->isHovered();
0328         const bool contains = button->contains(event->posF());
0329         if (!hovered && contains) {
0330             QHoverEvent e(QEvent::HoverEnter, event->posF(), event->oldPosF(), event->modifiers());
0331             QCoreApplication::instance()->sendEvent(button, &e);
0332         } else if (hovered && !contains) {
0333             QHoverEvent e(QEvent::HoverLeave, event->posF(), event->oldPosF(), event->modifiers());
0334             QCoreApplication::instance()->sendEvent(button, &e);
0335         } else if (hovered && contains) {
0336             QCoreApplication::instance()->sendEvent(button, event);
0337         }
0338     }
0339     auto flooredPos = QPoint(std::floor(event->posF().x()), std::floor(event->posF().y()));
0340     d->updateSectionUnderMouse(flooredPos);
0341 }
0342 
0343 void Decoration::mouseMoveEvent(QMouseEvent *event)
0344 {
0345     for (DecorationButton *button : d->buttons) {
0346         if (button->isPressed()) {
0347             QCoreApplication::instance()->sendEvent(button, event);
0348             return;
0349         }
0350     }
0351     // not handled, take care ourselves
0352 }
0353 
0354 void Decoration::mousePressEvent(QMouseEvent *event)
0355 {
0356     for (DecorationButton *button : d->buttons) {
0357         if (button->isHovered()) {
0358             if (button->acceptedButtons().testFlag(event->button())) {
0359                 QCoreApplication::instance()->sendEvent(button, event);
0360             }
0361             event->setAccepted(true);
0362             return;
0363         }
0364     }
0365 }
0366 
0367 void Decoration::mouseReleaseEvent(QMouseEvent *event)
0368 {
0369     for (DecorationButton *button : d->buttons) {
0370         if (button->isPressed() && button->acceptedButtons().testFlag(event->button())) {
0371             QCoreApplication::instance()->sendEvent(button, event);
0372             return;
0373         }
0374     }
0375     // not handled, take care ourselves
0376     d->updateSectionUnderMouse(event->pos());
0377 }
0378 
0379 void Decoration::wheelEvent(QWheelEvent *event)
0380 {
0381     for (DecorationButton *button : d->buttons) {
0382         if (button->contains(event->position())) {
0383             QCoreApplication::instance()->sendEvent(button, event);
0384             event->setAccepted(true);
0385         }
0386     }
0387 }
0388 
0389 void Decoration::update(const QRect &r)
0390 {
0391     Q_EMIT damaged(r.isNull() ? rect() : r);
0392 }
0393 
0394 void Decoration::update()
0395 {
0396     update(QRect());
0397 }
0398 
0399 void Decoration::setSettings(const std::shared_ptr<DecorationSettings> &settings)
0400 {
0401     d->settings = settings;
0402 }
0403 
0404 std::shared_ptr<DecorationSettings> Decoration::settings() const
0405 {
0406     return d->settings;
0407 }
0408 
0409 } // namespace
0410 
0411 #include "moc_decoration.cpp"