Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qpixmapstyle.cpp
Go to the documentation of this file.
1// Copyright (C) 2014 BlackBerry Limited. All rights reserved.
2// Copyright (C) 2015 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
3// Copyright (C) 2016 The Qt Company Ltd.
4// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
5// Qt-Security score:significant reason:default
6
9
10#include <QDebug>
11#if QT_CONFIG(textedit)
12#include <QTextEdit>
13#endif
14#include <QStringBuilder>
15#include <QPainter>
16#include <QPixmapCache>
17#include <QStyleOption>
18#include <QString>
19#if QT_CONFIG(progressbar)
20#include <QProgressBar>
21#endif
22#if QT_CONFIG(slider)
23#include <QSlider>
24#endif
25#include <QEvent>
26#if QT_CONFIG(combobox)
27#include <QComboBox>
28#endif
29#if QT_CONFIG(itemviews)
30#include <QAbstractItemView>
31#include <QStyledItemDelegate>
32#endif
33#if QT_CONFIG(listview)
34#include <QListView>
35#endif
36#include <QAbstractScrollArea>
37#if QT_CONFIG(scrollbar)
38#include <QScrollBar>
39#endif
40#if QT_CONFIG(scroller)
41#include <qscroller.h>
42#endif
43
45
46/*!
47 \class QPixmapStyle
48 \brief The QPixmapStyle class provides mechanism for writing pixmap based styles.
49
50 \since 5.7
51 \ingroup appearance
52 \inmodule QtWidgets
53 \internal
54
55 This is a convenience class that enables the implementation of a widget style using
56 pixmaps, on the same fashion used by the \l{BorderImage} QML type.
57
58 In order to style a QWidget, one simply needs to call QPixmapStyle::addDescriptor()
59 or QPixmapStyle::addPixmap() with the id of the component to be styled, the path of
60 the image to be used, the margins and the tiling rules:
61
62 \snippet styles/qcustompixmapstyle.cpp 0
63
64 \sa QStyle, QCommonStyle
65*/
66
67/*!
68 \internal
69
70 Constructs a QPixmapStyle object.
71*/
72QPixmapStyle::QPixmapStyle()
73 : QCommonStyle(*new QPixmapStylePrivate)
74{
75}
76
77/*!
78 Destroys the QPixmapStyle object.
79*/
80QPixmapStyle::~QPixmapStyle()
81{
82}
83
84/*!
85 \reimp
86*/
87void QPixmapStyle::polish(QApplication *application)
88{
89 QCommonStyle::polish(application);
90}
91
92/*!
93 \reimp
94*/
95void QPixmapStyle::polish(QPalette &palette)
96{
97 palette = proxy()->standardPalette();
98}
99
100/*!
101 \reimp
102*/
103void QPixmapStyle::polish(QWidget *widget)
104{
105 Q_D(QPixmapStyle);
106
107 // Don't fill the interior of the QTextEdit
108#if QT_CONFIG(textedit)
109 if (qobject_cast<QTextEdit*>(widget)) {
110 QPalette p = widget->palette();
111 p.setBrush(QPalette::Base, Qt::NoBrush);
112 widget->setPalette(p);
113 }
114#endif
115#if QT_CONFIG(progressbar)
116 if (QProgressBar *pb = qobject_cast<QProgressBar*>(widget)) {
117 // Center the text in the progress bar
118 pb->setAlignment(Qt::AlignCenter);
119 // Change the font size if needed, as it's used to compute the minimum size
120 QFont font = pb->font();
121 font.setPixelSize(d->descriptors.value(PB_HBackground).size.height()/2);
122 pb->setFont(font);
123 }
124#endif
125#if QT_CONFIG(slider)
126 if (qobject_cast<QSlider*>(widget))
127 widget->installEventFilter(this);
128#endif
129#if QT_CONFIG(combobox)
130 if (QComboBox *cb = qobject_cast<QComboBox*>(widget)) {
131 widget->installEventFilter(this);
132 // NOTE: This will break if the private API of QComboBox changes drastically
133 // Make sure the popup is created so we can change the frame style
134 QAbstractItemView *list = cb->view();
135 list->setProperty("_pixmap_combobox_list", true);
136 list->setItemDelegate(new QStyledItemDelegate(list));
137 QPalette p = list->palette();
138 p.setBrush(QPalette::Active, QPalette::Base, QBrush(Qt::transparent) );
139 p.setBrush(QPalette::Active, QPalette::AlternateBase, QBrush(Qt::transparent) );
140 p.setBrush(QPalette::Inactive, QPalette::Base, QBrush(Qt::transparent) );
141 p.setBrush(QPalette::Inactive, QPalette::AlternateBase, QBrush(Qt::transparent) );
142 p.setBrush(QPalette::Disabled, QPalette::Base, QBrush(Qt::transparent) );
143 p.setBrush(QPalette::Disabled, QPalette::AlternateBase, QBrush(Qt::transparent) );
144 list->setPalette(p);
145
146 QFrame *frame = qobject_cast<QFrame*>(list->parent());
147 if (frame) {
148 const QPixmapStyleDescriptor &desc = d->descriptors.value(DD_PopupDown);
149 const QPixmapStylePixmap &pix = d->pixmaps.value(DD_ItemSeparator);
150 frame->setContentsMargins(pix.margins.left(), desc.margins.top(),
151 pix.margins.right(), desc.margins.bottom());
152 frame->setAttribute(Qt::WA_TranslucentBackground);
153 }
154 }
155#endif // QT_CONFIG(combobox)
156 if (qstrcmp(widget->metaObject()->className(),"QComboBoxPrivateContainer") == 0)
157 widget->installEventFilter(this);
158
159#if QT_CONFIG(scrollarea)
160 if (QAbstractScrollArea *scrollArea = qobject_cast<QAbstractScrollArea*>(widget)) {
161 scrollArea->viewport()->setAutoFillBackground(false);
162#if QT_CONFIG(itemviews)
163 if (QAbstractItemView *view = qobject_cast<QAbstractItemView*>(scrollArea)) {
164 view->setHorizontalScrollMode(QAbstractItemView::ScrollPerPixel);
165 view->setVerticalScrollMode(QAbstractItemView::ScrollPerPixel);
166 }
167#endif
168#if QT_CONFIG(gestures) && QT_CONFIG(scroller)
169 QScroller::grabGesture(scrollArea->viewport(), QScroller::LeftMouseButtonGesture);
170#endif
171 }
172#endif // QT_CONFIG(scrollarea)
173#if QT_CONFIG(scrollbar)
174 if (qobject_cast<QScrollBar*>(widget))
175 widget->setAttribute(Qt::WA_OpaquePaintEvent, false);
176#endif
177#if !QT_CONFIG(progressbar) && !QT_CONFIG(combobox)
178 Q_UNUSED(d);
179#endif
180 QCommonStyle::polish(widget);
181}
182
183/*!
184 \reimp
185*/
186void QPixmapStyle::unpolish(QApplication *application)
187{
188 QCommonStyle::unpolish(application);
189}
190
191/*!
192 \reimp
193*/
194void QPixmapStyle::unpolish(QWidget *widget)
195{
196 if (
197#if QT_CONFIG(slider)
198 qobject_cast<QSlider*>(widget)
199#else
200 false
201#endif
202#if QT_CONFIG(combobox)
203 || qobject_cast<QComboBox*>(widget)
204#endif
205 ) {
206 widget->removeEventFilter(this);
207 }
208
209 if (qstrcmp(widget->metaObject()->className(),"QComboBoxPrivateContainer") == 0)
210 widget->removeEventFilter(this);
211
212#if QT_CONFIG(gestures) && QT_CONFIG(scrollarea) && QT_CONFIG(scroller)
213 if (QAbstractScrollArea *scrollArea = qobject_cast<QAbstractScrollArea*>(widget))
214 QScroller::ungrabGesture(scrollArea->viewport());
215#endif
216
217 QCommonStyle::unpolish(widget);
218}
219
220/*!
221 \reimp
222*/
223void QPixmapStyle::drawPrimitive(PrimitiveElement element, const QStyleOption *option,
224 QPainter *painter, const QWidget *widget) const
225{
226 switch (element) {
227 case PE_FrameFocusRect: //disable focus rectangle
228 break;
229 case PE_PanelButtonBevel:
230 case PE_PanelButtonCommand:
231 drawPushButton(option, painter, widget);
232 break;
233 case PE_PanelLineEdit:
234 case PE_FrameLineEdit:
235 drawLineEdit(option, painter, widget);
236 break;
237 case PE_Frame:
238#if QT_CONFIG(textedit)
239 case PE_FrameDefaultButton:
240 if (qobject_cast<const QTextEdit*>(widget))
241 drawTextEdit(option, painter, widget);
242 break;
243#endif
244 case PE_IndicatorCheckBox:
245 drawCheckBox(option, painter, widget);
246 break;
247 case PE_IndicatorRadioButton:
248 drawRadioButton(option, painter, widget);
249 break;
250 case PE_PanelItemViewItem:
251#if QT_CONFIG(listview)
252 if (qobject_cast<const QListView*>(widget))
253 drawPanelItemViewItem(option, painter, widget);
254 else
255#endif
256 QCommonStyle::drawPrimitive(element, option, painter, widget);
257 break;
258 default:
259 QCommonStyle::drawPrimitive(element, option, painter, widget);
260 }
261}
262
263/*!
264 \reimp
265*/
266void QPixmapStyle::drawControl(ControlElement element, const QStyleOption *option,
267 QPainter *painter, const QWidget *widget) const
268{
269 Q_D(const QPixmapStyle);
270
271 switch (element) {
272 case CE_ProgressBarGroove:
273 drawProgressBarBackground(option, painter, widget);
274 break;
275 case CE_ProgressBarLabel:
276 drawProgressBarLabel(option, painter, widget);
277 break;
278 case CE_ProgressBarContents:
279 drawProgressBarFill(option, painter, widget);
280 break;
281 case CE_ShapedFrame:
282 // NOTE: This will break if the private API of QComboBox changes drastically
283 if (qstrcmp(widget->metaObject()->className(),"QComboBoxPrivateContainer") == 0) {
284 const QPixmapStyleDescriptor &desc = d->descriptors.value(DD_PopupDown);
285 const QPixmapStylePixmap &pix = d->pixmaps.value(DD_ItemSeparator);
286 QRect rect = option->rect;
287 rect.adjust(-pix.margins.left(), -desc.margins.top(),
288 pix.margins.right(), desc.margins.bottom());
289 bool up = widget->property("_pixmapstyle_combobox_up").toBool();
290 drawCachedPixmap(up ? DD_PopupUp : DD_PopupDown, rect, painter);
291 }
292 else {
293 QCommonStyle::drawControl(element, option, painter, widget);
294 }
295 break;
296 default:
297 QCommonStyle::drawControl(element, option, painter, widget);
298 }
299}
300
301/*!
302 \reimp
303*/
304void QPixmapStyle::drawComplexControl(ComplexControl cc, const QStyleOptionComplex *option,
305 QPainter *painter, const QWidget *widget) const
306{
307 switch (cc) {
308 case CC_Slider:
309 drawSlider(option, painter, widget);
310 break;
311 case CC_ComboBox:
312 drawComboBox(option, painter, widget);
313 break;
314 case CC_ScrollBar:
315 drawScrollBar(option, painter, widget);
316 break;
317 default:
318 QCommonStyle::drawComplexControl(cc, option, painter, widget);
319 }
320}
321
322/*!
323 \reimp
324*/
325QSize QPixmapStyle::sizeFromContents(ContentsType type, const QStyleOption *option,
326 const QSize &contentsSize, const QWidget *widget) const
327{
328 switch (type) {
329 case CT_PushButton:
330 return pushButtonSizeFromContents(option, contentsSize, widget);
331 case CT_LineEdit:
332 return lineEditSizeFromContents(option, contentsSize, widget);
333 case CT_ProgressBar:
334 return progressBarSizeFromContents(option, contentsSize, widget);
335 case CT_Slider:
336 return sliderSizeFromContents(option, contentsSize, widget);
337 case CT_ComboBox:
338 return comboBoxSizeFromContents(option, contentsSize, widget);
339 case CT_ItemViewItem:
340 return itemViewSizeFromContents(option, contentsSize, widget);
341 default: ;
342 }
343
344 return QCommonStyle::sizeFromContents(type, option, contentsSize, widget);
345}
346
347/*!
348 \reimp
349*/
350QRect QPixmapStyle::subElementRect(SubElement element, const QStyleOption *option,
351 const QWidget *widget) const
352{
353 Q_D(const QPixmapStyle);
354
355 switch (element) {
356 case SE_LineEditContents:
357 {
358 QRect rect = QCommonStyle::subElementRect(element, option, widget);
359 const QPixmapStyleDescriptor &desc = d->descriptors.value(LE_Enabled);
360 rect.adjust(desc.margins.left(), desc.margins.top(),
361 -desc.margins.right(), -desc.margins.bottom());
362 rect = visualRect(option->direction, option->rect, rect);
363 return rect;
364 }
365 default: ;
366 }
367
368 return QCommonStyle::subElementRect(element, option, widget);
369}
370
371/*!
372 \reimp
373*/
374QRect QPixmapStyle::subControlRect(ComplexControl cc, const QStyleOptionComplex *option,
375 SubControl sc, const QWidget *widget) const
376{
377 switch (cc) {
378 case CC_ComboBox:
379 return comboBoxSubControlRect(option, sc, widget);
380 case CC_ScrollBar:
381 return scrollBarSubControlRect(option, sc, widget);
382 default: ;
383 }
384
385 return QCommonStyle::subControlRect(cc, option, sc, widget);
386}
387
388/*!
389 \reimp
390*/
391int QPixmapStyle::pixelMetric(PixelMetric metric, const QStyleOption *option,
392 const QWidget *widget) const
393{
394 Q_D(const QPixmapStyle);
395
396 switch (metric) {
397 case PM_ButtonShiftHorizontal:
398 case PM_ButtonShiftVertical:
399 return 0;
400 case PM_DefaultFrameWidth:
401#if QT_CONFIG(textedit)
402 if (qobject_cast<const QTextEdit*>(widget)) {
403 const QPixmapStyleDescriptor &desc = d->descriptors.value(LE_Enabled);
404 return qMax(qMax(desc.margins.left(), desc.margins.right()),
405 qMax(desc.margins.top(), desc.margins.bottom()));
406 }
407#endif
408 return 0;
409 case PM_IndicatorWidth:
410 return d->pixmaps.value(CB_Enabled).pixmap.width();
411 case PM_IndicatorHeight:
412 return d->pixmaps.value(CB_Enabled).pixmap.height();
413 case PM_CheckBoxLabelSpacing:
414 {
415 const QPixmapStylePixmap &pix = d->pixmaps.value(CB_Enabled);
416 return qMax(qMax(pix.margins.left(), pix.margins.right()),
417 qMax(pix.margins.top(), pix.margins.bottom()));
418 }
419 case PM_ExclusiveIndicatorWidth:
420 return d->pixmaps.value(RB_Enabled).pixmap.width();
421 case PM_ExclusiveIndicatorHeight:
422 return d->pixmaps.value(RB_Enabled).pixmap.height();
423 case PM_RadioButtonLabelSpacing:
424 {
425 const QPixmapStylePixmap &pix = d->pixmaps.value(RB_Enabled);
426 return qMax(qMax(pix.margins.left(), pix.margins.right()),
427 qMax(pix.margins.top(), pix.margins.bottom()));
428 }
429#if QT_CONFIG(slider)
430 case PM_SliderThickness:
431 if (const QStyleOptionSlider *slider =
432 qstyleoption_cast<const QStyleOptionSlider*>(option)) {
433 const QPixmapStyleDescriptor desc =
434 d->descriptors.value(slider->orientation == Qt::Horizontal
435 ? SG_HEnabled : SG_VEnabled);
436 return slider->orientation == Qt::Horizontal
437 ? desc.size.height() : desc.size.width();
438 }
439 break;
440 case PM_SliderControlThickness:
441 if (const QStyleOptionSlider *slider =
442 qstyleoption_cast<const QStyleOptionSlider*>(option)) {
443 const QPixmapStylePixmap pix =
444 d->pixmaps.value(slider->orientation == Qt::Horizontal
445 ? SH_HEnabled : SH_VEnabled);
446 return slider->orientation == Qt::Horizontal
447 ? pix.pixmap.height() : pix.pixmap.width();
448 }
449 break;
450 case PM_SliderLength:
451 if (const QStyleOptionSlider *slider =
452 qstyleoption_cast<const QStyleOptionSlider*>(option)) {
453 const QPixmapStylePixmap pix =
454 d->pixmaps.value(slider->orientation == Qt::Horizontal
455 ? SH_HEnabled : SH_VEnabled);
456 return slider->orientation == Qt::Horizontal
457 ? pix.pixmap.width() : pix.pixmap.height();
458 }
459 break;
460 case PM_ScrollBarExtent:
461 if (const QStyleOptionSlider *slider =
462 qstyleoption_cast<const QStyleOptionSlider*>(option)) {
463 const QPixmapStyleDescriptor desc =
464 d->descriptors.value(slider->orientation == Qt::Horizontal
465 ? SB_Horizontal : SB_Vertical);
466 return slider->orientation == Qt::Horizontal
467 ? desc.size.height() : desc.size.width();
468 }
469 break;
470#endif // QT_CONFIG(slider)
471 case PM_ScrollBarSliderMin:
472 return 0;
473 default: ;
474 }
475
476 return QCommonStyle::pixelMetric(metric, option, widget);
477}
478
479/*!
480 \reimp
481*/
482int QPixmapStyle::styleHint(StyleHint hint, const QStyleOption *option,
483 const QWidget *widget, QStyleHintReturn *returnData) const
484{
485 switch (hint) {
486 case SH_EtchDisabledText:
487 return false;
488 case SH_ComboBox_Popup:
489 return false;
490 default: ;
491 }
492
493 return QCommonStyle::styleHint(hint, option, widget, returnData);
494}
495
496/*!
497 \reimp
498*/
499QStyle::SubControl QPixmapStyle::hitTestComplexControl(QStyle::ComplexControl control,
500 const QStyleOptionComplex *option,
501 const QPoint &pos,
502 const QWidget *widget) const
503{
504 const SubControl sc = QCommonStyle::hitTestComplexControl(control, option, pos, widget);
505 if (control == CC_ScrollBar) {
506 if (sc == SC_ScrollBarAddLine)
507 return SC_ScrollBarAddPage;
508 else if (sc == SC_ScrollBarSubLine)
509 return SC_ScrollBarSubPage;
510 }
511
512 return sc;
513}
514
515/*!
516 \reimp
517*/
518bool QPixmapStyle::eventFilter(QObject *watched, QEvent *event)
519{
520 Q_D(QPixmapStyle);
521#if QT_CONFIG(slider)
522 if (QSlider *slider = qobject_cast<QSlider*>(watched)) {
523 switch (event->type()) {
524 case QEvent::MouseButtonPress:
525 case QEvent::MouseButtonRelease:
526 case QEvent::MouseMove:
527 slider->update();
528 break;
529 default: ;
530 }
531 }
532#endif // QT_CONFIG(slider)
533#if QT_CONFIG(combobox)
534 if (QComboBox *comboBox = qobject_cast<QComboBox*>(watched)) {
535 switch (event->type()) {
536 case QEvent::MouseButtonPress:
537 event->ignore();
538 comboBox->setProperty("_pixmapstyle_combobox_pressed", true);
539 comboBox->repaint();
540 return true;
541 case QEvent::MouseButtonRelease:
542 comboBox->setProperty("_pixmapstyle_combobox_pressed", false);
543 comboBox->repaint();
544 if ( comboBox->view() ) {
545 if ( comboBox->view()->isVisible() || (!comboBox->isEnabled()))
546 comboBox->hidePopup();
547 else
548 comboBox->showPopup();
549 }
550 break;
551 default: ;
552 }
553 }
554#endif // QT_CONFIG(combobox)
555
556 if (qstrcmp(watched->metaObject()->className(),"QComboBoxPrivateContainer") == 0) {
557 if (event->type() == QEvent::Show) {
558 QWidget *widget = qobject_cast<QWidget*>(watched);
559 int yPopup = widget->geometry().top();
560 int yCombo = widget->parentWidget()->mapToGlobal(QPoint(0, 0)).y();
561 QRect geom = widget->geometry();
562 const QPixmapStyleDescriptor &desc = d->descriptors.value(DD_ButtonEnabled);
563 const bool up = yPopup < yCombo;
564 geom.moveTop(geom.top() + (up ? desc.margins.top() : -desc.margins.bottom()));
565 widget->setGeometry(geom);
566 widget->setProperty("_pixmapstyle_combobox_up", up);
567 widget->parentWidget()->setProperty("_pixmapstyle_combobox_up", up);
568 }
569 }
570
571 return QCommonStyle::eventFilter(watched, event);
572}
573
574/*!
575 \fn void QPixmapStyle::addDescriptor(QPixmapStyle::ControlDescriptor control, const QString &fileName, QMargins margins, QTileRules tileRules)
576
577 Associates the pixmap having the given \a fileName with the given \a control. The \a margins parameter describe the boundaries
578 of the pixmap's top-left, top-right, bottom-left and bottom-right corners, as well as the left, right, top and bottorm segments
579 and the middle. The \a tileRules parameter describes how QPixmapStyle is supposed to handle the scaling of the center of the pixmap.
580
581 Use QPixmapStyle::addPixmap() for controls that are not resizable.
582
583 \snippet styles/qcustompixmapstyle.cpp 1
584
585 \sa addPixmap, copyDescriptor
586
587*/
588void QPixmapStyle::addDescriptor(QPixmapStyle::ControlDescriptor control, const QString &fileName,
589 QMargins margins, QTileRules tileRules)
590{
591 Q_D(QPixmapStyle);
592
593 QPixmapStyleDescriptor desc;
594 QImage image(fileName);
595
596 if (image.isNull())
597 return;
598
599 desc.fileName = fileName;
600 desc.margins = margins;
601 desc.tileRules = tileRules;
602 desc.size = image.size();
603
604 d->descriptors[control] = desc;
605}
606
607/*!
608 \fn void QPixmapStyle::copyDescriptor(QPixmapStyle::ControlDescriptor source, QPixmapStyle::ControlDescriptor dest)
609
610 Copies the data associated with the \a source descriptor to the \a dest descriptor.
611
612 \snippet styles/qcustompixmapstyle.cpp 2
613*/
614
615void QPixmapStyle::copyDescriptor(QPixmapStyle::ControlDescriptor source,
616 QPixmapStyle::ControlDescriptor dest)
617{
618 Q_D(QPixmapStyle);
619 d->descriptors[dest] = d->descriptors.value(source);
620}
621
622/*!
623 \fn void QPixmapStyle::drawCachedPixmap(QPixmapStyle::ControlDescriptor control, const QRect &rect, QPainter *painter) const
624
625 Draws the image associated with the current \a control on the given \a rect using the given \a painter.
626*/
627void QPixmapStyle::drawCachedPixmap(QPixmapStyle::ControlDescriptor control, const QRect &rect,
628 QPainter *p) const
629{
630 Q_D(const QPixmapStyle);
631 auto descriptor = d->descriptors.constFind(control);
632 if (descriptor == d->descriptors.constEnd())
633 return;
634 const QPixmap pix = d->getCachedPixmap(control, descriptor.value(), rect.size());
635 Q_ASSERT(!pix.isNull());
636 p->drawPixmap(rect, pix);
637}
638
639/*!
640 \fn void QPixmapStyle::addPixmap(ControlPixmap control, const QString &fileName, QMargins margins)
641
642 Use this function to style statically sized controls such as check boxes.
643
644 \sa addDescriptor, copyPixmap
645*/
646void QPixmapStyle::addPixmap(ControlPixmap control, const QString &fileName,
647 QMargins margins)
648{
649 Q_D(QPixmapStyle);
650
651 QPixmapStylePixmap pix;
652 QPixmap image(fileName);
653
654 if (image.isNull())
655 return;
656
657 pix.pixmap = image;
658 pix.margins = margins;
659
660 d->pixmaps[control] = pix;
661}
662
663/*
664 \fn void QPixmapStyle::copyPixmap(QPixmapStyle::ControlPixmap source, QPixmapStyle::ControlPixmap dest)
665
666 Copies the data associated with the \a source pixmap to the \a dest pixmap.
667
668 \sa addPixmap, addDescriptor, copyDescriptor
669*/
670void QPixmapStyle::copyPixmap(QPixmapStyle::ControlPixmap source, QPixmapStyle::ControlPixmap dest)
671{
672 Q_D(QPixmapStyle);
673 d->pixmaps[dest] = d->pixmaps.value(source);
674}
675
676/*!
677 \internal
678
679 Constructs a QPixmapStyle object.
680*/
681QPixmapStyle::QPixmapStyle(QPixmapStylePrivate &dd)
682 : QCommonStyle(dd)
683{}
684
685void QPixmapStyle::drawPushButton(const QStyleOption *option,
686 QPainter *painter, const QWidget *) const
687{
688 const bool checked = option->state & State_On;
689 const bool pressed = option->state & State_Sunken;
690 const bool enabled = option->state & State_Enabled;
691
692 ControlDescriptor control = PB_Enabled;
693 if (enabled)
694 control = pressed ? PB_Pressed : (checked ? PB_Checked : PB_Enabled);
695 else
696 control = checked ? PB_PressedDisabled : PB_Disabled;
697 drawCachedPixmap(control, option->rect, painter);
698}
699
700void QPixmapStyle::drawLineEdit(const QStyleOption *option,
701 QPainter *painter, const QWidget *widget) const
702{
703 // Don't draw for the line edit inside a combobox
704#if QT_CONFIG(combobox)
705 if (widget && qobject_cast<const QComboBox*>(widget->parentWidget()))
706 return;
707#else
708 Q_UNUSED(widget);
709#endif
710 const bool enabled = option->state & State_Enabled;
711 const bool focused = option->state & State_HasFocus;
712 ControlDescriptor control = enabled ? (focused ? LE_Focused : LE_Enabled) : LE_Disabled;
713 drawCachedPixmap(control, option->rect, painter);
714}
715
716void QPixmapStyle::drawTextEdit(const QStyleOption *option,
717 QPainter *painter, const QWidget *) const
718{
719 const bool enabled = option->state & State_Enabled;
720 const bool focused = option->state & State_HasFocus;
721 ControlDescriptor control = enabled ? (focused ? TE_Focused : TE_Enabled) : TE_Disabled;
722 drawCachedPixmap(control, option->rect, painter);
723}
724
725void QPixmapStyle::drawCheckBox(const QStyleOption *option,
726 QPainter *painter, const QWidget *) const
727{
728 Q_D(const QPixmapStyle);
729
730 const QStyleOptionButton *button = qstyleoption_cast<const QStyleOptionButton*>(option);
731
732 const bool down = button->state & State_Sunken;
733 const bool enabled = button->state & State_Enabled;
734 const bool on = button->state & State_On;
735
736 ControlPixmap control;
737 if (enabled)
738 control = on ? (down ? CB_PressedChecked : CB_Checked) : (down ? CB_Pressed : CB_Enabled);
739 else
740 control = on ? CB_DisabledChecked : CB_Disabled;
741 painter->drawPixmap(button->rect, d->pixmaps.value(control).pixmap);
742}
743
744void QPixmapStyle::drawRadioButton(const QStyleOption *option,
745 QPainter *painter, const QWidget *) const
746{
747 Q_D(const QPixmapStyle);
748
749 const QStyleOptionButton *button = qstyleoption_cast<const QStyleOptionButton*>(option);
750
751 const bool down = button->state & State_Sunken;
752 const bool enabled = button->state & State_Enabled;
753 const bool on = button->state & State_On;
754
755 ControlPixmap control;
756 if (enabled)
757 control = on ? RB_Checked : (down ? RB_Pressed : RB_Enabled);
758 else
759 control = on ? RB_DisabledChecked : RB_Disabled;
760 painter->drawPixmap(button->rect, d->pixmaps.value(control).pixmap);
761}
762
763void QPixmapStyle::drawPanelItemViewItem(const QStyleOption *option, QPainter *painter,
764 const QWidget *widget) const
765{
766 Q_D(const QPixmapStyle);
767
768 ControlPixmap cp = ID_Separator;
769 ControlDescriptor cd = ID_Selected;
770
771 if (widget && widget->property("_pixmap_combobox_list").toBool()) {
772 cp = DD_ItemSeparator;
773 cd = DD_ItemSelected;
774 }
775
776 QPixmap pix = d->pixmaps.value(cp).pixmap;
777 QRect rect = option->rect;
778 rect.setBottom(rect.top() + pix.height()-1);
779 painter->drawPixmap(rect, pix);
780 if (option->state & QStyle::State_Selected) {
781 rect = option->rect;
782 rect.setTop(rect.top() + pix.height());
783 drawCachedPixmap(cd, rect, painter);
784 }
785}
786
787void QPixmapStyle::drawProgressBarBackground(const QStyleOption *option,
788 QPainter *painter, const QWidget *) const
789{
790 bool vertical = false;
791 if (const QStyleOptionProgressBar *pb =
792 qstyleoption_cast<const QStyleOptionProgressBar *>(option)) {
793 vertical = !(pb->state & QStyle::State_Horizontal);
794 }
795 drawCachedPixmap(vertical ? PB_VBackground : PB_HBackground, option->rect, painter);
796}
797
798void QPixmapStyle::drawProgressBarLabel(const QStyleOption *option,
799 QPainter *painter, const QWidget *) const
800{
801 if (const QStyleOptionProgressBar *pb =
802 qstyleoption_cast<const QStyleOptionProgressBar *>(option)) {
803 const bool vertical = !(pb->state & QStyle::State_Horizontal);
804 if (!vertical) {
805 QPalette::ColorRole textRole = QPalette::ButtonText;
806 proxy()->drawItemText(painter, pb->rect,
807 Qt::AlignCenter | Qt::TextSingleLine, pb->palette,
808 pb->state & State_Enabled, pb->text, textRole);
809 }
810 }
811}
812
813void QPixmapStyle::drawProgressBarFill(const QStyleOption *option,
814 QPainter *painter, const QWidget *) const
815{
816 const QStyleOptionProgressBar *pbar =
817 qstyleoption_cast<const QStyleOptionProgressBar*>(option);
818 const bool vertical = !(pbar->state & QStyle::State_Horizontal);
819 const bool flip = (pbar->direction == Qt::RightToLeft) ^ pbar->invertedAppearance;
820
821 if (pbar->progress == pbar->maximum) {
822 drawCachedPixmap(vertical ? PB_VComplete : PB_HComplete, option->rect, painter);
823
824 } else {
825 if (pbar->progress == pbar->minimum)
826 return;
827 const auto totalSteps = qint64(pbar->maximum) - pbar->minimum;
828 const auto progressSteps = qint64(pbar->progress) - pbar->minimum;
829 const auto availablePixels = vertical ? option->rect.height() : option->rect.width();
830 const auto pixelsPerStep = double(availablePixels) / totalSteps;
831
832 const auto progress = static_cast<int>(progressSteps * pixelsPerStep); // width in pixels
833
834 QRect optRect = option->rect;
835 if (vertical) {
836 if (flip)
837 optRect.setBottom(optRect.top()+progress-1);
838 else
839 optRect.setTop(optRect.bottom()-progress+1);
840 } else {
841 if (flip)
842 optRect.setLeft(optRect.right()-progress+1);
843 else
844 optRect.setRight(optRect.left()+progress-1);
845 }
846
847 drawCachedPixmap(vertical ? PB_VContent : PB_HContent, optRect, painter);
848 }
849}
850
851void QPixmapStyle::drawSlider(const QStyleOptionComplex *option,
852 QPainter *painter, const QWidget *widget) const
853{
854#if QT_CONFIG(slider)
855 Q_D(const QPixmapStyle);
856
857 const QStyleOptionSlider *slider = qstyleoption_cast<const QStyleOptionSlider*>(option);
858 if (!slider)
859 return;
860
861 const bool enabled = option->state & State_Enabled;
862 const bool pressed = option->state & State_Sunken;
863 const Qt::Orientation orient = slider->orientation;
864
865 const QRect handle = proxy()->subControlRect(CC_Slider, option, SC_SliderHandle, widget);
866 if (option->subControls & SC_SliderGroove) {
867 QRect groove = proxy()->subControlRect(CC_Slider, option, SC_SliderGroove, widget);
868 if (groove.isValid()) {
869 // Draw the background
870 ControlDescriptor control;
871 if (orient == Qt::Horizontal)
872 control = enabled ? SG_HEnabled : SG_HDisabled;
873 else
874 control = enabled ? SG_VEnabled : SG_VDisabled;
875 drawCachedPixmap(control, groove, painter);
876
877 // Draw the active part
878 if (orient == Qt::Horizontal) {
879 control = enabled ? (pressed ? SG_HActivePressed : SG_HActiveEnabled )
880 : SG_HActiveDisabled;
881 } else {
882 control = enabled ? (pressed ? SG_VActivePressed : SG_VActiveEnabled )
883 : SG_VActiveDisabled;
884 }
885 const QPixmapStyleDescriptor &desc = d->descriptors.value(control);
886 const QPixmap pix = d->getCachedPixmap(control, desc, groove.size());
887 if (!pix.isNull()) {
888 groove.setRight(orient == Qt::Horizontal
889 ? handle.center().x() : handle.center().y());
890 painter->drawPixmap(groove, pix, groove);
891 }
892 }
893 }
894 if (option->subControls & SC_SliderHandle) {
895 if (handle.isValid()) {
896 ControlPixmap pix;
897 if (orient == Qt::Horizontal)
898 pix = enabled ? (pressed ? SH_HPressed : SH_HEnabled) : SH_HDisabled;
899 else
900 pix = enabled ? (pressed ? SH_VPressed : SH_VEnabled) : SH_VDisabled;
901 painter->drawPixmap(handle, d->pixmaps.value(pix).pixmap);
902 }
903 }
904#else
905 Q_UNUSED(option);
906 Q_UNUSED(painter);
907 Q_UNUSED(widget);
908#endif // QT_CONFIG(slider)
909}
910
911void QPixmapStyle::drawComboBox(const QStyleOptionComplex *option,
912 QPainter *painter, const QWidget *widget) const
913{
914 Q_D(const QPixmapStyle);
915
916 const bool enabled = option->state & State_Enabled;
917 const bool pressed = widget->property("_pixmapstyle_combobox_pressed").toBool();
918 const bool opened = option->state & State_On;
919
920 ControlDescriptor control =
921 enabled ? (pressed ? DD_ButtonPressed : DD_ButtonEnabled) : DD_ButtonDisabled;
922 drawCachedPixmap(control, option->rect, painter);
923
924 ControlPixmap cp = enabled ? (opened ? DD_ArrowOpen
925 : (pressed ? DD_ArrowPressed : DD_ArrowEnabled))
926 : DD_ArrowDisabled;
927 QPixmapStylePixmap pix = d->pixmaps.value(cp);
928 QRect rect = comboBoxSubControlRect(option, SC_ComboBoxArrow, widget);
929 painter->drawPixmap(rect, pix.pixmap);
930}
931
932void QPixmapStyle::drawScrollBar(const QStyleOptionComplex *option,
933 QPainter *painter, const QWidget *widget) const
934{
935#if QT_CONFIG(slider)
936 if (const QStyleOptionSlider *slider =
937 qstyleoption_cast<const QStyleOptionSlider*>(option)) {
938 // Do not draw the scrollbar
939 if (slider->minimum == slider->maximum)
940 return;
941
942 QRect rect = scrollBarSubControlRect(option, SC_ScrollBarSlider, widget);
943 ControlDescriptor control = slider->orientation == Qt::Horizontal
944 ? SB_Horizontal : SB_Vertical;
945 drawCachedPixmap(control, rect, painter);
946 }
947#else
948 Q_UNUSED(option);
949 Q_UNUSED(painter);
950 Q_UNUSED(widget);
951#endif // QT_CONFIG(slider)
952}
953
954QSize QPixmapStyle::pushButtonSizeFromContents(const QStyleOption *option,
955 const QSize &contentsSize,
956 const QWidget *widget) const
957{
958 Q_D(const QPixmapStyle);
959
960 const QPixmapStyleDescriptor &desc = d->descriptors.value(PB_Enabled);
961 const int bm = proxy()->pixelMetric(PM_ButtonMargin, option, widget);
962
963 int w = contentsSize.width();
964 int h = contentsSize.height();
965 w += desc.margins.left() + desc.margins.right() + bm;
966 h += desc.margins.top() + desc.margins.bottom() + bm;
967
968 return d->computeSize(desc, w, h);
969}
970
971QSize QPixmapStyle::lineEditSizeFromContents(const QStyleOption *option,
972 const QSize &contentsSize, const QWidget *) const
973{
974 Q_D(const QPixmapStyle);
975
976 const QPixmapStyleDescriptor &desc = d->descriptors.value(LE_Enabled);
977 const int border = 2 * proxy()->pixelMetric(PM_DefaultFrameWidth, option);
978
979 int w = contentsSize.width() + border + desc.margins.left() + desc.margins.right();
980 int h = contentsSize.height() + border + desc.margins.top() + desc.margins.bottom();
981
982 return d->computeSize(desc, w, h);
983}
984
985QSize QPixmapStyle::progressBarSizeFromContents(const QStyleOption *option,
986 const QSize &contentsSize,
987 const QWidget *widget) const
988{
989 Q_D(const QPixmapStyle);
990
991 bool vertical = false;
992 if (const QStyleOptionProgressBar *pb =
993 qstyleoption_cast<const QStyleOptionProgressBar *>(option)) {
994 vertical = !(pb->state & QStyle::State_Horizontal);
995 }
996 QSize result = QCommonStyle::sizeFromContents(CT_Slider, option, contentsSize, widget);
997 if (vertical) {
998 const QPixmapStyleDescriptor desc = d->descriptors.value(PB_VBackground);
999 return QSize(desc.size.height(), result.height());
1000 } else {
1001 const QPixmapStyleDescriptor desc = d->descriptors.value(PB_HBackground);
1002 return QSize(result.width(), desc.size.height());
1003 }
1004}
1005
1006QSize QPixmapStyle::sliderSizeFromContents(const QStyleOption *option,
1007 const QSize &contentsSize,
1008 const QWidget *widget) const
1009{
1010#if QT_CONFIG(slider)
1011 Q_D(const QPixmapStyle);
1012
1013 const QStyleOptionSlider *slider = qstyleoption_cast<const QStyleOptionSlider*>(option);
1014 if (!slider)
1015 return QSize();
1016
1017 QSize result = QCommonStyle::sizeFromContents(CT_Slider, option, contentsSize, widget);
1018
1019 const QPixmapStyleDescriptor desc = d->descriptors.value(slider->orientation == Qt::Horizontal
1020 ? SG_HEnabled : SG_VEnabled);
1021
1022 if (slider->orientation == Qt::Horizontal)
1023 return QSize(result.width(), desc.size.height());
1024 else
1025 return QSize(desc.size.width(), result.height());
1026#else // QT_CONFIG(slider)
1027 Q_UNUSED(option);
1028 Q_UNUSED(contentsSize);
1029 Q_UNUSED(widget);
1030 return QSize();
1031#endif // QT_CONFIG(slider)
1032}
1033
1034QSize QPixmapStyle::comboBoxSizeFromContents(const QStyleOption *option,
1035 const QSize &contentsSize,
1036 const QWidget *widget) const
1037{
1038 Q_D(const QPixmapStyle);
1039
1040 const QPixmapStyleDescriptor &desc = d->descriptors.value(DD_ButtonEnabled);
1041
1042 QSize result = QCommonStyle::sizeFromContents(CT_ComboBox, option, contentsSize, widget);
1043 return d->computeSize(desc, result.width(), result.height());
1044}
1045
1046QSize QPixmapStyle::itemViewSizeFromContents(const QStyleOption *option,
1047 const QSize &contentsSize,
1048 const QWidget *widget) const
1049{
1050 Q_D(const QPixmapStyle);
1051
1052 QSize size = QCommonStyle::sizeFromContents(CT_ItemViewItem, option, contentsSize, widget);
1053
1054 ControlPixmap cp = ID_Separator;
1055 ControlDescriptor cd = ID_Selected;
1056 if (widget && widget->property("_pixmap_combobox_list").toBool()) {
1057 cp = DD_ItemSeparator;
1058 cd = DD_ItemSelected;
1059 }
1060
1061 const QPixmapStyleDescriptor &desc = d->descriptors.value(cd);
1062 const QPixmapStylePixmap &pix = d->pixmaps.value(cp);
1063 size.setHeight(qMax(size.height(),
1064 desc.size.height() + pix.pixmap.height()));
1065 return size;
1066}
1067
1068QRect QPixmapStyle::comboBoxSubControlRect(const QStyleOptionComplex *option,
1069 QStyle::SubControl sc, const QWidget *) const
1070{
1071 Q_D(const QPixmapStyle);
1072
1073 QRect r = option->rect; // Default size
1074 const QPixmapStylePixmap &pix = d->pixmaps.value(DD_ArrowEnabled);
1075 const QPixmapStyleDescriptor &desc = d->descriptors.value(DD_ButtonEnabled);
1076
1077 switch (sc) {
1078 case SC_ComboBoxArrow:
1079 r.setRect(r.right() - pix.margins.right() - pix.pixmap.width(),
1080 r.top() + pix.margins.top(),
1081 pix.pixmap.width(), pix.pixmap.height());
1082 break;
1083 case SC_ComboBoxEditField:
1084 r.adjust(desc.margins.left(), desc.margins.right(),
1085 -desc.margins.right(), -desc.margins.bottom());
1086 r.setRight(r.right() - pix.margins.right() - pix.margins.left() - pix.pixmap.width());
1087 break;
1088 default:
1089 break;
1090 }
1091
1092 r = visualRect(option->direction, option->rect, r);
1093 return r;
1094}
1095
1096QRect QPixmapStyle::scrollBarSubControlRect(const QStyleOptionComplex *option,
1097 QStyle::SubControl sc, const QWidget *) const
1098{
1099#if QT_CONFIG(slider)
1100 if (const QStyleOptionSlider *slider =
1101 qstyleoption_cast<const QStyleOptionSlider*>(option)) {
1102 int length = (slider->orientation == Qt::Horizontal)
1103 ? slider->rect.width() : slider->rect.height();
1104 int page = length * slider->pageStep
1105 / (slider->maximum - slider->minimum + slider->pageStep);
1106 int pos = length * slider->sliderValue
1107 / (slider->maximum - slider->minimum + slider->pageStep);
1108 pos = qMin(pos+page, length) - page;
1109
1110 QRect rect = slider->rect;
1111
1112 if (slider->orientation == Qt::Horizontal) {
1113 switch (sc) {
1114 case SC_ScrollBarAddPage:
1115 rect.setLeft(pos+page);
1116 return rect;
1117 case SC_ScrollBarSubPage:
1118 rect.setRight(pos);
1119 return rect;
1120 case SC_ScrollBarGroove:
1121 return rect;
1122 case SC_ScrollBarSlider:
1123 rect.setLeft(pos);
1124 rect.setRight(pos+page);
1125 return rect;
1126 default: ;
1127 }
1128 } else {
1129 switch (sc) {
1130 case SC_ScrollBarAddPage:
1131 rect.setTop(pos+page);
1132 return rect;
1133 case SC_ScrollBarSubPage:
1134 rect.setBottom(pos);
1135 return rect;
1136 case SC_ScrollBarGroove:
1137 return rect;
1138 case SC_ScrollBarSlider:
1139 rect.setTop(pos);
1140 rect.setBottom(pos+page);
1141 return rect;
1142 default: ;
1143 }
1144 }
1145 }
1146#else
1147 Q_UNUSED(option);
1148 Q_UNUSED(sc);
1149#endif // QT_CONFIG(slider)
1150 return QRect();
1151}
1152
1153QPixmap QPixmapStylePrivate::scale(int w, int h, const QPixmap &pixmap, const QPixmapStyleDescriptor &desc)
1154{
1155 QPixmap result(w, h);
1156 {
1157 const QColor transparent(0, 0, 0, 0);
1158 result.fill( transparent );
1159 QPainter p( &result );
1160 const QMargins margins = desc.margins;
1161 qDrawBorderPixmap(&p, result.rect(), margins, pixmap,
1162 pixmap.rect(), margins, desc.tileRules);
1163 }
1164 return result;
1165}
1166
1167QPixmap QPixmapStylePrivate::getCachedPixmap(QPixmapStyle::ControlDescriptor control,
1168 const QPixmapStyleDescriptor &desc,
1169 const QSize &size) const
1170{
1171 Q_Q(const QPixmapStyle);
1172
1173 const QString sizeString = QString::number(size.width()) % u'*'
1174 % QString::number(size.height());
1175 const QString key = QLatin1StringView(q->metaObject()->className()) % QString::number(control)
1176 % u'@' % sizeString;
1177
1178 QPixmap result;
1179
1180 if (!QPixmapCache::find( key, &result)) {
1181 QPixmap source(desc.fileName);
1182 result = scale(size.width(), size.height(), source, desc);
1183 QPixmapCache::insert(key, result);
1184 }
1185 return result;
1186}
1187
1188QSize QPixmapStylePrivate::computeSize(const QPixmapStyleDescriptor &desc, int width, int height) const
1189{
1190 if (desc.tileRules.horizontal != Qt::RepeatTile)
1191 width = qMax(width, desc.size.width());
1192 if (desc.tileRules.vertical != Qt::RepeatTile)
1193 height = qMax(height, desc.size.height());
1194 return QSize(width, height);
1195}
1196
1197QT_END_NAMESPACE
1198
1199#include "moc_qpixmapstyle_p.cpp"
QPixmap getCachedPixmap(QPixmapStyle::ControlDescriptor control, const QPixmapStyleDescriptor &desc, const QSize &size) const
QSize computeSize(const QPixmapStyleDescriptor &desc, int width, int height) const