Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qwindowsvistastyle.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3// Qt-Security score:significant reason:default
4
8#include <qoperatingsystemversion.h>
9#include <qpainterstateguard.h>
10#include <qscreen.h>
11#include <qstylehints.h>
12#include <qwindow.h>
13#include <private/qstyleanimation_p.h>
14#include <private/qstylehelper_p.h>
15#include <qpa/qplatformnativeinterface.h>
16#include <private/qapplication_p.h>
17#include <private/qsystemlibrary_p.h>
18#include <private/qwindowsthemecache_p.h>
19
20#include "qdrawutil.h" // for now
21#include <qbackingstore.h>
22
23
24QT_BEGIN_NAMESPACE
25
26using namespace Qt::StringLiterals;
27
28static constexpr int windowsItemFrame = 2; // menu item frame width
29static constexpr int windowsItemHMargin = 3; // menu item hor text margin
30static constexpr int windowsItemVMargin = 4; // menu item ver text margin
31static constexpr int windowsArrowHMargin = 6; // arrow horizontal margin
32static constexpr int windowsRightBorder = 15; // right border on windows
33
34#ifndef TMT_CONTENTMARGINS
35# define TMT_CONTENTMARGINS 3602
36#endif
37#ifndef TMT_SIZINGMARGINS
38# define TMT_SIZINGMARGINS 3601
39#endif
40#ifndef LISS_NORMAL
41# define LISS_NORMAL 1
42# define LISS_HOT 2
43# define LISS_SELECTED 3
44# define LISS_DISABLED 4
45# define LISS_SELECTEDNOTFOCUS 5
46# define LISS_HOTSELECTED 6
47#endif
48#ifndef BP_COMMANDLINK
49# define BP_COMMANDLINK 6
50# define BP_COMMANDLINKGLYPH 7
51# define CMDLGS_NORMAL 1
52# define CMDLGS_HOT 2
53# define CMDLGS_PRESSED 3
54# define CMDLGS_DISABLED 4
55#endif
56
57// QWindowsVistaStylePrivate -------------------------------------------------------------------------
58// Static initializations
59QVarLengthFlatMap<const QScreen *, HWND, 4> QWindowsVistaStylePrivate::m_vistaTreeViewHelpers;
60bool QWindowsVistaStylePrivate::useVistaTheme = false;
61Q_CONSTINIT QBasicAtomicInt QWindowsVistaStylePrivate::ref = Q_BASIC_ATOMIC_INITIALIZER(-1); // -1 based refcounting
62
63static void qt_add_rect(HRGN &winRegion, QRect r)
64{
65 HRGN rgn = CreateRectRgn(r.left(), r.top(), r.x() + r.width(), r.y() + r.height());
66 if (rgn) {
67 HRGN dest = CreateRectRgn(0,0,0,0);
68 int result = CombineRgn(dest, winRegion, rgn, RGN_OR);
69 if (result) {
70 DeleteObject(winRegion);
71 winRegion = dest;
72 }
73 DeleteObject(rgn);
74 }
75}
76
77static HRGN qt_hrgn_from_qregion(const QRegion &region)
78{
79 HRGN hRegion = CreateRectRgn(0,0,0,0);
80 if (region.rectCount() == 1) {
81 qt_add_rect(hRegion, region.boundingRect());
82 return hRegion;
83 }
84 for (const QRect &rect : region)
85 qt_add_rect(hRegion, rect);
86 return hRegion;
87}
88
89static inline Qt::Orientation progressBarOrientation(const QStyleOption *option = nullptr)
90{
91 if (const auto *pb = qstyleoption_cast<const QStyleOptionProgressBar *>(option))
92 return pb->state & QStyle::State_Horizontal ? Qt::Horizontal : Qt::Vertical;
93 return Qt::Horizontal;
94}
95
96/* In order to obtain the correct VistaTreeViewTheme (arrows for PE_IndicatorBranch),
97 * we need to set the windows "explorer" theme explicitly on a native
98 * window and open the "TREEVIEW" theme handle passing its window handle
99 * in order to get Vista-style item view themes (particularly drawBackground()
100 * for selected items needs this).
101 * We invoke a service of the native Windows interface to create
102 * a non-visible window handle, open the theme on it and insert it into
103 * the cache so that it is found by QWindowsThemeData::handle() first.
104 */
105static inline HWND createTreeViewHelperWindow(const QScreen *screen)
106{
107 using QWindowsApplication = QNativeInterface::Private::QWindowsApplication;
108
109 HWND result = nullptr;
110 if (auto nativeWindowsApp = dynamic_cast<QWindowsApplication *>(QGuiApplicationPrivate::platformIntegration()))
111 result = nativeWindowsApp->createMessageWindow(QStringLiteral(u"QTreeViewThemeHelperWindowClass"),
112 QStringLiteral(u"QTreeViewThemeHelperWindow"));
113 const auto topLeft = screen->geometry().topLeft();
114 // make it a top-level window and move it the the correct screen to paint with the correct dpr later on
115 SetParent(result, NULL);
116 MoveWindow(result, topLeft.x(), topLeft.y(), 10, 10, FALSE);
117 return result;
118}
119
121
122static inline TransformType transformType(const QTransform &transform, qreal devicePixelRatio)
123{
124 if (transform.type() <= QTransform::TxTranslate)
125 return SimpleTransform;
126 if (transform.type() > QTransform::TxScale)
127 return ComplexTransform;
128 return qFuzzyCompare(transform.m11(), devicePixelRatio)
129 && qFuzzyCompare(transform.m22(), devicePixelRatio)
131}
132
133// QTBUG-60571: Exclude known fully opaque theme parts which produce values
134// invalid in ARGB32_Premultiplied (for example, 0x00ffffff).
135static inline bool isFullyOpaque(const QWindowsThemeData &themeData)
136{
137 return themeData.theme == QWindowsVistaStylePrivate::TaskDialogTheme && themeData.partId == TDLG_PRIMARYPANEL;
138}
139
140static inline QRectF scaleRect(const QRectF &r, qreal factor)
141{
142 return r.isValid() && factor > 1
143 ? QRectF(r.topLeft() * factor, r.size() * factor) : r;
144}
145
146static QRegion scaleRegion(const QRegion &region, qreal factor)
147{
148 if (region.isEmpty() || qFuzzyCompare(factor, qreal(1)))
149 return region;
150 QRegion result;
151 for (const QRect &rect : region)
152 result += QRectF(QPointF(rect.topLeft()) * factor, QSizeF(rect.size() * factor)).toRect();
153 return result;
154}
155
156
157/* \internal
158 Checks if the theme engine can/should be used, or if we should fall back
159 to Windows style. For Windows 10, this will still return false for the
160 High Contrast themes.
161*/
162bool QWindowsVistaStylePrivate::useVista(bool update)
163{
164 if (update)
165 useVistaTheme = IsThemeActive() && (IsAppThemed() || !QCoreApplication::instance());
166 return useVistaTheme;
167}
168
169/* \internal
170 Handles refcounting, and queries the theme engine for usage.
171*/
172void QWindowsVistaStylePrivate::init(bool force)
173{
174 if (ref.ref() && !force)
175 return;
176 if (!force) // -1 based atomic refcounting
177 ref.ref();
178
179 useVista(true);
180}
181
182/* \internal
183 Cleans up all static data.
184*/
185void QWindowsVistaStylePrivate::cleanup(bool force)
186{
187 if (bufferBitmap) {
188 if (bufferDC && nullBitmap)
189 SelectObject(bufferDC, nullBitmap);
190 DeleteObject(bufferBitmap);
191 bufferBitmap = nullptr;
192 }
193
194 if (bufferDC)
195 DeleteDC(bufferDC);
196 bufferDC = nullptr;
197
198 if (ref.deref() && !force)
199 return;
200 if (!force) // -1 based atomic refcounting
201 ref.deref();
202
203 useVistaTheme = false;
204 cleanupHandleMap();
205}
206
207bool QWindowsVistaStylePrivate::transitionsEnabled() const
208{
209 Q_Q(const QWindowsVistaStyle);
210 if (q->property("_q_no_animation").toBool())
211 return false;
212 if (QApplication::desktopSettingsAware()) {
213 BOOL animEnabled = false;
214 if (SystemParametersInfo(SPI_GETCLIENTAREAANIMATION, 0, &animEnabled, 0)) {
215 if (animEnabled)
216 return true;
217 }
218 }
219 return false;
220}
221
222int QWindowsVistaStylePrivate::pixelMetricFromSystemDp(QStyle::PixelMetric pm, const QStyleOption *option, const QWidget *widget)
223{
224 switch (pm) {
225 case QStyle::PM_IndicatorWidth:
226 return QWindowsThemeData::themeSize(widget, nullptr, QWindowsVistaStylePrivate::ButtonTheme, BP_CHECKBOX, CBS_UNCHECKEDNORMAL).width();
227 case QStyle::PM_IndicatorHeight:
228 return QWindowsThemeData::themeSize(widget, nullptr, QWindowsVistaStylePrivate::ButtonTheme, BP_CHECKBOX, CBS_UNCHECKEDNORMAL).height();
229 case QStyle::PM_ExclusiveIndicatorWidth:
230 return QWindowsThemeData::themeSize(widget, nullptr, QWindowsVistaStylePrivate::ButtonTheme, BP_RADIOBUTTON, RBS_UNCHECKEDNORMAL).width();
231 case QStyle::PM_ExclusiveIndicatorHeight:
232 return QWindowsThemeData::themeSize(widget, nullptr, QWindowsVistaStylePrivate::ButtonTheme, BP_RADIOBUTTON, RBS_UNCHECKEDNORMAL).height();
233 case QStyle::PM_ProgressBarChunkWidth:
234 return progressBarOrientation(option) == Qt::Horizontal
235 ? QWindowsThemeData::themeSize(widget, nullptr, QWindowsVistaStylePrivate::ProgressTheme, PP_CHUNK).width()
236 : QWindowsThemeData::themeSize(widget, nullptr, QWindowsVistaStylePrivate::ProgressTheme, PP_CHUNKVERT).height();
237 case QStyle::PM_SliderThickness:
238 return QWindowsThemeData::themeSize(widget, nullptr, QWindowsVistaStylePrivate::TrackBarTheme, TKP_THUMB).height();
239 case QStyle::PM_TitleBarHeight:
240 return QWindowsStylePrivate::pixelMetricFromSystemDp(QStyle::PM_TitleBarHeight, option, widget);
241 case QStyle::PM_MdiSubWindowFrameWidth:
242 return QWindowsThemeData::themeSize(widget, nullptr, QWindowsVistaStylePrivate::WindowTheme, WP_FRAMELEFT, FS_ACTIVE).width();
243 case QStyle::PM_DockWidgetFrameWidth:
244 return QWindowsThemeData::themeSize(widget, nullptr, QWindowsVistaStylePrivate::WindowTheme, WP_SMALLFRAMERIGHT, FS_ACTIVE).width();
245 default:
246 break;
247 }
248 return QWindowsVistaStylePrivate::InvalidMetric;
249}
250
251int QWindowsVistaStylePrivate::fixedPixelMetric(QStyle::PixelMetric pm)
252{
253 switch (pm) {
254 case QStyle::PM_DockWidgetTitleBarButtonMargin:
255 return 5;
256 case QStyle::PM_ScrollBarSliderMin:
257 return 18;
258 case QStyle::PM_MenuHMargin:
259 case QStyle::PM_MenuVMargin:
260 return 0;
261 case QStyle::PM_MenuPanelWidth:
262 return 3;
263 default:
264 break;
265 }
266
267 return QWindowsVistaStylePrivate::InvalidMetric;
268}
269
270bool QWindowsVistaStylePrivate::initVistaTreeViewTheming(const QScreen *screen)
271{
272 if (m_vistaTreeViewHelpers.contains(screen))
273 return true;
274 HWND helper = createTreeViewHelperWindow(screen);
275 if (FAILED(SetWindowTheme(helper, L"explorer", nullptr)))
276 {
277 qErrnoWarning("SetWindowTheme() failed.");
278 cleanupVistaTreeViewTheming();
279 return false;
280 }
281 m_vistaTreeViewHelpers.insert(screen, helper);
282 return true;
283}
284
285void QWindowsVistaStylePrivate::cleanupVistaTreeViewTheming()
286{
287 for (auto it = m_vistaTreeViewHelpers.begin(); it != m_vistaTreeViewHelpers.end(); ++it)
288 DestroyWindow(it.value());
289 m_vistaTreeViewHelpers.clear();
290}
291
292/* \internal
293 Closes all open theme data handles to ensure that we don't leak
294 resources, and that we don't refer to old handles when for
295 example the user changes the theme style.
296*/
297void QWindowsVistaStylePrivate::cleanupHandleMap()
298{
299 QWindowsThemeCache::clearAllThemeCaches();
300 QWindowsVistaStylePrivate::cleanupVistaTreeViewTheming();
301}
302
303HTHEME QWindowsVistaStylePrivate::createTheme(int theme, const QWidget *widget)
304{
305 const QScreen *screen = widget ? widget->screen() : qApp->primaryScreen();
306 HWND hwnd = QWindowsVistaStylePrivate::winId(widget);
307 if (theme == VistaTreeViewTheme && QWindowsVistaStylePrivate::initVistaTreeViewTheming(screen))
308 hwnd = QWindowsVistaStylePrivate::m_vistaTreeViewHelpers.value(screen);
309 return QWindowsThemeCache::createTheme(theme, hwnd);
310}
311
312QBackingStore *QWindowsVistaStylePrivate::backingStoreForWidget(const QWidget *widget)
313{
314 if (QBackingStore *backingStore = widget->backingStore())
315 return backingStore;
316 if (const QWidget *topLevel = widget->nativeParentWidget())
317 if (QBackingStore *topLevelBackingStore = topLevel->backingStore())
318 return topLevelBackingStore;
319 return nullptr;
320}
321
322HDC QWindowsVistaStylePrivate::hdcForWidgetBackingStore(const QWidget *widget)
323{
324 if (QBackingStore *backingStore = backingStoreForWidget(widget)) {
325 QPlatformNativeInterface *nativeInterface = QGuiApplication::platformNativeInterface();
326 if (nativeInterface)
327 return static_cast<HDC>(nativeInterface->nativeResourceForBackingStore(QByteArrayLiteral("getDC"), backingStore));
328 }
329 return nullptr;
330}
331
332QString QWindowsVistaStylePrivate::themeName(int theme)
333{
334 return QWindowsThemeCache::themeName(theme);
335}
336
337bool QWindowsVistaStylePrivate::isItemViewDelegateLineEdit(const QWidget *widget)
338{
339 if (!widget)
340 return false;
341 const QWidget *parent1 = widget->parentWidget();
342 // Exclude dialogs or other toplevels parented on item views.
343 if (!parent1 || parent1->isWindow())
344 return false;
345 const QWidget *parent2 = parent1->parentWidget();
346 return parent2 && widget->inherits("QLineEdit")
347 && parent2->inherits("QAbstractItemView");
348}
349
350// Returns whether base color is set for this widget
351bool QWindowsVistaStylePrivate::isLineEditBaseColorSet(const QStyleOption *option, const QWidget *widget)
352{
353 uint resolveMask = option->palette.resolveMask();
354 if (widget) {
355 // Since spin box includes a line edit we need to resolve the palette mask also from
356 // the parent, as while the color is always correct on the palette supplied by panel,
357 // the mask can still be empty. If either mask specifies custom base color, use that.
358#if QT_CONFIG(spinbox)
359 if (const QAbstractSpinBox *spinbox = qobject_cast<QAbstractSpinBox*>(widget->parentWidget()))
360 resolveMask |= spinbox->palette().resolveMask();
361#endif // QT_CONFIG(spinbox)
362 }
363 return (resolveMask & (1 << QPalette::Base)) != 0;
364}
365
366/*! \internal
367 This function will always return a valid window handle, and might
368 create a limbo widget to do so.
369 We often need a window handle to for example open theme data, so
370 this function ensures that we get one.
371*/
372HWND QWindowsVistaStylePrivate::winId(const QWidget *widget)
373{
374 if (widget) {
375 if (const HWND hwnd = QApplicationPrivate::getHWNDForWidget(const_cast<QWidget *>(widget)))
376 return hwnd;
377 }
378
379 // Find top level with native window (there might be dialogs that do not have one).
380 const auto allWindows = QGuiApplication::allWindows();
381 for (const QWindow *window : allWindows) {
382 if (window->isTopLevel() && window->handle() != nullptr)
383 return reinterpret_cast<HWND>(window->winId());
384 }
385
386 return GetDesktopWindow();
387}
388
389/*! \internal
390 Returns a native buffer (DIB section) of at least the size of
391 ( \a x , \a y ). The buffer has a 32 bit depth, to not lose
392 the alpha values on proper alpha-pixmaps.
393*/
394HBITMAP QWindowsVistaStylePrivate::buffer(int w, int h)
395{
396 // If we already have a HBITMAP which is of adequate size, just return that
397 if (bufferBitmap) {
398 if (bufferW >= w && bufferH >= h)
399 return bufferBitmap;
400 // Not big enough, discard the old one
401 if (bufferDC && nullBitmap)
402 SelectObject(bufferDC, nullBitmap);
403 DeleteObject(bufferBitmap);
404 bufferBitmap = nullptr;
405 }
406
407 w = qMax(bufferW, w);
408 h = qMax(bufferH, h);
409
410 if (!bufferDC) {
411 HDC displayDC = GetDC(nullptr);
412 bufferDC = CreateCompatibleDC(displayDC);
413 ReleaseDC(nullptr, displayDC);
414 }
415
416 // Define the header
417 BITMAPINFO bmi;
418 memset(&bmi, 0, sizeof(bmi));
419 bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
420 bmi.bmiHeader.biWidth = w;
421 bmi.bmiHeader.biHeight = -h;
422 bmi.bmiHeader.biPlanes = 1;
423 bmi.bmiHeader.biBitCount = 32;
424 bmi.bmiHeader.biCompression = BI_RGB;
425
426 // Create the pixmap
427 bufferPixels = nullptr;
428 bufferBitmap = CreateDIBSection(bufferDC, &bmi, DIB_RGB_COLORS, reinterpret_cast<void **>(&bufferPixels), nullptr, 0);
429 GdiFlush();
430 nullBitmap = static_cast<HBITMAP>(SelectObject(bufferDC, bufferBitmap));
431
432 if (Q_UNLIKELY(!bufferBitmap)) {
433 qErrnoWarning("QWindowsVistaStylePrivate::buffer(%dx%d), CreateDIBSection() failed.", w, h);
434 bufferW = 0;
435 bufferH = 0;
436 return nullptr;
437 }
438 if (Q_UNLIKELY(!bufferPixels)) {
439 qErrnoWarning("QWindowsVistaStylePrivate::buffer(%dx%d), CreateDIBSection() did not allocate pixel data.", w, h);
440 bufferW = 0;
441 bufferH = 0;
442 return nullptr;
443 }
444 bufferW = w;
445 bufferH = h;
446#ifdef DEBUG_XP_STYLE
447 qDebug("Creating new dib section (%d, %d)", w, h);
448#endif
449 return bufferBitmap;
450}
451
452/*! \internal
453 Returns \c true if the part contains any transparency at all. This does
454 not indicate what kind of transparency we're dealing with. It can be
455 - Alpha transparency
456 - Masked transparency
457*/
458bool QWindowsVistaStylePrivate::isTransparent(QWindowsThemeData &themeData)
459{
460 return IsThemeBackgroundPartiallyTransparent(themeData.handle(), themeData.partId,
461 themeData.stateId);
462}
463
464
465/*! \internal
466 Returns a QRegion of the region of the part
467*/
468QRegion QWindowsVistaStylePrivate::region(QWindowsThemeData &themeData)
469{
470 HRGN hRgn = nullptr;
471 const qreal factor = QWindowsStylePrivate::nativeMetricScaleFactor(themeData.widget);
472 RECT rect = themeData.toRECT(QRect(themeData.rect.topLeft() / factor, themeData.rect.size() / factor));
473 if (!SUCCEEDED(GetThemeBackgroundRegion(themeData.handle(), bufferHDC(), themeData.partId,
474 themeData.stateId, &rect, &hRgn))) {
475 return QRegion();
476 }
477
478 HRGN dest = CreateRectRgn(0, 0, 0, 0);
479 const bool success = CombineRgn(dest, hRgn, nullptr, RGN_COPY) != ERROR;
480
481 QRegion region;
482
483 if (success) {
484 QVarLengthArray<char> buf(256);
485 RGNDATA *rd = reinterpret_cast<RGNDATA *>(buf.data());
486 if (GetRegionData(dest, buf.size(), rd) == 0) {
487 const auto numBytes = GetRegionData(dest, 0, nullptr);
488 if (numBytes > 0) {
489 buf.resize(numBytes);
490 rd = reinterpret_cast<RGNDATA *>(buf.data());
491 if (GetRegionData(dest, numBytes, rd) == 0)
492 rd = nullptr;
493 } else {
494 rd = nullptr;
495 }
496 }
497 if (rd) {
498 RECT *r = reinterpret_cast<RECT *>(rd->Buffer);
499 for (uint i = 0; i < rd->rdh.nCount; ++i) {
500 QRect rect;
501 rect.setCoords(int(r->left * factor), int(r->top * factor),
502 int((r->right - 1) * factor), int((r->bottom - 1) * factor));
503 ++r;
504 region |= rect;
505 }
506 }
507 }
508
509 DeleteObject(hRgn);
510 DeleteObject(dest);
511
512 return region;
513}
514
515/*! \internal
516 Returns \c true if the native doublebuffer contains pixels with
517 varying alpha value.
518*/
519bool QWindowsVistaStylePrivate::hasAlphaChannel(const QRect &rect)
520{
521 const int startX = rect.left();
522 const int startY = rect.top();
523 const int w = rect.width();
524 const int h = rect.height();
525
526 int firstAlpha = -1;
527 for (int y = startY; y < h/2; ++y) {
528 auto buffer = reinterpret_cast<const DWORD *>(bufferPixels) + (y * bufferW);
529 for (int x = startX; x < w; ++x, ++buffer) {
530 int alpha = (*buffer) >> 24;
531 if (firstAlpha == -1)
532 firstAlpha = alpha;
533 else if (alpha != firstAlpha)
534 return true;
535 }
536 }
537 return false;
538}
539
540/*! \internal
541 When the theme engine paints both a true alpha pixmap and a glyph
542 into our buffer, the glyph might not contain a proper alpha value.
543 The rule of thumb for premultiplied pixmaps is that the color
544 values of a pixel can never be higher than the alpha values, so
545 we use this to our advantage here, and fix all instances where
546 this occurs.
547*/
548bool QWindowsVistaStylePrivate::fixAlphaChannel(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 bool hasFixedAlphaValue = false;
555
556 for (int y = startY; y < h; ++y) {
557 auto buffer = reinterpret_cast<DWORD *>(bufferPixels) + (y * bufferW);
558 for (int x = startX; x < w; ++x, ++buffer) {
559 uint pixel = *buffer;
560 int alpha = qAlpha(pixel);
561 if (qRed(pixel) > alpha || qGreen(pixel) > alpha || qBlue(pixel) > alpha) {
562 *buffer |= 0xff000000;
563 hasFixedAlphaValue = true;
564 }
565 }
566 }
567 return hasFixedAlphaValue;
568}
569
570/*! \internal
571 Swaps the alpha values on certain pixels:
572 0xFF?????? -> 0x00??????
573 0x00?????? -> 0xFF??????
574 Used to determine the mask of a non-alpha transparent pixmap in
575 the native doublebuffer, and swap the alphas so we may paint
576 the image as a Premultiplied QImage with drawImage(), and obtain
577 the mask transparency.
578*/
579bool QWindowsVistaStylePrivate::swapAlphaChannel(const QRect &rect, bool allPixels)
580{
581 const int startX = rect.left();
582 const int startY = rect.top();
583 const int w = rect.width();
584 const int h = rect.height();
585 bool valueChange = false;
586
587 // Flip the alphas, so that 255-alpha pixels are 0, and 0-alpha are 255.
588 for (int y = startY; y < h; ++y) {
589 auto buffer = reinterpret_cast<DWORD *>(bufferPixels) + (y * bufferW);
590 for (int x = startX; x < w; ++x, ++buffer) {
591 if (allPixels) {
592 *buffer |= 0xFF000000;
593 continue;
594 }
595 unsigned int alphaValue = (*buffer) & 0xFF000000;
596 if (alphaValue == 0xFF000000) {
597 *buffer = 0;
598 valueChange = true;
599 } else if (alphaValue == 0) {
600 *buffer |= 0xFF000000;
601 valueChange = true;
602 }
603 }
604 }
605 return valueChange;
606}
607
608/*! \internal
609 Main theme drawing function.
610 Determines the correct lowlevel drawing method depending on several
611 factors.
612 Use drawBackgroundThruNativeBuffer() if:
613 - Painter does not have an HDC
614 - Theme part is flipped (mirrored horizontally)
615 else use drawBackgroundDirectly().
616 \note drawBackgroundThruNativeBuffer() can return false for large
617 sizes due to buffer()/CreateDIBSection() failing.
618*/
619bool QWindowsVistaStylePrivate::drawBackground(QWindowsThemeData &themeData, qreal correctionFactor)
620{
621 if (themeData.rect.isEmpty())
622 return true;
623
624 QPainter *painter = themeData.painter;
625 Q_ASSERT_X(painter != nullptr, "QWindowsVistaStylePrivate::drawBackground()", "Trying to draw a theme part without a painter");
626 if (!painter || !painter->isActive())
627 return false;
628
629 painter->save();
630
631 // Access paintDevice via engine since the painter may
632 // return the clip device which can still be a widget device in case of grabWidget().
633
634 bool translucentToplevel = false;
635 const QPaintDevice *paintDevice = painter->device();
636 const qreal aditionalDevicePixelRatio = themeData.widget ? themeData.widget->devicePixelRatio() : qreal(1);
637 if (paintDevice->devType() == QInternal::Widget) {
638 const QWidget *window = static_cast<const QWidget *>(paintDevice)->window();
639 translucentToplevel = window->testAttribute(Qt::WA_TranslucentBackground);
640 }
641
642 const TransformType tt = transformType(painter->deviceTransform(), aditionalDevicePixelRatio);
643
644 bool canDrawDirectly = false;
645 if (themeData.widget && painter->opacity() == 1.0 && !themeData.rotate
646 && !isFullyOpaque(themeData)
647 && tt != ComplexTransform && !themeData.mirrorVertically && !themeData.invertPixels
648 && !translucentToplevel) {
649 // Draw on backing store DC only for real widgets or backing store images.
650 const QPaintDevice *enginePaintDevice = painter->paintEngine()->paintDevice();
651 switch (enginePaintDevice->devType()) {
652 case QInternal::Widget:
653 canDrawDirectly = true;
654 break;
655 case QInternal::Image:
656 // Ensure the backing store has received as resize and is initialized.
657 if (QBackingStore *bs = backingStoreForWidget(themeData.widget)) {
658 if (bs->size().isValid() && bs->paintDevice() == enginePaintDevice)
659 canDrawDirectly = true;
660 }
661 break;
662 }
663 }
664
665 const HDC dc = canDrawDirectly ? hdcForWidgetBackingStore(themeData.widget) : nullptr;
666 const bool result = dc && qFuzzyCompare(correctionFactor, qreal(1))
667 ? drawBackgroundDirectly(dc, themeData, aditionalDevicePixelRatio)
668 : drawBackgroundThruNativeBuffer(themeData, aditionalDevicePixelRatio, correctionFactor);
669 painter->restore();
670 return result;
671}
672
673/*! \internal
674 This function draws the theme parts directly to the paintengines HDC.
675 Do not use this if you need to perform other transformations on the
676 resulting data.
677*/
678bool QWindowsVistaStylePrivate::drawBackgroundDirectly(HDC dc, QWindowsThemeData &themeData, qreal additionalDevicePixelRatio)
679{
680 QPainter *painter = themeData.painter;
681
682 const auto &deviceTransform = painter->deviceTransform();
683 const QPointF redirectionDelta(deviceTransform.dx(), deviceTransform.dy());
684 const QRect area = scaleRect(QRectF(themeData.rect), additionalDevicePixelRatio).translated(redirectionDelta).toRect();
685
686 QRegion sysRgn = painter->paintEngine()->systemClip();
687 if (sysRgn.isEmpty())
688 sysRgn = area;
689 else
690 sysRgn &= area;
691 if (painter->hasClipping())
692 sysRgn &= scaleRegion(painter->clipRegion(), additionalDevicePixelRatio).translated(redirectionDelta.toPoint());
693 HRGN hrgn = qt_hrgn_from_qregion(sysRgn);
694 SelectClipRgn(dc, hrgn);
695
696#ifdef DEBUG_XP_STYLE
697 printf("---[ DIRECT PAINTING ]------------------> Name(%-10s) Part(%d) State(%d)\n",
698 qPrintable(themeData.name), themeData.partId, themeData.stateId);
699 showProperties(themeData);
700#endif
701
702 RECT drawRECT = themeData.toRECT(area);
703 DTBGOPTS drawOptions;
704 memset(&drawOptions, 0, sizeof(drawOptions));
705 drawOptions.dwSize = sizeof(drawOptions);
706 drawOptions.rcClip = themeData.toRECT(sysRgn.boundingRect());
707 drawOptions.dwFlags = DTBG_CLIPRECT
708 | (themeData.noBorder ? DTBG_OMITBORDER : 0)
709 | (themeData.noContent ? DTBG_OMITCONTENT : 0)
710 | (themeData.mirrorHorizontally ? DTBG_MIRRORDC : 0);
711
712 const HRESULT result = DrawThemeBackgroundEx(themeData.handle(), dc, themeData.partId, themeData.stateId, &(drawRECT), &drawOptions);
713 SelectClipRgn(dc, nullptr);
714 DeleteObject(hrgn);
715 return SUCCEEDED(result);
716}
717
718/*! \internal
719 This function uses a secondary Native doublebuffer for painting parts.
720 It should only be used when the painteengine doesn't provide a proper
721 HDC for direct painting (e.g. when doing a grabWidget(), painting to
722 other pixmaps etc), or when special transformations are needed (e.g.
723 flips (horizontal mirroring only, vertical are handled by the theme
724 engine).
725
726 \a correctionFactor is an additional factor used to scale up controls
727 that are too small on High DPI screens, as has been observed for
728 WP_MDICLOSEBUTTON, WP_MDIRESTOREBUTTON, WP_MDIMINBUTTON (QTBUG-75927).
729*/
730bool QWindowsVistaStylePrivate::drawBackgroundThruNativeBuffer(QWindowsThemeData &themeData,
731 qreal additionalDevicePixelRatio,
732 qreal correctionFactor)
733{
734 QPainter *painter = themeData.painter;
735 QRectF rectF = scaleRect(QRectF(themeData.rect), additionalDevicePixelRatio);
736
737 if ((themeData.rotate + 90) % 180 == 0) { // Catch 90,270,etc.. degree flips.
738 rectF = QRectF(0, 0, rectF.height(), rectF.width());
739 }
740 rectF.moveTo(0, 0);
741
742 const bool hasCorrectionFactor = !qFuzzyCompare(correctionFactor, qreal(1));
743 QRect rect = rectF.toRect();
744 const QRect drawRect = hasCorrectionFactor
745 ? QRectF(rectF.topLeft() / correctionFactor, rectF.size() / correctionFactor).toRect()
746 : rect;
747 int partId = themeData.partId;
748 int stateId = themeData.stateId;
749 int w = rect.width();
750 int h = rect.height();
751
752 // Values initialized later, either from cached values, or from function calls
753 AlphaChannelType alphaType = UnknownAlpha;
754 bool stateHasData = true; // We assume so;
755 bool hasAlpha = false;
756 bool partIsTransparent;
757 bool potentialInvalidAlpha;
758
759 QString pixmapCacheKey = QStringLiteral(u"$qt_xp_");
760 pixmapCacheKey.append(themeName(themeData.theme));
761 pixmapCacheKey.append(QLatin1Char('p'));
762 pixmapCacheKey.append(QString::number(partId));
763 pixmapCacheKey.append(QLatin1Char('s'));
764 pixmapCacheKey.append(QString::number(stateId));
765 pixmapCacheKey.append(QLatin1Char('s'));
766 pixmapCacheKey.append(themeData.noBorder ? QLatin1Char('0') : QLatin1Char('1'));
767 pixmapCacheKey.append(QLatin1Char('b'));
768 pixmapCacheKey.append(themeData.noContent ? QLatin1Char('0') : QLatin1Char('1'));
769 pixmapCacheKey.append(QString::number(w));
770 pixmapCacheKey.append(QLatin1Char('w'));
771 pixmapCacheKey.append(QString::number(h));
772 pixmapCacheKey.append(QLatin1Char('h'));
773 pixmapCacheKey.append(QString::number(additionalDevicePixelRatio));
774 pixmapCacheKey.append(QLatin1Char('d'));
775 if (hasCorrectionFactor) {
776 pixmapCacheKey.append(QLatin1Char('c'));
777 pixmapCacheKey.append(QString::number(correctionFactor));
778 }
779
780 QPixmap cachedPixmap;
781 ThemeMapKey key(themeData);
782 ThemeMapData data = alphaCache.value(key);
783
784 bool haveCachedPixmap = false;
785 bool isCached = data.dataValid;
786 if (isCached) {
787 partIsTransparent = data.partIsTransparent;
788 hasAlpha = data.hasAlphaChannel;
789 alphaType = data.alphaType;
790 potentialInvalidAlpha = data.hadInvalidAlpha;
791
792 haveCachedPixmap = QPixmapCache::find(pixmapCacheKey, &cachedPixmap);
793
794#ifdef DEBUG_XP_STYLE
795 char buf[25];
796 ::sprintf(buf, "+ Pixmap(%3d, %3d) ]", w, h);
797 printf("---[ CACHED %s--------> Name(%-10s) Part(%d) State(%d)\n",
798 haveCachedPixmap ? buf : "]-------------------",
799 qPrintable(themeData.name), themeData.partId, themeData.stateId);
800#endif
801 } else {
802 // Not cached, so get values from Theme Engine
803 BOOL tmt_borderonly = false;
804 COLORREF tmt_transparentcolor = 0x0;
805 PROPERTYORIGIN proporigin = PO_NOTFOUND;
806 GetThemeBool(themeData.handle(), themeData.partId, themeData.stateId, TMT_BORDERONLY, &tmt_borderonly);
807 GetThemeColor(themeData.handle(), themeData.partId, themeData.stateId, TMT_TRANSPARENTCOLOR, &tmt_transparentcolor);
808 GetThemePropertyOrigin(themeData.handle(), themeData.partId, themeData.stateId, TMT_CAPTIONMARGINS, &proporigin);
809
810 partIsTransparent = isTransparent(themeData);
811
812 potentialInvalidAlpha = false;
813 GetThemePropertyOrigin(themeData.handle(), themeData.partId, themeData.stateId, TMT_GLYPHTYPE, &proporigin);
814 if (proporigin == PO_PART || proporigin == PO_STATE) {
815 int tmt_glyphtype = GT_NONE;
816 GetThemeEnumValue(themeData.handle(), themeData.partId, themeData.stateId, TMT_GLYPHTYPE, &tmt_glyphtype);
817 potentialInvalidAlpha = partIsTransparent && tmt_glyphtype == GT_IMAGEGLYPH;
818 }
819
820#ifdef DEBUG_XP_STYLE
821 printf("---[ NOT CACHED ]-----------------------> Name(%-10s) Part(%d) State(%d)\n",
822 qPrintable(themeData.name), themeData.partId, themeData.stateId);
823 printf("-->partIsTransparen = %d\n", partIsTransparent);
824 printf("-->potentialInvalidAlpha = %d\n", potentialInvalidAlpha);
825 showProperties(themeData);
826#endif
827 }
828 bool wasAlphaSwapped = false;
829 bool wasAlphaFixed = false;
830
831 // OLD PSDK Workaround ------------------------------------------------------------------------
832 // See if we need extra clipping for the older PSDK, which does
833 // not have a DrawThemeBackgroundEx function for DTGB_OMITBORDER
834 // and DTGB_OMITCONTENT
835 bool addBorderContentClipping = false;
836 QRegion extraClip;
837 QRect area = drawRect;
838 if (themeData.noBorder || themeData.noContent) {
839 extraClip = area;
840 // We are running on a system where the uxtheme.dll does not have
841 // the DrawThemeBackgroundEx function, so we need to clip away
842 // borders or contents manually.
843
844 int borderSize = 0;
845 PROPERTYORIGIN origin = PO_NOTFOUND;
846 GetThemePropertyOrigin(themeData.handle(), themeData.partId, themeData.stateId, TMT_BORDERSIZE, &origin);
847 GetThemeInt(themeData.handle(), themeData.partId, themeData.stateId, TMT_BORDERSIZE, &borderSize);
848 borderSize *= additionalDevicePixelRatio;
849
850 // Clip away border region
851 if ((origin == PO_CLASS || origin == PO_PART || origin == PO_STATE) && borderSize > 0) {
852 if (themeData.noBorder) {
853 extraClip &= area;
854 area = area.adjusted(-borderSize, -borderSize, borderSize, borderSize);
855 }
856
857 // Clip away content region
858 if (themeData.noContent) {
859 QRegion content = area.adjusted(borderSize, borderSize, -borderSize, -borderSize);
860 extraClip ^= content;
861 }
862 }
863 addBorderContentClipping = (themeData.noBorder | themeData.noContent);
864 }
865
866 QImage img;
867 if (!haveCachedPixmap) { // If the pixmap is not cached, generate it! -------------------------
868 if (!buffer(drawRect.width(), drawRect.height())) // Ensure a buffer of at least (w, h) in size
869 return false;
870 HDC dc = bufferHDC();
871
872 // Clear the buffer
873 if (alphaType != NoAlpha) {
874 // Consider have separate "memset" function for small chunks for more speedup
875 memset(bufferPixels, 0x00, bufferW * drawRect.height() * 4);
876 }
877
878 // Difference between area and rect
879 int dx = area.x() - drawRect.x();
880 int dy = area.y() - drawRect.y();
881
882 // Adjust so painting rect starts from Origo
883 rect.moveTo(0,0);
884 area.moveTo(dx,dy);
885 DTBGOPTS drawOptions;
886 drawOptions.dwSize = sizeof(drawOptions);
887 drawOptions.rcClip = themeData.toRECT(rect);
888 drawOptions.dwFlags = DTBG_CLIPRECT
889 | (themeData.noBorder ? DTBG_OMITBORDER : 0)
890 | (themeData.noContent ? DTBG_OMITCONTENT : 0);
891
892 // Drawing the part into the backing store
893 RECT wRect(themeData.toRECT(area));
894 DrawThemeBackgroundEx(themeData.handle(), dc, themeData.partId, themeData.stateId, &wRect, &drawOptions);
895
896 // If not cached, analyze the buffer data to figure
897 // out alpha type, and if it contains data
898 if (!isCached) {
899 // SHORTCUT: If the part's state has no data, cache it for NOOP later
900 if (!stateHasData) {
901 memset(static_cast<void *>(&data), 0, sizeof(data));
902 data.dataValid = true;
903 alphaCache.insert(key, data);
904 return true;
905 }
906 hasAlpha = hasAlphaChannel(rect);
907 if (!hasAlpha && partIsTransparent)
908 potentialInvalidAlpha = true;
909#if defined(DEBUG_XP_STYLE) && 1
910 dumpNativeDIB(drawRect.width(), drawRect.height());
911#endif
912 }
913
914 // Fix alpha values, if needed
915 if (potentialInvalidAlpha)
916 wasAlphaFixed = fixAlphaChannel(drawRect);
917
918 QImage::Format format;
919 if ((partIsTransparent && !wasAlphaSwapped) || (!partIsTransparent && hasAlpha)) {
920 format = QImage::Format_ARGB32_Premultiplied;
921 alphaType = RealAlpha;
922 } else if (wasAlphaSwapped) {
923 format = QImage::Format_ARGB32_Premultiplied;
924 alphaType = MaskAlpha;
925 } else {
926 format = QImage::Format_RGB32;
927 // The image data we got from the theme engine does not have any transparency,
928 // thus the alpha channel is set to 0.
929 // However, Format_RGB32 requires the alpha part to be set to 0xff, thus
930 // we must flip it from 0x00 to 0xff
931 swapAlphaChannel(rect, true);
932 alphaType = NoAlpha;
933 }
934#if defined(DEBUG_XP_STYLE) && 1
935 printf("Image format is: %s\n", alphaType == RealAlpha ? "Real Alpha" : alphaType == MaskAlpha ? "Masked Alpha" : "No Alpha");
936#endif
937 img = QImage(bufferPixels, bufferW, bufferH, format);
938 if (themeData.invertPixels)
939 img.invertPixels();
940
941 if (hasCorrectionFactor)
942 img = img.scaled(img.size() * correctionFactor, Qt::KeepAspectRatio, Qt::SmoothTransformation);
943 img.setDevicePixelRatio(additionalDevicePixelRatio);
944 }
945
946 // Blitting backing store
947 bool useRegion = partIsTransparent && !hasAlpha && !wasAlphaSwapped;
948
949 QRegion newRegion;
950 QRegion oldRegion;
951 if (useRegion) {
952 newRegion = region(themeData);
953 oldRegion = painter->clipRegion();
954 painter->setClipRegion(newRegion);
955#if defined(DEBUG_XP_STYLE) && 0
956 printf("Using region:\n");
957 for (const QRect &r : newRegion)
958 printf(" (%d, %d, %d, %d)\n", r.x(), r.y(), r.right(), r.bottom());
959#endif
960 }
961
962 if (addBorderContentClipping)
963 painter->setClipRegion(scaleRegion(extraClip, 1.0 / additionalDevicePixelRatio), Qt::IntersectClip);
964
965 if (!themeData.mirrorHorizontally && !themeData.mirrorVertically && !themeData.rotate) {
966 if (!haveCachedPixmap)
967 painter->drawImage(themeData.rect, img, rect);
968 else
969 painter->drawPixmap(themeData.rect, cachedPixmap);
970 } else {
971 // This is _slow_!
972 // Make a copy containing only the necessary data, and mirror
973 // on all wanted axes. Then draw the copy.
974 // If cached, the normal pixmap is cached, instead of caching
975 // all possible orientations for each part and state.
976 QImage imgCopy;
977 if (!haveCachedPixmap)
978 imgCopy = img.copy(rect);
979 else
980 imgCopy = cachedPixmap.toImage();
981
982 if (themeData.rotate) {
983 QTransform rotMatrix;
984 rotMatrix.rotate(themeData.rotate);
985 imgCopy = imgCopy.transformed(rotMatrix);
986 }
987 Qt::Orientations orient = {};
988 if (themeData.mirrorHorizontally)
989 orient |= Qt::Horizontal;
990 if (themeData.mirrorVertically)
991 orient |= Qt::Vertical;
992 if (themeData.mirrorHorizontally || themeData.mirrorVertically)
993 imgCopy.flip(orient);
994 painter->drawImage(themeData.rect, imgCopy);
995 }
996
997 if (useRegion || addBorderContentClipping) {
998 if (oldRegion.isEmpty())
999 painter->setClipping(false);
1000 else
1001 painter->setClipRegion(oldRegion);
1002 }
1003
1004 // Cache the pixmap to avoid expensive swapAlphaChannel() calls
1005 if (!haveCachedPixmap && w && h) {
1006 QPixmap pix = QPixmap::fromImage(img).copy(rect);
1007 QPixmapCache::insert(pixmapCacheKey, pix);
1008#ifdef DEBUG_XP_STYLE
1009 printf("+++Adding pixmap to cache, size(%d, %d), wasAlphaSwapped(%d), wasAlphaFixed(%d), name(%s)\n",
1010 w, h, wasAlphaSwapped, wasAlphaFixed, qPrintable(pixmapCacheKey));
1011#endif
1012 }
1013
1014 // Add to theme part cache
1015 if (!isCached) {
1016 memset(static_cast<void *>(&data), 0, sizeof(data));
1017 data.dataValid = true;
1018 data.partIsTransparent = partIsTransparent;
1019 data.alphaType = alphaType;
1020 data.hasAlphaChannel = hasAlpha;
1021 data.wasAlphaSwapped = wasAlphaSwapped;
1022 data.hadInvalidAlpha = wasAlphaFixed;
1023 alphaCache.insert(key, data);
1024 }
1025 return true;
1026}
1027
1028/*!
1029 \internal
1030
1031 Animations are started at a frame that is based on the current time,
1032 which makes it impossible to run baseline tests with this style. Allow
1033 overriding through a dynamic property.
1034*/
1035QTime QWindowsVistaStylePrivate::animationTime() const
1036{
1037 Q_Q(const QWindowsVistaStyle);
1038 static bool animationTimeOverride = q->dynamicPropertyNames().contains("_qt_animation_time");
1039 if (animationTimeOverride)
1040 return q->property("_qt_animation_time").toTime();
1041 return QTime::currentTime();
1042}
1043
1044/* \internal
1045 Checks and returns the style object
1046*/
1047inline QObject *styleObject(const QStyleOption *option) {
1048 return option ? option->styleObject : nullptr;
1049}
1050
1051/* \internal
1052 Checks if we can animate on a style option
1053*/
1054bool canAnimate(const QStyleOption *option) {
1055 return option
1056 && option->styleObject
1057 && !option->styleObject->property("_q_no_animation").toBool();
1058}
1059
1060static inline QImage createAnimationBuffer(const QStyleOption *option, const QWidget *widget)
1061{
1062 const qreal devicePixelRatio = widget
1063 ? widget->devicePixelRatioF() : qApp->devicePixelRatio();
1064 QImage result(option->rect.size() * devicePixelRatio, QImage::Format_ARGB32_Premultiplied);
1065 result.setDevicePixelRatio(devicePixelRatio);
1066 result.fill(0);
1067 return result;
1068}
1069
1070/* \internal
1071 Used by animations to clone a styleoption and shift its offset
1072*/
1073QStyleOption *clonedAnimationStyleOption(const QStyleOption*option) {
1074 QStyleOption *styleOption = nullptr;
1075 if (const QStyleOptionSlider *slider = qstyleoption_cast<const QStyleOptionSlider*>(option))
1076 styleOption = new QStyleOptionSlider(*slider);
1077 else if (const QStyleOptionSpinBox *spinbox = qstyleoption_cast<const QStyleOptionSpinBox*>(option))
1078 styleOption = new QStyleOptionSpinBox(*spinbox);
1079 else if (const QStyleOptionGroupBox *groupBox = qstyleoption_cast<const QStyleOptionGroupBox*>(option))
1080 styleOption = new QStyleOptionGroupBox(*groupBox);
1081 else if (const QStyleOptionComboBox *combo = qstyleoption_cast<const QStyleOptionComboBox*>(option))
1082 styleOption = new QStyleOptionComboBox(*combo);
1083 else if (const QStyleOptionButton *button = qstyleoption_cast<const QStyleOptionButton*>(option))
1084 styleOption = new QStyleOptionButton(*button);
1085 else
1086 styleOption = new QStyleOption(*option);
1087 styleOption->rect = QRect(QPoint(0,0), option->rect.size());
1088 return styleOption;
1089}
1090
1091/* \internal
1092 Used by animations to delete cloned styleoption
1093*/
1094void deleteClonedAnimationStyleOption(const QStyleOption *option)
1095{
1096 if (const QStyleOptionSlider *slider = qstyleoption_cast<const QStyleOptionSlider*>(option))
1097 delete slider;
1098 else if (const QStyleOptionSpinBox *spinbox = qstyleoption_cast<const QStyleOptionSpinBox*>(option))
1099 delete spinbox;
1100 else if (const QStyleOptionGroupBox *groupBox = qstyleoption_cast<const QStyleOptionGroupBox*>(option))
1101 delete groupBox;
1102 else if (const QStyleOptionComboBox *combo = qstyleoption_cast<const QStyleOptionComboBox*>(option))
1103 delete combo;
1104 else if (const QStyleOptionButton *button = qstyleoption_cast<const QStyleOptionButton*>(option))
1105 delete button;
1106 else
1107 delete option;
1108}
1109
1110static void populateTitleBarButtonTheme(const QStyle *proxy, const QWidget *widget,
1111 const QStyleOptionComplex *option,
1112 QStyle::SubControl subControl,
1113 bool isTitleBarActive, int part,
1114 QWindowsThemeData *theme)
1115{
1116 theme->rect = proxy->subControlRect(QStyle::CC_TitleBar, option, subControl, widget);
1117 theme->partId = part;
1118 if (widget && !widget->isEnabled())
1119 theme->stateId = RBS_DISABLED;
1120 else if (option->activeSubControls == subControl && option->state.testFlag(QStyle::State_Sunken))
1121 theme->stateId = RBS_PUSHED;
1122 else if (option->activeSubControls == subControl && option->state.testFlag(QStyle::State_MouseOver))
1123 theme->stateId = RBS_HOT;
1124 else if (!isTitleBarActive)
1125 theme->stateId = RBS_INACTIVE;
1126 else
1127 theme->stateId = RBS_NORMAL;
1128}
1129
1130#if QT_CONFIG(mdiarea)
1131// Helper for drawing MDI buttons into the corner widget of QMenuBar in case a
1132// QMdiSubWindow is maximized.
1133static void populateMdiButtonTheme(const QStyle *proxy, const QWidget *widget,
1134 const QStyleOptionComplex *option,
1135 QStyle::SubControl subControl, int part,
1136 QWindowsThemeData *theme)
1137{
1138 theme->partId = part;
1139 theme->rect = proxy->subControlRect(QStyle::CC_MdiControls, option, subControl, widget);
1140 if (!option->state.testFlag(QStyle::State_Enabled))
1141 theme->stateId = CBS_INACTIVE;
1142 else if (option->state.testFlag(QStyle::State_Sunken) && option->activeSubControls.testFlag(subControl))
1143 theme->stateId = CBS_PUSHED;
1144 else if (option->state.testFlag(QStyle::State_MouseOver) && option->activeSubControls.testFlag(subControl))
1145 theme->stateId = CBS_HOT;
1146 else
1147 theme->stateId = CBS_NORMAL;
1148}
1149
1150// Calculate an small (max 2), empirical correction factor for scaling up
1151// WP_MDICLOSEBUTTON, WP_MDIRESTOREBUTTON, WP_MDIMINBUTTON, which are too
1152// small on High DPI screens (QTBUG-75927).
1153static qreal mdiButtonCorrectionFactor(QWindowsThemeData &theme, const QPaintDevice *pd = nullptr)
1154{
1155 const auto dpr = pd ? pd->devicePixelRatio() : qApp->devicePixelRatio();
1156 const QSizeF nativeSize = QSizeF(theme.size()) / dpr;
1157 const QSizeF requestedSize(theme.rect.size());
1158 const auto rawFactor = qMin(requestedSize.width() / nativeSize.width(),
1159 requestedSize.height() / nativeSize.height());
1160 const auto factor = rawFactor >= qreal(2) ? qreal(2) : qreal(1);
1161 return factor;
1162}
1163#endif // QT_CONFIG(mdiarea)
1164
1165/*
1166 This function is used by subControlRect to check if a button
1167 should be drawn for the given subControl given a set of window flags.
1168*/
1169static bool buttonVisible(const QStyle::SubControl sc, const QStyleOptionTitleBar *tb){
1170
1171 bool isMinimized = tb->titleBarState & Qt::WindowMinimized;
1172 bool isMaximized = tb->titleBarState & Qt::WindowMaximized;
1173 const auto flags = tb->titleBarFlags;
1174 bool retVal = false;
1175 switch (sc) {
1176 case QStyle::SC_TitleBarContextHelpButton:
1177 if (flags & Qt::WindowContextHelpButtonHint)
1178 retVal = true;
1179 break;
1180 case QStyle::SC_TitleBarMinButton:
1181 if (!isMinimized && (flags & Qt::WindowMinimizeButtonHint))
1182 retVal = true;
1183 break;
1184 case QStyle::SC_TitleBarNormalButton:
1185 if (isMinimized && (flags & Qt::WindowMinimizeButtonHint))
1186 retVal = true;
1187 else if (isMaximized && (flags & Qt::WindowMaximizeButtonHint))
1188 retVal = true;
1189 break;
1190 case QStyle::SC_TitleBarMaxButton:
1191 if (!isMaximized && (flags & Qt::WindowMaximizeButtonHint))
1192 retVal = true;
1193 break;
1194 case QStyle::SC_TitleBarShadeButton:
1195 if (!isMinimized && flags & Qt::WindowShadeButtonHint)
1196 retVal = true;
1197 break;
1198 case QStyle::SC_TitleBarUnshadeButton:
1199 if (isMinimized && flags & Qt::WindowShadeButtonHint)
1200 retVal = true;
1201 break;
1202 case QStyle::SC_TitleBarCloseButton:
1203 if (flags & Qt::WindowSystemMenuHint)
1204 retVal = true;
1205 break;
1206 case QStyle::SC_TitleBarSysMenu:
1207 if (flags & Qt::WindowSystemMenuHint)
1208 retVal = true;
1209 break;
1210 default :
1211 retVal = true;
1212 }
1213 return retVal;
1214}
1215
1216//convert Qt state flags to uxtheme button states
1217static int buttonStateId(int flags, int partId)
1218{
1219 int stateId = 0;
1220 if (partId == BP_RADIOBUTTON || partId == BP_CHECKBOX) {
1221 if (!(flags & QStyle::State_Enabled))
1222 stateId = RBS_UNCHECKEDDISABLED;
1223 else if (flags & QStyle::State_Sunken)
1224 stateId = RBS_UNCHECKEDPRESSED;
1225 else if (flags & QStyle::State_MouseOver)
1226 stateId = RBS_UNCHECKEDHOT;
1227 else
1228 stateId = RBS_UNCHECKEDNORMAL;
1229
1230 if (flags & QStyle::State_On)
1231 stateId += RBS_CHECKEDNORMAL-1;
1232
1233 } else if (partId == BP_PUSHBUTTON) {
1234 if (!(flags & QStyle::State_Enabled))
1235 stateId = PBS_DISABLED;
1236 else if (flags & (QStyle::State_Sunken | QStyle::State_On))
1237 stateId = PBS_PRESSED;
1238 else if (flags & QStyle::State_MouseOver)
1239 stateId = PBS_HOT;
1240 else
1241 stateId = PBS_NORMAL;
1242 } else {
1243 Q_ASSERT(1);
1244 }
1245 return stateId;
1246}
1247
1248static inline bool supportsStateTransition(QStyle::PrimitiveElement element,
1249 const QStyleOption *option,
1250 const QWidget *widget)
1251{
1252 bool result = false;
1253 switch (element) {
1254 case QStyle::PE_IndicatorRadioButton:
1255 case QStyle::PE_IndicatorCheckBox:
1256 result = true;
1257 break;
1258 // QTBUG-40634, do not animate when color is set in palette for PE_PanelLineEdit.
1259 case QStyle::PE_FrameLineEdit:
1260 result = !QWindowsVistaStylePrivate::isLineEditBaseColorSet(option, widget);
1261 break;
1262 default:
1263 break;
1264 }
1265 return result;
1266}
1267
1268/*!
1269 \class QWindowsVistaStyle
1270 \brief The QWindowsVistaStyle class provides a look and feel suitable for applications on Microsoft Windows Vista.
1271 \since 4.3
1272 \ingroup appearance
1273 \inmodule QtWidgets
1274 \internal
1275
1276 \warning This style is only available on the Windows Vista platform
1277 because it makes use of Windows Vista's style engine.
1278
1279 \sa QMacStyle, QFusionStyle
1280*/
1281
1282/*!
1283 Constructs a QWindowsVistaStyle object.
1284*/
1285QWindowsVistaStyle::QWindowsVistaStyle() : QWindowsVistaStyle(*new QWindowsVistaStylePrivate)
1286{
1287}
1288
1289/*!
1290 \internal
1291 Constructs a QWindowsStyle object.
1292*/
1293QWindowsVistaStyle::QWindowsVistaStyle(QWindowsVistaStylePrivate &dd) : QWindowsStyle(dd)
1294{
1295 Q_D(QWindowsVistaStyle);
1296 d->assetFont = QFont("Segoe MDL2 Assets"_L1);
1297 d->assetFont.setStyleStrategy(QFont::NoFontMerging);
1298}
1299
1300/*!
1301 Destructor.
1302*/
1303QWindowsVistaStyle::~QWindowsVistaStyle() = default;
1304
1305
1306/*!
1307 \internal
1308
1309 Animations are used for some state transitions on specific widgets.
1310
1311 Only one running animation can exist for a widget at any specific
1312 time. Animations can be added through
1313 QWindowsVistaStylePrivate::startAnimation(Animation *) and any
1314 existing animation on a widget can be retrieved with
1315 QWindowsVistaStylePrivate::widgetAnimation(Widget *).
1316
1317 Once an animation has been started,
1318 QWindowsVistaStylePrivate::timerEvent(QTimerEvent *) will
1319 continuously call update() on the widget until it is stopped,
1320 meaning that drawPrimitive will be called many times until the
1321 transition has completed. During this time, the result will be
1322 retrieved by the Animation::paint(...) function and not by the style
1323 itself.
1324
1325 To determine if a transition should occur, the style needs to know
1326 the previous state of the widget as well as the current one. This is
1327 solved by updating dynamic properties on the widget every time the
1328 function is called.
1329
1330 Transitions interrupting existing transitions should always be
1331 smooth, so whenever a hover-transition is started on a pulsating
1332 button, it uses the current frame of the pulse-animation as the
1333 starting image for the hover transition.
1334
1335 */
1336void QWindowsVistaStyle::drawPrimitive(PrimitiveElement element, const QStyleOption *option,
1337 QPainter *painter, const QWidget *widget) const
1338{
1339 if (!QWindowsVistaStylePrivate::useVista()) {
1340 QWindowsStyle::drawPrimitive(element, option, painter, widget);
1341 return;
1342 }
1343
1344 QWindowsVistaStylePrivate *d = const_cast<QWindowsVistaStylePrivate*>(d_func());
1345
1346 int state = option->state;
1347 QRect rect = option->rect;
1348
1349 if ((state & State_Enabled) && d->transitionsEnabled() && canAnimate(option)) {
1350 if (supportsStateTransition(element, option, widget)) {
1351 // Retrieve and update the dynamic properties tracking
1352 // the previous state of the widget:
1353 QObject *styleObject = option->styleObject;
1354 styleObject->setProperty("_q_no_animation", true);
1355 int oldState = styleObject->property("_q_stylestate").toInt();
1356 QRect oldRect = styleObject->property("_q_stylerect").toRect();
1357 QRect newRect = rect;
1358 styleObject->setProperty("_q_stylestate", int(option->state));
1359 styleObject->setProperty("_q_stylerect", option->rect);
1360
1361 bool doTransition = oldState &&
1362 ((state & State_Sunken) != (oldState & State_Sunken) ||
1363 (state & State_On) != (oldState & State_On) ||
1364 (state & State_MouseOver) != (oldState & State_MouseOver));
1365
1366 if (oldRect != newRect ||
1367 (state & State_Enabled) != (oldState & State_Enabled) ||
1368 (state & State_Active) != (oldState & State_Active))
1369 d->stopAnimation(styleObject);
1370
1371 if (state & State_ReadOnly && element == PE_FrameLineEdit) // Do not animate read only line edits
1372 doTransition = false;
1373
1374 if (doTransition) {
1375 QStyleOption *styleOption = clonedAnimationStyleOption(option);
1376 styleOption->state = QStyle::State(oldState);
1377
1378 QWindowsVistaAnimation *animate = qobject_cast<QWindowsVistaAnimation *>(d->animation(styleObject));
1379 QWindowsVistaTransition *transition = new QWindowsVistaTransition(styleObject);
1380
1381 // We create separate images for the initial and final transition states and store them in the
1382 // Transition object.
1383 QImage startImage = createAnimationBuffer(option, widget);
1384 QPainter startPainter(&startImage);
1385
1386 QImage endImage = createAnimationBuffer(option, widget);
1387 QPainter endPainter(&endImage);
1388
1389 // If we have a running animation on the widget already, we will use that to paint the initial
1390 // state of the new transition, this ensures a smooth transition from a current animation such as a
1391 // pulsating default button into the intended target state.
1392 if (!animate)
1393 proxy()->drawPrimitive(element, styleOption, &startPainter, widget);
1394 else
1395 animate->paint(&startPainter, styleOption);
1396
1397 transition->setStartImage(startImage);
1398
1399 // The end state of the transition is simply the result we would have painted
1400 // if the style was not animated.
1401 styleOption->styleObject = nullptr;
1402 styleOption->state = option->state;
1403 proxy()->drawPrimitive(element, styleOption, &endPainter, widget);
1404
1405 transition->setEndImage(endImage);
1406
1407 HTHEME theme;
1408 int partId;
1409 DWORD duration;
1410 int fromState = 0;
1411 int toState = 0;
1412
1413 //translate state flags to UXTHEME states :
1414 if (element == PE_FrameLineEdit) {
1415 theme = OpenThemeData(nullptr, L"Edit");
1416 partId = EP_EDITBORDER_NOSCROLL;
1417
1418 if (oldState & State_HasFocus)
1419 fromState = ETS_SELECTED;
1420 else if (oldState & State_MouseOver)
1421 fromState = ETS_HOT;
1422 else
1423 fromState = ETS_NORMAL;
1424
1425 if (state & State_HasFocus)
1426 toState = ETS_SELECTED;
1427 else if (state & State_MouseOver)
1428 toState = ETS_HOT;
1429 else
1430 toState = ETS_NORMAL;
1431
1432 } else {
1433 theme = OpenThemeData(nullptr, L"Button");
1434 if (element == PE_IndicatorRadioButton)
1435 partId = BP_RADIOBUTTON;
1436 else if (element == PE_IndicatorCheckBox)
1437 partId = BP_CHECKBOX;
1438 else
1439 partId = BP_PUSHBUTTON;
1440
1441 fromState = buttonStateId(oldState, partId);
1442 toState = buttonStateId(option->state, partId);
1443 }
1444
1445 // Retrieve the transition time between the states from the system.
1446 if (theme
1447 && SUCCEEDED(GetThemeTransitionDuration(theme, partId, fromState, toState,
1448 TMT_TRANSITIONDURATIONS, &duration))) {
1449 transition->setDuration(int(duration));
1450 }
1451 transition->setStartTime(d->animationTime());
1452
1453 deleteClonedAnimationStyleOption(styleOption);
1454 d->startAnimation(transition);
1455 }
1456 styleObject->setProperty("_q_no_animation", false);
1457 }
1458 }
1459
1460 int themeNumber = -1;
1461 int partId = 0;
1462 int stateId = 0;
1463 bool hMirrored = false;
1464 bool vMirrored = false;
1465 bool noBorder = false;
1466 bool noContent = false;
1467 int rotate = 0;
1468
1469 switch (element) {
1470 case PE_PanelButtonCommand:
1471 if (const auto *btn = qstyleoption_cast<const QStyleOptionButton *>(option)) {
1472 QBrush fill;
1473 if (!(state & State_Sunken) && (state & State_On))
1474 fill = QBrush(option->palette.light().color(), Qt::Dense4Pattern);
1475 else
1476 fill = option->palette.brush(QPalette::Button);
1477 if (btn->features & QStyleOptionButton::DefaultButton && state & State_Sunken) {
1478 painter->setPen(option->palette.dark().color());
1479 painter->setBrush(fill);
1480 painter->drawRect(rect.adjusted(0, 0, -1, -1));
1481 } else if (state & (State_Raised | State_On | State_Sunken)) {
1482 qDrawWinButton(painter, rect, option->palette, state & (State_Sunken | State_On),
1483 &fill);
1484 } else {
1485 painter->fillRect(rect, fill);
1486 }
1487 }
1488 break;
1489
1490 case PE_PanelButtonTool:
1491#if QT_CONFIG(dockwidget)
1492 if (widget && widget->inherits("QDockWidgetTitleButton")) {
1493 if (const QWidget *dw = widget->parentWidget())
1494 if (dw->isWindow()) {
1495 return;
1496 }
1497 }
1498#endif // QT_CONFIG(dockwidget)
1499 themeNumber = QWindowsVistaStylePrivate::ToolBarTheme;
1500 partId = TP_BUTTON;
1501 if (!(option->state & State_Enabled))
1502 stateId = TS_DISABLED;
1503 else if (option->state & State_Sunken)
1504 stateId = TS_PRESSED;
1505 else if (option->state & State_MouseOver)
1506 stateId = option->state & State_On ? TS_HOTCHECKED : TS_HOT;
1507 else if (option->state & State_On)
1508 stateId = TS_CHECKED;
1509 else if (!(option->state & State_AutoRaise))
1510 stateId = TS_HOT;
1511 else
1512 stateId = TS_NORMAL;
1513
1514 break;
1515
1516 case PE_IndicatorHeaderArrow:
1517 if (const auto *header = qstyleoption_cast<const QStyleOptionHeader *>(option)) {
1518 int stateId = HSAS_SORTEDDOWN;
1519 if (header->sortIndicator & QStyleOptionHeader::SortDown)
1520 stateId = HSAS_SORTEDUP; //note that the uxtheme sort down indicator is the inverse of ours
1521 QWindowsThemeData theme(widget, painter,
1522 QWindowsVistaStylePrivate::HeaderTheme,
1523 HP_HEADERSORTARROW, stateId, option->rect);
1524 d->drawBackground(theme);
1525 return;
1526 }
1527 break;
1528
1529 case PE_IndicatorCheckBox:
1530 if (auto *animate =
1531 qobject_cast<QWindowsVistaAnimation *>(d->animation(styleObject(option)))) {
1532 animate->paint(painter, option);
1533 return;
1534 } else {
1535 themeNumber = QWindowsVistaStylePrivate::ButtonTheme;
1536 partId = BP_CHECKBOX;
1537
1538 if (!(option->state & State_Enabled))
1539 stateId = CBS_UNCHECKEDDISABLED;
1540 else if (option->state & State_Sunken)
1541 stateId = CBS_UNCHECKEDPRESSED;
1542 else if (option->state & State_MouseOver)
1543 stateId = CBS_UNCHECKEDHOT;
1544 else
1545 stateId = CBS_UNCHECKEDNORMAL;
1546
1547 if (option->state & State_On)
1548 stateId += CBS_CHECKEDNORMAL-1;
1549 else if (option->state & State_NoChange)
1550 stateId += CBS_MIXEDNORMAL-1;
1551 }
1552 break;
1553
1554 case PE_IndicatorItemViewItemCheck: {
1555 QStyleOptionButton button;
1556 button.QStyleOption::operator=(*option);
1557 button.state &= ~State_MouseOver;
1558 proxy()->drawPrimitive(PE_IndicatorCheckBox, &button, painter, widget);
1559 return;
1560 }
1561
1562 case PE_IndicatorBranch: {
1563 QWindowsThemeData theme(widget, painter, QWindowsVistaStylePrivate::VistaTreeViewTheme);
1564 static int decoration_size = 0;
1565 if (!decoration_size && theme.isValid()) {
1566 QWindowsThemeData themeSize = theme;
1567 themeSize.partId = TVP_HOTGLYPH;
1568 themeSize.stateId = GLPS_OPENED;
1569 const QSizeF size = themeSize.size() * QWindowsStylePrivate::nativeMetricScaleFactor(widget);
1570 decoration_size = qRound(qMax(size.width(), size.height()));
1571 }
1572 int mid_h = option->rect.x() + option->rect.width() / 2;
1573 int mid_v = option->rect.y() + option->rect.height() / 2;
1574 if (option->state & State_Children) {
1575 int delta = decoration_size / 2;
1576 theme.rect = QRect(mid_h - delta, mid_v - delta, decoration_size, decoration_size);
1577 theme.partId = option->state & State_MouseOver ? TVP_HOTGLYPH : TVP_GLYPH;
1578 theme.stateId = option->state & QStyle::State_Open ? GLPS_OPENED : GLPS_CLOSED;
1579 if (option->direction == Qt::RightToLeft)
1580 theme.mirrorHorizontally = true;
1581 d->drawBackground(theme);
1582 }
1583 return;
1584 }
1585
1586 case PE_PanelButtonBevel:
1587 if (QWindowsVistaAnimation *animate =
1588 qobject_cast<QWindowsVistaAnimation *>(d->animation(styleObject(option)))) {
1589 animate->paint(painter, option);
1590 return;
1591 }
1592
1593 themeNumber = QWindowsVistaStylePrivate::ButtonTheme;
1594 partId = BP_PUSHBUTTON;
1595 if (!(option->state & State_Enabled))
1596 stateId = PBS_DISABLED;
1597 else if ((option->state & State_Sunken) || (option->state & State_On))
1598 stateId = PBS_PRESSED;
1599 else if (option->state & State_MouseOver)
1600 stateId = PBS_HOT;
1601 else
1602 stateId = PBS_NORMAL;
1603 break;
1604
1605 case PE_IndicatorRadioButton:
1606 if (QWindowsVistaAnimation *animate =
1607 qobject_cast<QWindowsVistaAnimation *>(d->animation(styleObject(option)))) {
1608 animate->paint(painter, option);
1609 return;
1610 } else {
1611 themeNumber = QWindowsVistaStylePrivate::ButtonTheme;
1612 partId = BP_RADIOBUTTON;
1613
1614 if (!(option->state & State_Enabled))
1615 stateId = RBS_UNCHECKEDDISABLED;
1616 else if (option->state & State_Sunken)
1617 stateId = RBS_UNCHECKEDPRESSED;
1618 else if (option->state & State_MouseOver)
1619 stateId = RBS_UNCHECKEDHOT;
1620 else
1621 stateId = RBS_UNCHECKEDNORMAL;
1622
1623 if (option->state & State_On)
1624 stateId += RBS_CHECKEDNORMAL-1;
1625 }
1626 break;
1627
1628 case PE_Frame:
1629 if (widget && widget->inherits("QComboBoxPrivateContainer")){
1630 QStyleOption copy = *option;
1631 copy.state |= State_Raised;
1632 proxy()->drawPrimitive(PE_PanelMenu, &copy, painter, widget);
1633 break;
1634 }
1635#if QT_CONFIG(accessibility)
1636 if (QStyleHelper::isInstanceOf(option->styleObject, QAccessible::EditableText)
1637 || QStyleHelper::isInstanceOf(option->styleObject, QAccessible::StaticText) ||
1638#else
1639 if (
1640#endif
1641 (widget && widget->inherits("QTextEdit"))) {
1642 painter->save();
1643 int stateId = ETS_NORMAL;
1644 if (!(state & State_Enabled))
1645 stateId = ETS_DISABLED;
1646 else if (state & State_ReadOnly)
1647 stateId = ETS_READONLY;
1648 else if (state & State_HasFocus)
1649 stateId = ETS_SELECTED;
1650 QWindowsThemeData theme(widget, painter,
1651 QWindowsVistaStylePrivate::EditTheme,
1652 EP_EDITBORDER_HVSCROLL, stateId, option->rect);
1653 // Since EP_EDITBORDER_HVSCROLL does not us borderfill, theme.noContent cannot be used for clipping
1654 int borderSize = 1;
1655 GetThemeInt(theme.handle(), theme.partId, theme.stateId, TMT_BORDERSIZE, &borderSize);
1656 QRegion clipRegion = option->rect;
1657 QRegion content = option->rect.adjusted(borderSize, borderSize, -borderSize, -borderSize);
1658 clipRegion ^= content;
1659 painter->setClipRegion(clipRegion);
1660 d->drawBackground(theme);
1661 painter->restore();
1662 return;
1663 } else {
1664 if (option->state & State_Raised)
1665 return;
1666
1667 themeNumber = QWindowsVistaStylePrivate::ListViewTheme;
1668 partId = LVP_LISTGROUP;
1669 QWindowsThemeData theme(widget, nullptr, themeNumber, partId);
1670
1671 if (!(option->state & State_Enabled))
1672 stateId = ETS_DISABLED;
1673 else
1674 stateId = ETS_NORMAL;
1675
1676 int fillType;
1677
1678 if (GetThemeEnumValue(theme.handle(), partId, stateId, TMT_BGTYPE, &fillType) == S_OK) {
1679 if (fillType == BT_BORDERFILL) {
1680 COLORREF bcRef;
1681 GetThemeColor(theme.handle(), partId, stateId, TMT_BORDERCOLOR, &bcRef);
1682 QColor bordercolor(qRgb(GetRValue(bcRef), GetGValue(bcRef), GetBValue(bcRef)));
1683 QPen oldPen = painter->pen();
1684
1685 // Inner white border
1686 painter->setPen(QPen(option->palette.base().color(), 0));
1687 const qreal dpi = QStyleHelper::dpi(option);
1688 const auto topLevelAdjustment = QStyleHelper::dpiScaled(0.5, dpi);
1689 const auto bottomRightAdjustment = QStyleHelper::dpiScaled(-1, dpi);
1690 painter->drawRect(QRectF(option->rect).adjusted(topLevelAdjustment, topLevelAdjustment,
1691 bottomRightAdjustment, bottomRightAdjustment));
1692 // Outer dark border
1693 painter->setPen(QPen(bordercolor, 0));
1694 painter->drawRect(QRectF(option->rect).adjusted(0, 0, -topLevelAdjustment, -topLevelAdjustment));
1695 painter->setPen(oldPen);
1696 }
1697
1698 if (fillType == BT_BORDERFILL || fillType == BT_NONE)
1699 return;
1700 }
1701 }
1702 break;
1703
1704 case PE_FrameMenu: {
1705 int stateId = option->state & State_Active ? MB_ACTIVE : MB_INACTIVE;
1706 QWindowsThemeData theme(widget, painter,
1707 QWindowsVistaStylePrivate::MenuTheme,
1708 MENU_POPUPBORDERS, stateId, option->rect);
1709 d->drawBackground(theme);
1710 return;
1711 }
1712
1713 case PE_PanelMenu:
1714 if (widget && widget->inherits("QComboBoxPrivateContainer")){
1715 //fill combobox popup background
1716 QWindowsThemeData popupbackgroundTheme(widget, painter, QWindowsVistaStylePrivate::MenuTheme,
1717 MENU_POPUPBACKGROUND, stateId, option->rect);
1718 d->drawBackground(popupbackgroundTheme);
1719 return;
1720 }
1721 break;
1722
1723 case PE_PanelMenuBar:
1724 break;
1725
1726#if QT_CONFIG(dockwidget)
1727 case PE_IndicatorDockWidgetResizeHandle:
1728 return;
1729
1730 case PE_FrameDockWidget:
1731 if (const auto *frm = qstyleoption_cast<const QStyleOptionFrame *>(option)) {
1732 themeNumber = QWindowsVistaStylePrivate::WindowTheme;
1733 if (option->state & State_Active)
1734 stateId = FS_ACTIVE;
1735 else
1736 stateId = FS_INACTIVE;
1737
1738 int fwidth = proxy()->pixelMetric(PM_DockWidgetFrameWidth, frm, widget);
1739
1740 QWindowsThemeData theme(widget, painter, themeNumber, 0, stateId);
1741
1742 if (!theme.isValid())
1743 break;
1744
1745 theme.rect = QRect(frm->rect.x(), frm->rect.y(), frm->rect.x()+fwidth, frm->rect.height()-fwidth);
1746 theme.partId = WP_SMALLFRAMELEFT;
1747 d->drawBackground(theme);
1748 theme.rect = QRect(frm->rect.width()-fwidth, frm->rect.y(), fwidth, frm->rect.height()-fwidth);
1749 theme.partId = WP_SMALLFRAMERIGHT;
1750 d->drawBackground(theme);
1751 theme.rect = QRect(frm->rect.x(), frm->rect.bottom()-fwidth+1, frm->rect.width(), fwidth);
1752 theme.partId = WP_SMALLFRAMEBOTTOM;
1753 d->drawBackground(theme);
1754 return;
1755 }
1756 break;
1757#endif // QT_CONFIG(dockwidget)
1758
1759 case PE_FrameTabWidget:
1760#if QT_CONFIG(tabwidget)
1761 if (const auto *tab = qstyleoption_cast<const QStyleOptionTabWidgetFrame *>(option)) {
1762 themeNumber = QWindowsVistaStylePrivate::TabTheme;
1763 partId = TABP_PANE;
1764
1765 if (widget) {
1766 bool useGradient = true;
1767 const int maxlength = 256;
1768 wchar_t themeFileName[maxlength];
1769 wchar_t themeColor[maxlength];
1770 // Due to a a scaling issue with the XP Silver theme, tab gradients are not used with it
1771 if (GetCurrentThemeName(themeFileName, maxlength, themeColor, maxlength, nullptr, 0) == S_OK) {
1772 wchar_t *offset = nullptr;
1773 if ((offset = wcsrchr(themeFileName, QChar(QLatin1Char('\\')).unicode())) != nullptr) {
1774 offset++;
1775 if (!lstrcmp(offset, L"Luna.msstyles") && !lstrcmp(offset, L"Metallic"))
1776 useGradient = false;
1777 }
1778 }
1779 // This should work, but currently there's an error in the ::drawBackgroundDirectly()
1780 // code, when using the HDC directly..
1781 if (useGradient) {
1782 QStyleOptionTabWidgetFrame frameOpt = *tab;
1783 frameOpt.rect = widget->rect();
1784 QRect contentsRect = subElementRect(SE_TabWidgetTabContents, &frameOpt, widget);
1785 QRegion reg = option->rect;
1786 reg -= contentsRect;
1787 painter->setClipRegion(reg);
1788 QWindowsThemeData theme(widget, painter, themeNumber, partId, stateId, rect);
1789 theme.mirrorHorizontally = hMirrored;
1790 theme.mirrorVertically = vMirrored;
1791 d->drawBackground(theme);
1792 painter->setClipRect(contentsRect);
1793 partId = TABP_BODY;
1794 }
1795 }
1796 switch (tab->shape) {
1797 case QTabBar::RoundedNorth:
1798 case QTabBar::TriangularNorth:
1799 break;
1800 case QTabBar::RoundedSouth:
1801 case QTabBar::TriangularSouth:
1802 vMirrored = true;
1803 break;
1804 case QTabBar::RoundedEast:
1805 case QTabBar::TriangularEast:
1806 rotate = 90;
1807 break;
1808 case QTabBar::RoundedWest:
1809 case QTabBar::TriangularWest:
1810 rotate = 90;
1811 hMirrored = true;
1812 break;
1813 default:
1814 break;
1815 }
1816 }
1817 break;
1818#endif // QT_CONFIG(tabwidget)
1819 case PE_FrameStatusBarItem:
1820 themeNumber = QWindowsVistaStylePrivate::StatusTheme;
1821 partId = SP_PANE;
1822 break;
1823
1824 case PE_FrameWindow:
1825 if (const auto *frm = qstyleoption_cast<const QStyleOptionFrame *>(option)) {
1826 themeNumber = QWindowsVistaStylePrivate::WindowTheme;
1827 if (option->state & State_Active)
1828 stateId = FS_ACTIVE;
1829 else
1830 stateId = FS_INACTIVE;
1831
1832 int fwidth = int((frm->lineWidth + frm->midLineWidth) / QWindowsStylePrivate::nativeMetricScaleFactor(widget));
1833
1834 QWindowsThemeData theme(widget, painter, themeNumber, 0, stateId);
1835 if (!theme.isValid())
1836 break;
1837
1838 // May fail due to too-large buffers for large widgets, fall back to Windows style.
1839 theme.rect = QRect(option->rect.x(), option->rect.y()+fwidth, option->rect.x()+fwidth, option->rect.height()-fwidth);
1840 theme.partId = WP_FRAMELEFT;
1841 if (!d->drawBackground(theme)) {
1842 QWindowsStyle::drawPrimitive(element, option, painter, widget);
1843 return;
1844 }
1845 theme.rect = QRect(option->rect.width()-fwidth, option->rect.y()+fwidth, fwidth, option->rect.height()-fwidth);
1846 theme.partId = WP_FRAMERIGHT;
1847 if (!d->drawBackground(theme)) {
1848 QWindowsStyle::drawPrimitive(element, option, painter, widget);
1849 return;
1850 }
1851 theme.rect = QRect(option->rect.x(), option->rect.height()-fwidth, option->rect.width(), fwidth);
1852 theme.partId = WP_FRAMEBOTTOM;
1853 if (!d->drawBackground(theme)) {
1854 QWindowsStyle::drawPrimitive(element, option, painter, widget);
1855 return;
1856 }
1857 theme.rect = QRect(option->rect.x(), option->rect.y(), option->rect.width(), option->rect.y()+fwidth);
1858 theme.partId = WP_CAPTION;
1859 if (!d->drawBackground(theme))
1860 QWindowsStyle::drawPrimitive(element, option, painter, widget);
1861 return;
1862 }
1863 break;
1864
1865 case PE_PanelLineEdit:
1866 if (const auto *panel = qstyleoption_cast<const QStyleOptionFrame *>(option)) {
1867 bool isEnabled = state & State_Enabled;
1868 if (QWindowsVistaStylePrivate::isLineEditBaseColorSet(option, widget)) {
1869 painter->fillRect(panel->rect, panel->palette.brush(QPalette::Base));
1870 } else {
1871 int partId = EP_BACKGROUND;
1872 int stateId = EBS_NORMAL;
1873 if (!isEnabled)
1874 stateId = EBS_DISABLED;
1875 else if (option->state & State_ReadOnly)
1876 stateId = EBS_READONLY;
1877 else if (option->state & State_MouseOver)
1878 stateId = EBS_HOT;
1879
1880 QWindowsThemeData theme(nullptr, painter, QWindowsVistaStylePrivate::EditTheme,
1881 partId, stateId, rect);
1882 if (!theme.isValid()) {
1883 QWindowsStyle::drawPrimitive(element, option, painter, widget);
1884 return;
1885 }
1886 int bgType;
1887 GetThemeEnumValue(theme.handle(), partId, stateId, TMT_BGTYPE, &bgType);
1888 if (bgType == BT_IMAGEFILE) {
1889 d->drawBackground(theme);
1890 } else {
1891 QBrush fillColor = option->palette.brush(QPalette::Base);
1892 if (!isEnabled) {
1893 PROPERTYORIGIN origin = PO_NOTFOUND;
1894 GetThemePropertyOrigin(theme.handle(), theme.partId, theme.stateId, TMT_FILLCOLOR, &origin);
1895 // Use only if the fill property comes from our part
1896 if ((origin == PO_PART || origin == PO_STATE)) {
1897 COLORREF bgRef;
1898 GetThemeColor(theme.handle(), partId, stateId, TMT_FILLCOLOR, &bgRef);
1899 fillColor = QBrush(qRgb(GetRValue(bgRef), GetGValue(bgRef), GetBValue(bgRef)));
1900 }
1901 }
1902 painter->fillRect(option->rect, fillColor);
1903 }
1904 }
1905 if (panel->lineWidth > 0)
1906 proxy()->drawPrimitive(PE_FrameLineEdit, panel, painter, widget);
1907 }
1908 return;
1909
1910 case PE_IndicatorButtonDropDown:
1911 themeNumber = QWindowsVistaStylePrivate::ToolBarTheme;
1912 partId = TP_SPLITBUTTONDROPDOWN;
1913 if (!(option->state & State_Enabled))
1914 stateId = TS_DISABLED;
1915 else if (option->state & State_Sunken)
1916 stateId = TS_PRESSED;
1917 else if (option->state & State_MouseOver)
1918 stateId = option->state & State_On ? TS_HOTCHECKED : TS_HOT;
1919 else if (option->state & State_On)
1920 stateId = TS_CHECKED;
1921 else if (!(option->state & State_AutoRaise))
1922 stateId = TS_HOT;
1923 else
1924 stateId = TS_NORMAL;
1925 if (option->direction == Qt::RightToLeft)
1926 hMirrored = true;
1927 break;
1928
1929 case PE_FrameLineEdit:
1930 if (QWindowsVistaAnimation *animate = qobject_cast<QWindowsVistaAnimation *>(d->animation(styleObject(option)))) {
1931 animate->paint(painter, option);
1932 } else {
1933 if (QWindowsVistaStylePrivate::isItemViewDelegateLineEdit(widget)) {
1934 // we try to check if this lineedit is a delegate on a QAbstractItemView-derived class.
1935 QPen oldPen = painter->pen();
1936 // Inner white border
1937 painter->setPen(option->palette.base().color());
1938 painter->drawRect(option->rect.adjusted(1, 1, -2, -2));
1939 // Outer dark border
1940 painter->setPen(option->palette.shadow().color());
1941 painter->drawRect(option->rect.adjusted(0, 0, -1, -1));
1942 painter->setPen(oldPen);
1943 return;
1944 }
1945 int stateId = ETS_NORMAL;
1946 if (!(state & State_Enabled))
1947 stateId = ETS_DISABLED;
1948 else if (state & State_ReadOnly)
1949 stateId = ETS_READONLY;
1950 else if (state & State_HasFocus)
1951 stateId = ETS_SELECTED;
1952 else if (state & State_MouseOver)
1953 stateId = ETS_HOT;
1954 QWindowsThemeData theme(widget, painter,
1955 QWindowsVistaStylePrivate::EditTheme,
1956 EP_EDITBORDER_NOSCROLL, stateId, option->rect);
1957 theme.noContent = true;
1958 painter->save();
1959 QRegion clipRegion = option->rect;
1960 clipRegion -= option->rect.adjusted(2, 2, -2, -2);
1961 painter->setClipRegion(clipRegion);
1962 d->drawBackground(theme);
1963 painter->restore();
1964 }
1965 return;
1966
1967 case PE_FrameGroupBox:
1968 themeNumber = QWindowsVistaStylePrivate::ButtonTheme;
1969 partId = BP_GROUPBOX;
1970 if (!(option->state & State_Enabled))
1971 stateId = GBS_DISABLED;
1972 else
1973 stateId = GBS_NORMAL;
1974 if (const auto *frame = qstyleoption_cast<const QStyleOptionFrame *>(option)) {
1975 if (frame->features & QStyleOptionFrame::Flat) {
1976 // Windows XP does not have a theme part for a flat GroupBox, paint it with the windows style
1977 QRect fr = frame->rect;
1978 QPoint p1(fr.x(), fr.y() + 1);
1979 QPoint p2(fr.x() + fr.width(), p1.y() + 1);
1980 rect = QRect(p1, p2);
1981 themeNumber = -1;
1982 }
1983 }
1984 break;
1985
1986 case PE_IndicatorToolBarHandle: {
1987 QWindowsThemeData theme;
1988 QRect rect;
1989 if (option->state & State_Horizontal) {
1990 theme = QWindowsThemeData(widget, painter,
1991 QWindowsVistaStylePrivate::RebarTheme,
1992 RP_GRIPPER, ETS_NORMAL, option->rect.adjusted(0, 1, -2, -2));
1993 rect = option->rect.adjusted(0, 1, 0, -2);
1994 rect.setWidth(4);
1995 } else {
1996 theme = QWindowsThemeData(widget, painter, QWindowsVistaStylePrivate::RebarTheme,
1997 RP_GRIPPERVERT, ETS_NORMAL, option->rect.adjusted(0, 1, -2, -2));
1998 rect = option->rect.adjusted(1, 0, -1, 0);
1999 rect.setHeight(4);
2000 }
2001 theme.rect = rect;
2002 d->drawBackground(theme);
2003 return;
2004 }
2005
2006 case PE_IndicatorToolBarSeparator: {
2007 QPen pen = painter->pen();
2008 int margin = 3;
2009 painter->setPen(option->palette.window().color().darker(114));
2010 if (option->state & State_Horizontal) {
2011 int x1 = option->rect.center().x();
2012 painter->drawLine(QPoint(x1, option->rect.top() + margin), QPoint(x1, option->rect.bottom() - margin));
2013 } else {
2014 int y1 = option->rect.center().y();
2015 painter->drawLine(QPoint(option->rect.left() + margin, y1), QPoint(option->rect.right() - margin, y1));
2016 }
2017 painter->setPen(pen);
2018 return;
2019 }
2020
2021 case PE_PanelTipLabel: {
2022 QWindowsThemeData theme(widget, painter,
2023 QWindowsVistaStylePrivate::ToolTipTheme,
2024 TTP_STANDARD, TTSS_NORMAL, option->rect);
2025 d->drawBackground(theme);
2026 return;
2027 }
2028
2029 case PE_FrameTabBarBase:
2030#if QT_CONFIG(tabbar)
2031 if (const auto *tbb = qstyleoption_cast<const QStyleOptionTabBarBase *>(option)) {
2032 painter->save();
2033 switch (tbb->shape) {
2034 case QTabBar::RoundedNorth:
2035 painter->setPen(QPen(tbb->palette.dark(), 0));
2036 painter->drawLine(tbb->rect.topLeft(), tbb->rect.topRight());
2037 break;
2038 case QTabBar::RoundedWest:
2039 painter->setPen(QPen(tbb->palette.dark(), 0));
2040 painter->drawLine(tbb->rect.left(), tbb->rect.top(), tbb->rect.left(), tbb->rect.bottom());
2041 break;
2042 case QTabBar::RoundedSouth:
2043 painter->setPen(QPen(tbb->palette.dark(), 0));
2044 painter->drawLine(tbb->rect.left(), tbb->rect.top(),
2045 tbb->rect.right(), tbb->rect.top());
2046 break;
2047 case QTabBar::RoundedEast:
2048 painter->setPen(QPen(tbb->palette.dark(), 0));
2049 painter->drawLine(tbb->rect.topLeft(), tbb->rect.bottomLeft());
2050 break;
2051 case QTabBar::TriangularNorth:
2052 case QTabBar::TriangularEast:
2053 case QTabBar::TriangularWest:
2054 case QTabBar::TriangularSouth:
2055 painter->restore();
2056 QWindowsStyle::drawPrimitive(element, option, painter, widget);
2057 return;
2058 }
2059 painter->restore();
2060 }
2061#endif // QT_CONFIG(tabbar)
2062 return;
2063
2064 case PE_Widget: {
2065#if QT_CONFIG(dialogbuttonbox)
2066 const QDialogButtonBox *buttonBox = nullptr;
2067 if (qobject_cast<const QMessageBox *> (widget))
2068 buttonBox = widget->findChild<const QDialogButtonBox *>(QLatin1String("qt_msgbox_buttonbox"));
2069 if (buttonBox) {
2070 //draw white panel part
2071 QWindowsThemeData theme(widget, painter,
2072 QWindowsVistaStylePrivate::TaskDialogTheme,
2073 TDLG_PRIMARYPANEL, 0, option->rect);
2074 QRect toprect = option->rect;
2075 toprect.setBottom(buttonBox->geometry().top());
2076 theme.rect = toprect;
2077 d->drawBackground(theme);
2078
2079 //draw bottom panel part
2080 QRect buttonRect = option->rect;
2081 buttonRect.setTop(buttonBox->geometry().top());
2082 theme.rect = buttonRect;
2083 theme.partId = TDLG_SECONDARYPANEL;
2084 d->drawBackground(theme);
2085 }
2086#endif
2087 return;
2088 }
2089
2090 case PE_PanelItemViewItem: {
2091 const QStyleOptionViewItem *vopt;
2092 bool newStyle = true;
2093 QAbstractItemView::SelectionBehavior selectionBehavior = QAbstractItemView::SelectRows;
2094 QAbstractItemView::SelectionMode selectionMode = QAbstractItemView::NoSelection;
2095 if (const QAbstractItemView *view = qobject_cast<const QAbstractItemView *>(widget)) {
2096 newStyle = !qobject_cast<const QTableView*>(view);
2097 selectionBehavior = view->selectionBehavior();
2098 selectionMode = view->selectionMode();
2099#if QT_CONFIG(accessibility)
2100 } else if (!widget) {
2101 newStyle = !QStyleHelper::hasAncestor(option->styleObject, QAccessible::MenuItem) ;
2102#endif
2103 }
2104
2105 if (newStyle && (vopt = qstyleoption_cast<const QStyleOptionViewItem *>(option))) {
2106 bool selected = vopt->state & QStyle::State_Selected;
2107 const bool hover = selectionMode != QAbstractItemView::NoSelection && (vopt->state & QStyle::State_MouseOver);
2108 bool active = vopt->state & QStyle::State_Active;
2109
2110 if (vopt->features & QStyleOptionViewItem::Alternate)
2111 painter->fillRect(vopt->rect, vopt->palette.alternateBase());
2112
2113 QPalette::ColorGroup cg = vopt->state & QStyle::State_Enabled
2114 ? QPalette::Normal : QPalette::Disabled;
2115 if (cg == QPalette::Normal && !(vopt->state & QStyle::State_Active))
2116 cg = QPalette::Inactive;
2117
2118 QRect itemRect = subElementRect(QStyle::SE_ItemViewItemFocusRect, option, widget).adjusted(-1, 0, 1, 0);
2119 itemRect.setTop(vopt->rect.top());
2120 itemRect.setBottom(vopt->rect.bottom());
2121
2122 QSize sectionSize = itemRect.size();
2123 if (vopt->showDecorationSelected)
2124 sectionSize = vopt->rect.size();
2125
2126 if (selectionBehavior == QAbstractItemView::SelectRows)
2127 sectionSize.setWidth(vopt->rect.width());
2128 QPixmap pixmap;
2129
2130 if (vopt->backgroundBrush.style() != Qt::NoBrush) {
2131 QPainterStateGuard psg(painter);
2132 painter->setBrushOrigin(vopt->rect.topLeft());
2133 painter->fillRect(vopt->rect, vopt->backgroundBrush);
2134 }
2135
2136 if (hover || selected) {
2137 if (sectionSize.width() > 0 && sectionSize.height() > 0) {
2138 QString key = QStringLiteral(u"qvdelegate-%1-%2-%3-%4-%5").arg(sectionSize.width())
2139 .arg(sectionSize.height()).arg(selected).arg(active).arg(hover);
2140 if (!QPixmapCache::find(key, &pixmap)) {
2141 pixmap = QPixmap(sectionSize);
2142 pixmap.fill(Qt::transparent);
2143
2144 int state;
2145 if (selected && hover)
2146 state = LISS_HOTSELECTED;
2147 else if (selected && !active)
2148 state = LISS_SELECTEDNOTFOCUS;
2149 else if (selected)
2150 state = LISS_SELECTED;
2151 else
2152 state = LISS_HOT;
2153
2154 QPainter pixmapPainter(&pixmap);
2155
2156 QWindowsThemeData theme(widget, &pixmapPainter,
2157 QWindowsVistaStylePrivate::VistaTreeViewTheme,
2158 LVP_LISTITEM, state, QRect(0, 0, sectionSize.width(), sectionSize.height()));
2159
2160 if (!theme.isValid())
2161 break;
2162
2163 d->drawBackground(theme);
2164 QPixmapCache::insert(key, pixmap);
2165 }
2166 }
2167
2168 if (vopt->showDecorationSelected) {
2169 const int frame = 2; //Assumes a 2 pixel pixmap border
2170 QRect srcRect = QRect(0, 0, sectionSize.width(), sectionSize.height());
2171 QRect pixmapRect = vopt->rect;
2172 bool reverse = vopt->direction == Qt::RightToLeft;
2173 bool leftSection = vopt->viewItemPosition == QStyleOptionViewItem::Beginning;
2174 bool rightSection = vopt->viewItemPosition == QStyleOptionViewItem::End;
2175 if (vopt->viewItemPosition == QStyleOptionViewItem::OnlyOne
2176 || vopt->viewItemPosition == QStyleOptionViewItem::Invalid)
2177 painter->drawPixmap(pixmapRect.topLeft(), pixmap);
2178 else if (reverse ? rightSection : leftSection){
2179 painter->drawPixmap(QRect(pixmapRect.topLeft(),
2180 QSize(frame, pixmapRect.height())), pixmap,
2181 QRect(QPoint(0, 0), QSize(frame, pixmapRect.height())));
2182 painter->drawPixmap(pixmapRect.adjusted(frame, 0, 0, 0),
2183 pixmap, srcRect.adjusted(frame, 0, -frame, 0));
2184 } else if (reverse ? leftSection : rightSection) {
2185 painter->drawPixmap(QRect(pixmapRect.topRight() - QPoint(frame - 1, 0),
2186 QSize(frame, pixmapRect.height())), pixmap,
2187 QRect(QPoint(pixmapRect.width() - frame, 0),
2188 QSize(frame, pixmapRect.height())));
2189 painter->drawPixmap(pixmapRect.adjusted(0, 0, -frame, 0),
2190 pixmap, srcRect.adjusted(frame, 0, -frame, 0));
2191 } else if (vopt->viewItemPosition == QStyleOptionViewItem::Middle)
2192 painter->drawPixmap(pixmapRect, pixmap,
2193 srcRect.adjusted(frame, 0, -frame, 0));
2194 } else {
2195 if (vopt->text.isEmpty() && vopt->icon.isNull())
2196 break;
2197 painter->drawPixmap(itemRect.topLeft(), pixmap);
2198 }
2199 }
2200 return;
2201 }
2202 break;
2203 }
2204
2205 default:
2206 break;
2207 }
2208
2209 QWindowsThemeData theme(widget, painter, themeNumber, partId, stateId, rect);
2210
2211 if (!theme.isValid()) {
2212 QWindowsStyle::drawPrimitive(element, option, painter, widget);
2213 return;
2214 }
2215
2216 theme.mirrorHorizontally = hMirrored;
2217 theme.mirrorVertically = vMirrored;
2218 theme.noBorder = noBorder;
2219 theme.noContent = noContent;
2220 theme.rotate = rotate;
2221
2222 d->drawBackground(theme);
2223}
2224
2225/*! \internal */
2226int QWindowsVistaStyle::styleHint(StyleHint hint, const QStyleOption *option, const QWidget *widget,
2227 QStyleHintReturn *returnData) const
2228{
2229 QWindowsVistaStylePrivate *d = const_cast<QWindowsVistaStylePrivate*>(d_func());
2230
2231 int ret = 0;
2232 switch (hint) {
2233 case SH_EtchDisabledText:
2234 ret = (qobject_cast<const QLabel*>(widget) != 0);
2235 break;
2236
2237 case SH_SpinControls_DisableOnBounds:
2238 ret = 0;
2239 break;
2240
2241 case SH_TitleBar_AutoRaise:
2242 case SH_TitleBar_NoBorder:
2243 ret = 1;
2244 break;
2245
2246 case SH_GroupBox_TextLabelColor:
2247 if (!widget || widget->isEnabled())
2248 ret = d->groupBoxTextColor;
2249 else
2250 ret = d->groupBoxTextColorDisabled;
2251 break;
2252
2253 case SH_WindowFrame_Mask: {
2254 ret = 1;
2255 auto *mask = qstyleoption_cast<QStyleHintReturnMask *>(returnData);
2256 const auto *titlebar = qstyleoption_cast<const QStyleOptionTitleBar *>(option);
2257 if (mask && titlebar) {
2258 // Note certain themes will not return the whole window frame but only the titlebar part when
2259 // queried This function needs to return the entire window mask, hence we will only fetch the mask for the
2260 // titlebar itself and add the remaining part of the window rect at the bottom.
2261 int tbHeight = proxy()->pixelMetric(PM_TitleBarHeight, option, widget);
2262 QRect titleBarRect = option->rect;
2263 titleBarRect.setHeight(tbHeight);
2264 QWindowsThemeData themeData;
2265 if (titlebar->titleBarState & Qt::WindowMinimized) {
2266 themeData = QWindowsThemeData(widget, nullptr,
2267 QWindowsVistaStylePrivate::WindowTheme,
2268 WP_MINCAPTION, CS_ACTIVE, titleBarRect);
2269 } else
2270 themeData = QWindowsThemeData(widget, nullptr,
2271 QWindowsVistaStylePrivate::WindowTheme,
2272 WP_CAPTION, CS_ACTIVE, titleBarRect);
2273 mask->region = d->region(themeData) +
2274 QRect(0, tbHeight, option->rect.width(), option->rect.height() - tbHeight);
2275 }
2276 break;
2277 }
2278
2279#if QT_CONFIG(rubberband)
2280 case SH_RubberBand_Mask:
2281 if (qstyleoption_cast<const QStyleOptionRubberBand *>(option))
2282 ret = 0;
2283 break;
2284#endif // QT_CONFIG(rubberband)
2285
2286 case SH_MessageBox_CenterButtons:
2287 ret = false;
2288 break;
2289
2290 case SH_ToolTip_Mask:
2291 if (option) {
2292 if (QStyleHintReturnMask *mask = qstyleoption_cast<QStyleHintReturnMask*>(returnData)) {
2293 ret = true;
2294 QWindowsThemeData themeData(widget, nullptr,
2295 QWindowsVistaStylePrivate::ToolTipTheme,
2296 TTP_STANDARD, TTSS_NORMAL, option->rect);
2297 mask->region = d->region(themeData);
2298 }
2299 }
2300 break;
2301
2302 case SH_Table_GridLineColor:
2303 if (option)
2304 ret = int(option->palette.color(QPalette::Base).darker(118).rgba());
2305 else
2306 ret = -1;
2307 break;
2308
2309 case SH_Header_ArrowAlignment:
2310 ret = Qt::AlignTop | Qt::AlignHCenter;
2311 break;
2312
2313 case SH_ItemView_DrawDelegateFrame:
2314 ret = 1;
2315 break;
2316
2317 case SH_ComboBox_ListMouseTracking_Current:
2318 ret = 0;
2319 break;
2320
2321 case SH_ComboBox_ListMouseTracking_Active:
2322 ret = 1;
2323 break;
2324
2325 default:
2326 ret = QWindowsStyle::styleHint(hint, option, widget, returnData);
2327 break;
2328 }
2329
2330 return ret;
2331}
2332
2333
2334/*!
2335 \internal
2336
2337 see drawPrimitive for comments on the animation support
2338 */
2339void QWindowsVistaStyle::drawControl(ControlElement element, const QStyleOption *option,
2340 QPainter *painter, const QWidget *widget) const
2341{
2342 if (!QWindowsVistaStylePrivate::useVista()) {
2343 QWindowsStyle::drawControl(element, option, painter, widget);
2344 return;
2345 }
2346
2347 QWindowsVistaStylePrivate *d = const_cast<QWindowsVistaStylePrivate*>(d_func());
2348
2349 bool selected = option->state & State_Selected;
2350 bool pressed = option->state & State_Sunken;
2351 bool disabled = !(option->state & State_Enabled);
2352
2353 int state = option->state;
2354 int themeNumber = -1;
2355
2356 QRect rect(option->rect);
2357 State flags = option->state;
2358 int partId = 0;
2359 int stateId = 0;
2360
2361 if (d->transitionsEnabled() && canAnimate(option)) {
2362 if (element == CE_PushButtonBevel) {
2363 QRect oldRect;
2364 QRect newRect;
2365
2366 QObject *styleObject = option->styleObject;
2367
2368 int oldState = styleObject->property("_q_stylestate").toInt();
2369 oldRect = styleObject->property("_q_stylerect").toRect();
2370 newRect = option->rect;
2371 styleObject->setProperty("_q_stylestate", int(option->state));
2372 styleObject->setProperty("_q_stylerect", option->rect);
2373
2374 bool wasDefault = false;
2375 bool isDefault = false;
2376 if (const QStyleOptionButton *button = qstyleoption_cast<const QStyleOptionButton *>(option)) {
2377 wasDefault = styleObject->property("_q_isdefault").toBool();
2378 isDefault = button->features & QStyleOptionButton::DefaultButton;
2379 styleObject->setProperty("_q_isdefault", isDefault);
2380 }
2381
2382 bool doTransition = ((state & State_Sunken) != (oldState & State_Sunken) ||
2383 (state & State_On) != (oldState & State_On) ||
2384 (state & State_MouseOver) != (oldState & State_MouseOver));
2385
2386 if (oldRect != newRect || (wasDefault && !isDefault)) {
2387 doTransition = false;
2388 d->stopAnimation(styleObject);
2389 }
2390
2391 if (doTransition) {
2392 styleObject->setProperty("_q_no_animation", true);
2393
2395 QWindowsVistaAnimation *anim = qobject_cast<QWindowsVistaAnimation *>(d->animation(styleObject));
2396 QStyleOption *styleOption = clonedAnimationStyleOption(option);
2397 styleOption->state = QStyle::State(oldState);
2398
2399 QImage startImage = createAnimationBuffer(option, widget);
2400 QPainter startPainter(&startImage);
2401
2402 // Use current state of existing animation if already one is running
2403 if (!anim) {
2404 proxy()->drawControl(element, styleOption, &startPainter, widget);
2405 } else {
2406 anim->paint(&startPainter, styleOption);
2407 d->stopAnimation(styleObject);
2408 }
2409
2410 t->setStartImage(startImage);
2411 QImage endImage = createAnimationBuffer(option, widget);
2412 QPainter endPainter(&endImage);
2413 styleOption->state = option->state;
2414 proxy()->drawControl(element, styleOption, &endPainter, widget);
2415 t->setEndImage(endImage);
2416
2417
2418 DWORD duration = 0;
2419 const HTHEME theme = OpenThemeData(nullptr, L"Button");
2420
2421 int fromState = buttonStateId(oldState, BP_PUSHBUTTON);
2422 int toState = buttonStateId(option->state, BP_PUSHBUTTON);
2423 if (GetThemeTransitionDuration(theme, BP_PUSHBUTTON, fromState, toState, TMT_TRANSITIONDURATIONS, &duration) == S_OK)
2424 t->setDuration(int(duration));
2425 else
2426 t->setDuration(0);
2427 t->setStartTime(d->animationTime());
2428 styleObject->setProperty("_q_no_animation", false);
2429
2430 deleteClonedAnimationStyleOption(styleOption);
2431 d->startAnimation(t);
2432 }
2433
2434 QWindowsVistaAnimation *anim = qobject_cast<QWindowsVistaAnimation *>(d->animation(styleObject));
2435 if (anim) {
2436 anim->paint(painter, option);
2437 return;
2438 }
2439
2440 }
2441 }
2442
2443 bool hMirrored = false;
2444 bool vMirrored = false;
2445 int rotate = 0;
2446
2447 switch (element) {
2448 case CE_PushButtonBevel:
2449 if (const QStyleOptionButton *btn = qstyleoption_cast<const QStyleOptionButton *>(option)) {
2450 themeNumber = QWindowsVistaStylePrivate::ButtonTheme;
2451 partId = BP_PUSHBUTTON;
2452 if (btn->features & QStyleOptionButton::CommandLinkButton)
2453 partId = BP_COMMANDLINK;
2454 bool justFlat = (btn->features & QStyleOptionButton::Flat) && !(flags & (State_On|State_Sunken));
2455 if (!(flags & State_Enabled) && !(btn->features & QStyleOptionButton::Flat))
2456 stateId = PBS_DISABLED;
2457 else if (justFlat)
2458 ;
2459 else if (flags & (State_Sunken | State_On))
2460 stateId = PBS_PRESSED;
2461 else if (flags & State_MouseOver)
2462 stateId = PBS_HOT;
2463 else if (btn->features & QStyleOptionButton::DefaultButton && (state & State_Active))
2464 stateId = PBS_DEFAULTED;
2465 else
2466 stateId = PBS_NORMAL;
2467
2468 if (!justFlat) {
2469
2470 if (d->transitionsEnabled() && (btn->features & QStyleOptionButton::DefaultButton) &&
2471 !(state & (State_Sunken | State_On)) && !(state & State_MouseOver) &&
2472 (state & State_Enabled) && (state & State_Active))
2473 {
2474 QWindowsVistaAnimation *anim = qobject_cast<QWindowsVistaAnimation *>(d->animation(styleObject(option)));
2475
2476 if (!anim) {
2477 QImage startImage = createAnimationBuffer(option, widget);
2478 QImage alternateImage = createAnimationBuffer(option, widget);
2479
2480 QWindowsVistaPulse *pulse = new QWindowsVistaPulse(styleObject(option));
2481
2482 QPainter startPainter(&startImage);
2483 stateId = PBS_DEFAULTED;
2484 QWindowsThemeData theme(widget, &startPainter, themeNumber, partId, stateId, rect);
2485 d->drawBackground(theme);
2486
2487 QPainter alternatePainter(&alternateImage);
2488 theme.stateId = PBS_DEFAULTED_ANIMATING;
2489 theme.painter = &alternatePainter;
2490 d->drawBackground(theme);
2491
2492 pulse->setStartImage(startImage);
2493 pulse->setEndImage(alternateImage);
2494 pulse->setStartTime(d->animationTime());
2495 pulse->setDuration(2000);
2496 d->startAnimation(pulse);
2497 anim = pulse;
2498 }
2499
2500 if (anim)
2501 anim->paint(painter, option);
2502 else {
2503 QWindowsThemeData theme(widget, painter, themeNumber, partId, stateId, rect);
2504 d->drawBackground(theme);
2505 }
2506 }
2507 else {
2508 QWindowsThemeData theme(widget, painter, themeNumber, partId, stateId, rect);
2509 d->drawBackground(theme);
2510 }
2511 }
2512
2513 if (btn->features & QStyleOptionButton::HasMenu) {
2514 int mbiw = 0, mbih = 0;
2515 QWindowsThemeData theme(widget, nullptr, QWindowsVistaStylePrivate::ToolBarTheme,
2516 TP_DROPDOWNBUTTON);
2517 if (theme.isValid()) {
2518 const QSizeF size = theme.size() * QStyleHelper::dpiScaled(1, option);
2519 if (!size.isEmpty()) {
2520 mbiw = qRound(size.width());
2521 mbih = qRound(size.height());
2522 }
2523 }
2524 QRect ir = subElementRect(SE_PushButtonContents, option, nullptr);
2525 QStyleOptionButton newBtn = *btn;
2526 newBtn.rect = QStyle::visualRect(option->direction, option->rect,
2527 QRect(ir.right() - mbiw - 2,
2528 option->rect.top() + (option->rect.height()/2) - (mbih/2),
2529 mbiw + 1, mbih + 1));
2530 proxy()->drawPrimitive(PE_IndicatorArrowDown, &newBtn, painter, widget);
2531 }
2532 }
2533 return;
2534
2535 case CE_SizeGrip: {
2536 themeNumber = QWindowsVistaStylePrivate::StatusTheme;
2537 partId = SP_GRIPPER;
2538 QWindowsThemeData theme(nullptr, painter, themeNumber, partId);
2539 QSize size = (theme.size() * QWindowsStylePrivate::nativeMetricScaleFactor(widget)).toSize();
2540 size.rheight()--;
2541 if (const auto *sg = qstyleoption_cast<const QStyleOptionSizeGrip *>(option)) {
2542 switch (sg->corner) {
2543 case Qt::BottomRightCorner:
2544 rect = QRect(QPoint(rect.right() - size.width(), rect.bottom() - size.height()), size);
2545 break;
2546 case Qt::BottomLeftCorner:
2547 rect = QRect(QPoint(rect.left() + 1, rect.bottom() - size.height()), size);
2548 hMirrored = true;
2549 break;
2550 case Qt::TopRightCorner:
2551 rect = QRect(QPoint(rect.right() - size.width(), rect.top() + 1), size);
2552 vMirrored = true;
2553 break;
2554 case Qt::TopLeftCorner:
2555 rect = QRect(rect.topLeft() + QPoint(1, 1), size);
2556 hMirrored = vMirrored = true;
2557 }
2558 }
2559 break;
2560 }
2561
2562 case CE_Splitter:
2563 painter->eraseRect(option->rect);
2564 return;
2565
2566 case CE_TabBarTab:
2567#if QT_CONFIG(tabwidget)
2568 if (const auto *tab = qstyleoption_cast<const QStyleOptionTab *>(option))
2569 stateId = tab->state & State_Enabled ? TIS_NORMAL : TIS_DISABLED;
2570#endif // QT_CONFIG(tabwidget)
2571 break;
2572
2573 case CE_TabBarTabShape:
2574#if QT_CONFIG(tabwidget)
2575 if (const auto *tab = qstyleoption_cast<const QStyleOptionTab *>(option)) {
2576 themeNumber = QWindowsVistaStylePrivate::TabTheme;
2577 const bool isDisabled = !(tab->state & State_Enabled);
2578 const bool hasFocus = tab->state & State_HasFocus;
2579 const bool isHot = tab->state & State_MouseOver;
2580 const bool selected = tab->state & State_Selected;
2581 bool lastTab = tab->position == QStyleOptionTab::End;
2582 bool firstTab = tab->position == QStyleOptionTab::Beginning;
2583 const bool onlyOne = tab->position == QStyleOptionTab::OnlyOneTab;
2584 const bool leftAligned = proxy()->styleHint(SH_TabBar_Alignment, tab, widget) == Qt::AlignLeft;
2585 const bool centerAligned = proxy()->styleHint(SH_TabBar_Alignment, tab, widget) == Qt::AlignCenter;
2586 const int borderThickness = proxy()->pixelMetric(PM_DefaultFrameWidth, option, widget);
2587 const int tabOverlap = proxy()->pixelMetric(PM_TabBarTabOverlap, option, widget);
2588
2589 if (isDisabled)
2590 stateId = TIS_DISABLED;
2591 else if (selected)
2592 stateId = TIS_SELECTED;
2593 else if (hasFocus)
2594 stateId = TIS_FOCUSED;
2595 else if (isHot)
2596 stateId = TIS_HOT;
2597 else
2598 stateId = TIS_NORMAL;
2599
2600 // Selecting proper part depending on position
2601 if (firstTab || onlyOne) {
2602 if (leftAligned)
2603 partId = TABP_TABITEMLEFTEDGE;
2604 else if (centerAligned)
2605 partId = TABP_TABITEM;
2606 else // rightAligned
2607 partId = TABP_TABITEMRIGHTEDGE;
2608 } else {
2609 partId = TABP_TABITEM;
2610 }
2611
2612 if (tab->direction == Qt::RightToLeft
2613 && (tab->shape == QTabBar::RoundedNorth || tab->shape == QTabBar::RoundedSouth)) {
2614 bool temp = firstTab;
2615 firstTab = lastTab;
2616 lastTab = temp;
2617 }
2618
2619 const bool begin = firstTab || onlyOne;
2620 const bool end = lastTab || onlyOne;
2621
2622 switch (tab->shape) {
2623 case QTabBar::RoundedNorth:
2624 if (selected)
2625 rect.adjust(begin ? 0 : -tabOverlap, 0, end ? 0 : tabOverlap, borderThickness);
2626 else
2627 rect.adjust(begin? tabOverlap : 0, tabOverlap, end ? -tabOverlap : 0, 0);
2628 break;
2629 case QTabBar::RoundedSouth:
2630 //vMirrored = true;
2631 rotate = 180; // Not 100% correct, but works
2632 if (selected)
2633 rect.adjust(begin ? 0 : -tabOverlap , -borderThickness, end ? 0 : tabOverlap, 0);
2634 else
2635 rect.adjust(begin ? tabOverlap : 0, 0, end ? -tabOverlap : 0 , -tabOverlap);
2636 break;
2637 case QTabBar::RoundedEast:
2638 rotate = 90;
2639 if (selected)
2640 rect.adjust(-borderThickness, begin ? 0 : -tabOverlap, 0, end ? 0 : tabOverlap);
2641 else
2642 rect.adjust(0, begin ? tabOverlap : 0, -tabOverlap, end ? -tabOverlap : 0);
2643 break;
2644 case QTabBar::RoundedWest:
2645 hMirrored = true;
2646 rotate = 90;
2647 if (selected)
2648 rect.adjust(0, begin ? 0 : -tabOverlap, borderThickness, end ? 0 : tabOverlap);
2649 else
2650 rect.adjust(tabOverlap, begin ? tabOverlap : 0, 0, end ? -tabOverlap : 0);
2651 break;
2652 default:
2653 themeNumber = -1; // Do our own painting for triangular
2654 break;
2655 }
2656
2657 if (!selected) {
2658 switch (tab->shape) {
2659 case QTabBar::RoundedNorth:
2660 rect.adjust(0,0, 0,-1);
2661 break;
2662 case QTabBar::RoundedSouth:
2663 rect.adjust(0,1, 0,0);
2664 break;
2665 case QTabBar::RoundedEast:
2666 rect.adjust( 1,0, 0,0);
2667 break;
2668 case QTabBar::RoundedWest:
2669 rect.adjust(0,0, -1,0);
2670 break;
2671 default:
2672 break;
2673 }
2674 }
2675 }
2676#endif // QT_CONFIG(tabwidget)
2677 break;
2678
2679 case CE_ProgressBarGroove: {
2680 Qt::Orientation orient = Qt::Horizontal;
2681 if (const auto *pb = qstyleoption_cast<const QStyleOptionProgressBar *>(option))
2682 if (!(pb->state & QStyle::State_Horizontal))
2683 orient = Qt::Vertical;
2684
2685 partId = (orient == Qt::Horizontal) ? PP_BAR : PP_BARVERT;
2686 themeNumber = QWindowsVistaStylePrivate::ProgressTheme;
2687 stateId = 1;
2688 break;
2689 }
2690
2691 case CE_ProgressBarContents:
2692 if (const auto *bar = qstyleoption_cast<const QStyleOptionProgressBar *>(option)) {
2693 bool isIndeterminate = (bar->minimum == 0 && bar->maximum == 0);
2694 const bool vertical = !(bar->state & QStyle::State_Horizontal);
2695 const bool inverted = bar->invertedAppearance;
2696
2697 if (isIndeterminate || (bar->progress > 0 && (bar->progress < bar->maximum) && d->transitionsEnabled())) {
2698 if (!d->animation(styleObject(option)))
2699 d->startAnimation(new QProgressStyleAnimation(d->animationFps, styleObject(option)));
2700 } else {
2701 d->stopAnimation(styleObject(option));
2702 }
2703
2704 QWindowsThemeData theme(widget, painter,
2705 QWindowsVistaStylePrivate::ProgressTheme,
2706 vertical ? PP_FILLVERT : PP_FILL);
2707 theme.rect = option->rect;
2708 bool reverse = (bar->direction == Qt::LeftToRight && inverted) || (bar->direction == Qt::RightToLeft && !inverted);
2709 QTime current = d->animationTime();
2710
2711 if (isIndeterminate) {
2712 if (auto *progressAnimation = qobject_cast<QProgressStyleAnimation *>(d->animation(styleObject(option)))) {
2713 int glowSize = 120;
2714 int animationWidth = glowSize * 2 + (vertical ? theme.rect.height() : theme.rect.width());
2715 int animOffset = progressAnimation->startTime().msecsTo(current) / 4;
2716 if (animOffset > animationWidth)
2717 progressAnimation->setStartTime(d->animationTime());
2718 painter->save();
2719 painter->setClipRect(theme.rect);
2720 QRect animRect;
2721 QSize pixmapSize(14, 14);
2722 if (vertical) {
2723 animRect = QRect(theme.rect.left(),
2724 inverted ? rect.top() - glowSize + animOffset :
2725 rect.bottom() + glowSize - animOffset,
2726 rect.width(), glowSize);
2727 pixmapSize.setHeight(animRect.height());
2728 } else {
2729 animRect = QRect(rect.left() - glowSize + animOffset,
2730 rect.top(), glowSize, rect.height());
2731 animRect = QStyle::visualRect(reverse ? Qt::RightToLeft : Qt::LeftToRight,
2732 option->rect, animRect);
2733 pixmapSize.setWidth(animRect.width());
2734 }
2735 QString name = QStringLiteral(u"qiprogress-%1-%2").arg(pixmapSize.width()).arg(pixmapSize.height());
2736 QPixmap pixmap;
2737 if (!QPixmapCache::find(name, &pixmap)) {
2738 QImage image(pixmapSize, QImage::Format_ARGB32);
2739 image.fill(Qt::transparent);
2740 QPainter imagePainter(&image);
2741 theme.painter = &imagePainter;
2742 theme.partId = vertical ? PP_FILLVERT : PP_FILL;
2743 theme.rect = QRect(QPoint(0,0), animRect.size());
2744 QLinearGradient alphaGradient(0, 0, vertical ? 0 : image.width(),
2745 vertical ? image.height() : 0);
2746 alphaGradient.setColorAt(0, QColor(0, 0, 0, 0));
2747 alphaGradient.setColorAt(0.5, QColor(0, 0, 0, 220));
2748 alphaGradient.setColorAt(1, QColor(0, 0, 0, 0));
2749 imagePainter.fillRect(image.rect(), alphaGradient);
2750 imagePainter.setCompositionMode(QPainter::CompositionMode_SourceIn);
2751 d->drawBackground(theme);
2752 imagePainter.end();
2753 pixmap = QPixmap::fromImage(image);
2754 QPixmapCache::insert(name, pixmap);
2755 }
2756 painter->drawPixmap(animRect, pixmap);
2757 painter->restore();
2758 }
2759 } else {
2760 qint64 progress = qMax<qint64>(bar->progress, bar->minimum); // workaround for bug in QProgressBar
2761
2762 if (vertical) {
2763 int maxHeight = option->rect.height();
2764 int minHeight = 0;
2765 double vc6_workaround = ((progress - qint64(bar->minimum)) / qMax(double(1.0), double(qint64(bar->maximum) - qint64(bar->minimum))) * maxHeight);
2766 int height = isIndeterminate ? maxHeight: qMax(int(vc6_workaround), minHeight);
2767 theme.rect.setHeight(height);
2768 if (!inverted)
2769 theme.rect.moveTop(rect.height() - theme.rect.height());
2770 } else {
2771 int maxWidth = option->rect.width();
2772 int minWidth = 0;
2773 double vc6_workaround = ((progress - qint64(bar->minimum)) / qMax(double(1.0), double(qint64(bar->maximum) - qint64(bar->minimum))) * maxWidth);
2774 int width = isIndeterminate ? maxWidth : qMax(int(vc6_workaround), minWidth);
2775 theme.rect.setWidth(width);
2776 theme.rect = QStyle::visualRect(reverse ? Qt::RightToLeft : Qt::LeftToRight,
2777 option->rect, theme.rect);
2778 }
2779 d->drawBackground(theme);
2780
2781 if (QProgressStyleAnimation *a = qobject_cast<QProgressStyleAnimation *>(d->animation(styleObject(option)))) {
2782 int glowSize = 140;
2783 int animationWidth = glowSize * 2 + (vertical ? theme.rect.height() : theme.rect.width());
2784 int animOffset = a->startTime().msecsTo(current) / 4;
2785 theme.partId = vertical ? PP_MOVEOVERLAYVERT : PP_MOVEOVERLAY;
2786 if (animOffset > animationWidth) {
2787 if (bar->progress < bar->maximum)
2788 a->setStartTime(d->animationTime());
2789 else
2790 d->stopAnimation(styleObject(option)); //we stop the glow motion only after it has
2791 //moved out of view
2792 }
2793 painter->save();
2794 painter->setClipRect(theme.rect);
2795 if (vertical) {
2796 theme.rect = QRect(theme.rect.left(),
2797 inverted ? rect.top() - glowSize + animOffset :
2798 rect.bottom() + glowSize - animOffset,
2799 rect.width(), glowSize);
2800 } else {
2801 theme.rect = QRect(rect.left() - glowSize + animOffset,rect.top(), glowSize, rect.height());
2802 theme.rect = QStyle::visualRect(reverse ? Qt::RightToLeft : Qt::LeftToRight, option->rect, theme.rect);
2803 }
2804 d->drawBackground(theme);
2805 painter->restore();
2806 }
2807 }
2808 }
2809 return;
2810
2811 case CE_MenuBarItem:
2812 if (const auto *mbi = qstyleoption_cast<const QStyleOptionMenuItem *>(option)) {
2813 if (mbi->menuItemType == QStyleOptionMenuItem::DefaultItem)
2814 break;
2815
2816 QPalette::ColorRole textRole = disabled ? QPalette::Text : QPalette::ButtonText;
2817 const auto dpr = QStyleHelper::getDpr(widget);
2818 const auto extent = proxy()->pixelMetric(PM_SmallIconSize, option, widget);
2819 const auto pix = mbi->icon.pixmap(QSize(extent, extent), dpr, QIcon::Normal);
2820
2821 int alignment = Qt::AlignCenter | Qt::TextShowMnemonic | Qt::TextDontClip | Qt::TextSingleLine;
2822 if (!proxy()->styleHint(SH_UnderlineShortcut, mbi, widget))
2823 alignment |= Qt::TextHideMnemonic;
2824
2825 if (widget && mbi->palette.color(QPalette::Window) != Qt::transparent) { // Not needed for QtQuick Controls
2826 //The rect adjustment is a workaround for the menu not really filling its background.
2827 QWindowsThemeData theme(widget, painter,
2828 QWindowsVistaStylePrivate::MenuTheme,
2829 MENU_BARBACKGROUND, 0, option->rect.adjusted(-1, 0, 2, 1));
2830 d->drawBackground(theme);
2831
2832 int stateId = MBI_NORMAL;
2833 if (disabled)
2834 stateId = MBI_DISABLED;
2835 else if (pressed)
2836 stateId = MBI_PUSHED;
2837 else if (selected)
2838 stateId = MBI_HOT;
2839
2840 QWindowsThemeData theme2(widget, painter,
2841 QWindowsVistaStylePrivate::MenuTheme,
2842 MENU_BARITEM, stateId, option->rect);
2843 d->drawBackground(theme2);
2844 }
2845
2846 if (!pix.isNull())
2847 drawItemPixmap(painter, mbi->rect, alignment, pix);
2848 else
2849 drawItemText(painter, mbi->rect, alignment, mbi->palette, mbi->state & State_Enabled, mbi->text, textRole);
2850 }
2851 return;
2852
2853#if QT_CONFIG(menu)
2854 case CE_MenuEmptyArea:
2855 if (const auto *menuitem = qstyleoption_cast<const QStyleOptionMenuItem *>(option)) {
2856 QBrush fill = menuitem->palette.brush((menuitem->state & State_Selected) ?
2857 QPalette::Highlight : QPalette::Button);
2858 painter->fillRect(rect, fill);
2859 break;
2860 }
2861 return;
2862
2863 case CE_MenuItem:
2864 if (const auto *menuitem = qstyleoption_cast<const QStyleOptionMenuItem *>(option)) {
2865 // windows always has a check column, regardless whether we have an icon or not
2866 const qreal factor = QWindowsVistaStylePrivate::nativeMetricScaleFactor(widget);
2867 int checkcol = qRound(qreal(25) * factor);
2868 const int gutterWidth = qRound(qreal(3) * factor);
2869 {
2870 QWindowsThemeData theme(widget, nullptr, QWindowsVistaStylePrivate::MenuTheme,
2871 MENU_POPUPCHECKBACKGROUND, MBI_HOT);
2872 QWindowsThemeData themeSize = theme;
2873 themeSize.partId = MENU_POPUPCHECK;
2874 themeSize.stateId = 0;
2875 const QSizeF size = themeSize.size() * QWindowsStylePrivate::nativeMetricScaleFactor(widget);
2876 const QMarginsF margins = themeSize.margins() * QWindowsStylePrivate::nativeMetricScaleFactor(widget);
2877 checkcol = qMax(menuitem->maxIconWidth, qRound(gutterWidth + size.width() + margins.left() + margins.right()));
2878 }
2879 QRect rect = option->rect;
2880
2881 //fill popup background
2882 QWindowsThemeData popupbackgroundTheme(widget, painter, QWindowsVistaStylePrivate::MenuTheme,
2883 MENU_POPUPBACKGROUND, stateId, option->rect);
2884 d->drawBackground(popupbackgroundTheme);
2885
2886 //draw vertical menu line
2887 if (option->direction == Qt::LeftToRight)
2888 checkcol += rect.x();
2889 QPoint p1 = QStyle::visualPos(option->direction, menuitem->rect, QPoint(checkcol, rect.top()));
2890 QPoint p2 = QStyle::visualPos(option->direction, menuitem->rect, QPoint(checkcol, rect.bottom()));
2891 QRect gutterRect(p1.x(), p1.y(), gutterWidth, p2.y() - p1.y() + 1);
2892 QWindowsThemeData theme2(widget, painter, QWindowsVistaStylePrivate::MenuTheme,
2893 MENU_POPUPGUTTER, stateId, gutterRect);
2894 d->drawBackground(theme2);
2895
2896 int x, y, w, h;
2897 menuitem->rect.getRect(&x, &y, &w, &h);
2898 int tab = menuitem->reservedShortcutWidth;
2899 bool dis = !(menuitem->state & State_Enabled);
2900 bool checked = menuitem->checkType != QStyleOptionMenuItem::NotCheckable
2901 ? menuitem->checked : false;
2902 bool act = menuitem->state & State_Selected;
2903
2904 if (menuitem->menuItemType == QStyleOptionMenuItem::Separator) {
2905 int yoff = y-2 + h / 2;
2906 const int separatorSize = qRound(qreal(6) * QWindowsStylePrivate::nativeMetricScaleFactor(widget));
2907 QPoint p1 = QPoint(x + checkcol, yoff);
2908 QPoint p2 = QPoint(x + w + separatorSize, yoff);
2909 stateId = MBI_HOT;
2910 QRect subRect(p1.x() + (gutterWidth - menuitem->rect.x()), p1.y(),
2911 p2.x() - p1.x(), separatorSize);
2912 subRect = QStyle::visualRect(option->direction, option->rect, subRect );
2913 QWindowsThemeData theme2(widget, painter,
2914 QWindowsVistaStylePrivate::MenuTheme,
2915 MENU_POPUPSEPARATOR, stateId, subRect);
2916 d->drawBackground(theme2);
2917 return;
2918 }
2919
2920 QRect vCheckRect = visualRect(option->direction, menuitem->rect, QRect(menuitem->rect.x(),
2921 menuitem->rect.y(), checkcol - (gutterWidth + menuitem->rect.x()), menuitem->rect.height()));
2922
2923 if (act) {
2924 stateId = dis ? MBI_DISABLED : MBI_HOT;
2925 QWindowsThemeData theme2(widget, painter,
2926 QWindowsVistaStylePrivate::MenuTheme,
2927 MENU_POPUPITEM, stateId, option->rect);
2928 d->drawBackground(theme2);
2929 }
2930
2931 if (checked) {
2932 QWindowsThemeData theme(widget, painter,
2933 QWindowsVistaStylePrivate::MenuTheme,
2934 MENU_POPUPCHECKBACKGROUND,
2935 menuitem->icon.isNull() ? MBI_HOT : MBI_PUSHED, vCheckRect);
2936 QWindowsThemeData themeSize = theme;
2937 themeSize.partId = MENU_POPUPCHECK;
2938 themeSize.stateId = 0;
2939 const QSizeF size = themeSize.size() * QWindowsStylePrivate::nativeMetricScaleFactor(widget);
2940 const QMarginsF margins = themeSize.margins() * QWindowsStylePrivate::nativeMetricScaleFactor(widget);
2941 QRect checkRect(0, 0, qRound(size.width() + margins.left() + margins.right()),
2942 qRound(size.height() + margins.bottom() + margins.top()));
2943 checkRect.moveCenter(vCheckRect.center());
2944 theme.rect = checkRect;
2945
2946 d->drawBackground(theme);
2947
2948 if (menuitem->icon.isNull()) {
2949 checkRect = QRect(QPoint(0, 0), size.toSize());
2950 checkRect.moveCenter(theme.rect.center());
2951 theme.rect = checkRect;
2952
2953 theme.partId = MENU_POPUPCHECK;
2954 bool bullet = menuitem->checkType & QStyleOptionMenuItem::Exclusive;
2955 if (dis)
2956 theme.stateId = bullet ? MC_BULLETDISABLED: MC_CHECKMARKDISABLED;
2957 else
2958 theme.stateId = bullet ? MC_BULLETNORMAL: MC_CHECKMARKNORMAL;
2959 d->drawBackground(theme);
2960 }
2961 }
2962
2963 if (!menuitem->icon.isNull()) {
2964 QIcon::Mode mode = dis ? QIcon::Disabled : QIcon::Normal;
2965 if (act && !dis)
2966 mode = QIcon::Active;
2967 const auto size = proxy()->pixelMetric(PM_SmallIconSize, option, widget);
2968 QRect pmr(QPoint(0, 0), QSize(size, size));
2969 pmr.moveCenter(vCheckRect.center());
2970 menuitem->icon.paint(painter, vCheckRect, Qt::AlignCenter, mode,
2971 checked ? QIcon::On : QIcon::Off);
2972 }
2973
2974 painter->setPen(menuitem->palette.buttonText().color());
2975
2976 const QColor textColor = menuitem->palette.text().color();
2977 if (dis)
2978 painter->setPen(textColor);
2979
2980 int xm = windowsItemFrame + checkcol + windowsItemHMargin + (gutterWidth - menuitem->rect.x()) - 1;
2981 int xpos = menuitem->rect.x() + xm;
2982 QRect textRect(xpos, y + windowsItemVMargin, w - xm - windowsRightBorder - tab + 1, h - 2 * windowsItemVMargin);
2983 QRect vTextRect = visualRect(option->direction, menuitem->rect, textRect);
2984 QString s = menuitem->text;
2985 if (!s.isEmpty()) { // draw text
2986 painter->save();
2987 int t = s.indexOf(QLatin1Char('\t'));
2988 int text_flags = Qt::AlignVCenter | Qt::TextShowMnemonic | Qt::TextDontClip | Qt::TextSingleLine;
2989 if (!proxy()->styleHint(SH_UnderlineShortcut, menuitem, widget))
2990 text_flags |= Qt::TextHideMnemonic;
2991 text_flags |= Qt::AlignLeft;
2992 if (t >= 0) {
2993 QRect vShortcutRect = visualRect(option->direction, menuitem->rect,
2994 QRect(textRect.topRight(), QPoint(menuitem->rect.right(), textRect.bottom())));
2995 painter->drawText(vShortcutRect, text_flags, s.mid(t + 1));
2996 s = s.left(t);
2997 }
2998 QFont font = menuitem->font;
2999 if (menuitem->menuItemType == QStyleOptionMenuItem::DefaultItem)
3000 font.setBold(true);
3001 painter->setFont(font);
3002 painter->setPen(textColor);
3003 painter->drawText(vTextRect, text_flags, s.left(t));
3004 painter->restore();
3005 }
3006 if (menuitem->menuItemType == QStyleOptionMenuItem::SubMenu) {// draw sub menu arrow
3007 int dim = (h - 2 * windowsItemFrame) / 2;
3008 PrimitiveElement arrow;
3009 arrow = (option->direction == Qt::RightToLeft) ? PE_IndicatorArrowLeft : PE_IndicatorArrowRight;
3010 xpos = x + w - windowsArrowHMargin - windowsItemFrame - dim;
3011 QRect vSubMenuRect = visualRect(option->direction, menuitem->rect, QRect(xpos, y + h / 2 - dim / 2, dim, dim));
3012 QStyleOptionMenuItem newMI = *menuitem;
3013 newMI.rect = vSubMenuRect;
3014 newMI.state = dis ? State_None : State_Enabled;
3015 proxy()->drawPrimitive(arrow, &newMI, painter, widget);
3016 }
3017 }
3018 return;
3019#endif // QT_CONFIG(menu)
3020
3021 case CE_HeaderSection:
3022 if (const QStyleOptionHeader *header = qstyleoption_cast<const QStyleOptionHeader *>(option)) {
3023 partId = HP_HEADERITEM;
3024 if (flags & State_Sunken)
3025 stateId = HIS_PRESSED;
3026 else if (flags & State_MouseOver)
3027 stateId = HIS_HOT;
3028 else
3029 stateId = HIS_NORMAL;
3030
3031 if (header->sortIndicator != QStyleOptionHeader::None)
3032 stateId += 3;
3033
3034 QWindowsThemeData theme(widget, painter,
3035 QWindowsVistaStylePrivate::HeaderTheme,
3036 partId, stateId, option->rect);
3037 d->drawBackground(theme);
3038 }
3039 return;
3040
3041 case CE_MenuBarEmptyArea: {
3042 stateId = MBI_NORMAL;
3043 if (!(state & State_Enabled))
3044 stateId = MBI_DISABLED;
3045 QWindowsThemeData theme(widget, painter, QWindowsVistaStylePrivate::MenuTheme,
3046 MENU_BARBACKGROUND, stateId, option->rect);
3047 d->drawBackground(theme);
3048 return;
3049 }
3050
3051 case CE_ToolBar:
3052#if QT_CONFIG(toolbar)
3053 if (const auto *toolbar = qstyleoption_cast<const QStyleOptionToolBar *>(option)) {
3054 QPalette pal = option->palette;
3055 pal.setColor(QPalette::Dark, option->palette.window().color().darker(130));
3056 QStyleOptionToolBar copyOpt = *toolbar;
3057 copyOpt.palette = pal;
3058 QWindowsStyle::drawControl(element, &copyOpt, painter, widget);
3059 }
3060#endif // QT_CONFIG(toolbar)
3061 return;
3062
3063#if QT_CONFIG(dockwidget)
3064 case CE_DockWidgetTitle:
3065 if (const auto *dwOpt = qstyleoption_cast<const QStyleOptionDockWidget *>(option)) {
3066 QRect rect = option->rect;
3067 const QDockWidget *dw = qobject_cast<const QDockWidget *>(widget);
3068 bool isFloating = dw && dw->isFloating();
3069 int buttonMargin = 4;
3070 int mw = proxy()->pixelMetric(QStyle::PM_DockWidgetTitleMargin, dwOpt, widget);
3071 int fw = proxy()->pixelMetric(PM_DockWidgetFrameWidth, dwOpt, widget);
3072
3073 const bool verticalTitleBar = dwOpt->verticalTitleBar;
3074
3075 if (verticalTitleBar) {
3076 rect = rect.transposed();
3077
3078 painter->translate(rect.left() - 1, rect.top() + rect.width());
3079 painter->rotate(-90);
3080 painter->translate(-rect.left() + 1, -rect.top());
3081 }
3082
3083 QRect r = option->rect.adjusted(0, 2, -1, -3);
3084 QRect titleRect = r;
3085
3086 if (dwOpt->closable) {
3087 QSize sz = proxy()->standardIcon(QStyle::SP_TitleBarCloseButton, dwOpt, widget).actualSize(QSize(10, 10));
3088 titleRect.adjust(0, 0, -sz.width() - mw - buttonMargin, 0);
3089 }
3090
3091 if (dwOpt->floatable) {
3092 QSize sz = proxy()->standardIcon(QStyle::SP_TitleBarMaxButton, dwOpt, widget).actualSize(QSize(10, 10));
3093 titleRect.adjust(0, 0, -sz.width() - mw - buttonMargin, 0);
3094 }
3095
3096 if (isFloating) {
3097 titleRect.adjust(0, -fw, 0, 0);
3098 if (widget && widget->windowIcon().cacheKey() != QApplication::windowIcon().cacheKey())
3099 titleRect.adjust(titleRect.height() + mw, 0, 0, 0);
3100 } else {
3101 titleRect.adjust(mw, 0, 0, 0);
3102 if (!dwOpt->floatable && !dwOpt->closable)
3103 titleRect.adjust(0, 0, -mw, 0);
3104 }
3105
3106 if (!verticalTitleBar)
3107 titleRect = visualRect(dwOpt->direction, r, titleRect);
3108
3109 if (isFloating) {
3110 const bool isActive = dwOpt->state & State_Active;
3111 themeNumber = QWindowsVistaStylePrivate::WindowTheme;
3112 if (isActive)
3113 stateId = CS_ACTIVE;
3114 else
3115 stateId = CS_INACTIVE;
3116
3117 rect = rect.adjusted(-fw, -fw, fw, 0);
3118
3119 QWindowsThemeData theme(widget, painter, themeNumber, 0, stateId);
3120 if (!theme.isValid())
3121 break;
3122
3123 // Draw small type title bar
3124 theme.rect = rect;
3125 theme.partId = WP_SMALLCAPTION;
3126 d->drawBackground(theme);
3127
3128 // Figure out maximal button space on title bar
3129
3130 QIcon ico = widget->windowIcon();
3131 bool hasIcon = (ico.cacheKey() != QApplication::windowIcon().cacheKey());
3132 if (hasIcon) {
3133 const auto titleHeight = rect.height() - 2;
3134 const auto dpr = QStyleHelper::getDpr(widget);
3135 const auto pxIco = ico.pixmap(QSize(titleHeight, titleHeight), dpr);
3136 if (!verticalTitleBar && dwOpt->direction == Qt::RightToLeft)
3137 painter->drawPixmap(rect.width() - titleHeight - pxIco.width(), rect.bottom() - titleHeight - 2, pxIco);
3138 else
3139 painter->drawPixmap(fw, rect.bottom() - titleHeight - 2, pxIco);
3140 }
3141 if (!dwOpt->title.isEmpty()) {
3142 QPen oldPen = painter->pen();
3143 QFont oldFont = painter->font();
3144 QFont titleFont = oldFont;
3145 titleFont.setBold(true);
3146 painter->setFont(titleFont);
3147 QString titleText
3148 = painter->fontMetrics().elidedText(dwOpt->title, Qt::ElideRight, titleRect.width());
3149
3150 int result = TST_NONE;
3151 GetThemeEnumValue(theme.handle(), WP_SMALLCAPTION, isActive ? CS_ACTIVE : CS_INACTIVE, TMT_TEXTSHADOWTYPE, &result);
3152 if (result != TST_NONE) {
3153 COLORREF textShadowRef;
3154 GetThemeColor(theme.handle(), WP_SMALLCAPTION, isActive ? CS_ACTIVE : CS_INACTIVE, TMT_TEXTSHADOWCOLOR, &textShadowRef);
3155 QColor textShadow = qRgb(GetRValue(textShadowRef), GetGValue(textShadowRef), GetBValue(textShadowRef));
3156 painter->setPen(textShadow);
3157 drawItemText(painter, titleRect.adjusted(1, 1, 1, 1),
3158 Qt::AlignLeft | Qt::AlignBottom | Qt::TextHideMnemonic, dwOpt->palette,
3159 dwOpt->state & State_Enabled, titleText);
3160 }
3161
3162 COLORREF captionText = GetSysColor(isActive ? COLOR_CAPTIONTEXT : COLOR_INACTIVECAPTIONTEXT);
3163 QColor textColor = qRgb(GetRValue(captionText), GetGValue(captionText), GetBValue(captionText));
3164 painter->setPen(textColor);
3165 drawItemText(painter, titleRect,
3166 Qt::AlignLeft | Qt::AlignBottom | Qt::TextHideMnemonic, dwOpt->palette,
3167 dwOpt->state & State_Enabled, titleText);
3168 painter->setFont(oldFont);
3169 painter->setPen(oldPen);
3170 }
3171 } else {
3172 painter->setBrush(option->palette.window().color().darker(110));
3173 painter->setPen(option->palette.window().color().darker(130));
3174 painter->drawRect(rect.adjusted(0, 1, -1, -3));
3175
3176 if (!dwOpt->title.isEmpty()) {
3177 QString titleText = painter->fontMetrics().elidedText(dwOpt->title, Qt::ElideRight,
3178 verticalTitleBar ? titleRect.height() : titleRect.width());
3179 const int indent = 4;
3180 drawItemText(painter, rect.adjusted(indent + 1, 1, -indent - 1, -1),
3181 Qt::AlignLeft | Qt::AlignVCenter | Qt::TextHideMnemonic,
3182 dwOpt->palette,
3183 dwOpt->state & State_Enabled, titleText,
3184 QPalette::WindowText);
3185 }
3186 }
3187 }
3188 return;
3189#endif // QT_CONFIG(dockwidget)
3190
3191#if QT_CONFIG(rubberband)
3192 case CE_RubberBand:
3193 if (qstyleoption_cast<const QStyleOptionRubberBand *>(option)) {
3194 QColor highlight = option->palette.color(QPalette::Active, QPalette::Highlight);
3195 painter->save();
3196 painter->setPen(highlight.darker(120));
3197 QColor dimHighlight(qMin(highlight.red()/2 + 110, 255),
3198 qMin(highlight.green()/2 + 110, 255),
3199 qMin(highlight.blue()/2 + 110, 255),
3200 (widget && widget->isWindow())? 255 : 127);
3201 painter->setBrush(dimHighlight);
3202 painter->drawRect(option->rect.adjusted(0, 0, -1, -1));
3203 painter->restore();
3204 return;
3205 }
3206 break;
3207#endif // QT_CONFIG(rubberband)
3208
3209 case CE_HeaderEmptyArea:
3210 if (option->state & State_Horizontal) {
3211 themeNumber = QWindowsVistaStylePrivate::HeaderTheme;
3212 stateId = HIS_NORMAL;
3213 } else {
3214 QWindowsStyle::drawControl(CE_HeaderEmptyArea, option, painter, widget);
3215 return;
3216 }
3217 break;
3218
3219#if QT_CONFIG(itemviews)
3220 case CE_ItemViewItem: {
3221 const QStyleOptionViewItem *vopt;
3222 const QAbstractItemView *view = qobject_cast<const QAbstractItemView *>(widget);
3223 bool newStyle = true;
3224
3225 if (qobject_cast<const QTableView*>(widget))
3226 newStyle = false;
3227
3228 QWindowsThemeData theme(widget, painter, themeNumber, partId, stateId, rect);
3229
3230 if (newStyle && view && (vopt = qstyleoption_cast<const QStyleOptionViewItem *>(option))) {
3231 /*
3232 // We cannot currently get the correct selection color for "explorer style" views
3233 COLORREF cref = 0;
3234 QWindowsThemeData theme(d->treeViewHelper(), 0, QLatin1String("LISTVIEW"), 0, 0);
3235 unsigned int res = GetThemeColor(theme.handle(), LVP_LISTITEM, LISS_SELECTED, TMT_TEXTCOLOR, &cref);
3236 QColor textColor(GetRValue(cref), GetGValue(cref), GetBValue(cref));
3237 */
3238 QPalette palette = vopt->palette;
3239 palette.setColor(QPalette::All, QPalette::HighlightedText, palette.color(QPalette::Active, QPalette::Text));
3240 // Note that setting a saturated color here results in ugly XOR colors in the focus rect
3241 palette.setColor(QPalette::All, QPalette::Highlight, palette.base().color().darker(108));
3242 QStyleOptionViewItem adjustedOption = *vopt;
3243 adjustedOption.palette = palette;
3244 // We hide the focusrect in singleselection as it is not required
3245 if ((view->selectionMode() == QAbstractItemView::SingleSelection)
3246 && !(vopt->state & State_KeyboardFocusChange))
3247 adjustedOption.state &= ~State_HasFocus;
3248 if (!theme.isValid()) {
3249 QWindowsStyle::drawControl(element, &adjustedOption, painter, widget);
3250 return;
3251 }
3252 } else {
3253 if (!theme.isValid()) {
3254 QWindowsStyle::drawControl(element, option, painter, widget);
3255 return;
3256 }
3257 }
3258
3259 theme.rotate = rotate;
3260 theme.mirrorHorizontally = hMirrored;
3261 theme.mirrorVertically = vMirrored;
3262 d->drawBackground(theme);
3263 return;
3264 }
3265#endif // QT_CONFIG(itemviews)
3266
3267#if QT_CONFIG(combobox)
3268 case CE_ComboBoxLabel:
3269 QCommonStyle::drawControl(element, option, painter, widget);
3270 return;
3271#endif // QT_CONFIG(combobox)
3272
3273 default:
3274 break;
3275 }
3276
3277 QWindowsThemeData theme(widget, painter, themeNumber, partId, stateId, rect);
3278
3279 if (!theme.isValid()) {
3280 QWindowsStyle::drawControl(element, option, painter, widget);
3281 return;
3282 }
3283
3284 theme.rotate = rotate;
3285 theme.mirrorHorizontally = hMirrored;
3286 theme.mirrorVertically = vMirrored;
3287
3288 d->drawBackground(theme);
3289}
3290
3291/*!
3292 \internal
3293 see drawPrimitive for comments on the animation support
3294
3295 */
3296void QWindowsVistaStyle::drawComplexControl(ComplexControl control, const QStyleOptionComplex *option,
3297 QPainter *painter, const QWidget *widget) const
3298{
3299 QWindowsVistaStylePrivate *d = const_cast<QWindowsVistaStylePrivate*>(d_func());
3300
3301 if (!QWindowsVistaStylePrivate::useVista()) {
3302 QWindowsStyle::drawComplexControl(control, option, painter, widget);
3303 return;
3304 }
3305
3306 State state = option->state;
3307 SubControls sub = option->subControls;
3308 QRect r = option->rect;
3309
3310 int partId = 0;
3311 int stateId = 0;
3312
3313 State flags = option->state;
3314 if (widget && widget->testAttribute(Qt::WA_UnderMouse) && widget->isActiveWindow())
3315 flags |= State_MouseOver;
3316
3317 if (d->transitionsEnabled() && canAnimate(option))
3318 {
3319 if (control == CC_ScrollBar || control == CC_SpinBox || control == CC_ComboBox) {
3320 QObject *styleObject = option->styleObject; // Can be widget or qquickitem
3321
3322 int oldState = styleObject->property("_q_stylestate").toInt();
3323 int oldActiveControls = styleObject->property("_q_stylecontrols").toInt();
3324
3325 QRect oldRect = styleObject->property("_q_stylerect").toRect();
3326 styleObject->setProperty("_q_stylestate", int(option->state));
3327 styleObject->setProperty("_q_stylecontrols", int(option->activeSubControls));
3328 styleObject->setProperty("_q_stylerect", option->rect);
3329
3330 bool doTransition = ((state & State_Sunken) != (oldState & State_Sunken)
3331 || (state & State_On) != (oldState & State_On)
3332 || (state & State_MouseOver) != (oldState & State_MouseOver)
3333 || oldActiveControls != int(option->activeSubControls));
3334
3335 if (qstyleoption_cast<const QStyleOptionSlider *>(option)) {
3336 QRect oldSliderPos = styleObject->property("_q_stylesliderpos").toRect();
3337 QRect currentPos = proxy()->subControlRect(CC_ScrollBar, option, SC_ScrollBarSlider, widget);
3338 styleObject->setProperty("_q_stylesliderpos", currentPos);
3339 if (oldSliderPos != currentPos) {
3340 doTransition = false;
3341 d->stopAnimation(styleObject);
3342 }
3343 } else if (control == CC_SpinBox) {
3344 //spinboxes have a transition when focus changes
3345 if (!doTransition)
3346 doTransition = (state & State_HasFocus) != (oldState & State_HasFocus);
3347 }
3348
3349 if (oldRect != option->rect) {
3350 doTransition = false;
3351 d->stopAnimation(styleObject);
3352 }
3353
3354 if (doTransition) {
3355 QImage startImage = createAnimationBuffer(option, widget);
3356 QPainter startPainter(&startImage);
3357
3358 QImage endImage = createAnimationBuffer(option, widget);
3359 QPainter endPainter(&endImage);
3360
3361 QWindowsVistaAnimation *anim = qobject_cast<QWindowsVistaAnimation *>(d->animation(styleObject));
3363
3364 // Draw the image that ends the animation by using the current styleoption
3365 QStyleOptionComplex *styleOption = qstyleoption_cast<QStyleOptionComplex*>(clonedAnimationStyleOption(option));
3366
3367 styleObject->setProperty("_q_no_animation", true);
3368
3369 // Draw transition source
3370 if (!anim) {
3371 styleOption->state = QStyle::State(oldState);
3372 styleOption->activeSubControls = QStyle::SubControl(oldActiveControls);
3373 proxy()->drawComplexControl(control, styleOption, &startPainter, widget);
3374 } else {
3375 anim->paint(&startPainter, option);
3376 }
3377 t->setStartImage(startImage);
3378
3379 // Draw transition target
3380 styleOption->state = option->state;
3381 styleOption->activeSubControls = option->activeSubControls;
3382 proxy()->drawComplexControl(control, styleOption, &endPainter, widget);
3383
3384 styleObject->setProperty("_q_no_animation", false);
3385
3386 t->setEndImage(endImage);
3387 t->setStartTime(d->animationTime());
3388
3389 if (option->state & State_MouseOver || option->state & State_Sunken)
3390 t->setDuration(150);
3391 else
3392 t->setDuration(500);
3393
3394 deleteClonedAnimationStyleOption(styleOption);
3395 d->startAnimation(t);
3396 }
3397 if (QWindowsVistaAnimation *anim = qobject_cast<QWindowsVistaAnimation *>(d->animation(styleObject))) {
3398 anim->paint(painter, option);
3399 return;
3400 }
3401 }
3402 }
3403
3404 switch (control) {
3405
3406#if QT_CONFIG(slider)
3407 case CC_Slider:
3408 if (const auto *slider = qstyleoption_cast<const QStyleOptionSlider *>(option)) {
3409 QWindowsThemeData theme(widget, painter, QWindowsVistaStylePrivate::TrackBarTheme);
3410 QRect slrect = slider->rect;
3411 QRegion tickreg = slrect;
3412 if (sub & SC_SliderGroove) {
3413 theme.rect = proxy()->subControlRect(CC_Slider, option, SC_SliderGroove, widget);
3414 if (slider->orientation == Qt::Horizontal) {
3415 partId = TKP_TRACK;
3416 stateId = TRS_NORMAL;
3417 theme.rect = QRect(slrect.left(), theme.rect.center().y() - 2, slrect.width(), 4);
3418 } else {
3419 partId = TKP_TRACKVERT;
3420 stateId = TRVS_NORMAL;
3421 theme.rect = QRect(theme.rect.center().x() - 2, slrect.top(), 4, slrect.height());
3422 }
3423 theme.partId = partId;
3424 theme.stateId = stateId;
3425 d->drawBackground(theme);
3426 tickreg -= theme.rect;
3427 }
3428 if (sub & SC_SliderTickmarks) {
3429 int tickOffset = proxy()->pixelMetric(PM_SliderTickmarkOffset, slider, widget);
3430 int ticks = slider->tickPosition;
3431 int thickness = proxy()->pixelMetric(PM_SliderControlThickness, slider, widget);
3432 int len = proxy()->pixelMetric(PM_SliderLength, slider, widget);
3433 int available = proxy()->pixelMetric(PM_SliderSpaceAvailable, slider, widget);
3434 int interval = slider->tickInterval;
3435 if (interval <= 0) {
3436 interval = slider->singleStep;
3437 if (QStyle::sliderPositionFromValue(slider->minimum, slider->maximum, interval,
3438 available)
3439 - QStyle::sliderPositionFromValue(slider->minimum, slider->maximum,
3440 0, available) < 3)
3441 interval = slider->pageStep;
3442 }
3443 if (!interval)
3444 interval = 1;
3445 int fudge = len / 2;
3446 int pos;
3447 int bothOffset = (ticks & QSlider::TicksAbove && ticks & QSlider::TicksBelow) ? 1 : 0;
3448 painter->setPen(d->sliderTickColor);
3449 QVarLengthArray<QLine, 32> lines;
3450 int v = slider->minimum;
3451 while (v <= slider->maximum + 1) {
3452 if (v == slider->maximum + 1 && interval == 1)
3453 break;
3454 const int v_ = qMin(v, slider->maximum);
3455 int tickLength = (v_ == slider->minimum || v_ >= slider->maximum) ? 4 : 3;
3456 pos = QStyle::sliderPositionFromValue(slider->minimum, slider->maximum,
3457 v_, available) + fudge;
3458 if (slider->orientation == Qt::Horizontal) {
3459 if (ticks & QSlider::TicksAbove) {
3460 lines.append(QLine(pos, tickOffset - 1 - bothOffset,
3461 pos, tickOffset - 1 - bothOffset - tickLength));
3462 }
3463
3464 if (ticks & QSlider::TicksBelow) {
3465 lines.append(QLine(pos, tickOffset + thickness + bothOffset,
3466 pos, tickOffset + thickness + bothOffset + tickLength));
3467 }
3468 } else {
3469 if (ticks & QSlider::TicksAbove) {
3470 lines.append(QLine(tickOffset - 1 - bothOffset, pos,
3471 tickOffset - 1 - bothOffset - tickLength, pos));
3472 }
3473
3474 if (ticks & QSlider::TicksBelow) {
3475 lines.append(QLine(tickOffset + thickness + bothOffset, pos,
3476 tickOffset + thickness + bothOffset + tickLength, pos));
3477 }
3478 }
3479 // in the case where maximum is max int
3480 int nextInterval = v + interval;
3481 if (nextInterval < v)
3482 break;
3483 v = nextInterval;
3484 }
3485 if (!lines.isEmpty()) {
3486 painter->save();
3487 painter->translate(slrect.topLeft());
3488 painter->drawLines(lines.constData(), lines.size());
3489 painter->restore();
3490 }
3491 }
3492 if (sub & SC_SliderHandle) {
3493 theme.rect = proxy()->subControlRect(CC_Slider, option, SC_SliderHandle, widget);
3494 if (slider->orientation == Qt::Horizontal) {
3495 if (slider->tickPosition == QSlider::TicksAbove)
3496 partId = TKP_THUMBTOP;
3497 else if (slider->tickPosition == QSlider::TicksBelow)
3498 partId = TKP_THUMBBOTTOM;
3499 else
3500 partId = TKP_THUMB;
3501
3502 if (!(slider->state & State_Enabled))
3503 stateId = TUS_DISABLED;
3504 else if (slider->activeSubControls & SC_SliderHandle && (slider->state & State_Sunken))
3505 stateId = TUS_PRESSED;
3506 else if (slider->activeSubControls & SC_SliderHandle && (slider->state & State_MouseOver))
3507 stateId = TUS_HOT;
3508 else if (flags & State_HasFocus)
3509 stateId = TUS_FOCUSED;
3510 else
3511 stateId = TUS_NORMAL;
3512 } else {
3513 if (slider->tickPosition == QSlider::TicksLeft)
3514 partId = TKP_THUMBLEFT;
3515 else if (slider->tickPosition == QSlider::TicksRight)
3516 partId = TKP_THUMBRIGHT;
3517 else
3518 partId = TKP_THUMBVERT;
3519
3520 if (!(slider->state & State_Enabled))
3521 stateId = TUVS_DISABLED;
3522 else if (slider->activeSubControls & SC_SliderHandle && (slider->state & State_Sunken))
3523 stateId = TUVS_PRESSED;
3524 else if (slider->activeSubControls & SC_SliderHandle && (slider->state & State_MouseOver))
3525 stateId = TUVS_HOT;
3526 else if (flags & State_HasFocus)
3527 stateId = TUVS_FOCUSED;
3528 else
3529 stateId = TUVS_NORMAL;
3530 }
3531 theme.partId = partId;
3532 theme.stateId = stateId;
3533 d->drawBackground(theme);
3534 }
3535 if (slider->state & State_HasFocus) {
3536 QStyleOptionFocusRect fropt;
3537 fropt.QStyleOption::operator=(*slider);
3538 fropt.rect = subElementRect(SE_SliderFocusRect, slider, widget);
3539 proxy()->drawPrimitive(PE_FrameFocusRect, &fropt, painter, widget);
3540 }
3541 }
3542 break;
3543#endif
3544
3545#if QT_CONFIG(toolbutton)
3546 case CC_ToolButton:
3547 if (const auto *toolbutton = qstyleoption_cast<const QStyleOptionToolButton *>(option)) {
3548 QRect button, menuarea;
3549 button = proxy()->subControlRect(control, toolbutton, SC_ToolButton, widget);
3550 menuarea = proxy()->subControlRect(control, toolbutton, SC_ToolButtonMenu, widget);
3551
3552 State bflags = toolbutton->state & ~State_Sunken;
3553 State mflags = bflags;
3554 bool autoRaise = flags & State_AutoRaise;
3555 if (autoRaise) {
3556 if (!(bflags & State_MouseOver) || !(bflags & State_Enabled))
3557 bflags &= ~State_Raised;
3558 }
3559
3560 if (toolbutton->state & State_Sunken) {
3561 if (toolbutton->activeSubControls & SC_ToolButton) {
3562 bflags |= State_Sunken;
3563 mflags |= State_MouseOver | State_Sunken;
3564 } else if (toolbutton->activeSubControls & SC_ToolButtonMenu) {
3565 mflags |= State_Sunken;
3566 bflags |= State_MouseOver;
3567 }
3568 }
3569
3570 QStyleOption tool = *toolbutton;
3571 if (toolbutton->subControls & SC_ToolButton) {
3572 if (flags & (State_Sunken | State_On | State_Raised) || !autoRaise) {
3573 if (toolbutton->features & QStyleOptionToolButton::MenuButtonPopup && autoRaise) {
3574 QWindowsThemeData theme(widget, painter, QWindowsVistaStylePrivate::ToolBarTheme);
3575 theme.partId = TP_SPLITBUTTON;
3576 theme.rect = button;
3577 if (!(bflags & State_Enabled))
3578 stateId = TS_DISABLED;
3579 else if (bflags & State_Sunken)
3580 stateId = TS_PRESSED;
3581 else if (bflags & State_MouseOver || !(flags & State_AutoRaise))
3582 stateId = flags & State_On ? TS_HOTCHECKED : TS_HOT;
3583 else if (bflags & State_On)
3584 stateId = TS_CHECKED;
3585 else
3586 stateId = TS_NORMAL;
3587 if (option->direction == Qt::RightToLeft)
3588 theme.mirrorHorizontally = true;
3589 theme.stateId = stateId;
3590 d->drawBackground(theme);
3591 } else {
3592 tool.rect = option->rect;
3593 tool.state = bflags;
3594 if (autoRaise) // for tool bars
3595 proxy()->drawPrimitive(PE_PanelButtonTool, &tool, painter, widget);
3596 else
3597 proxy()->drawPrimitive(PE_PanelButtonBevel, &tool, painter, widget);
3598 }
3599 }
3600 }
3601
3602 if (toolbutton->state & State_HasFocus) {
3603 QStyleOptionFocusRect fr;
3604 fr.QStyleOption::operator=(*toolbutton);
3605 fr.rect.adjust(3, 3, -3, -3);
3606 if (toolbutton->features & QStyleOptionToolButton::MenuButtonPopup)
3607 fr.rect.adjust(0, 0, -proxy()->pixelMetric(QStyle::PM_MenuButtonIndicator,
3608 toolbutton, widget), 0);
3609 proxy()->drawPrimitive(PE_FrameFocusRect, &fr, painter, widget);
3610 }
3611 QStyleOptionToolButton label = *toolbutton;
3612 label.state = bflags;
3613 int fw = 2;
3614 if (!autoRaise)
3615 label.state &= ~State_Sunken;
3616 label.rect = button.adjusted(fw, fw, -fw, -fw);
3617 proxy()->drawControl(CE_ToolButtonLabel, &label, painter, widget);
3618
3619 if (toolbutton->subControls & SC_ToolButtonMenu) {
3620 tool.rect = menuarea;
3621 tool.state = mflags;
3622 if (autoRaise) {
3623 proxy()->drawPrimitive(PE_IndicatorButtonDropDown, &tool, painter, widget);
3624 } else {
3625 tool.state = mflags;
3626 menuarea.adjust(-2, 0, 0, 0);
3627 // Draw menu button
3628 if ((bflags & State_Sunken) != (mflags & State_Sunken)){
3629 painter->save();
3630 painter->setClipRect(menuarea);
3631 tool.rect = option->rect;
3632 proxy()->drawPrimitive(PE_PanelButtonBevel, &tool, painter, nullptr);
3633 painter->restore();
3634 }
3635 // Draw arrow
3636 painter->save();
3637 painter->setPen(option->palette.dark().color());
3638 painter->drawLine(menuarea.left(), menuarea.top() + 3,
3639 menuarea.left(), menuarea.bottom() - 3);
3640 painter->setPen(option->palette.light().color());
3641 painter->drawLine(menuarea.left() - 1, menuarea.top() + 3,
3642 menuarea.left() - 1, menuarea.bottom() - 3);
3643
3644 tool.rect = menuarea.adjusted(2, 3, -2, -1);
3645 proxy()->drawPrimitive(PE_IndicatorArrowDown, &tool, painter, widget);
3646 painter->restore();
3647 }
3648 } else if (toolbutton->features & QStyleOptionToolButton::HasMenu) {
3649 int mbi = proxy()->pixelMetric(PM_MenuButtonIndicator, toolbutton, widget);
3650 QRect ir = toolbutton->rect;
3651 QStyleOptionToolButton newBtn = *toolbutton;
3652 newBtn.rect = QRect(ir.right() + 4 - mbi, ir.height() - mbi + 4, mbi - 5, mbi - 5);
3653 proxy()->drawPrimitive(PE_IndicatorArrowDown, &newBtn, painter, widget);
3654 }
3655 }
3656 break;
3657#endif // QT_CONFIG(toolbutton)
3658
3659 case CC_TitleBar:
3660 if (const auto *tb = qstyleoption_cast<const QStyleOptionTitleBar *>(option)) {
3661 const qreal factor = QWindowsStylePrivate::nativeMetricScaleFactor(widget);
3662 bool isActive = tb->titleBarState & QStyle::State_Active;
3663 QWindowsThemeData theme(widget, painter, QWindowsVistaStylePrivate::WindowTheme);
3664 if (sub & SC_TitleBarLabel) {
3665 partId = (tb->titleBarState & Qt::WindowMinimized) ? WP_MINCAPTION : WP_CAPTION;
3666 theme.rect = option->rect;
3667 if (widget && !widget->isEnabled())
3668 stateId = CS_DISABLED;
3669 else if (isActive)
3670 stateId = CS_ACTIVE;
3671 else
3672 stateId = CS_INACTIVE;
3673
3674 theme.partId = partId;
3675 theme.stateId = stateId;
3676 d->drawBackground(theme);
3677
3678 QRect ir = proxy()->subControlRect(CC_TitleBar, tb, SC_TitleBarLabel, widget);
3679
3680 int result = TST_NONE;
3681 GetThemeEnumValue(theme.handle(), WP_CAPTION, isActive ? CS_ACTIVE : CS_INACTIVE, TMT_TEXTSHADOWTYPE, &result);
3682 if (result != TST_NONE) {
3683 COLORREF textShadowRef;
3684 GetThemeColor(theme.handle(), WP_CAPTION, isActive ? CS_ACTIVE : CS_INACTIVE, TMT_TEXTSHADOWCOLOR, &textShadowRef);
3685 QColor textShadow = qRgb(GetRValue(textShadowRef), GetGValue(textShadowRef), GetBValue(textShadowRef));
3686 painter->setPen(textShadow);
3687 painter->drawText(int(ir.x() + 3 * factor), int(ir.y() + 2 * factor),
3688 int(ir.width() - 1 * factor), ir.height(),
3689 Qt::AlignLeft | Qt::AlignVCenter | Qt::TextSingleLine, tb->text);
3690 }
3691 COLORREF captionText = GetSysColor(isActive ? COLOR_CAPTIONTEXT : COLOR_INACTIVECAPTIONTEXT);
3692 QColor textColor = qRgb(GetRValue(captionText), GetGValue(captionText), GetBValue(captionText));
3693 painter->setPen(textColor);
3694 painter->drawText(int(ir.x() + 2 * factor), int(ir.y() + 1 * factor),
3695 int(ir.width() - 2 * factor), ir.height(),
3696 Qt::AlignLeft | Qt::AlignVCenter | Qt::TextSingleLine, tb->text);
3697 }
3698 if (sub & SC_TitleBarSysMenu && tb->titleBarFlags & Qt::WindowSystemMenuHint) {
3699 theme.rect = proxy()->subControlRect(CC_TitleBar, option, SC_TitleBarSysMenu, widget);
3700 partId = WP_SYSBUTTON;
3701 if ((widget && !widget->isEnabled()) || !isActive)
3702 stateId = SBS_DISABLED;
3703 else if (option->activeSubControls == SC_TitleBarSysMenu && (option->state & State_Sunken))
3704 stateId = SBS_PUSHED;
3705 else if (option->activeSubControls == SC_TitleBarSysMenu && (option->state & State_MouseOver))
3706 stateId = SBS_HOT;
3707 else
3708 stateId = SBS_NORMAL;
3709 if (!tb->icon.isNull()) {
3710 tb->icon.paint(painter, theme.rect);
3711 } else {
3712 theme.partId = partId;
3713 theme.stateId = stateId;
3714 if (theme.size().isEmpty()) {
3715 const auto extent = proxy()->pixelMetric(PM_SmallIconSize, tb, widget);
3716 const auto dpr = QStyleHelper::getDpr(widget);
3717 const auto icon = proxy()->standardIcon(SP_TitleBarMenuButton, tb, widget);
3718 const auto pm = icon.pixmap(QSize(extent, extent), dpr);
3719 drawItemPixmap(painter, theme.rect, Qt::AlignCenter, pm);
3720 } else {
3721 d->drawBackground(theme);
3722 }
3723 }
3724 }
3725
3726 if (sub & SC_TitleBarMinButton && tb->titleBarFlags & Qt::WindowMinimizeButtonHint
3727 && !(tb->titleBarState & Qt::WindowMinimized)) {
3728 populateTitleBarButtonTheme(proxy(), widget, option, SC_TitleBarMinButton, isActive, WP_MINBUTTON, &theme);
3729 d->drawBackground(theme);
3730 }
3731 if (sub & SC_TitleBarMaxButton && tb->titleBarFlags & Qt::WindowMaximizeButtonHint
3732 && !(tb->titleBarState & Qt::WindowMaximized)) {
3733 populateTitleBarButtonTheme(proxy(), widget, option, SC_TitleBarMaxButton, isActive, WP_MAXBUTTON, &theme);
3734 d->drawBackground(theme);
3735 }
3736 if (sub & SC_TitleBarContextHelpButton
3737 && tb->titleBarFlags & Qt::WindowContextHelpButtonHint) {
3738 populateTitleBarButtonTheme(proxy(), widget, option, SC_TitleBarContextHelpButton, isActive, WP_HELPBUTTON, &theme);
3739 d->drawBackground(theme);
3740 }
3741 bool drawNormalButton = (sub & SC_TitleBarNormalButton)
3742 && (((tb->titleBarFlags & Qt::WindowMinimizeButtonHint)
3743 && (tb->titleBarState & Qt::WindowMinimized))
3744 || ((tb->titleBarFlags & Qt::WindowMaximizeButtonHint)
3745 && (tb->titleBarState & Qt::WindowMaximized)));
3746 if (drawNormalButton) {
3747 populateTitleBarButtonTheme(proxy(), widget, option, SC_TitleBarNormalButton, isActive, WP_RESTOREBUTTON, &theme);
3748 d->drawBackground(theme);
3749 }
3750 if (sub & SC_TitleBarShadeButton && tb->titleBarFlags & Qt::WindowShadeButtonHint
3751 && !(tb->titleBarState & Qt::WindowMinimized)) {
3752 populateTitleBarButtonTheme(proxy(), widget, option, SC_TitleBarShadeButton, isActive, WP_MINBUTTON, &theme);
3753 d->drawBackground(theme);
3754 }
3755 if (sub & SC_TitleBarUnshadeButton && tb->titleBarFlags & Qt::WindowShadeButtonHint
3756 && tb->titleBarState & Qt::WindowMinimized) {
3757 populateTitleBarButtonTheme(proxy(), widget, option, SC_TitleBarUnshadeButton, isActive, WP_RESTOREBUTTON, &theme);
3758 d->drawBackground(theme);
3759 }
3760 if (sub & SC_TitleBarCloseButton && tb->titleBarFlags & Qt::WindowSystemMenuHint) {
3761 populateTitleBarButtonTheme(proxy(), widget, option, SC_TitleBarCloseButton, isActive, WP_CLOSEBUTTON, &theme);
3762 d->drawBackground(theme);
3763 }
3764 }
3765 break;
3766
3767#if QT_CONFIG(mdiarea)
3768 case CC_MdiControls: {
3769 QWindowsThemeData theme(widget, painter, QWindowsVistaStylePrivate::WindowTheme, WP_MDICLOSEBUTTON, CBS_NORMAL);
3770 if (Q_UNLIKELY(!theme.isValid()))
3771 return;
3772
3773 if (option->subControls.testFlag(SC_MdiCloseButton)) {
3774 populateMdiButtonTheme(proxy(), widget, option, SC_MdiCloseButton, WP_MDICLOSEBUTTON, &theme);
3775 d->drawBackground(theme, mdiButtonCorrectionFactor(theme, widget));
3776 }
3777 if (option->subControls.testFlag(SC_MdiNormalButton)) {
3778 populateMdiButtonTheme(proxy(), widget, option, SC_MdiNormalButton, WP_MDIRESTOREBUTTON, &theme);
3779 d->drawBackground(theme, mdiButtonCorrectionFactor(theme, widget));
3780 }
3781 if (option->subControls.testFlag(QStyle::SC_MdiMinButton)) {
3782 populateMdiButtonTheme(proxy(), widget, option, SC_MdiMinButton, WP_MDIMINBUTTON, &theme);
3783 d->drawBackground(theme, mdiButtonCorrectionFactor(theme, widget));
3784 }
3785 break;
3786 }
3787#endif // QT_CONFIG(mdiarea)
3788
3789#if QT_CONFIG(dial)
3790 case CC_Dial:
3791 if (const auto *dial = qstyleoption_cast<const QStyleOptionSlider *>(option))
3792 QStyleHelper::drawDial(dial, painter);
3793 break;
3794#endif // QT_CONFIG(dial)
3795
3796 case CC_ComboBox:
3797 if (const QStyleOptionComboBox *cmb = qstyleoption_cast<const QStyleOptionComboBox *>(option)) {
3798 if (cmb->editable) {
3799 if (sub & SC_ComboBoxEditField) {
3800 partId = EP_EDITBORDER_NOSCROLL;
3801 if (!(flags & State_Enabled))
3802 stateId = ETS_DISABLED;
3803 else if (flags & State_MouseOver)
3804 stateId = ETS_HOT;
3805 else if (flags & State_HasFocus)
3806 stateId = ETS_FOCUSED;
3807 else
3808 stateId = ETS_NORMAL;
3809
3810 QWindowsThemeData theme(widget, painter,
3811 QWindowsVistaStylePrivate::EditTheme,
3812 partId, stateId, r);
3813
3814 d->drawBackground(theme);
3815 }
3816 if (sub & SC_ComboBoxArrow) {
3817 QRect subRect = proxy()->subControlRect(CC_ComboBox, option, SC_ComboBoxArrow, widget);
3818 QWindowsThemeData theme(widget, painter, QWindowsVistaStylePrivate::ComboboxTheme);
3819 theme.rect = subRect;
3820 partId = option->direction == Qt::RightToLeft ? CP_DROPDOWNBUTTONLEFT : CP_DROPDOWNBUTTONRIGHT;
3821
3822 if (!(cmb->state & State_Enabled))
3823 stateId = CBXS_DISABLED;
3824 else if (cmb->state & State_Sunken || cmb->state & State_On)
3825 stateId = CBXS_PRESSED;
3826 else if (cmb->state & State_MouseOver && option->activeSubControls & SC_ComboBoxArrow)
3827 stateId = CBXS_HOT;
3828 else
3829 stateId = CBXS_NORMAL;
3830
3831 theme.partId = partId;
3832 theme.stateId = stateId;
3833 d->drawBackground(theme);
3834 }
3835
3836 } else {
3837 if (sub & SC_ComboBoxFrame) {
3838 QWindowsThemeData theme(widget, painter, QWindowsVistaStylePrivate::ComboboxTheme);
3839 theme.rect = option->rect;
3840 theme.partId = CP_READONLY;
3841 if (!(cmb->state & State_Enabled))
3842 theme.stateId = CBXS_DISABLED;
3843 else if (cmb->state & State_Sunken || cmb->state & State_On)
3844 theme.stateId = CBXS_PRESSED;
3845 else if (cmb->state & State_MouseOver)
3846 theme.stateId = CBXS_HOT;
3847 else
3848 theme.stateId = CBXS_NORMAL;
3849 d->drawBackground(theme);
3850 }
3851 if (sub & SC_ComboBoxArrow) {
3852 QWindowsThemeData theme(widget, painter, QWindowsVistaStylePrivate::ComboboxTheme);
3853 theme.rect = proxy()->subControlRect(CC_ComboBox, option, SC_ComboBoxArrow, widget);
3854 theme.partId = option->direction == Qt::RightToLeft ? CP_DROPDOWNBUTTONLEFT : CP_DROPDOWNBUTTONRIGHT;
3855 if (!(cmb->state & State_Enabled))
3856 theme.stateId = CBXS_DISABLED;
3857 else
3858 theme.stateId = CBXS_NORMAL;
3859 d->drawBackground(theme);
3860 }
3861 if ((sub & SC_ComboBoxEditField) && (flags & State_HasFocus)) {
3862 QStyleOptionFocusRect fropt;
3863 fropt.QStyleOption::operator=(*cmb);
3864 fropt.rect = proxy()->subControlRect(CC_ComboBox, option, SC_ComboBoxEditField, widget);
3865 proxy()->drawPrimitive(PE_FrameFocusRect, &fropt, painter, widget);
3866 }
3867 }
3868 }
3869 break;
3870
3871 case CC_ScrollBar:
3872 if (const QStyleOptionSlider *scrollbar = qstyleoption_cast<const QStyleOptionSlider *>(option)) {
3873 QWindowsThemeData theme(widget, painter, QWindowsVistaStylePrivate::ScrollBarTheme);
3874 bool maxedOut = (scrollbar->maximum == scrollbar->minimum);
3875 if (maxedOut)
3876 flags &= ~State_Enabled;
3877
3878 bool isHorz = flags & State_Horizontal;
3879 bool isRTL = option->direction == Qt::RightToLeft;
3880 if (sub & SC_ScrollBarAddLine) {
3881 theme.rect = proxy()->subControlRect(CC_ScrollBar, option, SC_ScrollBarAddLine, widget);
3882 partId = SBP_ARROWBTN;
3883 if (!(flags & State_Enabled))
3884 stateId = (isHorz ? (isRTL ? ABS_LEFTDISABLED : ABS_RIGHTDISABLED) : ABS_DOWNDISABLED);
3885 else if (scrollbar->activeSubControls & SC_ScrollBarAddLine && (scrollbar->state & State_Sunken))
3886 stateId = (isHorz ? (isRTL ? ABS_LEFTPRESSED : ABS_RIGHTPRESSED) : ABS_DOWNPRESSED);
3887 else if (scrollbar->activeSubControls & SC_ScrollBarAddLine && (scrollbar->state & State_MouseOver))
3888 stateId = (isHorz ? (isRTL ? ABS_LEFTHOT : ABS_RIGHTHOT) : ABS_DOWNHOT);
3889 else if (scrollbar->state & State_MouseOver)
3890 stateId = (isHorz ? (isRTL ? ABS_LEFTHOVER : ABS_RIGHTHOVER) : ABS_DOWNHOVER);
3891 else
3892 stateId = (isHorz ? (isRTL ? ABS_LEFTNORMAL : ABS_RIGHTNORMAL) : ABS_DOWNNORMAL);
3893 theme.partId = partId;
3894 theme.stateId = stateId;
3895 d->drawBackground(theme);
3896 }
3897 if (sub & SC_ScrollBarSubLine) {
3898 theme.rect = proxy()->subControlRect(CC_ScrollBar, option, SC_ScrollBarSubLine, widget);
3899 partId = SBP_ARROWBTN;
3900 if (!(flags & State_Enabled))
3901 stateId = (isHorz ? (isRTL ? ABS_RIGHTDISABLED : ABS_LEFTDISABLED) : ABS_UPDISABLED);
3902 else if (scrollbar->activeSubControls & SC_ScrollBarSubLine && (scrollbar->state & State_Sunken))
3903 stateId = (isHorz ? (isRTL ? ABS_RIGHTPRESSED : ABS_LEFTPRESSED) : ABS_UPPRESSED);
3904 else if (scrollbar->activeSubControls & SC_ScrollBarSubLine && (scrollbar->state & State_MouseOver))
3905 stateId = (isHorz ? (isRTL ? ABS_RIGHTHOT : ABS_LEFTHOT) : ABS_UPHOT);
3906 else if (scrollbar->state & State_MouseOver)
3907 stateId = (isHorz ? (isRTL ? ABS_RIGHTHOVER : ABS_LEFTHOVER) : ABS_UPHOVER);
3908 else
3909 stateId = (isHorz ? (isRTL ? ABS_RIGHTNORMAL : ABS_LEFTNORMAL) : ABS_UPNORMAL);
3910 theme.partId = partId;
3911 theme.stateId = stateId;
3912 d->drawBackground(theme);
3913 }
3914 if (maxedOut) {
3915 theme.rect = proxy()->subControlRect(CC_ScrollBar, option, SC_ScrollBarSlider, widget);
3916 theme.rect = theme.rect.united(proxy()->subControlRect(CC_ScrollBar, option, SC_ScrollBarSubPage, widget));
3917 theme.rect = theme.rect.united(proxy()->subControlRect(CC_ScrollBar, option, SC_ScrollBarAddPage, widget));
3918 partId = flags & State_Horizontal ? SBP_LOWERTRACKHORZ : SBP_LOWERTRACKVERT;
3919 stateId = SCRBS_DISABLED;
3920 theme.partId = partId;
3921 theme.stateId = stateId;
3922 d->drawBackground(theme);
3923 } else {
3924 if (sub & SC_ScrollBarSubPage) {
3925 theme.rect = proxy()->subControlRect(CC_ScrollBar, option, SC_ScrollBarSubPage, widget);
3926 partId = flags & State_Horizontal ? SBP_UPPERTRACKHORZ : SBP_UPPERTRACKVERT;
3927 if (!(flags & State_Enabled))
3928 stateId = SCRBS_DISABLED;
3929 else if (scrollbar->activeSubControls & SC_ScrollBarSubPage && (scrollbar->state & State_Sunken))
3930 stateId = SCRBS_PRESSED;
3931 else if (scrollbar->activeSubControls & SC_ScrollBarSubPage && (scrollbar->state & State_MouseOver))
3932 stateId = SCRBS_HOT;
3933 else
3934 stateId = SCRBS_NORMAL;
3935 theme.partId = partId;
3936 theme.stateId = stateId;
3937 d->drawBackground(theme);
3938 }
3939 if (sub & SC_ScrollBarAddPage) {
3940 theme.rect = proxy()->subControlRect(CC_ScrollBar, option, SC_ScrollBarAddPage, widget);
3941 partId = flags & State_Horizontal ? SBP_LOWERTRACKHORZ : SBP_LOWERTRACKVERT;
3942 if (!(flags & State_Enabled))
3943 stateId = SCRBS_DISABLED;
3944 else if (scrollbar->activeSubControls & SC_ScrollBarAddPage && (scrollbar->state & State_Sunken))
3945 stateId = SCRBS_PRESSED;
3946 else if (scrollbar->activeSubControls & SC_ScrollBarAddPage && (scrollbar->state & State_MouseOver))
3947 stateId = SCRBS_HOT;
3948 else
3949 stateId = SCRBS_NORMAL;
3950 theme.partId = partId;
3951 theme.stateId = stateId;
3952 d->drawBackground(theme);
3953 }
3954 if (sub & SC_ScrollBarSlider) {
3955 theme.rect = proxy()->subControlRect(CC_ScrollBar, option, SC_ScrollBarSlider, widget);
3956 if (!(flags & State_Enabled))
3957 stateId = SCRBS_DISABLED;
3958 else if (scrollbar->activeSubControls & SC_ScrollBarSlider && (scrollbar->state & State_Sunken))
3959 stateId = SCRBS_PRESSED;
3960 else if (scrollbar->activeSubControls & SC_ScrollBarSlider && (scrollbar->state & State_MouseOver))
3961 stateId = SCRBS_HOT;
3962 else if (option->state & State_MouseOver)
3963 stateId = SCRBS_HOVER;
3964 else
3965 stateId = SCRBS_NORMAL;
3966
3967 // Draw handle
3968 theme.partId = flags & State_Horizontal ? SBP_THUMBBTNHORZ : SBP_THUMBBTNVERT;
3969 theme.stateId = stateId;
3970 d->drawBackground(theme);
3971 }
3972 }
3973 }
3974 break;
3975
3976#if QT_CONFIG(spinbox)
3977 case CC_SpinBox:
3978 if (const QStyleOptionSpinBox *sb = qstyleoption_cast<const QStyleOptionSpinBox *>(option)) {
3979 QWindowsThemeData theme(widget, painter, QWindowsVistaStylePrivate::SpinTheme);
3980 if (sb->frame && (sub & SC_SpinBoxFrame)) {
3981 partId = EP_EDITBORDER_NOSCROLL;
3982 if (!(flags & State_Enabled))
3983 stateId = ETS_DISABLED;
3984 else if (flags & State_MouseOver)
3985 stateId = ETS_HOT;
3986 else if (flags & State_HasFocus)
3987 stateId = ETS_SELECTED;
3988 else
3989 stateId = ETS_NORMAL;
3990
3991 QWindowsThemeData ftheme(widget, painter,
3992 QWindowsVistaStylePrivate::EditTheme,
3993 partId, stateId, r);
3994 // The spinbox in Windows QStyle is drawn with frameless QLineEdit inside it
3995 // That however breaks with QtQuickControls where this results in transparent
3996 // spinbox background, so if there's no "widget" passed (QtQuickControls case),
3997 // let ftheme.noContent be false, which fixes the spinbox rendering in QQC
3998 ftheme.noContent = (widget != nullptr);
3999 d->drawBackground(ftheme);
4000 }
4001 if (sub & SC_SpinBoxUp) {
4002 theme.rect = proxy()->subControlRect(CC_SpinBox, option, SC_SpinBoxUp, widget).adjusted(0, 0, 0, 1);
4003 partId = SPNP_UP;
4004 if (!(sb->stepEnabled & QAbstractSpinBox::StepUpEnabled) || !(flags & State_Enabled))
4005 stateId = UPS_DISABLED;
4006 else if (sb->activeSubControls == SC_SpinBoxUp && (sb->state & State_Sunken))
4007 stateId = UPS_PRESSED;
4008 else if (sb->activeSubControls == SC_SpinBoxUp && (sb->state & State_MouseOver))
4009 stateId = UPS_HOT;
4010 else
4011 stateId = UPS_NORMAL;
4012 theme.partId = partId;
4013 theme.stateId = stateId;
4014 d->drawBackground(theme);
4015 }
4016 if (sub & SC_SpinBoxDown) {
4017 theme.rect = proxy()->subControlRect(CC_SpinBox, option, SC_SpinBoxDown, widget);
4018 partId = SPNP_DOWN;
4019 if (!(sb->stepEnabled & QAbstractSpinBox::StepDownEnabled) || !(flags & State_Enabled))
4020 stateId = DNS_DISABLED;
4021 else if (sb->activeSubControls == SC_SpinBoxDown && (sb->state & State_Sunken))
4022 stateId = DNS_PRESSED;
4023 else if (sb->activeSubControls == SC_SpinBoxDown && (sb->state & State_MouseOver))
4024 stateId = DNS_HOT;
4025 else
4026 stateId = DNS_NORMAL;
4027 theme.partId = partId;
4028 theme.stateId = stateId;
4029 d->drawBackground(theme);
4030 }
4031 }
4032 break;
4033#endif // QT_CONFIG(spinbox)
4034
4035 default:
4036 QWindowsStyle::drawComplexControl(control, option, painter, widget);
4037 break;
4038 }
4039}
4040
4041/*!
4042 \internal
4043 */
4044QSize QWindowsVistaStyle::sizeFromContents(ContentsType type, const QStyleOption *option,
4045 const QSize &size, const QWidget *widget) const
4046{
4047 if (!QWindowsVistaStylePrivate::useVista())
4048 return QWindowsStyle::sizeFromContents(type, option, size, widget);
4049
4050 QSize contentSize(size);
4051
4052 switch (type) {
4053 case CT_LineEdit:
4054 case CT_ComboBox: {
4055 QWindowsThemeData buttontheme(widget, nullptr, QWindowsVistaStylePrivate::ButtonTheme, BP_PUSHBUTTON, PBS_NORMAL);
4056 if (buttontheme.isValid()) {
4057 const qreal factor = QWindowsStylePrivate::nativeMetricScaleFactor(widget);
4058 const QMarginsF borderSize = buttontheme.margins() * factor;
4059 if (!borderSize.isNull()) {
4060 const qreal margin = qreal(2) * factor;
4061 contentSize.rwidth() += qRound(borderSize.left() + borderSize.right() - margin);
4062 contentSize.rheight() += int(borderSize.bottom() + borderSize.top() - margin
4063 + qreal(1) / factor - 1);
4064 }
4065 const int textMargins = 2*(proxy()->pixelMetric(PM_FocusFrameHMargin, option) + 1);
4066 contentSize += QSize(qMax(pixelMetric(QStyle::PM_ScrollBarExtent, option, widget)
4067 + textMargins, 23), 0); //arrow button
4068 }
4069 break;
4070 }
4071
4072 case CT_TabWidget:
4073 contentSize += QSize(6, 6);
4074 break;
4075
4076 case CT_Menu:
4077 contentSize += QSize(1, 0);
4078 break;
4079
4080#if QT_CONFIG(menubar)
4081 case CT_MenuBarItem:
4082 if (!contentSize.isEmpty())
4083 contentSize += QSize(windowsItemHMargin * 5 + 1, 5);
4084 break;
4085#endif
4086
4087 case CT_MenuItem: {
4088 contentSize = QWindowsStyle::sizeFromContents(type, option, size, widget);
4089 QWindowsThemeData theme(widget, nullptr,
4090 QWindowsVistaStylePrivate::MenuTheme,
4091 MENU_POPUPCHECKBACKGROUND, MBI_HOT);
4092 QWindowsThemeData themeSize = theme;
4093 themeSize.partId = MENU_POPUPCHECK;
4094 themeSize.stateId = 0;
4095 const QSizeF size = themeSize.size() * QWindowsStylePrivate::nativeMetricScaleFactor(widget);
4096 const QMarginsF margins = themeSize.margins() * QWindowsStylePrivate::nativeMetricScaleFactor(widget);
4097 int minimumHeight = qMax(qRound(size.height() + margins.bottom() + margins.top()), contentSize.height());
4098 contentSize.rwidth() += qRound(size.width() + margins.left() + margins.right());
4099 if (const QStyleOptionMenuItem *menuitem = qstyleoption_cast<const QStyleOptionMenuItem *>(option)) {
4100 if (menuitem->menuItemType != QStyleOptionMenuItem::Separator)
4101 contentSize.setHeight(minimumHeight);
4102 }
4103 break;
4104 }
4105
4106 case CT_MdiControls: {
4107 contentSize.setHeight(int(QStyleHelper::dpiScaled(19, option)));
4108 int width = 54;
4109 if (const auto *styleOpt = qstyleoption_cast<const QStyleOptionComplex *>(option)) {
4110 width = 0;
4111 if (styleOpt->subControls & SC_MdiMinButton)
4112 width += 17 + 1;
4113 if (styleOpt->subControls & SC_MdiNormalButton)
4114 width += 17 + 1;
4115 if (styleOpt->subControls & SC_MdiCloseButton)
4116 width += 17 + 1;
4117 }
4118 contentSize.setWidth(int(QStyleHelper::dpiScaled(width, option)));
4119 break;
4120 }
4121
4122 case CT_ItemViewItem:
4123 contentSize = QWindowsStyle::sizeFromContents(type, option, size, widget);
4124 contentSize.rheight() += 2;
4125 break;
4126
4127 case CT_SpinBox: {
4128 //Spinbox adds frame twice
4129 contentSize = QWindowsStyle::sizeFromContents(type, option, size, widget);
4130 int border = proxy()->pixelMetric(PM_SpinBoxFrameWidth, option, widget);
4131 contentSize -= QSize(2*border, 2*border);
4132 break;
4133 }
4134
4135 case CT_HeaderSection:
4136 // When there is a sort indicator it adds to the width but it is shown
4137 // above the text natively and not on the side
4138 if (QStyleOptionHeader *hdr = qstyleoption_cast<QStyleOptionHeader *>(const_cast<QStyleOption *>(option))) {
4139 QStyleOptionHeader::SortIndicator sortInd = hdr->sortIndicator;
4140 hdr->sortIndicator = QStyleOptionHeader::None;
4141 contentSize = QWindowsStyle::sizeFromContents(type, hdr, size, widget);
4142 hdr->sortIndicator = sortInd;
4143 }
4144 break;
4145
4146 default:
4147 contentSize = QWindowsStyle::sizeFromContents(type, option, size, widget);
4148 break;
4149 }
4150
4151 return contentSize;
4152}
4153
4154/*!
4155 \internal
4156 */
4157QRect QWindowsVistaStyle::subElementRect(SubElement element, const QStyleOption *option, const QWidget *widget) const
4158{
4159 if (!QWindowsVistaStylePrivate::useVista())
4160 return QWindowsStyle::subElementRect(element, option, widget);
4161
4162 QRect rect(option->rect);
4163
4164 switch (element) {
4165 case SE_PushButtonContents:
4166 if (const QStyleOptionButton *btn = qstyleoption_cast<const QStyleOptionButton *>(option)) {
4167 MARGINS borderSize;
4168 const HTHEME theme = OpenThemeData(widget ? QWindowsVistaStylePrivate::winId(widget) : nullptr, L"Button");
4169 if (theme) {
4170 int stateId = PBS_NORMAL;
4171 if (!(option->state & State_Enabled))
4172 stateId = PBS_DISABLED;
4173 else if (option->state & State_Sunken)
4174 stateId = PBS_PRESSED;
4175 else if (option->state & State_MouseOver)
4176 stateId = PBS_HOT;
4177 else if (btn->features & QStyleOptionButton::DefaultButton)
4178 stateId = PBS_DEFAULTED;
4179
4180 int border = proxy()->pixelMetric(PM_DefaultFrameWidth, btn, widget);
4181 rect = option->rect.adjusted(border, border, -border, -border);
4182
4183 if (SUCCEEDED(GetThemeMargins(theme, nullptr, BP_PUSHBUTTON, stateId, TMT_CONTENTMARGINS, nullptr, &borderSize))) {
4184 rect.adjust(borderSize.cxLeftWidth, borderSize.cyTopHeight,
4185 -borderSize.cxRightWidth, -borderSize.cyBottomHeight);
4186 rect = visualRect(option->direction, option->rect, rect);
4187 }
4188 }
4189 }
4190 break;
4191
4192 case SE_DockWidgetCloseButton:
4193 case SE_DockWidgetFloatButton:
4194 rect = QWindowsStyle::subElementRect(element, option, widget);
4195 return rect.translated(0, 1);
4196
4197 case SE_TabWidgetTabContents:
4198 rect = QWindowsStyle::subElementRect(element, option, widget);
4199#if QT_CONFIG(tabwidget)
4200 if (qstyleoption_cast<const QStyleOptionTabWidgetFrame *>(option)) {
4201 rect = QWindowsStyle::subElementRect(element, option, widget);
4202 if (const QTabWidget *tabWidget = qobject_cast<const QTabWidget *>(widget)) {
4203 if (tabWidget->documentMode())
4204 break;
4205 rect.adjust(0, 0, -2, -2);
4206 }
4207 }
4208#endif // QT_CONFIG(tabwidget)
4209 break;
4210
4211 case SE_TabWidgetTabBar: {
4212 rect = QWindowsStyle::subElementRect(element, option, widget);
4213#if QT_CONFIG(tabwidget)
4214 const auto *twfOption = qstyleoption_cast<const QStyleOptionTabWidgetFrame *>(option);
4215 if (twfOption && twfOption->direction == Qt::RightToLeft
4216 && (twfOption->shape == QTabBar::RoundedNorth
4217 || twfOption->shape == QTabBar::RoundedSouth))
4218 {
4219 QStyleOptionTab otherOption;
4220 otherOption.shape = (twfOption->shape == QTabBar::RoundedNorth
4221 ? QTabBar::RoundedEast : QTabBar::RoundedSouth);
4222 int overlap = proxy()->pixelMetric(PM_TabBarBaseOverlap, &otherOption, widget);
4223 int borderThickness = proxy()->pixelMetric(PM_DefaultFrameWidth, option, widget);
4224 rect.adjust(-overlap + borderThickness, 0, -overlap + borderThickness, 0);
4225 }
4226#endif // QT_CONFIG(tabwidget)
4227 break;
4228 }
4229
4230 case SE_HeaderArrow: {
4231 rect = QWindowsStyle::subElementRect(element, option, widget);
4232 QRect r = rect;
4233 int h = option->rect.height();
4234 int w = option->rect.width();
4235 int x = option->rect.x();
4236 int y = option->rect.y();
4237 int margin = proxy()->pixelMetric(QStyle::PM_HeaderMargin, option, widget);
4238
4239 QWindowsThemeData theme(widget, nullptr,
4240 QWindowsVistaStylePrivate::HeaderTheme,
4241 HP_HEADERSORTARROW, HSAS_SORTEDDOWN, option->rect);
4242
4243 int arrowWidth = 13;
4244 int arrowHeight = 5;
4245 if (theme.isValid()) {
4246 const QSizeF size = theme.size() * QWindowsStylePrivate::nativeMetricScaleFactor(widget);
4247 if (!size.isEmpty()) {
4248 arrowWidth = qRound(size.width());
4249 arrowHeight = qRound(size.height());
4250 }
4251 }
4252 if (option->state & State_Horizontal) {
4253 r.setRect(x + w/2 - arrowWidth/2, y , arrowWidth, arrowHeight);
4254 } else {
4255 int vert_size = w / 2;
4256 r.setRect(x + 5, y + h - margin * 2 - vert_size,
4257 w - margin * 2 - 5, vert_size);
4258 }
4259 rect = visualRect(option->direction, option->rect, r);
4260 break;
4261 }
4262
4263 case SE_HeaderLabel: {
4264 rect = QWindowsStyle::subElementRect(element, option, widget);
4265 int margin = proxy()->pixelMetric(QStyle::PM_HeaderMargin, option, widget);
4266 QRect r = option->rect;
4267 r.setRect(option->rect.x() + margin, option->rect.y() + margin,
4268 option->rect.width() - margin * 2, option->rect.height() - margin * 2);
4269 if (const QStyleOptionHeader *header = qstyleoption_cast<const QStyleOptionHeader *>(option)) {
4270 // Subtract width needed for arrow, if there is one
4271 if (header->sortIndicator != QStyleOptionHeader::None) {
4272 if (!(option->state & State_Horizontal)) //horizontal arrows are positioned on top
4273 r.setHeight(r.height() - (option->rect.width() / 2) - (margin * 2));
4274 }
4275 }
4276 rect = visualRect(option->direction, option->rect, r);
4277 break;
4278 }
4279
4280 case SE_ProgressBarContents:
4281 rect = QCommonStyle::subElementRect(SE_ProgressBarGroove, option, widget);
4282 break;
4283
4284 case SE_ItemViewItemFocusRect:
4285 rect = QWindowsStyle::subElementRect(element, option, widget);
4286 if (const QStyleOptionViewItem *vopt = qstyleoption_cast<const QStyleOptionViewItem *>(option)) {
4287 QRect textRect = subElementRect(QStyle::SE_ItemViewItemText, option, widget);
4288 QRect displayRect = subElementRect(QStyle::SE_ItemViewItemDecoration, option, widget);
4289 if (!vopt->icon.isNull())
4290 rect = textRect.united(displayRect);
4291 else
4292 rect = textRect;
4293 rect = rect.adjusted(1, 0, -1, 0);
4294 }
4295 break;
4296
4297 default:
4298 rect = QWindowsStyle::subElementRect(element, option, widget);
4299 break;
4300 }
4301
4302 return rect;
4303}
4304
4305/*!
4306 \internal
4307 */
4308QRect QWindowsVistaStyle::subControlRect(ComplexControl control, const QStyleOptionComplex *option,
4309 SubControl subControl, const QWidget *widget) const
4310{
4311 if (!QWindowsVistaStylePrivate::useVista())
4312 return QWindowsStyle::subControlRect(control, option, subControl, widget);
4313
4314 QRect rect;
4315
4316 switch (control) {
4317#if QT_CONFIG(combobox)
4318 case CC_ComboBox:
4319 if (const auto *cb = qstyleoption_cast<const QStyleOptionComboBox *>(option)) {
4320 const int x = cb->rect.x(), y = cb->rect.y(), wi = cb->rect.width(), he = cb->rect.height();
4321 const int margin = cb->frame ? 3 : 0;
4322 const int bmarg = cb->frame ? 2 : 0;
4323 const int arrowWidth = qRound(QStyleHelper::dpiScaled(16, option));
4324 const int arrowButtonWidth = bmarg + arrowWidth;
4325 const int xpos = x + wi - arrowButtonWidth;
4326
4327 switch (subControl) {
4328 case SC_ComboBoxFrame:
4329 case SC_ComboBoxListBoxPopup:
4330 rect = cb->rect;
4331 break;
4332
4333 case SC_ComboBoxArrow: {
4334 rect.setRect(xpos, y , arrowButtonWidth, he);
4335 }
4336 break;
4337
4338 case SC_ComboBoxEditField: {
4339 rect.setRect(x + margin, y + margin, wi - 2 * margin - arrowWidth, he - 2 * margin);
4340 }
4341 break;
4342
4343 default:
4344 break;
4345 }
4346 }
4347 break;
4348#endif // QT_CONFIG(combobox)
4349
4350 case CC_TitleBar:
4351 if (const QStyleOptionTitleBar *tb = qstyleoption_cast<const QStyleOptionTitleBar *>(option)) {
4352 if (!buttonVisible(subControl, tb))
4353 return rect;
4354 const qreal factor = QWindowsStylePrivate::nativeMetricScaleFactor(widget);
4355 const bool isToolTitle = false;
4356 const int height = tb->rect.height();
4357 const int width = tb->rect.width();
4358
4359 const int buttonMargin = int(QStyleHelper::dpiScaled(4, option));
4360 int buttonHeight = qRound(qreal(GetSystemMetrics(SM_CYSIZE)) * factor)
4361 - buttonMargin;
4362 const int buttonWidth =
4363 qRound(qreal(GetSystemMetrics(SM_CXSIZE)) * factor - QStyleHelper::dpiScaled(4, option));
4364
4365 const int frameWidth = proxy()->pixelMetric(PM_MdiSubWindowFrameWidth, option, widget);
4366 const bool sysmenuHint = (tb->titleBarFlags & Qt::WindowSystemMenuHint) != 0;
4367 const bool minimizeHint = (tb->titleBarFlags & Qt::WindowMinimizeButtonHint) != 0;
4368 const bool maximizeHint = (tb->titleBarFlags & Qt::WindowMaximizeButtonHint) != 0;
4369 const bool contextHint = (tb->titleBarFlags & Qt::WindowContextHelpButtonHint) != 0;
4370 const bool shadeHint = (tb->titleBarFlags & Qt::WindowShadeButtonHint) != 0;
4371
4372 bool isMinimized = tb->titleBarState & Qt::WindowMinimized;
4373 bool isMaximized = tb->titleBarState & Qt::WindowMaximized;
4374 int offset = 0;
4375 const int delta = buttonWidth + 2;
4376 int controlTop = option->rect.bottom() - buttonHeight - 2;
4377
4378 switch (subControl) {
4379 case SC_TitleBarLabel: {
4380 rect = QRect(frameWidth, 0, width - (buttonWidth + frameWidth + 10), height);
4381 if (isToolTitle) {
4382 if (sysmenuHint) {
4383 rect.adjust(0, 0, int(-buttonWidth - 3 * factor), 0);
4384 }
4385 if (minimizeHint || maximizeHint)
4386 rect.adjust(0, 0, int(-buttonWidth - 2 * factor), 0);
4387 } else {
4388 if (sysmenuHint) {
4389 const int leftOffset = int(height - 8 * factor);
4390 rect.adjust(leftOffset, 0, 0, int(4 * factor));
4391 }
4392 if (minimizeHint)
4393 rect.adjust(0, 0, int(-buttonWidth - 2 * factor), 0);
4394 if (maximizeHint)
4395 rect.adjust(0, 0, int(-buttonWidth - 2 * factor), 0);
4396 if (contextHint)
4397 rect.adjust(0, 0, int(-buttonWidth - 2 * factor), 0);
4398 if (shadeHint)
4399 rect.adjust(0, 0, int(-buttonWidth - 2 * factor), 0);
4400 }
4401 rect.translate(0, int(2 * factor));
4402 }
4403 break;
4404
4405 case SC_TitleBarContextHelpButton:
4406 if (tb->titleBarFlags & Qt::WindowContextHelpButtonHint)
4407 offset += delta;
4408 Q_FALLTHROUGH();
4409 case SC_TitleBarMinButton:
4410 if (!isMinimized && (tb->titleBarFlags & Qt::WindowMinimizeButtonHint))
4411 offset += delta;
4412 else if (subControl == SC_TitleBarMinButton)
4413 break;
4414 Q_FALLTHROUGH();
4415 case SC_TitleBarNormalButton:
4416 if (isMinimized && (tb->titleBarFlags & Qt::WindowMinimizeButtonHint))
4417 offset += delta;
4418 else if (isMaximized && (tb->titleBarFlags & Qt::WindowMaximizeButtonHint))
4419 offset += delta;
4420 else if (subControl == SC_TitleBarNormalButton)
4421 break;
4422 Q_FALLTHROUGH();
4423 case SC_TitleBarMaxButton:
4424 if (!isMaximized && (tb->titleBarFlags & Qt::WindowMaximizeButtonHint))
4425 offset += delta;
4426 else if (subControl == SC_TitleBarMaxButton)
4427 break;
4428 Q_FALLTHROUGH();
4429 case SC_TitleBarShadeButton:
4430 if (!isMinimized && (tb->titleBarFlags & Qt::WindowShadeButtonHint))
4431 offset += delta;
4432 else if (subControl == SC_TitleBarShadeButton)
4433 break;
4434 Q_FALLTHROUGH();
4435 case SC_TitleBarUnshadeButton:
4436 if (isMinimized && (tb->titleBarFlags & Qt::WindowShadeButtonHint))
4437 offset += delta;
4438 else if (subControl == SC_TitleBarUnshadeButton)
4439 break;
4440 Q_FALLTHROUGH();
4441 case SC_TitleBarCloseButton:
4442 if (tb->titleBarFlags & Qt::WindowSystemMenuHint)
4443 offset += delta;
4444 else if (subControl == SC_TitleBarCloseButton)
4445 break;
4446
4447 rect.setRect(width - offset - controlTop + 1, controlTop,
4448 buttonWidth, buttonHeight);
4449 break;
4450
4451 case SC_TitleBarSysMenu: {
4452 const int controlTop = int(6 * factor);
4453 const int controlHeight = int(height - controlTop - 3 * factor);
4454 int iconExtent = proxy()->pixelMetric(PM_SmallIconSize, option);
4455 QSize iconSize = tb->icon.actualSize(QSize(iconExtent, iconExtent));
4456 if (tb->icon.isNull())
4457 iconSize = QSize(controlHeight, controlHeight);
4458 int hPad = (controlHeight - iconSize.height())/2;
4459 int vPad = (controlHeight - iconSize.width())/2;
4460 rect = QRect(frameWidth + hPad, controlTop + vPad, iconSize.width(), iconSize.height());
4461 rect.translate(0, int(3 * factor));
4462 }
4463 break;
4464
4465 default:
4466 break;
4467 }
4468 }
4469 break;
4470
4471#if QT_CONFIG(mdiarea)
4472 case CC_MdiControls: {
4473 int numSubControls = 0;
4474 if (option->subControls & SC_MdiCloseButton)
4475 ++numSubControls;
4476 if (option->subControls & SC_MdiMinButton)
4477 ++numSubControls;
4478 if (option->subControls & SC_MdiNormalButton)
4479 ++numSubControls;
4480 if (numSubControls == 0)
4481 break;
4482
4483 int buttonWidth = option->rect.width() / numSubControls;
4484 int offset = 0;
4485
4486 switch (subControl) {
4487 case SC_MdiCloseButton:
4488 // Only one sub control, no offset needed.
4489 if (numSubControls == 1)
4490 break;
4491 offset += buttonWidth;
4492 Q_FALLTHROUGH();
4493 case SC_MdiNormalButton:
4494 // No offset needed if
4495 // 1) There's only one sub control
4496 // 2) We have a close button and a normal button (offset already added in SC_MdiClose)
4497 if (numSubControls == 1 || (numSubControls == 2 && !(option->subControls & SC_MdiMinButton)))
4498 break;
4499 if (option->subControls & SC_MdiNormalButton)
4500 offset += buttonWidth;
4501 break;
4502 default:
4503 break;
4504 }
4505
4506 rect = QRect(offset, 0, buttonWidth, option->rect.height());
4507 break;
4508 }
4509#endif // QT_CONFIG(mdiarea)
4510
4511 default:
4512 return QWindowsStyle::subControlRect(control, option, subControl, widget);
4513 }
4514
4515 return visualRect(option->direction, option->rect, rect);
4516}
4517
4518/*!
4519 \internal
4520 */
4521QStyle::SubControl QWindowsVistaStyle::hitTestComplexControl(ComplexControl control, const QStyleOptionComplex *option,
4522 const QPoint &pos, const QWidget *widget) const
4523{
4524 return QWindowsStyle::hitTestComplexControl(control, option, pos, widget);
4525}
4526
4527/*!
4528 \internal
4529 */
4530int QWindowsVistaStyle::pixelMetric(PixelMetric metric, const QStyleOption *option, const QWidget *widget) const
4531{
4532 if (!QWindowsVistaStylePrivate::useVista())
4533 return QWindowsStyle::pixelMetric(metric, option, widget);
4534
4535 int ret = QWindowsVistaStylePrivate::fixedPixelMetric(metric);
4536 if (ret != QWindowsStylePrivate::InvalidMetric)
4537 return int(QStyleHelper::dpiScaled(ret, option));
4538
4539 int res = QWindowsVistaStylePrivate::pixelMetricFromSystemDp(metric, option, widget);
4540 if (res != QWindowsStylePrivate::InvalidMetric)
4541 return qRound(qreal(res) * QWindowsStylePrivate::nativeMetricScaleFactor(widget));
4542
4543 res = 0;
4544
4545 switch (metric) {
4546 case PM_MenuBarPanelWidth:
4547 case PM_ButtonDefaultIndicator:
4548 res = 0;
4549 break;
4550
4551 case PM_DefaultFrameWidth:
4552 res = qobject_cast<const QListView*>(widget) ? 2 : 1;
4553 break;
4554 case PM_MenuPanelWidth:
4555 case PM_SpinBoxFrameWidth:
4556 res = 1;
4557 break;
4558
4559 case PM_TabBarTabOverlap:
4560 case PM_MenuHMargin:
4561 case PM_MenuVMargin:
4562 res = 2;
4563 break;
4564
4565#if QT_CONFIG(tabbar)
4566 case PM_TabBarBaseOverlap:
4567 if (const auto *tab = qstyleoption_cast<const QStyleOptionTab *>(option)) {
4568 switch (tab->shape) {
4569 case QTabBar::RoundedNorth:
4570 case QTabBar::TriangularNorth:
4571 case QTabBar::RoundedWest:
4572 case QTabBar::TriangularWest:
4573 res = 1;
4574 break;
4575 case QTabBar::RoundedSouth:
4576 case QTabBar::TriangularSouth:
4577 res = 2;
4578 break;
4579 case QTabBar::RoundedEast:
4580 case QTabBar::TriangularEast:
4581 res = 3;
4582 break;
4583 }
4584 }
4585 break;
4586#endif // QT_CONFIG(tabbar)
4587
4588 case PM_SplitterWidth:
4589 res = QStyleHelper::dpiScaled(5., option);
4590 break;
4591
4592 case PM_MdiSubWindowMinimizedWidth:
4593 res = 160;
4594 break;
4595
4596#if QT_CONFIG(toolbar)
4597 case PM_ToolBarHandleExtent:
4598 res = int(QStyleHelper::dpiScaled(8., option));
4599 break;
4600
4601#endif // QT_CONFIG(toolbar)
4602 case PM_DockWidgetSeparatorExtent:
4603 case PM_DockWidgetTitleMargin:
4604 res = int(QStyleHelper::dpiScaled(4., option));
4605 break;
4606
4607#if QT_CONFIG(toolbutton)
4608 case PM_ButtonShiftHorizontal:
4609 case PM_ButtonShiftVertical:
4610 res = qstyleoption_cast<const QStyleOptionToolButton *>(option) ? 1 : 0;
4611 break;
4612#endif // QT_CONFIG(toolbutton)
4613
4614 default:
4615 res = QWindowsStyle::pixelMetric(metric, option, widget);
4616 }
4617
4618 return res;
4619}
4620
4621/*!
4622 \internal
4623 */
4624void QWindowsVistaStyle::polish(QWidget *widget)
4625{
4626 QWindowsStyle::polish(widget);
4627 if (false
4628#if QT_CONFIG(abstractbutton)
4629 || qobject_cast<QAbstractButton*>(widget)
4630#endif // QT_CONFIG(abstractbutton)
4631#if QT_CONFIG(toolbutton)
4632 || qobject_cast<QToolButton*>(widget)
4633#endif // QT_CONFIG(toolbutton)
4634#if QT_CONFIG(tabbar)
4635 || qobject_cast<QTabBar*>(widget)
4636#endif // QT_CONFIG(tabbar)
4637#if QT_CONFIG(combobox)
4638 || qobject_cast<QComboBox*>(widget)
4639#endif // QT_CONFIG(combobox)
4640 || qobject_cast<QScrollBar*>(widget)
4641 || qobject_cast<QSlider*>(widget)
4642 || qobject_cast<QHeaderView*>(widget)
4643#if QT_CONFIG(spinbox)
4644 || qobject_cast<QAbstractSpinBox*>(widget)
4645 || qobject_cast<QSpinBox*>(widget)
4646#endif // QT_CONFIG(spinbox)
4647#if QT_CONFIG(lineedit)
4648 || qobject_cast<QLineEdit*>(widget)
4649#endif // QT_CONFIG(lineedit)
4650 || qobject_cast<QGroupBox*>(widget)
4651 ) {
4652 widget->setAttribute(Qt::WA_Hover);
4653 } else if (QTreeView *tree = qobject_cast<QTreeView *> (widget)) {
4654 tree->viewport()->setAttribute(Qt::WA_Hover);
4655 } else if (QListView *list = qobject_cast<QListView *> (widget)) {
4656 list->viewport()->setAttribute(Qt::WA_Hover);
4657 }
4658
4659 if (!QWindowsVistaStylePrivate::useVista())
4660 return;
4661
4662#if QT_CONFIG(rubberband)
4663 if (qobject_cast<QRubberBand*>(widget))
4664 widget->setWindowOpacity(0.6);
4665 else
4666#endif
4667#if QT_CONFIG(commandlinkbutton)
4668 if (qobject_cast<QCommandLinkButton*>(widget)) {
4669 widget->setProperty("_qt_usingVistaStyle", true);
4670 QFont buttonFont = widget->font();
4671 buttonFont.setFamilies(QStringList{QLatin1String("Segoe UI")});
4672 widget->setFont(buttonFont);
4673 QPalette pal = widget->palette();
4674 pal.setColor(QPalette::Active, QPalette::ButtonText, QColor(21, 28, 85));
4675 pal.setColor(QPalette::Active, QPalette::BrightText, QColor(7, 64, 229));
4676 widget->setPalette(pal);
4677 } else
4678#endif // QT_CONFIG(commandlinkbutton)
4679 if (widget->inherits("QTipLabel")) {
4680 //note that since tooltips are not reused
4681 //we do not have to care about unpolishing
4682 widget->setContentsMargins(3, 0, 4, 0);
4683 COLORREF bgRef;
4684 HTHEME theme = OpenThemeData(widget ? QWindowsVistaStylePrivate::winId(widget) : nullptr, L"TOOLTIP");
4685 if (theme && SUCCEEDED(GetThemeColor(theme, TTP_STANDARD, TTSS_NORMAL, TMT_TEXTCOLOR, &bgRef))) {
4686 QColor textColor = QColor::fromRgb(bgRef);
4687 QPalette pal;
4688 pal.setColor(QPalette::All, QPalette::ToolTipText, textColor);
4689 pal.setResolveMask(0);
4690 widget->setPalette(pal);
4691 }
4692 } else if (qobject_cast<QMessageBox *> (widget)) {
4693 widget->setAttribute(Qt::WA_StyledBackground);
4694#if QT_CONFIG(dialogbuttonbox)
4695 QDialogButtonBox *buttonBox = widget->findChild<QDialogButtonBox *>(QLatin1String("qt_msgbox_buttonbox"));
4696 if (buttonBox)
4697 buttonBox->setContentsMargins(0, 9, 0, 0);
4698#endif
4699 }
4700}
4701
4702/*!
4703 \internal
4704 */
4705void QWindowsVistaStyle::unpolish(QWidget *widget)
4706{
4707 Q_D(QWindowsVistaStyle);
4708
4709#if QT_CONFIG(rubberband)
4710 if (qobject_cast<QRubberBand*>(widget))
4711 widget->setWindowOpacity(1.0);
4712#endif
4713
4714 // Unpolish of widgets is the first thing that
4715 // happens when a theme changes, or the theme
4716 // engine is turned off. So we detect it here.
4718 bool newState = QWindowsVistaStylePrivate::useVista(true);
4719 if ((oldState != newState) && newState) {
4720 d->cleanup(true);
4721 d->init(true);
4722 } else {
4723 // Cleanup handle map, if just changing style,
4724 // or turning it on. In both cases the values
4725 // already in the map might be old (other style).
4726 d->cleanupHandleMap();
4727 }
4728 if (false
4729 #if QT_CONFIG(abstractbutton)
4730 || qobject_cast<QAbstractButton*>(widget)
4731 #endif
4732 #if QT_CONFIG(toolbutton)
4733 || qobject_cast<QToolButton*>(widget)
4734 #endif // QT_CONFIG(toolbutton)
4735 #if QT_CONFIG(tabbar)
4736 || qobject_cast<QTabBar*>(widget)
4737 #endif // QT_CONFIG(tabbar)
4738 #if QT_CONFIG(combobox)
4739 || qobject_cast<QComboBox*>(widget)
4740 #endif // QT_CONFIG(combobox)
4741 || qobject_cast<QScrollBar*>(widget)
4742 || qobject_cast<QSlider*>(widget)
4743 || qobject_cast<QHeaderView*>(widget)
4744 #if QT_CONFIG(spinbox)
4745 || qobject_cast<QAbstractSpinBox*>(widget)
4746 || qobject_cast<QSpinBox*>(widget)
4747 #endif // QT_CONFIG(spinbox)
4748 ) {
4749 widget->setAttribute(Qt::WA_Hover, false);
4750 }
4751
4752 QWindowsStyle::unpolish(widget);
4753
4754 d->stopAnimation(widget);
4755
4756#if QT_CONFIG(lineedit)
4757 if (qobject_cast<QLineEdit*>(widget))
4758 widget->setAttribute(Qt::WA_Hover, false);
4759 else {
4760#endif // QT_CONFIG(lineedit)
4761 if (qobject_cast<QGroupBox*>(widget))
4762 widget->setAttribute(Qt::WA_Hover, false);
4763 else if (qobject_cast<QMessageBox *> (widget)) {
4764 widget->setAttribute(Qt::WA_StyledBackground, false);
4765#if QT_CONFIG(dialogbuttonbox)
4766 QDialogButtonBox *buttonBox = widget->findChild<QDialogButtonBox *>(QLatin1String("qt_msgbox_buttonbox"));
4767 if (buttonBox)
4768 buttonBox->setContentsMargins(0, 0, 0, 0);
4769#endif
4770 }
4771 else if (QTreeView *tree = qobject_cast<QTreeView *> (widget)) {
4772 tree->viewport()->setAttribute(Qt::WA_Hover, false);
4773 }
4774#if QT_CONFIG(commandlinkbutton)
4775 else if (qobject_cast<QCommandLinkButton*>(widget)) {
4776 QFont font = QApplication::font("QCommandLinkButton");
4777 QFont widgetFont = widget->font();
4778 widgetFont.setFamilies(font.families()); //Only family set by polish
4779 widget->setFont(widgetFont);
4780 }
4781#endif // QT_CONFIG(commandlinkbutton)
4782 }
4783}
4784
4785/*!
4786 \internal
4787 */
4788void QWindowsVistaStyle::polish(QPalette &pal)
4789{
4790 Q_D(QWindowsVistaStyle);
4791
4792 if (QGuiApplication::styleHints()->colorScheme() == Qt::ColorScheme::Dark) {
4793 // System runs in dark mode, but the Vista style cannot use a dark palette.
4794 // Overwrite with the light system palette.
4795 using QWindowsApplication = QNativeInterface::Private::QWindowsApplication;
4796 if (auto nativeWindowsApp = dynamic_cast<QWindowsApplication *>(QGuiApplicationPrivate::platformIntegration()))
4797 nativeWindowsApp->populateLightSystemPalette(pal);
4798 }
4799
4800 QPixmapCache::clear();
4801 d->alphaCache.clear();
4802 d->hasInitColors = false;
4803
4804 if (!d->hasInitColors) {
4805 // Get text color for group box labels
4806 QWindowsThemeData theme(nullptr, nullptr, QWindowsVistaStylePrivate::ButtonTheme, 0, 0);
4807 COLORREF cref;
4808 GetThemeColor(theme.handle(), BP_GROUPBOX, GBS_NORMAL, TMT_TEXTCOLOR, &cref);
4809 d->groupBoxTextColor = qRgb(GetRValue(cref), GetGValue(cref), GetBValue(cref));
4810 GetThemeColor(theme.handle(), BP_GROUPBOX, GBS_DISABLED, TMT_TEXTCOLOR, &cref);
4811 d->groupBoxTextColorDisabled = qRgb(GetRValue(cref), GetGValue(cref), GetBValue(cref));
4812 //Work around Windows API returning the same color for enabled and disabled group boxes
4813 if (d->groupBoxTextColor == d->groupBoxTextColorDisabled)
4814 d->groupBoxTextColorDisabled = pal.color(QPalette::Disabled, QPalette::ButtonText).rgb();
4815 // Where does this color come from?
4816 //GetThemeColor(theme.handle(), TKP_TICS, TSS_NORMAL, TMT_COLOR, &cref);
4817 d->sliderTickColor = qRgb(165, 162, 148);
4818 d->hasInitColors = true;
4819 }
4820
4821 QWindowsStyle::polish(pal);
4822 pal.setBrush(QPalette::AlternateBase, pal.base().color().darker(104));
4823}
4824
4825/*!
4826 \internal
4827 */
4828void QWindowsVistaStyle::polish(QApplication *app)
4829{
4830 // Override windows theme palettes to light
4831 if (qApp->styleHints()->colorScheme() == Qt::ColorScheme::Dark) {
4832 static constexpr const char* themedWidgets[] = {
4833 "QToolButton",
4834 "QAbstractButton",
4835 "QCheckBox",
4836 "QRadioButton",
4837 "QHeaderView",
4838 "QAbstractItemView",
4839 "QMessageBoxLabel",
4840 "QTabBar",
4841 "QLabel",
4842 "QGroupBox",
4843 "QMenu",
4844 "QMenuBar",
4845 "QTextEdit",
4846 "QTextControl",
4847 "QLineEdit"
4848 };
4849 for (const auto& themedWidget : std::as_const(themedWidgets)) {
4850 auto defaultResolveMask = QApplication::palette().resolveMask();
4851 auto widgetResolveMask = QApplication::palette(themedWidget).resolveMask();
4852 if (widgetResolveMask != defaultResolveMask)
4853 QApplication::setPalette(QApplication::palette(), themedWidget);
4854 }
4855 }
4856
4857 QWindowsStyle::polish(app);
4858}
4859
4860/*!
4861 \internal
4862 */
4863QPixmap QWindowsVistaStyle::standardPixmap(StandardPixmap standardPixmap, const QStyleOption *option,
4864 const QWidget *widget) const
4865{
4867 return QWindowsStyle::standardPixmap(standardPixmap, option, widget);
4868 }
4869
4870 switch (standardPixmap) {
4871 case SP_TitleBarMinButton:
4872 case SP_TitleBarMaxButton:
4873 case SP_TitleBarCloseButton:
4874 case SP_TitleBarNormalButton: {
4875 QWindowsThemeData theme(widget, nullptr, QWindowsVistaStylePrivate::WindowTheme, WP_SMALLCLOSEBUTTON, CBS_NORMAL);
4876 if (theme.isValid()) {
4877 const QSize size = (theme.size() * QWindowsStylePrivate::nativeMetricScaleFactor(widget)).toSize();
4878 return standardIcon(standardPixmap, option, widget).pixmap(size);
4879 }
4880 break;
4881 }
4882
4883 default:
4884 break;
4885 }
4886
4887 return QWindowsStyle::standardPixmap(standardPixmap, option, widget);
4888}
4889
4890/*!
4891\reimp
4892*/
4893QIcon QWindowsVistaStyle::standardIcon(StandardPixmap standardIcon,
4894 const QStyleOption *option,
4895 const QWidget *widget) const
4896{
4898 return QWindowsStyle::standardIcon(standardIcon, option, widget);
4899 }
4900
4901 auto *d = const_cast<QWindowsVistaStylePrivate*>(d_func());
4902
4903 switch (standardIcon) {
4904 case SP_TitleBarMinButton:
4905 if (d->m_titleBarMinIcon.isNull())
4906 d->m_titleBarMinIcon = QIcon(new WinFontIconEngine(QChar(0xE921), d->assetFont));
4907 return d->m_titleBarMinIcon;
4908
4909 case SP_TitleBarMaxButton:
4910 if (d->m_titleBarMaxIcon.isNull())
4911 d->m_titleBarMaxIcon = QIcon(new WinFontIconEngine(QChar(0xE922), d->assetFont));
4912 return d->m_titleBarMaxIcon;
4913
4914 case SP_TitleBarCloseButton:
4915 if (d->m_titleBarCloseIcon.isNull())
4916 d->m_titleBarCloseIcon = QIcon(new WinFontIconEngine(QChar(0xE8BB), d->assetFont));
4917 return d->m_titleBarCloseIcon;
4918
4919 case SP_TitleBarNormalButton:
4920 if (d->m_titleBarNormalIcon.isNull())
4921 d->m_titleBarNormalIcon = QIcon(new WinFontIconEngine(QChar(0xE923), d->assetFont));
4922 return d->m_titleBarNormalIcon;
4923
4924 case SP_CommandLink: {
4925 QWindowsThemeData theme(nullptr, nullptr, QWindowsVistaStylePrivate::ButtonTheme,
4927 if (theme.isValid()) {
4928 const QSize size = theme.size().toSize();
4929 QIcon linkGlyph;
4930 QPixmap pm(size);
4931 pm.fill(Qt::transparent);
4932 QPainter p(&pm);
4933 theme.painter = &p;
4934 theme.rect = QRect(QPoint(0, 0), size);
4935 d->drawBackground(theme);
4936 linkGlyph.addPixmap(pm, QIcon::Normal, QIcon::Off); // Normal
4937 pm.fill(Qt::transparent);
4938
4939 theme.stateId = CMDLGS_PRESSED;
4940 d->drawBackground(theme);
4941 linkGlyph.addPixmap(pm, QIcon::Normal, QIcon::On); // Pressed
4942 pm.fill(Qt::transparent);
4943
4944 theme.stateId = CMDLGS_HOT;
4945 d->drawBackground(theme);
4946 linkGlyph.addPixmap(pm, QIcon::Active, QIcon::Off); // Hover
4947 pm.fill(Qt::transparent);
4948
4949 theme.stateId = CMDLGS_DISABLED;
4950 d->drawBackground(theme);
4951 linkGlyph.addPixmap(pm, QIcon::Disabled, QIcon::Off); // Disabled
4952 return linkGlyph;
4953 }
4954 break;
4955 }
4956
4957 default:
4958 break;
4959 }
4960
4961 return QWindowsStyle::standardIcon(standardIcon, option, widget);
4962}
4963
4964WinFontIconEngine::WinFontIconEngine(const QString &glyph, const QFont &font)
4965 : QFontIconEngine({}, font)
4966 , m_font(font)
4967 , m_glyph(glyph)
4968{
4969}
4970
4971QString WinFontIconEngine::key() const
4972{
4973 return "WinFontIconEngine"_L1;
4974}
4975
4977{
4978 return new WinFontIconEngine(*this);
4979}
4980
4982{
4983 return m_glyph;
4984}
4985
4986void WinFontIconEngine::setScale(double scale)
4987{
4988 m_scale = scale;
4989}
4990
4991void WinFontIconEngine::paint(QPainter *painter, const QRect &rect, QIcon::Mode mode,
4992 QIcon::State)
4993{
4994 // we can't use QFontIconEngine because
4995 // - the text is drawn too large
4996 // - the palette from the widget is not used
4997 const QPaintDevice *paintDevice = painter->device();
4998 const bool isWidget = paintDevice->devType() == QInternal::Widget;
4999 const auto palette = isWidget ? static_cast<const QWidget *>(paintDevice)->palette()
5000 : qApp->palette();
5001 QColor color = Qt::black;
5002 switch (mode) {
5003 case QIcon::Active:
5004 color = palette.color(QPalette::Active, QPalette::Text);
5005 break;
5006 case QIcon::Normal:
5007 color = palette.color(QPalette::Active, QPalette::Text);
5008 break;
5009 case QIcon::Disabled:
5010 color = palette.color(QPalette::Disabled, QPalette::Text);
5011 break;
5012 case QIcon::Selected:
5013 color = palette.color(QPalette::Active, QPalette::HighlightedText);
5014 break;
5015 }
5016 QFont renderFont(m_font);
5017 renderFont.setPixelSize(rect.height() * m_scale);
5018 painter->save();
5019 painter->setFont(renderFont);
5020 painter->setPen(color);
5021 painter->drawText(rect, Qt::AlignCenter, m_glyph);
5022 painter->restore();
5023}
5024
5025QT_END_NAMESPACE
friend class QPainter
friend class QWidget
Definition qpainter.h:431
\inmodule QtCore\reentrant
Definition qpoint.h:231
static bool useVista(bool update=false)
The QWindowsVistaStyle class provides a look and feel suitable for applications on Microsoft Windows ...
QString string() const override
QString key() const override
\variable QIconEngine::ScaledPixmapArgument::size
QIconEngine * clone() const override
Reimplement this method to return a clone of this icon engine.
void setScale(double scale)
#define qApp
#define RBS_INACTIVE
#define TMT_TEXTSHADOWCOLOR
@ UnknownAlpha
#define TST_NONE
static bool isFullyOpaque(const QWindowsThemeData &themeData)
#define BP_COMMANDLINK
#define CMDLGS_NORMAL
static QRectF scaleRect(const QRectF &r, qreal factor)
#define LISS_HOTSELECTED
#define TMT_CONTENTMARGINS
static bool buttonVisible(const QStyle::SubControl sc, const QStyleOptionTitleBar *tb)
static constexpr int windowsRightBorder
#define LISS_SELECTEDNOTFOCUS
static HWND createTreeViewHelperWindow(const QScreen *screen)
bool canAnimate(const QStyleOption *option)
#define LISS_HOT
QStyleOption * clonedAnimationStyleOption(const QStyleOption *option)
static void qt_add_rect(HRGN &winRegion, QRect r)
#define CMDLGS_PRESSED
static int buttonStateId(int flags, int partId)
#define LISS_SELECTED
static QRegion scaleRegion(const QRegion &region, qreal factor)
static constexpr int windowsItemVMargin
static TransformType transformType(const QTransform &transform, qreal devicePixelRatio)
static void populateTitleBarButtonTheme(const QStyle *proxy, const QWidget *widget, const QStyleOptionComplex *option, QStyle::SubControl subControl, bool isTitleBarActive, int part, QWindowsThemeData *theme)
QObject * styleObject(const QStyleOption *option)
#define BP_COMMANDLINKGLYPH
static Qt::Orientation progressBarOrientation(const QStyleOption *option=nullptr)
static HRGN qt_hrgn_from_qregion(const QRegion &region)
static constexpr int windowsItemFrame
void deleteClonedAnimationStyleOption(const QStyleOption *option)
@ SimpleTransform
@ ComplexTransform
@ HighDpiScalingTransform
static constexpr int windowsArrowHMargin
static QImage createAnimationBuffer(const QStyleOption *option, const QWidget *widget)
#define CMDLGS_DISABLED
static constexpr int windowsItemHMargin
#define CMDLGS_HOT
static bool supportsStateTransition(QStyle::PrimitiveElement element, const QStyleOption *option, const QWidget *widget)