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

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