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