File indexing completed on 2024-05-05 05:29:53
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 void Decoration::requestClose() 0148 { 0149 d->client->d->requestClose(); 0150 } 0151 0152 void Decoration::requestContextHelp() 0153 { 0154 d->client->d->requestContextHelp(); 0155 } 0156 0157 void Decoration::requestMinimize() 0158 { 0159 d->client->d->requestMinimize(); 0160 } 0161 0162 void Decoration::requestToggleOnAllDesktops() 0163 { 0164 d->client->d->requestToggleOnAllDesktops(); 0165 } 0166 0167 void Decoration::requestToggleShade() 0168 { 0169 d->client->d->requestToggleShade(); 0170 } 0171 0172 void Decoration::requestToggleKeepAbove() 0173 { 0174 d->client->d->requestToggleKeepAbove(); 0175 } 0176 0177 void Decoration::requestToggleKeepBelow() 0178 { 0179 d->client->d->requestToggleKeepBelow(); 0180 } 0181 0182 #if KDECORATIONS2_ENABLE_DEPRECATED_SINCE(5, 21) 0183 void Decoration::requestShowWindowMenu() 0184 { 0185 requestShowWindowMenu(QRect()); 0186 } 0187 #endif 0188 0189 void Decoration::requestShowWindowMenu(const QRect &rect) 0190 { 0191 d->client->d->requestShowWindowMenu(rect); 0192 } 0193 0194 void Decoration::requestShowToolTip(const QString &text) 0195 { 0196 d->client->d->requestShowToolTip(text); 0197 } 0198 0199 void Decoration::requestHideToolTip() 0200 { 0201 d->client->d->requestHideToolTip(); 0202 } 0203 0204 void Decoration::requestToggleMaximization(Qt::MouseButtons buttons) 0205 { 0206 d->client->d->requestToggleMaximization(buttons); 0207 } 0208 0209 void Decoration::showApplicationMenu(int actionId) 0210 { 0211 const auto it = std::find_if(d->buttons.constBegin(), d->buttons.constEnd(), [](DecorationButton *button) { 0212 return button->type() == DecorationButtonType::ApplicationMenu; 0213 }); 0214 if (it != d->buttons.constEnd()) { 0215 requestShowApplicationMenu((*it)->geometry().toRect(), actionId); 0216 } 0217 } 0218 0219 void Decoration::requestShowApplicationMenu(const QRect &rect, int actionId) 0220 { 0221 if (auto *appMenuEnabledPrivate = dynamic_cast<ApplicationMenuEnabledDecoratedClientPrivate *>(d->client->d.get())) { 0222 appMenuEnabledPrivate->requestShowApplicationMenu(rect, actionId); 0223 } 0224 } 0225 0226 void Decoration::setBlurRegion(const QRegion ®ion) 0227 { 0228 if (d->blurRegion != region) { 0229 d->blurRegion = region; 0230 Q_EMIT blurRegionChanged(); 0231 } 0232 } 0233 0234 void Decoration::setBorders(const QMargins &borders) 0235 { 0236 if (d->borders != borders) { 0237 d->borders = borders; 0238 Q_EMIT bordersChanged(); 0239 } 0240 } 0241 0242 void Decoration::setResizeOnlyBorders(const QMargins &borders) 0243 { 0244 if (d->resizeOnlyBorders != borders) { 0245 d->resizeOnlyBorders = borders; 0246 Q_EMIT resizeOnlyBordersChanged(); 0247 } 0248 } 0249 0250 void Decoration::setTitleBar(const QRect &rect) 0251 { 0252 if (d->titleBar != rect) { 0253 d->titleBar = rect; 0254 Q_EMIT titleBarChanged(); 0255 } 0256 } 0257 0258 void Decoration::setOpaque(bool opaque) 0259 { 0260 if (d->opaque != opaque) { 0261 d->opaque = opaque; 0262 Q_EMIT opaqueChanged(opaque); 0263 } 0264 } 0265 0266 void Decoration::setShadow(const std::shared_ptr<DecorationShadow> &shadow) 0267 { 0268 if (d->shadow != shadow) { 0269 d->shadow = shadow; 0270 Q_EMIT shadowChanged(shadow); 0271 } 0272 } 0273 0274 QRegion Decoration::blurRegion() const 0275 { 0276 return d->blurRegion; 0277 } 0278 0279 QMargins Decoration::borders() const 0280 { 0281 return d->borders; 0282 } 0283 0284 QMargins Decoration::resizeOnlyBorders() const 0285 { 0286 return d->resizeOnlyBorders; 0287 } 0288 0289 QRect Decoration::titleBar() const 0290 { 0291 return d->titleBar; 0292 } 0293 0294 Qt::WindowFrameSection Decoration::sectionUnderMouse() const 0295 { 0296 return d->sectionUnderMouse; 0297 } 0298 0299 std::shared_ptr<DecorationShadow> Decoration::shadow() const 0300 { 0301 return d->shadow; 0302 } 0303 0304 bool Decoration::isOpaque() const 0305 { 0306 return d->opaque; 0307 } 0308 0309 int Decoration::borderLeft() const 0310 { 0311 return d->borders.left(); 0312 } 0313 0314 int Decoration::resizeOnlyBorderLeft() const 0315 { 0316 return d->resizeOnlyBorders.left(); 0317 } 0318 0319 int Decoration::borderRight() const 0320 { 0321 return d->borders.right(); 0322 } 0323 0324 int Decoration::resizeOnlyBorderRight() const 0325 { 0326 return d->resizeOnlyBorders.right(); 0327 } 0328 0329 int Decoration::borderTop() const 0330 { 0331 return d->borders.top(); 0332 } 0333 0334 int Decoration::resizeOnlyBorderTop() const 0335 { 0336 return d->resizeOnlyBorders.top(); 0337 } 0338 0339 int Decoration::borderBottom() const 0340 { 0341 return d->borders.bottom(); 0342 } 0343 0344 int Decoration::resizeOnlyBorderBottom() const 0345 { 0346 return d->resizeOnlyBorders.bottom(); 0347 } 0348 0349 QSize Decoration::size() const 0350 { 0351 const QMargins &b = d->borders; 0352 return QSize(d->client->width() + b.left() + b.right(), // 0353 (d->client->isShaded() ? 0 : d->client->height()) + b.top() + b.bottom()); 0354 } 0355 0356 QRect Decoration::rect() const 0357 { 0358 return QRect(QPoint(0, 0), size()); 0359 } 0360 0361 bool Decoration::event(QEvent *event) 0362 { 0363 switch (event->type()) { 0364 case QEvent::HoverEnter: 0365 hoverEnterEvent(static_cast<QHoverEvent *>(event)); 0366 return true; 0367 case QEvent::HoverLeave: 0368 hoverLeaveEvent(static_cast<QHoverEvent *>(event)); 0369 return true; 0370 case QEvent::HoverMove: 0371 hoverMoveEvent(static_cast<QHoverEvent *>(event)); 0372 return true; 0373 case QEvent::MouseButtonPress: 0374 mousePressEvent(static_cast<QMouseEvent *>(event)); 0375 return true; 0376 case QEvent::MouseButtonRelease: 0377 mouseReleaseEvent(static_cast<QMouseEvent *>(event)); 0378 return true; 0379 case QEvent::MouseMove: 0380 mouseMoveEvent(static_cast<QMouseEvent *>(event)); 0381 return true; 0382 case QEvent::Wheel: 0383 wheelEvent(static_cast<QWheelEvent *>(event)); 0384 return true; 0385 default: 0386 return QObject::event(event); 0387 } 0388 } 0389 0390 void Decoration::hoverEnterEvent(QHoverEvent *event) 0391 { 0392 for (DecorationButton *button : d->buttons) { 0393 QCoreApplication::instance()->sendEvent(button, event); 0394 } 0395 auto flooredPos = QPoint(std::floor(event->position().x()), std::floor(event->position().y())); 0396 d->updateSectionUnderMouse(flooredPos); 0397 } 0398 0399 void Decoration::hoverLeaveEvent(QHoverEvent *event) 0400 { 0401 for (DecorationButton *button : d->buttons) { 0402 QCoreApplication::instance()->sendEvent(button, event); 0403 } 0404 d->setSectionUnderMouse(Qt::NoSection); 0405 } 0406 0407 void Decoration::hoverMoveEvent(QHoverEvent *event) 0408 { 0409 for (DecorationButton *button : d->buttons) { 0410 if (!button->isEnabled() || !button->isVisible()) { 0411 continue; 0412 } 0413 const bool hovered = button->isHovered(); 0414 const bool contains = button->contains(event->position()); 0415 if (!hovered && contains) { 0416 QHoverEvent e(QEvent::HoverEnter, event->position(), event->oldPosF(), event->modifiers()); 0417 QCoreApplication::instance()->sendEvent(button, &e); 0418 } else if (hovered && !contains) { 0419 QHoverEvent e(QEvent::HoverLeave, event->position(), event->oldPosF(), event->modifiers()); 0420 QCoreApplication::instance()->sendEvent(button, &e); 0421 } else if (hovered && contains) { 0422 QCoreApplication::instance()->sendEvent(button, event); 0423 } 0424 } 0425 auto flooredPos = QPoint(std::floor(event->position().x()), std::floor(event->position().y())); 0426 d->updateSectionUnderMouse(flooredPos); 0427 } 0428 0429 void Decoration::mouseMoveEvent(QMouseEvent *event) 0430 { 0431 for (DecorationButton *button : d->buttons) { 0432 if (button->isPressed()) { 0433 QCoreApplication::instance()->sendEvent(button, event); 0434 return; 0435 } 0436 } 0437 // not handled, take care ourselves 0438 } 0439 0440 void Decoration::mousePressEvent(QMouseEvent *event) 0441 { 0442 for (DecorationButton *button : d->buttons) { 0443 if (button->isHovered()) { 0444 if (button->acceptedButtons().testFlag(event->button())) { 0445 QCoreApplication::instance()->sendEvent(button, event); 0446 } 0447 event->setAccepted(true); 0448 return; 0449 } 0450 } 0451 } 0452 0453 void Decoration::mouseReleaseEvent(QMouseEvent *event) 0454 { 0455 for (DecorationButton *button : d->buttons) { 0456 if (button->isPressed() && button->acceptedButtons().testFlag(event->button())) { 0457 QCoreApplication::instance()->sendEvent(button, event); 0458 return; 0459 } 0460 } 0461 // not handled, take care ourselves 0462 d->updateSectionUnderMouse(event->pos()); 0463 } 0464 0465 void Decoration::wheelEvent(QWheelEvent *event) 0466 { 0467 for (DecorationButton *button : d->buttons) { 0468 if (button->contains(event->position())) { 0469 QCoreApplication::instance()->sendEvent(button, event); 0470 event->setAccepted(true); 0471 } 0472 } 0473 } 0474 0475 void Decoration::update(const QRect &r) 0476 { 0477 Q_EMIT damaged(r.isNull() ? rect() : r); 0478 } 0479 0480 void Decoration::update() 0481 { 0482 update(QRect()); 0483 } 0484 0485 void Decoration::setSettings(const std::shared_ptr<DecorationSettings> &settings) 0486 { 0487 d->settings = settings; 0488 } 0489 0490 std::shared_ptr<DecorationSettings> Decoration::settings() const 0491 { 0492 return d->settings; 0493 } 0494 0495 } // namespace 0496 0497 #include "moc_decoration.cpp"