summaryrefslogtreecommitdiffstats
path: root/src/plugins/decorations
diff options
context:
space:
mode:
Diffstat (limited to 'src/plugins/decorations')
-rw-r--r--src/plugins/decorations/CMakeLists.txt9
-rw-r--r--src/plugins/decorations/adwaita/CMakeLists.txt25
-rw-r--r--src/plugins/decorations/adwaita/adwaita.json3
-rw-r--r--src/plugins/decorations/adwaita/main.cpp36
-rw-r--r--src/plugins/decorations/adwaita/qwaylandadwaitadecoration.cpp731
-rw-r--r--src/plugins/decorations/adwaita/qwaylandadwaitadecoration_p.h155
-rw-r--r--src/plugins/decorations/bradient/CMakeLists.txt23
-rw-r--r--src/plugins/decorations/bradient/bradient.pro12
-rw-r--r--src/plugins/decorations/bradient/main.cpp249
-rw-r--r--src/plugins/decorations/decorations.pro3
10 files changed, 1111 insertions, 135 deletions
diff --git a/src/plugins/decorations/CMakeLists.txt b/src/plugins/decorations/CMakeLists.txt
new file mode 100644
index 000000000..abe3c375b
--- /dev/null
+++ b/src/plugins/decorations/CMakeLists.txt
@@ -0,0 +1,9 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+# Generated from decorations.pro.
+if (QT_FEATURE_wayland_decoration_adwaita)
+ add_subdirectory(adwaita)
+endif()
+
+add_subdirectory(bradient)
diff --git a/src/plugins/decorations/adwaita/CMakeLists.txt b/src/plugins/decorations/adwaita/CMakeLists.txt
new file mode 100644
index 000000000..b318c2b8b
--- /dev/null
+++ b/src/plugins/decorations/adwaita/CMakeLists.txt
@@ -0,0 +1,25 @@
+# Copyright (C) 2023 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+#####################################################################
+## QWaylandAdwaitaDecorationPlugin Plugin:
+#####################################################################
+
+qt_internal_add_plugin(QWaylandAdwaitaDecorationPlugin
+ OUTPUT_NAME adwaita
+ PLUGIN_TYPE wayland-decoration-client
+ SOURCES
+ main.cpp
+ qwaylandadwaitadecoration.cpp
+ LIBRARIES
+ Qt::Core
+ Qt::DBus
+ Qt::Gui
+ Qt::Svg
+ Qt::WaylandClientPrivate
+ Wayland::Client
+)
+
+#### Keys ignored in scope 1:.:.:bradient.pro:<TRUE>:
+# OTHER_FILES = "bradient.json"
+
diff --git a/src/plugins/decorations/adwaita/adwaita.json b/src/plugins/decorations/adwaita/adwaita.json
new file mode 100644
index 000000000..69ec79e9b
--- /dev/null
+++ b/src/plugins/decorations/adwaita/adwaita.json
@@ -0,0 +1,3 @@
+{
+ "Keys": [ "adwaita", "gnome" ]
+}
diff --git a/src/plugins/decorations/adwaita/main.cpp b/src/plugins/decorations/adwaita/main.cpp
new file mode 100644
index 000000000..e5b1be830
--- /dev/null
+++ b/src/plugins/decorations/adwaita/main.cpp
@@ -0,0 +1,36 @@
+// Copyright (C) 2023 Jan Grulich <jgrulich@redhat.com>
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include <QtWaylandClient/private/qwaylanddecorationplugin_p.h>
+
+#include "qwaylandadwaitadecoration_p.h"
+
+QT_BEGIN_NAMESPACE
+
+using namespace Qt::StringLiterals;
+
+namespace QtWaylandClient {
+
+class QWaylandAdwaitaDecorationPlugin : public QWaylandDecorationPlugin
+{
+ Q_OBJECT
+ Q_PLUGIN_METADATA(IID QWaylandDecorationFactoryInterface_iid FILE "adwaita.json")
+public:
+ QWaylandAbstractDecoration *create(const QString &key, const QStringList &params) override;
+};
+
+QWaylandAbstractDecoration *QWaylandAdwaitaDecorationPlugin::create(const QString &key, const QStringList &params)
+{
+ Q_UNUSED(params);
+ if (!key.compare("adwaita"_L1, Qt::CaseInsensitive) ||
+ !key.compare("gnome"_L1, Qt::CaseInsensitive))
+ return new QWaylandAdwaitaDecoration();
+ return nullptr;
+}
+
+}
+
+QT_END_NAMESPACE
+
+#include "main.moc"
diff --git a/src/plugins/decorations/adwaita/qwaylandadwaitadecoration.cpp b/src/plugins/decorations/adwaita/qwaylandadwaitadecoration.cpp
new file mode 100644
index 000000000..2d3575bce
--- /dev/null
+++ b/src/plugins/decorations/adwaita/qwaylandadwaitadecoration.cpp
@@ -0,0 +1,731 @@
+// Copyright (C) 2023 Jan Grulich <jgrulich@redhat.com>
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qwaylandadwaitadecoration_p.h"
+
+// QtCore
+#include <QtCore/QLoggingCategory>
+#include <QScopeGuard>
+
+// QtDBus
+#include <QtDBus/QDBusArgument>
+#include <QtDBus/QDBusConnection>
+#include <QtDBus/QDBusMessage>
+#include <QtDBus/QDBusPendingCall>
+#include <QtDBus/QDBusPendingCallWatcher>
+#include <QtDBus/QDBusPendingReply>
+#include <QtDBus/QDBusVariant>
+#include <QtDBus/QtDBus>
+
+// QtGui
+#include <QtGui/QColor>
+#include <QtGui/QPainter>
+#include <QtGui/QPainterPath>
+
+#include <QtGui/private/qguiapplication_p.h>
+#include <QtGui/qpa/qplatformtheme.h>
+
+// QtSvg
+#include <QtSvg/QSvgRenderer>
+
+// QtWayland
+#include <QtWaylandClient/private/qwaylandshmbackingstore_p.h>
+#include <QtWaylandClient/private/qwaylandwindow_p.h>
+
+
+QT_BEGIN_NAMESPACE
+
+using namespace Qt::StringLiterals;
+
+namespace QtWaylandClient {
+
+static constexpr int ceButtonSpacing = 12;
+static constexpr int ceButtonWidth = 24;
+static constexpr int ceCornerRadius = 12;
+static constexpr int ceShadowsWidth = 10;
+static constexpr int ceTitlebarHeight = 38;
+static constexpr int ceWindowBorderWidth = 1;
+static constexpr qreal ceTitlebarSeperatorWidth = 0.5;
+
+static QMap<QWaylandAdwaitaDecoration::ButtonIcon, QString> buttonMap = {
+ { QWaylandAdwaitaDecoration::CloseIcon, "window-close-symbolic"_L1 },
+ { QWaylandAdwaitaDecoration::MinimizeIcon, "window-minimize-symbolic"_L1 },
+ { QWaylandAdwaitaDecoration::MaximizeIcon, "window-maximize-symbolic"_L1 },
+ { QWaylandAdwaitaDecoration::RestoreIcon, "window-restore-symbolic"_L1 }
+};
+
+const QDBusArgument &operator>>(const QDBusArgument &argument, QMap<QString, QVariantMap> &map)
+{
+ argument.beginMap();
+ map.clear();
+
+ while (!argument.atEnd()) {
+ QString key;
+ QVariantMap value;
+ argument.beginMapEntry();
+ argument >> key >> value;
+ argument.endMapEntry();
+ map.insert(key, value);
+ }
+
+ argument.endMap();
+ return argument;
+}
+
+Q_LOGGING_CATEGORY(lcQWaylandAdwaitaDecorationLog, "qt.qpa.qwaylandadwaitadecoration", QtWarningMsg)
+
+QWaylandAdwaitaDecoration::QWaylandAdwaitaDecoration()
+ : QWaylandAbstractDecoration()
+{
+ m_lastButtonClick = QDateTime::currentDateTime();
+
+ QTextOption option(Qt::AlignHCenter | Qt::AlignVCenter);
+ option.setWrapMode(QTextOption::NoWrap);
+ m_windowTitle.setTextOption(option);
+ m_windowTitle.setTextFormat(Qt::PlainText);
+
+ const QPlatformTheme *theme = QGuiApplicationPrivate::platformTheme();
+ if (const QFont *font = theme->font(QPlatformTheme::TitleBarFont))
+ m_font = std::make_unique<QFont>(*font);
+ if (!m_font) // Fallback to GNOME's default font
+ m_font = std::make_unique<QFont>("Cantarell"_L1, 10);
+
+ QTimer::singleShot(0, this, &QWaylandAdwaitaDecoration::loadConfiguration);
+}
+
+QMargins QWaylandAdwaitaDecoration::margins(QWaylandAbstractDecoration::MarginsType marginsType) const
+{
+ const bool onlyShadows = marginsType == QWaylandAbstractDecoration::ShadowsOnly;
+ const bool shadowsExcluded = marginsType == ShadowsExcluded;
+
+ if (waylandWindow()->windowStates() & Qt::WindowMaximized) {
+ // Maximized windows don't have anything around, no shadows, border,
+ // etc. Only report titlebar height in case we are not asking for shadow
+ // margins.
+ return QMargins(0, onlyShadows ? 0 : ceTitlebarHeight, 0, 0);
+ }
+
+ const QWaylandWindow::ToplevelWindowTilingStates tilingStates = waylandWindow()->toplevelWindowTilingStates();
+
+ // Since all sides (left, right, bottom) are going to be same
+ const int marginsBase = shadowsExcluded ? ceWindowBorderWidth : ceShadowsWidth + ceWindowBorderWidth;
+ const int sideMargins = onlyShadows ? ceShadowsWidth : marginsBase;
+ const int topMargins = onlyShadows ? ceShadowsWidth : ceTitlebarHeight + marginsBase;
+
+ return QMargins(tilingStates & QWaylandWindow::WindowTiledLeft ? 0 : sideMargins,
+ tilingStates & QWaylandWindow::WindowTiledTop ? onlyShadows ? 0 : ceTitlebarHeight : topMargins,
+ tilingStates & QWaylandWindow::WindowTiledRight ? 0 : sideMargins,
+ tilingStates & QWaylandWindow::WindowTiledBottom ? 0 : sideMargins);
+}
+
+void QWaylandAdwaitaDecoration::paint(QPaintDevice *device)
+{
+ const QRect surfaceRect = waylandWindow()->windowContentGeometry() + margins(ShadowsOnly);
+
+ QPainter p(device);
+ p.setRenderHint(QPainter::Antialiasing);
+
+ /*
+ * Titlebar and window border
+ */
+ const int titleBarWidth = surfaceRect.width() - margins().left() - margins().right();
+ QPainterPath path;
+
+ // Maximized or tiled won't have rounded corners
+ if (waylandWindow()->windowStates() & Qt::WindowMaximized
+ || waylandWindow()->toplevelWindowTilingStates() != QWaylandWindow::WindowNoState)
+ path.addRect(margins().left(), margins().bottom(), titleBarWidth, margins().top());
+ else
+ path.addRoundedRect(margins().left(), margins().bottom(), titleBarWidth,
+ margins().top() + ceCornerRadius, ceCornerRadius, ceCornerRadius);
+
+ p.save();
+ p.setPen(color(Border));
+ p.fillPath(path.simplified(), color(Background));
+ p.drawPath(path);
+ p.drawRect(margins().left(), margins().top(), titleBarWidth, surfaceRect.height() - margins().top() - margins().bottom());
+ p.restore();
+
+
+ /*
+ * Titlebar separator
+ */
+ p.save();
+ p.setPen(color(Border));
+ p.drawLine(QLineF(margins().left(), margins().top() - ceTitlebarSeperatorWidth,
+ surfaceRect.width() - margins().right(),
+ margins().top() - ceTitlebarSeperatorWidth));
+ p.restore();
+
+
+ /*
+ * Window title
+ */
+ const QRect top = QRect(margins().left(), margins().bottom(), surfaceRect.width(),
+ margins().top() - margins().bottom());
+ const QString windowTitleText = waylandWindow()->windowTitle();
+ if (!windowTitleText.isEmpty()) {
+ if (m_windowTitle.text() != windowTitleText) {
+ m_windowTitle.setText(windowTitleText);
+ m_windowTitle.prepare();
+ }
+
+ QRect titleBar = top;
+ if (m_placement == Right) {
+ titleBar.setLeft(margins().left());
+ titleBar.setRight(static_cast<int>(buttonRect(Minimize).left()) - 8);
+ } else {
+ titleBar.setLeft(static_cast<int>(buttonRect(Minimize).right()) + 8);
+ titleBar.setRight(surfaceRect.width() - margins().right());
+ }
+
+ p.save();
+ p.setClipRect(titleBar);
+ p.setPen(color(Foreground));
+ QSize size = m_windowTitle.size().toSize();
+ int dx = (top.width() - size.width()) / 2;
+ int dy = (top.height() - size.height()) / 2;
+ p.setFont(*m_font);
+ QPoint windowTitlePoint(top.topLeft().x() + dx, top.topLeft().y() + dy);
+ p.drawStaticText(windowTitlePoint, m_windowTitle);
+ p.restore();
+ }
+
+
+ /*
+ * Buttons
+ */
+ if (m_buttons.contains(Close))
+ drawButton(Close, &p);
+
+ if (m_buttons.contains(Maximize))
+ drawButton(Maximize, &p);
+
+ if (m_buttons.contains(Minimize))
+ drawButton(Minimize, &p);
+}
+
+bool QWaylandAdwaitaDecoration::handleMouse(QWaylandInputDevice *inputDevice, const QPointF &local,
+ const QPointF &global, Qt::MouseButtons b,
+ Qt::KeyboardModifiers mods)
+{
+ Q_UNUSED(global)
+
+ if (local.y() > margins().top())
+ updateButtonHoverState(Button::None);
+
+ // Figure out what area mouse is in
+ QRect surfaceRect = waylandWindow()->windowContentGeometry() + margins(ShadowsOnly);
+ if (local.y() <= surfaceRect.top() + margins().top())
+ processMouseTop(inputDevice, local, b, mods);
+ else if (local.y() > surfaceRect.bottom() - margins().bottom())
+ processMouseBottom(inputDevice, local, b, mods);
+ else if (local.x() <= surfaceRect.left() + margins().left())
+ processMouseLeft(inputDevice, local, b, mods);
+ else if (local.x() > surfaceRect.right() - margins().right())
+ processMouseRight(inputDevice, local, b, mods);
+ else {
+#if QT_CONFIG(cursor)
+ waylandWindow()->restoreMouseCursor(inputDevice);
+#endif
+ }
+
+ // Reset clicking state in case a button press is released outside
+ // the button area
+ if (isLeftReleased(b)) {
+ m_clicking = None;
+ requestRepaint();
+ }
+
+ setMouseButtons(b);
+ return false;
+}
+
+bool QWaylandAdwaitaDecoration::handleTouch(QWaylandInputDevice *inputDevice, const QPointF &local,
+ const QPointF &global, QEventPoint::State state,
+ Qt::KeyboardModifiers mods)
+{
+ Q_UNUSED(inputDevice)
+ Q_UNUSED(global)
+ Q_UNUSED(mods)
+
+ bool handled = state == QEventPoint::Pressed;
+
+ if (handled) {
+ if (buttonRect(Close).contains(local))
+ QWindowSystemInterface::handleCloseEvent(window());
+ else if (m_buttons.contains(Maximize) && buttonRect(Maximize).contains(local))
+ window()->setWindowStates(window()->windowStates() ^ Qt::WindowMaximized);
+ else if (m_buttons.contains(Minimize) && buttonRect(Minimize).contains(local))
+ window()->setWindowState(Qt::WindowMinimized);
+ else if (local.y() <= margins().top())
+ waylandWindow()->shellSurface()->move(inputDevice);
+ else
+ handled = false;
+ }
+
+ return handled;
+}
+
+QString getIconSvg(const QString &iconName)
+{
+ const QStringList themeNames = { QIcon::themeName(), QIcon::fallbackThemeName(), "Adwaita"_L1 };
+
+ qCDebug(lcQWaylandAdwaitaDecorationLog) << "Searched icon themes: " << themeNames;
+
+ for (const QString &themeName : themeNames) {
+ if (themeName.isEmpty())
+ continue;
+
+ for (const QString &path : QIcon::themeSearchPaths()) {
+ if (path.startsWith(QLatin1Char(':')))
+ continue;
+
+ const QString fullPath = QString("%1/%2").arg(path).arg(themeName);
+ QDirIterator dirIt(fullPath, {"*.svg"}, QDir::Files, QDirIterator::Subdirectories);
+ while (dirIt.hasNext()) {
+ const QString fileName = dirIt.next();
+ const QFileInfo fileInfo(fileName);
+
+ if (fileInfo.fileName() == iconName) {
+ qCDebug(lcQWaylandAdwaitaDecorationLog) << "Using " << iconName << " from " << themeName << " theme";
+ QFile readFile(fileInfo.filePath());
+ readFile.open(QFile::ReadOnly);
+ return readFile.readAll();
+ }
+ }
+ }
+ }
+
+ qCWarning(lcQWaylandAdwaitaDecorationLog) << "Failed to find an svg icon for " << iconName;
+
+ return QString();
+}
+
+void QWaylandAdwaitaDecoration::loadConfiguration()
+{
+ qRegisterMetaType<QDBusVariant>();
+ qDBusRegisterMetaType<QMap<QString, QVariantMap>>();
+
+ QDBusConnection connection = QDBusConnection::sessionBus();
+
+ QDBusMessage message = QDBusMessage::createMethodCall("org.freedesktop.portal.Desktop"_L1,
+ "/org/freedesktop/portal/desktop"_L1,
+ "org.freedesktop.portal.Settings"_L1,
+ "ReadAll"_L1);
+ message << QStringList{ { "org.gnome.desktop.wm.preferences"_L1 },
+ { "org.freedesktop.appearance"_L1 } };
+
+ QDBusPendingCall pendingCall = connection.asyncCall(message);
+ QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(pendingCall, this);
+ QObject::connect(watcher, &QDBusPendingCallWatcher::finished, this, [this](QDBusPendingCallWatcher *watcher) {
+ QDBusPendingReply<QMap<QString, QVariantMap>> reply = *watcher;
+ if (reply.isValid()) {
+ QMap<QString, QVariantMap> settings = reply.value();
+ if (!settings.isEmpty()) {
+ const uint colorScheme = settings.value("org.freedesktop.appearance"_L1).value("color-scheme"_L1).toUInt();
+ updateColors(colorScheme == 1); // 1 == Prefer Dark
+ const QString buttonLayout = settings.value("org.gnome.desktop.wm.preferences"_L1).value("button-layout"_L1).toString();
+ if (!buttonLayout.isEmpty())
+ updateTitlebarLayout(buttonLayout);
+ // Workaround for QGtkStyle not having correct titlebar font
+ const QString titlebarFont =
+ settings.value("org.gnome.desktop.wm.preferences"_L1).value("titlebar-font"_L1).toString();
+ if (titlebarFont.contains("bold"_L1, Qt::CaseInsensitive)) {
+ m_font->setBold(true);
+ }
+ }
+ }
+ watcher->deleteLater();
+ });
+
+ QDBusConnection::sessionBus().connect(QString(), "/org/freedesktop/portal/desktop"_L1,
+ "org.freedesktop.portal.Settings"_L1, "SettingChanged"_L1, this,
+ SLOT(settingChanged(QString, QString, QDBusVariant)));
+
+ // Load SVG icons
+ for (auto mapIt = buttonMap.constBegin(); mapIt != buttonMap.constEnd(); mapIt++) {
+ const QString fullName = mapIt.value() + QStringLiteral(".svg");
+ m_icons[mapIt.key()] = getIconSvg(fullName);
+ }
+
+ updateColors(false);
+}
+
+void QWaylandAdwaitaDecoration::updateColors(bool isDark)
+{
+ qCDebug(lcQWaylandAdwaitaDecorationLog) << "Color scheme changed to: " << (isDark ? "dark" : "light");
+
+ m_colors = { { Background, isDark ? QColor(0x303030) : QColor(0xffffff) },
+ { BackgroundInactive, isDark ? QColor(0x242424) : QColor(0xfafafa) },
+ { Foreground, isDark ? QColor(0xffffff) : QColor(0x2e2e2e) },
+ { ForegroundInactive, isDark ? QColor(0x919191) : QColor(0x949494) },
+ { Border, isDark ? QColor(0x3b3b3b) : QColor(0xdbdbdb) },
+ { BorderInactive, isDark ? QColor(0x303030) : QColor(0xdbdbdb) },
+ { ButtonBackground, isDark ? QColor(0x444444) : QColor(0xebebeb) },
+ { ButtonBackgroundInactive, isDark ? QColor(0x2e2e2e) : QColor(0xf0f0f0) },
+ { HoveredButtonBackground, isDark ? QColor(0x4f4f4f) : QColor(0xe0e0e0) },
+ { PressedButtonBackground, isDark ? QColor(0x6e6e6e) : QColor(0xc2c2c2) } };
+ requestRepaint();
+}
+
+void QWaylandAdwaitaDecoration::updateTitlebarLayout(const QString &layout)
+{
+ const QStringList layouts = layout.split(QLatin1Char(':'));
+ if (layouts.count() != 2)
+ return;
+
+ // Remove previous configuration
+ m_buttons.clear();
+
+ const QString &leftLayout = layouts.at(0);
+ const QString &rightLayout = layouts.at(1);
+ m_placement = leftLayout.contains("close"_L1) ? Left : Right;
+
+ int pos = 1;
+ const QString &buttonLayout = m_placement == Right ? rightLayout : leftLayout;
+
+ QStringList buttonList = buttonLayout.split(QLatin1Char(','));
+ if (m_placement == Right)
+ std::reverse(buttonList.begin(), buttonList.end());
+
+ for (const QString &button : buttonList) {
+ if (button == "close"_L1)
+ m_buttons.insert(Close, pos);
+ else if (button == "maximize"_L1)
+ m_buttons.insert(Maximize, pos);
+ else if (button == "minimize"_L1)
+ m_buttons.insert(Minimize, pos);
+
+ pos++;
+ }
+
+ qCDebug(lcQWaylandAdwaitaDecorationLog) << "Button layout changed to: " << layout;
+
+ requestRepaint();
+}
+
+void QWaylandAdwaitaDecoration::settingChanged(const QString &group, const QString &key,
+ const QDBusVariant &value)
+{
+ if (group == "org.gnome.desktop.wm.preferences"_L1 && key == "button-layout"_L1) {
+ const QString layout = value.variant().toString();
+ updateTitlebarLayout(layout);
+ } else if (group == "org.freedesktop.appearance"_L1 && key == "color-scheme"_L1) {
+ const uint colorScheme = value.variant().toUInt();
+ updateColors(colorScheme == 1); // 1 == Prefer Dark
+ }
+}
+
+QRectF QWaylandAdwaitaDecoration::buttonRect(Button button) const
+{
+ int xPos;
+ int yPos;
+ const int btnPos = m_buttons.value(button);
+
+ const QRect surfaceRect = waylandWindow()->windowContentGeometry() + margins(QWaylandAbstractDecoration::ShadowsOnly);
+ if (m_placement == Right) {
+ xPos = surfaceRect.width();
+ xPos -= ceButtonWidth * btnPos;
+ xPos -= ceButtonSpacing * btnPos;
+ xPos -= margins(ShadowsOnly).right();
+ } else {
+ xPos = 0;
+ xPos += ceButtonWidth * btnPos;
+ xPos += ceButtonSpacing * btnPos;
+ xPos += margins(ShadowsOnly).left();
+ // We are painting from the left to the right so the real
+ // position doesn't need to by moved by the size of the button.
+ xPos -= ceButtonWidth;
+ }
+
+ yPos = margins().top();
+ yPos += margins().bottom();
+ yPos -= ceButtonWidth;
+ yPos /= 2;
+
+ return QRectF(xPos, yPos, ceButtonWidth, ceButtonWidth);
+}
+
+static void renderFlatRoundedButtonFrame(QPainter *painter, const QRect &rect, const QColor &color)
+{
+ painter->save();
+ painter->setRenderHint(QPainter::Antialiasing, true);
+ painter->setPen(Qt::NoPen);
+ painter->setBrush(color);
+ painter->drawEllipse(rect);
+ painter->restore();
+}
+
+static void renderButtonIcon(const QString &svgIcon, QPainter *painter, const QRect &rect, const QColor &color)
+{
+ painter->save();
+ painter->setRenderHints(QPainter::Antialiasing, true);
+
+ QString icon = svgIcon;
+ QRegularExpression regexp("fill=[\"']#[0-9A-F]{6}[\"']", QRegularExpression::CaseInsensitiveOption);
+ QRegularExpression regexpAlt("fill:#[0-9A-F]{6}", QRegularExpression::CaseInsensitiveOption);
+ QRegularExpression regexpCurrentColor("fill=[\"']currentColor[\"']");
+ icon.replace(regexp, QString("fill=\"%1\"").arg(color.name()));
+ icon.replace(regexpAlt, QString("fill:%1").arg(color.name()));
+ icon.replace(regexpCurrentColor, QString("fill=\"%1\"").arg(color.name()));
+ QSvgRenderer svgRenderer(icon.toLocal8Bit());
+ svgRenderer.render(painter, rect);
+
+ painter->restore();
+}
+
+static void renderButtonIcon(QWaylandAdwaitaDecoration::ButtonIcon buttonIcon, QPainter *painter, const QRect &rect)
+{
+ QString iconName = buttonMap[buttonIcon];
+
+ painter->save();
+ painter->setRenderHints(QPainter::Antialiasing, true);
+ painter->drawPixmap(rect, QIcon::fromTheme(iconName).pixmap(ceButtonWidth, ceButtonWidth));
+ painter->restore();
+}
+
+static QWaylandAdwaitaDecoration::ButtonIcon iconFromButtonAndState(QWaylandAdwaitaDecoration::Button button, bool maximized)
+{
+ if (button == QWaylandAdwaitaDecoration::Close)
+ return QWaylandAdwaitaDecoration::CloseIcon;
+ else if (button == QWaylandAdwaitaDecoration::Minimize)
+ return QWaylandAdwaitaDecoration::MinimizeIcon;
+ else if (button == QWaylandAdwaitaDecoration::Maximize && maximized)
+ return QWaylandAdwaitaDecoration::RestoreIcon;
+ else
+ return QWaylandAdwaitaDecoration::MaximizeIcon;
+}
+
+void QWaylandAdwaitaDecoration::drawButton(Button button, QPainter *painter)
+{
+ const Qt::WindowStates windowStates = waylandWindow()->windowStates();
+ const bool maximized = windowStates & Qt::WindowMaximized;
+
+ const QRect btnRect = buttonRect(button).toRect();
+ renderFlatRoundedButtonFrame(painter, btnRect, color(ButtonBackground, button));
+
+ QRect adjustedBtnRect = btnRect;
+ adjustedBtnRect.setSize(QSize(16, 16));
+ adjustedBtnRect.translate(4, 4);
+ const QString svgIcon = m_icons[iconFromButtonAndState(button, maximized)];
+ if (!svgIcon.isEmpty())
+ renderButtonIcon(svgIcon, painter, adjustedBtnRect, color(Foreground));
+ else // Fallback to use QIcon
+ renderButtonIcon(iconFromButtonAndState(button, maximized), painter, adjustedBtnRect);
+}
+
+QColor QWaylandAdwaitaDecoration::color(ColorType type, Button button)
+{
+ const bool active = waylandWindow()->windowStates() & Qt::WindowActive;
+
+ switch (type) {
+ case Background:
+ case BackgroundInactive:
+ return active ? m_colors[Background] : m_colors[BackgroundInactive];
+ case Foreground:
+ case ForegroundInactive:
+ return active ? m_colors[Foreground] : m_colors[ForegroundInactive];
+ case Border:
+ case BorderInactive:
+ return active ? m_colors[Border] : m_colors[BorderInactive];
+ case ButtonBackground:
+ case ButtonBackgroundInactive:
+ case HoveredButtonBackground: {
+ if (m_clicking == button) {
+ return m_colors[PressedButtonBackground];
+ } else if (m_hoveredButtons.testFlag(button)) {
+ return m_colors[HoveredButtonBackground];
+ }
+ return active ? m_colors[ButtonBackground] : m_colors[ButtonBackgroundInactive];
+ }
+ default:
+ return m_colors[Background];
+ }
+}
+
+bool QWaylandAdwaitaDecoration::clickButton(Qt::MouseButtons b, Button btn)
+{
+ auto repaint = qScopeGuard([this] { requestRepaint(); });
+
+ if (isLeftClicked(b)) {
+ m_clicking = btn;
+ return false;
+ } else if (isLeftReleased(b)) {
+ if (m_clicking == btn) {
+ m_clicking = None;
+ return true;
+ } else {
+ m_clicking = None;
+ }
+ }
+ return false;
+}
+
+bool QWaylandAdwaitaDecoration::doubleClickButton(Qt::MouseButtons b, const QPointF &local,
+ const QDateTime &currentTime)
+{
+ if (isLeftClicked(b)) {
+ const qint64 clickInterval = m_lastButtonClick.msecsTo(currentTime);
+ m_lastButtonClick = currentTime;
+ const int doubleClickDistance = 5;
+ const QPointF posDiff = m_lastButtonClickPosition - local;
+ if ((clickInterval <= 500)
+ && ((posDiff.x() <= doubleClickDistance && posDiff.x() >= -doubleClickDistance)
+ && ((posDiff.y() <= doubleClickDistance && posDiff.y() >= -doubleClickDistance)))) {
+ return true;
+ }
+
+ m_lastButtonClickPosition = local;
+ }
+
+ return false;
+}
+
+void QWaylandAdwaitaDecoration::updateButtonHoverState(Button hoveredButton)
+{
+ bool currentCloseButtonState = m_hoveredButtons.testFlag(Close);
+ bool currentMaximizeButtonState = m_hoveredButtons.testFlag(Maximize);
+ bool currentMinimizeButtonState = m_hoveredButtons.testFlag(Minimize);
+
+ m_hoveredButtons.setFlag(Close, hoveredButton == Button::Close);
+ m_hoveredButtons.setFlag(Maximize, hoveredButton == Button::Maximize);
+ m_hoveredButtons.setFlag(Minimize, hoveredButton == Button::Minimize);
+
+ if (m_hoveredButtons.testFlag(Close) != currentCloseButtonState
+ || m_hoveredButtons.testFlag(Maximize) != currentMaximizeButtonState
+ || m_hoveredButtons.testFlag(Minimize) != currentMinimizeButtonState) {
+ requestRepaint();
+ }
+}
+
+void QWaylandAdwaitaDecoration::processMouseTop(QWaylandInputDevice *inputDevice, const QPointF &local,
+ Qt::MouseButtons b, Qt::KeyboardModifiers mods)
+{
+ Q_UNUSED(mods)
+
+ QDateTime currentDateTime = QDateTime::currentDateTime();
+ QRect surfaceRect = waylandWindow()->windowContentGeometry() + margins(ShadowsOnly);
+
+ if (!buttonRect(Close).contains(local) && !buttonRect(Maximize).contains(local)
+ && !buttonRect(Minimize).contains(local))
+ updateButtonHoverState(Button::None);
+
+ if (local.y() <= surfaceRect.top() + margins().bottom()) {
+ if (local.x() <= margins().left()) {
+ // top left bit
+#if QT_CONFIG(cursor)
+ waylandWindow()->setMouseCursor(inputDevice, Qt::SizeFDiagCursor);
+#endif
+ startResize(inputDevice, Qt::TopEdge | Qt::LeftEdge, b);
+ } else if (local.x() > surfaceRect.right() - margins().left()) {
+ // top right bit
+#if QT_CONFIG(cursor)
+ waylandWindow()->setMouseCursor(inputDevice, Qt::SizeBDiagCursor);
+#endif
+ startResize(inputDevice, Qt::TopEdge | Qt::RightEdge, b);
+ } else {
+ // top resize bit
+#if QT_CONFIG(cursor)
+ waylandWindow()->setMouseCursor(inputDevice, Qt::SizeVerCursor);
+#endif
+ startResize(inputDevice, Qt::TopEdge, b);
+ }
+ } else if (local.x() <= surfaceRect.left() + margins().left()) {
+ processMouseLeft(inputDevice, local, b, mods);
+ } else if (local.x() > surfaceRect.right() - margins().right()) {
+ processMouseRight(inputDevice, local, b, mods);
+ } else if (buttonRect(Close).contains(local)) {
+ if (clickButton(b, Close)) {
+ QWindowSystemInterface::handleCloseEvent(window());
+ m_hoveredButtons.setFlag(Close, false);
+ }
+ updateButtonHoverState(Close);
+ } else if (m_buttons.contains(Maximize) && buttonRect(Maximize).contains(local)) {
+ updateButtonHoverState(Maximize);
+ if (clickButton(b, Maximize)) {
+ window()->setWindowStates(window()->windowStates() ^ Qt::WindowMaximized);
+ m_hoveredButtons.setFlag(Maximize, false);
+ }
+ } else if (m_buttons.contains(Minimize) && buttonRect(Minimize).contains(local)) {
+ updateButtonHoverState(Minimize);
+ if (clickButton(b, Minimize)) {
+ window()->setWindowState(Qt::WindowMinimized);
+ m_hoveredButtons.setFlag(Minimize, false);
+ }
+ } else if (doubleClickButton(b, local, currentDateTime)) {
+ window()->setWindowStates(window()->windowStates() ^ Qt::WindowMaximized);
+ } else {
+ // Show window menu
+ if (b == Qt::MouseButton::RightButton)
+ waylandWindow()->shellSurface()->showWindowMenu(inputDevice);
+#if QT_CONFIG(cursor)
+ waylandWindow()->restoreMouseCursor(inputDevice);
+#endif
+ startMove(inputDevice, b);
+ }
+}
+
+void QWaylandAdwaitaDecoration::processMouseBottom(QWaylandInputDevice *inputDevice, const QPointF &local,
+ Qt::MouseButtons b, Qt::KeyboardModifiers mods)
+{
+ Q_UNUSED(mods)
+ if (local.x() <= margins().left()) {
+ // bottom left bit
+#if QT_CONFIG(cursor)
+ waylandWindow()->setMouseCursor(inputDevice, Qt::SizeBDiagCursor);
+#endif
+ startResize(inputDevice, Qt::BottomEdge | Qt::LeftEdge, b);
+ } else if (local.x() > window()->width() + margins().right()) {
+ // bottom right bit
+#if QT_CONFIG(cursor)
+ waylandWindow()->setMouseCursor(inputDevice, Qt::SizeFDiagCursor);
+#endif
+ startResize(inputDevice, Qt::BottomEdge | Qt::RightEdge, b);
+ } else {
+ // bottom bit
+#if QT_CONFIG(cursor)
+ waylandWindow()->setMouseCursor(inputDevice, Qt::SizeVerCursor);
+#endif
+ startResize(inputDevice, Qt::BottomEdge, b);
+ }
+}
+
+void QWaylandAdwaitaDecoration::processMouseLeft(QWaylandInputDevice *inputDevice, const QPointF &local,
+ Qt::MouseButtons b, Qt::KeyboardModifiers mods)
+{
+ Q_UNUSED(local)
+ Q_UNUSED(mods)
+#if QT_CONFIG(cursor)
+ waylandWindow()->setMouseCursor(inputDevice, Qt::SizeHorCursor);
+#endif
+ startResize(inputDevice, Qt::LeftEdge, b);
+}
+
+void QWaylandAdwaitaDecoration::processMouseRight(QWaylandInputDevice *inputDevice, const QPointF &local,
+ Qt::MouseButtons b, Qt::KeyboardModifiers mods)
+{
+ Q_UNUSED(local)
+ Q_UNUSED(mods)
+#if QT_CONFIG(cursor)
+ waylandWindow()->setMouseCursor(inputDevice, Qt::SizeHorCursor);
+#endif
+ startResize(inputDevice, Qt::RightEdge, b);
+}
+
+void QWaylandAdwaitaDecoration::requestRepaint() const
+{
+ // Set dirty flag
+ if (waylandWindow()->decoration())
+ waylandWindow()->decoration()->update();
+
+ // Request re-paint
+ waylandWindow()->window()->requestUpdate();
+}
+
+} // namespace QtWaylandClient
+
+QT_END_NAMESPACE
+
+#include "moc_qwaylandadwaitadecoration_p.cpp"
diff --git a/src/plugins/decorations/adwaita/qwaylandadwaitadecoration_p.h b/src/plugins/decorations/adwaita/qwaylandadwaitadecoration_p.h
new file mode 100644
index 000000000..34874e088
--- /dev/null
+++ b/src/plugins/decorations/adwaita/qwaylandadwaitadecoration_p.h
@@ -0,0 +1,155 @@
+// Copyright (C) 2023 Jan Grulich <jgrulich@redhat.com>
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QWAYLANDADWAITADECORATION_P_H
+#define QWAYLANDADWAITADECORATION_P_H
+
+#include <QtWaylandClient/private/qwaylandabstractdecoration_p.h>
+
+#include <QtCore/QDateTime>
+
+QT_BEGIN_NAMESPACE
+
+class QDBusVariant;
+class QPainter;
+
+namespace QtWaylandClient {
+
+//
+// INFO
+// -------------
+//
+// This is a Qt decoration plugin implementing Adwaita-like (GNOME) client-side
+// window decorations. It uses xdg-desktop-portal to get the user configuration.
+// This plugin was originally part of QGnomePlatform and later made a separate
+// project named QAdwaitaDecorations.
+//
+// INFO: How SVG icons are used here?
+// We try to find an SVG icon for a particular button from the current icon theme.
+// This icon is then opened as a file, it's content saved and later loaded to be
+// painted with QSvgRenderer, but before it's painted, we try to find following
+// patterns:
+// 1) fill=[\"']#[0-9A-F]{6}[\"']
+// 2) fill:#[0-9A-F]{6}
+// 3) fill=[\"']currentColor[\"']
+// The color in this case doesn't match the theme and is replaced by Foreground color.
+//
+// FIXME/TODO:
+// This plugin currently have all the colors for the decorations hardcoded.
+// There might be a way to get these from GTK/libadwaita (not sure), but problem is
+// we want Gtk4 version and using Gtk4 together with QGtk3Theme from QtBase that links
+// to Gtk3 will not work out. Possibly in future we can make a QGtk4Theme providing us
+// what we need to paint the decorations without having to deal with the colors ourself.
+//
+// TODO: Implement shadows
+
+
+class QWaylandAdwaitaDecoration : public QWaylandAbstractDecoration
+{
+ Q_OBJECT
+public:
+ enum ColorType {
+ Background,
+ BackgroundInactive,
+ Foreground,
+ ForegroundInactive,
+ Border,
+ BorderInactive,
+ ButtonBackground,
+ ButtonBackgroundInactive,
+ HoveredButtonBackground,
+ PressedButtonBackground
+ };
+
+ enum Placement {
+ Left = 0,
+ Right = 1
+ };
+
+ enum Button {
+ None = 0x0,
+ Close = 0x1,
+ Minimize = 0x02,
+ Maximize = 0x04
+ };
+ Q_DECLARE_FLAGS(Buttons, Button);
+
+ enum ButtonIcon {
+ CloseIcon,
+ MinimizeIcon,
+ MaximizeIcon,
+ RestoreIcon
+ };
+
+ QWaylandAdwaitaDecoration();
+ virtual ~QWaylandAdwaitaDecoration() = default;
+
+protected:
+ QMargins margins(MarginsType marginsType = Full) const override;
+ void paint(QPaintDevice *device) override;
+ 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;
+
+private Q_SLOTS:
+ void settingChanged(const QString &group, const QString &key, const QDBusVariant &value);
+
+private:
+ // Makes a call to xdg-desktop-portal (Settings) to load initial configuration
+ void loadConfiguration();
+ // Updates color scheme from light to dark and vice-versa
+ void updateColors(bool isDark);
+ // Updates titlebar layout with position and button order
+ void updateTitlebarLayout(const QString &layout);
+
+ // Returns a bounding rect for a given button type
+ QRectF buttonRect(Button button) const;
+ // Draw given button type using SVG icon (when found) or fallback to QPixmap icon
+ void drawButton(Button button, QPainter *painter);
+
+ // Returns color for given type and button
+ QColor color(ColorType type, Button button = None);
+
+ // Returns whether the left button was clicked i.e. pressed and released
+ bool clickButton(Qt::MouseButtons b, Button btn);
+ // Returns whether the left button was double-clicked
+ bool doubleClickButton(Qt::MouseButtons b, const QPointF &local, const QDateTime &currentTime);
+ // Updates button hover state
+ void updateButtonHoverState(Button hoveredButton);
+
+ void processMouseTop(QWaylandInputDevice *inputDevice, const QPointF &local, Qt::MouseButtons b,
+ Qt::KeyboardModifiers mods);
+ void processMouseBottom(QWaylandInputDevice *inputDevice, const QPointF &local,
+ Qt::MouseButtons b, Qt::KeyboardModifiers mods);
+ void processMouseLeft(QWaylandInputDevice *inputDevice, const QPointF &local,
+ Qt::MouseButtons b, Qt::KeyboardModifiers mods);
+ void processMouseRight(QWaylandInputDevice *inputDevice, const QPointF &local,
+ Qt::MouseButtons b, Qt::KeyboardModifiers mods);
+ // Request to repaint the decorations. This will be invoked when button hover changes or
+ // when there is a setting change (e.g. layout change).
+ void requestRepaint() const;
+
+ // Button states
+ Button m_clicking = None;
+ Buttons m_hoveredButtons = None;
+ QDateTime m_lastButtonClick;
+ QPointF m_lastButtonClickPosition;
+
+ // Configuration
+ QMap<Button, uint> m_buttons;
+ QMap<ColorType, QColor> m_colors;
+ QMap<ButtonIcon, QString> m_icons;
+ std::unique_ptr<QFont> m_font;
+ Placement m_placement = Right;
+
+ QStaticText m_windowTitle;
+};
+Q_DECLARE_OPERATORS_FOR_FLAGS(QWaylandAdwaitaDecoration::Buttons)
+
+} // namespace QtWaylandClient
+
+QT_END_NAMESPACE
+
+#endif // QWAYLANDADWAITADECORATION_P_H
diff --git a/src/plugins/decorations/bradient/CMakeLists.txt b/src/plugins/decorations/bradient/CMakeLists.txt
new file mode 100644
index 000000000..065d0f18c
--- /dev/null
+++ b/src/plugins/decorations/bradient/CMakeLists.txt
@@ -0,0 +1,23 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+# Generated from bradient.pro.
+
+#####################################################################
+## QWaylandBradientDecorationPlugin Plugin:
+#####################################################################
+
+qt_internal_add_plugin(QWaylandBradientDecorationPlugin
+ OUTPUT_NAME bradient
+ PLUGIN_TYPE wayland-decoration-client
+ SOURCES
+ main.cpp
+ LIBRARIES
+ Qt::Core
+ Qt::Gui
+ Qt::WaylandClientPrivate
+ Wayland::Client
+)
+
+#### Keys ignored in scope 1:.:.:bradient.pro:<TRUE>:
+# OTHER_FILES = "bradient.json"
diff --git a/src/plugins/decorations/bradient/bradient.pro b/src/plugins/decorations/bradient/bradient.pro
deleted file mode 100644
index e31576783..000000000
--- a/src/plugins/decorations/bradient/bradient.pro
+++ /dev/null
@@ -1,12 +0,0 @@
-QT += waylandclient-private
-
-OTHER_FILES += \
- bradient.json
-
-SOURCES += main.cpp
-
-QMAKE_USE += wayland-client
-
-PLUGIN_TYPE = wayland-decoration-client
-PLUGIN_CLASS_NAME = QWaylandBradientDecorationPlugin
-load(qt_plugin)
diff --git a/src/plugins/decorations/bradient/main.cpp b/src/plugins/decorations/bradient/main.cpp
index e8e35775e..32f2d8db8 100644
--- a/src/plugins/decorations/bradient/main.cpp
+++ b/src/plugins/decorations/bradient/main.cpp
@@ -1,47 +1,13 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 Robin Burchell <robin.burchell@viroteck.net>
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the plugins of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 Robin Burchell <robin.burchell@viroteck.net>
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include <QtGui/QCursor>
#include <QtGui/QPainter>
+#include <QtGui/QPainterPath>
#include <QtGui/QPalette>
#include <QtGui/QLinearGradient>
+#include <QtGui/QPainterPath>
#include <qpa/qwindowsysteminterface.h>
@@ -66,29 +32,31 @@ enum Button
Minimize
};
-class Q_WAYLAND_CLIENT_EXPORT QWaylandBradientDecoration : public QWaylandAbstractDecoration
+class Q_WAYLANDCLIENT_EXPORT QWaylandBradientDecoration : public QWaylandAbstractDecoration
{
public:
QWaylandBradientDecoration();
protected:
- QMargins margins() const override;
+ QMargins margins(MarginsType marginsType = Full) const override;
void paint(QPaintDevice *device) override;
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, Qt::TouchPointState state, Qt::KeyboardModifiers mods) override;
+ bool handleTouch(QWaylandInputDevice *inputDevice, const QPointF &local, const QPointF &global, QEventPoint::State state, Qt::KeyboardModifiers mods) override;
private:
- void processMouseTop(QWaylandInputDevice *inputDevice, const QPointF &local, Qt::MouseButtons b,Qt::KeyboardModifiers mods);
- void processMouseBottom(QWaylandInputDevice *inputDevice, const QPointF &local, Qt::MouseButtons b,Qt::KeyboardModifiers mods);
- void processMouseLeft(QWaylandInputDevice *inputDevice, const QPointF &local, Qt::MouseButtons b,Qt::KeyboardModifiers mods);
- void processMouseRight(QWaylandInputDevice *inputDevice, const QPointF &local, Qt::MouseButtons b,Qt::KeyboardModifiers mods);
+ enum class PointerType {
+ Mouse,
+ Touch
+ };
+
+ void processPointerTop(QWaylandInputDevice *inputDevice, const QPointF &local, Qt::MouseButtons b,Qt::KeyboardModifiers mods, PointerType type);
+ void processPointerBottom(QWaylandInputDevice *inputDevice, const QPointF &local, Qt::MouseButtons b,Qt::KeyboardModifiers mods, PointerType type);
+ void processPointerLeft(QWaylandInputDevice *inputDevice, const QPointF &local, Qt::MouseButtons b,Qt::KeyboardModifiers mods, PointerType type);
+ void processPointerRight(QWaylandInputDevice *inputDevice, const QPointF &local, Qt::MouseButtons b,Qt::KeyboardModifiers mods, PointerType type);
bool clickButton(Qt::MouseButtons b, Button btn);
QRectF closeButtonRect() const;
QRectF maximizeButtonRect() const;
QRectF minimizeButtonRect() const;
- QColor m_foregroundColor;
- QColor m_foregroundInactiveColor;
- QColor m_backgroundColor;
QStaticText m_windowTitle;
Button m_clicking = None;
};
@@ -97,56 +65,61 @@ private:
QWaylandBradientDecoration::QWaylandBradientDecoration()
{
- QPalette palette;
- m_foregroundColor = palette.color(QPalette::Active, QPalette::WindowText);
- m_backgroundColor = palette.color(QPalette::Active, QPalette::Window);
- m_foregroundInactiveColor = palette.color(QPalette::Disabled, QPalette::WindowText);
-
QTextOption option(Qt::AlignHCenter | Qt::AlignVCenter);
option.setWrapMode(QTextOption::NoWrap);
m_windowTitle.setTextOption(option);
+ m_windowTitle.setTextFormat(Qt::PlainText);
}
QRectF QWaylandBradientDecoration::closeButtonRect() const
{
- const int windowRight = waylandWindow()->windowContentGeometry().right() + 1;
+ const int windowRight = waylandWindow()->surfaceSize().width() - margins(ShadowsOnly).right();
return QRectF(windowRight - BUTTON_WIDTH - BUTTON_SPACING * 0 - BUTTONS_RIGHT_MARGIN,
(margins().top() - BUTTON_WIDTH) / 2, BUTTON_WIDTH, BUTTON_WIDTH);
}
QRectF QWaylandBradientDecoration::maximizeButtonRect() const
{
- const int windowRight = waylandWindow()->windowContentGeometry().right() + 1;
+ const int windowRight = waylandWindow()->surfaceSize().width() - margins(ShadowsOnly).right();
return QRectF(windowRight - BUTTON_WIDTH * 2 - BUTTON_SPACING * 1 - BUTTONS_RIGHT_MARGIN,
(margins().top() - BUTTON_WIDTH) / 2, BUTTON_WIDTH, BUTTON_WIDTH);
}
QRectF QWaylandBradientDecoration::minimizeButtonRect() const
{
- const int windowRight = waylandWindow()->windowContentGeometry().right() + 1;
+ const int windowRight = waylandWindow()->surfaceSize().width() - margins(ShadowsOnly).right();
return QRectF(windowRight - BUTTON_WIDTH * 3 - BUTTON_SPACING * 2 - BUTTONS_RIGHT_MARGIN,
(margins().top() - BUTTON_WIDTH) / 2, BUTTON_WIDTH, BUTTON_WIDTH);
}
-QMargins QWaylandBradientDecoration::margins() const
+QMargins QWaylandBradientDecoration::margins(MarginsType marginsType) const
{
+ if (marginsType == ShadowsOnly)
+ return QMargins();
+
return QMargins(3, 30, 3, 3);
}
void QWaylandBradientDecoration::paint(QPaintDevice *device)
{
bool active = window()->handle()->isActive();
- QRect wg = waylandWindow()->windowContentGeometry();
+ QRect wg = QRect(QPoint(), waylandWindow()->surfaceSize()).marginsRemoved(margins(ShadowsOnly));
+ QRect cg = wg.marginsRemoved(margins(ShadowsExcluded));
QRect clips[] =
{
- QRect(wg.left(), wg.top(), wg.width(), margins().top()),
- QRect(wg.left(), (wg.bottom() + 1) - margins().bottom(), wg.width(), margins().bottom()),
- QRect(wg.left(), margins().top(), margins().left(), wg.height() - margins().top() - margins().bottom()),
- QRect((wg.right() + 1) - margins().right(), wg.top() + margins().top(), margins().right(), wg.height() - margins().top() - margins().bottom())
+ QRect(wg.left(), wg.top(), wg.width(), margins(ShadowsExcluded).top()),
+ QRect(wg.left(), cg.bottom() + 1, wg.width(), margins(ShadowsExcluded).bottom()),
+ QRect(wg.left(), cg.top(), margins(ShadowsExcluded).left(), cg.height()),
+ QRect(cg.right() + 1, cg.top(), margins(ShadowsExcluded).right(), cg.height())
};
QRect top = clips[0];
+ QPalette palette;
+ const QColor foregroundColor = palette.color(QPalette::Active, QPalette::WindowText);
+ const QColor backgroundColor = palette.color(QPalette::Active, QPalette::Window);
+ const QColor foregroundInactiveColor = palette.color(QPalette::Disabled, QPalette::WindowText);
+
QPainter p(device);
p.setRenderHint(QPainter::Antialiasing);
@@ -156,24 +129,21 @@ void QWaylandBradientDecoration::paint(QPaintDevice *device)
for (int i = 0; i < 4; ++i) {
p.save();
p.setClipRect(clips[i]);
- p.fillPath(roundedRect, m_backgroundColor);
+ p.fillPath(roundedRect, backgroundColor);
p.restore();
}
// Window icon
QIcon icon = waylandWindow()->windowIcon();
if (!icon.isNull()) {
- QPixmap pixmap = icon.pixmap(QSize(128, 128));
- QPixmap scaled = pixmap.scaled(22, 22, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
-
QRectF iconRect(0, 0, 22, 22);
- p.drawPixmap(iconRect.adjusted(margins().left() + BUTTON_SPACING, 4,
- margins().left() + BUTTON_SPACING, 4),
- scaled, iconRect);
+ iconRect.adjust(margins().left() + BUTTON_SPACING, 4,
+ margins().left() + BUTTON_SPACING, 4),
+ icon.paint(&p, iconRect.toRect());
}
// Window title
- QString windowTitleText = window()->title();
+ QString windowTitleText = waylandWindow()->windowTitle();
if (!windowTitleText.isEmpty()) {
if (m_windowTitle.text() != windowTitleText) {
m_windowTitle.setText(windowTitleText);
@@ -187,7 +157,7 @@ void QWaylandBradientDecoration::paint(QPaintDevice *device)
p.save();
p.setClipRect(titleBar);
- p.setPen(active ? m_foregroundColor : m_foregroundInactiveColor);
+ p.setPen(active ? foregroundColor : foregroundInactiveColor);
QSizeF size = m_windowTitle.size();
int dx = (top.width() - size.width()) /2;
int dy = (top.height()- size.height()) /2;
@@ -203,7 +173,7 @@ void QWaylandBradientDecoration::paint(QPaintDevice *device)
QRectF rect;
// Default pen
- QPen pen(active ? m_foregroundColor : m_foregroundInactiveColor);
+ QPen pen(active ? foregroundColor : foregroundInactiveColor);
p.setPen(pen);
// Close button
@@ -227,7 +197,7 @@ void QWaylandBradientDecoration::paint(QPaintDevice *device)
QRectF rect1 = rect.adjusted(inset, 0, 0, -inset);
QRectF rect2 = rect.adjusted(0, inset, -inset, 0);
p.drawRect(rect1);
- p.setBrush(m_backgroundColor); // need to cover up some lines from the other rect
+ p.setBrush(backgroundColor); // need to cover up some lines from the other rect
p.drawRect(rect2);
} else {
p.drawRect(rect);
@@ -267,15 +237,15 @@ bool QWaylandBradientDecoration::handleMouse(QWaylandInputDevice *inputDevice, c
Q_UNUSED(global);
// Figure out what area mouse is in
- QRect wg = waylandWindow()->windowContentGeometry();
- if (local.y() <= wg.top() + margins().top()) {
- processMouseTop(inputDevice,local,b,mods);
- } else if (local.y() > wg.bottom() - margins().bottom()) {
- processMouseBottom(inputDevice,local,b,mods);
- } else if (local.x() <= wg.left() + margins().left()) {
- processMouseLeft(inputDevice,local,b,mods);
- } else if (local.x() > wg.right() - margins().right()) {
- processMouseRight(inputDevice,local,b,mods);
+ QSize ss = waylandWindow()->surfaceSize();
+ if (local.y() <= margins().top()) {
+ processPointerTop(inputDevice, local, b, mods, PointerType::Mouse);
+ } else if (local.y() >= ss.height() - margins().bottom()) {
+ processPointerBottom(inputDevice, local, b, mods, PointerType::Mouse);
+ } else if (local.x() <= margins().left()) {
+ processPointerLeft(inputDevice, local, b, mods, PointerType::Mouse);
+ } else if (local.x() >= ss.width() - margins().right()) {
+ processPointerRight(inputDevice, local, b, mods, PointerType::Mouse);
} else {
#if QT_CONFIG(cursor)
waylandWindow()->restoreMouseCursor(inputDevice);
@@ -288,115 +258,154 @@ bool QWaylandBradientDecoration::handleMouse(QWaylandInputDevice *inputDevice, c
return true;
}
-bool QWaylandBradientDecoration::handleTouch(QWaylandInputDevice *inputDevice, const QPointF &local, const QPointF &global, Qt::TouchPointState state, Qt::KeyboardModifiers mods)
+bool QWaylandBradientDecoration::handleTouch(QWaylandInputDevice *inputDevice, const QPointF &local, const QPointF &global, QEventPoint::State state, Qt::KeyboardModifiers mods)
{
- Q_UNUSED(inputDevice);
Q_UNUSED(global);
- Q_UNUSED(mods);
- bool handled = state == Qt::TouchPointPressed;
+ QSize ss = waylandWindow()->surfaceSize();
+
+ bool handled = state == QEventPoint::Pressed;
if (handled) {
- if (closeButtonRect().contains(local))
- QWindowSystemInterface::handleCloseEvent(window());
- else if (maximizeButtonRect().contains(local))
- window()->setWindowStates(window()->windowStates() ^ Qt::WindowMaximized);
- else if (minimizeButtonRect().contains(local))
- window()->setWindowState(Qt::WindowMinimized);
- else if (local.y() <= margins().top())
- waylandWindow()->shellSurface()->move(inputDevice);
- else
+ if (local.y() <= margins().top()) {
+ processPointerTop(inputDevice, local, Qt::LeftButton, mods, PointerType::Touch);
+ } else if (local.y() >= ss.height() - margins().bottom()) {
+ processPointerBottom(inputDevice, local, Qt::LeftButton, mods, PointerType::Touch);
+ } else if (local.x() <= margins().left()) {
+ processPointerLeft(inputDevice, local, Qt::LeftButton, mods, PointerType::Touch);
+ } else if (local.x() >= ss.width() - margins().right()) {
+ processPointerRight(inputDevice, local, Qt::LeftButton, mods, PointerType::Touch);
+ } else {
handled = false;
+ }
}
return handled;
}
-void QWaylandBradientDecoration::processMouseTop(QWaylandInputDevice *inputDevice, const QPointF &local, Qt::MouseButtons b, Qt::KeyboardModifiers mods)
+void QWaylandBradientDecoration::processPointerTop(QWaylandInputDevice *inputDevice,
+ const QPointF &local,
+ Qt::MouseButtons b,
+ Qt::KeyboardModifiers mods,
+ PointerType type)
{
- QRect wg = waylandWindow()->windowContentGeometry();
+#if !QT_CONFIG(cursor)
+ Q_UNUSED(type);
+#endif
+
+ QSize ss = waylandWindow()->surfaceSize();
Q_UNUSED(mods);
- if (local.y() <= wg.top() + margins().bottom()) {
+ if (local.y() <= margins().bottom()) {
if (local.x() <= margins().left()) {
//top left bit
#if QT_CONFIG(cursor)
- waylandWindow()->setMouseCursor(inputDevice, Qt::SizeFDiagCursor);
+ if (type == PointerType::Mouse)
+ waylandWindow()->setMouseCursor(inputDevice, Qt::SizeFDiagCursor);
#endif
startResize(inputDevice, Qt::TopEdge | Qt::LeftEdge, b);
- } else if (local.x() > wg.right() - margins().right()) {
+ } else if (local.x() >= ss.width() - margins().right()) {
//top right bit
#if QT_CONFIG(cursor)
- waylandWindow()->setMouseCursor(inputDevice, Qt::SizeBDiagCursor);
+ if (type == PointerType::Mouse)
+ waylandWindow()->setMouseCursor(inputDevice, Qt::SizeBDiagCursor);
#endif
startResize(inputDevice, Qt::TopEdge | Qt::RightEdge, b);
} else {
//top resize bit
#if QT_CONFIG(cursor)
- waylandWindow()->setMouseCursor(inputDevice, Qt::SplitVCursor);
+ if (type == PointerType::Mouse)
+ waylandWindow()->setMouseCursor(inputDevice, Qt::SplitVCursor);
#endif
startResize(inputDevice, Qt::TopEdge, b);
}
- } else if (local.x() <= wg.left() + margins().left()) {
- processMouseLeft(inputDevice, local, b, mods);
- } else if (local.x() > wg.right() - margins().right()) {
- processMouseRight(inputDevice, local, b, mods);
+ } else if (local.x() <= margins().left()) {
+ processPointerLeft(inputDevice, local, b, mods, type);
+ } else if (local.x() >= ss.width() - margins().right()) {
+ processPointerRight(inputDevice, local, b, mods, type);
} else if (isRightClicked(b)) {
showWindowMenu(inputDevice);
} else if (closeButtonRect().contains(local)) {
- if (clickButton(b, Close))
+ if (type == PointerType::Touch || clickButton(b, Close))
QWindowSystemInterface::handleCloseEvent(window());
} else if (maximizeButtonRect().contains(local)) {
- if (clickButton(b, Maximize))
+ if (type == PointerType::Touch || clickButton(b, Maximize))
window()->setWindowStates(window()->windowStates() ^ Qt::WindowMaximized);
} else if (minimizeButtonRect().contains(local)) {
- if (clickButton(b, Minimize))
+ if (type == PointerType::Touch || clickButton(b, Minimize))
window()->setWindowState(Qt::WindowMinimized);
} else {
#if QT_CONFIG(cursor)
- waylandWindow()->restoreMouseCursor(inputDevice);
+ if (type == PointerType::Mouse)
+ waylandWindow()->restoreMouseCursor(inputDevice);
#endif
startMove(inputDevice,b);
}
}
-void QWaylandBradientDecoration::processMouseBottom(QWaylandInputDevice *inputDevice, const QPointF &local, Qt::MouseButtons b, Qt::KeyboardModifiers mods)
+void QWaylandBradientDecoration::processPointerBottom(QWaylandInputDevice *inputDevice,
+ const QPointF &local,
+ Qt::MouseButtons b,
+ Qt::KeyboardModifiers mods,
+ PointerType type)
{
Q_UNUSED(mods);
+#if !QT_CONFIG(cursor)
+ Q_UNUSED(type);
+#endif
+
+ QSize ss = waylandWindow()->surfaceSize();
if (local.x() <= margins().left()) {
//bottom left bit
#if QT_CONFIG(cursor)
- waylandWindow()->setMouseCursor(inputDevice, Qt::SizeBDiagCursor);
+ if (type == PointerType::Mouse)
+ waylandWindow()->setMouseCursor(inputDevice, Qt::SizeBDiagCursor);
#endif
startResize(inputDevice, Qt::BottomEdge | Qt::LeftEdge, b);
- } else if (local.x() > window()->width() + margins().left()) {
+ } else if (local.x() >= ss.width() - margins().right()) {
//bottom right bit
#if QT_CONFIG(cursor)
- waylandWindow()->setMouseCursor(inputDevice, Qt::SizeFDiagCursor);
+ if (type == PointerType::Mouse)
+ waylandWindow()->setMouseCursor(inputDevice, Qt::SizeFDiagCursor);
#endif
startResize(inputDevice, Qt::BottomEdge | Qt::RightEdge, b);
} else {
//bottom bit
#if QT_CONFIG(cursor)
- waylandWindow()->setMouseCursor(inputDevice, Qt::SplitVCursor);
+ if (type == PointerType::Mouse)
+ waylandWindow()->setMouseCursor(inputDevice, Qt::SizeVerCursor);
#endif
startResize(inputDevice, Qt::BottomEdge, b);
}
}
-void QWaylandBradientDecoration::processMouseLeft(QWaylandInputDevice *inputDevice, const QPointF &local, Qt::MouseButtons b, Qt::KeyboardModifiers mods)
+void QWaylandBradientDecoration::processPointerLeft(QWaylandInputDevice *inputDevice,
+ const QPointF &local,
+ Qt::MouseButtons b,
+ Qt::KeyboardModifiers mods,
+ PointerType type)
{
Q_UNUSED(local);
Q_UNUSED(mods);
#if QT_CONFIG(cursor)
- waylandWindow()->setMouseCursor(inputDevice, Qt::SplitHCursor);
+ if (type == PointerType::Mouse)
+ waylandWindow()->setMouseCursor(inputDevice, Qt::SizeHorCursor);
+#else
+ Q_UNUSED(type);
#endif
startResize(inputDevice, Qt::LeftEdge, b);
}
-void QWaylandBradientDecoration::processMouseRight(QWaylandInputDevice *inputDevice, const QPointF &local, Qt::MouseButtons b, Qt::KeyboardModifiers mods)
+void QWaylandBradientDecoration::processPointerRight(QWaylandInputDevice *inputDevice,
+ const QPointF &local,
+ Qt::MouseButtons b,
+ Qt::KeyboardModifiers mods,
+ PointerType type)
{
Q_UNUSED(local);
Q_UNUSED(mods);
#if QT_CONFIG(cursor)
- waylandWindow()->setMouseCursor(inputDevice, Qt::SplitHCursor);
+ if (type == PointerType::Mouse)
+ waylandWindow()->setMouseCursor(inputDevice, Qt::SizeHorCursor);
+#else
+ Q_UNUSED(type);
#endif
startResize(inputDevice, Qt::RightEdge, b);
}
diff --git a/src/plugins/decorations/decorations.pro b/src/plugins/decorations/decorations.pro
deleted file mode 100644
index 6d51a450f..000000000
--- a/src/plugins/decorations/decorations.pro
+++ /dev/null
@@ -1,3 +0,0 @@
-TEMPLATE = subdirs
-SUBDIRS += \
- bradient