1 /****************************************************************************
2 **
3 ** Copyright (C) 2013 BogDan Vatra <bogdan@kde.org>
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of the QtWidgets module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
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 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 3 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL3 included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 3 requirements
23 ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24 **
25 ** GNU General Public License Usage
26 ** Alternatively, this file may be used under the terms of the GNU
27 ** General Public License version 2.0 or (at your option) the GNU General
28 ** Public license version 3 or any later version approved by the KDE Free
29 ** Qt Foundation. The licenses are as published by the Free Software
30 ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31 ** included in the packaging of this file. Please review the following
32 ** information to ensure the GNU General Public License requirements will
33 ** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34 ** https://www.gnu.org/licenses/gpl-3.0.html.
35 **
36 ** $QT_END_LICENSE$
37 **
38 ****************************************************************************/
39 
40 #include "qandroidstyle_p.h"
41 
42 #include <QFile>
43 #include <QFont>
44 #include <QApplication>
45 #include <qdrawutil.h>
46 #include <QPixmapCache>
47 #include <QFileInfo>
48 #include <QStyleOption>
49 #include <QPainter>
50 #include <QJsonDocument>
51 #include <QJsonObject>
52 #include <QDebug>
53 
54 #include <QGuiApplication>
55 #include <qpa/qplatformnativeinterface.h>
56 #include <qpa/qplatformtheme.h>
57 
58 QT_BEGIN_NAMESPACE
59 
60 namespace {
61     const quint32 NO_COLOR = 1;
62     const quint32 TRANSPARENT_COLOR = 0;
63 }
64 
QAndroidStyle()65 QAndroidStyle::QAndroidStyle()
66     : QFusionStyle()
67 {
68     QPixmapCache::clear();
69     checkBoxControl = NULL;
70     QPlatformNativeInterface *nativeInterface = QGuiApplication::platformNativeInterface();
71     QPalette *standardPalette = reinterpret_cast<QPalette *>(nativeInterface->nativeResourceForIntegration("AndroidStandardPalette"));
72     if (standardPalette)
73         m_standardPalette = *standardPalette;
74 
75     QHash<QByteArray, QFont> *qwidgetsFonts = reinterpret_cast<QHash<QByteArray, QFont> *>(nativeInterface->nativeResourceForIntegration("AndroidQWidgetFonts"));
76     if (qwidgetsFonts) {
77         for (auto it = qwidgetsFonts->constBegin(); it != qwidgetsFonts->constEnd(); ++it)
78             QApplication::setFont(it.value(), it.key());
79         qwidgetsFonts->clear(); // free the memory
80     }
81 
82     QJsonObject *object = reinterpret_cast<QJsonObject *>(nativeInterface->nativeResourceForIntegration("AndroidStyleData"));
83     if (!object)
84         return;
85 
86     for (QJsonObject::const_iterator objectIterator = object->constBegin();
87          objectIterator != object->constEnd();
88          ++objectIterator) {
89         QString key = objectIterator.key();
90         QJsonValue value = objectIterator.value();
91         if (Q_UNLIKELY(!value.isObject())) {
92             qWarning("Style.json structure is unrecognized.");
93             continue;
94         }
95 
96         QJsonObject item = value.toObject();
97         QAndroidStyle::ItemType itemType = qtControl(key);
98         if (QC_UnknownType == itemType)
99             continue;
100 
101         switch (itemType) {
102         case QC_Checkbox:
103             checkBoxControl = new AndroidCompoundButtonControl(item.toVariantMap(), itemType);
104             m_androidControlsHash[int(itemType)] = checkBoxControl;
105             break;
106         case QC_RadioButton:
107             m_androidControlsHash[int(itemType)] = new AndroidCompoundButtonControl(item.toVariantMap(),
108                                                                                     itemType);
109             break;
110 
111         case QC_ProgressBar:
112             m_androidControlsHash[int(itemType)] = new AndroidProgressBarControl(item.toVariantMap(),
113                                                                                  itemType);
114             break;
115 
116         case QC_Slider:
117             m_androidControlsHash[int(itemType)] = new AndroidSeekBarControl(item.toVariantMap(),
118                                                                              itemType);
119             break;
120 
121         case QC_Combobox:
122             m_androidControlsHash[int(itemType)] = new AndroidSpinnerControl(item.toVariantMap(),
123                                                                              itemType);
124             break;
125 
126         default:
127             m_androidControlsHash[int(itemType)] = new AndroidControl(item.toVariantMap(),
128                                                                       itemType);
129             break;
130         }
131     }
132     *object = QJsonObject(); // free memory
133 }
134 
~QAndroidStyle()135 QAndroidStyle::~QAndroidStyle()
136 {
137     qDeleteAll(m_androidControlsHash);
138 }
139 
qtControl(const QString & android)140 QAndroidStyle::ItemType QAndroidStyle::qtControl(const QString &android)
141 {
142     if (android == QLatin1String("buttonStyle"))
143         return QC_Button;
144     if (android == QLatin1String("editTextStyle"))
145         return QC_EditText;
146     if (android == QLatin1String("radioButtonStyle"))
147         return QC_RadioButton;
148     if (android == QLatin1String("checkboxStyle"))
149         return QC_Checkbox;
150     if (android == QLatin1String("textViewStyle"))
151         return QC_View;
152     if (android == QLatin1String("buttonStyleToggle"))
153         return QC_Switch;
154     if (android == QLatin1String("spinnerStyle"))
155         return QC_Combobox;
156     if (android == QLatin1String("progressBarStyleHorizontal"))
157         return QC_ProgressBar;
158     if (android == QLatin1String("seekBarStyle"))
159         return QC_Slider;
160 
161     return QC_UnknownType;
162 }
163 
qtControl(QStyle::ComplexControl control)164 QAndroidStyle::ItemType QAndroidStyle::qtControl(QStyle::ComplexControl control)
165 {
166     switch (control) {
167     case CC_ComboBox:
168         return QC_Combobox;
169     case CC_Slider:
170         return QC_Slider;
171     default:
172         return QC_UnknownType;
173     }
174 }
175 
qtControl(QStyle::ContentsType contentsType)176 QAndroidStyle::ItemType QAndroidStyle::qtControl(QStyle::ContentsType contentsType)
177 {
178     switch (contentsType) {
179     case CT_PushButton:
180         return QC_Button;
181     case CT_CheckBox:
182         return QC_Checkbox;
183     case CT_RadioButton:
184         return QC_RadioButton;
185     case CT_ComboBox:
186         return QC_Combobox;
187     case CT_ProgressBar:
188         return QC_ProgressBar;
189     case CT_Slider:
190         return QC_Slider;
191     case CT_ScrollBar:
192         return QC_Slider;
193     case CT_TabWidget:
194         return QC_Tab;
195     case CT_TabBarTab:
196         return QC_TabButton;
197     case CT_LineEdit:
198         return QC_EditText;
199     case CT_GroupBox:
200         return QC_GroupBox;
201     default:
202         return QC_UnknownType;
203     }
204 }
205 
qtControl(QStyle::ControlElement controlElement)206 QAndroidStyle::ItemType QAndroidStyle::qtControl(QStyle::ControlElement controlElement)
207 {
208     switch (controlElement) {
209     case CE_PushButton:
210     case CE_PushButtonBevel:
211     case CE_PushButtonLabel:
212         return QC_Button;
213 
214     case CE_CheckBox:
215     case CE_CheckBoxLabel:
216         return QC_Checkbox;
217 
218     case CE_RadioButton:
219     case CE_RadioButtonLabel:
220         return QC_RadioButton;
221 
222     case CE_TabBarTab:
223     case CE_TabBarTabShape:
224     case CE_TabBarTabLabel:
225         return QC_Tab;
226 
227     case CE_ProgressBar:
228     case CE_ProgressBarGroove:
229     case CE_ProgressBarContents:
230     case CE_ProgressBarLabel:
231         return QC_ProgressBar;
232 
233     case CE_ComboBoxLabel:
234         return QC_Combobox;
235 
236     case CE_ShapedFrame:
237         return QC_View;
238 
239     default:
240         return QC_UnknownType;
241     }
242 }
243 
qtControl(QStyle::PrimitiveElement primitiveElement)244 QAndroidStyle::ItemType QAndroidStyle::qtControl(QStyle::PrimitiveElement primitiveElement)
245 {
246     switch (primitiveElement) {
247     case QStyle::PE_PanelLineEdit:
248     case QStyle::PE_FrameLineEdit:
249         return QC_EditText;
250 
251     case QStyle::PE_IndicatorViewItemCheck:
252     case QStyle::PE_IndicatorCheckBox:
253         return QC_Checkbox;
254 
255     case QStyle::PE_FrameWindow:
256     case QStyle::PE_Widget:
257     case QStyle::PE_Frame:
258     case QStyle::PE_FrameFocusRect:
259         return QC_View;
260     default:
261         return QC_UnknownType;
262     }
263 }
264 
qtControl(QStyle::SubElement subElement)265 QAndroidStyle::ItemType QAndroidStyle::qtControl(QStyle::SubElement subElement)
266 {
267     switch (subElement) {
268     case QStyle::SE_LineEditContents:
269         return QC_EditText;
270 
271     case QStyle::SE_PushButtonContents:
272     case QStyle::SE_PushButtonFocusRect:
273         return QC_Button;
274 
275     case SE_RadioButtonContents:
276         return QC_RadioButton;
277 
278     case SE_CheckBoxContents:
279         return QC_Checkbox;
280 
281     default:
282         return QC_UnknownType;
283     }
284 }
285 
drawPrimitive(PrimitiveElement pe,const QStyleOption * opt,QPainter * p,const QWidget * w) const286 void QAndroidStyle::drawPrimitive(PrimitiveElement pe,
287                                   const QStyleOption *opt,
288                                   QPainter *p,
289                                   const QWidget *w) const
290 {
291     const ItemType itemType = qtControl(pe);
292     AndroidControlsHash::const_iterator it = itemType != QC_UnknownType
293                                              ? m_androidControlsHash.find(itemType)
294                                              : m_androidControlsHash.end();
295     if (it != m_androidControlsHash.end()) {
296         if (itemType != QC_EditText) {
297             it.value()->drawControl(opt, p, w);
298         } else {
299             QStyleOption copy(*opt);
300             copy.state &= ~QStyle::State_Sunken;
301             it.value()->drawControl(&copy, p, w);
302         }
303     } else if (pe == PE_FrameGroupBox) {
304         if (const QStyleOptionFrame *frame = qstyleoption_cast<const QStyleOptionFrame *>(opt)) {
305             if (frame->features & QStyleOptionFrame::Flat) {
306                 QRect fr = frame->rect;
307                 QPoint p1(fr.x(), fr.y() + 1);
308                 QPoint p2(fr.x() + fr.width(), p1.y());
309                 qDrawShadeLine(p, p1, p2, frame->palette, true,
310                                frame->lineWidth, frame->midLineWidth);
311             } else {
312                 qDrawShadeRect(p, frame->rect.x(), frame->rect.y(), frame->rect.width(),
313                                frame->rect.height(), frame->palette, true,
314                                frame->lineWidth, frame->midLineWidth);
315             }
316         }
317     } else {
318         QFusionStyle::drawPrimitive(pe, opt, p, w);
319     }
320 }
321 
322 
drawControl(QStyle::ControlElement element,const QStyleOption * opt,QPainter * p,const QWidget * w) const323 void QAndroidStyle::drawControl(QStyle::ControlElement element,
324                                 const QStyleOption *opt,
325                                 QPainter *p,
326                                 const QWidget *w) const
327 {
328     const ItemType itemType = qtControl(element);
329     AndroidControlsHash::const_iterator it = itemType != QC_UnknownType
330                                              ? m_androidControlsHash.find(itemType)
331                                              : m_androidControlsHash.end();
332     if (it != m_androidControlsHash.end()) {
333         AndroidControl *androidControl = it.value();
334 
335         if (element != QStyle::CE_CheckBoxLabel
336                 && element != QStyle::CE_PushButtonLabel
337                 && element != QStyle::CE_RadioButtonLabel
338                 && element != QStyle::CE_TabBarTabLabel
339                 && element != QStyle::CE_ProgressBarLabel) {
340             androidControl->drawControl(opt, p, w);
341         }
342 
343         if (element != QStyle::CE_PushButtonBevel
344                 && element != QStyle::CE_TabBarTabShape
345                 && element != QStyle::CE_ProgressBarGroove) {
346             switch (itemType) {
347             case QC_Button:
348                 if (const QStyleOptionButton *buttonOption =
349                     qstyleoption_cast<const QStyleOptionButton *>(opt)) {
350                     QMargins padding = androidControl->padding();
351                     QStyleOptionButton copy(*buttonOption);
352                     copy.rect.adjust(padding.left(), padding.top(), -padding.right(), -padding.bottom());
353                     QFusionStyle::drawControl(CE_PushButtonLabel, &copy, p, w);
354                 }
355                 break;
356             case QC_Checkbox:
357             case QC_RadioButton:
358                 if (const QStyleOptionButton *btn =
359                     qstyleoption_cast<const QStyleOptionButton *>(opt)) {
360                     const bool isRadio = (element == CE_RadioButton);
361                     QStyleOptionButton subopt(*btn);
362                     subopt.rect = subElementRect(isRadio ? SE_RadioButtonContents
363                                                  : SE_CheckBoxContents, btn, w);
364                     QFusionStyle::drawControl(isRadio ? CE_RadioButtonLabel : CE_CheckBoxLabel, &subopt, p, w);
365                 }
366                 break;
367             case QC_Combobox:
368                 if (const QStyleOptionComboBox *comboboxOption =
369                     qstyleoption_cast<const QStyleOptionComboBox *>(opt)) {
370                     QMargins padding = androidControl->padding();
371                     QStyleOptionComboBox copy (*comboboxOption);
372                     copy.rect.adjust(padding.left(), padding.top(), -padding.right(), -padding.bottom());
373                     QFusionStyle::drawControl(CE_ComboBoxLabel, comboboxOption, p, w);
374                 }
375                 break;
376             default:
377                 QFusionStyle::drawControl(element, opt, p, w);
378                 break;
379             }
380         }
381     } else {
382         QFusionStyle::drawControl(element, opt, p, w);
383     }
384 }
385 
subElementRect(SubElement subElement,const QStyleOption * option,const QWidget * widget) const386 QRect QAndroidStyle::subElementRect(SubElement subElement,
387                                     const QStyleOption *option,
388                                     const QWidget *widget) const
389 {
390     const ItemType itemType = qtControl(subElement);
391     AndroidControlsHash::const_iterator it = itemType != QC_UnknownType
392                                              ? m_androidControlsHash.find(itemType)
393                                              : m_androidControlsHash.end();
394     if (it != m_androidControlsHash.end())
395         return it.value()->subElementRect(subElement, option, widget);
396     return QFusionStyle::subElementRect(subElement, option, widget);
397 }
398 
drawComplexControl(ComplexControl cc,const QStyleOptionComplex * opt,QPainter * p,const QWidget * widget) const399 void QAndroidStyle::drawComplexControl(ComplexControl cc,
400                                        const QStyleOptionComplex *opt,
401                                        QPainter *p,
402                                        const QWidget *widget) const
403 {
404     const ItemType itemType = qtControl(cc);
405     AndroidControlsHash::const_iterator it = itemType != QC_UnknownType
406                                              ? m_androidControlsHash.find(itemType)
407                                              : m_androidControlsHash.end();
408     if (it != m_androidControlsHash.end()) {
409         it.value()->drawControl(opt, p, widget);
410         return;
411     }
412     if (cc == CC_GroupBox) {
413         if (const QStyleOptionGroupBox *groupBox = qstyleoption_cast<const QStyleOptionGroupBox *>(opt)) {
414             // Draw frame
415             QRect textRect = subControlRect(CC_GroupBox, opt, SC_GroupBoxLabel, widget);
416             QRect checkBoxRect;
417             if (groupBox->subControls & SC_GroupBoxCheckBox)
418                 checkBoxRect = subControlRect(CC_GroupBox, opt, SC_GroupBoxCheckBox, widget);
419             if (groupBox->subControls & QStyle::SC_GroupBoxFrame) {
420                 QStyleOptionFrame frame;
421                 frame.QStyleOption::operator=(*groupBox);
422                 frame.features = groupBox->features;
423                 frame.lineWidth = groupBox->lineWidth;
424                 frame.midLineWidth = groupBox->midLineWidth;
425                 frame.rect = subControlRect(CC_GroupBox, opt, SC_GroupBoxFrame, widget);
426                 p->save();
427                 QRegion region(groupBox->rect);
428                 if (!groupBox->text.isEmpty()) {
429                     bool ltr = groupBox->direction == Qt::LeftToRight;
430                     QRect finalRect;
431                     if (groupBox->subControls & QStyle::SC_GroupBoxCheckBox) {
432                         finalRect = checkBoxRect.united(textRect);
433                         finalRect.adjust(ltr ? -4 : 0, 0, ltr ? 0 : 4, 0);
434                     } else {
435                         finalRect = textRect;
436                     }
437                     region -= finalRect;
438                 }
439                 p->setClipRegion(region);
440                 drawPrimitive(PE_FrameGroupBox, &frame, p, widget);
441                 p->restore();
442             }
443 
444             // Draw title
445             if ((groupBox->subControls & QStyle::SC_GroupBoxLabel) && !groupBox->text.isEmpty()) {
446                 QColor textColor = groupBox->textColor;
447                 if (textColor.isValid())
448                     p->setPen(textColor);
449                 int alignment = int(groupBox->textAlignment);
450                 if (!styleHint(QStyle::SH_UnderlineShortcut, opt, widget))
451                     alignment |= Qt::TextHideMnemonic;
452 
453                 drawItemText(p, textRect, Qt::TextShowMnemonic | Qt::AlignHCenter | alignment,
454                              groupBox->palette, groupBox->state & State_Enabled, groupBox->text,
455                              textColor.isValid() ? QPalette::NoRole : QPalette::WindowText);
456 
457                 if (groupBox->state & State_HasFocus) {
458                     QStyleOptionFocusRect fropt;
459                     fropt.QStyleOption::operator=(*groupBox);
460                     fropt.rect = textRect;
461                     drawPrimitive(PE_FrameFocusRect, &fropt, p, widget);
462                 }
463             }
464 
465             // Draw checkbox
466             if (groupBox->subControls & SC_GroupBoxCheckBox) {
467                 QStyleOptionButton box;
468                 box.QStyleOption::operator=(*groupBox);
469                 box.rect = checkBoxRect;
470                 checkBoxControl->drawControl(&box, p, widget);
471             }
472         }
473         return;
474     }
475     QFusionStyle::drawComplexControl(cc, opt, p, widget);
476 }
477 
hitTestComplexControl(ComplexControl cc,const QStyleOptionComplex * opt,const QPoint & pt,const QWidget * widget) const478 QStyle::SubControl QAndroidStyle::hitTestComplexControl(ComplexControl cc,
479                                                         const QStyleOptionComplex *opt,
480                                                         const QPoint &pt,
481                                                         const QWidget *widget) const
482 {
483     const ItemType itemType = qtControl(cc);
484     AndroidControlsHash::const_iterator it = itemType != QC_UnknownType
485                                              ? m_androidControlsHash.find(itemType)
486                                              : m_androidControlsHash.end();
487     if (it != m_androidControlsHash.end()) {
488         switch (cc) {
489         case CC_Slider:
490             if (const QStyleOptionSlider *slider = qstyleoption_cast<const QStyleOptionSlider *>(opt)) {
491                 QRect r = it.value()->subControlRect(slider, SC_SliderHandle, widget);
492                 if (r.isValid() && r.contains(pt)) {
493                     return SC_SliderHandle;
494                 } else {
495                     r = it.value()->subControlRect(slider, SC_SliderGroove, widget);
496                     if (r.isValid() && r.contains(pt))
497                         return SC_SliderGroove;
498                 }
499             }
500             break;
501         default:
502             break;
503         }
504     }
505     return QFusionStyle::hitTestComplexControl(cc, opt, pt, widget);
506 }
507 
subControlRect(ComplexControl cc,const QStyleOptionComplex * opt,SubControl sc,const QWidget * widget) const508 QRect QAndroidStyle::subControlRect(ComplexControl cc,
509                                     const QStyleOptionComplex *opt,
510                                     SubControl sc,
511                                     const QWidget *widget) const
512 {
513     const ItemType itemType = qtControl(cc);
514     AndroidControlsHash::const_iterator it = itemType != QC_UnknownType
515                                              ? m_androidControlsHash.find(itemType)
516                                              : m_androidControlsHash.end();
517     if (it != m_androidControlsHash.end())
518         return it.value()->subControlRect(opt, sc, widget);
519     QRect rect = opt->rect;
520     switch (cc) {
521         case CC_GroupBox: {
522             if (const QStyleOptionGroupBox *groupBox = qstyleoption_cast<const QStyleOptionGroupBox *>(opt)) {
523                 QSize textSize = opt->fontMetrics.boundingRect(groupBox->text).size() + QSize(2, 2);
524                 QSize checkBoxSize = checkBoxControl->size(opt);
525                 int indicatorWidth = checkBoxSize.width();
526                 int indicatorHeight = checkBoxSize.height();
527                 QRect checkBoxRect;
528                 if (opt->subControls & QStyle::SC_GroupBoxCheckBox) {
529                     checkBoxRect.setWidth(indicatorWidth);
530                     checkBoxRect.setHeight(indicatorHeight);
531                 }
532                 checkBoxRect.moveLeft(1);
533                 QRect textRect = checkBoxRect;
534                 textRect.setSize(textSize);
535                 if (opt->subControls & QStyle::SC_GroupBoxCheckBox)
536                     textRect.translate(indicatorWidth + 5, (indicatorHeight - textSize.height()) / 2);
537                 if (sc == SC_GroupBoxFrame) {
538                     rect = opt->rect.adjusted(0, 0, 0, 0);
539                     rect.translate(0, textRect.height() / 2);
540                     rect.setHeight(rect.height() - textRect.height() / 2);
541                 } else if (sc == SC_GroupBoxContents) {
542                     QRect frameRect = opt->rect.adjusted(0, 0, 0, -groupBox->lineWidth);
543                     int margin = 3;
544                     int leftMarginExtension = 0;
545                     int topMargin = qMax(pixelMetric(PM_ExclusiveIndicatorHeight), opt->fontMetrics.height()) + groupBox->lineWidth;
546                     frameRect.adjust(leftMarginExtension + margin, margin + topMargin, -margin, -margin - groupBox->lineWidth);
547                     frameRect.translate(0, textRect.height() / 2);
548                     rect = frameRect;
549                     rect.setHeight(rect.height() - textRect.height() / 2);
550                 } else if (sc == SC_GroupBoxCheckBox) {
551                     rect = checkBoxRect;
552                 } else if (sc == SC_GroupBoxLabel) {
553                     rect = textRect;
554                 }
555                 return visualRect(opt->direction, opt->rect, rect);
556             }
557 
558             return rect;
559         }
560 
561         default:
562             break;
563     }
564 
565 
566     return QFusionStyle::subControlRect(cc, opt, sc, widget);
567 }
568 
pixelMetric(PixelMetric metric,const QStyleOption * option,const QWidget * widget) const569 int QAndroidStyle::pixelMetric(PixelMetric metric, const QStyleOption *option,
570                         const QWidget *widget) const
571 {
572     switch (metric) {
573     case PM_ButtonMargin:
574     case PM_FocusFrameVMargin:
575     case PM_FocusFrameHMargin:
576     case PM_ComboBoxFrameWidth:
577     case PM_SpinBoxFrameWidth:
578     case PM_ScrollBarExtent:
579         return 0;
580     case PM_IndicatorWidth:
581         return checkBoxControl->size(option).width();
582     case PM_IndicatorHeight:
583         return checkBoxControl->size(option).height();
584     default:
585         return QFusionStyle::pixelMetric(metric, option, widget);
586     }
587 
588 }
589 
sizeFromContents(ContentsType ct,const QStyleOption * opt,const QSize & contentsSize,const QWidget * w) const590 QSize QAndroidStyle::sizeFromContents(ContentsType ct,
591                                       const QStyleOption *opt,
592                                       const QSize &contentsSize,
593                                       const QWidget *w) const
594 {
595     QSize sz = QFusionStyle::sizeFromContents(ct, opt, contentsSize, w);
596     if (ct == CT_HeaderSection) {
597         if (const QStyleOptionHeader *hdr = qstyleoption_cast<const QStyleOptionHeader *>(opt)) {
598             bool nullIcon = hdr->icon.isNull();
599             int margin = pixelMetric(QStyle::PM_HeaderMargin, hdr, w);
600             int iconSize = nullIcon ? 0 : checkBoxControl->size(opt).width();
601             QSize txt;
602 /*
603  * These next 4 lines are a bad hack to fix a bug in case a QStyleSheet is applied at QApplication level.
604  * In that case, even if the stylesheet does not refer to headers, the header font is changed to application
605  * font, which is wrong. Even worst, hdr->fontMetrics(...) does not report the proper size.
606  */
607             if (qApp->styleSheet().isEmpty())
608                 txt = hdr->fontMetrics.size(0, hdr->text);
609             else
610                 txt = qApp->fontMetrics().size(0, hdr->text);
611 
612             sz.setHeight(margin + qMax(iconSize, txt.height()) + margin);
613             sz.setWidth((nullIcon ? 0 : margin) + iconSize
614                         + (hdr->text.isNull() ? 0 : margin) + txt.width() + margin);
615             if (hdr->sortIndicator != QStyleOptionHeader::None) {
616                 int margin = pixelMetric(QStyle::PM_HeaderMargin, hdr, w);
617                 if (hdr->orientation == Qt::Horizontal)
618                     sz.rwidth() += sz.height() + margin;
619                 else
620                     sz.rheight() += sz.width() + margin;
621             }
622             return sz;
623         }
624     }
625     const ItemType itemType = qtControl(ct);
626     AndroidControlsHash::const_iterator it = itemType != QC_UnknownType
627                                              ? m_androidControlsHash.find(itemType)
628                                              : m_androidControlsHash.end();
629     if (it != m_androidControlsHash.end())
630         return it.value()->sizeFromContents(opt, sz, w);
631     if (ct == CT_GroupBox) {
632         if (const QStyleOptionGroupBox *groupBox = qstyleoption_cast<const QStyleOptionGroupBox *>(opt)) {
633             QSize textSize = opt->fontMetrics.boundingRect(groupBox->text).size() + QSize(2, 2);
634             QSize checkBoxSize = checkBoxControl->size(opt);
635             int indicatorWidth = checkBoxSize.width();
636             int indicatorHeight = checkBoxSize.height();
637             QRect checkBoxRect;
638             if (groupBox->subControls & QStyle::SC_GroupBoxCheckBox) {
639                 checkBoxRect.setWidth(indicatorWidth);
640                 checkBoxRect.setHeight(indicatorHeight);
641             }
642             checkBoxRect.moveLeft(1);
643             QRect textRect = checkBoxRect;
644             textRect.setSize(textSize);
645             if (groupBox->subControls & QStyle::SC_GroupBoxCheckBox)
646                 textRect.translate(indicatorWidth + 5, (indicatorHeight - textSize.height()) / 2);
647             QRect u = textRect.united(checkBoxRect);
648             sz = QSize(sz.width(), sz.height() + u.height());
649         }
650     }
651     return sz;
652 }
653 
standardPixmap(StandardPixmap standardPixmap,const QStyleOption * opt,const QWidget * widget) const654 QPixmap QAndroidStyle::standardPixmap(StandardPixmap standardPixmap,
655                                       const QStyleOption *opt,
656                                       const QWidget *widget) const
657 {
658     return QFusionStyle::standardPixmap(standardPixmap, opt, widget);
659 }
660 
generatedIconPixmap(QIcon::Mode iconMode,const QPixmap & pixmap,const QStyleOption * opt) const661 QPixmap QAndroidStyle::generatedIconPixmap(QIcon::Mode iconMode,
662                                            const QPixmap &pixmap,
663                                            const QStyleOption *opt) const
664 {
665     return QFusionStyle::generatedIconPixmap(iconMode, pixmap, opt);
666 }
667 
styleHint(QStyle::StyleHint hint,const QStyleOption * option,const QWidget * widget,QStyleHintReturn * returnData) const668 int QAndroidStyle::styleHint(QStyle::StyleHint hint, const QStyleOption *option, const QWidget *widget, QStyleHintReturn *returnData) const
669 {
670     switch (hint) {
671     case SH_Slider_AbsoluteSetButtons:
672         return Qt::LeftButton;
673 
674     case SH_Slider_PageSetButtons:
675         return 0;
676 
677     case SH_RequestSoftwareInputPanel:
678         return RSIP_OnMouseClick;
679 
680     default:
681         return QFusionStyle::styleHint(hint, option, widget, returnData);
682     }
683 }
684 
standardPalette() const685 QPalette QAndroidStyle::standardPalette() const
686 {
687     return m_standardPalette;
688 }
689 
polish(QWidget * widget)690 void QAndroidStyle::polish(QWidget *widget)
691 {
692     widget->setAttribute(Qt::WA_StyledBackground, true);
693 }
694 
unpolish(QWidget * widget)695 void QAndroidStyle::unpolish(QWidget *widget)
696 {
697     widget->setAttribute(Qt::WA_StyledBackground, false);
698 }
699 
AndroidDrawable(const QVariantMap & drawable,QAndroidStyle::ItemType itemType)700 QAndroidStyle::AndroidDrawable::AndroidDrawable(const QVariantMap &drawable,
701                                                 QAndroidStyle::ItemType itemType)
702 {
703     initPadding(drawable);
704     m_itemType = itemType;
705 }
706 
~AndroidDrawable()707 QAndroidStyle::AndroidDrawable::~AndroidDrawable()
708 {
709 }
710 
initPadding(const QVariantMap & drawable)711 void QAndroidStyle::AndroidDrawable::initPadding(const QVariantMap &drawable)
712 {
713     QVariantMap::const_iterator it = drawable.find(QLatin1String("padding"));
714     if (it != drawable.end())
715         m_padding = extractMargins(it.value().toMap());
716 }
717 
padding() const718 const QMargins &QAndroidStyle::AndroidDrawable::padding() const
719 {
720     return m_padding;
721 }
722 
size() const723 QSize QAndroidStyle::AndroidDrawable::size() const
724 {
725     if (type() == Image || type() == NinePatch)
726         return static_cast<const QAndroidStyle::AndroidImageDrawable *>(this)->size();
727 
728     return QSize();
729 }
730 
fromMap(const QVariantMap & drawable,ItemType itemType)731 QAndroidStyle::AndroidDrawable * QAndroidStyle::AndroidDrawable::fromMap(const QVariantMap &drawable,
732                                                                          ItemType itemType)
733 {
734     const QString type = drawable.value(QLatin1String("type")).toString();
735     if (type == QLatin1String("image"))
736         return new QAndroidStyle::AndroidImageDrawable(drawable, itemType);
737     if (type == QLatin1String("9patch"))
738         return new QAndroidStyle::Android9PatchDrawable(drawable, itemType);
739     if (type == QLatin1String("stateslist"))
740         return new QAndroidStyle::AndroidStateDrawable(drawable, itemType);
741     if (type == QLatin1String("layer"))
742         return new QAndroidStyle::AndroidLayerDrawable(drawable, itemType);
743     if (type == QLatin1String("gradient"))
744         return new QAndroidStyle::AndroidGradientDrawable(drawable, itemType);
745     if (type == QLatin1String("clipDrawable"))
746         return new QAndroidStyle::AndroidClipDrawable(drawable, itemType);
747     if (type == QLatin1String("color"))
748         return new QAndroidStyle::AndroidColorDrawable(drawable, itemType);
749     return 0;
750 }
751 
extractMargins(const QVariantMap & value)752 QMargins QAndroidStyle::AndroidDrawable::extractMargins(const QVariantMap &value)
753 {
754     QMargins m;
755     m.setLeft(value.value(QLatin1String("left")).toInt());
756     m.setRight(value.value(QLatin1String("right")).toInt());
757     m.setTop(value.value(QLatin1String("top")).toInt());
758     m.setBottom(value.value(QLatin1String("bottom")).toInt());
759     return m;
760 }
761 
setPaddingLeftToSizeWidth()762 void QAndroidStyle::AndroidDrawable::setPaddingLeftToSizeWidth()
763 {
764     QSize sz = size();
765     if (m_padding.isNull() && !sz.isNull())
766         m_padding.setLeft(sz.width());
767 }
768 
769 
AndroidImageDrawable(const QVariantMap & drawable,QAndroidStyle::ItemType itemType)770 QAndroidStyle::AndroidImageDrawable::AndroidImageDrawable(const QVariantMap &drawable,
771                                                           QAndroidStyle::ItemType itemType)
772     : AndroidDrawable(drawable, itemType)
773 {
774     m_filePath = drawable.value(QLatin1String("path")).toString();
775     m_size.setHeight(drawable.value(QLatin1String("height")).toInt());
776     m_size.setWidth(drawable.value(QLatin1String("width")).toInt());
777 }
778 
type() const779 QAndroidStyle::AndroidDrawableType QAndroidStyle::AndroidImageDrawable::type() const
780 {
781     return QAndroidStyle::Image;
782 }
783 
draw(QPainter * painter,const QStyleOption * opt) const784 void QAndroidStyle::AndroidImageDrawable::draw(QPainter *painter, const QStyleOption *opt) const
785 {
786     if (m_hashKey.isEmpty())
787         m_hashKey = QFileInfo(m_filePath).fileName();
788 
789     QPixmap pm;
790     if (!QPixmapCache::find(m_hashKey, &pm)) {
791         pm.load(m_filePath);
792         QPixmapCache::insert(m_hashKey, pm);
793     }
794 
795     painter->drawPixmap(opt->rect.x(), opt->rect.y() + (opt->rect.height() - pm.height()) / 2, pm);
796 }
797 
size() const798 QSize QAndroidStyle::AndroidImageDrawable::size() const
799 {
800     return m_size;
801 }
802 
AndroidColorDrawable(const QVariantMap & drawable,ItemType itemType)803 QAndroidStyle::AndroidColorDrawable::AndroidColorDrawable(const QVariantMap &drawable,
804                                                           ItemType itemType)
805     : AndroidDrawable(drawable, itemType)
806 {
807     m_color.setRgba(QRgb(drawable.value(QLatin1String("color")).toInt()));
808 }
809 
type() const810 QAndroidStyle::AndroidDrawableType QAndroidStyle::AndroidColorDrawable::type() const
811 {
812     return QAndroidStyle::Color;
813 }
814 
draw(QPainter * painter,const QStyleOption * opt) const815 void QAndroidStyle::AndroidColorDrawable::draw(QPainter *painter, const QStyleOption *opt) const
816 {
817     painter->fillRect(opt->rect, m_color);
818 }
819 
Android9PatchDrawable(const QVariantMap & drawable,QAndroidStyle::ItemType itemType)820 QAndroidStyle::Android9PatchDrawable::Android9PatchDrawable(const QVariantMap &drawable,
821                                                             QAndroidStyle::ItemType itemType)
822     : AndroidImageDrawable(drawable.value(QLatin1String("drawable")).toMap(), itemType)
823 {
824     initPadding(drawable);
825     QVariantMap chunk = drawable.value(QLatin1String("chunkInfo")).toMap();
826     extractIntArray(chunk.value(QLatin1String("xdivs")).toList(), m_chunkData.xDivs);
827     extractIntArray(chunk.value(QLatin1String("ydivs")).toList(), m_chunkData.yDivs);
828     extractIntArray(chunk.value(QLatin1String("colors")).toList(), m_chunkData.colors);
829 }
830 
type() const831 QAndroidStyle::AndroidDrawableType QAndroidStyle::Android9PatchDrawable::type() const
832 {
833     return QAndroidStyle::NinePatch;
834 }
835 
calculateStretch(int boundsLimit,int startingPoint,int srcSpace,int numStrechyPixelsRemaining,int numFixedPixelsRemaining)836 int QAndroidStyle::Android9PatchDrawable::calculateStretch(int boundsLimit,
837                                                            int startingPoint,
838                                                            int srcSpace,
839                                                            int numStrechyPixelsRemaining,
840                                                            int numFixedPixelsRemaining)
841 {
842     int spaceRemaining = boundsLimit - startingPoint;
843     int stretchySpaceRemaining = spaceRemaining - numFixedPixelsRemaining;
844     return (float(srcSpace) * stretchySpaceRemaining / numStrechyPixelsRemaining + .5);
845 }
846 
extractIntArray(const QVariantList & values,QVector<int> & array)847 void QAndroidStyle::Android9PatchDrawable::extractIntArray(const QVariantList &values,
848                                                            QVector<int> & array)
849 {
850     for (const QVariant &value : values)
851         array << value.toInt();
852 }
853 
854 
draw(QPainter * painter,const QStyleOption * opt) const855 void QAndroidStyle::Android9PatchDrawable::draw(QPainter *painter, const QStyleOption *opt) const
856 {
857     if (m_hashKey.isEmpty())
858         m_hashKey = QFileInfo(m_filePath).fileName();
859 
860     QPixmap pixmap;
861     if (!QPixmapCache::find(m_hashKey, &pixmap)) {
862         pixmap.load(m_filePath);
863         QPixmapCache::insert(m_hashKey, pixmap);
864     }
865 
866     const QRect &bounds = opt->rect;
867 
868     // shamelessly stolen from Android's sources (NinepatchImpl.cpp) and adapted for Qt
869     const int pixmapWidth = pixmap.width();
870     const int pixmapHeight = pixmap.height();
871 
872     if (bounds.isNull() || !pixmapWidth || !pixmapHeight)
873         return;
874 
875     QPainter::RenderHints savedHints = painter->renderHints();
876 
877     // The patchs doesn't need smooth transform !
878     painter->setRenderHints(QPainter::SmoothPixmapTransform, false);
879 
880     QRectF dst;
881     QRectF src;
882 
883     const qint32 x0 = m_chunkData.xDivs[0];
884     const qint32 y0 = m_chunkData.yDivs[0];
885     const quint8 numXDivs = m_chunkData.xDivs.size();
886     const quint8 numYDivs = m_chunkData.yDivs.size();
887     int i;
888     int j;
889     int colorIndex = 0;
890     quint32 color;
891     bool xIsStretchable;
892     const bool initialXIsStretchable = (x0 == 0);
893     bool yIsStretchable = (y0 == 0);
894     const int bitmapWidth = pixmap.width();
895     const int bitmapHeight = pixmap.height();
896 
897     int *dstRights = static_cast<int *>(alloca((numXDivs + 1) * sizeof(int)));
898     bool dstRightsHaveBeenCached = false;
899 
900     int numStretchyXPixelsRemaining = 0;
901     for (i = 0; i < numXDivs; i += 2)
902         numStretchyXPixelsRemaining += m_chunkData.xDivs[i + 1] - m_chunkData.xDivs[i];
903 
904     int numFixedXPixelsRemaining = bitmapWidth - numStretchyXPixelsRemaining;
905     int numStretchyYPixelsRemaining = 0;
906     for (i = 0; i < numYDivs; i += 2)
907         numStretchyYPixelsRemaining += m_chunkData.yDivs[i + 1] - m_chunkData.yDivs[i];
908 
909     int numFixedYPixelsRemaining = bitmapHeight - numStretchyYPixelsRemaining;
910     src.setTop(0);
911     dst.setTop(bounds.top());
912     // The first row always starts with the top being at y=0 and the bottom
913     // being either yDivs[1] (if yDivs[0]=0) of yDivs[0].  In the former case
914     // the first row is stretchable along the Y axis, otherwise it is fixed.
915     // The last row always ends with the bottom being bitmap.height and the top
916     // being either yDivs[numYDivs-2] (if yDivs[numYDivs-1]=bitmap.height) or
917     // yDivs[numYDivs-1]. In the former case the last row is stretchable along
918     // the Y axis, otherwise it is fixed.
919     //
920     // The first and last columns are similarly treated with respect to the X
921     // axis.
922     //
923     // The above is to help explain some of the special casing that goes on the
924     // code below.
925 
926     // The initial yDiv and whether the first row is considered stretchable or
927     // not depends on whether yDiv[0] was zero or not.
928     for (j = yIsStretchable ? 1 : 0;
929          j <= numYDivs && src.top() < bitmapHeight;
930           j++, yIsStretchable = !yIsStretchable) {
931         src.setLeft(0);
932         dst.setLeft(bounds.left());
933         if (j == numYDivs) {
934             src.setBottom(bitmapHeight);
935             dst.setBottom(bounds.bottom());
936         } else {
937             src.setBottom(m_chunkData.yDivs[j]);
938             const int srcYSize = src.bottom() - src.top();
939             if (yIsStretchable) {
940                 dst.setBottom(dst.top() + calculateStretch(bounds.bottom(), dst.top(),
941                                                           srcYSize,
942                                                           numStretchyYPixelsRemaining,
943                                                           numFixedYPixelsRemaining));
944                 numStretchyYPixelsRemaining -= srcYSize;
945             } else {
946                 dst.setBottom(dst.top() + srcYSize);
947                 numFixedYPixelsRemaining -= srcYSize;
948             }
949         }
950 
951         xIsStretchable = initialXIsStretchable;
952         // The initial xDiv and whether the first column is considered
953         // stretchable or not depends on whether xDiv[0] was zero or not.
954         for (i = xIsStretchable ? 1 : 0;
955               i <= numXDivs && src.left() < bitmapWidth;
956               i++, xIsStretchable = !xIsStretchable) {
957             color = m_chunkData.colors[colorIndex++];
958             if (color != TRANSPARENT_COLOR)
959                 color = NO_COLOR;
960             if (i == numXDivs) {
961                 src.setRight(bitmapWidth);
962                 dst.setRight(bounds.right());
963             } else {
964                 src.setRight(m_chunkData.xDivs[i]);
965                 if (dstRightsHaveBeenCached) {
966                     dst.setRight(dstRights[i]);
967                 } else {
968                     const int srcXSize = src.right() - src.left();
969                     if (xIsStretchable) {
970                         dst.setRight(dst.left() + calculateStretch(bounds.right(), dst.left(),
971                                                                   srcXSize,
972                                                                   numStretchyXPixelsRemaining,
973                                                                   numFixedXPixelsRemaining));
974                         numStretchyXPixelsRemaining -= srcXSize;
975                     } else {
976                         dst.setRight(dst.left() + srcXSize);
977                         numFixedXPixelsRemaining -= srcXSize;
978                     }
979                     dstRights[i] = dst.right();
980                 }
981             }
982             // If this horizontal patch is too small to be displayed, leave
983             // the destination left edge where it is and go on to the next patch
984             // in the source.
985             if (src.left() >= src.right()) {
986                 src.setLeft(src.right());
987                 continue;
988             }
989             // Make sure that we actually have room to draw any bits
990             if (dst.right() <= dst.left() || dst.bottom() <= dst.top()) {
991                 goto nextDiv;
992             }
993             // If this patch is transparent, skip and don't draw.
994             if (color == TRANSPARENT_COLOR)
995                 goto nextDiv;
996             if (color != NO_COLOR)
997                 painter->fillRect(dst, QRgb(color));
998             else
999                 painter->drawPixmap(dst, pixmap, src);
1000 nextDiv:
1001             src.setLeft(src.right());
1002             dst.setLeft(dst.right());
1003         }
1004         src.setTop(src.bottom());
1005         dst.setTop(dst.bottom());
1006         dstRightsHaveBeenCached = true;
1007     }
1008     painter->setRenderHints(savedHints);
1009 }
1010 
AndroidGradientDrawable(const QVariantMap & drawable,QAndroidStyle::ItemType itemType)1011 QAndroidStyle::AndroidGradientDrawable::AndroidGradientDrawable(const QVariantMap &drawable,
1012                                                                 QAndroidStyle::ItemType itemType)
1013     : AndroidDrawable(drawable, itemType), m_orientation(TOP_BOTTOM)
1014 {
1015     m_radius = drawable.value(QLatin1String("radius")).toInt();
1016     if (m_radius < 0)
1017         m_radius = 0;
1018 
1019     QVariantList colors = drawable.value(QLatin1String("colors")).toList();
1020     QVariantList positions = drawable.value(QLatin1String("positions")).toList();
1021     int min = colors.size() < positions.size() ? colors.size() : positions.size();
1022     for (int i = 0; i < min; i++)
1023         m_gradient.setColorAt(positions.at(i).toDouble(), QRgb(colors.at(i).toInt()));
1024 
1025     QByteArray orientation = drawable.value(QLatin1String("orientation")).toByteArray();
1026     if (orientation == "TOP_BOTTOM") // draw the gradient from the top to the bottom
1027         m_orientation = TOP_BOTTOM;
1028     else if (orientation == "TR_BL") // draw the gradient from the top-right to the bottom-left
1029         m_orientation = TR_BL;
1030     else if (orientation == "RIGHT_LEFT") // draw the gradient from the right to the left
1031         m_orientation = RIGHT_LEFT;
1032     else if (orientation == "BR_TL") // draw the gradient from the bottom-right to the top-left
1033         m_orientation = BR_TL;
1034     else if (orientation == "BOTTOM_TOP") // draw the gradient from the bottom to the top
1035         m_orientation = BOTTOM_TOP;
1036     else if (orientation == "BL_TR") // draw the gradient from the bottom-left to the top-right
1037         m_orientation = BL_TR;
1038     else if (orientation == "LEFT_RIGHT") // draw the gradient from the left to the right
1039         m_orientation = LEFT_RIGHT;
1040     else if (orientation == "TL_BR") // draw the gradient from the top-left to the bottom-right
1041         m_orientation = TL_BR;
1042     else
1043         qWarning("AndroidGradientDrawable: unknown orientation");
1044 }
1045 
type() const1046 QAndroidStyle::AndroidDrawableType QAndroidStyle::AndroidGradientDrawable::type() const
1047 {
1048     return QAndroidStyle::Gradient;
1049 }
1050 
draw(QPainter * painter,const QStyleOption * opt) const1051 void QAndroidStyle::AndroidGradientDrawable::draw(QPainter *painter, const QStyleOption *opt) const
1052 {
1053     const int width = opt->rect.width();
1054     const int height = opt->rect.height();
1055     switch (m_orientation) {
1056     case TOP_BOTTOM:
1057         // draw the gradient from the top to the bottom
1058         m_gradient.setStart(width / 2, 0);
1059         m_gradient.setFinalStop(width / 2, height);
1060         break;
1061     case TR_BL:
1062         // draw the gradient from the top-right to the bottom-left
1063         m_gradient.setStart(width, 0);
1064         m_gradient.setFinalStop(0, height);
1065         break;
1066     case RIGHT_LEFT:
1067         // draw the gradient from the right to the left
1068         m_gradient.setStart(width, height / 2);
1069         m_gradient.setFinalStop(0, height / 2);
1070         break;
1071     case BR_TL:
1072         // draw the gradient from the bottom-right to the top-left
1073         m_gradient.setStart(width, height);
1074         m_gradient.setFinalStop(0, 0);
1075         break;
1076     case BOTTOM_TOP:
1077         // draw the gradient from the bottom to the top
1078         m_gradient.setStart(width / 2, height);
1079         m_gradient.setFinalStop(width / 2, 0);
1080         break;
1081     case BL_TR:
1082         // draw the gradient from the bottom-left to the top-right
1083         m_gradient.setStart(0, height);
1084         m_gradient.setFinalStop(width, 0);
1085         break;
1086     case LEFT_RIGHT:
1087         // draw the gradient from the left to the right
1088         m_gradient.setStart(0, height / 2);
1089         m_gradient.setFinalStop(width, height / 2);
1090         break;
1091     case TL_BR:
1092         // draw the gradient from the top-left to the bottom-right
1093         m_gradient.setStart(0, 0);
1094         m_gradient.setFinalStop(width, height);
1095         break;
1096     }
1097 
1098     const QBrush &oldBrush = painter->brush();
1099     const QPen oldPen = painter->pen();
1100     painter->setPen(Qt::NoPen);
1101     painter->setBrush(m_gradient);
1102     painter->drawRoundedRect(opt->rect, m_radius, m_radius);
1103     painter->setBrush(oldBrush);
1104     painter->setPen(oldPen);
1105 }
1106 
size() const1107 QSize QAndroidStyle::AndroidGradientDrawable::size() const
1108 {
1109     return QSize(m_radius * 2, m_radius * 2);
1110 }
1111 
AndroidClipDrawable(const QVariantMap & drawable,QAndroidStyle::ItemType itemType)1112 QAndroidStyle::AndroidClipDrawable::AndroidClipDrawable(const QVariantMap &drawable,
1113                                                         QAndroidStyle::ItemType itemType)
1114     : AndroidDrawable(drawable, itemType)
1115 {
1116     m_drawable = fromMap(drawable.value(QLatin1String("drawable")).toMap(), itemType);
1117     m_factor = 0;
1118     m_orientation = Qt::Horizontal;
1119 }
1120 
~AndroidClipDrawable()1121 QAndroidStyle::AndroidClipDrawable::~AndroidClipDrawable()
1122 {
1123     delete m_drawable;
1124 }
1125 
type() const1126 QAndroidStyle::AndroidDrawableType QAndroidStyle::AndroidClipDrawable::type() const
1127 {
1128     return QAndroidStyle::Clip;
1129 }
1130 
setFactor(double factor,Qt::Orientation orientation)1131 void QAndroidStyle::AndroidClipDrawable::setFactor(double factor, Qt::Orientation orientation)
1132 {
1133     m_factor = factor;
1134     m_orientation = orientation;
1135 }
1136 
draw(QPainter * painter,const QStyleOption * opt) const1137 void QAndroidStyle::AndroidClipDrawable::draw(QPainter *painter, const QStyleOption *opt) const
1138 {
1139     QStyleOption copy(*opt);
1140     if (m_orientation == Qt::Horizontal)
1141         copy.rect.setWidth(copy.rect.width() * m_factor);
1142     else
1143         copy.rect.setHeight(copy.rect.height() * m_factor);
1144 
1145     m_drawable->draw(painter, &copy);
1146 }
1147 
AndroidStateDrawable(const QVariantMap & drawable,QAndroidStyle::ItemType itemType)1148 QAndroidStyle::AndroidStateDrawable::AndroidStateDrawable(const QVariantMap &drawable,
1149                                                           QAndroidStyle::ItemType itemType)
1150     : AndroidDrawable(drawable, itemType)
1151 {
1152     const QVariantList states = drawable.value(QLatin1String("stateslist")).toList();
1153     for (const QVariant &stateVariant : states) {
1154         QVariantMap state = stateVariant.toMap();
1155         const int s = extractState(state.value(QLatin1String("states")).toMap());
1156         if (-1 == s)
1157             continue;
1158         const AndroidDrawable *ad = fromMap(state.value(QLatin1String("drawable")).toMap(), itemType);
1159         if (!ad)
1160             continue;
1161         StateType item;
1162         item.first = s;
1163         item.second = ad;
1164         m_states<<item;
1165     }
1166 }
1167 
~AndroidStateDrawable()1168 QAndroidStyle::AndroidStateDrawable::~AndroidStateDrawable()
1169 {
1170     for (const StateType &type : qAsConst(m_states))
1171         delete type.second;
1172 }
1173 
type() const1174 QAndroidStyle::AndroidDrawableType QAndroidStyle::AndroidStateDrawable::type() const
1175 {
1176     return QAndroidStyle::State;
1177 }
1178 
draw(QPainter * painter,const QStyleOption * opt) const1179 void QAndroidStyle::AndroidStateDrawable::draw(QPainter *painter, const QStyleOption *opt) const
1180 {
1181     const AndroidDrawable *drawable = bestAndroidStateMatch(opt);
1182     if (drawable)
1183         drawable->draw(painter, opt);
1184 }
sizeImage(const QStyleOption * opt) const1185 QSize QAndroidStyle::AndroidStateDrawable::sizeImage(const QStyleOption *opt) const
1186 {
1187     QSize s;
1188     const AndroidDrawable *drawable = bestAndroidStateMatch(opt);
1189     if (drawable)
1190         s = drawable->size();
1191     return s;
1192 }
1193 
bestAndroidStateMatch(const QStyleOption * opt) const1194 const QAndroidStyle::AndroidDrawable * QAndroidStyle::AndroidStateDrawable::bestAndroidStateMatch(const QStyleOption *opt) const
1195 {
1196     const AndroidDrawable *bestMatch = 0;
1197     if (!opt) {
1198         if (m_states.size())
1199             return m_states[0].second;
1200         return bestMatch;
1201     }
1202 
1203     uint bestCost = 0xffff;
1204     for (const StateType & state : m_states) {
1205         if (int(opt->state) == state.first)
1206             return state.second;
1207         uint cost = 1;
1208 
1209         int difference = int(opt->state^state.first);
1210 
1211         if (difference & QStyle::State_Active)
1212             cost <<= 1;
1213 
1214         if (difference & QStyle::State_Enabled)
1215             cost <<= 1;
1216 
1217         if (difference & QStyle::State_Raised)
1218             cost <<= 1;
1219 
1220         if (difference & QStyle::State_Sunken)
1221             cost <<= 1;
1222 
1223         if (difference & QStyle::State_Off)
1224             cost <<= 1;
1225 
1226         if (difference & QStyle::State_On)
1227             cost <<= 1;
1228 
1229         if (difference & QStyle::State_HasFocus)
1230             cost <<= 1;
1231 
1232         if (difference & QStyle::State_Selected)
1233             cost <<= 1;
1234 
1235         if (cost < bestCost) {
1236             bestCost = cost;
1237             bestMatch = state.second;
1238         }
1239     }
1240     return bestMatch;
1241 }
1242 
extractState(const QVariantMap & value)1243 int QAndroidStyle::AndroidStateDrawable::extractState(const QVariantMap &value)
1244 {
1245     QStyle::State state = QStyle::State_Enabled | QStyle::State_Active;;
1246     for (auto it = value.cbegin(), end = value.cend(); it != end; ++it) {
1247         const QString &key = it.key();
1248         bool val = it.value().toString() == QLatin1String("true");
1249         if (key == QLatin1String("enabled")) {
1250             state.setFlag(QStyle::State_Enabled, val);
1251             continue;
1252         }
1253 
1254         if (key == QLatin1String("window_focused")) {
1255             state.setFlag(QStyle::State_Active, val);
1256             continue;
1257         }
1258 
1259         if (key == QLatin1String("focused")) {
1260             state.setFlag(QStyle::State_HasFocus, val);
1261             continue;
1262         }
1263 
1264         if (key == QLatin1String("checked")) {
1265             state |= val ? QStyle::State_On : QStyle::State_Off;
1266             continue;
1267         }
1268 
1269         if (key == QLatin1String("pressed")) {
1270             state |= val ? QStyle::State_Sunken : QStyle::State_Raised;
1271             continue;
1272         }
1273 
1274         if (key == QLatin1String("selected")) {
1275             state.setFlag(QStyle::State_Selected, val);
1276             continue;
1277         }
1278 
1279         if (key == QLatin1String("active")) {
1280             state.setFlag(QStyle::State_Active, val);
1281             continue;
1282         }
1283 
1284         if (key == QLatin1String("multiline"))
1285             return 0;
1286 
1287         if (key == QLatin1String("background") && val)
1288             return -1;
1289     }
1290     return static_cast<int>(state);
1291 }
1292 
setPaddingLeftToSizeWidth()1293 void QAndroidStyle::AndroidStateDrawable::setPaddingLeftToSizeWidth()
1294 {
1295     for (const StateType &type : qAsConst(m_states))
1296         const_cast<AndroidDrawable *>(type.second)->setPaddingLeftToSizeWidth();
1297 }
1298 
AndroidLayerDrawable(const QVariantMap & drawable,QAndroidStyle::ItemType itemType)1299 QAndroidStyle::AndroidLayerDrawable::AndroidLayerDrawable(const QVariantMap &drawable,
1300                                                           QAndroidStyle::ItemType itemType)
1301     : AndroidDrawable(drawable, itemType)
1302 {
1303     m_id = 0;
1304     m_factor = 1;
1305     m_orientation = Qt::Horizontal;
1306     const QVariantList layers = drawable.value(QLatin1String("layers")).toList();
1307     for (const QVariant &layer : layers) {
1308         QVariantMap layerMap = layer.toMap();
1309         AndroidDrawable *ad = fromMap(layerMap, itemType);
1310         if (ad) {
1311             LayerType l;
1312             l.second =  ad;
1313             l.first = layerMap.value(QLatin1String("id")).toInt();
1314             m_layers << l;
1315         }
1316     }
1317 }
1318 
~AndroidLayerDrawable()1319 QAndroidStyle::AndroidLayerDrawable::~AndroidLayerDrawable()
1320 {
1321     for (const LayerType &layer : qAsConst(m_layers))
1322         delete layer.second;
1323 }
1324 
type() const1325 QAndroidStyle::AndroidDrawableType QAndroidStyle::AndroidLayerDrawable::type() const
1326 {
1327     return QAndroidStyle::Layer;
1328 }
1329 
setFactor(int id,double factor,Qt::Orientation orientation)1330 void QAndroidStyle::AndroidLayerDrawable::setFactor(int id, double factor, Qt::Orientation orientation)
1331 {
1332     m_id = id;
1333     m_factor = factor;
1334     m_orientation = orientation;
1335 }
1336 
draw(QPainter * painter,const QStyleOption * opt) const1337 void QAndroidStyle::AndroidLayerDrawable::draw(QPainter *painter, const QStyleOption *opt) const
1338 {
1339     for (const LayerType &layer : m_layers) {
1340         if (layer.first == m_id) {
1341             QStyleOption copy(*opt);
1342             if (m_orientation == Qt::Horizontal)
1343                 copy.rect.setWidth(copy.rect.width() * m_factor);
1344             else
1345                 copy.rect.setHeight(copy.rect.height() * m_factor);
1346             layer.second->draw(painter, &copy);
1347         } else {
1348             layer.second->draw(painter, opt);
1349         }
1350     }
1351 }
1352 
layer(int id) const1353 QAndroidStyle::AndroidDrawable *QAndroidStyle::AndroidLayerDrawable::layer(int id) const
1354 {
1355     for (const LayerType &layer : m_layers)
1356         if (layer.first == id)
1357             return layer.second;
1358     return 0;
1359 }
1360 
size() const1361 QSize QAndroidStyle::AndroidLayerDrawable::size() const
1362 {
1363     QSize sz;
1364     for (const LayerType &layer : m_layers)
1365         sz = sz.expandedTo(layer.second->size());
1366     return sz;
1367 }
1368 
AndroidControl(const QVariantMap & control,QAndroidStyle::ItemType itemType)1369 QAndroidStyle::AndroidControl::AndroidControl(const QVariantMap &control,
1370                                               QAndroidStyle::ItemType itemType)
1371 {
1372     QVariantMap::const_iterator it = control.find(QLatin1String("View_background"));
1373     if (it != control.end())
1374         m_background = AndroidDrawable::fromMap(it.value().toMap(), itemType);
1375     else
1376         m_background = 0;
1377 
1378     it = control.find(QLatin1String("View_minWidth"));
1379     if (it != control.end())
1380         m_minSize.setWidth(it.value().toInt());
1381 
1382     it = control.find(QLatin1String("View_minHeight"));
1383     if (it != control.end())
1384         m_minSize.setHeight(it.value().toInt());
1385 
1386     it = control.find(QLatin1String("View_maxWidth"));
1387     if (it != control.end())
1388         m_maxSize.setWidth(it.value().toInt());
1389 
1390     it = control.find(QLatin1String("View_maxHeight"));
1391     if (it != control.end())
1392         m_maxSize.setHeight(it.value().toInt());
1393 }
1394 
~AndroidControl()1395 QAndroidStyle::AndroidControl::~AndroidControl()
1396 {
1397     delete m_background;
1398 }
1399 
drawControl(const QStyleOption * opt,QPainter * p,const QWidget *)1400 void QAndroidStyle::AndroidControl::drawControl(const QStyleOption *opt, QPainter *p, const QWidget * /* w */)
1401 {
1402     if (m_background) {
1403         m_background->draw(p, opt);
1404     } else {
1405         if (const QStyleOptionFrame *frame = qstyleoption_cast<const QStyleOptionFrame *>(opt)) {
1406             if ((frame->state & State_Sunken) || (frame->state & State_Raised)) {
1407                 qDrawShadePanel(p, frame->rect, frame->palette, frame->state & State_Sunken,
1408                                 frame->lineWidth);
1409             } else {
1410                 qDrawPlainRect(p, frame->rect, frame->palette.foreground().color(), frame->lineWidth);
1411             }
1412         } else {
1413             if (const QStyleOptionFocusRect *fropt = qstyleoption_cast<const QStyleOptionFocusRect *>(opt)) {
1414                 QColor bg = fropt->backgroundColor;
1415                 QPen oldPen = p->pen();
1416                 if (bg.isValid()) {
1417                     int h, s, v;
1418                     bg.getHsv(&h, &s, &v);
1419                     if (v >= 128)
1420                         p->setPen(Qt::black);
1421                     else
1422                         p->setPen(Qt::white);
1423                 } else {
1424                     p->setPen(opt->palette.foreground().color());
1425                 }
1426                 QRect focusRect = opt->rect.adjusted(1, 1, -1, -1);
1427                 p->drawRect(focusRect.adjusted(0, 0, -1, -1)); //draw pen inclusive
1428                 p->setPen(oldPen);
1429             } else {
1430                 p->fillRect(opt->rect, opt->palette.brush(QPalette::Background));
1431             }
1432         }
1433     }
1434 }
1435 
subElementRect(QStyle::SubElement,const QStyleOption * option,const QWidget *) const1436 QRect QAndroidStyle::AndroidControl::subElementRect(QStyle::SubElement /* subElement */,
1437                                                     const QStyleOption *option,
1438                                                     const QWidget * /* widget */) const
1439 {
1440     if (const AndroidDrawable *drawable = backgroundDrawable()) {
1441         if (drawable->type() == State)
1442             drawable = static_cast<const AndroidStateDrawable *>(backgroundDrawable())->bestAndroidStateMatch(option);
1443 
1444         const QMargins &padding = drawable->padding();
1445 
1446         QRect r = option->rect.adjusted(padding.left(), padding.top(),
1447                                         -padding.right(), -padding.bottom());
1448 
1449         if (r.width() < m_minSize.width())
1450             r.setWidth(m_minSize.width());
1451 
1452         if (r.height() < m_minSize.height())
1453             r.setHeight(m_minSize.height());
1454 
1455         return visualRect(option->direction, option->rect, r);
1456     }
1457     return option->rect;
1458 }
1459 
subControlRect(const QStyleOptionComplex * option,QStyle::SubControl,const QWidget * widget) const1460 QRect QAndroidStyle::AndroidControl::subControlRect(const QStyleOptionComplex *option,
1461                                                     QStyle::SubControl /*sc*/,
1462                                                     const QWidget *widget) const
1463 {
1464     return subElementRect(QStyle::SE_CustomBase, option, widget);
1465 }
1466 
sizeFromContents(const QStyleOption * opt,const QSize & contentsSize,const QWidget *) const1467 QSize QAndroidStyle::AndroidControl::sizeFromContents(const QStyleOption *opt,
1468                                                       const QSize &contentsSize,
1469                                                       const QWidget * /* w */) const
1470 {
1471     QSize sz;
1472     if (const AndroidDrawable *drawable = backgroundDrawable()) {
1473 
1474         if (drawable->type() == State)
1475             drawable = static_cast<const AndroidStateDrawable*>(backgroundDrawable())->bestAndroidStateMatch(opt);
1476         const QMargins &padding = drawable->padding();
1477         sz.setWidth(padding.left() + padding.right());
1478         sz.setHeight(padding.top() + padding.bottom());
1479         if (sz.isEmpty())
1480             sz = drawable->size();
1481     }
1482     sz += contentsSize;
1483     if (contentsSize.height() < opt->fontMetrics.height())
1484         sz.setHeight(sz.height() + (opt->fontMetrics.height() - contentsSize.height()));
1485     if (sz.height() < m_minSize.height())
1486         sz.setHeight(m_minSize.height());
1487     if (sz.width() < m_minSize.width())
1488         sz.setWidth(m_minSize.width());
1489     return sz;
1490 }
1491 
padding()1492 QMargins QAndroidStyle::AndroidControl::padding()
1493 {
1494     if (const AndroidDrawable *drawable = m_background) {
1495         if (drawable->type() == State)
1496             drawable = static_cast<const AndroidStateDrawable *>(m_background)->bestAndroidStateMatch(0);
1497         return drawable->padding();
1498     }
1499     return QMargins();
1500 }
1501 
size(const QStyleOption * option)1502 QSize QAndroidStyle::AndroidControl::size(const QStyleOption *option)
1503 {
1504     if (const AndroidDrawable *drawable = backgroundDrawable()) {
1505         if (drawable->type() == State)
1506             drawable = static_cast<const AndroidStateDrawable *>(backgroundDrawable())->bestAndroidStateMatch(option);
1507         return drawable->size();
1508     }
1509     return QSize();
1510 }
1511 
backgroundDrawable() const1512 const QAndroidStyle::AndroidDrawable *QAndroidStyle::AndroidControl::backgroundDrawable() const
1513 {
1514     return m_background;
1515 }
1516 
AndroidCompoundButtonControl(const QVariantMap & control,ItemType itemType)1517 QAndroidStyle::AndroidCompoundButtonControl::AndroidCompoundButtonControl(const QVariantMap &control,
1518                                                                           ItemType itemType)
1519     : AndroidControl(control, itemType)
1520 {
1521     QVariantMap::const_iterator it = control.find(QLatin1String("CompoundButton_button"));
1522     if (it != control.end()) {
1523         m_button = AndroidDrawable::fromMap(it.value().toMap(), itemType);
1524         const_cast<AndroidDrawable *>(m_button)->setPaddingLeftToSizeWidth();
1525     } else {
1526         m_button = 0;
1527     }
1528 }
1529 
~AndroidCompoundButtonControl()1530 QAndroidStyle::AndroidCompoundButtonControl::~AndroidCompoundButtonControl()
1531 {
1532     delete m_button;
1533 }
1534 
drawControl(const QStyleOption * opt,QPainter * p,const QWidget * w)1535 void QAndroidStyle::AndroidCompoundButtonControl::drawControl(const QStyleOption *opt,
1536                                                               QPainter *p,
1537                                                               const QWidget *w)
1538 {
1539     AndroidControl::drawControl(opt, p, w);
1540     if (m_button)
1541         m_button->draw(p, opt);
1542 }
1543 
padding()1544 QMargins QAndroidStyle::AndroidCompoundButtonControl::padding()
1545 {
1546     if (m_button)
1547         return m_button->padding();
1548     return AndroidControl::padding();
1549 }
1550 
size(const QStyleOption * option)1551 QSize QAndroidStyle::AndroidCompoundButtonControl::size(const QStyleOption *option)
1552 {
1553     if (m_button) {
1554         if (m_button->type() == State)
1555             return static_cast<const AndroidStateDrawable *>(m_button)->bestAndroidStateMatch(option)->size();
1556         return m_button->size();
1557     }
1558     return AndroidControl::size(option);
1559 }
1560 
backgroundDrawable() const1561 const QAndroidStyle::AndroidDrawable * QAndroidStyle::AndroidCompoundButtonControl::backgroundDrawable() const
1562 {
1563     return m_background ? m_background : m_button;
1564 }
1565 
AndroidProgressBarControl(const QVariantMap & control,ItemType itemType)1566 QAndroidStyle::AndroidProgressBarControl::AndroidProgressBarControl(const QVariantMap &control,
1567                                                                     ItemType itemType)
1568     : AndroidControl(control, itemType)
1569 {
1570     QVariantMap::const_iterator it = control.find(QLatin1String("ProgressBar_indeterminateDrawable"));
1571     if (it != control.end())
1572         m_indeterminateDrawable = AndroidDrawable::fromMap(it.value().toMap(), itemType);
1573     else
1574         m_indeterminateDrawable = 0;
1575 
1576     it = control.find(QLatin1String("ProgressBar_progressDrawable"));
1577     if (it != control.end())
1578         m_progressDrawable = AndroidDrawable::fromMap(it.value().toMap(), itemType);
1579     else
1580         m_progressDrawable = 0;
1581 
1582     it = control.find(QLatin1String("ProgressBar_progress_id"));
1583     if (it != control.end())
1584         m_progressId = it.value().toInt();
1585 
1586     it = control.find(QLatin1String("ProgressBar_secondaryProgress_id"));
1587     if (it != control.end())
1588         m_secondaryProgress_id = it.value().toInt();
1589 
1590     it = control.find(QLatin1String("ProgressBar_minWidth"));
1591     if (it != control.end())
1592         m_minSize.setWidth(it.value().toInt());
1593 
1594     it = control.find(QLatin1String("ProgressBar_minHeight"));
1595     if (it != control.end())
1596         m_minSize.setHeight(it.value().toInt());
1597 
1598     it = control.find(QLatin1String("ProgressBar_maxWidth"));
1599     if (it != control.end())
1600         m_maxSize.setWidth(it.value().toInt());
1601 
1602     it = control.find(QLatin1String("ProgressBar_maxHeight"));
1603     if (it != control.end())
1604         m_maxSize.setHeight(it.value().toInt());
1605 }
1606 
~AndroidProgressBarControl()1607 QAndroidStyle::AndroidProgressBarControl::~AndroidProgressBarControl()
1608 {
1609     delete m_progressDrawable;
1610     delete m_indeterminateDrawable;
1611 }
1612 
drawControl(const QStyleOption * option,QPainter * p,const QWidget *)1613 void QAndroidStyle::AndroidProgressBarControl::drawControl(const QStyleOption *option, QPainter *p, const QWidget * /* w */)
1614 {
1615     if (!m_progressDrawable)
1616         return;
1617 
1618     if (const QStyleOptionProgressBar *pb = qstyleoption_cast<const QStyleOptionProgressBar *>(option)) {
1619         if (m_progressDrawable->type() == QAndroidStyle::Layer) {
1620             const double fraction = double(qint64(pb->progress) - pb->minimum) / (qint64(pb->maximum) - pb->minimum);
1621             QAndroidStyle::AndroidDrawable *clipDrawable = static_cast<QAndroidStyle::AndroidLayerDrawable *>(m_progressDrawable)->layer(m_progressId);
1622             if (clipDrawable->type() == QAndroidStyle::Clip)
1623                 static_cast<AndroidClipDrawable *>(clipDrawable)->setFactor(fraction, pb->orientation);
1624             else
1625                 static_cast<AndroidLayerDrawable *>(m_progressDrawable)->setFactor(m_progressId, fraction, pb->orientation);
1626         }
1627         m_progressDrawable->draw(p, option);
1628     }
1629 }
1630 
subElementRect(QStyle::SubElement subElement,const QStyleOption * option,const QWidget * widget) const1631 QRect QAndroidStyle::AndroidProgressBarControl::subElementRect(QStyle::SubElement subElement,
1632                                                                const QStyleOption *option,
1633                                                                const QWidget *widget) const
1634 {
1635     if (const QStyleOptionProgressBar *progressBarOption =
1636            qstyleoption_cast<const QStyleOptionProgressBar *>(option)) {
1637         const bool horizontal = progressBarOption->orientation == Qt::Vertical;
1638         if (!m_background)
1639             return option->rect;
1640 
1641         QMargins padding = m_background->padding();
1642         QRect p(padding.left(), padding.top(), padding.right() - padding.left(), padding.bottom() - padding.top());
1643         padding = m_indeterminateDrawable->padding();
1644         p |= QRect(padding.left(), padding.top(), padding.right() - padding.left(), padding.bottom() - padding.top());
1645         padding = m_progressDrawable->padding();
1646         p |= QRect(padding.left(), padding.top(), padding.right() - padding.left(), padding.bottom() - padding.top());
1647         QRect r = option->rect.adjusted(p.left(), p.top(), -p.right(), -p.bottom());
1648 
1649         if (horizontal) {
1650             if (r.height()<m_minSize.height())
1651                 r.setHeight(m_minSize.height());
1652 
1653             if (r.height()>m_maxSize.height())
1654                 r.setHeight(m_maxSize.height());
1655         } else {
1656             if (r.width()<m_minSize.width())
1657                 r.setWidth(m_minSize.width());
1658 
1659             if (r.width()>m_maxSize.width())
1660                 r.setWidth(m_maxSize.width());
1661         }
1662         return visualRect(option->direction, option->rect, r);
1663     }
1664     return AndroidControl::subElementRect(subElement, option, widget);
1665 }
1666 
sizeFromContents(const QStyleOption * opt,const QSize & contentsSize,const QWidget *) const1667 QSize QAndroidStyle::AndroidProgressBarControl::sizeFromContents(const QStyleOption *opt,
1668                                                                  const QSize &contentsSize,
1669                                                                  const QWidget * /* w */) const
1670 {
1671     QSize sz(contentsSize);
1672     if (sz.height() < m_minSize.height())
1673         sz.setHeight(m_minSize.height());
1674     if (sz.width() < m_minSize.width())
1675         sz.setWidth(m_minSize.width());
1676 
1677     if (const QStyleOptionProgressBar *progressBarOption =
1678            qstyleoption_cast<const QStyleOptionProgressBar *>(opt)) {
1679         if (progressBarOption->orientation == Qt::Vertical) {
1680             if (sz.height() > m_maxSize.height())
1681                 sz.setHeight(m_maxSize.height());
1682         } else {
1683             if (sz.width() > m_maxSize.width())
1684                 sz.setWidth(m_maxSize.width());
1685         }
1686     }
1687     return contentsSize;
1688 }
1689 
AndroidSeekBarControl(const QVariantMap & control,ItemType itemType)1690 QAndroidStyle::AndroidSeekBarControl::AndroidSeekBarControl(const QVariantMap &control,
1691                                                             ItemType itemType)
1692     : AndroidProgressBarControl(control, itemType)
1693 {
1694     QVariantMap::const_iterator it = control.find(QLatin1String("SeekBar_thumb"));
1695     if (it != control.end())
1696         m_seekBarThumb = AndroidDrawable::fromMap(it.value().toMap(), itemType);
1697     else
1698         m_seekBarThumb = 0;
1699 }
1700 
~AndroidSeekBarControl()1701 QAndroidStyle::AndroidSeekBarControl::~AndroidSeekBarControl()
1702 {
1703     delete m_seekBarThumb;
1704 }
1705 
drawControl(const QStyleOption * option,QPainter * p,const QWidget *)1706 void QAndroidStyle::AndroidSeekBarControl::drawControl(const QStyleOption *option,
1707                                                        QPainter *p,
1708                                                        const QWidget * /* w */)
1709 {
1710     if (!m_seekBarThumb || !m_progressDrawable)
1711         return;
1712 
1713     if (const QStyleOptionSlider *styleOption =
1714            qstyleoption_cast<const QStyleOptionSlider *>(option)) {
1715         double factor = double(styleOption->sliderPosition - styleOption->minimum)
1716                 / double(styleOption->maximum - styleOption->minimum);
1717 
1718         // Android does not have a vertical slider. To support the vertical orientation, we rotate
1719         // the painter and pretend that we are horizontal.
1720         if (styleOption->orientation == Qt::Vertical)
1721             factor = 1 - factor;
1722 
1723         if (m_progressDrawable->type() == QAndroidStyle::Layer) {
1724             QAndroidStyle::AndroidDrawable *clipDrawable = static_cast<QAndroidStyle::AndroidLayerDrawable *>(m_progressDrawable)->layer(m_progressId);
1725             if (clipDrawable->type() == QAndroidStyle::Clip)
1726                 static_cast<QAndroidStyle::AndroidClipDrawable *>(clipDrawable)->setFactor(factor, Qt::Horizontal);
1727             else
1728                 static_cast<QAndroidStyle::AndroidLayerDrawable *>(m_progressDrawable)->setFactor(m_progressId, factor, Qt::Horizontal);
1729         }
1730         const AndroidDrawable *drawable = m_seekBarThumb;
1731         if (drawable->type() == State)
1732             drawable = static_cast<const QAndroidStyle::AndroidStateDrawable *>(m_seekBarThumb)->bestAndroidStateMatch(option);
1733         QStyleOption copy(*option);
1734 
1735         p->save();
1736 
1737         if (styleOption->orientation == Qt::Vertical) {
1738             // rotate the painter, and transform the rectangle to match
1739             p->rotate(90);
1740             copy.rect = QRect(copy.rect.y(), copy.rect.x() - copy.rect.width(), copy.rect.height(), copy.rect.width());
1741         }
1742 
1743         copy.rect.setHeight(m_progressDrawable->size().height());
1744         copy.rect.setWidth(copy.rect.width() - drawable->size().width());
1745         const int yTranslate = abs(drawable->size().height() - copy.rect.height()) / 2;
1746         copy.rect.translate(drawable->size().width() / 2, yTranslate);
1747         m_progressDrawable->draw(p, &copy);
1748         int pos = copy.rect.width() * factor - drawable->size().width() / 2;
1749         copy.rect.translate(pos, -yTranslate);
1750         copy.rect.setSize(drawable->size());
1751         m_seekBarThumb->draw(p, &copy);
1752 
1753         p->restore();
1754     }
1755 }
1756 
sizeFromContents(const QStyleOption * opt,const QSize & contentsSize,const QWidget * w) const1757 QSize QAndroidStyle::AndroidSeekBarControl::sizeFromContents(const QStyleOption *opt,
1758                                                              const QSize &contentsSize,
1759                                                              const QWidget *w) const
1760 {
1761     QSize sz = AndroidProgressBarControl::sizeFromContents(opt, contentsSize, w);
1762     if (!m_seekBarThumb)
1763         return sz;
1764     const AndroidDrawable *drawable = m_seekBarThumb;
1765     if (drawable->type() == State)
1766         drawable = static_cast<const QAndroidStyle::AndroidStateDrawable *>(m_seekBarThumb)->bestAndroidStateMatch(opt);
1767     return sz.expandedTo(drawable->size());
1768 }
1769 
subControlRect(const QStyleOptionComplex * option,SubControl sc,const QWidget *) const1770 QRect QAndroidStyle::AndroidSeekBarControl::subControlRect(const QStyleOptionComplex *option,
1771                                                            SubControl sc,
1772                                                            const QWidget * /* widget */) const
1773 {
1774     const QStyleOptionSlider *styleOption =
1775         qstyleoption_cast<const QStyleOptionSlider *>(option);
1776 
1777     if (m_seekBarThumb && sc == SC_SliderHandle && styleOption) {
1778         const AndroidDrawable *drawable = m_seekBarThumb;
1779         if (drawable->type() == State)
1780             drawable = static_cast<const QAndroidStyle::AndroidStateDrawable *>(m_seekBarThumb)->bestAndroidStateMatch(option);
1781 
1782         QRect r(option->rect);
1783         double factor = double(styleOption->sliderPosition - styleOption->minimum)
1784                 / (styleOption->maximum - styleOption->minimum);
1785         if (styleOption->orientation == Qt::Vertical) {
1786             int pos = option->rect.height() * (1 - factor) - double(drawable->size().height() / 2);
1787             r.setY(r.y() + pos);
1788         } else {
1789             int pos = option->rect.width() * factor - double(drawable->size().width() / 2);
1790             r.setX(r.x() + pos);
1791         }
1792         r.setSize(drawable->size());
1793         return r;
1794     }
1795     return option->rect;
1796 }
1797 
AndroidSpinnerControl(const QVariantMap & control,QAndroidStyle::ItemType itemType)1798 QAndroidStyle::AndroidSpinnerControl::AndroidSpinnerControl(const QVariantMap &control,
1799                                                             QAndroidStyle::ItemType itemType)
1800     : AndroidControl(control, itemType)
1801 {}
1802 
subControlRect(const QStyleOptionComplex * option,SubControl sc,const QWidget * widget) const1803 QRect QAndroidStyle::AndroidSpinnerControl::subControlRect(const QStyleOptionComplex *option,
1804                                                            SubControl sc,
1805                                                            const QWidget *widget) const
1806 {
1807     if (sc == QStyle::SC_ComboBoxListBoxPopup)
1808         return option->rect;
1809     if (sc == QStyle::SC_ComboBoxArrow) {
1810         const QRect editField = subControlRect(option, QStyle::SC_ComboBoxEditField, widget);
1811         return QRect(editField.topRight(), QSize(option->rect.width() - editField.width(), option->rect.height()));
1812     }
1813     return AndroidControl::subControlRect(option, sc, widget);
1814 }
1815 
1816 QT_END_NAMESPACE
1817