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 
cached(const QString & img)62 QPixmap 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 
ArthurStyle()77 ArthurStyle::ArthurStyle()
78     : QCommonStyle()
79 {
80     Q_INIT_RESOURCE(shared);
81 }
82 
83 
drawHoverRect(QPainter * painter,const QRect & r) const84 void 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 
drawPrimitive(PrimitiveElement element,const QStyleOption * option,QPainter * painter,const QWidget * widget) const100 void 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 
drawComplexControl(ComplexControl control,const QStyleOptionComplex * option,QPainter * painter,const QWidget * widget) const254 void 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 
drawControl(QStyle::ControlElement element,const QStyleOption * option,QPainter * painter,const QWidget * widget) const323 void 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 
subControlRect(ComplexControl control,const QStyleOptionComplex * option,SubControl subControl,const QWidget * widget) const361 QRect 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 
sizeFromContents(ContentsType type,const QStyleOption * option,const QSize & size,const QWidget * widget) const408 QSize 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 
pixelMetric(PixelMetric pm,const QStyleOption * opt,const QWidget * widget) const434 int 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 
polish(QWidget * widget)441 void 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 
unpolish(QWidget * widget)466 void 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 
polish(QPalette & palette)475 void ArthurStyle::polish(QPalette &palette)
476 {
477     palette.setColor(QPalette::Window, QColor(241, 241, 241));
478 }
479 
subElementRect(SubElement element,const QStyleOption * option,const QWidget * widget) const480 QRect 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