Revision control
++ // 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 ¤tTime);
++ // 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