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
54 QT_BEGIN_NAMESPACE
55
56 namespace QtWaylandClient {
57
58 #define BUTTON_SPACING 5
59 #define BUTTON_WIDTH 18
60 #define BUTTONS_RIGHT_MARGIN 8
61
62 enum Button
63 {
64 None,
65 Close,
66 Maximize,
67 Minimize
68 };
69
70 class Q_WAYLAND_CLIENT_EXPORT QWaylandBradientDecoration : public QWaylandAbstractDecoration
71 {
72 public:
73 QWaylandBradientDecoration();
74 protected:
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;
79 private:
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
QWaylandBradientDecoration()99 QWaylandBradientDecoration::QWaylandBradientDecoration()
100 {
101 QPalette palette;
102 m_foregroundColor = palette.color(QPalette::Active, QPalette::WindowText);
103 m_backgroundColor = palette.color(QPalette::Active, QPalette::Window);
104 m_foregroundInactiveColor = palette.color(QPalette::Disabled, QPalette::WindowText);
105
106 QTextOption option(Qt::AlignHCenter | Qt::AlignVCenter);
107 option.setWrapMode(QTextOption::NoWrap);
108 m_windowTitle.setTextOption(option);
109 }
110
closeButtonRect() const111 QRectF 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
maximizeButtonRect() const118 QRectF 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
minimizeButtonRect() const125 QRectF 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
margins() const132 QMargins QWaylandBradientDecoration::margins() const
133 {
134 return QMargins(3, 30, 3, 3);
135 }
136
paint(QPaintDevice * device)137 void 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(QPainter::Antialiasing);
153
154 // Title bar
155 QPainterPath roundedRect;
156 roundedRect.addRoundedRect(wg, 3, 3);
157 for (int i = 0; i < 4; ++i) {
158 p.save();
159 p.setClipRect(clips[i]);
160 p.fillPath(roundedRect, m_backgroundColor);
161 p.restore();
162 }
163
164 // Window icon
165 QIcon icon = waylandWindow()->windowIcon();
166 if (!icon.isNull()) {
167 QPixmap pixmap = icon.pixmap(QSize(128, 128));
168 QPixmap scaled = pixmap.scaled(22, 22, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
169
170 QRectF iconRect(0, 0, 22, 22);
171 p.drawPixmap(iconRect.adjusted(margins().left() + BUTTON_SPACING, 4,
172 margins().left() + BUTTON_SPACING, 4),
173 scaled, iconRect);
174 }
175
176 // Window title
177 QString windowTitleText = window()->title();
178 if (!windowTitleText.isEmpty()) {
179 if (m_windowTitle.text() != windowTitleText) {
180 m_windowTitle.setText(windowTitleText);
181 m_windowTitle.prepare();
182 }
183
184 QRect titleBar = top;
185 titleBar.setLeft(margins().left() + BUTTON_SPACING +
186 (icon.isNull() ? 0 : 22 + BUTTON_SPACING));
187 titleBar.setRight(minimizeButtonRect().left() - BUTTON_SPACING);
188
189 p.save();
190 p.setClipRect(titleBar);
191 p.setPen(active ? m_foregroundColor : m_foregroundInactiveColor);
192 QSizeF size = m_windowTitle.size();
193 int dx = (top.width() - size.width()) /2;
194 int dy = (top.height()- size.height()) /2;
195 QFont font = p.font();
196 font.setPixelSize(14);
197 p.setFont(font);
198 QPoint windowTitlePoint(top.topLeft().x() + dx,
199 top.topLeft().y() + dy);
200 p.drawStaticText(windowTitlePoint, m_windowTitle);
201 p.restore();
202 }
203
204 QRectF rect;
205
206 // Default pen
207 QPen pen(active ? m_foregroundColor : m_foregroundInactiveColor);
208 p.setPen(pen);
209
210 // Close button
211 p.save();
212 rect = closeButtonRect();
213 qreal crossSize = rect.height() / 2.3;
214 QPointF crossCenter(rect.center());
215 QRectF crossRect(crossCenter.x() - crossSize / 2, crossCenter.y() - crossSize / 2, crossSize, crossSize);
216 pen.setWidth(2);
217 p.setPen(pen);
218 p.drawLine(crossRect.topLeft(), crossRect.bottomRight());
219 p.drawLine(crossRect.bottomLeft(), crossRect.topRight());
220 p.restore();
221
222 // Maximize button
223 p.save();
224 p.setRenderHint(QPainter::Antialiasing, false);
225 rect = maximizeButtonRect().adjusted(4, 5, -4, -5);
226 if ((window()->windowStates() & Qt::WindowMaximized)) {
227 qreal inset = 2;
228 QRectF rect1 = rect.adjusted(inset, 0, 0, -inset);
229 QRectF rect2 = rect.adjusted(0, inset, -inset, 0);
230 p.drawRect(rect1);
231 p.setBrush(m_backgroundColor); // need to cover up some lines from the other rect
232 p.drawRect(rect2);
233 } else {
234 p.drawRect(rect);
235 p.drawLine(rect.left(), rect.top() + 1, rect.right(), rect.top() + 1);
236 }
237 p.restore();
238
239 // Minimize button
240 p.save();
241 p.setRenderHint(QPainter::Antialiasing, false);
242 rect = minimizeButtonRect().adjusted(5, 5, -5, -5);
243 pen.setWidth(2);
244 p.setPen(pen);
245 p.drawLine(rect.bottomLeft(), rect.bottomRight());
246 p.restore();
247 }
248
clickButton(Qt::MouseButtons b,Button btn)249 bool QWaylandBradientDecoration::clickButton(Qt::MouseButtons b, Button btn)
250 {
251 if (isLeftClicked(b)) {
252 m_clicking = btn;
253 return false;
254 } else if (isLeftReleased(b)) {
255 if (m_clicking == btn) {
256 m_clicking = None;
257 return true;
258 } else {
259 m_clicking = None;
260 }
261 }
262 return false;
263 }
264
handleMouse(QWaylandInputDevice * inputDevice,const QPointF & local,const QPointF & global,Qt::MouseButtons b,Qt::KeyboardModifiers mods)265 bool QWaylandBradientDecoration::handleMouse(QWaylandInputDevice *inputDevice, const QPointF &local, const QPointF &global, Qt::MouseButtons b, Qt::KeyboardModifiers mods)
266
267 {
268 Q_UNUSED(global);
269
270 // Figure out what area mouse is in
271 QRect wg = waylandWindow()->windowContentGeometry();
272 if (local.y() <= wg.top() + margins().top()) {
273 processMouseTop(inputDevice,local,b,mods);
274 } else if (local.y() > wg.bottom() - margins().bottom()) {
275 processMouseBottom(inputDevice,local,b,mods);
276 } else if (local.x() <= wg.left() + margins().left()) {
277 processMouseLeft(inputDevice,local,b,mods);
278 } else if (local.x() > wg.right() - margins().right()) {
279 processMouseRight(inputDevice,local,b,mods);
280 } else {
281 #if QT_CONFIG(cursor)
282 waylandWindow()->restoreMouseCursor(inputDevice);
283 #endif
284 setMouseButtons(b);
285 return false;
286 }
287
288 setMouseButtons(b);
289 return true;
290 }
291
handleTouch(QWaylandInputDevice * inputDevice,const QPointF & local,const QPointF & global,Qt::TouchPointState state,Qt::KeyboardModifiers mods)292 bool QWaylandBradientDecoration::handleTouch(QWaylandInputDevice *inputDevice, const QPointF &local, const QPointF &global, Qt::TouchPointState state, Qt::KeyboardModifiers mods)
293 {
294 Q_UNUSED(inputDevice);
295 Q_UNUSED(global);
296 Q_UNUSED(mods);
297 bool handled = state == Qt::TouchPointPressed;
298 if (handled) {
299 if (closeButtonRect().contains(local))
300 QWindowSystemInterface::handleCloseEvent(window());
301 else if (maximizeButtonRect().contains(local))
302 window()->setWindowStates(window()->windowStates() ^ Qt::WindowMaximized);
303 else if (minimizeButtonRect().contains(local))
304 window()->setWindowState(Qt::WindowMinimized);
305 else if (local.y() <= margins().top())
306 waylandWindow()->shellSurface()->move(inputDevice);
307 else
308 handled = false;
309 }
310
311 return handled;
312 }
313
processMouseTop(QWaylandInputDevice * inputDevice,const QPointF & local,Qt::MouseButtons b,Qt::KeyboardModifiers mods)314 void QWaylandBradientDecoration::processMouseTop(QWaylandInputDevice *inputDevice, const QPointF &local, Qt::MouseButtons b, Qt::KeyboardModifiers mods)
315 {
316 QRect wg = waylandWindow()->windowContentGeometry();
317 Q_UNUSED(mods);
318 if (local.y() <= wg.top() + margins().bottom()) {
319 if (local.x() <= margins().left()) {
320 //top left bit
321 #if QT_CONFIG(cursor)
322 waylandWindow()->setMouseCursor(inputDevice, Qt::SizeFDiagCursor);
323 #endif
324 startResize(inputDevice, Qt::TopEdge | Qt::LeftEdge, b);
325 } else if (local.x() > wg.right() - margins().right()) {
326 //top right bit
327 #if QT_CONFIG(cursor)
328 waylandWindow()->setMouseCursor(inputDevice, Qt::SizeBDiagCursor);
329 #endif
330 startResize(inputDevice, Qt::TopEdge | Qt::RightEdge, b);
331 } else {
332 //top resize bit
333 #if QT_CONFIG(cursor)
334 waylandWindow()->setMouseCursor(inputDevice, Qt::SplitVCursor);
335 #endif
336 startResize(inputDevice, Qt::TopEdge, b);
337 }
338 } else if (local.x() <= wg.left() + margins().left()) {
339 processMouseLeft(inputDevice, local, b, mods);
340 } else if (local.x() > wg.right() - margins().right()) {
341 processMouseRight(inputDevice, local, b, mods);
342 } else if (isRightClicked(b)) {
343 showWindowMenu(inputDevice);
344 } else if (closeButtonRect().contains(local)) {
345 if (clickButton(b, Close))
346 QWindowSystemInterface::handleCloseEvent(window());
347 } else if (maximizeButtonRect().contains(local)) {
348 if (clickButton(b, Maximize))
349 window()->setWindowStates(window()->windowStates() ^ Qt::WindowMaximized);
350 } else if (minimizeButtonRect().contains(local)) {
351 if (clickButton(b, Minimize))
352 window()->setWindowState(Qt::WindowMinimized);
353 } else {
354 #if QT_CONFIG(cursor)
355 waylandWindow()->restoreMouseCursor(inputDevice);
356 #endif
357 startMove(inputDevice,b);
358 }
359 }
360
processMouseBottom(QWaylandInputDevice * inputDevice,const QPointF & local,Qt::MouseButtons b,Qt::KeyboardModifiers mods)361 void QWaylandBradientDecoration::processMouseBottom(QWaylandInputDevice *inputDevice, const QPointF &local, Qt::MouseButtons b, Qt::KeyboardModifiers mods)
362 {
363 Q_UNUSED(mods);
364 if (local.x() <= margins().left()) {
365 //bottom left bit
366 #if QT_CONFIG(cursor)
367 waylandWindow()->setMouseCursor(inputDevice, Qt::SizeBDiagCursor);
368 #endif
369 startResize(inputDevice, Qt::BottomEdge | Qt::LeftEdge, b);
370 } else if (local.x() > window()->width() + margins().left()) {
371 //bottom right bit
372 #if QT_CONFIG(cursor)
373 waylandWindow()->setMouseCursor(inputDevice, Qt::SizeFDiagCursor);
374 #endif
375 startResize(inputDevice, Qt::BottomEdge | Qt::RightEdge, b);
376 } else {
377 //bottom bit
378 #if QT_CONFIG(cursor)
379 waylandWindow()->setMouseCursor(inputDevice, Qt::SplitVCursor);
380 #endif
381 startResize(inputDevice, Qt::BottomEdge, b);
382 }
383 }
384
processMouseLeft(QWaylandInputDevice * inputDevice,const QPointF & local,Qt::MouseButtons b,Qt::KeyboardModifiers mods)385 void QWaylandBradientDecoration::processMouseLeft(QWaylandInputDevice *inputDevice, const QPointF &local, Qt::MouseButtons b, Qt::KeyboardModifiers mods)
386 {
387 Q_UNUSED(local);
388 Q_UNUSED(mods);
389 #if QT_CONFIG(cursor)
390 waylandWindow()->setMouseCursor(inputDevice, Qt::SplitHCursor);
391 #endif
392 startResize(inputDevice, Qt::LeftEdge, b);
393 }
394
processMouseRight(QWaylandInputDevice * inputDevice,const QPointF & local,Qt::MouseButtons b,Qt::KeyboardModifiers mods)395 void QWaylandBradientDecoration::processMouseRight(QWaylandInputDevice *inputDevice, const QPointF &local, Qt::MouseButtons b, Qt::KeyboardModifiers mods)
396 {
397 Q_UNUSED(local);
398 Q_UNUSED(mods);
399 #if QT_CONFIG(cursor)
400 waylandWindow()->setMouseCursor(inputDevice, Qt::SplitHCursor);
401 #endif
402 startResize(inputDevice, Qt::RightEdge, b);
403 }
404
405 class QWaylandBradientDecorationPlugin : public QWaylandDecorationPlugin
406 {
407 Q_OBJECT
408 Q_PLUGIN_METADATA(IID QWaylandDecorationFactoryInterface_iid FILE "bradient.json")
409 public:
410 QWaylandAbstractDecoration *create(const QString&, const QStringList&) override;
411 };
412
create(const QString & system,const QStringList & paramList)413 QWaylandAbstractDecoration *QWaylandBradientDecorationPlugin::create(const QString& system, const QStringList& paramList)
414 {
415 Q_UNUSED(paramList);
416 Q_UNUSED(system);
417 return new QWaylandBradientDecoration();
418 }
419
420 }
421
422 QT_END_NAMESPACE
423
424 #include "main.moc"
425