1 | // Copyright (C) 2023 Jan Grulich <jgrulich@redhat.com> |
2 | // Copyright (C) 2023 The Qt Company Ltd. |
3 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only |
4 | |
5 | #ifndef QWAYLANDADWAITADECORATION_P_H |
6 | #define QWAYLANDADWAITADECORATION_P_H |
7 | |
8 | #include <QtWaylandClient/private/qwaylandabstractdecoration_p.h> |
9 | |
10 | #include <QtCore/QDateTime> |
11 | |
12 | QT_BEGIN_NAMESPACE |
13 | |
14 | class QDBusVariant; |
15 | class QPainter; |
16 | |
17 | namespace QtWaylandClient { |
18 | |
19 | // |
20 | // INFO |
21 | // ------------- |
22 | // |
23 | // This is a Qt decoration plugin implementing Adwaita-like (GNOME) client-side |
24 | // window decorations. It uses xdg-desktop-portal to get the user configuration. |
25 | // This plugin was originally part of QGnomePlatform and later made a separate |
26 | // project named QAdwaitaDecorations. |
27 | // |
28 | // INFO: How SVG icons are used here? |
29 | // We try to find an SVG icon for a particular button from the current icon theme. |
30 | // This icon is then opened as a file, it's content saved and later loaded to be |
31 | // painted with QSvgRenderer, but before it's painted, we try to find following |
32 | // patterns: |
33 | // 1) fill=[\"']#[0-9A-F]{6}[\"'] |
34 | // 2) fill:#[0-9A-F]{6} |
35 | // 3) fill=[\"']currentColor[\"'] |
36 | // The color in this case doesn't match the theme and is replaced by Foreground color. |
37 | // |
38 | // FIXME/TODO: |
39 | // This plugin currently have all the colors for the decorations hardcoded. |
40 | // There might be a way to get these from GTK/libadwaita (not sure), but problem is |
41 | // we want Gtk4 version and using Gtk4 together with QGtk3Theme from QtBase that links |
42 | // to Gtk3 will not work out. Possibly in future we can make a QGtk4Theme providing us |
43 | // what we need to paint the decorations without having to deal with the colors ourself. |
44 | // |
45 | // TODO: Implement shadows |
46 | |
47 | |
48 | class QWaylandAdwaitaDecoration : public QWaylandAbstractDecoration |
49 | { |
50 | Q_OBJECT |
51 | public: |
52 | enum ColorType { |
53 | Background, |
54 | BackgroundInactive, |
55 | Foreground, |
56 | ForegroundInactive, |
57 | Border, |
58 | BorderInactive, |
59 | ButtonBackground, |
60 | ButtonBackgroundInactive, |
61 | HoveredButtonBackground, |
62 | PressedButtonBackground |
63 | }; |
64 | |
65 | enum Placement { |
66 | Left = 0, |
67 | Right = 1 |
68 | }; |
69 | |
70 | enum Button { |
71 | None = 0x0, |
72 | Close = 0x1, |
73 | Minimize = 0x02, |
74 | Maximize = 0x04 |
75 | }; |
76 | Q_DECLARE_FLAGS(Buttons, Button); |
77 | |
78 | enum ButtonIcon { |
79 | CloseIcon, |
80 | MinimizeIcon, |
81 | MaximizeIcon, |
82 | RestoreIcon |
83 | }; |
84 | |
85 | QWaylandAdwaitaDecoration(); |
86 | virtual ~QWaylandAdwaitaDecoration() = default; |
87 | |
88 | protected: |
89 | QMargins margins(MarginsType marginsType = Full) const override; |
90 | void paint(QPaintDevice *device) override; |
91 | bool handleMouse(QWaylandInputDevice *inputDevice, const QPointF &local, const QPointF &global, |
92 | Qt::MouseButtons b, Qt::KeyboardModifiers mods) override; |
93 | bool handleTouch(QWaylandInputDevice *inputDevice, const QPointF &local, const QPointF &global, |
94 | QEventPoint::State state, Qt::KeyboardModifiers mods) override; |
95 | |
96 | private Q_SLOTS: |
97 | void settingChanged(const QString &group, const QString &key, const QDBusVariant &value); |
98 | |
99 | private: |
100 | // Makes a call to xdg-desktop-portal (Settings) to load initial configuration |
101 | void loadConfiguration(); |
102 | // Updates color scheme from light to dark and vice-versa |
103 | void updateColors(bool isDark); |
104 | // Updates titlebar layout with position and button order |
105 | void updateTitlebarLayout(const QString &layout); |
106 | |
107 | // Returns a bounding rect for a given button type |
108 | QRectF buttonRect(Button button) const; |
109 | // Draw given button type using SVG icon (when found) or fallback to QPixmap icon |
110 | void drawButton(Button button, QPainter *painter); |
111 | |
112 | // Returns color for given type and button |
113 | QColor color(ColorType type, Button button = None); |
114 | |
115 | // Returns whether the left button was clicked i.e. pressed and released |
116 | bool clickButton(Qt::MouseButtons b, Button btn); |
117 | // Returns whether the left button was double-clicked |
118 | bool doubleClickButton(Qt::MouseButtons b, const QPointF &local, const QDateTime ¤tTime); |
119 | // Updates button hover state |
120 | void updateButtonHoverState(Button hoveredButton); |
121 | |
122 | void processMouseTop(QWaylandInputDevice *inputDevice, const QPointF &local, Qt::MouseButtons b, |
123 | Qt::KeyboardModifiers mods); |
124 | void processMouseBottom(QWaylandInputDevice *inputDevice, const QPointF &local, |
125 | Qt::MouseButtons b, Qt::KeyboardModifiers mods); |
126 | void processMouseLeft(QWaylandInputDevice *inputDevice, const QPointF &local, |
127 | Qt::MouseButtons b, Qt::KeyboardModifiers mods); |
128 | void processMouseRight(QWaylandInputDevice *inputDevice, const QPointF &local, |
129 | Qt::MouseButtons b, Qt::KeyboardModifiers mods); |
130 | // Request to repaint the decorations. This will be invoked when button hover changes or |
131 | // when there is a setting change (e.g. layout change). |
132 | void requestRepaint() const; |
133 | |
134 | // Button states |
135 | Button m_clicking = None; |
136 | Buttons m_hoveredButtons = None; |
137 | QDateTime m_lastButtonClick; |
138 | QPointF m_lastButtonClickPosition; |
139 | |
140 | // Configuration |
141 | QMap<Button, uint> m_buttons; |
142 | QMap<ColorType, QColor> m_colors; |
143 | QMap<ButtonIcon, QString> m_icons; |
144 | std::unique_ptr<QFont> m_font; |
145 | Placement m_placement = Right; |
146 | |
147 | QStaticText m_windowTitle; |
148 | }; |
149 | Q_DECLARE_OPERATORS_FOR_FLAGS(QWaylandAdwaitaDecoration::Buttons) |
150 | |
151 | } // namespace QtWaylandClient |
152 | |
153 | QT_END_NAMESPACE |
154 | |
155 | #endif // QWAYLANDADWAITADECORATION_P_H |
156 | |