summaryrefslogtreecommitdiffstats
path: root/src/plugins/decorations/adwaita/qwaylandadwaitadecoration_p.h
blob: 34874e08859a6f3d55136ef16dc59942ecfdadbf (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
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