Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
Loading...
Searching...
No Matches
qwaylandadwaitadecoration.cpp
Go to the documentation of this file.
1// Copyright (C) 2023 Jan Grulich <jgrulich@redhat.com>
2// Copyright (C) 2023 The Qt Company Ltd.
3// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
4
6
7// QtCore
8#include <QtCore/QLoggingCategory>
9#include <QScopeGuard>
10
11// QtDBus
12#include <QtDBus/QDBusArgument>
13#include <QtDBus/QDBusConnection>
14#include <QtDBus/QDBusMessage>
15#include <QtDBus/QDBusPendingCall>
16#include <QtDBus/QDBusPendingCallWatcher>
17#include <QtDBus/QDBusPendingReply>
18#include <QtDBus/QDBusVariant>
19#include <QtDBus/QtDBus>
20
21// QtGui
22#include <QtGui/QColor>
23#include <QtGui/QPainter>
24#include <QtGui/QPainterPath>
25
26#include <QtGui/private/qguiapplication_p.h>
27#include <QtGui/qpa/qplatformtheme.h>
28
29// QtSvg
30#include <QtSvg/QSvgRenderer>
31
32// QtWayland
33#include <QtWaylandClient/private/qwaylandshmbackingstore_p.h>
34#include <QtWaylandClient/private/qwaylandwindow_p.h>
35
36
38
39using namespace Qt::StringLiterals;
40
41namespace QtWaylandClient {
42
43static constexpr int ceButtonSpacing = 12;
44static constexpr int ceButtonWidth = 24;
45static constexpr int ceCornerRadius = 12;
46static constexpr int ceShadowsWidth = 10;
47static constexpr int ceTitlebarHeight = 38;
48static constexpr int ceWindowBorderWidth = 1;
49static constexpr qreal ceTitlebarSeperatorWidth = 0.5;
50
51static QMap<QWaylandAdwaitaDecoration::ButtonIcon, QString> buttonMap = {
52 { QWaylandAdwaitaDecoration::CloseIcon, "window-close-symbolic"_L1 },
53 { QWaylandAdwaitaDecoration::MinimizeIcon, "window-minimize-symbolic"_L1 },
54 { QWaylandAdwaitaDecoration::MaximizeIcon, "window-maximize-symbolic"_L1 },
55 { QWaylandAdwaitaDecoration::RestoreIcon, "window-restore-symbolic"_L1 }
56};
57
58const QDBusArgument &operator>>(const QDBusArgument &argument, QMap<QString, QVariantMap> &map)
59{
61 map.clear();
62
63 while (!argument.atEnd()) {
67 argument >> key >> value;
70 }
71
73 return argument;
74}
75
76Q_LOGGING_CATEGORY(lcQWaylandAdwaitaDecorationLog, "qt.qpa.qwaylandadwaitadecoration", QtWarningMsg)
77
80{
81 m_lastButtonClick = QDateTime::currentDateTime();
82
84 option.setWrapMode(QTextOption::NoWrap);
85 m_windowTitle.setTextOption(option);
86 m_windowTitle.setTextFormat(Qt::PlainText);
87
89 if (const QFont *font = theme->font(QPlatformTheme::TitleBarFont))
90 m_font = std::make_unique<QFont>(*font);
91 if (!m_font) // Fallback to GNOME's default font
92 m_font = std::make_unique<QFont>("Cantarell"_L1, 10);
93
94 QTimer::singleShot(0, this, &QWaylandAdwaitaDecoration::loadConfiguration);
95}
96
98{
99 const bool onlyShadows = marginsType == QWaylandAbstractDecoration::ShadowsOnly;
100 const bool shadowsExcluded = marginsType == ShadowsExcluded;
101
102 if (waylandWindow()->windowStates() & Qt::WindowMaximized) {
103 // Maximized windows don't have anything around, no shadows, border,
104 // etc. Only report titlebar height in case we are not asking for shadow
105 // margins.
106 return QMargins(0, onlyShadows ? 0 : ceTitlebarHeight, 0, 0);
107 }
108
109 const QWaylandWindow::ToplevelWindowTilingStates tilingStates = waylandWindow()->toplevelWindowTilingStates();
110
111 // Since all sides (left, right, bottom) are going to be same
112 const int marginsBase = shadowsExcluded ? ceWindowBorderWidth : ceShadowsWidth + ceWindowBorderWidth;
113 const int sideMargins = onlyShadows ? ceShadowsWidth : marginsBase;
114 const int topMargins = onlyShadows ? ceShadowsWidth : ceTitlebarHeight + marginsBase;
115
116 return QMargins(tilingStates & QWaylandWindow::WindowTiledLeft ? 0 : sideMargins,
117 tilingStates & QWaylandWindow::WindowTiledTop ? onlyShadows ? 0 : ceTitlebarHeight : topMargins,
118 tilingStates & QWaylandWindow::WindowTiledRight ? 0 : sideMargins,
119 tilingStates & QWaylandWindow::WindowTiledBottom ? 0 : sideMargins);
120}
121
123{
124 const QRect surfaceRect = waylandWindow()->windowContentGeometry() + margins(ShadowsOnly);
125
127 p.setRenderHint(QPainter::Antialiasing);
128
129 /*
130 * Titlebar and window border
131 */
132 const int titleBarWidth = surfaceRect.width() - margins().left() - margins().right();
134
135 // Maximized or tiled won't have rounded corners
136 if (waylandWindow()->windowStates() & Qt::WindowMaximized
137 || waylandWindow()->toplevelWindowTilingStates() != QWaylandWindow::WindowNoState)
138 path.addRect(margins().left(), margins().bottom(), titleBarWidth, margins().top());
139 else
140 path.addRoundedRect(margins().left(), margins().bottom(), titleBarWidth,
142
143 p.save();
144 p.setPen(color(Border));
145 p.fillPath(path.simplified(), color(Background));
146 p.drawPath(path);
147 p.drawRect(margins().left(), margins().top(), titleBarWidth, surfaceRect.height() - margins().top() - margins().bottom());
148 p.restore();
149
150
151 /*
152 * Titlebar separator
153 */
154 p.save();
155 p.setPen(color(Border));
157 surfaceRect.width() - margins().right(),
159 p.restore();
160
161
162 /*
163 * Window title
164 */
165 const QRect top = QRect(margins().left(), margins().bottom(), surfaceRect.width(),
166 margins().top() - margins().bottom());
167 const QString windowTitleText = waylandWindow()->windowTitle();
168 if (!windowTitleText.isEmpty()) {
169 if (m_windowTitle.text() != windowTitleText) {
170 m_windowTitle.setText(windowTitleText);
171 m_windowTitle.prepare();
172 }
173
174 QRect titleBar = top;
175 if (m_placement == Right) {
176 titleBar.setLeft(margins().left());
177 titleBar.setRight(static_cast<int>(buttonRect(Minimize).left()) - 8);
178 } else {
179 titleBar.setLeft(static_cast<int>(buttonRect(Minimize).right()) + 8);
180 titleBar.setRight(surfaceRect.width() - margins().right());
181 }
182
183 p.save();
184 p.setClipRect(titleBar);
185 p.setPen(color(Foreground));
186 QSize size = m_windowTitle.size().toSize();
187 int dx = (top.width() - size.width()) / 2;
188 int dy = (top.height() - size.height()) / 2;
189 p.setFont(*m_font);
190 QPoint windowTitlePoint(top.topLeft().x() + dx, top.topLeft().y() + dy);
191 p.drawStaticText(windowTitlePoint, m_windowTitle);
192 p.restore();
193 }
194
195
196 /*
197 * Buttons
198 */
199 if (m_buttons.contains(Close))
200 drawButton(Close, &p);
201
202 if (m_buttons.contains(Maximize))
203 drawButton(Maximize, &p);
204
205 if (m_buttons.contains(Minimize))
206 drawButton(Minimize, &p);
207}
208
210 const QPointF &global, Qt::MouseButtons b,
211 Qt::KeyboardModifiers mods)
212{
214
215 if (local.y() > margins().top())
216 updateButtonHoverState(Button::None);
217
218 // Figure out what area mouse is in
220 if (local.y() <= surfaceRect.top() + margins().top())
221 processMouseTop(inputDevice, local, b, mods);
222 else if (local.y() > surfaceRect.bottom() - margins().bottom())
223 processMouseBottom(inputDevice, local, b, mods);
224 else if (local.x() <= surfaceRect.left() + margins().left())
225 processMouseLeft(inputDevice, local, b, mods);
226 else if (local.x() > surfaceRect.right() - margins().right())
227 processMouseRight(inputDevice, local, b, mods);
228 else {
229#if QT_CONFIG(cursor)
230 waylandWindow()->restoreMouseCursor(inputDevice);
231#endif
232 }
233
234 // Reset clicking state in case a button press is released outside
235 // the button area
236 if (isLeftReleased(b)) {
237 m_clicking = None;
238 requestRepaint();
239 }
240
242 return false;
243}
244
247 Qt::KeyboardModifiers mods)
248{
249 Q_UNUSED(inputDevice)
251 Q_UNUSED(mods)
252
253 bool handled = state == QEventPoint::Pressed;
254
255 if (handled) {
256 if (buttonRect(Close).contains(local))
258 else if (m_buttons.contains(Maximize) && buttonRect(Maximize).contains(local))
259 window()->setWindowStates(window()->windowStates() ^ Qt::WindowMaximized);
260 else if (m_buttons.contains(Minimize) && buttonRect(Minimize).contains(local))
261 window()->setWindowState(Qt::WindowMinimized);
262 else if (local.y() <= margins().top())
263 waylandWindow()->shellSurface()->move(inputDevice);
264 else
265 handled = false;
266 }
267
268 return handled;
269}
270
271QString getIconSvg(const QString &iconName)
272{
274
275 qCDebug(lcQWaylandAdwaitaDecorationLog) << "Searched icon themes: " << themeNames;
276
277 for (const QString &themeName : themeNames) {
278 if (themeName.isEmpty())
279 continue;
280
281 for (const QString &path : QIcon::themeSearchPaths()) {
282 if (path.startsWith(QLatin1Char(':')))
283 continue;
284
285 const QString fullPath = QString("%1/%2").arg(path).arg(themeName);
286 QDirIterator dirIt(fullPath, {"*.svg"}, QDir::Files, QDirIterator::Subdirectories);
287 while (dirIt.hasNext()) {
288 const QString fileName = dirIt.next();
289 const QFileInfo fileInfo(fileName);
290
291 if (fileInfo.fileName() == iconName) {
292 qCDebug(lcQWaylandAdwaitaDecorationLog) << "Using " << iconName << " from " << themeName << " theme";
293 QFile readFile(fileInfo.filePath());
294 readFile.open(QFile::ReadOnly);
295 return readFile.readAll();
296 }
297 }
298 }
299 }
300
301 qCWarning(lcQWaylandAdwaitaDecorationLog) << "Failed to find an svg icon for " << iconName;
302
303 return QString();
304}
305
306void QWaylandAdwaitaDecoration::loadConfiguration()
307{
308 qRegisterMetaType<QDBusVariant>();
309 qDBusRegisterMetaType<QMap<QString, QVariantMap>>();
310
312
313 QDBusMessage message = QDBusMessage::createMethodCall("org.freedesktop.portal.Desktop"_L1,
314 "/org/freedesktop/portal/desktop"_L1,
315 "org.freedesktop.portal.Settings"_L1,
316 "ReadAll"_L1);
317 message << QStringList{ { "org.gnome.desktop.wm.preferences"_L1 },
318 { "org.freedesktop.appearance"_L1 } };
319
320 QDBusPendingCall pendingCall = connection.asyncCall(message);
323 QDBusPendingReply<QMap<QString, QVariantMap>> reply = *watcher;
324 if (reply.isValid()) {
325 QMap<QString, QVariantMap> settings = reply.value();
326 if (!settings.isEmpty()) {
327 const uint colorScheme = settings.value("org.freedesktop.appearance"_L1).value("color-scheme"_L1).toUInt();
328 updateColors(colorScheme == 1); // 1 == Prefer Dark
329 const QString buttonLayout = settings.value("org.gnome.desktop.wm.preferences"_L1).value("button-layout"_L1).toString();
330 if (!buttonLayout.isEmpty())
331 updateTitlebarLayout(buttonLayout);
332 // Workaround for QGtkStyle not having correct titlebar font
333 const QString titlebarFont =
334 settings.value("org.gnome.desktop.wm.preferences"_L1).value("titlebar-font"_L1).toString();
335 if (titlebarFont.contains("bold"_L1, Qt::CaseInsensitive)) {
336 m_font->setBold(true);
337 }
338 }
339 }
340 watcher->deleteLater();
341 });
342
343 QDBusConnection::sessionBus().connect(QString(), "/org/freedesktop/portal/desktop"_L1,
344 "org.freedesktop.portal.Settings"_L1, "SettingChanged"_L1, this,
345 SLOT(settingChanged(QString, QString, QDBusVariant)));
346
347 // Load SVG icons
348 for (auto mapIt = buttonMap.constBegin(); mapIt != buttonMap.constEnd(); mapIt++) {
349 const QString fullName = mapIt.value() + QStringLiteral(".svg");
350 m_icons[mapIt.key()] = getIconSvg(fullName);
351 }
352
353 updateColors(false);
354}
355
356void QWaylandAdwaitaDecoration::updateColors(bool isDark)
357{
358 qCDebug(lcQWaylandAdwaitaDecorationLog) << "Color scheme changed to: " << (isDark ? "dark" : "light");
359
360 m_colors = { { Background, isDark ? QColor(0x303030) : QColor(0xffffff) },
361 { BackgroundInactive, isDark ? QColor(0x242424) : QColor(0xfafafa) },
362 { Foreground, isDark ? QColor(0xffffff) : QColor(0x2e2e2e) },
363 { ForegroundInactive, isDark ? QColor(0x919191) : QColor(0x949494) },
364 { Border, isDark ? QColor(0x3b3b3b) : QColor(0xdbdbdb) },
365 { BorderInactive, isDark ? QColor(0x303030) : QColor(0xdbdbdb) },
366 { ButtonBackground, isDark ? QColor(0x444444) : QColor(0xebebeb) },
367 { ButtonBackgroundInactive, isDark ? QColor(0x2e2e2e) : QColor(0xf0f0f0) },
368 { HoveredButtonBackground, isDark ? QColor(0x4f4f4f) : QColor(0xe0e0e0) },
369 { PressedButtonBackground, isDark ? QColor(0x6e6e6e) : QColor(0xc2c2c2) } };
370 requestRepaint();
371}
372
373void QWaylandAdwaitaDecoration::updateTitlebarLayout(const QString &layout)
374{
375 const QStringList layouts = layout.split(QLatin1Char(':'));
376 if (layouts.count() != 2)
377 return;
378
379 // Remove previous configuration
380 m_buttons.clear();
381
382 const QString &leftLayout = layouts.at(0);
383 const QString &rightLayout = layouts.at(1);
384 m_placement = leftLayout.contains("close"_L1) ? Left : Right;
385
386 int pos = 1;
387 const QString &buttonLayout = m_placement == Right ? rightLayout : leftLayout;
388
389 QStringList buttonList = buttonLayout.split(QLatin1Char(','));
390 if (m_placement == Right)
391 std::reverse(buttonList.begin(), buttonList.end());
392
393 for (const QString &button : buttonList) {
394 if (button == "close"_L1)
395 m_buttons.insert(Close, pos);
396 else if (button == "maximize"_L1)
397 m_buttons.insert(Maximize, pos);
398 else if (button == "minimize"_L1)
399 m_buttons.insert(Minimize, pos);
400
401 pos++;
402 }
403
404 qCDebug(lcQWaylandAdwaitaDecorationLog) << "Button layout changed to: " << layout;
405
406 requestRepaint();
407}
408
409void QWaylandAdwaitaDecoration::settingChanged(const QString &group, const QString &key,
410 const QDBusVariant &value)
411{
412 if (group == "org.gnome.desktop.wm.preferences"_L1 && key == "button-layout"_L1) {
413 const QString layout = value.variant().toString();
414 updateTitlebarLayout(layout);
415 } else if (group == "org.freedesktop.appearance"_L1 && key == "color-scheme"_L1) {
416 const uint colorScheme = value.variant().toUInt();
417 updateColors(colorScheme == 1); // 1 == Prefer Dark
418 }
419}
420
421QRectF QWaylandAdwaitaDecoration::buttonRect(Button button) const
422{
423 int xPos;
424 int yPos;
425 const int btnPos = m_buttons.value(button);
426
427 const QRect surfaceRect = waylandWindow()->windowContentGeometry() + margins(QWaylandAbstractDecoration::ShadowsOnly);
428 if (m_placement == Right) {
429 xPos = surfaceRect.width();
430 xPos -= ceButtonWidth * btnPos;
431 xPos -= ceButtonSpacing * btnPos;
432 xPos -= margins(ShadowsOnly).right();
433 } else {
434 xPos = 0;
435 xPos += ceButtonWidth * btnPos;
436 xPos += ceButtonSpacing * btnPos;
437 xPos += margins(ShadowsOnly).left();
438 // We are painting from the left to the right so the real
439 // position doesn't need to by moved by the size of the button.
440 xPos -= ceButtonWidth;
441 }
442
443 yPos = margins().top();
444 yPos += margins().bottom();
445 yPos -= ceButtonWidth;
446 yPos /= 2;
447
448 return QRectF(xPos, yPos, ceButtonWidth, ceButtonWidth);
449}
450
460
461static void renderButtonIcon(const QString &svgIcon, QPainter *painter, const QRect &rect, const QColor &color)
462{
463 painter->save();
465
466 QString icon = svgIcon;
467 QRegularExpression regexp("fill=[\"']#[0-9A-F]{6}[\"']", QRegularExpression::CaseInsensitiveOption);
469 QRegularExpression regexpCurrentColor("fill=[\"']currentColor[\"']");
470 icon.replace(regexp, QString("fill=\"%1\"").arg(color.name()));
471 icon.replace(regexpAlt, QString("fill:%1").arg(color.name()));
472 icon.replace(regexpCurrentColor, QString("fill=\"%1\"").arg(color.name()));
473 QSvgRenderer svgRenderer(icon.toLocal8Bit());
474 svgRenderer.render(painter, rect);
475
476 painter->restore();
477}
478
488
490{
491 if (button == QWaylandAdwaitaDecoration::Close)
492 return QWaylandAdwaitaDecoration::CloseIcon;
493 else if (button == QWaylandAdwaitaDecoration::Minimize)
494 return QWaylandAdwaitaDecoration::MinimizeIcon;
495 else if (button == QWaylandAdwaitaDecoration::Maximize && maximized)
496 return QWaylandAdwaitaDecoration::RestoreIcon;
497 else
498 return QWaylandAdwaitaDecoration::MaximizeIcon;
499}
500
501void QWaylandAdwaitaDecoration::drawButton(Button button, QPainter *painter)
502{
503 const Qt::WindowStates windowStates = waylandWindow()->windowStates();
504 const bool maximized = windowStates & Qt::WindowMaximized;
505
506 const QRect btnRect = buttonRect(button).toRect();
507 renderFlatRoundedButtonFrame(painter, btnRect, color(ButtonBackground, button));
508
509 QRect adjustedBtnRect = btnRect;
510 adjustedBtnRect.setSize(QSize(16, 16));
511 adjustedBtnRect.translate(4, 4);
512 const QString svgIcon = m_icons[iconFromButtonAndState(button, maximized)];
513 if (!svgIcon.isEmpty())
514 renderButtonIcon(svgIcon, painter, adjustedBtnRect, color(Foreground));
515 else // Fallback to use QIcon
516 renderButtonIcon(iconFromButtonAndState(button, maximized), painter, adjustedBtnRect);
517}
518
519QColor QWaylandAdwaitaDecoration::color(ColorType type, Button button)
520{
521 const bool active = waylandWindow()->windowStates() & Qt::WindowActive;
522
523 switch (type) {
524 case Background:
525 case BackgroundInactive:
526 return active ? m_colors[Background] : m_colors[BackgroundInactive];
527 case Foreground:
528 case ForegroundInactive:
529 return active ? m_colors[Foreground] : m_colors[ForegroundInactive];
530 case Border:
531 case BorderInactive:
532 return active ? m_colors[Border] : m_colors[BorderInactive];
533 case ButtonBackground:
534 case ButtonBackgroundInactive:
535 case HoveredButtonBackground: {
536 if (m_clicking == button) {
537 return m_colors[PressedButtonBackground];
538 } else if (m_hoveredButtons.testFlag(button)) {
539 return m_colors[HoveredButtonBackground];
540 }
541 return active ? m_colors[ButtonBackground] : m_colors[ButtonBackgroundInactive];
542 }
543 default:
544 return m_colors[Background];
545 }
546}
547
548bool QWaylandAdwaitaDecoration::clickButton(Qt::MouseButtons b, Button btn)
549{
550 auto repaint = qScopeGuard([this] { requestRepaint(); });
551
552 if (isLeftClicked(b)) {
553 m_clicking = btn;
554 return false;
555 } else if (isLeftReleased(b)) {
556 if (m_clicking == btn) {
557 m_clicking = None;
558 return true;
559 } else {
560 m_clicking = None;
561 }
562 }
563 return false;
564}
565
566bool QWaylandAdwaitaDecoration::doubleClickButton(Qt::MouseButtons b, const QPointF &local,
567 const QDateTime &currentTime)
568{
569 if (isLeftClicked(b)) {
570 const qint64 clickInterval = m_lastButtonClick.msecsTo(currentTime);
571 m_lastButtonClick = currentTime;
572 const int doubleClickDistance = 5;
573 const QPointF posDiff = m_lastButtonClickPosition - local;
574 if ((clickInterval <= 500)
575 && ((posDiff.x() <= doubleClickDistance && posDiff.x() >= -doubleClickDistance)
576 && ((posDiff.y() <= doubleClickDistance && posDiff.y() >= -doubleClickDistance)))) {
577 return true;
578 }
579
580 m_lastButtonClickPosition = local;
581 }
582
583 return false;
584}
585
586void QWaylandAdwaitaDecoration::updateButtonHoverState(Button hoveredButton)
587{
588 bool currentCloseButtonState = m_hoveredButtons.testFlag(Close);
589 bool currentMaximizeButtonState = m_hoveredButtons.testFlag(Maximize);
590 bool currentMinimizeButtonState = m_hoveredButtons.testFlag(Minimize);
591
592 m_hoveredButtons.setFlag(Close, hoveredButton == Button::Close);
593 m_hoveredButtons.setFlag(Maximize, hoveredButton == Button::Maximize);
594 m_hoveredButtons.setFlag(Minimize, hoveredButton == Button::Minimize);
595
596 if (m_hoveredButtons.testFlag(Close) != currentCloseButtonState
597 || m_hoveredButtons.testFlag(Maximize) != currentMaximizeButtonState
598 || m_hoveredButtons.testFlag(Minimize) != currentMinimizeButtonState) {
599 requestRepaint();
600 }
601}
602
603void QWaylandAdwaitaDecoration::processMouseTop(QWaylandInputDevice *inputDevice, const QPointF &local,
604 Qt::MouseButtons b, Qt::KeyboardModifiers mods)
605{
606 Q_UNUSED(mods)
607
608 QDateTime currentDateTime = QDateTime::currentDateTime();
609 QRect surfaceRect = waylandWindow()->windowContentGeometry() + margins(ShadowsOnly);
610
611 if (!buttonRect(Close).contains(local) && !buttonRect(Maximize).contains(local)
612 && !buttonRect(Minimize).contains(local))
613 updateButtonHoverState(Button::None);
614
615 if (local.y() <= surfaceRect.top() + margins().bottom()) {
616 if (local.x() <= margins().left()) {
617 // top left bit
618#if QT_CONFIG(cursor)
619 waylandWindow()->setMouseCursor(inputDevice, Qt::SizeFDiagCursor);
620#endif
621 startResize(inputDevice, Qt::TopEdge | Qt::LeftEdge, b);
622 } else if (local.x() > surfaceRect.right() - margins().left()) {
623 // top right bit
624#if QT_CONFIG(cursor)
625 waylandWindow()->setMouseCursor(inputDevice, Qt::SizeBDiagCursor);
626#endif
627 startResize(inputDevice, Qt::TopEdge | Qt::RightEdge, b);
628 } else {
629 // top resize bit
630#if QT_CONFIG(cursor)
631 waylandWindow()->setMouseCursor(inputDevice, Qt::SizeVerCursor);
632#endif
633 startResize(inputDevice, Qt::TopEdge, b);
634 }
635 } else if (local.x() <= surfaceRect.left() + margins().left()) {
636 processMouseLeft(inputDevice, local, b, mods);
637 } else if (local.x() > surfaceRect.right() - margins().right()) {
638 processMouseRight(inputDevice, local, b, mods);
639 } else if (buttonRect(Close).contains(local)) {
640 if (clickButton(b, Close)) {
642 m_hoveredButtons.setFlag(Close, false);
643 }
644 updateButtonHoverState(Close);
645 } else if (m_buttons.contains(Maximize) && buttonRect(Maximize).contains(local)) {
646 updateButtonHoverState(Maximize);
647 if (clickButton(b, Maximize)) {
648 window()->setWindowStates(window()->windowStates() ^ Qt::WindowMaximized);
649 m_hoveredButtons.setFlag(Maximize, false);
650 }
651 } else if (m_buttons.contains(Minimize) && buttonRect(Minimize).contains(local)) {
652 updateButtonHoverState(Minimize);
653 if (clickButton(b, Minimize)) {
654 window()->setWindowState(Qt::WindowMinimized);
655 m_hoveredButtons.setFlag(Minimize, false);
656 }
657 } else if (doubleClickButton(b, local, currentDateTime)) {
658 window()->setWindowStates(window()->windowStates() ^ Qt::WindowMaximized);
659 } else {
660 // Show window menu
662 waylandWindow()->shellSurface()->showWindowMenu(inputDevice);
663#if QT_CONFIG(cursor)
664 waylandWindow()->restoreMouseCursor(inputDevice);
665#endif
666 startMove(inputDevice, b);
667 }
668}
669
670void QWaylandAdwaitaDecoration::processMouseBottom(QWaylandInputDevice *inputDevice, const QPointF &local,
671 Qt::MouseButtons b, Qt::KeyboardModifiers mods)
672{
673 Q_UNUSED(mods)
674 if (local.x() <= margins().left()) {
675 // bottom left bit
676#if QT_CONFIG(cursor)
677 waylandWindow()->setMouseCursor(inputDevice, Qt::SizeBDiagCursor);
678#endif
679 startResize(inputDevice, Qt::BottomEdge | Qt::LeftEdge, b);
680 } else if (local.x() > window()->width() + margins().right()) {
681 // bottom right bit
682#if QT_CONFIG(cursor)
683 waylandWindow()->setMouseCursor(inputDevice, Qt::SizeFDiagCursor);
684#endif
685 startResize(inputDevice, Qt::BottomEdge | Qt::RightEdge, b);
686 } else {
687 // bottom bit
688#if QT_CONFIG(cursor)
689 waylandWindow()->setMouseCursor(inputDevice, Qt::SizeVerCursor);
690#endif
691 startResize(inputDevice, Qt::BottomEdge, b);
692 }
693}
694
695void QWaylandAdwaitaDecoration::processMouseLeft(QWaylandInputDevice *inputDevice, const QPointF &local,
696 Qt::MouseButtons b, Qt::KeyboardModifiers mods)
697{
698 Q_UNUSED(local)
699 Q_UNUSED(mods)
700#if QT_CONFIG(cursor)
701 waylandWindow()->setMouseCursor(inputDevice, Qt::SizeHorCursor);
702#endif
703 startResize(inputDevice, Qt::LeftEdge, b);
704}
705
706void QWaylandAdwaitaDecoration::processMouseRight(QWaylandInputDevice *inputDevice, const QPointF &local,
707 Qt::MouseButtons b, Qt::KeyboardModifiers mods)
708{
709 Q_UNUSED(local)
710 Q_UNUSED(mods)
711#if QT_CONFIG(cursor)
712 waylandWindow()->setMouseCursor(inputDevice, Qt::SizeHorCursor);
713#endif
714 startResize(inputDevice, Qt::RightEdge, b);
715}
716
717void QWaylandAdwaitaDecoration::requestRepaint() const
718{
719 // Set dirty flag
720 if (waylandWindow()->decoration())
721 waylandWindow()->decoration()->update();
722
723 // Request re-paint
724 waylandWindow()->window()->requestUpdate();
725}
726
727} // namespace QtWaylandClient
728
730
731#include "moc_qwaylandadwaitadecoration_p.cpp"
IOBluetoothDevice * device
The QColor class provides colors based on RGB, HSV or CMYK values.
Definition qcolor.h:31
\inmodule QtDBus
void beginMapEntry()
Opens a D-Bus map entry suitable for appending the key and value entries.
void endMapEntry()
Closes a D-Bus map entry opened with beginMapEntry().
void endMap()
Closes a D-Bus map opened with beginMap().
bool atEnd() const
Returns true if there are no more elements to be extracted from this QDBusArgument.
void beginMap(int keyMetaTypeId, int valueMetaTypeId)
\inmodule QtDBus
static QDBusConnection sessionBus()
Returns a QDBusConnection object opened with the session bus.
\inmodule QtDBus
static QDBusMessage createMethodCall(const QString &destination, const QString &path, const QString &interface, const QString &method)
Constructs a new DBus message representing a method call.
void finished(QDBusPendingCallWatcher *self=nullptr)
This signal is emitted when the pending call has finished and its reply is available.
\inmodule QtDBus
\inmodule QtDBus
\inmodule QtCore\reentrant
Definition qdatetime.h:283
static QDateTime currentDateTime()
This is an overloaded member function, provided for convenience. It differs from the above function o...
The QDirIterator class provides an iterator for directory entrylists.
@ Files
Definition qdir.h:23
State
Specifies the state of this event point.
Definition qeventpoint.h:48
QString fileName() const
QString filePath() const
Returns the path of the file system entry this QFileInfo refers to; the path may be absolute or relat...
\inmodule QtCore
Definition qfile.h:93
\reentrant
Definition qfont.h:22
static QPlatformTheme * platformTheme()
static QString themeName()
Definition qicon.cpp:1274
static QStringList themeSearchPaths()
Definition qicon.cpp:1193
static QString fallbackThemeName()
Definition qicon.cpp:1293
static QIcon fromTheme(const QString &name)
Definition qicon.cpp:1344
\inmodule QtCore\compares equality \compareswith equality QLine \endcompareswith
Definition qline.h:192
iterator insert(const Key &key, const T &value)
Definition qmap.h:688
bool contains(const Key &key) const
Definition qmap.h:341
void clear()
Definition qmap.h:289
\inmodule QtCore
Definition qmargins.h:24
constexpr int left() const noexcept
Returns the left margin.
Definition qmargins.h:106
constexpr int right() const noexcept
Returns the right margin.
Definition qmargins.h:112
static QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *member, Qt::ConnectionType=Qt::AutoConnection)
\threadsafe
Definition qobject.cpp:2960
\inmodule QtGui
The QPainter class performs low-level painting on widgets and other paint devices.
Definition qpainter.h:46
void setPen(const QColor &color)
This is an overloaded member function, provided for convenience. It differs from the above function o...
void restore()
Restores the current painter state (pops a saved state off the stack).
void save()
Saves the current painter state (pushes the state onto a stack).
void drawPixmap(const QRectF &targetRect, const QPixmap &pixmap, const QRectF &sourceRect)
Draws the rectangular portion source of the given pixmap into the given target in the paint device.
void drawEllipse(const QRectF &r)
Draws the ellipse defined by the given rectangle.
void setBrush(const QBrush &brush)
Sets the painter's brush to the given brush.
@ Antialiasing
Definition qpainter.h:52
void setRenderHints(RenderHints hints, bool on=true)
void setRenderHint(RenderHint hint, bool on=true)
Sets the given render hint on the painter if on is true; otherwise clears the render hint.
The QPlatformTheme class allows customizing the UI based on themes.
virtual const QFont * font(Font type=SystemFont) const
\inmodule QtCore\reentrant
Definition qpoint.h:217
constexpr qreal x() const noexcept
Returns the x coordinate of this point.
Definition qpoint.h:343
constexpr qreal y() const noexcept
Returns the y coordinate of this point.
Definition qpoint.h:348
\inmodule QtCore\reentrant
Definition qpoint.h:25
\inmodule QtCore\reentrant
Definition qrect.h:484
\inmodule QtCore\reentrant
Definition qrect.h:30
constexpr void setRight(int pos) noexcept
Sets the right edge of the rectangle to the given x coordinate.
Definition qrect.h:197
constexpr void setSize(const QSize &s) noexcept
Sets the size of the rectangle to the given size.
Definition qrect.h:387
constexpr void setLeft(int pos) noexcept
Sets the left edge of the rectangle to the given x coordinate.
Definition qrect.h:191
\inmodule QtCore \reentrant
constexpr QSize toSize() const noexcept
Returns an integer based copy of this size.
Definition qsize.h:401
\inmodule QtCore
Definition qsize.h:25
void setText(const QString &text)
Sets the text of the QStaticText to text.
void prepare(const QTransform &matrix=QTransform(), const QFont &font=QFont())
Prepares the QStaticText object for being painted with the given matrix and the given font to avoid o...
QSizeF size() const
Returns the size of the bounding rect for this QStaticText.
QString text() const
Returns the text of the QStaticText.
\inmodule QtCore
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:129
QStringList split(const QString &sep, Qt::SplitBehavior behavior=Qt::KeepEmptyParts, Qt::CaseSensitivity cs=Qt::CaseSensitive) const
Splits the string into substrings wherever sep occurs, and returns the list of those strings.
Definition qstring.cpp:8218
bool isEmpty() const noexcept
Returns true if the string has no characters; otherwise returns false.
Definition qstring.h:192
QString arg(qlonglong a, int fieldwidth=0, int base=10, QChar fillChar=u' ') const
Definition qstring.cpp:8870
const QChar at(qsizetype i) const
Returns the character at the given index position in the string.
Definition qstring.h:1226
\inmodule QtSvg
\reentrant
Definition qtextoption.h:18
bool singleShot
whether the timer is a single-shot timer
Definition qtimer.h:22
static bool handleCloseEvent(QWindow *window)
bool isLeftReleased(Qt::MouseButtons newMouseButtonState)
bool handleMouse(QWaylandInputDevice *inputDevice, const QPointF &local, const QPointF &global, Qt::MouseButtons b, Qt::KeyboardModifiers mods) override
bool handleTouch(QWaylandInputDevice *inputDevice, const QPointF &local, const QPointF &global, QEventPoint::State state, Qt::KeyboardModifiers mods) override
QMargins margins(MarginsType marginsType=Full) const override
virtual bool move(QWaylandInputDevice *)
QWaylandShellSurface * shellSurface() const
ToplevelWindowTilingStates toplevelWindowTilingStates() const
QRect windowContentGeometry() const
Window geometry as defined by the xdg-shell spec (in wl_surface coordinates) topLeft is where the sha...
QMap< QString, QString > map
[6]
QPushButton * button
[2]
rect
[4]
else opt state
[0]
const QStyleOptionButton * btn
[3]
@ Background
bool doubleClickButton(QQuickAbstractButton *button)
bool clickButton(QQuickAbstractButton *button)
Combined button and popup list for selecting options.
static QWaylandAdwaitaDecoration::ButtonIcon iconFromButtonAndState(QWaylandAdwaitaDecoration::Button button, bool maximized)
static constexpr int ceShadowsWidth
static constexpr int ceButtonWidth
const QDBusArgument & operator>>(const QDBusArgument &argument, QMap< QString, QVariantMap > &map)
static QMap< QWaylandAdwaitaDecoration::ButtonIcon, QString > buttonMap
static void renderButtonIcon(const QString &svgIcon, QPainter *painter, const QRect &rect, const QColor &color)
QString getIconSvg(const QString &iconName)
static constexpr int ceButtonSpacing
static constexpr qreal ceTitlebarSeperatorWidth
static constexpr int ceWindowBorderWidth
static constexpr int ceCornerRadius
static void renderFlatRoundedButtonFrame(QPainter *painter, const QRect &rect, const QColor &color)
static constexpr int ceTitlebarHeight
@ WindowMinimized
Definition qnamespace.h:253
@ WindowMaximized
Definition qnamespace.h:254
@ WindowActive
Definition qnamespace.h:256
@ AlignVCenter
Definition qnamespace.h:155
@ AlignHCenter
Definition qnamespace.h:148
@ RightButton
Definition qnamespace.h:59
@ PlainText
@ SizeHorCursor
@ SizeVerCursor
@ SizeFDiagCursor
@ SizeBDiagCursor
@ NoPen
@ RightEdge
@ TopEdge
@ BottomEdge
@ LeftEdge
DBusConnection * connection
EGLOutputLayerEXT EGLint EGLAttrib value
[5]
@ None
Definition qhash.cpp:531
@ QtWarningMsg
Definition qlogging.h:31
#define Q_LOGGING_CATEGORY(name,...)
#define qCWarning(category,...)
#define qCDebug(category,...)
Button
Qt::MouseButtons m_buttons
Definition qnsview.mm:102
#define SLOT(a)
Definition qobjectdefs.h:52
static QString themeName()
static bool contains(const QJsonArray &haystack, unsigned needle)
Definition qopengl.cpp:116
GLboolean GLboolean GLboolean b
GLuint64 key
GLenum GLuint GLintptr GLsizeiptr size
[1]
GLdouble GLdouble GLdouble GLdouble top
GLdouble GLdouble right
GLint GLsizei width
GLuint color
[2]
GLint left
GLenum type
GLboolean GLuint group
GLint GLint bottom
GLuint GLsizei const GLchar * message
GLsizei const GLchar *const * path
GLfloat GLfloat p
[1]
GLuint GLenum option
@ Left
@ Right
QScopeGuard< typename std::decay< F >::type > qScopeGuard(F &&f)
[qScopeGuard]
Definition qscopeguard.h:60
SSL_CTX int void * arg
#define QStringLiteral(str)
#define Q_UNUSED(x)
unsigned int uint
Definition qtypes.h:34
long long qint64
Definition qtypes.h:60
double qreal
Definition qtypes.h:187
static double currentTime()
QT_BEGIN_NAMESPACE constexpr const wchar_t * themeNames[]
QFutureWatcher< int > watcher
QVBoxLayout * layout
widget render & pixmap
QPainter painter(this)
[7]
aWidget window() -> setWindowTitle("New Window Title")
[2]
QNetworkReply * reply
QDBusArgument argument
QJSValue global
QJSValue fullName
\inmodule QtCore \reentrant
Definition qchar.h:18