1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the demonstration applications of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:BSD$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** BSD License Usage
18** Alternatively, you may use this file under the terms of the BSD license
19** as follows:
20**
21** "Redistribution and use in source and binary forms, with or without
22** modification, are permitted provided that the following conditions are
23** met:
24** * Redistributions of source code must retain the above copyright
25** notice, this list of conditions and the following disclaimer.
26** * Redistributions in binary form must reproduce the above copyright
27** notice, this list of conditions and the following disclaimer in
28** the documentation and/or other materials provided with the
29** distribution.
30** * Neither the name of The Qt Company Ltd nor the names of its
31** contributors may be used to endorse or promote products derived
32** from this software without specific prior written permission.
33**
34**
35** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
36** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
37** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
38** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
39** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
40** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
41** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
42** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
43** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
44** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
45** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
46**
47** $QT_END_LICENSE$
48**
49****************************************************************************/
50
51#include "arthurstyle.h"
52#include "arthurwidgets.h"
53#include <QLayout>
54#include <QPainter>
55#include <QPainterPath>
56#include <QPixmapCache>
57#include <QRadioButton>
58#include <QString>
59#include <QStyleOption>
60#include <QtDebug>
61
62QPixmap cached(const QString &img)
63{
64 QPixmap pm;
65 if (QPixmapCache::find(img, &pm))
66 return pm;
67
68 pm = QPixmap::fromImage(QImage(img), Qt::OrderedDither | Qt::OrderedAlphaDither);
69 if (pm.isNull())
70 return QPixmap();
71
72 QPixmapCache::insert(img, pm);
73 return pm;
74}
75
76
77ArthurStyle::ArthurStyle()
78 : QCommonStyle()
79{
80 Q_INIT_RESOURCE(shared);
81}
82
83
84void ArthurStyle::drawHoverRect(QPainter *painter, const QRect &r) const
85{
86 qreal h = r.height();
87 qreal h2 = r.height() / qreal(2);
88 QPainterPath path;
89 path.addRect(r.x() + h2, r.y() + 0, r.width() - h2 * 2, r.height());
90 path.addEllipse(r.x(), r.y(), h, h);
91 path.addEllipse(r.x() + r.width() - h, r.y(), h, h);
92 path.setFillRule(Qt::WindingFill);
93 painter->setPen(Qt::NoPen);
94 painter->setBrush(QColor(191, 215, 191));
95 painter->setRenderHint(QPainter::Antialiasing);
96 painter->drawPath(path);
97}
98
99
100void ArthurStyle::drawPrimitive(PrimitiveElement element, const QStyleOption *option,
101 QPainter *painter, const QWidget *widget) const
102{
103
104 Q_ASSERT(option);
105 switch (element) {
106 case PE_FrameFocusRect:
107 break;
108
109 case PE_IndicatorRadioButton:
110 if (const QStyleOptionButton *button = qstyleoption_cast<const QStyleOptionButton *>(option)) {
111 bool hover = (button->state & State_Enabled) && (button->state & State_MouseOver);
112 painter->save();
113 QPixmap radio;
114 if (hover)
115 drawHoverRect(painter, widget->rect());
116
117 if (button->state & State_Sunken)
118 radio = cached(":res/images/radiobutton-on.png");
119 else if (button->state & State_On)
120 radio = cached(":res/images/radiobutton_on.png");
121 else
122 radio = cached(":res/images/radiobutton_off.png");
123 painter->drawPixmap(button->rect.topLeft(), radio);
124
125 painter->restore();
126 }
127 break;
128
129 case PE_PanelButtonCommand:
130 if (const QStyleOptionButton *button = qstyleoption_cast<const QStyleOptionButton *>(option)) {
131 bool hover = (button->state & State_Enabled) && (button->state & State_MouseOver);
132
133 painter->save();
134 const QPushButton *pushButton = qobject_cast<const QPushButton *>(widget);
135 Q_ASSERT(pushButton);
136 QWidget *parent = pushButton->parentWidget();
137 if (parent && qobject_cast<QGroupBox *>(parent)) {
138 QLinearGradient lg(0, 0, 0, parent->height());
139 lg.setColorAt(0, QColor(224,224,224));
140 lg.setColorAt(1, QColor(255,255,255));
141 painter->setPen(Qt::NoPen);
142 painter->setBrush(lg);
143 painter->setBrushOrigin(-widget->mapToParent(QPoint(0,0)));
144 painter->drawRect(button->rect);
145 painter->setBrushOrigin(0,0);
146 }
147
148 bool down = (button->state & State_Sunken) || (button->state & State_On);
149
150 QPixmap left, right, mid;
151 if (down) {
152 left = cached(":res/images/button_pressed_cap_left.png");
153 right = cached(":res/images/button_pressed_cap_right.png");
154 mid = cached(":res/images/button_pressed_stretch.png");
155 } else {
156 left = cached(":res/images/button_normal_cap_left.png");
157 right = cached(":res/images/button_normal_cap_right.png");
158 mid = cached(":res/images/button_normal_stretch.png");
159 }
160 painter->drawPixmap(button->rect.topLeft(), left);
161 painter->drawTiledPixmap(QRect(button->rect.x() + left.width(),
162 button->rect.y(),
163 button->rect.width() - left.width() - right.width(),
164 left.height()),
165 mid);
166 painter->drawPixmap(button->rect.x() + button->rect.width() - right.width(),
167 button->rect.y(),
168 right);
169 if (hover)
170 painter->fillRect(widget->rect().adjusted(3,5,-3,-5), QColor(31,127,31,63));
171 painter->restore();
172 }
173 break;
174
175 case PE_FrameGroupBox:
176 if (const QStyleOptionFrame *group
177 = qstyleoption_cast<const QStyleOptionFrame *>(option)) {
178 const QRect &r = group->rect;
179
180 painter->save();
181 int radius = 14;
182 int radius2 = radius*2;
183 QPainterPath clipPath;
184 clipPath.moveTo(radius, 0);
185 clipPath.arcTo(r.right() - radius2, 0, radius2, radius2, 90, -90);
186 clipPath.arcTo(r.right() - radius2, r.bottom() - radius2, radius2, radius2, 0, -90);
187 clipPath.arcTo(r.left(), r.bottom() - radius2, radius2, radius2, 270, -90);
188 clipPath.arcTo(r.left(), r.top(), radius2, radius2, 180, -90);
189 painter->setClipPath(clipPath);
190 QPixmap titleStretch = cached(":res/images/title_stretch.png");
191 QPixmap topLeft = cached(":res/images/groupframe_topleft.png");
192 QPixmap topRight = cached(":res/images/groupframe_topright.png");
193 QPixmap bottomLeft = cached(":res/images/groupframe_bottom_left.png");
194 QPixmap bottomRight = cached(":res/images/groupframe_bottom_right.png");
195 QPixmap leftStretch = cached(":res/images/groupframe_left_stretch.png");
196 QPixmap topStretch = cached(":res/images/groupframe_top_stretch.png");
197 QPixmap rightStretch = cached(":res/images/groupframe_right_stretch.png");
198 QPixmap bottomStretch = cached(":res/images/groupframe_bottom_stretch.png");
199 QLinearGradient lg(0, 0, 0, r.height());
200 lg.setColorAt(0, QColor(224,224,224));
201 lg.setColorAt(1, QColor(255,255,255));
202 painter->setPen(Qt::NoPen);
203 painter->setBrush(lg);
204 painter->drawRect(r.adjusted(0, titleStretch.height()/2, 0, 0));
205 painter->setClipping(false);
206
207 int topFrameOffset = titleStretch.height()/2 - 2;
208 painter->drawPixmap(r.topLeft() + QPoint(0, topFrameOffset), topLeft);
209 painter->drawPixmap(r.topRight() - QPoint(topRight.width()-1, 0)
210 + QPoint(0, topFrameOffset), topRight);
211 painter->drawPixmap(r.bottomLeft() - QPoint(0, bottomLeft.height()-1), bottomLeft);
212 painter->drawPixmap(r.bottomRight() - QPoint(bottomRight.width()-1,
213 bottomRight.height()-1), bottomRight);
214
215 QRect left = r;
216 left.setY(r.y() + topLeft.height() + topFrameOffset);
217 left.setWidth(leftStretch.width());
218 left.setHeight(r.height() - topLeft.height() - bottomLeft.height() - topFrameOffset);
219 painter->drawTiledPixmap(left, leftStretch);
220
221 QRect top = r;
222 top.setX(r.x() + topLeft.width());
223 top.setY(r.y() + topFrameOffset);
224 top.setWidth(r.width() - topLeft.width() - topRight.width());
225 top.setHeight(topLeft.height());
226 painter->drawTiledPixmap(top, topStretch);
227
228 QRect right = r;
229 right.setX(r.right() - rightStretch.width()+1);
230 right.setY(r.y() + topRight.height() + topFrameOffset);
231 right.setWidth(rightStretch.width());
232 right.setHeight(r.height() - topRight.height()
233 - bottomRight.height() - topFrameOffset);
234 painter->drawTiledPixmap(right, rightStretch);
235
236 QRect bottom = r;
237 bottom.setX(r.x() + bottomLeft.width());
238 bottom.setY(r.bottom() - bottomStretch.height()+1);
239 bottom.setWidth(r.width() - bottomLeft.width() - bottomRight.width());
240 bottom.setHeight(bottomLeft.height());
241 painter->drawTiledPixmap(bottom, bottomStretch);
242 painter->restore();
243 }
244 break;
245
246 default:
247 QCommonStyle::drawPrimitive(element, option, painter, widget);
248 break;
249 }
250 return;
251}
252
253
254void ArthurStyle::drawComplexControl(ComplexControl control, const QStyleOptionComplex *option,
255 QPainter *painter, const QWidget *widget) const
256{
257 switch (control) {
258 case CC_Slider:
259 if (const QStyleOptionSlider *slider = qstyleoption_cast<const QStyleOptionSlider *>(option)) {
260 QRect groove = subControlRect(CC_Slider, option, SC_SliderGroove, widget);
261 QRect handle = subControlRect(CC_Slider, option, SC_SliderHandle, widget);
262
263 painter->save();
264
265 bool hover = (slider->state & State_Enabled) && (slider->state & State_MouseOver);
266 if (hover) {
267 QRect moderated = widget->rect().adjusted(0, 4, 0, -4);
268 drawHoverRect(painter, moderated);
269 }
270
271 if ((option->subControls & SC_SliderGroove) && groove.isValid()) {
272 QPixmap grv = cached(":res/images/slider_bar.png");
273 painter->drawPixmap(QRect(groove.x() + 5, groove.y(),
274 groove.width() - 10, grv.height()),
275 grv);
276 }
277 if ((option->subControls & SC_SliderHandle) && handle.isValid()) {
278 QPixmap hndl = cached(":res/images/slider_thumb_on.png");
279 painter->drawPixmap(handle.topLeft(), hndl);
280 }
281
282 painter->restore();
283 }
284 break;
285 case CC_GroupBox:
286 if (const QStyleOptionGroupBox *groupBox
287 = qstyleoption_cast<const QStyleOptionGroupBox *>(option)) {
288 QStyleOptionGroupBox groupBoxCopy(*groupBox);
289 groupBoxCopy.subControls &= ~SC_GroupBoxLabel;
290 QCommonStyle::drawComplexControl(control, &groupBoxCopy, painter, widget);
291
292 if (groupBox->subControls & SC_GroupBoxLabel) {
293 const QRect &r = groupBox->rect;
294 QPixmap titleLeft = cached(":res/images/title_cap_left.png");
295 QPixmap titleRight = cached(":res/images/title_cap_right.png");
296 QPixmap titleStretch = cached(":res/images/title_stretch.png");
297 int txt_width = groupBox->fontMetrics.horizontalAdvance(groupBox->text) + 20;
298 painter->drawPixmap(r.center().x() - txt_width/2, 0, titleLeft);
299 QRect tileRect = subControlRect(control, groupBox, SC_GroupBoxLabel, widget);
300 painter->drawTiledPixmap(tileRect, titleStretch);
301 painter->drawPixmap(tileRect.x() + tileRect.width(), 0, titleRight);
302 int opacity = 31;
303 painter->setPen(QColor(0, 0, 0, opacity));
304 painter->drawText(tileRect.translated(0, 1),
305 Qt::AlignVCenter | Qt::AlignHCenter, groupBox->text);
306 painter->drawText(tileRect.translated(2, 1),
307 Qt::AlignVCenter | Qt::AlignHCenter, groupBox->text);
308 painter->setPen(QColor(0, 0, 0, opacity * 2));
309 painter->drawText(tileRect.translated(1, 1),
310 Qt::AlignVCenter | Qt::AlignHCenter, groupBox->text);
311 painter->setPen(Qt::white);
312 painter->drawText(tileRect, Qt::AlignVCenter | Qt::AlignHCenter, groupBox->text);
313 }
314 }
315 break;
316 default:
317 QCommonStyle::drawComplexControl(control, option, painter, widget);
318 break;
319 }
320 return;
321}
322
323void ArthurStyle::drawControl(QStyle::ControlElement element, const QStyleOption *option,
324 QPainter *painter, const QWidget *widget) const
325{
326 switch (element) {
327 case CE_RadioButtonLabel:
328 if (const QStyleOptionButton *button
329 = qstyleoption_cast<const QStyleOptionButton *>(option)) {
330
331 if (button->text.isEmpty()) {
332 QCommonStyle::drawControl(element, option, painter, widget);
333 } else {
334 painter->save();
335 painter->setPen(Qt::black);
336 painter->drawText(button->rect, Qt::AlignVCenter, button->text);
337 painter->restore();
338 }
339 }
340 break;
341 case CE_PushButtonLabel:
342 if (const QStyleOptionButton *button
343 = qstyleoption_cast<const QStyleOptionButton *>(option)) {
344
345 if (button->text.isEmpty()) {
346 QCommonStyle::drawControl(element, option, painter, widget);
347 } else {
348 painter->save();
349 painter->setPen(Qt::black);
350 painter->drawText(button->rect, Qt::AlignVCenter | Qt::AlignHCenter, button->text);
351 painter->restore();
352 }
353 }
354 break;
355 default:
356 QCommonStyle::drawControl(element, option, painter, widget);
357 break;
358 }
359}
360
361QRect ArthurStyle::subControlRect(ComplexControl control, const QStyleOptionComplex *option,
362 SubControl subControl, const QWidget *widget) const
363{
364 QRect rect;
365
366 switch (control) {
367 default:
368 rect = QCommonStyle::subControlRect(control, option, subControl, widget);
369 break;
370 case CC_GroupBox:
371 if (const QStyleOptionGroupBox *group
372 = qstyleoption_cast<const QStyleOptionGroupBox *>(option)) {
373 switch (subControl) {
374 default:
375 rect = QCommonStyle::subControlRect(control, option, subControl, widget);
376 break;
377 case SC_GroupBoxContents:
378 rect = QCommonStyle::subControlRect(control, option, subControl, widget);
379 rect.adjust(0, -8, 0, 0);
380 break;
381 case SC_GroupBoxFrame:
382 rect = group->rect;
383 break;
384 case SC_GroupBoxLabel:
385 QPixmap titleLeft = cached(":res/images/title_cap_left.png");
386 QPixmap titleRight = cached(":res/images/title_cap_right.png");
387 QPixmap titleStretch = cached(":res/images/title_stretch.png");
388 int txt_width = group->fontMetrics.horizontalAdvance(group->text) + 20;
389 rect = QRect(group->rect.center().x() - txt_width/2 + titleLeft.width(), 0,
390 txt_width - titleLeft.width() - titleRight.width(),
391 titleStretch.height());
392 break;
393 }
394 }
395 break;
396 }
397
398 if (control == CC_Slider && subControl == SC_SliderHandle) {
399 rect.setWidth(13);
400 rect.setHeight(27);
401 } else if (control == CC_Slider && subControl == SC_SliderGroove) {
402 rect.setHeight(9);
403 rect.moveTop(27/2 - 9/2);
404 }
405 return rect;
406}
407
408QSize ArthurStyle::sizeFromContents(ContentsType type, const QStyleOption *option,
409 const QSize &size, const QWidget *widget) const
410{
411 QSize newSize = QCommonStyle::sizeFromContents(type, option, size, widget);
412
413
414 switch (type) {
415 case CT_RadioButton:
416 newSize += QSize(20, 0);
417 break;
418
419 case CT_PushButton:
420 newSize.setHeight(26);
421 break;
422
423 case CT_Slider:
424 newSize.setHeight(27);
425 break;
426
427 default:
428 break;
429 }
430
431 return newSize;
432}
433
434int ArthurStyle::pixelMetric(PixelMetric pm, const QStyleOption *opt, const QWidget *widget) const
435{
436 if (pm == PM_SliderLength)
437 return 13;
438 return QCommonStyle::pixelMetric(pm, opt, widget);
439}
440
441void ArthurStyle::polish(QWidget *widget)
442{
443 if (widget->layout() && qobject_cast<QGroupBox *>(widget)) {
444 if (widget->findChildren<QGroupBox *>().size() == 0) {
445 widget->layout()->setSpacing(0);
446 widget->layout()->setContentsMargins(12, 12, 12, 12);
447 } else {
448 widget->layout()->setContentsMargins(13, 13, 13, 13);
449 }
450 }
451
452 if (qobject_cast<QPushButton *>(widget)
453 || qobject_cast<QRadioButton *>(widget)
454 || qobject_cast<QSlider *>(widget)) {
455 widget->setAttribute(Qt::WA_Hover);
456 }
457
458 QPalette pal = widget->palette();
459 if (widget->isWindow()) {
460 pal.setColor(QPalette::Window, QColor(241, 241, 241));
461 widget->setPalette(pal);
462 }
463
464}
465
466void ArthurStyle::unpolish(QWidget *widget)
467{
468 if (qobject_cast<QPushButton *>(widget)
469 || qobject_cast<QRadioButton *>(widget)
470 || qobject_cast<QSlider *>(widget)) {
471 widget->setAttribute(Qt::WA_Hover, false);
472 }
473}
474
475void ArthurStyle::polish(QPalette &palette)
476{
477 palette.setColor(QPalette::Window, QColor(241, 241, 241));
478}
479
480QRect ArthurStyle::subElementRect(SubElement element, const QStyleOption *option, const QWidget *widget) const
481{
482 QRect r;
483 switch(element) {
484 case SE_RadioButtonClickRect:
485 r = widget->rect();
486 break;
487 case SE_RadioButtonContents:
488 r = widget->rect().adjusted(20, 0, 0, 0);
489 break;
490 default:
491 r = QCommonStyle::subElementRect(element, option, widget);
492 break;
493 }
494
495 if (qobject_cast<const QRadioButton*>(widget))
496 r = r.adjusted(5, 0, -5, 0);
497
498 return r;
499}
500