Revision control

Copy as Markdown

// 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
// Qt-Security score:significant reason:default
#ifndef QWAYLANDADWAITADECORATION_P_H
#define QWAYLANDADWAITADECORATION_P_H
#include <QtWaylandClient/private/qwaylandabstractdecoration_p.h>
#include <QtGui/qcolor.h>
#include <QtGui/qfont.h>
#include <QtGui/qstatictext.h>
#include <QtCore/QDateTime>
#include <QtCore/qflags.h>
#include <QtCore/qmap.h>
#include <QtCore/qpoint.h>
#include <QtCore/qstring.h>
#include <memory>
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,
Shadow,
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