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