1// Copyright (C) 2016 Robin Burchell <robin.burchell@viroteck.net>
2// Copyright (C) 2016 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#include <QtGui/QCursor>
6#include <QtGui/QPainter>
7#include <QtGui/QPainterPath>
8#include <QtGui/QPalette>
9#include <QtGui/QLinearGradient>
10#include <QtGui/QPainterPath>
11
12#include <qpa/qwindowsysteminterface.h>
13
14#include <QtWaylandClient/private/qwaylanddecorationplugin_p.h>
15#include <QtWaylandClient/private/qwaylandabstractdecoration_p.h>
16#include <QtWaylandClient/private/qwaylandwindow_p.h>
17#include <QtWaylandClient/private/qwaylandshellsurface_p.h>
18
19QT_BEGIN_NAMESPACE
20
21namespace QtWaylandClient {
22
23#define BUTTON_SPACING 5
24#define BUTTON_WIDTH 18
25#define BUTTONS_RIGHT_MARGIN 8
26
27enum Button
28{
29 None,
30 Close,
31 Maximize,
32 Minimize
33};
34
35class Q_WAYLANDCLIENT_EXPORT QWaylandBradientDecoration : public QWaylandAbstractDecoration
36{
37public:
38 QWaylandBradientDecoration();
39protected:
40 QMargins margins(MarginsType marginsType = Full) const override;
41 void paint(QPaintDevice *device) override;
42 bool handleMouse(QWaylandInputDevice *inputDevice, const QPointF &local, const QPointF &global,Qt::MouseButtons b,Qt::KeyboardModifiers mods) override;
43 bool handleTouch(QWaylandInputDevice *inputDevice, const QPointF &local, const QPointF &global, QEventPoint::State state, Qt::KeyboardModifiers mods) override;
44private:
45 enum class PointerType {
46 Mouse,
47 Touch
48 };
49
50 void processPointerTop(QWaylandInputDevice *inputDevice, const QPointF &local, Qt::MouseButtons b,Qt::KeyboardModifiers mods, PointerType type);
51 void processPointerBottom(QWaylandInputDevice *inputDevice, const QPointF &local, Qt::MouseButtons b,Qt::KeyboardModifiers mods, PointerType type);
52 void processPointerLeft(QWaylandInputDevice *inputDevice, const QPointF &local, Qt::MouseButtons b,Qt::KeyboardModifiers mods, PointerType type);
53 void processPointerRight(QWaylandInputDevice *inputDevice, const QPointF &local, Qt::MouseButtons b,Qt::KeyboardModifiers mods, PointerType type);
54 bool clickButton(Qt::MouseButtons b, Button btn);
55
56 QRectF closeButtonRect() const;
57 QRectF maximizeButtonRect() const;
58 QRectF minimizeButtonRect() const;
59
60 QStaticText m_windowTitle;
61 Button m_clicking = None;
62};
63
64
65
66QWaylandBradientDecoration::QWaylandBradientDecoration()
67{
68 QTextOption option(Qt::AlignHCenter | Qt::AlignVCenter);
69 option.setWrapMode(QTextOption::NoWrap);
70 m_windowTitle.setTextOption(option);
71 m_windowTitle.setTextFormat(Qt::PlainText);
72}
73
74QRectF QWaylandBradientDecoration::closeButtonRect() const
75{
76 const int windowRight = waylandWindow()->surfaceSize().width() - margins(marginsType: ShadowsOnly).right();
77 return QRectF(windowRight - BUTTON_WIDTH - BUTTON_SPACING * 0 - BUTTONS_RIGHT_MARGIN,
78 (margins().top() - BUTTON_WIDTH) / 2, BUTTON_WIDTH, BUTTON_WIDTH);
79}
80
81QRectF QWaylandBradientDecoration::maximizeButtonRect() const
82{
83 const int windowRight = waylandWindow()->surfaceSize().width() - margins(marginsType: ShadowsOnly).right();
84 return QRectF(windowRight - BUTTON_WIDTH * 2 - BUTTON_SPACING * 1 - BUTTONS_RIGHT_MARGIN,
85 (margins().top() - BUTTON_WIDTH) / 2, BUTTON_WIDTH, BUTTON_WIDTH);
86}
87
88QRectF QWaylandBradientDecoration::minimizeButtonRect() const
89{
90 const int windowRight = waylandWindow()->surfaceSize().width() - margins(marginsType: ShadowsOnly).right();
91 return QRectF(windowRight - BUTTON_WIDTH * 3 - BUTTON_SPACING * 2 - BUTTONS_RIGHT_MARGIN,
92 (margins().top() - BUTTON_WIDTH) / 2, BUTTON_WIDTH, BUTTON_WIDTH);
93}
94
95QMargins QWaylandBradientDecoration::margins(MarginsType marginsType) const
96{
97 if (marginsType == ShadowsOnly)
98 return QMargins();
99
100 return QMargins(3, 30, 3, 3);
101}
102
103void QWaylandBradientDecoration::paint(QPaintDevice *device)
104{
105 bool active = window()->handle()->isActive();
106 QRect wg = QRect(QPoint(), waylandWindow()->surfaceSize()).marginsRemoved(margins: margins(marginsType: ShadowsOnly));
107 QRect cg = wg.marginsRemoved(margins: margins(marginsType: ShadowsExcluded));
108 QRect clips[] =
109 {
110 QRect(wg.left(), wg.top(), wg.width(), margins(marginsType: ShadowsExcluded).top()),
111 QRect(wg.left(), cg.bottom() + 1, wg.width(), margins(marginsType: ShadowsExcluded).bottom()),
112 QRect(wg.left(), cg.top(), margins(marginsType: ShadowsExcluded).left(), cg.height()),
113 QRect(cg.right() + 1, cg.top(), margins(marginsType: ShadowsExcluded).right(), cg.height())
114 };
115
116 QRect top = clips[0];
117
118 QPalette palette;
119 const QColor foregroundColor = palette.color(cg: QPalette::Active, cr: QPalette::WindowText);
120 const QColor backgroundColor = palette.color(cg: QPalette::Active, cr: QPalette::Window);
121 const QColor foregroundInactiveColor = palette.color(cg: QPalette::Disabled, cr: QPalette::WindowText);
122
123 QPainter p(device);
124 p.setRenderHint(hint: QPainter::Antialiasing);
125
126 // Title bar
127 QPainterPath roundedRect;
128 roundedRect.addRoundedRect(rect: wg, xRadius: 3, yRadius: 3);
129 for (int i = 0; i < 4; ++i) {
130 p.save();
131 p.setClipRect(clips[i]);
132 p.fillPath(path: roundedRect, brush: backgroundColor);
133 p.restore();
134 }
135
136 // Window icon
137 QIcon icon = waylandWindow()->windowIcon();
138 if (!icon.isNull()) {
139 QRectF iconRect(0, 0, 22, 22);
140 iconRect.adjust(xp1: margins().left() + BUTTON_SPACING, yp1: 4,
141 xp2: margins().left() + BUTTON_SPACING, yp2: 4),
142 icon.paint(painter: &p, rect: iconRect.toRect());
143 }
144
145 // Window title
146 QString windowTitleText = waylandWindow()->windowTitle();
147 if (!windowTitleText.isEmpty()) {
148 if (m_windowTitle.text() != windowTitleText) {
149 m_windowTitle.setText(windowTitleText);
150 m_windowTitle.prepare();
151 }
152
153 QRect titleBar = top;
154 titleBar.setLeft(margins().left() + BUTTON_SPACING +
155 (icon.isNull() ? 0 : 22 + BUTTON_SPACING));
156 titleBar.setRight(minimizeButtonRect().left() - BUTTON_SPACING);
157
158 p.save();
159 p.setClipRect(titleBar);
160 p.setPen(active ? foregroundColor : foregroundInactiveColor);
161 QSizeF size = m_windowTitle.size();
162 int dx = (top.width() - size.width()) /2;
163 int dy = (top.height()- size.height()) /2;
164 QFont font = p.font();
165 font.setPixelSize(14);
166 p.setFont(font);
167 QPoint windowTitlePoint(top.topLeft().x() + dx,
168 top.topLeft().y() + dy);
169 p.drawStaticText(p: windowTitlePoint, staticText: m_windowTitle);
170 p.restore();
171 }
172
173 QRectF rect;
174
175 // Default pen
176 QPen pen(active ? foregroundColor : foregroundInactiveColor);
177 p.setPen(pen);
178
179 // Close button
180 p.save();
181 rect = closeButtonRect();
182 qreal crossSize = rect.height() / 2.3;
183 QPointF crossCenter(rect.center());
184 QRectF crossRect(crossCenter.x() - crossSize / 2, crossCenter.y() - crossSize / 2, crossSize, crossSize);
185 pen.setWidth(2);
186 p.setPen(pen);
187 p.drawLine(p1: crossRect.topLeft(), p2: crossRect.bottomRight());
188 p.drawLine(p1: crossRect.bottomLeft(), p2: crossRect.topRight());
189 p.restore();
190
191 // Maximize button
192 p.save();
193 p.setRenderHint(hint: QPainter::Antialiasing, on: false);
194 rect = maximizeButtonRect().adjusted(xp1: 4, yp1: 5, xp2: -4, yp2: -5);
195 if ((window()->windowStates() & Qt::WindowMaximized)) {
196 qreal inset = 2;
197 QRectF rect1 = rect.adjusted(xp1: inset, yp1: 0, xp2: 0, yp2: -inset);
198 QRectF rect2 = rect.adjusted(xp1: 0, yp1: inset, xp2: -inset, yp2: 0);
199 p.drawRect(rect: rect1);
200 p.setBrush(backgroundColor); // need to cover up some lines from the other rect
201 p.drawRect(rect: rect2);
202 } else {
203 p.drawRect(rect);
204 p.drawLine(x1: rect.left(), y1: rect.top() + 1, x2: rect.right(), y2: rect.top() + 1);
205 }
206 p.restore();
207
208 // Minimize button
209 p.save();
210 p.setRenderHint(hint: QPainter::Antialiasing, on: false);
211 rect = minimizeButtonRect().adjusted(xp1: 5, yp1: 5, xp2: -5, yp2: -5);
212 pen.setWidth(2);
213 p.setPen(pen);
214 p.drawLine(p1: rect.bottomLeft(), p2: rect.bottomRight());
215 p.restore();
216}
217
218bool QWaylandBradientDecoration::clickButton(Qt::MouseButtons b, Button btn)
219{
220 if (isLeftClicked(newMouseButtonState: b)) {
221 m_clicking = btn;
222 return false;
223 } else if (isLeftReleased(newMouseButtonState: b)) {
224 if (m_clicking == btn) {
225 m_clicking = None;
226 return true;
227 } else {
228 m_clicking = None;
229 }
230 }
231 return false;
232}
233
234bool QWaylandBradientDecoration::handleMouse(QWaylandInputDevice *inputDevice, const QPointF &local, const QPointF &global, Qt::MouseButtons b, Qt::KeyboardModifiers mods)
235
236{
237 Q_UNUSED(global);
238
239 // Figure out what area mouse is in
240 QSize ss = waylandWindow()->surfaceSize();
241 if (local.y() <= margins().top()) {
242 processPointerTop(inputDevice, local, b, mods, type: PointerType::Mouse);
243 } else if (local.y() >= ss.height() - margins().bottom()) {
244 processPointerBottom(inputDevice, local, b, mods, type: PointerType::Mouse);
245 } else if (local.x() <= margins().left()) {
246 processPointerLeft(inputDevice, local, b, mods, type: PointerType::Mouse);
247 } else if (local.x() >= ss.width() - margins().right()) {
248 processPointerRight(inputDevice, local, b, mods, type: PointerType::Mouse);
249 } else {
250#if QT_CONFIG(cursor)
251 waylandWindow()->restoreMouseCursor(device: inputDevice);
252#endif
253 setMouseButtons(b);
254 return false;
255 }
256
257 setMouseButtons(b);
258 return true;
259}
260
261bool QWaylandBradientDecoration::handleTouch(QWaylandInputDevice *inputDevice, const QPointF &local, const QPointF &global, QEventPoint::State state, Qt::KeyboardModifiers mods)
262{
263 Q_UNUSED(global);
264 QSize ss = waylandWindow()->surfaceSize();
265
266 bool handled = state == QEventPoint::Pressed;
267 if (handled) {
268 if (local.y() <= margins().top()) {
269 processPointerTop(inputDevice, local, b: Qt::LeftButton, mods, type: PointerType::Touch);
270 } else if (local.y() >= ss.height() - margins().bottom()) {
271 processPointerBottom(inputDevice, local, b: Qt::LeftButton, mods, type: PointerType::Touch);
272 } else if (local.x() <= margins().left()) {
273 processPointerLeft(inputDevice, local, b: Qt::LeftButton, mods, type: PointerType::Touch);
274 } else if (local.x() >= ss.width() - margins().right()) {
275 processPointerRight(inputDevice, local, b: Qt::LeftButton, mods, type: PointerType::Touch);
276 } else {
277 handled = false;
278 }
279 }
280
281 return handled;
282}
283
284void QWaylandBradientDecoration::processPointerTop(QWaylandInputDevice *inputDevice,
285 const QPointF &local,
286 Qt::MouseButtons b,
287 Qt::KeyboardModifiers mods,
288 PointerType type)
289{
290#if !QT_CONFIG(cursor)
291 Q_UNUSED(type);
292#endif
293
294 QSize ss = waylandWindow()->surfaceSize();
295 Q_UNUSED(mods);
296 if (local.y() <= margins().bottom()) {
297 if (local.x() <= margins().left()) {
298 //top left bit
299#if QT_CONFIG(cursor)
300 if (type == PointerType::Mouse)
301 waylandWindow()->setMouseCursor(device: inputDevice, cursor: Qt::SizeFDiagCursor);
302#endif
303 startResize(inputDevice, edges: Qt::TopEdge | Qt::LeftEdge, buttons: b);
304 } else if (local.x() >= ss.width() - margins().right()) {
305 //top right bit
306#if QT_CONFIG(cursor)
307 if (type == PointerType::Mouse)
308 waylandWindow()->setMouseCursor(device: inputDevice, cursor: Qt::SizeBDiagCursor);
309#endif
310 startResize(inputDevice, edges: Qt::TopEdge | Qt::RightEdge, buttons: b);
311 } else {
312 //top resize bit
313#if QT_CONFIG(cursor)
314 if (type == PointerType::Mouse)
315 waylandWindow()->setMouseCursor(device: inputDevice, cursor: Qt::SplitVCursor);
316#endif
317 startResize(inputDevice, edges: Qt::TopEdge, buttons: b);
318 }
319 } else if (local.x() <= margins().left()) {
320 processPointerLeft(inputDevice, local, b, mods, type);
321 } else if (local.x() >= ss.width() - margins().right()) {
322 processPointerRight(inputDevice, local, b, mods, type);
323 } else if (isRightClicked(newMouseButtonState: b)) {
324 showWindowMenu(inputDevice);
325 } else if (closeButtonRect().contains(p: local)) {
326 if (type == PointerType::Touch || clickButton(b, btn: Close))
327 QWindowSystemInterface::handleCloseEvent(window: window());
328 } else if (maximizeButtonRect().contains(p: local)) {
329 if (type == PointerType::Touch || clickButton(b, btn: Maximize))
330 window()->setWindowStates(window()->windowStates() ^ Qt::WindowMaximized);
331 } else if (minimizeButtonRect().contains(p: local)) {
332 if (type == PointerType::Touch || clickButton(b, btn: Minimize))
333 window()->setWindowState(Qt::WindowMinimized);
334 } else {
335#if QT_CONFIG(cursor)
336 if (type == PointerType::Mouse)
337 waylandWindow()->restoreMouseCursor(device: inputDevice);
338#endif
339 startMove(inputDevice,buttons: b);
340 }
341}
342
343void QWaylandBradientDecoration::processPointerBottom(QWaylandInputDevice *inputDevice,
344 const QPointF &local,
345 Qt::MouseButtons b,
346 Qt::KeyboardModifiers mods,
347 PointerType type)
348{
349 Q_UNUSED(mods);
350#if !QT_CONFIG(cursor)
351 Q_UNUSED(type);
352#endif
353
354 QSize ss = waylandWindow()->surfaceSize();
355 if (local.x() <= margins().left()) {
356 //bottom left bit
357#if QT_CONFIG(cursor)
358 if (type == PointerType::Mouse)
359 waylandWindow()->setMouseCursor(device: inputDevice, cursor: Qt::SizeBDiagCursor);
360#endif
361 startResize(inputDevice, edges: Qt::BottomEdge | Qt::LeftEdge, buttons: b);
362 } else if (local.x() >= ss.width() - margins().right()) {
363 //bottom right bit
364#if QT_CONFIG(cursor)
365 if (type == PointerType::Mouse)
366 waylandWindow()->setMouseCursor(device: inputDevice, cursor: Qt::SizeFDiagCursor);
367#endif
368 startResize(inputDevice, edges: Qt::BottomEdge | Qt::RightEdge, buttons: b);
369 } else {
370 //bottom bit
371#if QT_CONFIG(cursor)
372 if (type == PointerType::Mouse)
373 waylandWindow()->setMouseCursor(device: inputDevice, cursor: Qt::SizeVerCursor);
374#endif
375 startResize(inputDevice, edges: Qt::BottomEdge, buttons: b);
376 }
377}
378
379void QWaylandBradientDecoration::processPointerLeft(QWaylandInputDevice *inputDevice,
380 const QPointF &local,
381 Qt::MouseButtons b,
382 Qt::KeyboardModifiers mods,
383 PointerType type)
384{
385 Q_UNUSED(local);
386 Q_UNUSED(mods);
387#if QT_CONFIG(cursor)
388 if (type == PointerType::Mouse)
389 waylandWindow()->setMouseCursor(device: inputDevice, cursor: Qt::SizeHorCursor);
390#else
391 Q_UNUSED(type);
392#endif
393 startResize(inputDevice, edges: Qt::LeftEdge, buttons: b);
394}
395
396void QWaylandBradientDecoration::processPointerRight(QWaylandInputDevice *inputDevice,
397 const QPointF &local,
398 Qt::MouseButtons b,
399 Qt::KeyboardModifiers mods,
400 PointerType type)
401{
402 Q_UNUSED(local);
403 Q_UNUSED(mods);
404#if QT_CONFIG(cursor)
405 if (type == PointerType::Mouse)
406 waylandWindow()->setMouseCursor(device: inputDevice, cursor: Qt::SizeHorCursor);
407#else
408 Q_UNUSED(type);
409#endif
410 startResize(inputDevice, edges: Qt::RightEdge, buttons: b);
411}
412
413class QWaylandBradientDecorationPlugin : public QWaylandDecorationPlugin
414{
415 Q_OBJECT
416 Q_PLUGIN_METADATA(IID QWaylandDecorationFactoryInterface_iid FILE "bradient.json")
417public:
418 QWaylandAbstractDecoration *create(const QString&, const QStringList&) override;
419};
420
421QWaylandAbstractDecoration *QWaylandBradientDecorationPlugin::create(const QString& system, const QStringList& paramList)
422{
423 Q_UNUSED(paramList);
424 Q_UNUSED(system);
425 return new QWaylandBradientDecoration();
426}
427
428}
429
430QT_END_NAMESPACE
431
432#include "main.moc"
433

Provided by KDAB

Privacy Policy
Start learning QML with our Intro Training
Find out more

source code of qtwayland/src/plugins/decorations/bradient/main.cpp