1 /****************************************************************************
2 **
3 ** Copyright (C) 2016 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of the 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 #include "qwindowsxpstyle_p.h"
40 #include "qwindowsxpstyle_p_p.h"
41
42 #include <private/qobject_p.h>
43 #include <private/qpaintengine_raster_p.h>
44 #include <private/qapplication_p.h>
45 #include <qpa/qplatformnativeinterface.h>
46 #include <private/qstylehelper_p.h>
47 #include <private/qwidget_p.h>
48 #include <qpainter.h>
49 #include <qpaintengine.h>
50 #include <qwidget.h>
51 #include <qbackingstore.h>
52 #include <qapplication.h>
53 #include <qpixmapcache.h>
54 #include <private/qapplication_p.h>
55 #include <qpa/qplatformnativeinterface.h>
56
57 #if QT_CONFIG(toolbutton)
58 #include <qtoolbutton.h>
59 #endif
60 #if QT_CONFIG(tabbar)
61 #include <qtabbar.h>
62 #endif
63 #if QT_CONFIG(combobox)
64 #include <qcombobox.h>
65 #endif
66 #if QT_CONFIG(scrollbar)
67 #include <qscrollbar.h>
68 #endif
69 #include <qheaderview.h>
70 #if QT_CONFIG(spinbox)
71 #include <qspinbox.h>
72 #endif
73 #if QT_CONFIG(listview)
74 #include <qlistview.h>
75 #endif
76 #if QT_CONFIG(stackedwidget)
77 #include <qstackedwidget.h>
78 #endif
79 #if QT_CONFIG(pushbutton)
80 #include <qpushbutton.h>
81 #endif
82 #if QT_CONFIG(toolbar)
83 #include <qtoolbar.h>
84 #endif
85 #include <qlabel.h>
86 #include <qvarlengtharray.h>
87 #include <qdebug.h>
88
89 #include <algorithm>
90
91 QT_BEGIN_NAMESPACE
92
93 // General const values
94 static const int windowsItemFrame = 2; // menu item frame width
95 static const int windowsItemHMargin = 3; // menu item hor text margin
96 static const int windowsItemVMargin = 0; // menu item ver text margin
97 static const int windowsArrowHMargin = 6; // arrow horizontal margin
98 static const int windowsRightBorder = 12; // right border on windows
99
100 // External function calls
101 extern Q_WIDGETS_EXPORT HDC qt_win_display_dc();
102 extern QRegion qt_region_from_HRGN(HRGN rgn);
103
104 // Theme names matching the QWindowsXPStylePrivate::Theme enumeration.
105 static const wchar_t *themeNames[QWindowsXPStylePrivate::NThemes] =
106 {
107 L"BUTTON", L"COMBOBOX", L"EDIT", L"HEADER", L"LISTVIEW",
108 L"MENU", L"PROGRESS", L"REBAR", L"SCROLLBAR", L"SPIN",
109 L"TAB", L"TASKDIALOG", L"TOOLBAR", L"TOOLTIP", L"TRACKBAR",
110 L"TREEVIEW", L"WINDOW", L"STATUS", L"TREEVIEW"
111 };
112
backingStoreForWidget(const QWidget * widget)113 static inline QBackingStore *backingStoreForWidget(const QWidget *widget)
114 {
115 if (QBackingStore *backingStore = widget->backingStore())
116 return backingStore;
117 if (const QWidget *topLevel = widget->nativeParentWidget())
118 if (QBackingStore *topLevelBackingStore = topLevel->backingStore())
119 return topLevelBackingStore;
120 return nullptr;
121 }
122
hdcForWidgetBackingStore(const QWidget * widget)123 static inline HDC hdcForWidgetBackingStore(const QWidget *widget)
124 {
125 if (QBackingStore *backingStore = backingStoreForWidget(widget)) {
126 QPlatformNativeInterface *nativeInterface = QGuiApplication::platformNativeInterface();
127 if (nativeInterface)
128 return static_cast<HDC>(nativeInterface->nativeResourceForBackingStore(QByteArrayLiteral("getDC"), backingStore));
129 }
130 return nullptr;
131 }
132
133 // Theme data helper ------------------------------------------------------------------------------
134 /* \internal
135 Returns \c true if the themedata is valid for use.
136 */
isValid()137 bool XPThemeData::isValid()
138 {
139 return QWindowsXPStylePrivate::useXP() && theme >= 0 && handle();
140 }
141
142
143 /* \internal
144 Returns the theme engine handle to the specific class.
145 If the handle hasn't been opened before, it opens the data, and
146 adds it to a static map, for caching.
147 */
handle()148 HTHEME XPThemeData::handle()
149 {
150 if (!QWindowsXPStylePrivate::useXP())
151 return nullptr;
152
153 if (!htheme)
154 htheme = QWindowsXPStylePrivate::createTheme(theme, QWindowsXPStylePrivate::winId(widget));
155 return htheme;
156 }
157
158 /* \internal
159 Converts a QRect to the native RECT structure.
160 */
toRECT(const QRect & qr)161 RECT XPThemeData::toRECT(const QRect &qr)
162 {
163 RECT r;
164 r.left = qr.x();
165 r.right = qr.x() + qr.width();
166 r.top = qr.y();
167 r.bottom = qr.y() + qr.height();
168 return r;
169 }
170
171 /* \internal
172 Returns the native region of a part, if the part is considered
173 transparent. The region is scaled to the parts size (rect).
174 */
mask(QWidget * widget)175 HRGN XPThemeData::mask(QWidget *widget)
176 {
177 if (!IsThemeBackgroundPartiallyTransparent(handle(), partId, stateId))
178 return nullptr;
179
180 HRGN hrgn;
181 HDC dc = nullptr;
182 if (widget)
183 dc = hdcForWidgetBackingStore(widget);
184 RECT nativeRect = toRECT(rect);
185 GetThemeBackgroundRegion(handle(), dc, partId, stateId, &nativeRect, &hrgn);
186 return hrgn;
187 }
188
189 // QWindowsXPStylePrivate -------------------------------------------------------------------------
190 // Static initializations
191 HWND QWindowsXPStylePrivate::m_vistaTreeViewHelper = nullptr;
192 HTHEME QWindowsXPStylePrivate::m_themes[NThemes];
193 bool QWindowsXPStylePrivate::use_xp = false;
194 QBasicAtomicInt QWindowsXPStylePrivate::ref = Q_BASIC_ATOMIC_INITIALIZER(-1); // -1 based refcounting
195
qt_add_rect(HRGN & winRegion,QRect r)196 static void qt_add_rect(HRGN &winRegion, QRect r)
197 {
198 HRGN rgn = CreateRectRgn(r.left(), r.top(), r.x() + r.width(), r.y() + r.height());
199 if (rgn) {
200 HRGN dest = CreateRectRgn(0,0,0,0);
201 int result = CombineRgn(dest, winRegion, rgn, RGN_OR);
202 if (result) {
203 DeleteObject(winRegion);
204 winRegion = dest;
205 }
206 DeleteObject(rgn);
207 }
208 }
209
qt_hrgn_from_qregion(const QRegion & region)210 static HRGN qt_hrgn_from_qregion(const QRegion ®ion)
211 {
212 HRGN hRegion = CreateRectRgn(0,0,0,0);
213 if (region.rectCount() == 1) {
214 qt_add_rect(hRegion, region.boundingRect());
215 return hRegion;
216 }
217 for (const QRect &rect : region)
218 qt_add_rect(hRegion, rect);
219 return hRegion;
220 }
221
222 /* \internal
223 Checks if the theme engine can/should be used, or if we should
224 fall back to Windows style.
225 */
useXP(bool update)226 bool QWindowsXPStylePrivate::useXP(bool update)
227 {
228 if (update) {
229 use_xp = IsThemeActive() && (IsAppThemed() || !QCoreApplication::instance())
230 && !QWindowsStylePrivate::isDarkMode();
231 }
232 return use_xp;
233 }
234
235 /* \internal
236 Handles refcounting, and queries the theme engine for usage.
237 */
init(bool force)238 void QWindowsXPStylePrivate::init(bool force)
239 {
240 if (ref.ref() && !force)
241 return;
242 if (!force) // -1 based atomic refcounting
243 ref.ref();
244
245 useXP(true);
246 std::fill(m_themes, m_themes + NThemes, nullptr);
247 }
248
249 /* \internal
250 Cleans up all static data.
251 */
cleanup(bool force)252 void QWindowsXPStylePrivate::cleanup(bool force)
253 {
254 if(bufferBitmap) {
255 if (bufferDC && nullBitmap)
256 SelectObject(bufferDC, nullBitmap);
257 DeleteObject(bufferBitmap);
258 bufferBitmap = nullptr;
259 }
260
261 if(bufferDC)
262 DeleteDC(bufferDC);
263 bufferDC = nullptr;
264
265 if (ref.deref() && !force)
266 return;
267 if (!force) // -1 based atomic refcounting
268 ref.deref();
269
270 use_xp = false;
271 cleanupHandleMap();
272 }
273
274 /* In order to obtain the correct VistaTreeViewTheme (arrows for PE_IndicatorBranch),
275 * we need to set the windows "explorer" theme explicitly on a native
276 * window and open the "TREEVIEW" theme handle passing its window handle
277 * in order to get Vista-style item view themes (particulary drawBackground()
278 * for selected items needs this).
279 * We invoke a service of the native Windows interface to create
280 * a non-visible window handle, open the theme on it and insert it into
281 * the cache so that it is found by XPThemeData::handle() first.
282 */
283
createTreeViewHelperWindow()284 static inline HWND createTreeViewHelperWindow()
285 {
286 if (QPlatformNativeInterface *ni = QGuiApplication::platformNativeInterface()) {
287 void *hwnd = nullptr;
288 void *wndProc = reinterpret_cast<void *>(DefWindowProc);
289 if (QMetaObject::invokeMethod(ni, "createMessageWindow", Qt::DirectConnection,
290 Q_RETURN_ARG(void*, hwnd),
291 Q_ARG(QString, QStringLiteral("QTreeViewThemeHelperWindowClass")),
292 Q_ARG(QString, QStringLiteral("QTreeViewThemeHelperWindow")),
293 Q_ARG(void*, wndProc)) && hwnd) {
294 return reinterpret_cast<HWND>(hwnd);
295 }
296 }
297 return nullptr;
298 }
299
initVistaTreeViewTheming()300 bool QWindowsXPStylePrivate::initVistaTreeViewTheming()
301 {
302 if (m_vistaTreeViewHelper)
303 return true;
304
305 m_vistaTreeViewHelper = createTreeViewHelperWindow();
306 if (!m_vistaTreeViewHelper) {
307 qWarning("Unable to create the treeview helper window.");
308 return false;
309 }
310 if (FAILED(SetWindowTheme(m_vistaTreeViewHelper, L"explorer", nullptr))) {
311 qErrnoWarning("SetWindowTheme() failed.");
312 cleanupVistaTreeViewTheming();
313 return false;
314 }
315 return true;
316 }
317
cleanupVistaTreeViewTheming()318 void QWindowsXPStylePrivate::cleanupVistaTreeViewTheming()
319 {
320 if (m_vistaTreeViewHelper) {
321 DestroyWindow(m_vistaTreeViewHelper);
322 m_vistaTreeViewHelper = nullptr;
323 }
324 }
325
326 /* \internal
327 Closes all open theme data handles to ensure that we don't leak
328 resources, and that we don't refere to old handles when for
329 example the user changes the theme style.
330 */
cleanupHandleMap()331 void QWindowsXPStylePrivate::cleanupHandleMap()
332 {
333 for (auto &theme : m_themes) {
334 if (theme) {
335 CloseThemeData(theme);
336 theme = nullptr;
337 }
338 }
339 QWindowsXPStylePrivate::cleanupVistaTreeViewTheming();
340 }
341
createTheme(int theme,HWND hwnd)342 HTHEME QWindowsXPStylePrivate::createTheme(int theme, HWND hwnd)
343 {
344 if (Q_UNLIKELY(theme < 0 || theme >= NThemes || !hwnd)) {
345 qWarning("Invalid parameters #%d, %p", theme, hwnd);
346 return nullptr;
347 }
348 if (!m_themes[theme]) {
349 const wchar_t *name = themeNames[theme];
350 if (theme == VistaTreeViewTheme && QWindowsXPStylePrivate::initVistaTreeViewTheming())
351 hwnd = QWindowsXPStylePrivate::m_vistaTreeViewHelper;
352 m_themes[theme] = OpenThemeData(hwnd, name);
353 if (Q_UNLIKELY(!m_themes[theme]))
354 qErrnoWarning("OpenThemeData() failed for theme %d (%s).",
355 theme, qPrintable(themeName(theme)));
356 }
357 return m_themes[theme];
358 }
359
themeName(int theme)360 QString QWindowsXPStylePrivate::themeName(int theme)
361 {
362 return theme >= 0 && theme < NThemes ?
363 QString::fromWCharArray(themeNames[theme]) :
364 QString();
365 }
366
isItemViewDelegateLineEdit(const QWidget * widget)367 bool QWindowsXPStylePrivate::isItemViewDelegateLineEdit(const QWidget *widget)
368 {
369 if (!widget)
370 return false;
371 const QWidget *parent1 = widget->parentWidget();
372 // Exlude dialogs or other toplevels parented on item views.
373 if (!parent1 || parent1->isWindow())
374 return false;
375 const QWidget *parent2 = parent1->parentWidget();
376 return parent2 && widget->inherits("QLineEdit")
377 && parent2->inherits("QAbstractItemView");
378 }
379
380 // Returns whether base color is set for this widget
isLineEditBaseColorSet(const QStyleOption * option,const QWidget * widget)381 bool QWindowsXPStylePrivate::isLineEditBaseColorSet(const QStyleOption *option, const QWidget *widget)
382 {
383 uint resolveMask = option->palette.resolve();
384 if (widget) {
385 // Since spin box includes a line edit we need to resolve the palette mask also from
386 // the parent, as while the color is always correct on the palette supplied by panel,
387 // the mask can still be empty. If either mask specifies custom base color, use that.
388 #if QT_CONFIG(spinbox)
389 if (const QAbstractSpinBox *spinbox = qobject_cast<QAbstractSpinBox*>(widget->parentWidget()))
390 resolveMask |= spinbox->palette().resolve();
391 #endif // QT_CONFIG(spinbox)
392 }
393 return (resolveMask & (1 << QPalette::Base)) != 0;
394 }
395
396 /*! \internal
397 This function will always return a valid window handle, and might
398 create a limbo widget to do so.
399 We often need a window handle to for example open theme data, so
400 this function ensures that we get one.
401 */
winId(const QWidget * widget)402 HWND QWindowsXPStylePrivate::winId(const QWidget *widget)
403 {
404 if (widget)
405 if (const HWND hwnd = QApplicationPrivate::getHWNDForWidget(const_cast<QWidget *>(widget)))
406 return hwnd;
407
408 // Find top level with native window (there might be dialogs that do not have one).
409 const auto allWindows = QGuiApplication::allWindows();
410 for (const QWindow *window : allWindows) {
411 if (window->isTopLevel() && window->type() != Qt::Desktop && window->handle() != nullptr)
412 return reinterpret_cast<HWND>(window->winId());
413 }
414
415 return GetDesktopWindow();
416 }
417
418 /*! \internal
419 Returns a native buffer (DIB section) of at least the size of
420 ( \a x , \a y ). The buffer has a 32 bit depth, to not lose
421 the alpha values on proper alpha-pixmaps.
422 */
buffer(int w,int h)423 HBITMAP QWindowsXPStylePrivate::buffer(int w, int h)
424 {
425 // If we already have a HBITMAP which is of adequate size, just return that
426 if (bufferBitmap) {
427 if (bufferW >= w && bufferH >= h)
428 return bufferBitmap;
429 // Not big enough, discard the old one
430 if (bufferDC && nullBitmap)
431 SelectObject(bufferDC, nullBitmap);
432 DeleteObject(bufferBitmap);
433 bufferBitmap = nullptr;
434 }
435
436 w = qMax(bufferW, w);
437 h = qMax(bufferH, h);
438
439 if (!bufferDC) {
440 HDC displayDC = GetDC(nullptr);
441 bufferDC = CreateCompatibleDC(displayDC);
442 ReleaseDC(nullptr, displayDC);
443 }
444
445 // Define the header
446 BITMAPINFO bmi;
447 memset(&bmi, 0, sizeof(bmi));
448 bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
449 bmi.bmiHeader.biWidth = w;
450 bmi.bmiHeader.biHeight = -h;
451 bmi.bmiHeader.biPlanes = 1;
452 bmi.bmiHeader.biBitCount = 32;
453 bmi.bmiHeader.biCompression = BI_RGB;
454
455 // Create the pixmap
456 bufferPixels = nullptr;
457 bufferBitmap = CreateDIBSection(bufferDC, &bmi, DIB_RGB_COLORS, reinterpret_cast<void **>(&bufferPixels), nullptr, 0);
458 GdiFlush();
459 nullBitmap = static_cast<HBITMAP>(SelectObject(bufferDC, bufferBitmap));
460
461 if (Q_UNLIKELY(!bufferBitmap)) {
462 qErrnoWarning("QWindowsXPStylePrivate::buffer(%dx%d), CreateDIBSection() failed.", w, h);
463 bufferW = 0;
464 bufferH = 0;
465 return nullptr;
466 }
467 if (Q_UNLIKELY(!bufferPixels)) {
468 qErrnoWarning("QWindowsXPStylePrivate::buffer(%dx%d), CreateDIBSection() did not allocate pixel data.", w, h);
469 bufferW = 0;
470 bufferH = 0;
471 return nullptr;
472 }
473 bufferW = w;
474 bufferH = h;
475 #ifdef DEBUG_XP_STYLE
476 qDebug("Creating new dib section (%d, %d)", w, h);
477 #endif
478 return bufferBitmap;
479 }
480
481 /*! \internal
482 Returns \c true if the part contains any transparency at all. This does
483 not indicate what kind of transparency we're dealing with. It can be
484 - Alpha transparency
485 - Masked transparency
486 */
isTransparent(XPThemeData & themeData)487 bool QWindowsXPStylePrivate::isTransparent(XPThemeData &themeData)
488 {
489 return IsThemeBackgroundPartiallyTransparent(themeData.handle(), themeData.partId,
490 themeData.stateId);
491 }
492
493
494 /*! \internal
495 Returns a QRegion of the region of the part
496 */
region(XPThemeData & themeData)497 QRegion QWindowsXPStylePrivate::region(XPThemeData &themeData)
498 {
499 HRGN hRgn = nullptr;
500 const qreal factor = QWindowsStylePrivate::nativeMetricScaleFactor(themeData.widget);
501 RECT rect = themeData.toRECT(QRect(themeData.rect.topLeft() / factor, themeData.rect.size() / factor));
502 if (!SUCCEEDED(GetThemeBackgroundRegion(themeData.handle(), bufferHDC(), themeData.partId,
503 themeData.stateId, &rect, &hRgn))) {
504 return QRegion();
505 }
506
507 HRGN dest = CreateRectRgn(0, 0, 0, 0);
508 const bool success = CombineRgn(dest, hRgn, nullptr, RGN_COPY) != ERROR;
509
510 QRegion region;
511
512 if (success) {
513 const auto numBytes = GetRegionData(dest, 0, nullptr);
514 if (numBytes == 0)
515 return QRegion();
516
517 char *buf = new (std::nothrow) char[numBytes];
518 if (!buf)
519 return QRegion();
520
521 RGNDATA *rd = reinterpret_cast<RGNDATA*>(buf);
522 if (GetRegionData(dest, numBytes, rd) == 0) {
523 delete [] buf;
524 return QRegion();
525 }
526
527 RECT *r = reinterpret_cast<RECT*>(rd->Buffer);
528 for (uint i = 0; i < rd->rdh.nCount; ++i) {
529 QRect rect;
530 rect.setCoords(int(r->left * factor), int(r->top * factor), int((r->right - 1) * factor), int((r->bottom - 1) * factor));
531 ++r;
532 region |= rect;
533 }
534
535 delete [] buf;
536 }
537
538 DeleteObject(hRgn);
539 DeleteObject(dest);
540
541 return region;
542 }
543
544 /*! \internal
545 Returns \c true if the native doublebuffer contains pixels with
546 varying alpha value.
547 */
hasAlphaChannel(const QRect & rect)548 bool QWindowsXPStylePrivate::hasAlphaChannel(const QRect &rect)
549 {
550 const int startX = rect.left();
551 const int startY = rect.top();
552 const int w = rect.width();
553 const int h = rect.height();
554
555 int firstAlpha = -1;
556 for (int y = startY; y < h/2; ++y) {
557 auto buffer = reinterpret_cast<const DWORD *>(bufferPixels) + (y * bufferW);
558 for (int x = startX; x < w; ++x, ++buffer) {
559 int alpha = (*buffer) >> 24;
560 if (firstAlpha == -1)
561 firstAlpha = alpha;
562 else if (alpha != firstAlpha)
563 return true;
564 }
565 }
566 return false;
567 }
568
569 /*! \internal
570 When the theme engine paints both a true alpha pixmap and a glyph
571 into our buffer, the glyph might not contain a proper alpha value.
572 The rule of thumb for premultiplied pixmaps is that the color
573 values of a pixel can never be higher than the alpha values, so
574 we use this to our advantage here, and fix all instances where
575 this occures.
576 */
fixAlphaChannel(const QRect & rect)577 bool QWindowsXPStylePrivate::fixAlphaChannel(const QRect &rect)
578 {
579 const int startX = rect.left();
580 const int startY = rect.top();
581 const int w = rect.width();
582 const int h = rect.height();
583 bool hasFixedAlphaValue = false;
584
585 for (int y = startY; y < h; ++y) {
586 auto buffer = reinterpret_cast<DWORD *>(bufferPixels) + (y * bufferW);
587 for (int x = startX; x < w; ++x, ++buffer) {
588 uint pixel = *buffer;
589 int alpha = qAlpha(pixel);
590 if (qRed(pixel) > alpha || qGreen(pixel) > alpha || qBlue(pixel) > alpha) {
591 *buffer |= 0xff000000;
592 hasFixedAlphaValue = true;
593 }
594 }
595 }
596 return hasFixedAlphaValue;
597 }
598
599 /*! \internal
600 Swaps the alpha values on certain pixels:
601 0xFF?????? -> 0x00??????
602 0x00?????? -> 0xFF??????
603 Used to determin the mask of a non-alpha transparent pixmap in
604 the native doublebuffer, and swap the alphas so we may paint
605 the image as a Premultiplied QImage with drawImage(), and obtain
606 the mask transparency.
607 */
swapAlphaChannel(const QRect & rect,bool allPixels)608 bool QWindowsXPStylePrivate::swapAlphaChannel(const QRect &rect, bool allPixels)
609 {
610 const int startX = rect.left();
611 const int startY = rect.top();
612 const int w = rect.width();
613 const int h = rect.height();
614 bool valueChange = false;
615
616 // Flip the alphas, so that 255-alpha pixels are 0, and 0-alpha are 255.
617 for (int y = startY; y < h; ++y) {
618 auto buffer = reinterpret_cast<DWORD *>(bufferPixels) + (y * bufferW);
619 for (int x = startX; x < w; ++x, ++buffer) {
620 if (allPixels) {
621 *buffer |= 0xFF000000;
622 continue;
623 }
624 unsigned int alphaValue = (*buffer) & 0xFF000000;
625 if (alphaValue == 0xFF000000) {
626 *buffer = 0;
627 valueChange = true;
628 } else if (alphaValue == 0) {
629 *buffer |= 0xFF000000;
630 valueChange = true;
631 }
632 }
633 }
634 return valueChange;
635 }
636
637 enum TransformType { SimpleTransform, HighDpiScalingTransform, ComplexTransform };
638
transformType(const QTransform & transform,qreal devicePixelRatio)639 static inline TransformType transformType(const QTransform &transform, qreal devicePixelRatio)
640 {
641 if (transform.type() <= QTransform::TxTranslate)
642 return SimpleTransform;
643 if (transform.type() > QTransform::TxScale)
644 return ComplexTransform;
645 return qFuzzyCompare(transform.m11(), devicePixelRatio)
646 && qFuzzyCompare(transform.m22(), devicePixelRatio)
647 ? HighDpiScalingTransform : ComplexTransform;
648 }
649
650 // QTBUG-60571: Exclude known fully opaque theme parts which produce values
651 // invalid in ARGB32_Premultiplied (for example, 0x00ffffff).
isFullyOpaque(const XPThemeData & themeData)652 static inline bool isFullyOpaque(const XPThemeData &themeData)
653 {
654 return themeData.theme == QWindowsXPStylePrivate::TaskDialogTheme && themeData.partId == TDLG_PRIMARYPANEL;
655 }
656
657 /*! \internal
658 Main theme drawing function.
659 Determines the correct lowlevel drawing method depending on several
660 factors.
661 Use drawBackgroundThruNativeBuffer() if:
662 - Painter does not have an HDC
663 - Theme part is flipped (mirrored horizontally)
664 else use drawBackgroundDirectly().
665 \note drawBackgroundThruNativeBuffer() can return false for large
666 sizes due to buffer()/CreateDIBSection() failing.
667 */
drawBackground(XPThemeData & themeData,qreal correctionFactor)668 bool QWindowsXPStylePrivate::drawBackground(XPThemeData &themeData, qreal correctionFactor)
669 {
670 if (themeData.rect.isEmpty())
671 return true;
672
673 QPainter *painter = themeData.painter;
674 Q_ASSERT_X(painter != nullptr, "QWindowsXPStylePrivate::drawBackground()", "Trying to draw a theme part without a painter");
675 if (!painter || !painter->isActive())
676 return false;
677
678 painter->save();
679
680 // Access paintDevice via engine since the painter may
681 // return the clip device which can still be a widget device in case of grabWidget().
682
683 bool translucentToplevel = false;
684 const QPaintDevice *paintDevice = painter->device();
685 const qreal aditionalDevicePixelRatio = themeData.widget ? themeData.widget->devicePixelRatioF() : qreal(1);
686 if (paintDevice->devType() == QInternal::Widget) {
687 const QWidget *window = static_cast<const QWidget *>(paintDevice)->window();
688 translucentToplevel = window->testAttribute(Qt::WA_TranslucentBackground);
689 }
690
691 const TransformType tt = transformType(painter->deviceTransform(), aditionalDevicePixelRatio);
692
693 bool canDrawDirectly = false;
694 if (themeData.widget && painter->opacity() == 1.0 && !themeData.rotate
695 && !isFullyOpaque(themeData)
696 && tt != ComplexTransform && !themeData.mirrorVertically
697 && !translucentToplevel) {
698 // Draw on backing store DC only for real widgets or backing store images.
699 const QPaintDevice *enginePaintDevice = painter->paintEngine()->paintDevice();
700 switch (enginePaintDevice->devType()) {
701 case QInternal::Widget:
702 canDrawDirectly = true;
703 break;
704 case QInternal::Image:
705 // Ensure the backing store has received as resize and is initialized.
706 if (QBackingStore *bs = backingStoreForWidget(themeData.widget))
707 if (bs->size().isValid() && bs->paintDevice() == enginePaintDevice)
708 canDrawDirectly = true;
709 }
710 }
711
712 const HDC dc = canDrawDirectly ? hdcForWidgetBackingStore(themeData.widget) : nullptr;
713 const bool result = dc && qFuzzyCompare(correctionFactor, qreal(1))
714 ? drawBackgroundDirectly(dc, themeData, aditionalDevicePixelRatio)
715 : drawBackgroundThruNativeBuffer(themeData, aditionalDevicePixelRatio, correctionFactor);
716 painter->restore();
717 return result;
718 }
719
scaleRect(const QRectF & r,qreal factor)720 static inline QRectF scaleRect(const QRectF &r, qreal factor)
721 {
722 return r.isValid() && factor > 1
723 ? QRectF(r.topLeft() * factor, r.size() * factor)
724 : r;
725 }
726
scaleRegion(const QRegion & region,qreal factor)727 static QRegion scaleRegion(const QRegion ®ion, qreal factor)
728 {
729 if (region.isEmpty() || qFuzzyCompare(factor, qreal(1)))
730 return region;
731 QRegion result;
732 for (const QRect &rect : region)
733 result += QRectF(QPointF(rect.topLeft()) * factor, QSizeF(rect.size() * factor)).toRect();
734 return result;
735 }
736
737 /*! \internal
738 This function draws the theme parts directly to the paintengines HDC.
739 Do not use this if you need to perform other transformations on the
740 resulting data.
741 */
drawBackgroundDirectly(HDC dc,XPThemeData & themeData,qreal additionalDevicePixelRatio)742 bool QWindowsXPStylePrivate::drawBackgroundDirectly(HDC dc, XPThemeData &themeData, qreal additionalDevicePixelRatio)
743 {
744 QPainter *painter = themeData.painter;
745
746 const auto &deviceTransform = painter->deviceTransform();
747 const QPointF redirectionDelta(deviceTransform.dx(), deviceTransform.dy());
748 const QRect area = scaleRect(QRectF(themeData.rect), additionalDevicePixelRatio).translated(redirectionDelta).toRect();
749
750 QRegion sysRgn = painter->paintEngine()->systemClip();
751 if (sysRgn.isEmpty())
752 sysRgn = area;
753 else
754 sysRgn &= area;
755 if (painter->hasClipping())
756 sysRgn &= scaleRegion(painter->clipRegion(), additionalDevicePixelRatio).translated(redirectionDelta.toPoint());
757 HRGN hrgn = qt_hrgn_from_qregion(sysRgn);
758 SelectClipRgn(dc, hrgn);
759
760 #ifdef DEBUG_XP_STYLE
761 printf("---[ DIRECT PAINTING ]------------------> Name(%-10s) Part(%d) State(%d)\n",
762 qPrintable(themeData.name), themeData.partId, themeData.stateId);
763 showProperties(themeData);
764 #endif
765
766 RECT drawRECT = themeData.toRECT(area);
767 DTBGOPTS drawOptions;
768 memset(&drawOptions, 0, sizeof(drawOptions));
769 drawOptions.dwSize = sizeof(drawOptions);
770 drawOptions.rcClip = themeData.toRECT(sysRgn.boundingRect());
771 drawOptions.dwFlags = DTBG_CLIPRECT
772 | (themeData.noBorder ? DTBG_OMITBORDER : 0)
773 | (themeData.noContent ? DTBG_OMITCONTENT : 0)
774 | (themeData.mirrorHorizontally ? DTBG_MIRRORDC : 0);
775
776 const HRESULT result = DrawThemeBackgroundEx(themeData.handle(), dc, themeData.partId, themeData.stateId, &(drawRECT), &drawOptions);
777 SelectClipRgn(dc, nullptr);
778 DeleteObject(hrgn);
779 return SUCCEEDED(result);
780 }
781
782 /*! \internal
783 This function uses a secondary Native doublebuffer for painting parts.
784 It should only be used when the painteengine doesn't provide a proper
785 HDC for direct painting (e.g. when doing a grabWidget(), painting to
786 other pixmaps etc), or when special transformations are needed (e.g.
787 flips (horizonal mirroring only, vertical are handled by the theme
788 engine).
789
790 \a correctionFactor is an additional factor used to scale up controls
791 that are too small on High DPI screens, as has been observed for
792 WP_MDICLOSEBUTTON, WP_MDIRESTOREBUTTON, WP_MDIMINBUTTON (QTBUG-75927).
793 */
drawBackgroundThruNativeBuffer(XPThemeData & themeData,qreal additionalDevicePixelRatio,qreal correctionFactor)794 bool QWindowsXPStylePrivate::drawBackgroundThruNativeBuffer(XPThemeData &themeData,
795 qreal additionalDevicePixelRatio,
796 qreal correctionFactor)
797 {
798 QPainter *painter = themeData.painter;
799 QRectF rectF = scaleRect(QRectF(themeData.rect), additionalDevicePixelRatio);
800
801 if ((themeData.rotate + 90) % 180 == 0) { // Catch 90,270,etc.. degree flips.
802 rectF = QRectF(0, 0, rectF.height(), rectF.width());
803 }
804 rectF.moveTo(0, 0);
805
806 const bool hasCorrectionFactor = !qFuzzyCompare(correctionFactor, qreal(1));
807 QRect rect = rectF.toRect();
808 QRect drawRect = hasCorrectionFactor
809 ? QRectF(rectF.topLeft() / correctionFactor, rectF.size() / correctionFactor).toRect() : rect;
810 int partId = themeData.partId;
811 int stateId = themeData.stateId;
812 int w = rect.width();
813 int h = rect.height();
814
815 // Values initialized later, either from cached values, or from function calls
816 AlphaChannelType alphaType = UnknownAlpha;
817 bool stateHasData = true; // We assume so;
818 bool hasAlpha = false;
819 bool partIsTransparent;
820 bool potentialInvalidAlpha;
821
822 QString pixmapCacheKey = QStringLiteral("$qt_xp_");
823 pixmapCacheKey.append(themeName(themeData.theme));
824 pixmapCacheKey.append(QLatin1Char('p'));
825 pixmapCacheKey.append(QString::number(partId));
826 pixmapCacheKey.append(QLatin1Char('s'));
827 pixmapCacheKey.append(QString::number(stateId));
828 pixmapCacheKey.append(QLatin1Char('s'));
829 pixmapCacheKey.append(themeData.noBorder ? QLatin1Char('0') : QLatin1Char('1'));
830 pixmapCacheKey.append(QLatin1Char('b'));
831 pixmapCacheKey.append(themeData.noContent ? QLatin1Char('0') : QLatin1Char('1'));
832 pixmapCacheKey.append(QString::number(w));
833 pixmapCacheKey.append(QLatin1Char('w'));
834 pixmapCacheKey.append(QString::number(h));
835 pixmapCacheKey.append(QLatin1Char('h'));
836 pixmapCacheKey.append(QString::number(additionalDevicePixelRatio));
837 pixmapCacheKey.append(QLatin1Char('d'));
838 if (hasCorrectionFactor) {
839 pixmapCacheKey.append(QLatin1Char('c'));
840 pixmapCacheKey.append(QString::number(correctionFactor));
841 }
842
843 QPixmap cachedPixmap;
844 ThemeMapKey key(themeData);
845 ThemeMapData data = alphaCache.value(key);
846
847 bool haveCachedPixmap = false;
848 bool isCached = data.dataValid;
849 if (isCached) {
850 partIsTransparent = data.partIsTransparent;
851 hasAlpha = data.hasAlphaChannel;
852 alphaType = data.alphaType;
853 potentialInvalidAlpha = data.hadInvalidAlpha;
854
855 haveCachedPixmap = QPixmapCache::find(pixmapCacheKey, &cachedPixmap);
856
857 #ifdef DEBUG_XP_STYLE
858 char buf[25];
859 ::sprintf(buf, "+ Pixmap(%3d, %3d) ]", w, h);
860 printf("---[ CACHED %s--------> Name(%-10s) Part(%d) State(%d)\n",
861 haveCachedPixmap ? buf : "]-------------------",
862 qPrintable(themeData.name), themeData.partId, themeData.stateId);
863 #endif
864 } else {
865 // Not cached, so get values from Theme Engine
866 BOOL tmt_borderonly = false;
867 COLORREF tmt_transparentcolor = 0x0;
868 PROPERTYORIGIN proporigin = PO_NOTFOUND;
869 GetThemeBool(themeData.handle(), themeData.partId, themeData.stateId, TMT_BORDERONLY, &tmt_borderonly);
870 GetThemeColor(themeData.handle(), themeData.partId, themeData.stateId, TMT_TRANSPARENTCOLOR, &tmt_transparentcolor);
871 GetThemePropertyOrigin(themeData.handle(), themeData.partId, themeData.stateId, TMT_CAPTIONMARGINS, &proporigin);
872
873 partIsTransparent = isTransparent(themeData);
874
875 potentialInvalidAlpha = false;
876 GetThemePropertyOrigin(themeData.handle(), themeData.partId, themeData.stateId, TMT_GLYPHTYPE, &proporigin);
877 if (proporigin == PO_PART || proporigin == PO_STATE) {
878 int tmt_glyphtype = GT_NONE;
879 GetThemeEnumValue(themeData.handle(), themeData.partId, themeData.stateId, TMT_GLYPHTYPE, &tmt_glyphtype);
880 potentialInvalidAlpha = partIsTransparent && tmt_glyphtype == GT_IMAGEGLYPH;
881 }
882
883 #ifdef DEBUG_XP_STYLE
884 printf("---[ NOT CACHED ]-----------------------> Name(%-10s) Part(%d) State(%d)\n",
885 qPrintable(themeData.name), themeData.partId, themeData.stateId);
886 printf("-->partIsTransparen = %d\n", partIsTransparent);
887 printf("-->potentialInvalidAlpha = %d\n", potentialInvalidAlpha);
888 showProperties(themeData);
889 #endif
890 }
891 bool wasAlphaSwapped = false;
892 bool wasAlphaFixed = false;
893
894 // OLD PSDK Workaround ------------------------------------------------------------------------
895 // See if we need extra clipping for the older PSDK, which does
896 // not have a DrawThemeBackgroundEx function for DTGB_OMITBORDER
897 // and DTGB_OMITCONTENT
898 bool addBorderContentClipping = false;
899 QRegion extraClip;
900 QRect area = drawRect;
901 if (themeData.noBorder || themeData.noContent) {
902 extraClip = area;
903 // We are running on a system where the uxtheme.dll does not have
904 // the DrawThemeBackgroundEx function, so we need to clip away
905 // borders or contents manually.
906
907 int borderSize = 0;
908 PROPERTYORIGIN origin = PO_NOTFOUND;
909 GetThemePropertyOrigin(themeData.handle(), themeData.partId, themeData.stateId, TMT_BORDERSIZE, &origin);
910 GetThemeInt(themeData.handle(), themeData.partId, themeData.stateId, TMT_BORDERSIZE, &borderSize);
911 borderSize *= additionalDevicePixelRatio;
912
913 // Clip away border region
914 if ((origin == PO_CLASS || origin == PO_PART || origin == PO_STATE) && borderSize > 0) {
915 if (themeData.noBorder) {
916 extraClip &= area;
917 area = area.adjusted(-borderSize, -borderSize, borderSize, borderSize);
918 }
919
920 // Clip away content region
921 if (themeData.noContent) {
922 QRegion content = area.adjusted(borderSize, borderSize, -borderSize, -borderSize);
923 extraClip ^= content;
924 }
925 }
926 addBorderContentClipping = (themeData.noBorder | themeData.noContent);
927 }
928
929 QImage img;
930 if (!haveCachedPixmap) { // If the pixmap is not cached, generate it! -------------------------
931 if (!buffer(drawRect.width(), drawRect.height())) // Ensure a buffer of at least (w, h) in size
932 return false;
933 HDC dc = bufferHDC();
934
935 // Clear the buffer
936 if (alphaType != NoAlpha) {
937 // Consider have separate "memset" function for small chunks for more speedup
938 memset(bufferPixels, 0x00, bufferW * drawRect.height() * 4);
939 }
940
941 // Difference between area and rect
942 int dx = area.x() - drawRect.x();
943 int dy = area.y() - drawRect.y();
944
945 // Adjust so painting rect starts from Origo
946 rect.moveTo(0,0);
947 area.moveTo(dx,dy);
948 DTBGOPTS drawOptions;
949 drawOptions.dwSize = sizeof(drawOptions);
950 drawOptions.rcClip = themeData.toRECT(rect);
951 drawOptions.dwFlags = DTBG_CLIPRECT
952 | (themeData.noBorder ? DTBG_OMITBORDER : 0)
953 | (themeData.noContent ? DTBG_OMITCONTENT : 0);
954
955 // Drawing the part into the backing store
956 RECT wRect(themeData.toRECT(area));
957 DrawThemeBackgroundEx(themeData.handle(), dc, themeData.partId, themeData.stateId, &wRect, &drawOptions);
958
959 // If not cached, analyze the buffer data to figure
960 // out alpha type, and if it contains data
961 if (!isCached) {
962 // SHORTCUT: If the part's state has no data, cache it for NOOP later
963 if (!stateHasData) {
964 memset(static_cast<void *>(&data), 0, sizeof(data));
965 data.dataValid = true;
966 alphaCache.insert(key, data);
967 return true;
968 }
969 hasAlpha = hasAlphaChannel(rect);
970 if (!hasAlpha && partIsTransparent)
971 potentialInvalidAlpha = true;
972 #if defined(DEBUG_XP_STYLE) && 1
973 dumpNativeDIB(drawRect.width(), drawRect.height());
974 #endif
975 }
976
977 // Fix alpha values, if needed
978 if (potentialInvalidAlpha)
979 wasAlphaFixed = fixAlphaChannel(drawRect);
980
981 QImage::Format format;
982 if ((partIsTransparent && !wasAlphaSwapped) || (!partIsTransparent && hasAlpha)) {
983 format = QImage::Format_ARGB32_Premultiplied;
984 alphaType = RealAlpha;
985 } else if (wasAlphaSwapped) {
986 format = QImage::Format_ARGB32_Premultiplied;
987 alphaType = MaskAlpha;
988 } else {
989 format = QImage::Format_RGB32;
990 // The image data we got from the theme engine does not have any transparency,
991 // thus the alpha channel is set to 0.
992 // However, Format_RGB32 requires the alpha part to be set to 0xff, thus
993 // we must flip it from 0x00 to 0xff
994 swapAlphaChannel(rect, true);
995 alphaType = NoAlpha;
996 }
997 #if defined(DEBUG_XP_STYLE) && 1
998 printf("Image format is: %s\n", alphaType == RealAlpha ? "Real Alpha" : alphaType == MaskAlpha ? "Masked Alpha" : "No Alpha");
999 #endif
1000 img = QImage(bufferPixels, bufferW, bufferH, format);
1001 if (hasCorrectionFactor)
1002 img = img.scaled(w, h, Qt::KeepAspectRatio, Qt::SmoothTransformation);
1003 img.setDevicePixelRatio(additionalDevicePixelRatio);
1004 }
1005
1006 // Blitting backing store
1007 bool useRegion = partIsTransparent && !hasAlpha && !wasAlphaSwapped;
1008
1009 QRegion newRegion;
1010 QRegion oldRegion;
1011 if (useRegion) {
1012 newRegion = region(themeData);
1013 oldRegion = painter->clipRegion();
1014 painter->setClipRegion(newRegion);
1015 #if defined(DEBUG_XP_STYLE) && 0
1016 printf("Using region:\n");
1017 for (const QRect &r : newRegion)
1018 printf(" (%d, %d, %d, %d)\n", r.x(), r.y(), r.right(), r.bottom());
1019 #endif
1020 }
1021
1022 if (addBorderContentClipping)
1023 painter->setClipRegion(scaleRegion(extraClip, 1.0 / additionalDevicePixelRatio), Qt::IntersectClip);
1024
1025 if (!themeData.mirrorHorizontally && !themeData.mirrorVertically && !themeData.rotate) {
1026 if (!haveCachedPixmap)
1027 painter->drawImage(themeData.rect, img, rect);
1028 else
1029 painter->drawPixmap(themeData.rect, cachedPixmap);
1030 } else {
1031 // This is _slow_!
1032 // Make a copy containing only the necessary data, and mirror
1033 // on all wanted axes. Then draw the copy.
1034 // If cached, the normal pixmap is cached, instead of caching
1035 // all possible orientations for each part and state.
1036 QImage imgCopy;
1037 if (!haveCachedPixmap)
1038 imgCopy = img.copy(rect);
1039 else
1040 imgCopy = cachedPixmap.toImage();
1041
1042 if (themeData.rotate) {
1043 QTransform rotMatrix;
1044 rotMatrix.rotate(themeData.rotate);
1045 imgCopy = imgCopy.transformed(rotMatrix);
1046 }
1047 if (themeData.mirrorHorizontally || themeData.mirrorVertically) {
1048 imgCopy = imgCopy.mirrored(themeData.mirrorHorizontally, themeData.mirrorVertically);
1049 }
1050 painter->drawImage(themeData.rect,
1051 imgCopy);
1052 }
1053
1054 if (useRegion || addBorderContentClipping) {
1055 if (oldRegion.isEmpty())
1056 painter->setClipping(false);
1057 else
1058 painter->setClipRegion(oldRegion);
1059 }
1060
1061 // Cache the pixmap to avoid expensive swapAlphaChannel() calls
1062 if (!haveCachedPixmap && w && h) {
1063 QPixmap pix = QPixmap::fromImage(img).copy(rect);
1064 QPixmapCache::insert(pixmapCacheKey, pix);
1065 #ifdef DEBUG_XP_STYLE
1066 printf("+++Adding pixmap to cache, size(%d, %d), wasAlphaSwapped(%d), wasAlphaFixed(%d), name(%s)\n",
1067 w, h, wasAlphaSwapped, wasAlphaFixed, qPrintable(pixmapCacheKey));
1068 #endif
1069 }
1070
1071 // Add to theme part cache
1072 if (!isCached) {
1073 memset(static_cast<void *>(&data), 0, sizeof(data));
1074 data.dataValid = true;
1075 data.partIsTransparent = partIsTransparent;
1076 data.alphaType = alphaType;
1077 data.hasAlphaChannel = hasAlpha;
1078 data.wasAlphaSwapped = wasAlphaSwapped;
1079 data.hadInvalidAlpha = wasAlphaFixed;
1080 alphaCache.insert(key, data);
1081 }
1082 return true;
1083 }
1084
1085
1086 // ------------------------------------------------------------------------------------------------
1087
1088 /*!
1089 \class QWindowsXPStyle
1090 \brief The QWindowsXPStyle class provides a Microsoft Windows XP-like look and feel.
1091
1092 \ingroup appearance
1093 \inmodule QtWidgets
1094 \internal
1095
1096 \warning This style is only available on the Windows XP platform
1097 because it makes use of Windows XP's style engine.
1098
1099 Most of the functions are documented in the base classes
1100 QWindowsStyle, QCommonStyle, and QStyle, but the
1101 QWindowsXPStyle overloads of drawComplexControl(), drawControl(),
1102 drawControlMask(), drawPrimitive(), proxy()->subControlRect(), and
1103 sizeFromContents(), are documented here.
1104
1105 \image qwindowsxpstyle.png
1106 \sa QMacStyle, QWindowsStyle, QFusionStyle
1107 */
1108
1109 /*!
1110 Constructs a QWindowsStyle
1111 */
QWindowsXPStyle()1112 QWindowsXPStyle::QWindowsXPStyle()
1113 : QWindowsStyle(*new QWindowsXPStylePrivate)
1114 {
1115 }
1116
1117 /*!
1118 Destroys the style.
1119 */
1120 QWindowsXPStyle::~QWindowsXPStyle() = default;
1121
1122 /*! \reimp */
unpolish(QApplication * app)1123 void QWindowsXPStyle::unpolish(QApplication *app)
1124 {
1125 QWindowsStyle::unpolish(app);
1126 }
1127
1128 /*! \reimp */
polish(QApplication * app)1129 void QWindowsXPStyle::polish(QApplication *app)
1130 {
1131 QWindowsStyle::polish(app);
1132 if (!QWindowsXPStylePrivate::useXP())
1133 return;
1134 }
1135
1136 /*! \reimp */
polish(QWidget * widget)1137 void QWindowsXPStyle::polish(QWidget *widget)
1138 {
1139 QWindowsStyle::polish(widget);
1140 if (!QWindowsXPStylePrivate::useXP())
1141 return;
1142
1143 if (false
1144 #if QT_CONFIG(abstractbutton)
1145 || qobject_cast<QAbstractButton*>(widget)
1146 #endif
1147 || qobject_cast<QToolButton*>(widget)
1148 || qobject_cast<QTabBar*>(widget)
1149 #if QT_CONFIG(combobox)
1150 || qobject_cast<QComboBox*>(widget)
1151 #endif // QT_CONFIG(combobox)
1152 || qobject_cast<QScrollBar*>(widget)
1153 || qobject_cast<QSlider*>(widget)
1154 || qobject_cast<QHeaderView*>(widget)
1155 #if QT_CONFIG(spinbox)
1156 || qobject_cast<QAbstractSpinBox*>(widget)
1157 || qobject_cast<QSpinBox*>(widget)
1158 #endif // QT_CONFIG(spinbox)
1159 ) {
1160 widget->setAttribute(Qt::WA_Hover);
1161 }
1162
1163 #if QT_CONFIG(rubberband)
1164 if (qobject_cast<QRubberBand*>(widget)) {
1165 widget->setWindowOpacity(0.6);
1166 }
1167 #endif
1168
1169 Q_D(QWindowsXPStyle);
1170 if (!d->hasInitColors) {
1171 // Get text color for group box labels
1172 COLORREF cref;
1173 XPThemeData theme(widget, nullptr, QWindowsXPStylePrivate::ButtonTheme, 0, 0);
1174 GetThemeColor(theme.handle(), BP_GROUPBOX, GBS_NORMAL, TMT_TEXTCOLOR, &cref);
1175 d->groupBoxTextColor = qRgb(GetRValue(cref), GetGValue(cref), GetBValue(cref));
1176 GetThemeColor(theme.handle(), BP_GROUPBOX, GBS_DISABLED, TMT_TEXTCOLOR, &cref);
1177 d->groupBoxTextColorDisabled = qRgb(GetRValue(cref), GetGValue(cref), GetBValue(cref));
1178 // Where does this color come from?
1179 //GetThemeColor(theme.handle(), TKP_TICS, TSS_NORMAL, TMT_COLOR, &cref);
1180 d->sliderTickColor = qRgb(165, 162, 148);
1181 d->hasInitColors = true;
1182 }
1183 }
1184
1185 /*! \reimp */
polish(QPalette & pal)1186 void QWindowsXPStyle::polish(QPalette &pal)
1187 {
1188 QWindowsStyle::polish(pal);
1189 pal.setBrush(QPalette::AlternateBase, pal.base().color().darker(110));
1190 }
1191
1192 /*! \reimp */
unpolish(QWidget * widget)1193 void QWindowsXPStyle::unpolish(QWidget *widget)
1194 {
1195 #if QT_CONFIG(rubberband)
1196 if (qobject_cast<QRubberBand*>(widget)) {
1197 widget->setWindowOpacity(1.0);
1198 }
1199 #endif
1200 Q_D(QWindowsXPStyle);
1201 // Unpolish of widgets is the first thing that
1202 // happens when a theme changes, or the theme
1203 // engine is turned off. So we detect it here.
1204 bool oldState = QWindowsXPStylePrivate::useXP();
1205 bool newState = QWindowsXPStylePrivate::useXP(true);
1206 if ((oldState != newState) && newState) {
1207 d->cleanup(true);
1208 d->init(true);
1209 } else {
1210 // Cleanup handle map, if just changing style,
1211 // or turning it on. In both cases the values
1212 // already in the map might be old (other style).
1213 d->cleanupHandleMap();
1214 }
1215 if (false
1216 #if QT_CONFIG(abstractbutton)
1217 || qobject_cast<QAbstractButton*>(widget)
1218 #endif
1219 || qobject_cast<QToolButton*>(widget)
1220 || qobject_cast<QTabBar*>(widget)
1221 #if QT_CONFIG(combobox)
1222 || qobject_cast<QComboBox*>(widget)
1223 #endif // QT_CONFIG(combobox)
1224 || qobject_cast<QScrollBar*>(widget)
1225 || qobject_cast<QSlider*>(widget)
1226 || qobject_cast<QHeaderView*>(widget)
1227 #if QT_CONFIG(spinbox)
1228 || qobject_cast<QAbstractSpinBox*>(widget)
1229 || qobject_cast<QSpinBox*>(widget)
1230 #endif // QT_CONFIG(spinbox)
1231 ) {
1232 widget->setAttribute(Qt::WA_Hover, false);
1233 }
1234 QWindowsStyle::unpolish(widget);
1235 }
1236
1237 /*! \reimp */
subElementRect(SubElement sr,const QStyleOption * option,const QWidget * widget) const1238 QRect QWindowsXPStyle::subElementRect(SubElement sr, const QStyleOption *option, const QWidget *widget) const
1239 {
1240 if (!QWindowsXPStylePrivate::useXP()) {
1241 return QWindowsStyle::subElementRect(sr, option, widget);
1242 }
1243
1244 QRect rect(option->rect);
1245 switch(sr) {
1246 case SE_DockWidgetCloseButton:
1247 case SE_DockWidgetFloatButton:
1248 rect = QWindowsStyle::subElementRect(sr, option, widget);
1249 return rect.translated(0, 1);
1250 break;
1251 case SE_TabWidgetTabContents:
1252 if (qstyleoption_cast<const QStyleOptionTabWidgetFrame *>(option))
1253 {
1254 rect = QWindowsStyle::subElementRect(sr, option, widget);
1255 if (sr == SE_TabWidgetTabContents) {
1256 if (const QTabWidget *tabWidget = qobject_cast<const QTabWidget *>(widget)) {
1257 if (tabWidget->documentMode())
1258 break;
1259 }
1260
1261 rect.adjust(0, 0, -2, -2);
1262 }
1263 }
1264 break;
1265 case SE_TabWidgetTabBar: {
1266 rect = QWindowsStyle::subElementRect(sr, option, widget);
1267 const QStyleOptionTabWidgetFrame *twfOption =
1268 qstyleoption_cast<const QStyleOptionTabWidgetFrame *>(option);
1269 if (twfOption && twfOption->direction == Qt::RightToLeft
1270 && (twfOption->shape == QTabBar::RoundedNorth
1271 || twfOption->shape == QTabBar::RoundedSouth))
1272 {
1273 QStyleOptionTab otherOption;
1274 otherOption.shape = (twfOption->shape == QTabBar::RoundedNorth
1275 ? QTabBar::RoundedEast : QTabBar::RoundedSouth);
1276 int overlap = proxy()->pixelMetric(PM_TabBarBaseOverlap, &otherOption, widget);
1277 int borderThickness = proxy()->pixelMetric(PM_DefaultFrameWidth, option, widget);
1278 rect.adjust(-overlap + borderThickness, 0, -overlap + borderThickness, 0);
1279 }
1280 break;}
1281
1282 case SE_PushButtonContents:
1283 if (const QStyleOptionButton *btn = qstyleoption_cast<const QStyleOptionButton *>(option)) {
1284 MARGINS borderSize;
1285 if (widget) {
1286 XPThemeData buttontheme(widget, nullptr, QWindowsXPStylePrivate::ButtonTheme);
1287 HTHEME theme = buttontheme.handle();
1288 if (theme) {
1289 int stateId;
1290 if (!(option->state & State_Enabled))
1291 stateId = PBS_DISABLED;
1292 else if (option->state & State_Sunken)
1293 stateId = PBS_PRESSED;
1294 else if (option->state & State_MouseOver)
1295 stateId = PBS_HOT;
1296 else if (btn->features & QStyleOptionButton::DefaultButton)
1297 stateId = PBS_DEFAULTED;
1298 else
1299 stateId = PBS_NORMAL;
1300
1301 int border = proxy()->pixelMetric(PM_DefaultFrameWidth, btn, widget);
1302 rect = option->rect.adjusted(border, border, -border, -border);
1303
1304 if (SUCCEEDED(GetThemeMargins(theme, nullptr, BP_PUSHBUTTON, stateId, TMT_CONTENTMARGINS, nullptr, &borderSize))) {
1305 rect.adjust(borderSize.cxLeftWidth, borderSize.cyTopHeight,
1306 -borderSize.cxRightWidth, -borderSize.cyBottomHeight);
1307 rect = visualRect(option->direction, option->rect, rect);
1308 }
1309 }
1310 }
1311 }
1312 break;
1313 case SE_ProgressBarContents:
1314 rect = QCommonStyle::subElementRect(SE_ProgressBarGroove, option, widget);
1315 if (option->state & QStyle::State_Horizontal)
1316 rect.adjust(4, 3, -4, -3);
1317 else
1318 rect.adjust(3, 2, -3, -2);
1319 break;
1320 default:
1321 rect = QWindowsStyle::subElementRect(sr, option, widget);
1322 }
1323 return rect;
1324 }
1325
1326 /*!
1327 \reimp
1328 */
drawPrimitive(PrimitiveElement pe,const QStyleOption * option,QPainter * p,const QWidget * widget) const1329 void QWindowsXPStyle::drawPrimitive(PrimitiveElement pe, const QStyleOption *option, QPainter *p,
1330 const QWidget *widget) const
1331 {
1332 QWindowsXPStylePrivate *d = const_cast<QWindowsXPStylePrivate*>(d_func());
1333
1334 if (!QWindowsXPStylePrivate::useXP()) {
1335 QWindowsStyle::drawPrimitive(pe, option, p, widget);
1336 return;
1337 }
1338
1339 int themeNumber = -1;
1340 int partId = 0;
1341 int stateId = 0;
1342 QRect rect = option->rect;
1343 State flags = option->state;
1344 bool hMirrored = false;
1345 bool vMirrored = false;
1346 bool noBorder = false;
1347 bool noContent = false;
1348 int rotate = 0;
1349
1350 switch (pe) {
1351 case PE_FrameTabBarBase:
1352 if (const QStyleOptionTabBarBase *tbb
1353 = qstyleoption_cast<const QStyleOptionTabBarBase *>(option)) {
1354 p->save();
1355 switch (tbb->shape) {
1356 case QTabBar::RoundedNorth:
1357 p->setPen(QPen(tbb->palette.dark(), 0));
1358 p->drawLine(tbb->rect.topLeft(), tbb->rect.topRight());
1359 break;
1360 case QTabBar::RoundedWest:
1361 p->setPen(QPen(tbb->palette.dark(), 0));
1362 p->drawLine(tbb->rect.left(), tbb->rect.top(), tbb->rect.left(), tbb->rect.bottom());
1363 break;
1364 case QTabBar::RoundedSouth:
1365 p->setPen(QPen(tbb->palette.dark(), 0));
1366 p->drawLine(tbb->rect.left(), tbb->rect.top(),
1367 tbb->rect.right(), tbb->rect.top());
1368 break;
1369 case QTabBar::RoundedEast:
1370 p->setPen(QPen(tbb->palette.dark(), 0));
1371 p->drawLine(tbb->rect.topLeft(), tbb->rect.bottomLeft());
1372 break;
1373 case QTabBar::TriangularNorth:
1374 case QTabBar::TriangularEast:
1375 case QTabBar::TriangularWest:
1376 case QTabBar::TriangularSouth:
1377 p->restore();
1378 QWindowsStyle::drawPrimitive(pe, option, p, widget);
1379 return;
1380 }
1381 p->restore();
1382 }
1383 return;
1384 case PE_PanelButtonBevel:
1385 themeNumber = QWindowsXPStylePrivate::ButtonTheme;
1386 partId = BP_PUSHBUTTON;
1387 if (!(flags & State_Enabled))
1388 stateId = PBS_DISABLED;
1389 else if ((flags & State_Sunken) || (flags & State_On))
1390 stateId = PBS_PRESSED;
1391 else if (flags & State_MouseOver)
1392 stateId = PBS_HOT;
1393 //else if (flags & State_ButtonDefault)
1394 // stateId = PBS_DEFAULTED;
1395 else
1396 stateId = PBS_NORMAL;
1397 break;
1398
1399 case PE_PanelButtonTool:
1400 if (widget && widget->inherits("QDockWidgetTitleButton")) {
1401 if (const QWidget *dw = widget->parentWidget())
1402 if (dw->isWindow())
1403 return;
1404 }
1405 themeNumber = QWindowsXPStylePrivate::ToolBarTheme;
1406 partId = TP_BUTTON;
1407 if (!(flags & State_Enabled))
1408 stateId = TS_DISABLED;
1409 else if (flags & State_Sunken)
1410 stateId = TS_PRESSED;
1411 else if (flags & State_MouseOver)
1412 stateId = flags & State_On ? TS_HOTCHECKED : TS_HOT;
1413 else if (flags & State_On)
1414 stateId = TS_CHECKED;
1415 else if (!(flags & State_AutoRaise))
1416 stateId = TS_HOT;
1417 else
1418 stateId = TS_NORMAL;
1419 break;
1420
1421 case PE_IndicatorButtonDropDown:
1422 themeNumber = QWindowsXPStylePrivate::ToolBarTheme;
1423 partId = TP_SPLITBUTTONDROPDOWN;
1424 if (!(flags & State_Enabled))
1425 stateId = TS_DISABLED;
1426 else if (flags & State_Sunken)
1427 stateId = TS_PRESSED;
1428 else if (flags & State_MouseOver)
1429 stateId = flags & State_On ? TS_HOTCHECKED : TS_HOT;
1430 else if (flags & State_On)
1431 stateId = TS_CHECKED;
1432 else if (!(flags & State_AutoRaise))
1433 stateId = TS_HOT;
1434 else
1435 stateId = TS_NORMAL;
1436 if (option->direction == Qt::RightToLeft)
1437 hMirrored = true;
1438 break;
1439
1440 case PE_IndicatorCheckBox:
1441 themeNumber = QWindowsXPStylePrivate::ButtonTheme;
1442 partId = BP_CHECKBOX;
1443 if (!(flags & State_Enabled))
1444 stateId = CBS_UNCHECKEDDISABLED;
1445 else if (flags & State_Sunken)
1446 stateId = CBS_UNCHECKEDPRESSED;
1447 else if (flags & State_MouseOver)
1448 stateId = CBS_UNCHECKEDHOT;
1449 else
1450 stateId = CBS_UNCHECKEDNORMAL;
1451
1452 if (flags & State_On)
1453 stateId += CBS_CHECKEDNORMAL-1;
1454 else if (flags & State_NoChange)
1455 stateId += CBS_MIXEDNORMAL-1;
1456
1457 break;
1458
1459 case PE_IndicatorRadioButton:
1460 themeNumber = QWindowsXPStylePrivate::ButtonTheme;
1461 partId = BP_RADIOBUTTON;
1462 if (!(flags & State_Enabled))
1463 stateId = RBS_UNCHECKEDDISABLED;
1464 else if (flags & State_Sunken)
1465 stateId = RBS_UNCHECKEDPRESSED;
1466 else if (flags & State_MouseOver)
1467 stateId = RBS_UNCHECKEDHOT;
1468 else
1469 stateId = RBS_UNCHECKEDNORMAL;
1470
1471 if (flags & State_On)
1472 stateId += RBS_CHECKEDNORMAL-1;
1473 break;
1474
1475 case PE_IndicatorDockWidgetResizeHandle:
1476 return;
1477
1478 case PE_Frame:
1479 {
1480 if (flags & State_Raised)
1481 return;
1482 themeNumber = QWindowsXPStylePrivate::ListViewTheme;
1483 partId = LVP_LISTGROUP;
1484 XPThemeData theme(widget, nullptr, themeNumber, partId);
1485
1486 if (!(flags & State_Enabled))
1487 stateId = ETS_DISABLED;
1488 else
1489 stateId = ETS_NORMAL;
1490 int fillType;
1491 if (GetThemeEnumValue(theme.handle(), partId, stateId, TMT_BGTYPE, &fillType) == S_OK) {
1492 if (fillType == BT_BORDERFILL) {
1493 COLORREF bcRef;
1494 GetThemeColor(theme.handle(), partId, stateId, TMT_BORDERCOLOR, &bcRef);
1495 QColor bordercolor(qRgb(GetRValue(bcRef), GetGValue(bcRef), GetBValue(bcRef)));
1496 QPen oldPen = p->pen();
1497 // int borderSize = 1;
1498 // GetThemeInt(theme.handle(), partId, stateId, TMT_BORDERCOLOR, &borderSize);
1499
1500 // Inner white border
1501 p->setPen(QPen(option->palette.base().color(), 0));
1502 const qreal dpi = QStyleHelper::dpi(option);
1503 const auto topLevelAdjustment = QStyleHelper::dpiScaled(0.5, dpi);
1504 const auto bottomRightAdjustment = QStyleHelper::dpiScaled(-1, dpi);
1505 p->drawRect(QRectF(option->rect).adjusted(topLevelAdjustment, topLevelAdjustment,
1506 bottomRightAdjustment, bottomRightAdjustment));
1507 // Outer dark border
1508 p->setPen(QPen(bordercolor, 0));
1509 p->drawRect(QRectF(option->rect).adjusted(0, 0, -topLevelAdjustment, -topLevelAdjustment));
1510 p->setPen(oldPen);
1511 return;
1512 }
1513 if (fillType == BT_NONE)
1514 return;
1515 }
1516 break;
1517 }
1518 case PE_FrameLineEdit: {
1519 // we try to check if this lineedit is a delegate on a QAbstractItemView-derived class.
1520 if (QWindowsXPStylePrivate::isItemViewDelegateLineEdit(widget)) {
1521 QPen oldPen = p->pen();
1522 // Inner white border
1523 p->setPen(QPen(option->palette.base().color(), 1));
1524 p->drawRect(option->rect.adjusted(1, 1, -2, -2));
1525 // Outer dark border
1526 p->setPen(QPen(option->palette.shadow().color(), 1));
1527 p->drawRect(option->rect.adjusted(0, 0, -1, -1));
1528 p->setPen(oldPen);
1529 return;
1530 }
1531 if (qstyleoption_cast<const QStyleOptionFrame *>(option)) {
1532 themeNumber = QWindowsXPStylePrivate::EditTheme;
1533 partId = EP_EDITTEXT;
1534 noContent = true;
1535 if (!(flags & State_Enabled))
1536 stateId = ETS_DISABLED;
1537 else
1538 stateId = ETS_NORMAL;
1539 }
1540 break;
1541 }
1542
1543 case PE_PanelLineEdit:
1544 if (const QStyleOptionFrame *panel = qstyleoption_cast<const QStyleOptionFrame *>(option)) {
1545 themeNumber = QWindowsXPStylePrivate::EditTheme;
1546 partId = EP_EDITTEXT;
1547 noBorder = true;
1548 bool isEnabled = flags & State_Enabled;
1549
1550 stateId = isEnabled ? ETS_NORMAL : ETS_DISABLED;
1551
1552 if (QWindowsXPStylePrivate::isLineEditBaseColorSet(option, widget)) {
1553 p->fillRect(panel->rect, panel->palette.brush(QPalette::Base));
1554 } else {
1555 XPThemeData theme(nullptr, p, themeNumber, partId, stateId, rect);
1556 if (!theme.isValid()) {
1557 QWindowsStyle::drawPrimitive(pe, option, p, widget);
1558 return;
1559 }
1560 int bgType;
1561 GetThemeEnumValue(theme.handle(), partId, stateId, TMT_BGTYPE, &bgType);
1562 if( bgType == BT_IMAGEFILE ) {
1563 theme.mirrorHorizontally = hMirrored;
1564 theme.mirrorVertically = vMirrored;
1565 theme.noBorder = noBorder;
1566 theme.noContent = noContent;
1567 theme.rotate = rotate;
1568 d->drawBackground(theme);
1569 } else {
1570 QBrush fillColor = option->palette.brush(QPalette::Base);
1571
1572 if (!isEnabled) {
1573 PROPERTYORIGIN origin = PO_NOTFOUND;
1574 GetThemePropertyOrigin(theme.handle(), theme.partId, theme.stateId, TMT_FILLCOLOR, &origin);
1575 // Use only if the fill property comes from our part
1576 if ((origin == PO_PART || origin == PO_STATE)) {
1577 COLORREF bgRef;
1578 GetThemeColor(theme.handle(), partId, stateId, TMT_FILLCOLOR, &bgRef);
1579 fillColor = QBrush(qRgb(GetRValue(bgRef), GetGValue(bgRef), GetBValue(bgRef)));
1580 }
1581 }
1582 p->fillRect(option->rect, fillColor);
1583 }
1584 }
1585
1586 if (panel->lineWidth > 0)
1587 proxy()->drawPrimitive(PE_FrameLineEdit, panel, p, widget);
1588 return;
1589 }
1590 break;
1591
1592 case PE_FrameTabWidget:
1593 if (const QStyleOptionTabWidgetFrame *tab = qstyleoption_cast<const QStyleOptionTabWidgetFrame *>(option))
1594 {
1595 themeNumber = QWindowsXPStylePrivate::TabTheme;
1596 partId = TABP_PANE;
1597
1598 if (widget) {
1599 bool useGradient = true;
1600 const int maxlength = 256;
1601 wchar_t themeFileName[maxlength];
1602 wchar_t themeColor[maxlength];
1603 // Due to a a scaling issue with the XP Silver theme, tab gradients are not used with it
1604 if (GetCurrentThemeName(themeFileName, maxlength, themeColor, maxlength, nullptr, 0) == S_OK) {
1605 wchar_t *offset = nullptr;
1606 if ((offset = wcsrchr(themeFileName, QChar(QLatin1Char('\\')).unicode())) != nullptr) {
1607 offset++;
1608 if (!lstrcmp(offset, L"Luna.msstyles") && !lstrcmp(offset, L"Metallic")) {
1609 useGradient = false;
1610 }
1611 }
1612 }
1613 // This should work, but currently there's an error in the ::drawBackgroundDirectly()
1614 // code, when using the HDC directly..
1615 if (useGradient) {
1616 QStyleOptionTabWidgetFrame frameOpt = *tab;
1617 frameOpt.rect = widget->rect();
1618 QRect contentsRect = subElementRect(SE_TabWidgetTabContents, &frameOpt, widget);
1619 QRegion reg = option->rect;
1620 reg -= contentsRect;
1621 p->setClipRegion(reg);
1622 XPThemeData theme(widget, p, themeNumber, partId, stateId, rect);
1623 theme.mirrorHorizontally = hMirrored;
1624 theme.mirrorVertically = vMirrored;
1625 d->drawBackground(theme);
1626 p->setClipRect(contentsRect);
1627 partId = TABP_BODY;
1628 }
1629 }
1630 switch (tab->shape) {
1631 case QTabBar::RoundedNorth:
1632 case QTabBar::TriangularNorth:
1633 break;
1634 case QTabBar::RoundedSouth:
1635 case QTabBar::TriangularSouth:
1636 vMirrored = true;
1637 break;
1638 case QTabBar::RoundedEast:
1639 case QTabBar::TriangularEast:
1640 rotate = 90;
1641 break;
1642 case QTabBar::RoundedWest:
1643 case QTabBar::TriangularWest:
1644 rotate = 90;
1645 hMirrored = true;
1646 break;
1647 default:
1648 break;
1649 }
1650 }
1651 break;
1652
1653 case PE_FrameMenu:
1654 p->save();
1655 p->setPen(option->palette.dark().color());
1656 p->drawRect(rect.adjusted(0, 0, -1, -1));
1657 p->restore();
1658 return;
1659
1660 case PE_PanelMenuBar:
1661 break;
1662
1663 case PE_FrameDockWidget:
1664 if (const QStyleOptionFrame *frm = qstyleoption_cast<const QStyleOptionFrame *>(option))
1665 {
1666 themeNumber = QWindowsXPStylePrivate::WindowTheme;
1667 if (flags & State_Active)
1668 stateId = FS_ACTIVE;
1669 else
1670 stateId = FS_INACTIVE;
1671
1672 int fwidth = proxy()->pixelMetric(PM_DockWidgetFrameWidth, frm, widget);
1673
1674 XPThemeData theme(widget, p, themeNumber, 0, stateId);
1675 if (!theme.isValid())
1676 break;
1677 theme.rect = QRect(frm->rect.x(), frm->rect.y(), frm->rect.x()+fwidth, frm->rect.height()-fwidth); theme.partId = WP_SMALLFRAMELEFT;
1678 d->drawBackground(theme);
1679 theme.rect = QRect(frm->rect.width()-fwidth, frm->rect.y(), fwidth, frm->rect.height()-fwidth);
1680 theme.partId = WP_SMALLFRAMERIGHT;
1681 d->drawBackground(theme);
1682 theme.rect = QRect(frm->rect.x(), frm->rect.bottom()-fwidth+1, frm->rect.width(), fwidth);
1683 theme.partId = WP_SMALLFRAMEBOTTOM;
1684 d->drawBackground(theme);
1685 return;
1686 }
1687 break;
1688
1689 case PE_IndicatorHeaderArrow:
1690 {
1691 #if 0 // XP theme engine doesn't know about this :(
1692 name = QWindowsXPStylePrivate::HeaderTheme;
1693 partId = HP_HEADERSORTARROW;
1694 if (flags & State_Down)
1695 stateId = HSAS_SORTEDDOWN;
1696 else
1697 stateId = HSAS_SORTEDUP;
1698 #else
1699 if (const QStyleOptionHeader *header = qstyleoption_cast<const QStyleOptionHeader *>(option)) {
1700 p->save();
1701 p->setPen(option->palette.dark().color());
1702 p->translate(0, option->rect.height()/2 - 4);
1703 if (header->sortIndicator & QStyleOptionHeader::SortUp) { // invert logic to follow Windows style guide
1704 p->drawLine(option->rect.x(), option->rect.y(), option->rect.x()+8, option->rect.y());
1705 p->drawLine(option->rect.x()+1, option->rect.y()+1, option->rect.x()+7, option->rect.y()+1);
1706 p->drawLine(option->rect.x()+2, option->rect.y()+2, option->rect.x()+6, option->rect.y()+2);
1707 p->drawLine(option->rect.x()+3, option->rect.y()+3, option->rect.x()+5, option->rect.y()+3);
1708 p->drawPoint(option->rect.x()+4, option->rect.y()+4);
1709 } else if(header->sortIndicator & QStyleOptionHeader::SortDown) {
1710 p->drawLine(option->rect.x(), option->rect.y()+4, option->rect.x()+8, option->rect.y()+4);
1711 p->drawLine(option->rect.x()+1, option->rect.y()+3, option->rect.x()+7, option->rect.y()+3);
1712 p->drawLine(option->rect.x()+2, option->rect.y()+2, option->rect.x()+6, option->rect.y()+2);
1713 p->drawLine(option->rect.x()+3, option->rect.y()+1, option->rect.x()+5, option->rect.y()+1);
1714 p->drawPoint(option->rect.x()+4, option->rect.y());
1715 }
1716 p->restore();
1717 return;
1718 }
1719 #endif
1720 }
1721 break;
1722
1723 case PE_FrameStatusBarItem:
1724 themeNumber = QWindowsXPStylePrivate::StatusTheme;
1725 partId = SP_PANE;
1726 break;
1727
1728 case PE_FrameGroupBox:
1729 themeNumber = QWindowsXPStylePrivate::ButtonTheme;
1730 partId = BP_GROUPBOX;
1731 if (!(flags & State_Enabled))
1732 stateId = GBS_DISABLED;
1733 else
1734 stateId = GBS_NORMAL;
1735 if (const QStyleOptionFrame *frame = qstyleoption_cast<const QStyleOptionFrame *>(option)) {
1736 if (frame->features & QStyleOptionFrame::Flat) {
1737 // Windows XP does not have a theme part for a flat GroupBox, paint it with the windows style
1738 QRect fr = frame->rect;
1739 QPoint p1(fr.x(), fr.y() + 1);
1740 QPoint p2(fr.x() + fr.width(), p1.y() + 1);
1741 rect = QRect(p1, p2);
1742 themeNumber = -1;
1743 }
1744 }
1745 break;
1746
1747 case PE_IndicatorProgressChunk:
1748 {
1749 Qt::Orientation orient = Qt::Horizontal;
1750 bool inverted = false;
1751 if (const QStyleOptionProgressBar *pb = qstyleoption_cast<const QStyleOptionProgressBar *>(option)) {
1752 orient = pb->orientation;
1753 inverted = pb->invertedAppearance;
1754 }
1755 if (orient == Qt::Horizontal) {
1756 partId = PP_CHUNK;
1757 rect = QRect(option->rect.x(), option->rect.y(), option->rect.width(), option->rect.height() );
1758 if (inverted && option->direction == Qt::LeftToRight)
1759 hMirrored = true;
1760 } else {
1761 partId = PP_CHUNKVERT;
1762 rect = QRect(option->rect.x(), option->rect.y(), option->rect.width(), option->rect.height());
1763 }
1764 themeNumber = QWindowsXPStylePrivate::ProgressTheme;
1765 stateId = 1;
1766 }
1767 break;
1768
1769 case PE_FrameWindow:
1770 if (const QStyleOptionFrame *frm = qstyleoption_cast<const QStyleOptionFrame *>(option))
1771 {
1772 themeNumber = QWindowsXPStylePrivate::WindowTheme;
1773 if (flags & State_Active)
1774 stateId = FS_ACTIVE;
1775 else
1776 stateId = FS_INACTIVE;
1777
1778 int fwidth = int((frm->lineWidth + frm->midLineWidth) / QWindowsStylePrivate::nativeMetricScaleFactor(widget));
1779
1780 XPThemeData theme(widget, p, themeNumber, 0, stateId);
1781 if (!theme.isValid())
1782 break;
1783
1784 // May fail due to too-large buffers for large widgets, fall back to Windows style.
1785 theme.rect = QRect(option->rect.x(), option->rect.y()+fwidth, option->rect.x()+fwidth, option->rect.height()-fwidth);
1786 theme.partId = WP_FRAMELEFT;
1787 if (!d->drawBackground(theme)) {
1788 QWindowsStyle::drawPrimitive(pe, option, p, widget);
1789 return;
1790 }
1791 theme.rect = QRect(option->rect.width()-fwidth, option->rect.y()+fwidth, fwidth, option->rect.height()-fwidth);
1792 theme.partId = WP_FRAMERIGHT;
1793 if (!d->drawBackground(theme)) {
1794 QWindowsStyle::drawPrimitive(pe, option, p, widget);
1795 return;
1796 }
1797 theme.rect = QRect(option->rect.x(), option->rect.height()-fwidth, option->rect.width(), fwidth);
1798 theme.partId = WP_FRAMEBOTTOM;
1799 if (!d->drawBackground(theme)) {
1800 QWindowsStyle::drawPrimitive(pe, option, p, widget);
1801 return;
1802 }
1803 theme.rect = QRect(option->rect.x(), option->rect.y(), option->rect.width(), option->rect.y()+fwidth);
1804 theme.partId = WP_CAPTION;
1805 if (!d->drawBackground(theme))
1806 QWindowsStyle::drawPrimitive(pe, option, p, widget);
1807 return;
1808 }
1809 break;
1810
1811 case PE_IndicatorBranch:
1812 {
1813 static const int decoration_size = 9;
1814 int mid_h = option->rect.x() + option->rect.width() / 2;
1815 int mid_v = option->rect.y() + option->rect.height() / 2;
1816 int bef_h = mid_h;
1817 int bef_v = mid_v;
1818 int aft_h = mid_h;
1819 int aft_v = mid_v;
1820 QBrush brush(option->palette.dark().color(), Qt::Dense4Pattern);
1821 if (option->state & State_Item) {
1822 if (option->direction == Qt::RightToLeft)
1823 p->fillRect(option->rect.left(), mid_v, bef_h - option->rect.left(), 1, brush);
1824 else
1825 p->fillRect(aft_h, mid_v, option->rect.right() - aft_h + 1, 1, brush);
1826 }
1827 if (option->state & State_Sibling)
1828 p->fillRect(mid_h, aft_v, 1, option->rect.bottom() - aft_v + 1, brush);
1829 if (option->state & (State_Open | State_Children | State_Item | State_Sibling))
1830 p->fillRect(mid_h, option->rect.y(), 1, bef_v - option->rect.y(), brush);
1831 if (option->state & State_Children) {
1832 int delta = decoration_size / 2;
1833 bef_h -= delta;
1834 bef_v -= delta;
1835 aft_h += delta;
1836 aft_v += delta;
1837 XPThemeData theme(nullptr, p, QWindowsXPStylePrivate::XpTreeViewTheme);
1838 theme.rect = QRect(bef_h, bef_v, decoration_size, decoration_size);
1839 theme.partId = TVP_GLYPH;
1840 theme.stateId = flags & QStyle::State_Open ? GLPS_OPENED : GLPS_CLOSED;
1841 d->drawBackground(theme);
1842 }
1843 }
1844 return;
1845
1846 case PE_IndicatorToolBarSeparator:
1847 if (option->rect.height() < 3) {
1848 // XP style requires a few pixels for the separator
1849 // to be visible.
1850 QWindowsStyle::drawPrimitive(pe, option, p, widget);
1851 return;
1852 }
1853 themeNumber = QWindowsXPStylePrivate::ToolBarTheme;
1854 partId = TP_SEPARATOR;
1855
1856 if (option->state & State_Horizontal)
1857 partId = TP_SEPARATOR;
1858 else
1859 partId = TP_SEPARATORVERT;
1860
1861 break;
1862
1863 case PE_IndicatorToolBarHandle:
1864
1865 themeNumber = QWindowsXPStylePrivate::RebarTheme;
1866 partId = RP_GRIPPER;
1867 if (option->state & State_Horizontal) {
1868 partId = RP_GRIPPER;
1869 rect.adjust(0, 0, -2, 0);
1870 }
1871 else {
1872 partId = RP_GRIPPERVERT;
1873 rect.adjust(0, 0, 0, -2);
1874 }
1875 break;
1876
1877 case PE_IndicatorItemViewItemCheck: {
1878 QStyleOptionButton button;
1879 button.QStyleOption::operator=(*option);
1880 button.state &= ~State_MouseOver;
1881 proxy()->drawPrimitive(PE_IndicatorCheckBox, &button, p, widget);
1882 return;
1883 }
1884
1885 default:
1886 break;
1887 }
1888
1889 XPThemeData theme(widget, p, themeNumber, partId, stateId, rect);
1890 if (!theme.isValid()) {
1891 QWindowsStyle::drawPrimitive(pe, option, p, widget);
1892 return;
1893 }
1894 theme.mirrorHorizontally = hMirrored;
1895 theme.mirrorVertically = vMirrored;
1896 theme.noBorder = noBorder;
1897 theme.noContent = noContent;
1898 theme.rotate = rotate;
1899 d->drawBackground(theme);
1900 }
1901
1902 /*!
1903 \reimp
1904 */
drawControl(ControlElement element,const QStyleOption * option,QPainter * p,const QWidget * widget) const1905 void QWindowsXPStyle::drawControl(ControlElement element, const QStyleOption *option, QPainter *p,
1906 const QWidget *widget) const
1907 {
1908 QWindowsXPStylePrivate *d = const_cast<QWindowsXPStylePrivate*>(d_func());
1909 if (!QWindowsXPStylePrivate::useXP()) {
1910 QWindowsStyle::drawControl(element, option, p, widget);
1911 return;
1912 }
1913
1914 QRect rect(option->rect);
1915 State flags = option->state;
1916
1917 int rotate = 0;
1918 bool hMirrored = false;
1919 bool vMirrored = false;
1920
1921 int themeNumber = -1;
1922 int partId = 0;
1923 int stateId = 0;
1924 switch (element) {
1925 case CE_SizeGrip:
1926 {
1927 themeNumber = QWindowsXPStylePrivate::StatusTheme;
1928 partId = SP_GRIPPER;
1929 XPThemeData theme(nullptr, p, themeNumber, partId);
1930 QSize size = (theme.size() * QWindowsStylePrivate::nativeMetricScaleFactor(widget)).toSize();
1931 size.rheight()--;
1932 if (const QStyleOptionSizeGrip *sg = qstyleoption_cast<const QStyleOptionSizeGrip *>(option)) {
1933 switch (sg->corner) {
1934 case Qt::BottomRightCorner:
1935 rect = QRect(QPoint(rect.right() - size.width(), rect.bottom() - size.height()), size);
1936 break;
1937 case Qt::BottomLeftCorner:
1938 rect = QRect(QPoint(rect.left() + 1, rect.bottom() - size.height()), size);
1939 hMirrored = true;
1940 break;
1941 case Qt::TopRightCorner:
1942 rect = QRect(QPoint(rect.right() - size.width(), rect.top() + 1), size);
1943 vMirrored = true;
1944 break;
1945 case Qt::TopLeftCorner:
1946 rect = QRect(rect.topLeft() + QPoint(1, 1), size);
1947 hMirrored = vMirrored = true;
1948 }
1949 }
1950 }
1951 break;
1952
1953 case CE_HeaderSection:
1954 themeNumber = QWindowsXPStylePrivate::HeaderTheme;
1955 partId = HP_HEADERITEM;
1956 if (flags & State_Sunken)
1957 stateId = HIS_PRESSED;
1958 else if (flags & State_MouseOver)
1959 stateId = HIS_HOT;
1960 else
1961 stateId = HIS_NORMAL;
1962 break;
1963
1964 case CE_Splitter:
1965 p->eraseRect(option->rect);
1966 return;
1967
1968 case CE_PushButtonBevel:
1969 if (const QStyleOptionButton *btn = qstyleoption_cast<const QStyleOptionButton *>(option))
1970 {
1971 themeNumber = QWindowsXPStylePrivate::ButtonTheme;
1972 partId = BP_PUSHBUTTON;
1973 bool justFlat = ((btn->features & QStyleOptionButton::Flat) && !(flags & (State_On|State_Sunken)))
1974 || ((btn->features & QStyleOptionButton::CommandLinkButton)
1975 && !(flags & State_MouseOver)
1976 && !(btn->features & QStyleOptionButton::DefaultButton));
1977 if (!(flags & State_Enabled) && !(btn->features & QStyleOptionButton::Flat))
1978 stateId = PBS_DISABLED;
1979 else if (justFlat)
1980 ;
1981 else if (flags & (State_Sunken | State_On))
1982 stateId = PBS_PRESSED;
1983 else if (flags & State_MouseOver)
1984 stateId = PBS_HOT;
1985 else if (btn->features & QStyleOptionButton::DefaultButton)
1986 stateId = PBS_DEFAULTED;
1987 else
1988 stateId = PBS_NORMAL;
1989
1990 if (!justFlat) {
1991 XPThemeData theme(widget, p, themeNumber, partId, stateId, rect);
1992 d->drawBackground(theme);
1993 }
1994
1995 if (btn->features & QStyleOptionButton::HasMenu) {
1996 int mbiw = 0, mbih = 0;
1997 XPThemeData theme(widget, nullptr,
1998 QWindowsXPStylePrivate::ToolBarTheme,
1999 TP_SPLITBUTTONDROPDOWN);
2000 if (theme.isValid()) {
2001 const QSize size = (theme.size() * QWindowsStylePrivate::nativeMetricScaleFactor(widget)).toSize();
2002 mbiw = size.width();
2003 mbih = size.height();
2004 }
2005
2006 QRect ir = btn->rect;
2007 QStyleOptionButton newBtn = *btn;
2008 newBtn.rect = QRect(ir.right() - mbiw - 1, 1 + (ir.height()/2) - (mbih/2), mbiw, mbih);
2009 proxy()->drawPrimitive(PE_IndicatorArrowDown, &newBtn, p, widget);
2010 }
2011 return;
2012 }
2013 break;
2014 case CE_TabBarTab:
2015 if (const QStyleOptionTab *tab = qstyleoption_cast<const QStyleOptionTab *>(option))
2016 {
2017 stateId = tab->state & State_Enabled ? TIS_NORMAL : TIS_DISABLED;
2018 }
2019 break;
2020
2021 case CE_TabBarTabShape:
2022 if (const QStyleOptionTab *tab = qstyleoption_cast<const QStyleOptionTab *>(option))
2023 {
2024 themeNumber = QWindowsXPStylePrivate::TabTheme;
2025 bool isDisabled = !(tab->state & State_Enabled);
2026 bool hasFocus = tab->state & State_HasFocus;
2027 bool isHot = tab->state & State_MouseOver;
2028 bool selected = tab->state & State_Selected;
2029 bool lastTab = tab->position == QStyleOptionTab::End;
2030 bool firstTab = tab->position == QStyleOptionTab::Beginning;
2031 bool onlyOne = tab->position == QStyleOptionTab::OnlyOneTab;
2032 bool leftAligned = proxy()->styleHint(SH_TabBar_Alignment, tab, widget) == Qt::AlignLeft;
2033 bool centerAligned = proxy()->styleHint(SH_TabBar_Alignment, tab, widget) == Qt::AlignCenter;
2034 int borderThickness = proxy()->pixelMetric(PM_DefaultFrameWidth, option, widget);
2035 int tabOverlap = proxy()->pixelMetric(PM_TabBarTabOverlap, option, widget);
2036
2037 if (isDisabled)
2038 stateId = TIS_DISABLED;
2039 else if (selected)
2040 stateId = TIS_SELECTED;
2041 else if (hasFocus)
2042 stateId = TIS_FOCUSED;
2043 else if (isHot)
2044 stateId = TIS_HOT;
2045 else
2046 stateId = TIS_NORMAL;
2047
2048 // Selecting proper part depending on position
2049 if (firstTab || onlyOne) {
2050 if (leftAligned) {
2051 partId = TABP_TABITEMLEFTEDGE;
2052 } else if (centerAligned) {
2053 partId = TABP_TABITEM;
2054 } else { // rightAligned
2055 partId = TABP_TABITEMRIGHTEDGE;
2056 }
2057 } else {
2058 partId = TABP_TABITEM;
2059 }
2060
2061 if (tab->direction == Qt::RightToLeft
2062 && (tab->shape == QTabBar::RoundedNorth
2063 || tab->shape == QTabBar::RoundedSouth)) {
2064 bool temp = firstTab;
2065 firstTab = lastTab;
2066 lastTab = temp;
2067 }
2068 bool begin = firstTab || onlyOne;
2069 bool end = lastTab || onlyOne;
2070 switch (tab->shape) {
2071 case QTabBar::RoundedNorth:
2072 if (selected)
2073 rect.adjust(begin ? 0 : -tabOverlap, 0, end ? 0 : tabOverlap, borderThickness);
2074 else
2075 rect.adjust(begin? tabOverlap : 0, tabOverlap, end ? -tabOverlap : 0, 0);
2076 break;
2077 case QTabBar::RoundedSouth:
2078 //vMirrored = true;
2079 rotate = 180; // Not 100% correct, but works
2080 if (selected)
2081 rect.adjust(begin ? 0 : -tabOverlap , -borderThickness, end ? 0 : tabOverlap, 0);
2082 else
2083 rect.adjust(begin ? tabOverlap : 0, 0, end ? -tabOverlap : 0 , -tabOverlap);
2084 break;
2085 case QTabBar::RoundedEast:
2086 rotate = 90;
2087 if (selected) {
2088 rect.adjust(-borderThickness, begin ? 0 : -tabOverlap, 0, end ? 0 : tabOverlap);
2089 }else{
2090 rect.adjust(0, begin ? tabOverlap : 0, -tabOverlap, end ? -tabOverlap : 0);
2091 }
2092 break;
2093 case QTabBar::RoundedWest:
2094 hMirrored = true;
2095 rotate = 90;
2096 if (selected) {
2097 rect.adjust(0, begin ? 0 : -tabOverlap, borderThickness, end ? 0 : tabOverlap);
2098 }else{
2099 rect.adjust(tabOverlap, begin ? tabOverlap : 0, 0, end ? -tabOverlap : 0);
2100 }
2101 break;
2102 default:
2103 themeNumber = -1; // Do our own painting for triangular
2104 break;
2105 }
2106
2107 if (!selected) {
2108 switch (tab->shape) {
2109 case QTabBar::RoundedNorth:
2110 rect.adjust(0,0, 0,-1);
2111 break;
2112 case QTabBar::RoundedSouth:
2113 rect.adjust(0,1, 0,0);
2114 break;
2115 case QTabBar::RoundedEast:
2116 rect.adjust( 1,0, 0,0);
2117 break;
2118 case QTabBar::RoundedWest:
2119 rect.adjust(0,0, -1,0);
2120 break;
2121 default:
2122 break;
2123 }
2124 }
2125 }
2126 break;
2127
2128 case CE_ProgressBarGroove:
2129 {
2130 Qt::Orientation orient = Qt::Horizontal;
2131 if (const QStyleOptionProgressBar *pb = qstyleoption_cast<const QStyleOptionProgressBar *>(option))
2132 orient = pb->orientation;
2133 partId = (orient == Qt::Horizontal) ? PP_BAR : PP_BARVERT;
2134 themeNumber = QWindowsXPStylePrivate::ProgressTheme;
2135 stateId = 1;
2136 }
2137 break;
2138
2139 case CE_MenuEmptyArea:
2140 case CE_MenuItem:
2141 if (const QStyleOptionMenuItem *menuitem = qstyleoption_cast<const QStyleOptionMenuItem *>(option))
2142 {
2143 int tab = menuitem->tabWidth;
2144 bool dis = !(menuitem->state & State_Enabled);
2145 bool act = menuitem->state & State_Selected;
2146 bool checkable = menuitem->menuHasCheckableItems;
2147 bool checked = checkable ? menuitem->checked : false;
2148
2149 // windows always has a check column, regardless whether we have an icon or not
2150 int checkcol = qMax(menuitem->maxIconWidth, 12);
2151
2152 int x, y, w, h;
2153 rect.getRect(&x, &y, &w, &h);
2154
2155 QBrush fill = menuitem->palette.brush(act ? QPalette::Highlight : QPalette::Button);
2156 p->fillRect(rect, fill);
2157
2158 if (element == CE_MenuEmptyArea)
2159 break;
2160
2161 // draw separator -------------------------------------------------
2162 if (menuitem->menuItemType == QStyleOptionMenuItem::Separator) {
2163 int yoff = y-1 + h / 2;
2164 p->setPen(menuitem->palette.dark().color());
2165 p->drawLine(x, yoff, x+w, yoff);
2166 ++yoff;
2167 p->setPen(menuitem->palette.light().color());
2168 p->drawLine(x, yoff, x+w, yoff);
2169 return;
2170 }
2171
2172 int xpos = x;
2173
2174 // draw icon ------------------------------------------------------
2175 if (!menuitem->icon.isNull()) {
2176 QIcon::Mode mode = dis ? QIcon::Disabled : QIcon::Normal;
2177 if (act && !dis)
2178 mode = QIcon::Active;
2179 QPixmap pixmap = checked ?
2180 menuitem->icon.pixmap(proxy()->pixelMetric(PM_SmallIconSize, option, widget), mode, QIcon::On) :
2181 menuitem->icon.pixmap(proxy()->pixelMetric(PM_SmallIconSize, option, widget), mode);
2182 const int pixw = pixmap.width() / pixmap.devicePixelRatio();
2183 const int pixh = pixmap.height() / pixmap.devicePixelRatio();
2184 QRect iconRect(0, 0, pixw, pixh);
2185 iconRect.moveCenter(QRect(xpos, y, checkcol, h).center());
2186 QRect vIconRect = visualRect(option->direction, option->rect, iconRect);
2187 p->setPen(menuitem->palette.text().color());
2188 p->setBrush(Qt::NoBrush);
2189 if (checked)
2190 p->drawRect(vIconRect.adjusted(-1, -1, 0, 0));
2191 p->drawPixmap(vIconRect.topLeft(), pixmap);
2192
2193 // draw checkmark -------------------------------------------------
2194 } else if (checked) {
2195 QStyleOptionMenuItem newMi = *menuitem;
2196 newMi.state = State_None;
2197 if (!dis)
2198 newMi.state |= State_Enabled;
2199 if (act)
2200 newMi.state |= State_On;
2201
2202 QRect checkMarkRect = QRect(menuitem->rect.x() + windowsItemFrame,
2203 menuitem->rect.y() + windowsItemFrame,
2204 checkcol - 2 * windowsItemFrame,
2205 menuitem->rect.height() - 2*windowsItemFrame);
2206 newMi.rect = visualRect(option->direction, option->rect, checkMarkRect);
2207 proxy()->drawPrimitive(PE_IndicatorMenuCheckMark, &newMi, p, widget);
2208 }
2209
2210 QColor textColor = dis ? menuitem->palette.text().color() :
2211 act ? menuitem->palette.highlightedText().color() : menuitem->palette.buttonText().color();
2212 p->setPen(textColor);
2213
2214 // draw text ------------------------------------------------------
2215 int xm = windowsItemFrame + checkcol + windowsItemHMargin;
2216 xpos = menuitem->rect.x() + xm;
2217 QRect textRect(xpos, y + windowsItemVMargin, w - xm - windowsRightBorder - tab + 1, h - 2 * windowsItemVMargin);
2218 QRect vTextRect = visualRect(option->direction, option->rect, textRect);
2219 QString s = menuitem->text;
2220 if (!s.isEmpty()) {
2221 p->save();
2222 int t = s.indexOf(QLatin1Char('\t'));
2223 int text_flags = Qt::AlignVCenter|Qt::TextShowMnemonic | Qt::TextDontClip | Qt::TextSingleLine | Qt::AlignLeft;
2224 if (!proxy()->styleHint(SH_UnderlineShortcut, menuitem, widget))
2225 text_flags |= Qt::TextHideMnemonic;
2226 // draw tab text ----------------
2227 if (t >= 0) {
2228 QRect vShortcutRect = visualRect(option->direction, option->rect, QRect(textRect.topRight(), menuitem->rect.bottomRight()));
2229 if (dis && !act && proxy()->styleHint(SH_EtchDisabledText, option, widget)) {
2230 p->setPen(menuitem->palette.light().color());
2231 p->drawText(vShortcutRect.adjusted(1,1,1,1), text_flags, s.mid(t + 1));
2232 p->setPen(textColor);
2233 }
2234 p->drawText(vShortcutRect, text_flags, s.mid(t + 1));
2235 s = s.left(t);
2236 }
2237 QFont font = menuitem->font;
2238 if (menuitem->menuItemType == QStyleOptionMenuItem::DefaultItem)
2239 font.setBold(true);
2240 p->setFont(font);
2241 if (dis && !act && proxy()->styleHint(SH_EtchDisabledText, option, widget)) {
2242 p->setPen(menuitem->palette.light().color());
2243 p->drawText(vTextRect.adjusted(1,1,1,1), text_flags, s.left(t));
2244 p->setPen(textColor);
2245 }
2246 p->drawText(vTextRect, text_flags, s);
2247 p->restore();
2248 }
2249
2250 // draw sub menu arrow --------------------------------------------
2251 if (menuitem->menuItemType == QStyleOptionMenuItem::SubMenu) {
2252 int dim = (h - 2) / 2;
2253 PrimitiveElement arrow;
2254 arrow = (option->direction == Qt::RightToLeft) ? PE_IndicatorArrowLeft : PE_IndicatorArrowRight;
2255 xpos = x + w - windowsArrowHMargin - windowsItemFrame - dim;
2256 QRect vSubMenuRect = visualRect(option->direction, option->rect, QRect(xpos, y + h / 2 - dim / 2, dim, dim));
2257 QStyleOptionMenuItem newMI = *menuitem;
2258 newMI.rect = vSubMenuRect;
2259 newMI.state = dis ? State_None : State_Enabled;
2260 if (act)
2261 newMI.palette.setColor(QPalette::ButtonText, newMI.palette.highlightedText().color());
2262 proxy()->drawPrimitive(arrow, &newMI, p, widget);
2263 }
2264 }
2265 return;
2266
2267 case CE_MenuBarItem:
2268 if (const QStyleOptionMenuItem *mbi = qstyleoption_cast<const QStyleOptionMenuItem *>(option))
2269 {
2270 if (mbi->menuItemType == QStyleOptionMenuItem::DefaultItem)
2271 break;
2272
2273 bool act = mbi->state & State_Selected;
2274 bool dis = !(mbi->state & State_Enabled);
2275
2276 QBrush fill = mbi->palette.brush(act ? QPalette::Highlight : QPalette::Button);
2277 QPalette::ColorRole textRole = dis ? QPalette::Text:
2278 act ? QPalette::HighlightedText : QPalette::ButtonText;
2279 QPixmap pix = mbi->icon.pixmap(proxy()->pixelMetric(PM_SmallIconSize, option, widget), QIcon::Normal);
2280
2281 uint alignment = Qt::AlignCenter | Qt::TextShowMnemonic | Qt::TextDontClip | Qt::TextSingleLine;
2282 if (!proxy()->styleHint(SH_UnderlineShortcut, mbi, widget))
2283 alignment |= Qt::TextHideMnemonic;
2284
2285 p->fillRect(rect, fill);
2286 if (!pix.isNull())
2287 drawItemPixmap(p, mbi->rect, alignment, pix);
2288 else
2289 drawItemText(p, mbi->rect, alignment, mbi->palette, mbi->state & State_Enabled, mbi->text, textRole);
2290 }
2291 return;
2292 #if QT_CONFIG(dockwidget)
2293 case CE_DockWidgetTitle:
2294 if (const QStyleOptionDockWidget *dwOpt = qstyleoption_cast<const QStyleOptionDockWidget *>(option))
2295 {
2296 int buttonMargin = 4;
2297 int mw = proxy()->pixelMetric(QStyle::PM_DockWidgetTitleMargin, dwOpt, widget);
2298 int fw = proxy()->pixelMetric(PM_DockWidgetFrameWidth, dwOpt, widget);
2299 bool isFloating = widget && widget->isWindow();
2300 bool isActive = dwOpt->state & State_Active;
2301
2302 const bool verticalTitleBar = dwOpt->verticalTitleBar;
2303
2304 if (verticalTitleBar) {
2305 rect = rect.transposed();
2306
2307 p->translate(rect.left() - 1, rect.top() + rect.width());
2308 p->rotate(-90);
2309 p->translate(-rect.left() + 1, -rect.top());
2310 }
2311 QRect r = rect.adjusted(0, 2, -1, -3);
2312 QRect titleRect = r;
2313
2314 if (dwOpt->closable) {
2315 QSize sz = proxy()->standardIcon(QStyle::SP_TitleBarCloseButton, dwOpt, widget).actualSize(QSize(10, 10));
2316 titleRect.adjust(0, 0, -sz.width() - mw - buttonMargin, 0);
2317 }
2318
2319 if (dwOpt->floatable) {
2320 QSize sz = proxy()->standardIcon(QStyle::SP_TitleBarMaxButton, dwOpt, widget).actualSize(QSize(10, 10));
2321 titleRect.adjust(0, 0, -sz.width() - mw - buttonMargin, 0);
2322 }
2323
2324 if (isFloating) {
2325 titleRect.adjust(0, -fw, 0, 0);
2326 if (widget != nullptr && widget->windowIcon().cacheKey() != QApplication::windowIcon().cacheKey())
2327 titleRect.adjust(titleRect.height() + mw, 0, 0, 0);
2328 } else {
2329 titleRect.adjust(mw, 0, 0, 0);
2330 if (!dwOpt->floatable && !dwOpt->closable)
2331 titleRect.adjust(0, 0, -mw, 0);
2332 }
2333
2334 if (!verticalTitleBar)
2335 titleRect = visualRect(dwOpt->direction, r, titleRect);
2336
2337 if (!isFloating) {
2338 QPen oldPen = p->pen();
2339 QString titleText = p->fontMetrics().elidedText(dwOpt->title, Qt::ElideRight, titleRect.width());
2340 p->setPen(dwOpt->palette.color(QPalette::Dark));
2341 p->drawRect(r);
2342
2343 if (!titleText.isEmpty()) {
2344 drawItemText(p, titleRect,
2345 Qt::AlignLeft | Qt::AlignVCenter | Qt::TextShowMnemonic, dwOpt->palette,
2346 dwOpt->state & State_Enabled, titleText,
2347 QPalette::WindowText);
2348 }
2349
2350 p->setPen(oldPen);
2351 } else {
2352 themeNumber = QWindowsXPStylePrivate::WindowTheme;
2353 if (isActive)
2354 stateId = CS_ACTIVE;
2355 else
2356 stateId = CS_INACTIVE;
2357
2358 int titleHeight = rect.height() - 2;
2359 rect = rect.adjusted(-fw, -fw, fw, 0);
2360
2361 XPThemeData theme(widget, p, themeNumber, 0, stateId);
2362 if (!theme.isValid())
2363 break;
2364
2365 // Draw small type title bar
2366 theme.rect = rect;
2367 theme.partId = WP_SMALLCAPTION;
2368 d->drawBackground(theme);
2369
2370 // Figure out maximal button space on title bar
2371
2372 QIcon ico = widget->windowIcon();
2373 bool hasIcon = (ico.cacheKey() != QApplication::windowIcon().cacheKey());
2374 if (hasIcon) {
2375 QPixmap pxIco = ico.pixmap(titleHeight);
2376 if (!verticalTitleBar && dwOpt->direction == Qt::RightToLeft)
2377 p->drawPixmap(rect.width() - titleHeight - pxIco.width(), rect.bottom() - titleHeight - 2, pxIco);
2378 else
2379 p->drawPixmap(fw, rect.bottom() - titleHeight - 2, pxIco);
2380 }
2381 if (!dwOpt->title.isEmpty()) {
2382 QPen oldPen = p->pen();
2383 QFont oldFont = p->font();
2384 QFont titleFont = oldFont;
2385 titleFont.setBold(true);
2386 p->setFont(titleFont);
2387 QString titleText
2388 = p->fontMetrics().elidedText(dwOpt->title, Qt::ElideRight, titleRect.width());
2389
2390 int result = TST_NONE;
2391 GetThemeEnumValue(theme.handle(), WP_SMALLCAPTION, isActive ? CS_ACTIVE : CS_INACTIVE, TMT_TEXTSHADOWTYPE, &result);
2392 if (result != TST_NONE) {
2393 COLORREF textShadowRef;
2394 GetThemeColor(theme.handle(), WP_SMALLCAPTION, isActive ? CS_ACTIVE : CS_INACTIVE, TMT_TEXTSHADOWCOLOR, &textShadowRef);
2395 QColor textShadow = qRgb(GetRValue(textShadowRef), GetGValue(textShadowRef), GetBValue(textShadowRef));
2396 p->setPen(textShadow);
2397 drawItemText(p, titleRect.adjusted(1, 1, 1, 1),
2398 Qt::AlignLeft | Qt::AlignBottom, dwOpt->palette,
2399 dwOpt->state & State_Enabled, titleText);
2400 }
2401
2402 COLORREF captionText = GetSysColor(isActive ? COLOR_CAPTIONTEXT : COLOR_INACTIVECAPTIONTEXT);
2403 QColor textColor = qRgb(GetRValue(captionText), GetGValue(captionText), GetBValue(captionText));
2404 p->setPen(textColor);
2405 drawItemText(p, titleRect,
2406 Qt::AlignLeft | Qt::AlignBottom, dwOpt->palette,
2407 dwOpt->state & State_Enabled, titleText);
2408 p->setFont(oldFont);
2409 p->setPen(oldPen);
2410 }
2411
2412 }
2413
2414 return;
2415 }
2416 break;
2417 #endif // QT_CONFIG(dockwidget)
2418 #if QT_CONFIG(rubberband)
2419 case CE_RubberBand:
2420 if (qstyleoption_cast<const QStyleOptionRubberBand *>(option)) {
2421 QColor highlight = option->palette.color(QPalette::Active, QPalette::Highlight);
2422 p->save();
2423 p->setPen(highlight.darker(120));
2424 QColor dimHighlight(qMin(highlight.red()/2 + 110, 255),
2425 qMin(highlight.green()/2 + 110, 255),
2426 qMin(highlight.blue()/2 + 110, 255),
2427 (widget && widget->isTopLevel())? 255 : 127);
2428 p->setBrush(dimHighlight);
2429 p->drawRect(option->rect.adjusted(0, 0, -1, -1));
2430 p->restore();
2431 return;
2432 }
2433 break;
2434 #endif // QT_CONFIG(rubberband)
2435 case CE_HeaderEmptyArea:
2436 if (option->state & State_Horizontal)
2437 {
2438 themeNumber = QWindowsXPStylePrivate::HeaderTheme;
2439 stateId = HIS_NORMAL;
2440 }
2441 else {
2442 QWindowsStyle::drawControl(CE_HeaderEmptyArea, option, p, widget);
2443 return;
2444 }
2445 break;
2446 default:
2447 break;
2448 }
2449
2450 XPThemeData theme(widget, p, themeNumber, partId, stateId, rect);
2451 if (!theme.isValid()) {
2452 QWindowsStyle::drawControl(element, option, p, widget);
2453 return;
2454 }
2455
2456 theme.rotate = rotate;
2457 theme.mirrorHorizontally = hMirrored;
2458 theme.mirrorVertically = vMirrored;
2459 d->drawBackground(theme);
2460 }
2461
scrollBarGripperBounds(QStyle::State flags,const QWidget * widget,XPThemeData * theme)2462 QRect QWindowsXPStylePrivate::scrollBarGripperBounds(QStyle::State flags, const QWidget *widget, XPThemeData *theme)
2463 {
2464 const bool horizontal = flags & QStyle::State_Horizontal;
2465 const qreal factor = QWindowsStylePrivate::nativeMetricScaleFactor(widget);
2466 const QMargins contentsMargin =
2467 (theme->margins(theme->rect, TMT_SIZINGMARGINS) * factor).toMargins();
2468 theme->partId = horizontal ? SBP_GRIPPERHORZ : SBP_GRIPPERVERT;
2469 const QSize size = (theme->size() * factor).toSize();
2470
2471 const int hSpace = theme->rect.width() - size.width();
2472 const int vSpace = theme->rect.height() - size.height();
2473 const bool sufficientSpace = (horizontal && hSpace > (contentsMargin.left() + contentsMargin.right()))
2474 || vSpace > contentsMargin.top() + contentsMargin.bottom();
2475 return sufficientSpace ? QRect(theme->rect.topLeft() + QPoint(hSpace, vSpace) / 2, size) : QRect();
2476 }
2477
2478 #if QT_CONFIG(mdiarea)
2479 // Helper for drawing MDI buttons into the corner widget of QMenuBar in case a
2480 // QMdiSubWindow is maximized.
populateMdiButtonTheme(const QStyle * proxy,const QWidget * widget,const QStyleOptionComplex * option,QStyle::SubControl subControl,int part,XPThemeData * theme)2481 static void populateMdiButtonTheme(const QStyle *proxy, const QWidget *widget,
2482 const QStyleOptionComplex *option,
2483 QStyle::SubControl subControl, int part,
2484 XPThemeData *theme)
2485 {
2486 theme->partId = part;
2487 theme->rect = proxy->subControlRect(QStyle::CC_MdiControls, option, subControl, widget);
2488 if (!option->state.testFlag(QStyle::State_Enabled))
2489 theme->stateId = CBS_INACTIVE;
2490 else if (option->state.testFlag(QStyle::State_Sunken) && option->activeSubControls.testFlag(subControl))
2491 theme->stateId = CBS_PUSHED;
2492 else if (option->state.testFlag(QStyle::State_MouseOver) && option->activeSubControls.testFlag(subControl))
2493 theme->stateId = CBS_HOT;
2494 else
2495 theme->stateId = CBS_NORMAL;
2496 }
2497
2498 // Calculate an small (max 2), empirical correction factor for scaling up
2499 // WP_MDICLOSEBUTTON, WP_MDIRESTOREBUTTON, WP_MDIMINBUTTON, which are too
2500 // small on High DPI screens (QTBUG-75927).
mdiButtonCorrectionFactor(XPThemeData & theme,const QPaintDevice * pd=nullptr)2501 qreal mdiButtonCorrectionFactor(XPThemeData &theme, const QPaintDevice *pd = nullptr)
2502 {
2503 const auto dpr = pd ? pd->devicePixelRatioF() : qApp->devicePixelRatio();
2504 const QSizeF nativeSize = QSizeF(theme.size()) / dpr;
2505 const QSizeF requestedSize(theme.rect.size());
2506 const auto rawFactor = qMin(requestedSize.width() / nativeSize.width(),
2507 requestedSize.height() / nativeSize.height());
2508 const auto factor = rawFactor >= qreal(2) ? qreal(2) : qreal(1);
2509 return factor;
2510 }
2511 #endif // QT_CONFIG(mdiarea)
2512
populateTitleBarButtonTheme(const QStyle * proxy,const QWidget * widget,const QStyleOptionComplex * option,QStyle::SubControl subControl,bool isTitleBarActive,int part,XPThemeData * theme)2513 static void populateTitleBarButtonTheme(const QStyle *proxy, const QWidget *widget,
2514 const QStyleOptionComplex *option,
2515 QStyle::SubControl subControl,
2516 bool isTitleBarActive, int part,
2517 XPThemeData *theme)
2518 {
2519 theme->rect = proxy->subControlRect(QStyle::CC_TitleBar, option, subControl, widget);
2520 theme->partId = part;
2521 if (widget && !widget->isEnabled())
2522 theme->stateId = RBS_DISABLED;
2523 else if (option->activeSubControls == subControl && option->state.testFlag(QStyle::State_Sunken))
2524 theme->stateId = RBS_PUSHED;
2525 else if (option->activeSubControls == subControl && option->state.testFlag(QStyle::State_MouseOver))
2526 theme->stateId = RBS_HOT;
2527 else if (!isTitleBarActive)
2528 theme->stateId = RBS_INACTIVE;
2529 else
2530 theme->stateId = RBS_NORMAL;
2531 }
2532
2533 /*!
2534 \reimp
2535 */
drawComplexControl(ComplexControl cc,const QStyleOptionComplex * option,QPainter * p,const QWidget * widget) const2536 void QWindowsXPStyle::drawComplexControl(ComplexControl cc, const QStyleOptionComplex *option,
2537 QPainter *p, const QWidget *widget) const
2538 {
2539 QWindowsXPStylePrivate *d = const_cast<QWindowsXPStylePrivate*>(d_func());
2540
2541 if (!QWindowsXPStylePrivate::useXP()) {
2542 QWindowsStyle::drawComplexControl(cc, option, p, widget);
2543 return;
2544 }
2545
2546 State flags = option->state;
2547 SubControls sub = option->subControls;
2548 QRect r = option->rect;
2549
2550 int partId = 0;
2551 int stateId = 0;
2552 if (widget && widget->testAttribute(Qt::WA_UnderMouse) && widget->isActiveWindow())
2553 flags |= State_MouseOver;
2554
2555 switch (cc) {
2556 #if QT_CONFIG(spinbox)
2557 case CC_SpinBox:
2558 if (const QStyleOptionSpinBox *sb = qstyleoption_cast<const QStyleOptionSpinBox *>(option))
2559 {
2560 XPThemeData theme(widget, p, QWindowsXPStylePrivate::SpinTheme);
2561
2562 if (sb->frame && (sub & SC_SpinBoxFrame)) {
2563 partId = EP_EDITTEXT;
2564 if (!(flags & State_Enabled))
2565 stateId = ETS_DISABLED;
2566 else if (flags & State_HasFocus)
2567 stateId = ETS_FOCUSED;
2568 else
2569 stateId = ETS_NORMAL;
2570
2571 XPThemeData ftheme(widget, p, QWindowsXPStylePrivate::EditTheme,
2572 partId, stateId, r);
2573 ftheme.noContent = true;
2574 d->drawBackground(ftheme);
2575 }
2576 if (sub & SC_SpinBoxUp) {
2577 theme.rect = proxy()->subControlRect(CC_SpinBox, option, SC_SpinBoxUp, widget);
2578 partId = SPNP_UP;
2579 if (!(sb->stepEnabled & QAbstractSpinBox::StepUpEnabled) || !(flags & State_Enabled))
2580 stateId = UPS_DISABLED;
2581 else if (sb->activeSubControls == SC_SpinBoxUp && (sb->state & State_Sunken))
2582 stateId = UPS_PRESSED;
2583 else if (sb->activeSubControls == SC_SpinBoxUp && (sb->state & State_MouseOver))
2584 stateId = UPS_HOT;
2585 else
2586 stateId = UPS_NORMAL;
2587 theme.partId = partId;
2588 theme.stateId = stateId;
2589 d->drawBackground(theme);
2590 }
2591 if (sub & SC_SpinBoxDown) {
2592 theme.rect = proxy()->subControlRect(CC_SpinBox, option, SC_SpinBoxDown, widget);
2593 partId = SPNP_DOWN;
2594 if (!(sb->stepEnabled & QAbstractSpinBox::StepDownEnabled) || !(flags & State_Enabled))
2595 stateId = DNS_DISABLED;
2596 else if (sb->activeSubControls == SC_SpinBoxDown && (sb->state & State_Sunken))
2597 stateId = DNS_PRESSED;
2598 else if (sb->activeSubControls == SC_SpinBoxDown && (sb->state & State_MouseOver))
2599 stateId = DNS_HOT;
2600 else
2601 stateId = DNS_NORMAL;
2602 theme.partId = partId;
2603 theme.stateId = stateId;
2604 d->drawBackground(theme);
2605 }
2606 }
2607 break;
2608 #endif // QT_CONFIG(spinbox)
2609 #if QT_CONFIG(combobox)
2610 case CC_ComboBox:
2611 if (const QStyleOptionComboBox *cmb = qstyleoption_cast<const QStyleOptionComboBox *>(option))
2612 {
2613 if (sub & SC_ComboBoxEditField) {
2614 if (cmb->frame) {
2615 partId = EP_EDITTEXT;
2616 if (!(flags & State_Enabled))
2617 stateId = ETS_DISABLED;
2618 else if (flags & State_HasFocus)
2619 stateId = ETS_FOCUSED;
2620 else
2621 stateId = ETS_NORMAL;
2622 XPThemeData theme(widget, p, QWindowsXPStylePrivate::EditTheme, partId, stateId, r);
2623 d->drawBackground(theme);
2624 } else {
2625 QBrush editBrush = cmb->palette.brush(QPalette::Base);
2626 p->fillRect(option->rect, editBrush);
2627 }
2628 if (!cmb->editable) {
2629 QRect re = proxy()->subControlRect(CC_ComboBox, option, SC_ComboBoxEditField, widget);
2630 if (option->state & State_HasFocus) {
2631 p->fillRect(re, option->palette.highlight());
2632 p->setPen(option->palette.highlightedText().color());
2633 p->setBackground(option->palette.highlight());
2634 } else {
2635 p->fillRect(re, option->palette.base());
2636 p->setPen(option->palette.text().color());
2637 p->setBackground(option->palette.base());
2638 }
2639 }
2640 }
2641
2642 if (sub & SC_ComboBoxArrow) {
2643 XPThemeData theme(widget, p, QWindowsXPStylePrivate::ComboboxTheme);
2644 theme.rect = proxy()->subControlRect(CC_ComboBox, option, SC_ComboBoxArrow, widget);
2645 partId = CP_DROPDOWNBUTTON;
2646 if (!(flags & State_Enabled))
2647 stateId = CBXS_DISABLED;
2648 else if (cmb->activeSubControls == SC_ComboBoxArrow && (cmb->state & State_Sunken))
2649 stateId = CBXS_PRESSED;
2650 else if (cmb->activeSubControls == SC_ComboBoxArrow && (cmb->state & State_MouseOver))
2651 stateId = CBXS_HOT;
2652 else
2653 stateId = CBXS_NORMAL;
2654 theme.partId = partId;
2655 theme.stateId = stateId;
2656 d->drawBackground(theme);
2657 }
2658 }
2659 break;
2660 #endif // QT_CONFIG(combobox)
2661 case CC_ScrollBar:
2662 if (const QStyleOptionSlider *scrollbar = qstyleoption_cast<const QStyleOptionSlider *>(option))
2663 {
2664 XPThemeData theme(widget, p, QWindowsXPStylePrivate::ScrollBarTheme);
2665 bool maxedOut = (scrollbar->maximum == scrollbar->minimum);
2666 if (maxedOut)
2667 flags &= ~State_Enabled;
2668
2669 bool isHorz = flags & State_Horizontal;
2670 bool isRTL = option->direction == Qt::RightToLeft;
2671 if (sub & SC_ScrollBarAddLine) {
2672 theme.rect = proxy()->subControlRect(CC_ScrollBar, option, SC_ScrollBarAddLine, widget);
2673 partId = SBP_ARROWBTN;
2674 if (!(flags & State_Enabled))
2675 stateId = (isHorz ? (isRTL ? ABS_LEFTDISABLED : ABS_RIGHTDISABLED) : ABS_DOWNDISABLED);
2676 else if (scrollbar->activeSubControls & SC_ScrollBarAddLine && (scrollbar->state & State_Sunken))
2677 stateId = (isHorz ? (isRTL ? ABS_LEFTPRESSED : ABS_RIGHTPRESSED) : ABS_DOWNPRESSED);
2678 else if (scrollbar->activeSubControls & SC_ScrollBarAddLine && (scrollbar->state & State_MouseOver))
2679 stateId = (isHorz ? (isRTL ? ABS_LEFTHOT : ABS_RIGHTHOT) : ABS_DOWNHOT);
2680 else
2681 stateId = (isHorz ? (isRTL ? ABS_LEFTNORMAL : ABS_RIGHTNORMAL) : ABS_DOWNNORMAL);
2682 theme.partId = partId;
2683 theme.stateId = stateId;
2684 d->drawBackground(theme);
2685 }
2686 if (sub & SC_ScrollBarSubLine) {
2687 theme.rect = proxy()->subControlRect(CC_ScrollBar, option, SC_ScrollBarSubLine, widget);
2688 partId = SBP_ARROWBTN;
2689 if (!(flags & State_Enabled))
2690 stateId = (isHorz ? (isRTL ? ABS_RIGHTDISABLED : ABS_LEFTDISABLED) : ABS_UPDISABLED);
2691 else if (scrollbar->activeSubControls & SC_ScrollBarSubLine && (scrollbar->state & State_Sunken))
2692 stateId = (isHorz ? (isRTL ? ABS_RIGHTPRESSED : ABS_LEFTPRESSED) : ABS_UPPRESSED);
2693 else if (scrollbar->activeSubControls & SC_ScrollBarSubLine && (scrollbar->state & State_MouseOver))
2694 stateId = (isHorz ? (isRTL ? ABS_RIGHTHOT : ABS_LEFTHOT) : ABS_UPHOT);
2695 else
2696 stateId = (isHorz ? (isRTL ? ABS_RIGHTNORMAL : ABS_LEFTNORMAL) : ABS_UPNORMAL);
2697 theme.partId = partId;
2698 theme.stateId = stateId;
2699 d->drawBackground(theme);
2700 }
2701 if (maxedOut) {
2702 theme.rect = proxy()->subControlRect(CC_ScrollBar, option, SC_ScrollBarSlider, widget);
2703 theme.rect = theme.rect.united(proxy()->subControlRect(CC_ScrollBar, option, SC_ScrollBarSubPage, widget));
2704 theme.rect = theme.rect.united(proxy()->subControlRect(CC_ScrollBar, option, SC_ScrollBarAddPage, widget));
2705 partId = scrollbar->orientation == Qt::Horizontal ? SBP_LOWERTRACKHORZ : SBP_LOWERTRACKVERT;
2706 stateId = SCRBS_DISABLED;
2707 theme.partId = partId;
2708 theme.stateId = stateId;
2709 d->drawBackground(theme);
2710 } else {
2711 if (sub & SC_ScrollBarSubPage) {
2712 theme.rect = proxy()->subControlRect(CC_ScrollBar, option, SC_ScrollBarSubPage, widget);
2713 partId = flags & State_Horizontal ? SBP_UPPERTRACKHORZ : SBP_UPPERTRACKVERT;
2714 if (!(flags & State_Enabled))
2715 stateId = SCRBS_DISABLED;
2716 else if (scrollbar->activeSubControls & SC_ScrollBarSubPage && (scrollbar->state & State_Sunken))
2717 stateId = SCRBS_PRESSED;
2718 else if (scrollbar->activeSubControls & SC_ScrollBarSubPage && (scrollbar->state & State_MouseOver))
2719 stateId = SCRBS_HOT;
2720 else
2721 stateId = SCRBS_NORMAL;
2722 theme.partId = partId;
2723 theme.stateId = stateId;
2724 d->drawBackground(theme);
2725 }
2726 if (sub & SC_ScrollBarAddPage) {
2727 theme.rect = proxy()->subControlRect(CC_ScrollBar, option, SC_ScrollBarAddPage, widget);
2728 partId = flags & State_Horizontal ? SBP_LOWERTRACKHORZ : SBP_LOWERTRACKVERT;
2729 if (!(flags & State_Enabled))
2730 stateId = SCRBS_DISABLED;
2731 else if (scrollbar->activeSubControls & SC_ScrollBarAddPage && (scrollbar->state & State_Sunken))
2732 stateId = SCRBS_PRESSED;
2733 else if (scrollbar->activeSubControls & SC_ScrollBarAddPage && (scrollbar->state & State_MouseOver))
2734 stateId = SCRBS_HOT;
2735 else
2736 stateId = SCRBS_NORMAL;
2737 theme.partId = partId;
2738 theme.stateId = stateId;
2739 d->drawBackground(theme);
2740 }
2741 if (sub & SC_ScrollBarSlider) {
2742 theme.rect = proxy()->subControlRect(CC_ScrollBar, option, SC_ScrollBarSlider, widget);
2743 if (!(flags & State_Enabled))
2744 stateId = SCRBS_DISABLED;
2745 else if (scrollbar->activeSubControls & SC_ScrollBarSlider && (scrollbar->state & State_Sunken))
2746 stateId = SCRBS_PRESSED;
2747 else if (scrollbar->activeSubControls & SC_ScrollBarSlider && (scrollbar->state & State_MouseOver))
2748 stateId = SCRBS_HOT;
2749 else
2750 stateId = SCRBS_NORMAL;
2751
2752 // Draw handle
2753 theme.partId = flags & State_Horizontal ? SBP_THUMBBTNHORZ : SBP_THUMBBTNVERT;
2754 theme.stateId = stateId;
2755 d->drawBackground(theme);
2756
2757 const QRect gripperBounds = QWindowsXPStylePrivate::scrollBarGripperBounds(flags, widget, &theme);
2758 // Draw gripper if there is enough space
2759 if (!gripperBounds.isEmpty()) {
2760 p->save();
2761 theme.rect = gripperBounds;
2762 p->setClipRegion(d->region(theme));// Only change inside the region of the gripper
2763 d->drawBackground(theme); // Transparent gripper ontop of background
2764 p->restore();
2765 }
2766 }
2767 }
2768 }
2769 break;
2770
2771 #if QT_CONFIG(slider)
2772 case CC_Slider:
2773 if (const QStyleOptionSlider *slider = qstyleoption_cast<const QStyleOptionSlider *>(option))
2774 {
2775 XPThemeData theme(widget, p, QWindowsXPStylePrivate::TrackBarTheme);
2776 QRect slrect = slider->rect;
2777 QRegion tickreg = slrect;
2778 if (sub & SC_SliderGroove) {
2779 theme.rect = proxy()->subControlRect(CC_Slider, option, SC_SliderGroove, widget);
2780 if (slider->orientation == Qt::Horizontal) {
2781 partId = TKP_TRACK;
2782 stateId = TRS_NORMAL;
2783 theme.rect = QRect(slrect.left(), theme.rect.center().y() - 2, slrect.width(), 4);
2784 } else {
2785 partId = TKP_TRACKVERT;
2786 stateId = TRVS_NORMAL;
2787 theme.rect = QRect(theme.rect.center().x() - 2, slrect.top(), 4, slrect.height());
2788 }
2789 theme.partId = partId;
2790 theme.stateId = stateId;
2791 d->drawBackground(theme);
2792 tickreg -= theme.rect;
2793 }
2794 if (sub & SC_SliderTickmarks) {
2795 int tickOffset = proxy()->pixelMetric(PM_SliderTickmarkOffset, slider, widget);
2796 int ticks = slider->tickPosition;
2797 int thickness = proxy()->pixelMetric(PM_SliderControlThickness, slider, widget);
2798 int len = proxy()->pixelMetric(PM_SliderLength, slider, widget);
2799 int available = proxy()->pixelMetric(PM_SliderSpaceAvailable, slider, widget);
2800 int interval = slider->tickInterval;
2801 if (interval <= 0) {
2802 interval = slider->singleStep;
2803 if (QStyle::sliderPositionFromValue(slider->minimum, slider->maximum, interval,
2804 available)
2805 - QStyle::sliderPositionFromValue(slider->minimum, slider->maximum,
2806 0, available) < 3)
2807 interval = slider->pageStep;
2808 }
2809 if (!interval)
2810 interval = 1;
2811 int fudge = len / 2;
2812 int pos;
2813 int bothOffset = (ticks & QSlider::TicksAbove && ticks & QSlider::TicksBelow) ? 1 : 0;
2814 p->setPen(d->sliderTickColor);
2815 QVarLengthArray<QLine, 32> lines;
2816 int v = slider->minimum;
2817 while (v <= slider->maximum + 1) {
2818 if (v == slider->maximum + 1 && interval == 1)
2819 break;
2820 const int v_ = qMin(v, slider->maximum);
2821 int tickLength = (v_ == slider->minimum || v_ >= slider->maximum) ? 4 : 3;
2822 pos = QStyle::sliderPositionFromValue(slider->minimum, slider->maximum,
2823 v_, available) + fudge;
2824 if (slider->orientation == Qt::Horizontal) {
2825 if (ticks & QSlider::TicksAbove)
2826 lines.append(QLine(pos, tickOffset - 1 - bothOffset,
2827 pos, tickOffset - 1 - bothOffset - tickLength));
2828
2829 if (ticks & QSlider::TicksBelow)
2830 lines.append(QLine(pos, tickOffset + thickness + bothOffset,
2831 pos, tickOffset + thickness + bothOffset + tickLength));
2832 } else {
2833 if (ticks & QSlider::TicksAbove)
2834 lines.append(QLine(tickOffset - 1 - bothOffset, pos,
2835 tickOffset - 1 - bothOffset - tickLength, pos));
2836
2837 if (ticks & QSlider::TicksBelow)
2838 lines.append(QLine(tickOffset + thickness + bothOffset, pos,
2839 tickOffset + thickness + bothOffset + tickLength, pos));
2840 }
2841 // in the case where maximum is max int
2842 int nextInterval = v + interval;
2843 if (nextInterval < v)
2844 break;
2845 v = nextInterval;
2846 }
2847 if (!lines.isEmpty()) {
2848 p->save();
2849 p->translate(slrect.topLeft());
2850 p->drawLines(lines.constData(), lines.size());
2851 p->restore();
2852 }
2853 }
2854 if (sub & SC_SliderHandle) {
2855 theme.rect = proxy()->subControlRect(CC_Slider, option, SC_SliderHandle, widget);
2856 if (slider->orientation == Qt::Horizontal) {
2857 if (slider->tickPosition == QSlider::TicksAbove)
2858 partId = TKP_THUMBTOP;
2859 else if (slider->tickPosition == QSlider::TicksBelow)
2860 partId = TKP_THUMBBOTTOM;
2861 else
2862 partId = TKP_THUMB;
2863
2864 if (!(slider->state & State_Enabled))
2865 stateId = TUS_DISABLED;
2866 else if (slider->activeSubControls & SC_SliderHandle && (slider->state & State_Sunken))
2867 stateId = TUS_PRESSED;
2868 else if (slider->activeSubControls & SC_SliderHandle && (slider->state & State_MouseOver))
2869 stateId = TUS_HOT;
2870 else if (flags & State_HasFocus)
2871 stateId = TUS_FOCUSED;
2872 else
2873 stateId = TUS_NORMAL;
2874 } else {
2875 if (slider->tickPosition == QSlider::TicksLeft)
2876 partId = TKP_THUMBLEFT;
2877 else if (slider->tickPosition == QSlider::TicksRight)
2878 partId = TKP_THUMBRIGHT;
2879 else
2880 partId = TKP_THUMBVERT;
2881
2882 if (!(slider->state & State_Enabled))
2883 stateId = TUVS_DISABLED;
2884 else if (slider->activeSubControls & SC_SliderHandle && (slider->state & State_Sunken))
2885 stateId = TUVS_PRESSED;
2886 else if (slider->activeSubControls & SC_SliderHandle && (slider->state & State_MouseOver))
2887 stateId = TUVS_HOT;
2888 else if (flags & State_HasFocus)
2889 stateId = TUVS_FOCUSED;
2890 else
2891 stateId = TUVS_NORMAL;
2892 }
2893 theme.partId = partId;
2894 theme.stateId = stateId;
2895 d->drawBackground(theme);
2896 }
2897 if (slider->state & State_HasFocus) {
2898 QStyleOptionFocusRect fropt;
2899 fropt.QStyleOption::operator=(*slider);
2900 fropt.rect = subElementRect(SE_SliderFocusRect, slider, widget);
2901 proxy()->drawPrimitive(PE_FrameFocusRect, &fropt, p, widget);
2902 }
2903 }
2904 break;
2905 #endif
2906 #if QT_CONFIG(toolbutton)
2907 case CC_ToolButton:
2908 if (const QStyleOptionToolButton *toolbutton
2909 = qstyleoption_cast<const QStyleOptionToolButton *>(option)) {
2910 QRect button, menuarea;
2911 button = proxy()->subControlRect(cc, toolbutton, SC_ToolButton, widget);
2912 menuarea = proxy()->subControlRect(cc, toolbutton, SC_ToolButtonMenu, widget);
2913
2914 State bflags = toolbutton->state & ~State_Sunken;
2915 State mflags = bflags;
2916 bool autoRaise = flags & State_AutoRaise;
2917 if (autoRaise) {
2918 if (!(bflags & State_MouseOver) || !(bflags & State_Enabled)) {
2919 bflags &= ~State_Raised;
2920 }
2921 }
2922
2923 if (toolbutton->state & State_Sunken) {
2924 if (toolbutton->activeSubControls & SC_ToolButton) {
2925 bflags |= State_Sunken;
2926 mflags |= State_MouseOver | State_Sunken;
2927 } else if (toolbutton->activeSubControls & SC_ToolButtonMenu) {
2928 mflags |= State_Sunken;
2929 bflags |= State_MouseOver;
2930 }
2931 }
2932
2933 QStyleOption tool = *toolbutton;
2934 if (toolbutton->subControls & SC_ToolButton) {
2935 if (flags & (State_Sunken | State_On | State_Raised) || !autoRaise) {
2936 if (toolbutton->features & QStyleOptionToolButton::MenuButtonPopup && autoRaise) {
2937 XPThemeData theme(widget, p, QWindowsXPStylePrivate::ToolBarTheme);
2938 theme.partId = TP_SPLITBUTTON;
2939 theme.rect = button;
2940 if (!(bflags & State_Enabled))
2941 stateId = TS_DISABLED;
2942 else if (bflags & State_Sunken)
2943 stateId = TS_PRESSED;
2944 else if (bflags & State_MouseOver || !(flags & State_AutoRaise))
2945 stateId = flags & State_On ? TS_HOTCHECKED : TS_HOT;
2946 else if (bflags & State_On)
2947 stateId = TS_CHECKED;
2948 else
2949 stateId = TS_NORMAL;
2950 if (option->direction == Qt::RightToLeft)
2951 theme.mirrorHorizontally = true;
2952 theme.stateId = stateId;
2953 d->drawBackground(theme);
2954 } else {
2955 tool.rect = option->rect;
2956 tool.state = bflags;
2957 if (autoRaise) // for tool bars
2958 proxy()->drawPrimitive(PE_PanelButtonTool, &tool, p, widget);
2959 else
2960 proxy()->drawPrimitive(PE_PanelButtonBevel, &tool, p, widget);
2961 }
2962 }
2963 }
2964
2965 if (toolbutton->state & State_HasFocus) {
2966 QStyleOptionFocusRect fr;
2967 fr.QStyleOption::operator=(*toolbutton);
2968 fr.rect.adjust(3, 3, -3, -3);
2969 if (toolbutton->features & QStyleOptionToolButton::MenuButtonPopup)
2970 fr.rect.adjust(0, 0, -proxy()->pixelMetric(QStyle::PM_MenuButtonIndicator,
2971 toolbutton, widget), 0);
2972 proxy()->drawPrimitive(PE_FrameFocusRect, &fr, p, widget);
2973 }
2974 QStyleOptionToolButton label = *toolbutton;
2975 label.state = bflags;
2976 int fw = 2;
2977 if (!autoRaise)
2978 label.state &= ~State_Sunken;
2979 label.rect = button.adjusted(fw, fw, -fw, -fw);
2980 proxy()->drawControl(CE_ToolButtonLabel, &label, p, widget);
2981
2982 if (toolbutton->subControls & SC_ToolButtonMenu) {
2983 tool.rect = menuarea;
2984 tool.state = mflags;
2985 if (autoRaise) {
2986 proxy()->drawPrimitive(PE_IndicatorButtonDropDown, &tool, p, widget);
2987 } else {
2988 tool.state = mflags;
2989 menuarea.adjust(-2, 0, 0, 0);
2990 // Draw menu button
2991 if ((bflags & State_Sunken) != (mflags & State_Sunken)){
2992 p->save();
2993 p->setClipRect(menuarea);
2994 tool.rect = option->rect;
2995 proxy()->drawPrimitive(PE_PanelButtonBevel, &tool, p, nullptr);
2996 p->restore();
2997 }
2998 // Draw arrow
2999 p->save();
3000 p->setPen(option->palette.dark().color());
3001 p->drawLine(menuarea.left(), menuarea.top() + 3,
3002 menuarea.left(), menuarea.bottom() - 3);
3003 p->setPen(option->palette.light().color());
3004 p->drawLine(menuarea.left() - 1, menuarea.top() + 3,
3005 menuarea.left() - 1, menuarea.bottom() - 3);
3006
3007 tool.rect = menuarea.adjusted(2, 3, -2, -1);
3008 proxy()->drawPrimitive(PE_IndicatorArrowDown, &tool, p, widget);
3009 p->restore();
3010 }
3011 } else if (toolbutton->features & QStyleOptionToolButton::HasMenu) {
3012 int mbi = proxy()->pixelMetric(PM_MenuButtonIndicator, toolbutton, widget);
3013 QRect ir = toolbutton->rect;
3014 QStyleOptionToolButton newBtn = *toolbutton;
3015 newBtn.rect = QRect(ir.right() + 4 - mbi, ir.height() - mbi + 4, mbi - 5, mbi - 5);
3016 proxy()->drawPrimitive(PE_IndicatorArrowDown, &newBtn, p, widget);
3017 }
3018 }
3019 break;
3020 #endif // QT_CONFIG(toolbutton)
3021
3022 case CC_TitleBar:
3023 {
3024 if (const QStyleOptionTitleBar *tb = qstyleoption_cast<const QStyleOptionTitleBar *>(option))
3025 {
3026 const qreal factor = QWindowsStylePrivate::nativeMetricScaleFactor(widget);
3027 bool isActive = tb->titleBarState & QStyle::State_Active;
3028 XPThemeData theme(widget, p, QWindowsXPStylePrivate::WindowTheme);
3029 if (sub & SC_TitleBarLabel) {
3030
3031 partId = (tb->titleBarState & Qt::WindowMinimized) ? WP_MINCAPTION : WP_CAPTION;
3032 theme.rect = option->rect;
3033 if (widget && !widget->isEnabled())
3034 stateId = CS_DISABLED;
3035 else if (isActive)
3036 stateId = CS_ACTIVE;
3037 else
3038 stateId = CS_INACTIVE;
3039
3040 theme.partId = partId;
3041 theme.stateId = stateId;
3042 d->drawBackground(theme);
3043
3044 QRect ir = proxy()->subControlRect(CC_TitleBar, tb, SC_TitleBarLabel, widget);
3045
3046 int result = TST_NONE;
3047 GetThemeEnumValue(theme.handle(), WP_CAPTION, isActive ? CS_ACTIVE : CS_INACTIVE, TMT_TEXTSHADOWTYPE, &result);
3048 if (result != TST_NONE) {
3049 COLORREF textShadowRef;
3050 GetThemeColor(theme.handle(), WP_CAPTION, isActive ? CS_ACTIVE : CS_INACTIVE, TMT_TEXTSHADOWCOLOR, &textShadowRef);
3051 QColor textShadow = qRgb(GetRValue(textShadowRef), GetGValue(textShadowRef), GetBValue(textShadowRef));
3052 p->setPen(textShadow);
3053 p->drawText(int(ir.x() + 3 * factor), int(ir.y() + 2 * factor),
3054 int(ir.width() - 1 * factor), ir.height(),
3055 Qt::AlignLeft | Qt::AlignVCenter | Qt::TextSingleLine, tb->text);
3056 }
3057 COLORREF captionText = GetSysColor(isActive ? COLOR_CAPTIONTEXT : COLOR_INACTIVECAPTIONTEXT);
3058 QColor textColor = qRgb(GetRValue(captionText), GetGValue(captionText), GetBValue(captionText));
3059 p->setPen(textColor);
3060 p->drawText(int(ir.x() + 2 * factor), int(ir.y() + 1 * factor),
3061 int(ir.width() - 2 * factor), ir.height(),
3062 Qt::AlignLeft | Qt::AlignVCenter | Qt::TextSingleLine, tb->text);
3063 }
3064 if (sub & SC_TitleBarSysMenu && tb->titleBarFlags & Qt::WindowSystemMenuHint) {
3065 theme.rect = proxy()->subControlRect(CC_TitleBar, option, SC_TitleBarSysMenu, widget);
3066 partId = WP_SYSBUTTON;
3067 if ((widget && !widget->isEnabled()) || !isActive)
3068 stateId = SBS_DISABLED;
3069 else if (option->activeSubControls == SC_TitleBarSysMenu && (option->state & State_Sunken))
3070 stateId = SBS_PUSHED;
3071 else if (option->activeSubControls == SC_TitleBarSysMenu && (option->state & State_MouseOver))
3072 stateId = SBS_HOT;
3073 else
3074 stateId = SBS_NORMAL;
3075 if (!tb->icon.isNull()) {
3076 tb->icon.paint(p, theme.rect);
3077 } else {
3078 theme.partId = partId;
3079 theme.stateId = stateId;
3080 if (theme.size().isEmpty()) {
3081 int iconSize = proxy()->pixelMetric(PM_SmallIconSize, tb, widget);
3082 QPixmap pm = proxy()->standardIcon(SP_TitleBarMenuButton, tb, widget).pixmap(iconSize, iconSize);
3083 p->save();
3084 drawItemPixmap(p, theme.rect, Qt::AlignCenter, pm);
3085 p->restore();
3086 } else {
3087 d->drawBackground(theme);
3088 }
3089 }
3090 }
3091
3092 if (sub & SC_TitleBarMinButton && tb->titleBarFlags & Qt::WindowMinimizeButtonHint
3093 && !(tb->titleBarState & Qt::WindowMinimized)) {
3094 populateTitleBarButtonTheme(proxy(), widget, option, SC_TitleBarMinButton, isActive, WP_MINBUTTON, &theme);
3095 d->drawBackground(theme);
3096 }
3097 if (sub & SC_TitleBarMaxButton && tb->titleBarFlags & Qt::WindowMaximizeButtonHint
3098 && !(tb->titleBarState & Qt::WindowMaximized)) {
3099 populateTitleBarButtonTheme(proxy(), widget, option, SC_TitleBarMaxButton, isActive, WP_MAXBUTTON, &theme);
3100 d->drawBackground(theme);
3101 }
3102 if (sub & SC_TitleBarContextHelpButton
3103 && tb->titleBarFlags & Qt::WindowContextHelpButtonHint) {
3104 populateTitleBarButtonTheme(proxy(), widget, option, SC_TitleBarContextHelpButton, isActive, WP_HELPBUTTON, &theme);
3105 d->drawBackground(theme);
3106 }
3107 bool drawNormalButton = (sub & SC_TitleBarNormalButton)
3108 && (((tb->titleBarFlags & Qt::WindowMinimizeButtonHint)
3109 && (tb->titleBarState & Qt::WindowMinimized))
3110 || ((tb->titleBarFlags & Qt::WindowMaximizeButtonHint)
3111 && (tb->titleBarState & Qt::WindowMaximized)));
3112 if (drawNormalButton) {
3113 populateTitleBarButtonTheme(proxy(), widget, option, SC_TitleBarNormalButton, isActive, WP_RESTOREBUTTON, &theme);
3114 d->drawBackground(theme);
3115 }
3116 if (sub & SC_TitleBarShadeButton && tb->titleBarFlags & Qt::WindowShadeButtonHint
3117 && !(tb->titleBarState & Qt::WindowMinimized)) {
3118 populateTitleBarButtonTheme(proxy(), widget, option, SC_TitleBarShadeButton, isActive, WP_MINBUTTON, &theme);
3119 d->drawBackground(theme);
3120 }
3121 if (sub & SC_TitleBarUnshadeButton && tb->titleBarFlags & Qt::WindowShadeButtonHint
3122 && tb->titleBarState & Qt::WindowMinimized) {
3123 populateTitleBarButtonTheme(proxy(), widget, option, SC_TitleBarUnshadeButton, isActive, WP_RESTOREBUTTON, &theme);
3124 d->drawBackground(theme);
3125 }
3126 if (sub & SC_TitleBarCloseButton && tb->titleBarFlags & Qt::WindowSystemMenuHint) {
3127 populateTitleBarButtonTheme(proxy(), widget, option, SC_TitleBarCloseButton, isActive, WP_CLOSEBUTTON, &theme);
3128 d->drawBackground(theme);
3129 }
3130 }
3131 }
3132 break;
3133
3134 #if QT_CONFIG(mdiarea)
3135 case CC_MdiControls:
3136 {
3137 XPThemeData theme(widget, p, QWindowsXPStylePrivate::WindowTheme, WP_MDICLOSEBUTTON, CBS_NORMAL);
3138 if (Q_UNLIKELY(!theme.isValid()))
3139 return;
3140
3141 if (option->subControls.testFlag(SC_MdiCloseButton)) {
3142 populateMdiButtonTheme(proxy(), widget, option, SC_MdiCloseButton, WP_MDICLOSEBUTTON, &theme);
3143 d->drawBackground(theme, mdiButtonCorrectionFactor(theme, widget));
3144 }
3145 if (option->subControls.testFlag(SC_MdiNormalButton)) {
3146 populateMdiButtonTheme(proxy(), widget, option, SC_MdiNormalButton, WP_MDIRESTOREBUTTON, &theme);
3147 d->drawBackground(theme, mdiButtonCorrectionFactor(theme, widget));
3148 }
3149 if (option->subControls.testFlag(QStyle::SC_MdiMinButton)) {
3150 populateMdiButtonTheme(proxy(), widget, option, SC_MdiMinButton, WP_MDIMINBUTTON, &theme);
3151 d->drawBackground(theme, mdiButtonCorrectionFactor(theme, widget));
3152 }
3153 }
3154 break;
3155 #endif // QT_CONFIG(mdiarea)
3156 #if QT_CONFIG(dial)
3157 case CC_Dial:
3158 if (const QStyleOptionSlider *dial = qstyleoption_cast<const QStyleOptionSlider *>(option))
3159 QStyleHelper::drawDial(dial, p);
3160 break;
3161 #endif // QT_CONFIG(dial)
3162 default:
3163 QWindowsStyle::drawComplexControl(cc, option, p, widget);
3164 break;
3165 }
3166 }
3167
progressBarOrientation(const QStyleOption * option=nullptr)3168 static inline Qt::Orientation progressBarOrientation(const QStyleOption *option = nullptr)
3169 {
3170 if (const QStyleOptionProgressBar *pb = qstyleoption_cast<const QStyleOptionProgressBar *>(option))
3171 return pb->orientation;
3172 return Qt::Horizontal;
3173 }
3174
pixelMetricFromSystemDp(QStyle::PixelMetric pm,const QStyleOption * option,const QWidget * widget)3175 int QWindowsXPStylePrivate::pixelMetricFromSystemDp(QStyle::PixelMetric pm, const QStyleOption *option, const QWidget *widget)
3176 {
3177 switch (pm) {
3178 case QStyle::PM_IndicatorWidth:
3179 return XPThemeData::themeSize(widget, nullptr, QWindowsXPStylePrivate::ButtonTheme, BP_CHECKBOX, CBS_UNCHECKEDNORMAL).width();
3180 case QStyle::PM_IndicatorHeight:
3181 return XPThemeData::themeSize(widget, nullptr, QWindowsXPStylePrivate::ButtonTheme, BP_CHECKBOX, CBS_UNCHECKEDNORMAL).height();
3182 case QStyle::PM_ExclusiveIndicatorWidth:
3183 return XPThemeData::themeSize(widget, nullptr, QWindowsXPStylePrivate::ButtonTheme, BP_RADIOBUTTON, RBS_UNCHECKEDNORMAL).width();
3184 case QStyle::PM_ExclusiveIndicatorHeight:
3185 return XPThemeData::themeSize(widget, nullptr, QWindowsXPStylePrivate::ButtonTheme, BP_RADIOBUTTON, RBS_UNCHECKEDNORMAL).height();
3186 case QStyle::PM_ProgressBarChunkWidth:
3187 return progressBarOrientation(option) == Qt::Horizontal
3188 ? XPThemeData::themeSize(widget, nullptr, QWindowsXPStylePrivate::ProgressTheme, PP_CHUNK).width()
3189 : XPThemeData::themeSize(widget, nullptr, QWindowsXPStylePrivate::ProgressTheme, PP_CHUNKVERT).height();
3190 case QStyle::PM_SliderThickness:
3191 return XPThemeData::themeSize(widget, nullptr, QWindowsXPStylePrivate::TrackBarTheme, TKP_THUMB).height();
3192 case QStyle::PM_TitleBarHeight:
3193 return widget && (widget->windowType() == Qt::Tool)
3194 ? GetSystemMetrics(SM_CYSMCAPTION) + GetSystemMetrics(SM_CXSIZEFRAME)
3195 : GetSystemMetrics(SM_CYCAPTION) + GetSystemMetrics(SM_CXSIZEFRAME);
3196 case QStyle::PM_MdiSubWindowFrameWidth:
3197 return XPThemeData::themeSize(widget, nullptr, QWindowsXPStylePrivate::WindowTheme, WP_FRAMELEFT, FS_ACTIVE).width();
3198 case QStyle::PM_DockWidgetFrameWidth:
3199 return XPThemeData::themeSize(widget, nullptr, QWindowsXPStylePrivate::WindowTheme, WP_SMALLFRAMERIGHT, FS_ACTIVE).width();
3200 default:
3201 break;
3202 }
3203 return QWindowsXPStylePrivate::InvalidMetric;
3204 }
3205
3206 /*! \reimp */
pixelMetric(PixelMetric pm,const QStyleOption * option,const QWidget * widget) const3207 int QWindowsXPStyle::pixelMetric(PixelMetric pm, const QStyleOption *option, const QWidget *widget) const
3208 {
3209 if (!QWindowsXPStylePrivate::useXP())
3210 return QWindowsStyle::pixelMetric(pm, option, widget);
3211
3212 int res = QWindowsXPStylePrivate::pixelMetricFromSystemDp(pm, option, widget);
3213 if (res != QWindowsStylePrivate::InvalidMetric)
3214 return qRound(qreal(res) * QWindowsStylePrivate::nativeMetricScaleFactor(widget));
3215
3216 res = 0;
3217 switch (pm) {
3218 case PM_MenuBarPanelWidth:
3219 case PM_ButtonDefaultIndicator:
3220 res = 0;
3221 break;
3222
3223 case PM_DefaultFrameWidth:
3224 res = qobject_cast<const QListView*>(widget) ? 2 : 1;
3225 break;
3226 case PM_MenuPanelWidth:
3227 case PM_SpinBoxFrameWidth:
3228 res = 1;
3229 break;
3230
3231 case PM_TabBarTabOverlap:
3232 case PM_MenuHMargin:
3233 case PM_MenuVMargin:
3234 res = 2;
3235 break;
3236
3237 case PM_TabBarBaseOverlap:
3238 if (const QStyleOptionTab *tab = qstyleoption_cast<const QStyleOptionTab *>(option)) {
3239 switch (tab->shape) {
3240 case QTabBar::RoundedNorth:
3241 case QTabBar::TriangularNorth:
3242 case QTabBar::RoundedWest:
3243 case QTabBar::TriangularWest:
3244 res = 1;
3245 break;
3246 case QTabBar::RoundedSouth:
3247 case QTabBar::TriangularSouth:
3248 res = 2;
3249 break;
3250 case QTabBar::RoundedEast:
3251 case QTabBar::TriangularEast:
3252 res = 3;
3253 break;
3254 }
3255 }
3256 break;
3257
3258 case PM_SplitterWidth:
3259 res = qMax(int(QStyleHelper::dpiScaled(5., option)),
3260 QApplication::globalStrut().width());
3261 break;
3262
3263 case PM_MdiSubWindowMinimizedWidth:
3264 res = 160;
3265 break;
3266
3267 #if QT_CONFIG(toolbar)
3268 case PM_ToolBarHandleExtent:
3269 res = int(QStyleHelper::dpiScaled(8., option));
3270 break;
3271
3272 #endif // QT_CONFIG(toolbar)
3273 case PM_DockWidgetSeparatorExtent:
3274 case PM_DockWidgetTitleMargin:
3275 res = int(QStyleHelper::dpiScaled(4., option));
3276 break;
3277
3278 case PM_ButtonShiftHorizontal:
3279 case PM_ButtonShiftVertical:
3280 res = qstyleoption_cast<const QStyleOptionToolButton *>(option) ? 1 : 0;
3281 break;
3282
3283 default:
3284 res = QWindowsStyle::pixelMetric(pm, option, widget);
3285 }
3286
3287 return res;
3288 }
3289
3290 /*
3291 This function is used by subControlRect to check if a button
3292 should be drawn for the given subControl given a set of window flags.
3293 */
buttonVisible(const QStyle::SubControl sc,const QStyleOptionTitleBar * tb)3294 static bool buttonVisible(const QStyle::SubControl sc, const QStyleOptionTitleBar *tb){
3295
3296 bool isMinimized = tb->titleBarState & Qt::WindowMinimized;
3297 bool isMaximized = tb->titleBarState & Qt::WindowMaximized;
3298 const uint flags = tb->titleBarFlags;
3299 bool retVal = false;
3300 switch (sc) {
3301 case QStyle::SC_TitleBarContextHelpButton:
3302 if (flags & Qt::WindowContextHelpButtonHint)
3303 retVal = true;
3304 break;
3305 case QStyle::SC_TitleBarMinButton:
3306 if (!isMinimized && (flags & Qt::WindowMinimizeButtonHint))
3307 retVal = true;
3308 break;
3309 case QStyle::SC_TitleBarNormalButton:
3310 if (isMinimized && (flags & Qt::WindowMinimizeButtonHint))
3311 retVal = true;
3312 else if (isMaximized && (flags & Qt::WindowMaximizeButtonHint))
3313 retVal = true;
3314 break;
3315 case QStyle::SC_TitleBarMaxButton:
3316 if (!isMaximized && (flags & Qt::WindowMaximizeButtonHint))
3317 retVal = true;
3318 break;
3319 case QStyle::SC_TitleBarShadeButton:
3320 if (!isMinimized && flags & Qt::WindowShadeButtonHint)
3321 retVal = true;
3322 break;
3323 case QStyle::SC_TitleBarUnshadeButton:
3324 if (isMinimized && flags & Qt::WindowShadeButtonHint)
3325 retVal = true;
3326 break;
3327 case QStyle::SC_TitleBarCloseButton:
3328 if (flags & Qt::WindowSystemMenuHint)
3329 retVal = true;
3330 break;
3331 case QStyle::SC_TitleBarSysMenu:
3332 if (flags & Qt::WindowSystemMenuHint)
3333 retVal = true;
3334 break;
3335 default :
3336 retVal = true;
3337 }
3338 return retVal;
3339 }
3340
3341 /*!
3342 \reimp
3343 */
subControlRect(ComplexControl cc,const QStyleOptionComplex * option,SubControl subControl,const QWidget * widget) const3344 QRect QWindowsXPStyle::subControlRect(ComplexControl cc, const QStyleOptionComplex *option,
3345 SubControl subControl, const QWidget *widget) const
3346 {
3347 if (!QWindowsXPStylePrivate::useXP())
3348 return QWindowsStyle::subControlRect(cc, option, subControl, widget);
3349
3350 QRect rect;
3351
3352 switch (cc) {
3353 case CC_TitleBar:
3354 if (const QStyleOptionTitleBar *tb = qstyleoption_cast<const QStyleOptionTitleBar *>(option)) {
3355 if (!buttonVisible(subControl, tb))
3356 return rect;
3357 const bool isToolTitle = false;
3358 const int height = tb->rect.height();
3359 const int width = tb->rect.width();
3360 const int buttonMargin = int(QStyleHelper::dpiScaled(4, option));
3361 const qreal factor = QWindowsStylePrivate::nativeMetricScaleFactor(widget);
3362 int buttonHeight = qRound(qreal(GetSystemMetrics(SM_CYSIZE)) * factor)
3363 - buttonMargin;
3364 int buttonWidth = qRound(qreal(GetSystemMetrics(SM_CXSIZE)) * factor)
3365 - buttonMargin;
3366 const int delta = buttonWidth + 2;
3367 int controlTop = option->rect.bottom() - buttonHeight - 2;
3368 const int frameWidth = proxy()->pixelMetric(PM_MdiSubWindowFrameWidth, option, widget);
3369 const bool sysmenuHint = (tb->titleBarFlags & Qt::WindowSystemMenuHint) != 0;
3370 const bool minimizeHint = (tb->titleBarFlags & Qt::WindowMinimizeButtonHint) != 0;
3371 const bool maximizeHint = (tb->titleBarFlags & Qt::WindowMaximizeButtonHint) != 0;
3372 const bool contextHint = (tb->titleBarFlags & Qt::WindowContextHelpButtonHint) != 0;
3373 const bool shadeHint = (tb->titleBarFlags & Qt::WindowShadeButtonHint) != 0;
3374 bool isMinimized = tb->titleBarState & Qt::WindowMinimized;
3375 bool isMaximized = tb->titleBarState & Qt::WindowMaximized;
3376 int offset = 0;
3377
3378 switch (subControl) {
3379 case SC_TitleBarLabel:
3380 rect = QRect(frameWidth, 0, width - (buttonWidth + frameWidth + 10), height);
3381 if (isToolTitle) {
3382 if (sysmenuHint) {
3383 rect.adjust(0, 0, -buttonWidth - 3, 0);
3384 }
3385 if (minimizeHint || maximizeHint)
3386 rect.adjust(0, 0, -buttonWidth - 2, 0);
3387 } else {
3388 if (sysmenuHint) {
3389 const int leftOffset = height - 8;
3390 rect.adjust(leftOffset, 0, 0, 0);
3391 }
3392 if (minimizeHint)
3393 rect.adjust(0, 0, -buttonWidth - 2, 0);
3394 if (maximizeHint)
3395 rect.adjust(0, 0, -buttonWidth - 2, 0);
3396 if (contextHint)
3397 rect.adjust(0, 0, -buttonWidth - 2, 0);
3398 if (shadeHint)
3399 rect.adjust(0, 0, -buttonWidth - 2, 0);
3400 }
3401 break;
3402
3403 case SC_TitleBarContextHelpButton:
3404 if (tb->titleBarFlags & Qt::WindowContextHelpButtonHint)
3405 offset += delta;
3406 Q_FALLTHROUGH();
3407 case SC_TitleBarMinButton:
3408 if (!isMinimized && (tb->titleBarFlags & Qt::WindowMinimizeButtonHint))
3409 offset += delta;
3410 else if (subControl == SC_TitleBarMinButton)
3411 break;
3412 Q_FALLTHROUGH();
3413 case SC_TitleBarNormalButton:
3414 if (isMinimized && (tb->titleBarFlags & Qt::WindowMinimizeButtonHint))
3415 offset += delta;
3416 else if (isMaximized && (tb->titleBarFlags & Qt::WindowMaximizeButtonHint))
3417 offset += delta;
3418 else if (subControl == SC_TitleBarNormalButton)
3419 break;
3420 Q_FALLTHROUGH();
3421 case SC_TitleBarMaxButton:
3422 if (!isMaximized && (tb->titleBarFlags & Qt::WindowMaximizeButtonHint))
3423 offset += delta;
3424 else if (subControl == SC_TitleBarMaxButton)
3425 break;
3426 Q_FALLTHROUGH();
3427 case SC_TitleBarShadeButton:
3428 if (!isMinimized && (tb->titleBarFlags & Qt::WindowShadeButtonHint))
3429 offset += delta;
3430 else if (subControl == SC_TitleBarShadeButton)
3431 break;
3432 Q_FALLTHROUGH();
3433 case SC_TitleBarUnshadeButton:
3434 if (isMinimized && (tb->titleBarFlags & Qt::WindowShadeButtonHint))
3435 offset += delta;
3436 else if (subControl == SC_TitleBarUnshadeButton)
3437 break;
3438 Q_FALLTHROUGH();
3439 case SC_TitleBarCloseButton:
3440 if (tb->titleBarFlags & Qt::WindowSystemMenuHint)
3441 offset += delta;
3442 else if (subControl == SC_TitleBarCloseButton)
3443 break;
3444
3445 rect.setRect(width - offset - controlTop + 1, controlTop,
3446 buttonWidth, buttonHeight);
3447 break;
3448
3449 case SC_TitleBarSysMenu:
3450 {
3451 const int controlTop = 6;
3452 const int controlHeight = height - controlTop - 3;
3453 const int iconExtent = proxy()->pixelMetric(PM_SmallIconSize, option);
3454 QSize iconSize = tb->icon.actualSize(QSize(iconExtent, iconExtent));
3455 if (tb->icon.isNull())
3456 iconSize = QSize(controlHeight, controlHeight);
3457 int hPad = (controlHeight - iconSize.height())/2;
3458 int vPad = (controlHeight - iconSize.width())/2;
3459 rect = QRect(frameWidth + hPad, controlTop + vPad, iconSize.width(), iconSize.height());
3460 }
3461 break;
3462 default:
3463 break;
3464 }
3465 }
3466 break;
3467
3468 case CC_ComboBox:
3469 if (const QStyleOptionComboBox *cmb = qstyleoption_cast<const QStyleOptionComboBox *>(option)) {
3470 const int x = cmb->rect.x(), y = cmb->rect.y(), wi = cmb->rect.width(), he = cmb->rect.height();
3471 const int xpos = x + wi - qRound(QStyleHelper::dpiScaled(1 + 16, option));
3472
3473 switch (subControl) {
3474 case SC_ComboBoxFrame:
3475 rect = cmb->rect;
3476 break;
3477
3478 case SC_ComboBoxArrow: {
3479 const qreal dpi = QStyleHelper::dpi(option);
3480 rect = QRect(xpos, y + qRound(QStyleHelper::dpiScaled(1, dpi)),
3481 qRound(QStyleHelper::dpiScaled(16, dpi)),
3482 he - qRound(QStyleHelper::dpiScaled(2, dpi)));
3483 }
3484 break;
3485
3486 case SC_ComboBoxEditField: {
3487 const qreal dpi = QStyleHelper::dpi(option);
3488 const int frame = qRound(QStyleHelper::dpiScaled(2, dpi));
3489 rect = QRect(x + frame, y + frame,
3490 wi - qRound(QStyleHelper::dpiScaled(3 + 16, dpi)),
3491 he - qRound(QStyleHelper::dpiScaled(4, dpi)));
3492 }
3493 break;
3494
3495 case SC_ComboBoxListBoxPopup:
3496 rect = cmb->rect;
3497 break;
3498
3499 default:
3500 break;
3501 }
3502 }
3503 break;
3504 #if QT_CONFIG(mdiarea)
3505 case CC_MdiControls:
3506 {
3507 int numSubControls = 0;
3508 if (option->subControls & SC_MdiCloseButton)
3509 ++numSubControls;
3510 if (option->subControls & SC_MdiMinButton)
3511 ++numSubControls;
3512 if (option->subControls & SC_MdiNormalButton)
3513 ++numSubControls;
3514 if (numSubControls == 0)
3515 break;
3516
3517 int buttonWidth = option->rect.width() / numSubControls;
3518 int offset = 0;
3519 switch (subControl) {
3520 case SC_MdiCloseButton:
3521 // Only one sub control, no offset needed.
3522 if (numSubControls == 1)
3523 break;
3524 offset += buttonWidth;
3525 Q_FALLTHROUGH();
3526 case SC_MdiNormalButton:
3527 // No offset needed if
3528 // 1) There's only one sub control
3529 // 2) We have a close button and a normal button (offset already added in SC_MdiClose)
3530 if (numSubControls == 1 || (numSubControls == 2 && !(option->subControls & SC_MdiMinButton)))
3531 break;
3532 if (option->subControls & SC_MdiNormalButton)
3533 offset += buttonWidth;
3534 break;
3535 default:
3536 break;
3537 }
3538 rect = QRect(offset, 0, buttonWidth, option->rect.height());
3539 break;
3540 }
3541 #endif // QT_CONFIG(mdiarea)
3542
3543 default:
3544 rect = visualRect(option->direction, option->rect,
3545 QWindowsStyle::subControlRect(cc, option, subControl, widget));
3546 break;
3547 }
3548 return visualRect(option->direction, option->rect, rect);
3549 }
3550
3551 /*!
3552 \reimp
3553 */
sizeFromContents(ContentsType ct,const QStyleOption * option,const QSize & contentsSize,const QWidget * widget) const3554 QSize QWindowsXPStyle::sizeFromContents(ContentsType ct, const QStyleOption *option,
3555 const QSize &contentsSize, const QWidget *widget) const
3556 {
3557 if (!QWindowsXPStylePrivate::useXP())
3558 return QWindowsStyle::sizeFromContents(ct, option, contentsSize, widget);
3559
3560 QSize sz(contentsSize);
3561 switch (ct) {
3562 case CT_LineEdit:
3563 case CT_ComboBox:
3564 {
3565 XPThemeData buttontheme(widget, nullptr, QWindowsXPStylePrivate::ButtonTheme, BP_PUSHBUTTON, PBS_NORMAL);
3566 if (buttontheme.isValid()) {
3567 const qreal factor = QWindowsXPStylePrivate::nativeMetricScaleFactor(widget);
3568 const QMarginsF borderSize = buttontheme.margins() * factor;
3569 if (!borderSize.isNull()) {
3570 const qreal margin = qreal(2) * factor;
3571 sz.rwidth() += qRound(borderSize.left() + borderSize.right() - margin);
3572 sz.rheight() += int(borderSize.bottom() + borderSize.top() - margin
3573 + qreal(1) / factor - 1);
3574 }
3575 const int textMargins = 2*(proxy()->pixelMetric(PM_FocusFrameHMargin, option) + 1);
3576 sz += QSize(qMax(pixelMetric(QStyle::PM_ScrollBarExtent, option, widget)
3577 + textMargins, 23), 0); //arrow button
3578 }
3579 }
3580 break;
3581 case CT_SpinBox:
3582 {
3583 //Spinbox adds frame twice
3584 sz = QWindowsStyle::sizeFromContents(ct, option, contentsSize, widget);
3585 int border = proxy()->pixelMetric(PM_SpinBoxFrameWidth, option, widget);
3586 sz -= QSize(2*border, 2*border);
3587 }
3588 break;
3589 case CT_TabWidget:
3590 sz += QSize(6, 6);
3591 break;
3592 case CT_Menu:
3593 sz += QSize(1, 0);
3594 break;
3595 #if QT_CONFIG(menubar)
3596 case CT_MenuBarItem:
3597 if (!sz.isEmpty())
3598 sz += QSize(windowsItemHMargin * 5 + 1, 6);
3599 break;
3600 #endif
3601 case CT_MenuItem:
3602 if (const QStyleOptionMenuItem *menuitem = qstyleoption_cast<const QStyleOptionMenuItem *>(option))
3603 {
3604 if (menuitem->menuItemType != QStyleOptionMenuItem::Separator) {
3605 sz = QWindowsStyle::sizeFromContents(ct, option, sz, widget);
3606 sz.setHeight(sz.height() - 2);
3607 return sz;
3608 }
3609 }
3610 sz = QWindowsStyle::sizeFromContents(ct, option, sz, widget);
3611 break;
3612
3613 case CT_MdiControls: {
3614 sz.setHeight(int(QStyleHelper::dpiScaled(19, option)));
3615 int width = 54;
3616 if (const QStyleOptionComplex *styleOpt = qstyleoption_cast<const QStyleOptionComplex *>(option)) {
3617 width = 0;
3618 if (styleOpt->subControls & SC_MdiMinButton)
3619 width += 17 + 1;
3620 if (styleOpt->subControls & SC_MdiNormalButton)
3621 width += 17 + 1;
3622 if (styleOpt->subControls & SC_MdiCloseButton)
3623 width += 17 + 1;
3624 }
3625 sz.setWidth(int(QStyleHelper::dpiScaled(width, option)));
3626 }
3627 break;
3628
3629 default:
3630 sz = QWindowsStyle::sizeFromContents(ct, option, sz, widget);
3631 break;
3632 }
3633
3634 return sz;
3635 }
3636
3637
3638 /*! \reimp */
styleHint(StyleHint hint,const QStyleOption * option,const QWidget * widget,QStyleHintReturn * returnData) const3639 int QWindowsXPStyle::styleHint(StyleHint hint, const QStyleOption *option, const QWidget *widget,
3640 QStyleHintReturn *returnData) const
3641 {
3642 QWindowsXPStylePrivate *d = const_cast<QWindowsXPStylePrivate*>(d_func());
3643 if (!QWindowsXPStylePrivate::useXP())
3644 return QWindowsStyle::styleHint(hint, option, widget, returnData);
3645
3646 int res = 0;
3647 switch (hint) {
3648
3649 case SH_EtchDisabledText:
3650 res = (qobject_cast<const QLabel*>(widget) != 0);
3651 break;
3652
3653 case SH_SpinControls_DisableOnBounds:
3654 res = 0;
3655 break;
3656
3657 case SH_TitleBar_AutoRaise:
3658 case SH_TitleBar_NoBorder:
3659 res = 1;
3660 break;
3661
3662 case SH_GroupBox_TextLabelColor:
3663 if (!widget || (widget && widget->isEnabled()))
3664 res = d->groupBoxTextColor;
3665 else
3666 res = d->groupBoxTextColorDisabled;
3667 break;
3668
3669 case SH_Table_GridLineColor:
3670 res = 0xC0C0C0;
3671 break;
3672
3673 case SH_WindowFrame_Mask:
3674 {
3675 res = 1;
3676 QStyleHintReturnMask *mask = qstyleoption_cast<QStyleHintReturnMask *>(returnData);
3677 const QStyleOptionTitleBar *titlebar = qstyleoption_cast<const QStyleOptionTitleBar *>(option);
3678 if (mask && titlebar) {
3679 // Note certain themes will not return the whole window frame but only the titlebar part when
3680 // queried This function needs to return the entire window mask, hence we will only fetch the mask for the
3681 // titlebar itself and add the remaining part of the window rect at the bottom.
3682 int tbHeight = proxy()->pixelMetric(PM_TitleBarHeight, option, widget);
3683 QRect titleBarRect = option->rect;
3684 titleBarRect.setHeight(tbHeight);
3685 XPThemeData themeData;
3686 if (titlebar->titleBarState & Qt::WindowMinimized) {
3687 themeData = XPThemeData(widget, nullptr,
3688 QWindowsXPStylePrivate::WindowTheme,
3689 WP_MINCAPTION, CS_ACTIVE, titleBarRect);
3690 } else
3691 themeData = XPThemeData(widget, nullptr,
3692 QWindowsXPStylePrivate::WindowTheme,
3693 WP_CAPTION, CS_ACTIVE, titleBarRect);
3694 mask->region = d->region(themeData) +
3695 QRect(0, tbHeight, option->rect.width(), option->rect.height() - tbHeight);
3696 }
3697 }
3698 break;
3699 #if QT_CONFIG(rubberband)
3700 case SH_RubberBand_Mask:
3701 if (qstyleoption_cast<const QStyleOptionRubberBand *>(option))
3702 res = 0;
3703 break;
3704 #endif // QT_CONFIG(rubberband)
3705
3706 case SH_ItemView_DrawDelegateFrame:
3707 res = 1;
3708 break;
3709
3710 default:
3711 res =QWindowsStyle::styleHint(hint, option, widget, returnData);
3712 }
3713
3714 return res;
3715 }
3716
3717 /*! \reimp */
standardPalette() const3718 QPalette QWindowsXPStyle::standardPalette() const
3719 {
3720 return QWindowsXPStylePrivate::useXP() ? QPalette() : QWindowsStyle::standardPalette();
3721 }
3722
3723 /*!
3724 \reimp
3725 */
standardPixmap(StandardPixmap standardPixmap,const QStyleOption * option,const QWidget * widget) const3726 QPixmap QWindowsXPStyle::standardPixmap(StandardPixmap standardPixmap, const QStyleOption *option,
3727 const QWidget *widget) const
3728 {
3729 if (!QWindowsXPStylePrivate::useXP())
3730 return QWindowsStyle::standardPixmap(standardPixmap, option, widget);
3731
3732 switch(standardPixmap) {
3733 case SP_TitleBarMaxButton:
3734 case SP_TitleBarCloseButton:
3735 if (qstyleoption_cast<const QStyleOptionDockWidget *>(option))
3736 {
3737 if (widget && widget->isWindow()) {
3738 XPThemeData theme(widget, nullptr, QWindowsXPStylePrivate::WindowTheme, WP_SMALLCLOSEBUTTON, CBS_NORMAL);
3739 if (theme.isValid()) {
3740 const QSize size = (theme.size() * QWindowsStylePrivate::nativeMetricScaleFactor(widget)).toSize();
3741 return QIcon(QWindowsStyle::standardPixmap(standardPixmap, option, widget)).pixmap(size);
3742 }
3743 }
3744 }
3745 break;
3746 default:
3747 break;
3748 }
3749 return QWindowsStyle::standardPixmap(standardPixmap, option, widget);
3750 }
3751
3752 /*!
3753 \reimp
3754 */
standardIcon(StandardPixmap standardIcon,const QStyleOption * option,const QWidget * widget) const3755 QIcon QWindowsXPStyle::standardIcon(StandardPixmap standardIcon,
3756 const QStyleOption *option,
3757 const QWidget *widget) const
3758 {
3759 if (!QWindowsXPStylePrivate::useXP()) {
3760 return QWindowsStyle::standardIcon(standardIcon, option, widget);
3761 }
3762
3763 QWindowsXPStylePrivate *d = const_cast<QWindowsXPStylePrivate*>(d_func());
3764 switch(standardIcon) {
3765 case SP_TitleBarMaxButton:
3766 if (qstyleoption_cast<const QStyleOptionDockWidget *>(option))
3767 {
3768 if (d->dockFloat.isNull()) {
3769 XPThemeData themeSize(nullptr, nullptr, QWindowsXPStylePrivate::WindowTheme,
3770 WP_SMALLCLOSEBUTTON, CBS_NORMAL);
3771 XPThemeData theme(nullptr, nullptr, QWindowsXPStylePrivate::WindowTheme,
3772 WP_MAXBUTTON, MAXBS_NORMAL);
3773 if (theme.isValid()) {
3774 const QSize size = (themeSize.size() * QWindowsStylePrivate::nativeMetricScaleFactor(widget)).toSize();
3775 QPixmap pm(size);
3776 pm.fill(Qt::transparent);
3777 QPainter p(&pm);
3778 theme.painter = &p;
3779 theme.rect = QRect(QPoint(0, 0), size);
3780 d->drawBackground(theme);
3781 d->dockFloat.addPixmap(pm, QIcon::Normal, QIcon::Off); // Normal
3782 pm.fill(Qt::transparent);
3783 theme.stateId = MAXBS_PUSHED;
3784 d->drawBackground(theme);
3785 d->dockFloat.addPixmap(pm, QIcon::Normal, QIcon::On); // Pressed
3786 pm.fill(Qt::transparent);
3787 theme.stateId = MAXBS_HOT;
3788 d->drawBackground(theme);
3789 d->dockFloat.addPixmap(pm, QIcon::Active, QIcon::Off); // Hover
3790 pm.fill(Qt::transparent);
3791 theme.stateId = MAXBS_INACTIVE;
3792 d->drawBackground(theme);
3793 d->dockFloat.addPixmap(pm, QIcon::Disabled, QIcon::Off); // Disabled
3794 }
3795 }
3796 if (widget && widget->isWindow())
3797 return d->dockFloat;
3798
3799 }
3800 break;
3801 case SP_TitleBarCloseButton:
3802 if (qstyleoption_cast<const QStyleOptionDockWidget *>(option))
3803 {
3804 if (d->dockClose.isNull()) {
3805 XPThemeData theme(nullptr, nullptr, QWindowsXPStylePrivate::WindowTheme,
3806 WP_SMALLCLOSEBUTTON, CBS_NORMAL);
3807 if (theme.isValid()) {
3808 const QSize size = (theme.size() * QWindowsStylePrivate::nativeMetricScaleFactor(widget)).toSize();
3809 QPixmap pm(size);
3810 pm.fill(Qt::transparent);
3811 QPainter p(&pm);
3812 theme.painter = &p;
3813 theme.partId = WP_CLOSEBUTTON; // ####
3814 theme.rect = QRect(QPoint(0, 0), size);
3815 d->drawBackground(theme);
3816 d->dockClose.addPixmap(pm, QIcon::Normal, QIcon::Off); // Normal
3817 pm.fill(Qt::transparent);
3818 theme.stateId = CBS_PUSHED;
3819 d->drawBackground(theme);
3820 d->dockClose.addPixmap(pm, QIcon::Normal, QIcon::On); // Pressed
3821 pm.fill(Qt::transparent);
3822 theme.stateId = CBS_HOT;
3823 d->drawBackground(theme);
3824 d->dockClose.addPixmap(pm, QIcon::Active, QIcon::Off); // Hover
3825 pm.fill(Qt::transparent);
3826 theme.stateId = CBS_INACTIVE;
3827 d->drawBackground(theme);
3828 d->dockClose.addPixmap(pm, QIcon::Disabled, QIcon::Off); // Disabled
3829 }
3830 }
3831 if (widget && widget->isWindow())
3832 return d->dockClose;
3833 }
3834 break;
3835 case SP_TitleBarNormalButton:
3836 if (qstyleoption_cast<const QStyleOptionDockWidget *>(option))
3837 {
3838 if (d->dockFloat.isNull()) {
3839 XPThemeData themeSize(nullptr, nullptr, QWindowsXPStylePrivate::WindowTheme,
3840 WP_SMALLCLOSEBUTTON, CBS_NORMAL);
3841 XPThemeData theme(nullptr, nullptr, QWindowsXPStylePrivate::WindowTheme,
3842 WP_RESTOREBUTTON, RBS_NORMAL);
3843 if (theme.isValid()) {
3844 const QSize size = (themeSize.size() * QWindowsStylePrivate::nativeMetricScaleFactor(widget)).toSize();
3845 QPixmap pm(size);
3846 pm.fill(Qt::transparent);
3847 QPainter p(&pm);
3848 theme.painter = &p;
3849 theme.rect = QRect(QPoint(0, 0), size);
3850 d->drawBackground(theme);
3851 d->dockFloat.addPixmap(pm, QIcon::Normal, QIcon::Off); // Normal
3852 pm.fill(Qt::transparent);
3853 theme.stateId = RBS_PUSHED;
3854 d->drawBackground(theme);
3855 d->dockFloat.addPixmap(pm, QIcon::Normal, QIcon::On); // Pressed
3856 pm.fill(Qt::transparent);
3857 theme.stateId = RBS_HOT;
3858 d->drawBackground(theme);
3859 d->dockFloat.addPixmap(pm, QIcon::Active, QIcon::Off); // Hover
3860 pm.fill(Qt::transparent);
3861 theme.stateId = RBS_INACTIVE;
3862 d->drawBackground(theme);
3863 d->dockFloat.addPixmap(pm, QIcon::Disabled, QIcon::Off); // Disabled
3864 }
3865 }
3866 if (widget && widget->isWindow())
3867 return d->dockFloat;
3868
3869 }
3870 break;
3871 default:
3872 break;
3873 }
3874
3875 return QWindowsStyle::standardIcon(standardIcon, option, widget);
3876 }
3877
3878 /*!
3879 \internal
3880
3881 Constructs a QWindowsXPStyle object.
3882 */
QWindowsXPStyle(QWindowsXPStylePrivate & dd)3883 QWindowsXPStyle::QWindowsXPStyle(QWindowsXPStylePrivate &dd) : QWindowsStyle(dd)
3884 {
3885 }
3886
3887 #ifndef QT_NO_DEBUG_STREAM
operator <<(QDebug d,const XPThemeData & t)3888 QDebug operator<<(QDebug d, const XPThemeData &t)
3889 {
3890 QDebugStateSaver saver(d);
3891 d.nospace();
3892 d << "XPThemeData(" << t.widget << ", theme=#" << t.theme << ", " << t.htheme
3893 << ", partId=" << t.partId << ", stateId=" << t.stateId << ", rect=" << t.rect
3894 << ", mirrorHorizontally=" << t.mirrorHorizontally << ", mirrorVertically="
3895 << t.mirrorVertically << ", noBorder=" << t.noBorder << ", noContent=" << t.noContent
3896 << ", rotate=" << t.rotate << ')';
3897 return d;
3898 }
3899
operator <<(QDebug d,const ThemeMapKey & k)3900 QDebug operator<<(QDebug d, const ThemeMapKey &k)
3901 {
3902 QDebugStateSaver saver(d);
3903 d.nospace();
3904 d << "ThemeMapKey(theme=#" << k.theme
3905 << ", partId=" << k.partId << ", stateId=" << k.stateId
3906 << ", noBorder=" << k.noBorder << ", noContent=" << k.noContent << ')';
3907 return d;
3908 }
3909
operator <<(QDebug d,const ThemeMapData & td)3910 QDebug operator<<(QDebug d, const ThemeMapData &td)
3911 {
3912 QDebugStateSaver saver(d);
3913 d.nospace();
3914 d << "ThemeMapData(alphaType=" << td.alphaType
3915 << ", dataValid=" << td.dataValid << ", partIsTransparent=" << td.partIsTransparent
3916 << ", hasAlphaChannel=" << td.hasAlphaChannel << ", wasAlphaSwapped=" << td.wasAlphaSwapped
3917 << ", hadInvalidAlpha=" << td.hadInvalidAlpha << ')';
3918 return d;
3919 }
3920 #endif // QT_NO_DEBUG_STREAM
3921
3922 // Debugging code ---------------------------------------------------------------------[ START ]---
3923 // The code for this point on is not compiled by default, but only used as assisting
3924 // debugging code when you uncomment the DEBUG_XP_STYLE define at the top of the file.
3925
3926 #ifdef DEBUG_XP_STYLE
3927 // The schema file expects these to be defined by the user.
3928 #define TMT_ENUMDEF 8
3929 #define TMT_ENUMVAL TEXT('A')
3930 #define TMT_ENUM TEXT('B')
3931 #define SCHEMA_STRINGS // For 2nd pass on schema file
3932 QT_BEGIN_INCLUDE_NAMESPACE
3933 #include <tmschema.h>
3934 QT_END_INCLUDE_NAMESPACE
3935
3936 // A property's value, type and name combo
3937 struct PropPair {
3938 int propValue;
3939 int propType;
3940 LPCWSTR propName;
3941 };
3942
3943 // Operator for sorting of PropPairs
operator <(PropPair a,PropPair b)3944 bool operator<(PropPair a, PropPair b) {
3945 return wcscmp(a.propName, b.propName) < 0;
3946 }
3947
3948 // Our list of all possible properties
3949 static QList<PropPair> all_props;
3950
3951
3952 /*! \internal
3953 Dumps a portion of the full native DIB section double buffer.
3954 The DIB section double buffer is only used when doing special
3955 transformations to the theme part, or when the real double
3956 buffer in the paintengine does not have an HDC we may use
3957 directly.
3958 Since we cannot rely on the pixel data we get from Microsoft
3959 when drawing into the DIB section, we use this function to
3960 see the actual data we got, and can determin the appropriate
3961 action.
3962 */
dumpNativeDIB(int w,int h)3963 void QWindowsXPStylePrivate::dumpNativeDIB(int w, int h)
3964 {
3965 if (w && h) {
3966 static int pCount = 0;
3967 DWORD *bufPix = (DWORD*)bufferPixels;
3968
3969 char *bufferDump = new char[bufferH * bufferW * 16];
3970 char *bufferPos = bufferDump;
3971
3972 memset(bufferDump, 0, sizeof(bufferDump));
3973 bufferPos += sprintf(bufferPos, "const int pixelBufferW%d = %d;\n", pCount, w);
3974 bufferPos += sprintf(bufferPos, "const int pixelBufferH%d = %d;\n", pCount, h);
3975 bufferPos += sprintf(bufferPos, "const unsigned DWORD pixelBuffer%d[] = {", pCount);
3976 for (int iy = 0; iy < h; ++iy) {
3977 bufferPos += sprintf(bufferPos, "\n ");
3978 bufPix = (DWORD*)(bufferPixels + (iy * bufferW * 4));
3979 for (int ix = 0; ix < w; ++ix) {
3980 bufferPos += sprintf(bufferPos, "0x%08x, ", *bufPix);
3981 ++bufPix;
3982 }
3983 }
3984 bufferPos += sprintf(bufferPos, "\n};\n\n");
3985 printf(bufferDump);
3986
3987 delete[] bufferDump;
3988 ++pCount;
3989 }
3990 }
3991
3992 /*! \internal
3993 Shows the value of a given property for a part.
3994 */
showProperty(XPThemeData & themeData,const PropPair & prop)3995 static void showProperty(XPThemeData &themeData, const PropPair &prop)
3996 {
3997 PROPERTYORIGIN origin = PO_NOTFOUND;
3998 GetThemePropertyOrigin(themeData.handle(), themeData.partId, themeData.stateId, prop.propValue, &origin);
3999 const char *originStr;
4000 switch(origin) {
4001 case PO_STATE:
4002 originStr = "State ";
4003 break;
4004 case PO_PART:
4005 originStr = "Part ";
4006 break;
4007 case PO_CLASS:
4008 originStr = "Class ";
4009 break;
4010 case PO_GLOBAL:
4011 originStr = "Globl ";
4012 break;
4013 case PO_NOTFOUND:
4014 default:
4015 originStr = "Unkwn ";
4016 break;
4017 }
4018
4019 switch(prop.propType) {
4020 case TMT_STRING:
4021 {
4022 wchar_t buffer[512];
4023 GetThemeString(themeData.handle(), themeData.partId, themeData.stateId, prop.propValue, buffer, 512);
4024 printf(" (%sString) %-20S: %S\n", originStr, prop.propName, buffer);
4025 }
4026 break;
4027 case TMT_ENUM:
4028 {
4029 int result = -1;
4030 GetThemeEnumValue(themeData.handle(), themeData.partId, themeData.stateId, prop.propValue, &result);
4031 printf(" (%sEnum) %-20S: %d\n", originStr, prop.propName, result);
4032 }
4033 break;
4034 case TMT_INT:
4035 {
4036 int result = -1;
4037 GetThemeInt(themeData.handle(), themeData.partId, themeData.stateId, prop.propValue, &result);
4038 printf(" (%sint) %-20S: %d\n", originStr, prop.propName, result);
4039 }
4040 break;
4041 case TMT_BOOL:
4042 {
4043 BOOL result = false;
4044 GetThemeBool(themeData.handle(), themeData.partId, themeData.stateId, prop.propValue, &result);
4045 printf(" (%sbool) %-20S: %d\n", originStr, prop.propName, result);
4046 }
4047 break;
4048 case TMT_COLOR:
4049 {
4050 COLORREF result = 0;
4051 GetThemeColor(themeData.handle(), themeData.partId, themeData.stateId, prop.propValue, &result);
4052 printf(" (%scolor) %-20S: 0x%08X\n", originStr, prop.propName, result);
4053 }
4054 break;
4055 case TMT_MARGINS:
4056 {
4057 MARGINS result;
4058 memset(&result, 0, sizeof(result));
4059 GetThemeMargins(themeData.handle(), 0, themeData.partId, themeData.stateId, prop.propValue, 0, &result);
4060 printf(" (%smargins) %-20S: (%d, %d, %d, %d)\n", originStr,
4061 prop.propName, result.cxLeftWidth, result.cyTopHeight, result.cxRightWidth, result.cyBottomHeight);
4062 }
4063 break;
4064 case TMT_FILENAME:
4065 {
4066 wchar_t buffer[512];
4067 GetThemeFilename(themeData.handle(), themeData.partId, themeData.stateId, prop.propValue, buffer, 512);
4068 printf(" (%sfilename)%-20S: %S\n", originStr, prop.propName, buffer);
4069 }
4070 break;
4071 case TMT_SIZE:
4072 {
4073 SIZE result1;
4074 SIZE result2;
4075 SIZE result3;
4076 memset(&result1, 0, sizeof(result1));
4077 memset(&result2, 0, sizeof(result2));
4078 memset(&result3, 0, sizeof(result3));
4079 GetThemePartSize(themeData.handle(), 0, themeData.partId, themeData.stateId, 0, TS_MIN, &result1);
4080 GetThemePartSize(themeData.handle(), 0, themeData.partId, themeData.stateId, 0, TS_TRUE, &result2);
4081 GetThemePartSize(themeData.handle(), 0, themeData.partId, themeData.stateId, 0, TS_DRAW, &result3);
4082 printf(" (%ssize) %-20S: Min (%d, %d), True(%d, %d), Draw(%d, %d)\n", originStr, prop.propName,
4083 result1.cx, result1.cy, result2.cx, result2.cy, result3.cx, result3.cy);
4084 }
4085 break;
4086 case TMT_POSITION:
4087 {
4088 POINT result;
4089 memset(&result, 0, sizeof(result));
4090 GetThemePosition(themeData.handle(), themeData.partId, themeData.stateId, prop.propValue, &result);
4091 printf(" (%sPosition)%-20S: (%d, %d)\n", originStr, prop.propName, result.x, result.y);
4092 }
4093 break;
4094 case TMT_RECT:
4095 {
4096 RECT result;
4097 memset(&result, 0, sizeof(result));
4098 GetThemeRect(themeData.handle(), themeData.partId, themeData.stateId, prop.propValue, &result);
4099 printf(" (%sRect) %-20S: (%d, %d, %d, %d)\n", originStr, prop.propName, result.left, result.top, result.right, result.bottom);
4100 }
4101 break;
4102 case TMT_FONT:
4103 {
4104 LOGFONT result;
4105 memset(&result, 0, sizeof(result));
4106 GetThemeFont(themeData.handle(), 0, themeData.partId, themeData.stateId, prop.propValue, &result);
4107 printf(" (%sFont) %-20S: %S height(%d) width(%d) weight(%d)\n", originStr, prop.propName,
4108 result.lfFaceName, result.lfHeight, result.lfWidth, result.lfWeight);
4109 }
4110 break;
4111 case TMT_INTLIST:
4112 {
4113 INTLIST result;
4114 memset(&result, 0, sizeof(result));
4115 GetThemeIntList(themeData.handle(), themeData.partId, themeData.stateId, prop.propValue, &result);
4116 printf(" (%sInt list)%-20S: { ", originStr, prop.propName);
4117 for (int i = 0; i < result.iValueCount; ++i)
4118 printf("%d ", result.iValues[i]);
4119 printf("}\n");
4120 }
4121 break;
4122 default:
4123 printf(" %s%S : Unknown property type (%d)!\n", originStr, prop.propName, prop.propType);
4124 }
4125 }
4126
4127 /*! \internal
4128 Dump all valid properties for a part.
4129 If it's the first time this function is called, then the name,
4130 enum value and documentation of all properties are shown, as
4131 well as all global properties.
4132 */
showProperties(XPThemeData & themeData)4133 void QWindowsXPStylePrivate::showProperties(XPThemeData &themeData)
4134 {
4135 if (!all_props.count()) {
4136 const TMSCHEMAINFO *infoTable = GetSchemaInfo();
4137 for (int i = 0; i < infoTable->iPropCount; ++i) {
4138 int propType = infoTable->pPropTable[i].bPrimVal;
4139 int propValue = infoTable->pPropTable[i].sEnumVal;
4140 LPCWSTR propName = infoTable->pPropTable[i].pszName;
4141
4142 switch(propType) {
4143 case TMT_ENUMDEF:
4144 case TMT_ENUMVAL:
4145 continue;
4146 default:
4147 if (propType != propValue) {
4148 PropPair prop;
4149 prop.propValue = propValue;
4150 prop.propName = propName;
4151 prop.propType = propType;
4152 all_props.append(prop);
4153 }
4154 }
4155 }
4156 std::sort(all_props.begin(), all_props.end());
4157
4158 {// List all properties
4159 printf("part properties count = %d:\n", all_props.count());
4160 printf(" Enum Property Name Description\n");
4161 printf("-----------------------------------------------------------\n");
4162 wchar_t themeName[256];
4163 pGetCurrentThemeName(themeName, 256, 0, 0, 0, 0);
4164 for (int j = 0; j < all_props.count(); ++j) {
4165 PropPair prop = all_props.at(j);
4166 wchar_t buf[500];
4167 GetThemeDocumentationProperty(themeName, prop.propName, buf, 500);
4168 printf("%3d: (%4d) %-20S %S\n", j, prop.propValue, prop.propName, buf);
4169 }
4170 }
4171
4172 {// Show Global values
4173 printf("Global Properties:\n");
4174 for (int j = 0; j < all_props.count(); ++j) {
4175 PropPair prop = all_props.at(j);
4176 PROPERTYORIGIN origin = PO_NOTFOUND;
4177 GetThemePropertyOrigin(themeData.handle(), themeData.partId, themeData.stateId, prop.propValue, &origin);
4178 if (origin == PO_GLOBAL) {
4179 showProperty(themeData, prop);
4180 }
4181 }
4182 }
4183 }
4184
4185 for (int j = 0; j < all_props.count(); ++j) {
4186 PropPair prop = all_props.at(j);
4187 PROPERTYORIGIN origin = PO_NOTFOUND;
4188 GetThemePropertyOrigin(themeData.handle(), themeData.partId, themeData.stateId, prop.propValue, &origin);
4189 if (origin != PO_NOTFOUND)
4190 {
4191 showProperty(themeData, prop);
4192 }
4193 }
4194 }
4195 #endif
4196 // Debugging code -----------------------------------------------------------------------[ END ]---
4197
4198
4199 QT_END_NAMESPACE
4200