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(©, 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, ©, 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, ©);
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, ©);
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, ©);
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, ©);
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