8#include <QtCore/QLoggingCategory>
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>
22#include <QtGui/QColor>
23#include <QtGui/QPainter>
24#include <QtGui/QPainterPath>
26#include <QtGui/private/qguiapplication_p.h>
27#include <QtGui/qpa/qplatformtheme.h>
30#include <QtSvg/QSvgRenderer>
33#include <QtWaylandClient/private/qwaylandshmbackingstore_p.h>
34#include <QtWaylandClient/private/qwaylandwindow_p.h>
51static QMap<QWaylandAdwaitaDecoration::ButtonIcon, QString>
buttonMap = {
85 m_windowTitle.setTextOption(
option);
90 m_font = std::make_unique<QFont>(*
font);
92 m_font = std::make_unique<QFont>(
"Cantarell"_L1, 10);
113 const int sideMargins = onlyShadows ?
ceShadowsWidth : marginsBase;
168 if (!windowTitleText.isEmpty()) {
169 if (m_windowTitle.
text() != windowTitleText) {
170 m_windowTitle.
setText(windowTitleText);
175 if (m_placement ==
Right) {
184 p.setClipRect(titleBar);
187 int dx = (
top.width() -
size.width()) / 2;
188 int dy = (
top.height() -
size.height()) / 2;
190 QPoint windowTitlePoint(
top.topLeft().x() + dx,
top.topLeft().y() + dy);
191 p.drawStaticText(windowTitlePoint, m_windowTitle);
211 Qt::KeyboardModifiers mods)
220 if (local.
y() <= surfaceRect.top() +
margins().
top())
221 processMouseTop(inputDevice, local,
b, mods);
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);
247 Qt::KeyboardModifiers mods)
275 qCDebug(lcQWaylandAdwaitaDecorationLog) <<
"Searched icon themes: " <<
themeNames;
287 while (dirIt.hasNext()) {
291 if (fileInfo.
fileName() == iconName) {
292 qCDebug(lcQWaylandAdwaitaDecorationLog) <<
"Using " << iconName <<
" from " <<
themeName <<
" theme";
295 return readFile.readAll();
301 qCWarning(lcQWaylandAdwaitaDecorationLog) <<
"Failed to find an svg icon for " << iconName;
306void QWaylandAdwaitaDecoration::loadConfiguration()
308 qRegisterMetaType<QDBusVariant>();
309 qDBusRegisterMetaType<QMap<QString, QVariantMap>>();
314 "/org/freedesktop/portal/desktop"_L1,
315 "org.freedesktop.portal.Settings"_L1,
318 {
"org.freedesktop.appearance"_L1 } };
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);
329 const QString buttonLayout = settings.value(
"org.gnome.desktop.wm.preferences"_L1).value(
"button-layout"_L1).toString();
330 if (!buttonLayout.isEmpty())
331 updateTitlebarLayout(buttonLayout);
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);
344 "org.freedesktop.portal.Settings"_L1,
"SettingChanged"_L1,
this,
356void QWaylandAdwaitaDecoration::updateColors(
bool isDark)
358 qCDebug(lcQWaylandAdwaitaDecorationLog) <<
"Color scheme changed to: " << (isDark ?
"dark" :
"light");
361 { BackgroundInactive, isDark ?
QColor(0x242424) :
QColor(0xfafafa) },
362 { Foreground, isDark ?
QColor(0xffffff) :
QColor(0x2e2e2e) },
363 { ForegroundInactive, isDark ?
QColor(0x919191) :
QColor(0x949494) },
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) } };
373void QWaylandAdwaitaDecoration::updateTitlebarLayout(
const QString &
layout)
376 if (layouts.count() != 2)
382 const QString &leftLayout = layouts.
at(0);
383 const QString &rightLayout = layouts.
at(1);
384 m_placement = leftLayout.contains(
"close"_L1) ?
Left :
Right;
387 const QString &buttonLayout = m_placement ==
Right ? rightLayout : leftLayout;
390 if (m_placement ==
Right)
391 std::reverse(buttonList.begin(), buttonList.end());
396 else if (
button ==
"maximize"_L1)
398 else if (
button ==
"minimize"_L1)
404 qCDebug(lcQWaylandAdwaitaDecorationLog) <<
"Button layout changed to: " <<
layout;
412 if (
group ==
"org.gnome.desktop.wm.preferences"_L1 &&
key ==
"button-layout"_L1) {
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);
427 const QRect surfaceRect = waylandWindow()->windowContentGeometry() + margins(QWaylandAbstractDecoration::ShadowsOnly);
428 if (m_placement ==
Right) {
429 xPos = surfaceRect.width();
432 xPos -= margins(ShadowsOnly).right();
437 xPos += margins(ShadowsOnly).left();
443 yPos = margins().top();
444 yPos += margins().bottom();
448 return QRectF(xPos, yPos, ceButtonWidth, ceButtonWidth);
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;
498 return QWaylandAdwaitaDecoration::MaximizeIcon;
503 const Qt::WindowStates windowStates = waylandWindow()->windowStates();
509 QRect adjustedBtnRect = btnRect;
511 adjustedBtnRect.translate(4, 4);
512 const QString svgIcon = m_icons[iconFromButtonAndState(
button, maximized)];
514 renderButtonIcon(svgIcon,
painter, adjustedBtnRect,
color(Foreground));
516 renderButtonIcon(iconFromButtonAndState(
button, maximized),
painter, adjustedBtnRect);
525 case BackgroundInactive:
526 return active ? m_colors[
Background] : m_colors[BackgroundInactive];
528 case ForegroundInactive:
529 return active ? m_colors[Foreground] : m_colors[ForegroundInactive];
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];
541 return active ? m_colors[ButtonBackground] : m_colors[ButtonBackgroundInactive];
548bool QWaylandAdwaitaDecoration::clickButton(Qt::MouseButtons
b,
Button btn)
550 auto repaint =
qScopeGuard([
this] { requestRepaint(); });
552 if (isLeftClicked(
b)) {
555 }
else if (isLeftReleased(
b)) {
556 if (m_clicking ==
btn) {
566bool QWaylandAdwaitaDecoration::doubleClickButton(Qt::MouseButtons
b,
const QPointF &local,
569 if (isLeftClicked(
b)) {
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)))) {
580 m_lastButtonClickPosition = local;
586void QWaylandAdwaitaDecoration::updateButtonHoverState(
Button hoveredButton)
588 bool currentCloseButtonState = m_hoveredButtons.testFlag(Close);
589 bool currentMaximizeButtonState = m_hoveredButtons.testFlag(Maximize);
590 bool currentMinimizeButtonState = m_hoveredButtons.testFlag(Minimize);
592 m_hoveredButtons.setFlag(Close, hoveredButton == Button::Close);
593 m_hoveredButtons.setFlag(Maximize, hoveredButton == Button::Maximize);
594 m_hoveredButtons.setFlag(Minimize, hoveredButton == Button::Minimize);
596 if (m_hoveredButtons.testFlag(Close) != currentCloseButtonState
597 || m_hoveredButtons.testFlag(Maximize) != currentMaximizeButtonState
598 || m_hoveredButtons.testFlag(Minimize) != currentMinimizeButtonState) {
603void QWaylandAdwaitaDecoration::processMouseTop(QWaylandInputDevice *inputDevice,
const QPointF &local,
604 Qt::MouseButtons
b, Qt::KeyboardModifiers mods)
609 QRect surfaceRect = waylandWindow()->windowContentGeometry() + margins(ShadowsOnly);
611 if (!buttonRect(Close).
contains(local) && !buttonRect(Maximize).
contains(local)
612 && !buttonRect(Minimize).
contains(local))
613 updateButtonHoverState(Button::None);
615 if (local.
y() <= surfaceRect.top() + margins().
bottom()) {
616 if (local.
x() <= margins().
left()) {
622 }
else if (local.
x() > surfaceRect.right() - margins().
left()) {
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)) {
642 m_hoveredButtons.setFlag(Close,
false);
644 updateButtonHoverState(Close);
645 }
else if (
m_buttons.contains(Maximize) && buttonRect(Maximize).
contains(local)) {
646 updateButtonHoverState(Maximize);
649 m_hoveredButtons.setFlag(Maximize,
false);
651 }
else if (
m_buttons.contains(Minimize) && buttonRect(Minimize).
contains(local)) {
652 updateButtonHoverState(Minimize);
655 m_hoveredButtons.setFlag(Minimize,
false);
662 waylandWindow()->shellSurface()->showWindowMenu(inputDevice);
664 waylandWindow()->restoreMouseCursor(inputDevice);
666 startMove(inputDevice,
b);
670void QWaylandAdwaitaDecoration::processMouseBottom(QWaylandInputDevice *inputDevice,
const QPointF &local,
671 Qt::MouseButtons
b, Qt::KeyboardModifiers mods)
674 if (local.
x() <= margins().
left()) {
695void QWaylandAdwaitaDecoration::processMouseLeft(QWaylandInputDevice *inputDevice,
const QPointF &local,
696 Qt::MouseButtons
b, Qt::KeyboardModifiers mods)
706void QWaylandAdwaitaDecoration::processMouseRight(QWaylandInputDevice *inputDevice,
const QPointF &local,
707 Qt::MouseButtons
b, Qt::KeyboardModifiers mods)
717void QWaylandAdwaitaDecoration::requestRepaint()
const
721 waylandWindow()->decoration()->update();
724 waylandWindow()->window()->requestUpdate();
731#include "moc_qwaylandadwaitadecoration_p.cpp"
IOBluetoothDevice * device
The QColor class provides colors based on RGB, HSV or CMYK values.
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)
static QDBusConnection sessionBus()
Returns a QDBusConnection object opened with the session bus.
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 QtCore\reentrant
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.
State
Specifies the state of this event point.
QString filePath() const
Returns the path of the file system entry this QFileInfo refers to; the path may be absolute or relat...
static QPlatformTheme * platformTheme()
static QString themeName()
static QStringList themeSearchPaths()
static QString fallbackThemeName()
static QIcon fromTheme(const QString &name)
\inmodule QtCore\compares equality \compareswith equality QLine \endcompareswith
iterator insert(const Key &key, const T &value)
bool contains(const Key &key) const
constexpr int left() const noexcept
Returns the left margin.
constexpr int right() const noexcept
Returns the right margin.
static QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *member, Qt::ConnectionType=Qt::AutoConnection)
\threadsafe
The QPainter class performs low-level painting on widgets and other paint devices.
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.
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.
\inmodule QtCore\reentrant
constexpr qreal x() const noexcept
Returns the x coordinate of this point.
constexpr qreal y() const noexcept
Returns the y coordinate of this point.
\inmodule QtCore\reentrant
\inmodule QtCore\reentrant
\inmodule QtCore\reentrant
constexpr void setRight(int pos) noexcept
Sets the right edge of the rectangle to the given x coordinate.
constexpr void setSize(const QSize &s) noexcept
Sets the size of the rectangle to the given size.
constexpr void setLeft(int pos) noexcept
Sets the left edge of the rectangle to the given x coordinate.
\inmodule QtCore \reentrant
constexpr QSize toSize() const noexcept
Returns an integer based copy of this size.
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.
\macro QT_RESTRICTED_CAST_FROM_ASCII
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.
bool isEmpty() const noexcept
Returns true if the string has no characters; otherwise returns false.
QString arg(qlonglong a, int fieldwidth=0, int base=10, QChar fillChar=u' ') const
const QChar at(qsizetype i) const
Returns the character at the given index position in the string.
bool singleShot
whether the timer is a single-shot timer
static bool handleCloseEvent(QWindow *window)
QWaylandWindow * waylandWindow() const
void setMouseButtons(Qt::MouseButtons mb)
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
void paint(QPaintDevice *device) override
QMargins margins(MarginsType marginsType=Full) const override
virtual bool move(QWaylandInputDevice *)
QString windowTitle() const
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]
const QStyleOptionButton * btn
[3]
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
DBusConnection * connection
EGLOutputLayerEXT EGLint EGLAttrib value
[5]
#define Q_LOGGING_CATEGORY(name,...)
#define qCWarning(category,...)
#define qCDebug(category,...)
Qt::MouseButtons m_buttons
static QString themeName()
static bool contains(const QJsonArray &haystack, unsigned needle)
GLboolean GLboolean GLboolean b
GLenum GLuint GLintptr GLsizeiptr size
[1]
GLdouble GLdouble GLdouble GLdouble top
GLuint GLsizei const GLchar * message
GLsizei const GLchar *const * path
QScopeGuard< typename std::decay< F >::type > qScopeGuard(F &&f)
[qScopeGuard]
#define QStringLiteral(str)
static double currentTime()
QT_BEGIN_NAMESPACE constexpr const wchar_t * themeNames[]
QFutureWatcher< int > watcher
\inmodule QtCore \reentrant