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"