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 const QSize sz(ceil(option->rect.width() * devicePixelRatio), ceil(option->rect.height() * devicePixelRatio));
1064 QImage result(sz, QImage::Format_ARGB32_Premultiplied);
1065 result.setDevicePixelRatio(devicePixelRatio);
1066 result.fill(0);
1067 return result;
1068}
1069
1070/* \internal
1071 Used by animations to clone a styleoption and shift its offset
1072*/
1073QStyleOption *clonedAnimationStyleOption(const QStyleOption*option) {
1074 QStyleOption *styleOption = nullptr;
1075 if (const QStyleOptionSlider *slider = qstyleoption_cast<const QStyleOptionSlider*>(option))
1076 styleOption = new QStyleOptionSlider(*slider);
1077 else if (const QStyleOptionSpinBox *spinbox = qstyleoption_cast<const QStyleOptionSpinBox*>(option))
1078 styleOption = new QStyleOptionSpinBox(*spinbox);
1079 else if (const QStyleOptionGroupBox *groupBox = qstyleoption_cast<const QStyleOptionGroupBox*>(option))
1080 styleOption = new QStyleOptionGroupBox(*groupBox);
1081 else if (const QStyleOptionComboBox *combo = qstyleoption_cast<const QStyleOptionComboBox*>(option))
1082 styleOption = new QStyleOptionComboBox(*combo);
1083 else if (const QStyleOptionButton *button = qstyleoption_cast<const QStyleOptionButton*>(option))
1084 styleOption = new QStyleOptionButton(*button);
1085 else
1086 styleOption = new QStyleOption(*option);
1087 styleOption->rect = QRect(QPoint(0,0), option->rect.size());
1088 return styleOption;
1089}
1090
1091/* \internal
1092 Used by animations to delete cloned styleoption
1093*/
1094void deleteClonedAnimationStyleOption(const QStyleOption *option)
1095{
1096 if (const QStyleOptionSlider *slider = qstyleoption_cast<const QStyleOptionSlider*>(option))
1097 delete slider;
1098 else if (const QStyleOptionSpinBox *spinbox = qstyleoption_cast<const QStyleOptionSpinBox*>(option))
1099 delete spinbox;
1100 else if (const QStyleOptionGroupBox *groupBox = qstyleoption_cast<const QStyleOptionGroupBox*>(option))
1101 delete groupBox;
1102 else if (const QStyleOptionComboBox *combo = qstyleoption_cast<const QStyleOptionComboBox*>(option))
1103 delete combo;
1104 else if (const QStyleOptionButton *button = qstyleoption_cast<const QStyleOptionButton*>(option))
1105 delete button;
1106 else
1107 delete option;
1108}
1109
1110static void populateTitleBarButtonTheme(const QStyle *proxy, const QWidget *widget,
1111 const QStyleOptionComplex *option,
1112 QStyle::SubControl subControl,
1113 bool isTitleBarActive, int part,
1114 QWindowsThemeData *theme)
1115{
1116 theme->rect = proxy->subControlRect(QStyle::CC_TitleBar, option, subControl, widget);
1117 theme->partId = part;
1118 if (widget && !widget->isEnabled())
1119 theme->stateId = RBS_DISABLED;
1120 else if (option->activeSubControls == subControl && option->state.testFlag(QStyle::State_Sunken))
1121 theme->stateId = RBS_PUSHED;
1122 else if (option->activeSubControls == subControl && option->state.testFlag(QStyle::State_MouseOver))
1123 theme->stateId = RBS_HOT;
1124 else if (!isTitleBarActive)
1125 theme->stateId = RBS_INACTIVE;
1126 else
1127 theme->stateId = RBS_NORMAL;
1128}
1129
1130#if QT_CONFIG(mdiarea)
1131// Helper for drawing MDI buttons into the corner widget of QMenuBar in case a
1132// QMdiSubWindow is maximized.
1133static void populateMdiButtonTheme(const QStyle *proxy, const QWidget *widget,
1134 const QStyleOptionComplex *option,
1135 QStyle::SubControl subControl, int part,
1136 QWindowsThemeData *theme)
1137{
1138 theme->partId = part;
1139 theme->rect = proxy->subControlRect(QStyle::CC_MdiControls, option, subControl, widget);
1140 if (!option->state.testFlag(QStyle::State_Enabled))
1141 theme->stateId = CBS_INACTIVE;
1142 else if (option->state.testFlag(QStyle::State_Sunken) && option->activeSubControls.testFlag(subControl))
1143 theme->stateId = CBS_PUSHED;
1144 else if (option->state.testFlag(QStyle::State_MouseOver) && option->activeSubControls.testFlag(subControl))
1145 theme->stateId = CBS_HOT;
1146 else
1147 theme->stateId = CBS_NORMAL;
1148}
1149
1150// Calculate an small (max 2), empirical correction factor for scaling up
1151// WP_MDICLOSEBUTTON, WP_MDIRESTOREBUTTON, WP_MDIMINBUTTON, which are too
1152// small on High DPI screens (QTBUG-75927).
1153static qreal mdiButtonCorrectionFactor(QWindowsThemeData &theme, const QPaintDevice *pd = nullptr)
1154{
1155 const auto dpr = pd ? pd->devicePixelRatio() : qApp->devicePixelRatio();
1156 const QSizeF nativeSize = QSizeF(theme.size()) / dpr;
1157 const QSizeF requestedSize(theme.rect.size());
1158 const auto rawFactor = qMin(requestedSize.width() / nativeSize.width(),
1159 requestedSize.height() / nativeSize.height());
1160 const auto factor = rawFactor >= qreal(2) ? qreal(2) : qreal(1);
1161 return factor;
1162}
1163#endif // QT_CONFIG(mdiarea)
1164
1165/*
1166 This function is used by subControlRect to check if a button
1167 should be drawn for the given subControl given a set of window flags.
1168*/
1169static bool buttonVisible(const QStyle::SubControl sc, const QStyleOptionTitleBar *tb){
1170
1171 bool isMinimized = tb->titleBarState & Qt::WindowMinimized;
1172 bool isMaximized = tb->titleBarState & Qt::WindowMaximized;
1173 const auto flags = tb->titleBarFlags;
1174 bool retVal = false;
1175 switch (sc) {
1176 case QStyle::SC_TitleBarContextHelpButton:
1177 if (flags & Qt::WindowContextHelpButtonHint)
1178 retVal = true;
1179 break;
1180 case QStyle::SC_TitleBarMinButton:
1181 if (!isMinimized && (flags & Qt::WindowMinimizeButtonHint))
1182 retVal = true;
1183 break;
1184 case QStyle::SC_TitleBarNormalButton:
1185 if (isMinimized && (flags & Qt::WindowMinimizeButtonHint))
1186 retVal = true;
1187 else if (isMaximized && (flags & Qt::WindowMaximizeButtonHint))
1188 retVal = true;
1189 break;
1190 case QStyle::SC_TitleBarMaxButton:
1191 if (!isMaximized && (flags & Qt::WindowMaximizeButtonHint))
1192 retVal = true;
1193 break;
1194 case QStyle::SC_TitleBarShadeButton:
1195 if (!isMinimized && flags & Qt::WindowShadeButtonHint)
1196 retVal = true;
1197 break;
1198 case QStyle::SC_TitleBarUnshadeButton:
1199 if (isMinimized && flags & Qt::WindowShadeButtonHint)
1200 retVal = true;
1201 break;
1202 case QStyle::SC_TitleBarCloseButton:
1203 if (flags & Qt::WindowSystemMenuHint)
1204 retVal = true;
1205 break;
1206 case QStyle::SC_TitleBarSysMenu:
1207 if (flags & Qt::WindowSystemMenuHint)
1208 retVal = true;
1209 break;
1210 default :
1211 retVal = true;
1212 }
1213 return retVal;
1214}
1215
1216//convert Qt state flags to uxtheme button states
1217static int buttonStateId(int flags, int partId)
1218{
1219 int stateId = 0;
1220 if (partId == BP_RADIOBUTTON || partId == BP_CHECKBOX) {
1221 if (!(flags & QStyle::State_Enabled))
1222 stateId = RBS_UNCHECKEDDISABLED;
1223 else if (flags & QStyle::State_Sunken)
1224 stateId = RBS_UNCHECKEDPRESSED;
1225 else if (flags & QStyle::State_MouseOver)
1226 stateId = RBS_UNCHECKEDHOT;
1227 else
1228 stateId = RBS_UNCHECKEDNORMAL;
1229
1230 if (flags & QStyle::State_On)
1231 stateId += RBS_CHECKEDNORMAL-1;
1232
1233 } else if (partId == BP_PUSHBUTTON) {
1234 if (!(flags & QStyle::State_Enabled))
1235 stateId = PBS_DISABLED;
1236 else if (flags & (QStyle::State_Sunken | QStyle::State_On))
1237 stateId = PBS_PRESSED;
1238 else if (flags & QStyle::State_MouseOver)
1239 stateId = PBS_HOT;
1240 else
1241 stateId = PBS_NORMAL;
1242 } else {
1243 Q_ASSERT(1);
1244 }
1245 return stateId;
1246}
1247
1248static inline bool supportsStateTransition(QStyle::PrimitiveElement element,
1249 const QStyleOption *option,
1250 const QWidget *widget)
1251{
1252 bool result = false;
1253 switch (element) {
1254 case QStyle::PE_IndicatorRadioButton:
1255 case QStyle::PE_IndicatorCheckBox:
1256 result = true;
1257 break;
1258 // QTBUG-40634, do not animate when color is set in palette for PE_PanelLineEdit.
1259 case QStyle::PE_FrameLineEdit:
1260 result = !QWindowsVistaStylePrivate::isLineEditBaseColorSet(option, widget);
1261 break;
1262 default:
1263 break;
1264 }
1265 return result;
1266}
1267
1268/*!
1269 \class QWindowsVistaStyle
1270 \brief The QWindowsVistaStyle class provides a look and feel suitable for applications on Microsoft Windows Vista.
1271 \since 4.3
1272 \ingroup appearance
1273 \inmodule QtWidgets
1274 \internal
1275
1276 \warning This style is only available on the Windows Vista platform
1277 because it makes use of Windows Vista's style engine.
1278
1279 \sa QMacStyle, QFusionStyle
1280*/
1281
1282/*!
1283 Constructs a QWindowsVistaStyle object.
1284*/
1285QWindowsVistaStyle::QWindowsVistaStyle() : QWindowsVistaStyle(*new QWindowsVistaStylePrivate)
1286{
1287}
1288
1289/*!
1290 \internal
1291 Constructs a QWindowsStyle object.
1292*/
1293QWindowsVistaStyle::QWindowsVistaStyle(QWindowsVistaStylePrivate &dd) : QWindowsStyle(dd)
1294{
1295 Q_D(QWindowsVistaStyle);
1296 d->m_assetFont = QFont("Segoe MDL2 Assets"_L1);
1297 d->m_assetFont.setStyleStrategy(QFont::NoFontMerging);
1298}
1299
1300/*!
1301 Destructor.
1302*/
1303QWindowsVistaStyle::~QWindowsVistaStyle() = default;
1304
1305
1306/*!
1307 \internal
1308
1309 Animations are used for some state transitions on specific widgets.
1310
1311 Only one running animation can exist for a widget at any specific
1312 time. Animations can be added through
1313 QWindowsVistaStylePrivate::startAnimation(Animation *) and any
1314 existing animation on a widget can be retrieved with
1315 QWindowsVistaStylePrivate::widgetAnimation(Widget *).
1316
1317 Once an animation has been started,
1318 QWindowsVistaStylePrivate::timerEvent(QTimerEvent *) will
1319 continuously call update() on the widget until it is stopped,
1320 meaning that drawPrimitive will be called many times until the
1321 transition has completed. During this time, the result will be
1322 retrieved by the Animation::paint(...) function and not by the style
1323 itself.
1324
1325 To determine if a transition should occur, the style needs to know
1326 the previous state of the widget as well as the current one. This is
1327 solved by updating dynamic properties on the widget every time the
1328 function is called.
1329
1330 Transitions interrupting existing transitions should always be
1331 smooth, so whenever a hover-transition is started on a pulsating
1332 button, it uses the current frame of the pulse-animation as the
1333 starting image for the hover transition.
1334
1335 */
1336void QWindowsVistaStyle::drawPrimitive(PrimitiveElement element, const QStyleOption *option,
1337 QPainter *painter, const QWidget *widget) const
1338{
1339 if (!QWindowsVistaStylePrivate::useVista()) {
1340 QWindowsStyle::drawPrimitive(element, option, painter, widget);
1341 return;
1342 }
1343
1344 QWindowsVistaStylePrivate *d = const_cast<QWindowsVistaStylePrivate*>(d_func());
1345
1346 const int state = option->state;
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 const QRect oldRect = styleObject->property("_q_stylerect").toRect();
1356 styleObject->setProperty("_q_stylestate", int(option->state));
1357 styleObject->setProperty("_q_stylerect", option->rect);
1358
1359 bool doTransition = oldState &&
1360 ((state & State_Sunken) != (oldState & State_Sunken) ||
1361 (state & State_On) != (oldState & State_On) ||
1362 (state & State_MouseOver) != (oldState & State_MouseOver));
1363
1364 if (oldRect != option->rect ||
1365 (state & State_Enabled) != (oldState & State_Enabled) ||
1366 (state & State_Active) != (oldState & State_Active))
1367 d->stopAnimation(styleObject);
1368
1369 if (state & State_ReadOnly && element == PE_FrameLineEdit) // Do not animate read only line edits
1370 doTransition = false;
1371
1372 if (doTransition) {
1373 QStyleOption *styleOption = clonedAnimationStyleOption(option);
1374 styleOption->state = QStyle::State(oldState);
1375
1376 QWindowsVistaAnimation *animate = qobject_cast<QWindowsVistaAnimation *>(d->animation(styleObject));
1377 QWindowsVistaTransition *transition = new QWindowsVistaTransition(styleObject);
1378
1379 // We create separate images for the initial and final transition states and store them in the
1380 // Transition object.
1381 QImage startImage = createAnimationBuffer(option, widget);
1382 QPainter startPainter(&startImage);
1383
1384 QImage endImage = createAnimationBuffer(option, widget);
1385 QPainter endPainter(&endImage);
1386
1387 // If we have a running animation on the widget already, we will use that to paint the initial
1388 // state of the new transition, this ensures a smooth transition from a current animation such as a
1389 // pulsating default button into the intended target state.
1390 if (!animate)
1391 proxy()->drawPrimitive(element, styleOption, &startPainter, widget);
1392 else
1393 animate->paint(&startPainter, styleOption);
1394
1395 transition->setStartImage(startImage);
1396
1397 // The end state of the transition is simply the result we would have painted
1398 // if the style was not animated.
1399 styleOption->styleObject = nullptr;
1400 styleOption->state = option->state;
1401 proxy()->drawPrimitive(element, styleOption, &endPainter, widget);
1402
1403 transition->setEndImage(endImage);
1404
1405 HTHEME theme;
1406 int partId;
1407 DWORD duration;
1408 int fromState = 0;
1409 int toState = 0;
1410
1411 //translate state flags to UXTHEME states :
1412 if (element == PE_FrameLineEdit) {
1413 theme = OpenThemeData(nullptr, L"Edit");
1414 partId = EP_EDITBORDER_NOSCROLL;
1415
1416 if (oldState & State_HasFocus)
1417 fromState = ETS_SELECTED;
1418 else if (oldState & State_MouseOver)
1419 fromState = ETS_HOT;
1420 else
1421 fromState = ETS_NORMAL;
1422
1423 if (state & State_HasFocus)
1424 toState = ETS_SELECTED;
1425 else if (state & State_MouseOver)
1426 toState = ETS_HOT;
1427 else
1428 toState = ETS_NORMAL;
1429
1430 } else {
1431 theme = OpenThemeData(nullptr, L"Button");
1432 if (element == PE_IndicatorRadioButton)
1433 partId = BP_RADIOBUTTON;
1434 else if (element == PE_IndicatorCheckBox)
1435 partId = BP_CHECKBOX;
1436 else
1437 partId = BP_PUSHBUTTON;
1438
1439 fromState = buttonStateId(oldState, partId);
1440 toState = buttonStateId(option->state, partId);
1441 }
1442
1443 // Retrieve the transition time between the states from the system.
1444 if (theme
1445 && SUCCEEDED(GetThemeTransitionDuration(theme, partId, fromState, toState,
1446 TMT_TRANSITIONDURATIONS, &duration))) {
1447 transition->setDuration(int(duration));
1448 }
1449 transition->setStartTime(d->animationTime());
1450
1451 deleteClonedAnimationStyleOption(styleOption);
1452 d->startAnimation(transition);
1453 }
1454 styleObject->setProperty("_q_no_animation", false);
1455 }
1456 }
1457
1458 int themeNumber = -1;
1459 int partId = 0;
1460 int stateId = 0;
1461 bool hMirrored = false;
1462 bool vMirrored = false;
1463 bool noBorder = false;
1464 bool noContent = false;
1465 int rotate = 0;
1466
1467 switch (element) {
1468 case PE_PanelButtonCommand:
1469 if (const auto *btn = qstyleoption_cast<const QStyleOptionButton *>(option)) {
1470 QBrush fill;
1471 if (!(state & State_Sunken) && (state & State_On))
1472 fill = QBrush(option->palette.light().color(), Qt::Dense4Pattern);
1473 else
1474 fill = option->palette.brush(QPalette::Button);
1475 const auto &rect = option->rect;
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, option->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, option->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 themeNumber = -1;
1977 }
1978 }
1979 break;
1980
1981 case PE_IndicatorToolBarHandle: {
1982 QWindowsThemeData theme;
1983 QRect rect;
1984 if (option->state & State_Horizontal) {
1985 theme = QWindowsThemeData(widget, painter,
1986 QWindowsVistaStylePrivate::RebarTheme,
1987 RP_GRIPPER, ETS_NORMAL, option->rect.adjusted(0, 1, -2, -2));
1988 rect = option->rect.adjusted(0, 1, 0, -2);
1989 rect.setWidth(4);
1990 } else {
1991 theme = QWindowsThemeData(widget, painter, QWindowsVistaStylePrivate::RebarTheme,
1992 RP_GRIPPERVERT, ETS_NORMAL, option->rect.adjusted(0, 1, -2, -2));
1993 rect = option->rect.adjusted(1, 0, -1, 0);
1994 rect.setHeight(4);
1995 }
1996 theme.rect = rect;
1997 d->drawBackground(theme);
1998 return;
1999 }
2000
2001 case PE_IndicatorToolBarSeparator: {
2002 QPen pen = painter->pen();
2003 int margin = 3;
2004 painter->setPen(option->palette.window().color().darker(114));
2005 if (option->state & State_Horizontal) {
2006 int x1 = option->rect.center().x();
2007 painter->drawLine(QPoint(x1, option->rect.top() + margin), QPoint(x1, option->rect.bottom() - margin));
2008 } else {
2009 int y1 = option->rect.center().y();
2010 painter->drawLine(QPoint(option->rect.left() + margin, y1), QPoint(option->rect.right() - margin, y1));
2011 }
2012 painter->setPen(pen);
2013 return;
2014 }
2015
2016 case PE_PanelTipLabel: {
2017 QWindowsThemeData theme(widget, painter,
2018 QWindowsVistaStylePrivate::ToolTipTheme,
2019 TTP_STANDARD, TTSS_NORMAL, option->rect);
2020 d->drawBackground(theme);
2021 return;
2022 }
2023
2024 case PE_FrameTabBarBase:
2025#if QT_CONFIG(tabbar)
2026 if (const auto *tbb = qstyleoption_cast<const QStyleOptionTabBarBase *>(option)) {
2027 painter->save();
2028 switch (tbb->shape) {
2029 case QTabBar::RoundedNorth:
2030 painter->setPen(QPen(tbb->palette.dark(), 0));
2031 painter->drawLine(tbb->rect.topLeft(), tbb->rect.topRight());
2032 break;
2033 case QTabBar::RoundedWest:
2034 painter->setPen(QPen(tbb->palette.dark(), 0));
2035 painter->drawLine(tbb->rect.left(), tbb->rect.top(), tbb->rect.left(), tbb->rect.bottom());
2036 break;
2037 case QTabBar::RoundedSouth:
2038 painter->setPen(QPen(tbb->palette.dark(), 0));
2039 painter->drawLine(tbb->rect.left(), tbb->rect.top(),
2040 tbb->rect.right(), tbb->rect.top());
2041 break;
2042 case QTabBar::RoundedEast:
2043 painter->setPen(QPen(tbb->palette.dark(), 0));
2044 painter->drawLine(tbb->rect.topLeft(), tbb->rect.bottomLeft());
2045 break;
2046 case QTabBar::TriangularNorth:
2047 case QTabBar::TriangularEast:
2048 case QTabBar::TriangularWest:
2049 case QTabBar::TriangularSouth:
2050 painter->restore();
2051 QWindowsStyle::drawPrimitive(element, option, painter, widget);
2052 return;
2053 }
2054 painter->restore();
2055 }
2056#endif // QT_CONFIG(tabbar)
2057 return;
2058
2059 case PE_Widget: {
2060#if QT_CONFIG(dialogbuttonbox)
2061 const QDialogButtonBox *buttonBox = nullptr;
2062 if (qobject_cast<const QMessageBox *> (widget))
2063 buttonBox = widget->findChild<const QDialogButtonBox *>(QLatin1String("qt_msgbox_buttonbox"));
2064 if (buttonBox) {
2065 //draw white panel part
2066 QWindowsThemeData theme(widget, painter,
2067 QWindowsVistaStylePrivate::TaskDialogTheme,
2068 TDLG_PRIMARYPANEL, 0, option->rect);
2069 QRect toprect = option->rect;
2070 toprect.setBottom(buttonBox->geometry().top());
2071 theme.rect = toprect;
2072 d->drawBackground(theme);
2073
2074 //draw bottom panel part
2075 QRect buttonRect = option->rect;
2076 buttonRect.setTop(buttonBox->geometry().top());
2077 theme.rect = buttonRect;
2078 theme.partId = TDLG_SECONDARYPANEL;
2079 d->drawBackground(theme);
2080 }
2081#endif
2082 return;
2083 }
2084
2085 case PE_PanelItemViewItem: {
2086 const QStyleOptionViewItem *vopt;
2087 bool newStyle = true;
2088 QAbstractItemView::SelectionBehavior selectionBehavior = QAbstractItemView::SelectRows;
2089 QAbstractItemView::SelectionMode selectionMode = QAbstractItemView::NoSelection;
2090 if (const QAbstractItemView *view = qobject_cast<const QAbstractItemView *>(widget)) {
2091 newStyle = !qobject_cast<const QTableView*>(view);
2092 selectionBehavior = view->selectionBehavior();
2093 selectionMode = view->selectionMode();
2094#if QT_CONFIG(accessibility)
2095 } else if (!widget) {
2096 newStyle = !QStyleHelper::hasAncestor(option->styleObject, QAccessible::MenuItem) ;
2097#endif
2098 }
2099
2100 if (newStyle && (vopt = qstyleoption_cast<const QStyleOptionViewItem *>(option))) {
2101 bool selected = vopt->state & QStyle::State_Selected;
2102 const bool hover = selectionMode != QAbstractItemView::NoSelection && (vopt->state & QStyle::State_MouseOver);
2103 bool active = vopt->state & QStyle::State_Active;
2104
2105 if (vopt->features & QStyleOptionViewItem::Alternate)
2106 painter->fillRect(vopt->rect, vopt->palette.alternateBase());
2107
2108 QPalette::ColorGroup cg = vopt->state & QStyle::State_Enabled
2109 ? QPalette::Normal : QPalette::Disabled;
2110 if (cg == QPalette::Normal && !(vopt->state & QStyle::State_Active))
2111 cg = QPalette::Inactive;
2112
2113 QRect itemRect = subElementRect(QStyle::SE_ItemViewItemFocusRect, option, widget).adjusted(-1, 0, 1, 0);
2114 itemRect.setTop(vopt->rect.top());
2115 itemRect.setBottom(vopt->rect.bottom());
2116
2117 QSize sectionSize = itemRect.size();
2118 if (vopt->showDecorationSelected)
2119 sectionSize = vopt->rect.size();
2120
2121 if (selectionBehavior == QAbstractItemView::SelectRows)
2122 sectionSize.setWidth(vopt->rect.width());
2123 QPixmap pixmap;
2124
2125 if (vopt->backgroundBrush.style() != Qt::NoBrush) {
2126 QPainterStateGuard psg(painter);
2127 painter->setBrushOrigin(vopt->rect.topLeft());
2128 painter->fillRect(vopt->rect, vopt->backgroundBrush);
2129 }
2130
2131 if (hover || selected) {
2132 if (sectionSize.width() > 0 && sectionSize.height() > 0) {
2133 QString key = QStringLiteral(u"qvdelegate-%1-%2-%3-%4-%5").arg(sectionSize.width())
2134 .arg(sectionSize.height()).arg(selected).arg(active).arg(hover);
2135 if (!QPixmapCache::find(key, &pixmap)) {
2136 pixmap = QPixmap(sectionSize);
2137 pixmap.fill(Qt::transparent);
2138
2139 int state;
2140 if (selected && hover)
2141 state = LISS_HOTSELECTED;
2142 else if (selected && !active)
2143 state = LISS_SELECTEDNOTFOCUS;
2144 else if (selected)
2145 state = LISS_SELECTED;
2146 else
2147 state = LISS_HOT;
2148
2149 QPainter pixmapPainter(&pixmap);
2150
2151 QWindowsThemeData theme(widget, &pixmapPainter,
2152 QWindowsVistaStylePrivate::VistaTreeViewTheme,
2153 LVP_LISTITEM, state, QRect(0, 0, sectionSize.width(), sectionSize.height()));
2154
2155 if (!theme.isValid())
2156 break;
2157
2158 d->drawBackground(theme);
2159 QPixmapCache::insert(key, pixmap);
2160 }
2161 }
2162
2163 if (vopt->showDecorationSelected) {
2164 const int frame = 2; //Assumes a 2 pixel pixmap border
2165 QRect srcRect = QRect(0, 0, sectionSize.width(), sectionSize.height());
2166 QRect pixmapRect = vopt->rect;
2167 bool reverse = vopt->direction == Qt::RightToLeft;
2168 bool leftSection = vopt->viewItemPosition == QStyleOptionViewItem::Beginning;
2169 bool rightSection = vopt->viewItemPosition == QStyleOptionViewItem::End;
2170 if (vopt->viewItemPosition == QStyleOptionViewItem::OnlyOne
2171 || vopt->viewItemPosition == QStyleOptionViewItem::Invalid)
2172 painter->drawPixmap(pixmapRect.topLeft(), pixmap);
2173 else if (reverse ? rightSection : leftSection){
2174 painter->drawPixmap(QRect(pixmapRect.topLeft(),
2175 QSize(frame, pixmapRect.height())), pixmap,
2176 QRect(QPoint(0, 0), QSize(frame, pixmapRect.height())));
2177 painter->drawPixmap(pixmapRect.adjusted(frame, 0, 0, 0),
2178 pixmap, srcRect.adjusted(frame, 0, -frame, 0));
2179 } else if (reverse ? leftSection : rightSection) {
2180 painter->drawPixmap(QRect(pixmapRect.topRight() - QPoint(frame - 1, 0),
2181 QSize(frame, pixmapRect.height())), pixmap,
2182 QRect(QPoint(pixmapRect.width() - frame, 0),
2183 QSize(frame, pixmapRect.height())));
2184 painter->drawPixmap(pixmapRect.adjusted(0, 0, -frame, 0),
2185 pixmap, srcRect.adjusted(frame, 0, -frame, 0));
2186 } else if (vopt->viewItemPosition == QStyleOptionViewItem::Middle)
2187 painter->drawPixmap(pixmapRect, pixmap,
2188 srcRect.adjusted(frame, 0, -frame, 0));
2189 } else {
2190 if (vopt->text.isEmpty() && vopt->icon.isNull())
2191 break;
2192 painter->drawPixmap(itemRect.topLeft(), pixmap);
2193 }
2194 }
2195 return;
2196 }
2197 break;
2198 }
2199
2200 default:
2201 break;
2202 }
2203
2204 QWindowsThemeData theme(widget, painter, themeNumber, partId, stateId, option->rect);
2205
2206 if (!theme.isValid()) {
2207 QWindowsStyle::drawPrimitive(element, option, painter, widget);
2208 return;
2209 }
2210
2211 theme.mirrorHorizontally = hMirrored;
2212 theme.mirrorVertically = vMirrored;
2213 theme.noBorder = noBorder;
2214 theme.noContent = noContent;
2215 theme.rotate = rotate;
2216
2217 d->drawBackground(theme);
2218}
2219
2220/*! \internal */
2221int QWindowsVistaStyle::styleHint(StyleHint hint, const QStyleOption *option, const QWidget *widget,
2222 QStyleHintReturn *returnData) const
2223{
2224 QWindowsVistaStylePrivate *d = const_cast<QWindowsVistaStylePrivate*>(d_func());
2225
2226 int ret = 0;
2227 switch (hint) {
2228 case SH_EtchDisabledText:
2229 ret = (qobject_cast<const QLabel*>(widget) != 0);
2230 break;
2231
2232 case SH_SpinControls_DisableOnBounds:
2233 ret = 0;
2234 break;
2235
2236 case SH_TitleBar_AutoRaise:
2237 case SH_TitleBar_NoBorder:
2238 ret = 1;
2239 break;
2240
2241 case SH_GroupBox_TextLabelColor:
2242 if (!widget || widget->isEnabled())
2243 ret = d->groupBoxTextColor;
2244 else
2245 ret = d->groupBoxTextColorDisabled;
2246 break;
2247
2248 case SH_WindowFrame_Mask: {
2249 ret = 1;
2250 auto *mask = qstyleoption_cast<QStyleHintReturnMask *>(returnData);
2251 const auto *titlebar = qstyleoption_cast<const QStyleOptionTitleBar *>(option);
2252 if (mask && titlebar) {
2253 // Note certain themes will not return the whole window frame but only the titlebar part when
2254 // queried This function needs to return the entire window mask, hence we will only fetch the mask for the
2255 // titlebar itself and add the remaining part of the window rect at the bottom.
2256 int tbHeight = proxy()->pixelMetric(PM_TitleBarHeight, option, widget);
2257 QRect titleBarRect = option->rect;
2258 titleBarRect.setHeight(tbHeight);
2259 QWindowsThemeData themeData;
2260 if (titlebar->titleBarState & Qt::WindowMinimized) {
2261 themeData = QWindowsThemeData(widget, nullptr,
2262 QWindowsVistaStylePrivate::WindowTheme,
2263 WP_MINCAPTION, CS_ACTIVE, titleBarRect);
2264 } else
2265 themeData = QWindowsThemeData(widget, nullptr,
2266 QWindowsVistaStylePrivate::WindowTheme,
2267 WP_CAPTION, CS_ACTIVE, titleBarRect);
2268 mask->region = d->region(themeData) +
2269 QRect(0, tbHeight, option->rect.width(), option->rect.height() - tbHeight);
2270 }
2271 break;
2272 }
2273
2274#if QT_CONFIG(rubberband)
2275 case SH_RubberBand_Mask:
2276 if (qstyleoption_cast<const QStyleOptionRubberBand *>(option))
2277 ret = 0;
2278 break;
2279#endif // QT_CONFIG(rubberband)
2280
2281 case SH_MessageBox_CenterButtons:
2282 ret = false;
2283 break;
2284
2285 case SH_ToolTip_Mask:
2286 if (option) {
2287 if (QStyleHintReturnMask *mask = qstyleoption_cast<QStyleHintReturnMask*>(returnData)) {
2288 ret = true;
2289 QWindowsThemeData themeData(widget, nullptr,
2290 QWindowsVistaStylePrivate::ToolTipTheme,
2291 TTP_STANDARD, TTSS_NORMAL, option->rect);
2292 mask->region = d->region(themeData);
2293 }
2294 }
2295 break;
2296
2297 case SH_Table_GridLineColor:
2298 if (option)
2299 ret = int(option->palette.color(QPalette::Base).darker(118).rgba());
2300 else
2301 ret = -1;
2302 break;
2303
2304 case SH_Header_ArrowAlignment:
2305 ret = Qt::AlignTop | Qt::AlignHCenter;
2306 break;
2307
2308 case SH_ItemView_DrawDelegateFrame:
2309 ret = 1;
2310 break;
2311
2312 case SH_ComboBox_ListMouseTracking_Current:
2313 ret = 0;
2314 break;
2315
2316 case SH_ComboBox_ListMouseTracking_Active:
2317 ret = 1;
2318 break;
2319
2320 default:
2321 ret = QWindowsStyle::styleHint(hint, option, widget, returnData);
2322 break;
2323 }
2324
2325 return ret;
2326}
2327
2328
2329/*!
2330 \internal
2331
2332 see drawPrimitive for comments on the animation support
2333 */
2334void QWindowsVistaStyle::drawControl(ControlElement element, const QStyleOption *option,
2335 QPainter *painter, const QWidget *widget) const
2336{
2337 if (!QWindowsVistaStylePrivate::useVista()) {
2338 QWindowsStyle::drawControl(element, option, painter, widget);
2339 return;
2340 }
2341
2342 QWindowsVistaStylePrivate *d = const_cast<QWindowsVistaStylePrivate*>(d_func());
2343
2344 bool selected = option->state & State_Selected;
2345 bool pressed = option->state & State_Sunken;
2346 bool disabled = !(option->state & State_Enabled);
2347
2348 int state = option->state;
2349 int themeNumber = -1;
2350
2351 QRect rect(option->rect);
2352 State flags = option->state;
2353 int partId = 0;
2354 int stateId = 0;
2355
2356 if (d->transitionsEnabled() && canAnimate(option)) {
2357 if (element == CE_PushButtonBevel) {
2358 QObject *styleObject = option->styleObject;
2359
2360 int oldState = styleObject->property("_q_stylestate").toInt();
2361 const QRect oldRect = styleObject->property("_q_stylerect").toRect();
2362 styleObject->setProperty("_q_stylestate", int(option->state));
2363 styleObject->setProperty("_q_stylerect", option->rect);
2364
2365 bool wasDefault = false;
2366 bool isDefault = false;
2367 if (const QStyleOptionButton *button = qstyleoption_cast<const QStyleOptionButton *>(option)) {
2368 wasDefault = styleObject->property("_q_isdefault").toBool();
2369 isDefault = button->features & QStyleOptionButton::DefaultButton;
2370 styleObject->setProperty("_q_isdefault", isDefault);
2371 }
2372
2373 bool doTransition = ((state & State_Sunken) != (oldState & State_Sunken) ||
2374 (state & State_On) != (oldState & State_On) ||
2375 (state & State_MouseOver) != (oldState & State_MouseOver));
2376
2377 if (oldRect != option->rect || (wasDefault && !isDefault)) {
2378 doTransition = false;
2379 d->stopAnimation(styleObject);
2380 }
2381
2382 if (doTransition) {
2383 styleObject->setProperty("_q_no_animation", true);
2384
2386 QWindowsVistaAnimation *anim = qobject_cast<QWindowsVistaAnimation *>(d->animation(styleObject));
2387 QStyleOption *styleOption = clonedAnimationStyleOption(option);
2388 styleOption->state = QStyle::State(oldState);
2389
2390 QImage startImage = createAnimationBuffer(option, widget);
2391 QPainter startPainter(&startImage);
2392
2393 // Use current state of existing animation if already one is running
2394 if (!anim) {
2395 proxy()->drawControl(element, styleOption, &startPainter, widget);
2396 } else {
2397 anim->paint(&startPainter, styleOption);
2398 d->stopAnimation(styleObject);
2399 }
2400
2401 t->setStartImage(startImage);
2402 QImage endImage = createAnimationBuffer(option, widget);
2403 QPainter endPainter(&endImage);
2404 styleOption->state = option->state;
2405 proxy()->drawControl(element, styleOption, &endPainter, widget);
2406 t->setEndImage(endImage);
2407
2408
2409 DWORD duration = 0;
2410 const HTHEME theme = OpenThemeData(nullptr, L"Button");
2411
2412 int fromState = buttonStateId(oldState, BP_PUSHBUTTON);
2413 int toState = buttonStateId(option->state, BP_PUSHBUTTON);
2414 if (GetThemeTransitionDuration(theme, BP_PUSHBUTTON, fromState, toState, TMT_TRANSITIONDURATIONS, &duration) == S_OK)
2415 t->setDuration(int(duration));
2416 else
2417 t->setDuration(0);
2418 t->setStartTime(d->animationTime());
2419 styleObject->setProperty("_q_no_animation", false);
2420
2421 deleteClonedAnimationStyleOption(styleOption);
2422 d->startAnimation(t);
2423 }
2424
2425 QWindowsVistaAnimation *anim = qobject_cast<QWindowsVistaAnimation *>(d->animation(styleObject));
2426 if (anim) {
2427 anim->paint(painter, option);
2428 return;
2429 }
2430
2431 }
2432 }
2433
2434 bool hMirrored = false;
2435 bool vMirrored = false;
2436 int rotate = 0;
2437
2438 switch (element) {
2439 case CE_PushButtonBevel:
2440 if (const QStyleOptionButton *btn = qstyleoption_cast<const QStyleOptionButton *>(option)) {
2441 themeNumber = QWindowsVistaStylePrivate::ButtonTheme;
2442 partId = BP_PUSHBUTTON;
2443 if (btn->features & QStyleOptionButton::CommandLinkButton)
2444 partId = BP_COMMANDLINK;
2445 bool justFlat = (btn->features & QStyleOptionButton::Flat) && !(flags & (State_On|State_Sunken));
2446 if (!(flags & State_Enabled) && !(btn->features & QStyleOptionButton::Flat))
2447 stateId = PBS_DISABLED;
2448 else if (justFlat)
2449 ;
2450 else if (flags & (State_Sunken | State_On))
2451 stateId = PBS_PRESSED;
2452 else if (flags & State_MouseOver)
2453 stateId = PBS_HOT;
2454 else if (btn->features & QStyleOptionButton::DefaultButton && (state & State_Active))
2455 stateId = PBS_DEFAULTED;
2456 else
2457 stateId = PBS_NORMAL;
2458
2459 if (!justFlat) {
2460 QWindowsThemeData theme(widget, painter, themeNumber, partId, stateId, rect);
2461 d->drawBackground(theme);
2462 }
2463
2464 if (btn->features & QStyleOptionButton::HasMenu) {
2465 int mbiw = 0, mbih = 0;
2466 QWindowsThemeData theme(widget, nullptr, QWindowsVistaStylePrivate::ToolBarTheme,
2467 TP_DROPDOWNBUTTON);
2468 if (theme.isValid()) {
2469 const QSizeF size = theme.size() * QStyleHelper::dpiScaled(1, option);
2470 if (!size.isEmpty()) {
2471 mbiw = qRound(size.width());
2472 mbih = qRound(size.height());
2473 }
2474 }
2475 QRect ir = subElementRect(SE_PushButtonContents, option, nullptr);
2476 QStyleOptionButton newBtn = *btn;
2477 newBtn.rect = QStyle::visualRect(option->direction, option->rect,
2478 QRect(ir.right() - mbiw - 2,
2479 option->rect.top() + (option->rect.height()/2) - (mbih/2),
2480 mbiw + 1, mbih + 1));
2481 proxy()->drawPrimitive(PE_IndicatorArrowDown, &newBtn, painter, widget);
2482 }
2483 }
2484 return;
2485
2486 case CE_SizeGrip: {
2487 themeNumber = QWindowsVistaStylePrivate::StatusTheme;
2488 partId = SP_GRIPPER;
2489 QWindowsThemeData theme(nullptr, painter, themeNumber, partId);
2490 QSize size = (theme.size() * QWindowsStylePrivate::nativeMetricScaleFactor(widget)).toSize();
2491 size.rheight()--;
2492 if (const auto *sg = qstyleoption_cast<const QStyleOptionSizeGrip *>(option)) {
2493 switch (sg->corner) {
2494 case Qt::BottomRightCorner:
2495 rect = QRect(QPoint(rect.right() - size.width(), rect.bottom() - size.height()), size);
2496 break;
2497 case Qt::BottomLeftCorner:
2498 rect = QRect(QPoint(rect.left() + 1, rect.bottom() - size.height()), size);
2499 hMirrored = true;
2500 break;
2501 case Qt::TopRightCorner:
2502 rect = QRect(QPoint(rect.right() - size.width(), rect.top() + 1), size);
2503 vMirrored = true;
2504 break;
2505 case Qt::TopLeftCorner:
2506 rect = QRect(rect.topLeft() + QPoint(1, 1), size);
2507 hMirrored = vMirrored = true;
2508 }
2509 }
2510 break;
2511 }
2512
2513 case CE_Splitter:
2514 painter->eraseRect(option->rect);
2515 return;
2516
2517 case CE_TabBarTab:
2518#if QT_CONFIG(tabwidget)
2519 if (const auto *tab = qstyleoption_cast<const QStyleOptionTab *>(option))
2520 stateId = tab->state & State_Enabled ? TIS_NORMAL : TIS_DISABLED;
2521#endif // QT_CONFIG(tabwidget)
2522 break;
2523
2524 case CE_TabBarTabShape:
2525#if QT_CONFIG(tabwidget)
2526 if (const auto *tab = qstyleoption_cast<const QStyleOptionTab *>(option)) {
2527 themeNumber = QWindowsVistaStylePrivate::TabTheme;
2528 const bool isDisabled = !(tab->state & State_Enabled);
2529 const bool hasFocus = tab->state & State_HasFocus;
2530 const bool isHot = tab->state & State_MouseOver;
2531 const bool selected = tab->state & State_Selected;
2532 bool lastTab = tab->position == QStyleOptionTab::End;
2533 bool firstTab = tab->position == QStyleOptionTab::Beginning;
2534 const bool onlyOne = tab->position == QStyleOptionTab::OnlyOneTab;
2535 const bool leftAligned = proxy()->styleHint(SH_TabBar_Alignment, tab, widget) == Qt::AlignLeft;
2536 const bool centerAligned = proxy()->styleHint(SH_TabBar_Alignment, tab, widget) == Qt::AlignCenter;
2537 const int borderThickness = proxy()->pixelMetric(PM_DefaultFrameWidth, option, widget);
2538 const int tabOverlap = proxy()->pixelMetric(PM_TabBarTabOverlap, option, widget);
2539
2540 if (isDisabled)
2541 stateId = TIS_DISABLED;
2542 else if (selected)
2543 stateId = TIS_SELECTED;
2544 else if (hasFocus)
2545 stateId = TIS_FOCUSED;
2546 else if (isHot)
2547 stateId = TIS_HOT;
2548 else
2549 stateId = TIS_NORMAL;
2550
2551 // Selecting proper part depending on position
2552 if (firstTab || onlyOne) {
2553 if (leftAligned)
2554 partId = TABP_TABITEMLEFTEDGE;
2555 else if (centerAligned)
2556 partId = TABP_TABITEM;
2557 else // rightAligned
2558 partId = TABP_TABITEMRIGHTEDGE;
2559 } else {
2560 partId = TABP_TABITEM;
2561 }
2562
2563 if (tab->direction == Qt::RightToLeft
2564 && (tab->shape == QTabBar::RoundedNorth || tab->shape == QTabBar::RoundedSouth)) {
2565 bool temp = firstTab;
2566 firstTab = lastTab;
2567 lastTab = temp;
2568 }
2569
2570 const bool begin = firstTab || onlyOne;
2571 const bool end = lastTab || onlyOne;
2572
2573 switch (tab->shape) {
2574 case QTabBar::RoundedNorth:
2575 if (selected)
2576 rect.adjust(begin ? 0 : -tabOverlap, 0, end ? 0 : tabOverlap, borderThickness);
2577 else
2578 rect.adjust(begin? tabOverlap : 0, tabOverlap, end ? -tabOverlap : 0, 0);
2579 break;
2580 case QTabBar::RoundedSouth:
2581 //vMirrored = true;
2582 rotate = 180; // Not 100% correct, but works
2583 if (selected)
2584 rect.adjust(begin ? 0 : -tabOverlap , -borderThickness, end ? 0 : tabOverlap, 0);
2585 else
2586 rect.adjust(begin ? tabOverlap : 0, 0, end ? -tabOverlap : 0 , -tabOverlap);
2587 break;
2588 case QTabBar::RoundedEast:
2589 rotate = 90;
2590 if (selected)
2591 rect.adjust(-borderThickness, begin ? 0 : -tabOverlap, 0, end ? 0 : tabOverlap);
2592 else
2593 rect.adjust(0, begin ? tabOverlap : 0, -tabOverlap, end ? -tabOverlap : 0);
2594 break;
2595 case QTabBar::RoundedWest:
2596 hMirrored = true;
2597 rotate = 90;
2598 if (selected)
2599 rect.adjust(0, begin ? 0 : -tabOverlap, borderThickness, end ? 0 : tabOverlap);
2600 else
2601 rect.adjust(tabOverlap, begin ? tabOverlap : 0, 0, end ? -tabOverlap : 0);
2602 break;
2603 default:
2604 themeNumber = -1; // Do our own painting for triangular
2605 break;
2606 }
2607
2608 if (!selected) {
2609 switch (tab->shape) {
2610 case QTabBar::RoundedNorth:
2611 rect.adjust(0,0, 0,-1);
2612 break;
2613 case QTabBar::RoundedSouth:
2614 rect.adjust(0,1, 0,0);
2615 break;
2616 case QTabBar::RoundedEast:
2617 rect.adjust( 1,0, 0,0);
2618 break;
2619 case QTabBar::RoundedWest:
2620 rect.adjust(0,0, -1,0);
2621 break;
2622 default:
2623 break;
2624 }
2625 }
2626 }
2627#endif // QT_CONFIG(tabwidget)
2628 break;
2629
2630 case CE_ProgressBarGroove: {
2631 Qt::Orientation orient = Qt::Horizontal;
2632 if (const auto *pb = qstyleoption_cast<const QStyleOptionProgressBar *>(option))
2633 if (!(pb->state & QStyle::State_Horizontal))
2634 orient = Qt::Vertical;
2635
2636 partId = (orient == Qt::Horizontal) ? PP_BAR : PP_BARVERT;
2637 themeNumber = QWindowsVistaStylePrivate::ProgressTheme;
2638 stateId = 1;
2639 break;
2640 }
2641
2642 case CE_ProgressBarContents:
2643 if (const auto *bar = qstyleoption_cast<const QStyleOptionProgressBar *>(option)) {
2644 bool isIndeterminate = (bar->minimum == 0 && bar->maximum == 0);
2645 const bool vertical = !(bar->state & QStyle::State_Horizontal);
2646 const bool inverted = bar->invertedAppearance;
2647
2648 if (isIndeterminate || (bar->progress > 0 && (bar->progress < bar->maximum) && d->transitionsEnabled())) {
2649 if (!d->animation(styleObject(option)))
2650 d->startAnimation(new QProgressStyleAnimation(d->animationFps, styleObject(option)));
2651 } else {
2652 d->stopAnimation(styleObject(option));
2653 }
2654
2655 QWindowsThemeData theme(widget, painter,
2656 QWindowsVistaStylePrivate::ProgressTheme,
2657 vertical ? PP_FILLVERT : PP_FILL);
2658 theme.rect = option->rect;
2659 bool reverse = (bar->direction == Qt::LeftToRight && inverted) || (bar->direction == Qt::RightToLeft && !inverted);
2660 QTime current = d->animationTime();
2661
2662 if (isIndeterminate) {
2663 if (auto *progressAnimation = qobject_cast<QProgressStyleAnimation *>(d->animation(styleObject(option)))) {
2664 int glowSize = 120;
2665 int animationWidth = glowSize * 2 + (vertical ? theme.rect.height() : theme.rect.width());
2666 int animOffset = progressAnimation->startTime().msecsTo(current) / 4;
2667 if (animOffset > animationWidth)
2668 progressAnimation->setStartTime(d->animationTime());
2669 painter->save();
2670 painter->setClipRect(theme.rect);
2671 QRect animRect;
2672 QSize pixmapSize(14, 14);
2673 if (vertical) {
2674 animRect = QRect(theme.rect.left(),
2675 inverted ? rect.top() - glowSize + animOffset :
2676 rect.bottom() + glowSize - animOffset,
2677 rect.width(), glowSize);
2678 pixmapSize.setHeight(animRect.height());
2679 } else {
2680 animRect = QRect(rect.left() - glowSize + animOffset,
2681 rect.top(), glowSize, rect.height());
2682 animRect = QStyle::visualRect(reverse ? Qt::RightToLeft : Qt::LeftToRight,
2683 option->rect, animRect);
2684 pixmapSize.setWidth(animRect.width());
2685 }
2686 QString name = QStringLiteral(u"qiprogress-%1-%2").arg(pixmapSize.width()).arg(pixmapSize.height());
2687 QPixmap pixmap;
2688 if (!QPixmapCache::find(name, &pixmap)) {
2689 QImage image(pixmapSize, QImage::Format_ARGB32);
2690 image.fill(Qt::transparent);
2691 QPainter imagePainter(&image);
2692 theme.painter = &imagePainter;
2693 theme.partId = vertical ? PP_FILLVERT : PP_FILL;
2694 theme.rect = QRect(QPoint(0,0), animRect.size());
2695 QLinearGradient alphaGradient(0, 0, vertical ? 0 : image.width(),
2696 vertical ? image.height() : 0);
2697 alphaGradient.setColorAt(0, QColor(0, 0, 0, 0));
2698 alphaGradient.setColorAt(0.5, QColor(0, 0, 0, 220));
2699 alphaGradient.setColorAt(1, QColor(0, 0, 0, 0));
2700 imagePainter.fillRect(image.rect(), alphaGradient);
2701 imagePainter.setCompositionMode(QPainter::CompositionMode_SourceIn);
2702 d->drawBackground(theme);
2703 imagePainter.end();
2704 pixmap = QPixmap::fromImage(image);
2705 QPixmapCache::insert(name, pixmap);
2706 }
2707 painter->drawPixmap(animRect, pixmap);
2708 painter->restore();
2709 }
2710 } else {
2711 qint64 progress = qMax<qint64>(bar->progress, bar->minimum); // workaround for bug in QProgressBar
2712
2713 if (vertical) {
2714 int maxHeight = option->rect.height();
2715 int minHeight = 0;
2716 double vc6_workaround = ((progress - qint64(bar->minimum)) / qMax(double(1.0), double(qint64(bar->maximum) - qint64(bar->minimum))) * maxHeight);
2717 int height = isIndeterminate ? maxHeight: qMax(int(vc6_workaround), minHeight);
2718 theme.rect.setHeight(height);
2719 if (!inverted)
2720 theme.rect.moveTop(rect.height() - theme.rect.height());
2721 } else {
2722 int maxWidth = option->rect.width();
2723 int minWidth = 0;
2724 double vc6_workaround = ((progress - qint64(bar->minimum)) / qMax(double(1.0), double(qint64(bar->maximum) - qint64(bar->minimum))) * maxWidth);
2725 int width = isIndeterminate ? maxWidth : qMax(int(vc6_workaround), minWidth);
2726 theme.rect.setWidth(width);
2727 theme.rect = QStyle::visualRect(reverse ? Qt::RightToLeft : Qt::LeftToRight,
2728 option->rect, theme.rect);
2729 }
2730 d->drawBackground(theme);
2731
2732 if (QProgressStyleAnimation *a = qobject_cast<QProgressStyleAnimation *>(d->animation(styleObject(option)))) {
2733 int glowSize = 140;
2734 int animationWidth = glowSize * 2 + (vertical ? theme.rect.height() : theme.rect.width());
2735 int animOffset = a->startTime().msecsTo(current) / 4;
2736 theme.partId = vertical ? PP_MOVEOVERLAYVERT : PP_MOVEOVERLAY;
2737 if (animOffset > animationWidth) {
2738 if (bar->progress < bar->maximum)
2739 a->setStartTime(d->animationTime());
2740 else
2741 d->stopAnimation(styleObject(option)); //we stop the glow motion only after it has
2742 //moved out of view
2743 }
2744 painter->save();
2745 painter->setClipRect(theme.rect);
2746 if (vertical) {
2747 theme.rect = QRect(theme.rect.left(),
2748 inverted ? rect.top() - glowSize + animOffset :
2749 rect.bottom() + glowSize - animOffset,
2750 rect.width(), glowSize);
2751 } else {
2752 theme.rect = QRect(rect.left() - glowSize + animOffset,rect.top(), glowSize, rect.height());
2753 theme.rect = QStyle::visualRect(reverse ? Qt::RightToLeft : Qt::LeftToRight, option->rect, theme.rect);
2754 }
2755 d->drawBackground(theme);
2756 painter->restore();
2757 }
2758 }
2759 }
2760 return;
2761
2762 case CE_MenuBarItem:
2763 if (const auto *mbi = qstyleoption_cast<const QStyleOptionMenuItem *>(option)) {
2764 if (mbi->menuItemType == QStyleOptionMenuItem::DefaultItem)
2765 break;
2766
2767 QPalette::ColorRole textRole = disabled ? QPalette::Text : QPalette::ButtonText;
2768 const auto dpr = QStyleHelper::getDpr(widget);
2769 const auto extent = proxy()->pixelMetric(PM_SmallIconSize, option, widget);
2770 const auto pix = mbi->icon.pixmap(QSize(extent, extent), dpr, QIcon::Normal);
2771
2772 int alignment = Qt::AlignCenter | Qt::TextShowMnemonic | Qt::TextDontClip | Qt::TextSingleLine;
2773 if (!proxy()->styleHint(SH_UnderlineShortcut, mbi, widget))
2774 alignment |= Qt::TextHideMnemonic;
2775
2776 if (widget && mbi->palette.color(QPalette::Window) != Qt::transparent) { // Not needed for QtQuick Controls
2777 //The rect adjustment is a workaround for the menu not really filling its background.
2778 QWindowsThemeData theme(widget, painter,
2779 QWindowsVistaStylePrivate::MenuTheme,
2780 MENU_BARBACKGROUND, 0, option->rect.adjusted(-1, 0, 2, 1));
2781 d->drawBackground(theme);
2782
2783 int stateId = MBI_NORMAL;
2784 if (disabled)
2785 stateId = MBI_DISABLED;
2786 else if (pressed)
2787 stateId = MBI_PUSHED;
2788 else if (selected)
2789 stateId = MBI_HOT;
2790
2791 QWindowsThemeData theme2(widget, painter,
2792 QWindowsVistaStylePrivate::MenuTheme,
2793 MENU_BARITEM, stateId, option->rect);
2794 d->drawBackground(theme2);
2795 }
2796
2797 if (!pix.isNull())
2798 drawItemPixmap(painter, mbi->rect, alignment, pix);
2799 else
2800 drawItemText(painter, mbi->rect, alignment, mbi->palette, mbi->state & State_Enabled, mbi->text, textRole);
2801 }
2802 return;
2803
2804#if QT_CONFIG(menu)
2805 case CE_MenuEmptyArea:
2806 if (const auto *menuitem = qstyleoption_cast<const QStyleOptionMenuItem *>(option)) {
2807 QBrush fill = menuitem->palette.brush((menuitem->state & State_Selected) ?
2808 QPalette::Highlight : QPalette::Button);
2809 painter->fillRect(rect, fill);
2810 break;
2811 }
2812 return;
2813
2814 case CE_MenuItem:
2815 if (const auto *menuitem = qstyleoption_cast<const QStyleOptionMenuItem *>(option)) {
2816 // windows always has a check column, regardless whether we have an icon or not
2817 const qreal factor = QWindowsVistaStylePrivate::nativeMetricScaleFactor(widget);
2818 int checkcol = qRound(qreal(25) * factor);
2819 const int gutterWidth = qRound(qreal(3) * factor);
2820 {
2821 QWindowsThemeData theme(widget, nullptr, QWindowsVistaStylePrivate::MenuTheme,
2822 MENU_POPUPCHECKBACKGROUND, MBI_HOT);
2823 QWindowsThemeData themeSize = theme;
2824 themeSize.partId = MENU_POPUPCHECK;
2825 themeSize.stateId = 0;
2826 const QSizeF size = themeSize.size() * QWindowsStylePrivate::nativeMetricScaleFactor(widget);
2827 const QMarginsF margins = themeSize.margins() * QWindowsStylePrivate::nativeMetricScaleFactor(widget);
2828 checkcol = qMax(menuitem->maxIconWidth, qRound(gutterWidth + size.width() + margins.left() + margins.right()));
2829 }
2830 QRect rect = option->rect;
2831
2832 //fill popup background
2833 QWindowsThemeData popupbackgroundTheme(widget, painter, QWindowsVistaStylePrivate::MenuTheme,
2834 MENU_POPUPBACKGROUND, stateId, option->rect);
2835 d->drawBackground(popupbackgroundTheme);
2836
2837 //draw vertical menu line
2838 if (option->direction == Qt::LeftToRight)
2839 checkcol += rect.x();
2840 QPoint p1 = QStyle::visualPos(option->direction, menuitem->rect, QPoint(checkcol, rect.top()));
2841 QPoint p2 = QStyle::visualPos(option->direction, menuitem->rect, QPoint(checkcol, rect.bottom()));
2842 QRect gutterRect(p1.x(), p1.y(), gutterWidth, p2.y() - p1.y() + 1);
2843 QWindowsThemeData theme2(widget, painter, QWindowsVistaStylePrivate::MenuTheme,
2844 MENU_POPUPGUTTER, stateId, gutterRect);
2845 d->drawBackground(theme2);
2846
2847 int x, y, w, h;
2848 menuitem->rect.getRect(&x, &y, &w, &h);
2849 int tab = menuitem->reservedShortcutWidth;
2850 bool dis = !(menuitem->state & State_Enabled);
2851 bool checked = menuitem->checkType != QStyleOptionMenuItem::NotCheckable
2852 ? menuitem->checked : false;
2853 bool act = menuitem->state & State_Selected;
2854
2855 if (menuitem->menuItemType == QStyleOptionMenuItem::Separator) {
2856 int yoff = y-2 + h / 2;
2857 const int separatorSize = qRound(qreal(6) * QWindowsStylePrivate::nativeMetricScaleFactor(widget));
2858 QPoint p1 = QPoint(x + checkcol, yoff);
2859 QPoint p2 = QPoint(x + w + separatorSize, yoff);
2860 stateId = MBI_HOT;
2861 QRect subRect(p1.x() + (gutterWidth - menuitem->rect.x()), p1.y(),
2862 p2.x() - p1.x(), separatorSize);
2863 subRect = QStyle::visualRect(option->direction, option->rect, subRect );
2864 QWindowsThemeData theme2(widget, painter,
2865 QWindowsVistaStylePrivate::MenuTheme,
2866 MENU_POPUPSEPARATOR, stateId, subRect);
2867 d->drawBackground(theme2);
2868 return;
2869 }
2870
2871 QRect vCheckRect = visualRect(option->direction, menuitem->rect, QRect(menuitem->rect.x(),
2872 menuitem->rect.y(), checkcol - (gutterWidth + menuitem->rect.x()), menuitem->rect.height()));
2873
2874 if (act) {
2875 stateId = dis ? MBI_DISABLED : MBI_HOT;
2876 QWindowsThemeData theme2(widget, painter,
2877 QWindowsVistaStylePrivate::MenuTheme,
2878 MENU_POPUPITEM, stateId, option->rect);
2879 d->drawBackground(theme2);
2880 }
2881
2882 if (checked) {
2883 QWindowsThemeData theme(widget, painter,
2884 QWindowsVistaStylePrivate::MenuTheme,
2885 MENU_POPUPCHECKBACKGROUND,
2886 menuitem->icon.isNull() ? MBI_HOT : MBI_PUSHED, vCheckRect);
2887 QWindowsThemeData themeSize = theme;
2888 themeSize.partId = MENU_POPUPCHECK;
2889 themeSize.stateId = 0;
2890 const QSizeF size = themeSize.size() * QWindowsStylePrivate::nativeMetricScaleFactor(widget);
2891 const QMarginsF margins = themeSize.margins() * QWindowsStylePrivate::nativeMetricScaleFactor(widget);
2892 QRect checkRect(0, 0, qRound(size.width() + margins.left() + margins.right()),
2893 qRound(size.height() + margins.bottom() + margins.top()));
2894 checkRect.moveCenter(vCheckRect.center());
2895 theme.rect = checkRect;
2896
2897 d->drawBackground(theme);
2898
2899 if (menuitem->icon.isNull()) {
2900 checkRect = QRect(QPoint(0, 0), size.toSize());
2901 checkRect.moveCenter(theme.rect.center());
2902 theme.rect = checkRect;
2903
2904 theme.partId = MENU_POPUPCHECK;
2905 bool bullet = menuitem->checkType & QStyleOptionMenuItem::Exclusive;
2906 if (dis)
2907 theme.stateId = bullet ? MC_BULLETDISABLED: MC_CHECKMARKDISABLED;
2908 else
2909 theme.stateId = bullet ? MC_BULLETNORMAL: MC_CHECKMARKNORMAL;
2910 d->drawBackground(theme);
2911 }
2912 }
2913
2914 if (!menuitem->icon.isNull()) {
2915 QIcon::Mode mode = dis ? QIcon::Disabled : QIcon::Normal;
2916 if (act && !dis)
2917 mode = QIcon::Active;
2918 const auto size = proxy()->pixelMetric(PM_SmallIconSize, option, widget);
2919 QRect pmr(QPoint(0, 0), QSize(size, size));
2920 pmr.moveCenter(vCheckRect.center());
2921 menuitem->icon.paint(painter, pmr, Qt::AlignCenter, mode,
2922 checked ? QIcon::On : QIcon::Off);
2923 }
2924
2925 painter->setPen(menuitem->palette.buttonText().color());
2926
2927 const QColor textColor = menuitem->palette.text().color();
2928 if (dis)
2929 painter->setPen(textColor);
2930
2931 int xm = windowsItemFrame + checkcol + windowsItemHMargin + (gutterWidth - menuitem->rect.x()) - 1;
2932 int xpos = menuitem->rect.x() + xm;
2933 QRect textRect(xpos, y + windowsItemVMargin, w - xm - windowsRightBorder - tab + 1, h - 2 * windowsItemVMargin);
2934 QRect vTextRect = visualRect(option->direction, menuitem->rect, textRect);
2935 QString s = menuitem->text;
2936 if (!s.isEmpty()) { // draw text
2937 painter->save();
2938 int t = s.indexOf(QLatin1Char('\t'));
2939 int text_flags = Qt::AlignVCenter | Qt::TextShowMnemonic | Qt::TextDontClip | Qt::TextSingleLine;
2940 if (!proxy()->styleHint(SH_UnderlineShortcut, menuitem, widget))
2941 text_flags |= Qt::TextHideMnemonic;
2942 text_flags |= Qt::AlignLeft;
2943 if (t >= 0) {
2944 QRect vShortcutRect = visualRect(option->direction, menuitem->rect,
2945 QRect(textRect.topRight(), QPoint(menuitem->rect.right(), textRect.bottom())));
2946 painter->drawText(vShortcutRect, text_flags, s.mid(t + 1));
2947 s = s.left(t);
2948 }
2949 QFont font = menuitem->font;
2950 if (menuitem->menuItemType == QStyleOptionMenuItem::DefaultItem)
2951 font.setBold(true);
2952 painter->setFont(font);
2953 painter->setPen(textColor);
2954 painter->drawText(vTextRect, text_flags, s.left(t));
2955 painter->restore();
2956 }
2957 if (menuitem->menuItemType == QStyleOptionMenuItem::SubMenu) {// draw sub menu arrow
2958 int dim = (h - 2 * windowsItemFrame) / 2;
2959 PrimitiveElement arrow;
2960 arrow = (option->direction == Qt::RightToLeft) ? PE_IndicatorArrowLeft : PE_IndicatorArrowRight;
2961 xpos = x + w - windowsArrowHMargin - windowsItemFrame - dim;
2962 QRect vSubMenuRect = visualRect(option->direction, menuitem->rect, QRect(xpos, y + h / 2 - dim / 2, dim, dim));
2963 QStyleOptionMenuItem newMI = *menuitem;
2964 newMI.rect = vSubMenuRect;
2965 newMI.state = dis ? State_None : State_Enabled;
2966 proxy()->drawPrimitive(arrow, &newMI, painter, widget);
2967 }
2968 }
2969 return;
2970#endif // QT_CONFIG(menu)
2971
2972 case CE_HeaderSection:
2973 if (const QStyleOptionHeader *header = qstyleoption_cast<const QStyleOptionHeader *>(option)) {
2974 partId = HP_HEADERITEM;
2975 if (flags & State_Sunken)
2976 stateId = HIS_PRESSED;
2977 else if (flags & State_MouseOver)
2978 stateId = HIS_HOT;
2979 else
2980 stateId = HIS_NORMAL;
2981
2982 if (header->sortIndicator != QStyleOptionHeader::None)
2983 stateId += 3;
2984
2985 QWindowsThemeData theme(widget, painter,
2986 QWindowsVistaStylePrivate::HeaderTheme,
2987 partId, stateId, option->rect);
2988 d->drawBackground(theme);
2989 }
2990 return;
2991
2992 case CE_MenuBarEmptyArea: {
2993 stateId = MBI_NORMAL;
2994 if (!(state & State_Enabled))
2995 stateId = MBI_DISABLED;
2996 QWindowsThemeData theme(widget, painter, QWindowsVistaStylePrivate::MenuTheme,
2997 MENU_BARBACKGROUND, stateId, option->rect);
2998 d->drawBackground(theme);
2999 return;
3000 }
3001
3002 case CE_ToolBar:
3003#if QT_CONFIG(toolbar)
3004 if (const auto *toolbar = qstyleoption_cast<const QStyleOptionToolBar *>(option)) {
3005 QPalette pal = option->palette;
3006 pal.setColor(QPalette::Dark, option->palette.window().color().darker(130));
3007 QStyleOptionToolBar copyOpt = *toolbar;
3008 copyOpt.palette = pal;
3009 QWindowsStyle::drawControl(element, &copyOpt, painter, widget);
3010 }
3011#endif // QT_CONFIG(toolbar)
3012 return;
3013
3014#if QT_CONFIG(dockwidget)
3015 case CE_DockWidgetTitle:
3016 if (const auto *dwOpt = qstyleoption_cast<const QStyleOptionDockWidget *>(option)) {
3017 QRect rect = option->rect;
3018 const QDockWidget *dw = qobject_cast<const QDockWidget *>(widget);
3019 bool isFloating = dw && dw->isFloating();
3020 int buttonMargin = 4;
3021 int mw = proxy()->pixelMetric(QStyle::PM_DockWidgetTitleMargin, dwOpt, widget);
3022 int fw = proxy()->pixelMetric(PM_DockWidgetFrameWidth, dwOpt, widget);
3023
3024 const bool verticalTitleBar = dwOpt->verticalTitleBar;
3025
3026 if (verticalTitleBar) {
3027 rect = rect.transposed();
3028
3029 painter->translate(rect.left() - 1, rect.top() + rect.width());
3030 painter->rotate(-90);
3031 painter->translate(-rect.left() + 1, -rect.top());
3032 }
3033
3034 QRect r = option->rect.adjusted(0, 2, -1, -3);
3035 QRect titleRect = r;
3036
3037 if (dwOpt->closable) {
3038 QSize sz = proxy()->standardIcon(QStyle::SP_TitleBarCloseButton, dwOpt, widget).actualSize(QSize(10, 10));
3039 titleRect.adjust(0, 0, -sz.width() - mw - buttonMargin, 0);
3040 }
3041
3042 if (dwOpt->floatable) {
3043 QSize sz = proxy()->standardIcon(QStyle::SP_TitleBarMaxButton, dwOpt, widget).actualSize(QSize(10, 10));
3044 titleRect.adjust(0, 0, -sz.width() - mw - buttonMargin, 0);
3045 }
3046
3047 if (isFloating) {
3048 titleRect.adjust(0, -fw, 0, 0);
3049 if (widget && widget->windowIcon().cacheKey() != QApplication::windowIcon().cacheKey())
3050 titleRect.adjust(titleRect.height() + mw, 0, 0, 0);
3051 } else {
3052 titleRect.adjust(mw, 0, 0, 0);
3053 if (!dwOpt->floatable && !dwOpt->closable)
3054 titleRect.adjust(0, 0, -mw, 0);
3055 }
3056
3057 if (!verticalTitleBar)
3058 titleRect = visualRect(dwOpt->direction, r, titleRect);
3059
3060 if (isFloating) {
3061 const bool isActive = dwOpt->state & State_Active;
3062 themeNumber = QWindowsVistaStylePrivate::WindowTheme;
3063 if (isActive)
3064 stateId = CS_ACTIVE;
3065 else
3066 stateId = CS_INACTIVE;
3067
3068 rect = rect.adjusted(-fw, -fw, fw, 0);
3069
3070 QWindowsThemeData theme(widget, painter, themeNumber, 0, stateId);
3071 if (!theme.isValid())
3072 break;
3073
3074 // Draw small type title bar
3075 theme.rect = rect;
3076 theme.partId = WP_SMALLCAPTION;
3077 d->drawBackground(theme);
3078
3079 // Figure out maximal button space on title bar
3080
3081 QIcon ico = widget->windowIcon();
3082 bool hasIcon = (ico.cacheKey() != QApplication::windowIcon().cacheKey());
3083 if (hasIcon) {
3084 const auto titleHeight = rect.height() - 2;
3085 const auto dpr = QStyleHelper::getDpr(widget);
3086 const auto pxIco = ico.pixmap(QSize(titleHeight, titleHeight), dpr);
3087 if (!verticalTitleBar && dwOpt->direction == Qt::RightToLeft)
3088 painter->drawPixmap(rect.width() - titleHeight - pxIco.width(), rect.bottom() - titleHeight - 2, pxIco);
3089 else
3090 painter->drawPixmap(fw, rect.bottom() - titleHeight - 2, pxIco);
3091 }
3092 if (!dwOpt->title.isEmpty()) {
3093 QPen oldPen = painter->pen();
3094 QFont oldFont = painter->font();
3095 QFont titleFont = oldFont;
3096 titleFont.setBold(true);
3097 painter->setFont(titleFont);
3098 QString titleText
3099 = painter->fontMetrics().elidedText(dwOpt->title, Qt::ElideRight, titleRect.width());
3100
3101 int result = TST_NONE;
3102 GetThemeEnumValue(theme.handle(), WP_SMALLCAPTION, isActive ? CS_ACTIVE : CS_INACTIVE, TMT_TEXTSHADOWTYPE, &result);
3103 if (result != TST_NONE) {
3104 COLORREF textShadowRef;
3105 GetThemeColor(theme.handle(), WP_SMALLCAPTION, isActive ? CS_ACTIVE : CS_INACTIVE, TMT_TEXTSHADOWCOLOR, &textShadowRef);
3106 QColor textShadow = qRgb(GetRValue(textShadowRef), GetGValue(textShadowRef), GetBValue(textShadowRef));
3107 painter->setPen(textShadow);
3108 drawItemText(painter, titleRect.adjusted(1, 1, 1, 1),
3109 Qt::AlignLeft | Qt::AlignBottom | Qt::TextHideMnemonic, dwOpt->palette,
3110 dwOpt->state & State_Enabled, titleText);
3111 }
3112
3113 COLORREF captionText = GetSysColor(isActive ? COLOR_CAPTIONTEXT : COLOR_INACTIVECAPTIONTEXT);
3114 QColor textColor = qRgb(GetRValue(captionText), GetGValue(captionText), GetBValue(captionText));
3115 painter->setPen(textColor);
3116 drawItemText(painter, titleRect,
3117 Qt::AlignLeft | Qt::AlignBottom | Qt::TextHideMnemonic, dwOpt->palette,
3118 dwOpt->state & State_Enabled, titleText);
3119 painter->setFont(oldFont);
3120 painter->setPen(oldPen);
3121 }
3122 } else {
3123 painter->setBrush(option->palette.window().color().darker(110));
3124 painter->setPen(option->palette.window().color().darker(130));
3125 painter->drawRect(rect.adjusted(0, 1, -1, -3));
3126
3127 if (!dwOpt->title.isEmpty()) {
3128 QString titleText = painter->fontMetrics().elidedText(dwOpt->title, Qt::ElideRight,
3129 verticalTitleBar ? titleRect.height() : titleRect.width());
3130 const int indent = 4;
3131 drawItemText(painter, rect.adjusted(indent + 1, 1, -indent - 1, -1),
3132 Qt::AlignLeft | Qt::AlignVCenter | Qt::TextHideMnemonic,
3133 dwOpt->palette,
3134 dwOpt->state & State_Enabled, titleText,
3135 QPalette::WindowText);
3136 }
3137 }
3138 }
3139 return;
3140#endif // QT_CONFIG(dockwidget)
3141
3142#if QT_CONFIG(rubberband)
3143 case CE_RubberBand:
3144 if (qstyleoption_cast<const QStyleOptionRubberBand *>(option)) {
3145 QColor highlight = option->palette.color(QPalette::Active, QPalette::Highlight);
3146 painter->save();
3147 painter->setPen(highlight.darker(120));
3148 QColor dimHighlight(qMin(highlight.red()/2 + 110, 255),
3149 qMin(highlight.green()/2 + 110, 255),
3150 qMin(highlight.blue()/2 + 110, 255),
3151 (widget && widget->isWindow())? 255 : 127);
3152 painter->setBrush(dimHighlight);
3153 painter->drawRect(option->rect.adjusted(0, 0, -1, -1));
3154 painter->restore();
3155 return;
3156 }
3157 break;
3158#endif // QT_CONFIG(rubberband)
3159
3160 case CE_HeaderEmptyArea:
3161 if (option->state & State_Horizontal) {
3162 themeNumber = QWindowsVistaStylePrivate::HeaderTheme;
3163 stateId = HIS_NORMAL;
3164 } else {
3165 QWindowsStyle::drawControl(CE_HeaderEmptyArea, option, painter, widget);
3166 return;
3167 }
3168 break;
3169
3170#if QT_CONFIG(itemviews)
3171 case CE_ItemViewItem: {
3172 const QStyleOptionViewItem *vopt;
3173 const QAbstractItemView *view = qobject_cast<const QAbstractItemView *>(widget);
3174 bool newStyle = true;
3175
3176 if (qobject_cast<const QTableView*>(widget))
3177 newStyle = false;
3178
3179 QWindowsThemeData theme(widget, painter, themeNumber, partId, stateId, rect);
3180
3181 if (newStyle && view && (vopt = qstyleoption_cast<const QStyleOptionViewItem *>(option))) {
3182 /*
3183 // We cannot currently get the correct selection color for "explorer style" views
3184 COLORREF cref = 0;
3185 QWindowsThemeData theme(d->treeViewHelper(), 0, QLatin1String("LISTVIEW"), 0, 0);
3186 unsigned int res = GetThemeColor(theme.handle(), LVP_LISTITEM, LISS_SELECTED, TMT_TEXTCOLOR, &cref);
3187 QColor textColor(GetRValue(cref), GetGValue(cref), GetBValue(cref));
3188 */
3189 QPalette palette = vopt->palette;
3190 palette.setColor(QPalette::All, QPalette::HighlightedText, palette.color(QPalette::Active, QPalette::Text));
3191 // Note that setting a saturated color here results in ugly XOR colors in the focus rect
3192 palette.setColor(QPalette::All, QPalette::Highlight, palette.base().color().darker(108));
3193 QStyleOptionViewItem adjustedOption = *vopt;
3194 adjustedOption.palette = palette;
3195 // We hide the focusrect in singleselection as it is not required
3196 if ((view->selectionMode() == QAbstractItemView::SingleSelection)
3197 && !(vopt->state & State_KeyboardFocusChange))
3198 adjustedOption.state &= ~State_HasFocus;
3199 if (!theme.isValid()) {
3200 QWindowsStyle::drawControl(element, &adjustedOption, painter, widget);
3201 return;
3202 }
3203 } else {
3204 if (!theme.isValid()) {
3205 QWindowsStyle::drawControl(element, option, painter, widget);
3206 return;
3207 }
3208 }
3209
3210 theme.rotate = rotate;
3211 theme.mirrorHorizontally = hMirrored;
3212 theme.mirrorVertically = vMirrored;
3213 d->drawBackground(theme);
3214 return;
3215 }
3216#endif // QT_CONFIG(itemviews)
3217
3218#if QT_CONFIG(combobox)
3219 case CE_ComboBoxLabel:
3220 QCommonStyle::drawControl(element, option, painter, widget);
3221 return;
3222#endif // QT_CONFIG(combobox)
3223
3224 default:
3225 break;
3226 }
3227
3228 QWindowsThemeData theme(widget, painter, themeNumber, partId, stateId, rect);
3229
3230 if (!theme.isValid()) {
3231 QWindowsStyle::drawControl(element, option, painter, widget);
3232 return;
3233 }
3234
3235 theme.rotate = rotate;
3236 theme.mirrorHorizontally = hMirrored;
3237 theme.mirrorVertically = vMirrored;
3238
3239 d->drawBackground(theme);
3240}
3241
3242/*!
3243 \internal
3244 see drawPrimitive for comments on the animation support
3245
3246 */
3247void QWindowsVistaStyle::drawComplexControl(ComplexControl control, const QStyleOptionComplex *option,
3248 QPainter *painter, const QWidget *widget) const
3249{
3250 QWindowsVistaStylePrivate *d = const_cast<QWindowsVistaStylePrivate*>(d_func());
3251
3252 if (!QWindowsVistaStylePrivate::useVista()) {
3253 QWindowsStyle::drawComplexControl(control, option, painter, widget);
3254 return;
3255 }
3256
3257 State state = option->state;
3258 SubControls sub = option->subControls;
3259
3260 int partId = 0;
3261 int stateId = 0;
3262
3263 State flags = option->state;
3264 if (widget && widget->testAttribute(Qt::WA_UnderMouse) && widget->isActiveWindow())
3265 flags |= State_MouseOver;
3266
3267 if (d->transitionsEnabled() && canAnimate(option))
3268 {
3269 if (control == CC_ScrollBar || control == CC_SpinBox || control == CC_ComboBox) {
3270 QObject *styleObject = option->styleObject; // Can be widget or qquickitem
3271
3272 int oldState = styleObject->property("_q_stylestate").toInt();
3273 int oldActiveControls = styleObject->property("_q_stylecontrols").toInt();
3274
3275 QRect oldRect = styleObject->property("_q_stylerect").toRect();
3276 styleObject->setProperty("_q_stylestate", int(option->state));
3277 styleObject->setProperty("_q_stylecontrols", int(option->activeSubControls));
3278 styleObject->setProperty("_q_stylerect", option->rect);
3279
3280 bool doTransition = ((state & State_Sunken) != (oldState & State_Sunken)
3281 || (state & State_On) != (oldState & State_On)
3282 || (state & State_MouseOver) != (oldState & State_MouseOver)
3283 || oldActiveControls != int(option->activeSubControls));
3284
3285 if (qstyleoption_cast<const QStyleOptionSlider *>(option)) {
3286 QRect oldSliderPos = styleObject->property("_q_stylesliderpos").toRect();
3287 QRect currentPos = proxy()->subControlRect(CC_ScrollBar, option, SC_ScrollBarSlider, widget);
3288 styleObject->setProperty("_q_stylesliderpos", currentPos);
3289 if (oldSliderPos != currentPos) {
3290 doTransition = false;
3291 d->stopAnimation(styleObject);
3292 }
3293 } else if (control == CC_SpinBox) {
3294 //spinboxes have a transition when focus changes
3295 if (!doTransition)
3296 doTransition = (state & State_HasFocus) != (oldState & State_HasFocus);
3297 }
3298
3299 if (oldRect != option->rect) {
3300 doTransition = false;
3301 d->stopAnimation(styleObject);
3302 }
3303
3304 if (doTransition) {
3305 QImage startImage = createAnimationBuffer(option, widget);
3306 QPainter startPainter(&startImage);
3307
3308 QImage endImage = createAnimationBuffer(option, widget);
3309 QPainter endPainter(&endImage);
3310
3311 QWindowsVistaAnimation *anim = qobject_cast<QWindowsVistaAnimation *>(d->animation(styleObject));
3313
3314 // Draw the image that ends the animation by using the current styleoption
3315 QStyleOptionComplex *styleOption = qstyleoption_cast<QStyleOptionComplex*>(clonedAnimationStyleOption(option));
3316
3317 styleObject->setProperty("_q_no_animation", true);
3318
3319 // Draw transition source
3320 if (!anim) {
3321 styleOption->state = QStyle::State(oldState);
3322 styleOption->activeSubControls = QStyle::SubControl(oldActiveControls);
3323 proxy()->drawComplexControl(control, styleOption, &startPainter, widget);
3324 } else {
3325 anim->paint(&startPainter, option);
3326 }
3327 t->setStartImage(startImage);
3328
3329 // Draw transition target
3330 styleOption->state = option->state;
3331 styleOption->activeSubControls = option->activeSubControls;
3332 proxy()->drawComplexControl(control, styleOption, &endPainter, widget);
3333
3334 styleObject->setProperty("_q_no_animation", false);
3335
3336 t->setEndImage(endImage);
3337 t->setStartTime(d->animationTime());
3338
3339 if (option->state & State_MouseOver || option->state & State_Sunken)
3340 t->setDuration(150);
3341 else
3342 t->setDuration(500);
3343
3344 deleteClonedAnimationStyleOption(styleOption);
3345 d->startAnimation(t);
3346 }
3347 if (QWindowsVistaAnimation *anim = qobject_cast<QWindowsVistaAnimation *>(d->animation(styleObject))) {
3348 anim->paint(painter, option);
3349 return;
3350 }
3351 }
3352 }
3353
3354 switch (control) {
3355
3356#if QT_CONFIG(slider)
3357 case CC_Slider:
3358 if (const auto *slider = qstyleoption_cast<const QStyleOptionSlider *>(option)) {
3359 QWindowsThemeData theme(widget, painter, QWindowsVistaStylePrivate::TrackBarTheme);
3360 QRect slrect = slider->rect;
3361 QRegion tickreg = slrect;
3362 if (sub & SC_SliderGroove) {
3363 theme.rect = proxy()->subControlRect(CC_Slider, option, SC_SliderGroove, widget);
3364 if (slider->orientation == Qt::Horizontal) {
3365 partId = TKP_TRACK;
3366 stateId = TRS_NORMAL;
3367 theme.rect = QRect(slrect.left(), theme.rect.center().y() - 2, slrect.width(), 4);
3368 } else {
3369 partId = TKP_TRACKVERT;
3370 stateId = TRVS_NORMAL;
3371 theme.rect = QRect(theme.rect.center().x() - 2, slrect.top(), 4, slrect.height());
3372 }
3373 theme.partId = partId;
3374 theme.stateId = stateId;
3375 d->drawBackground(theme);
3376 tickreg -= theme.rect;
3377 }
3378 if (sub & SC_SliderTickmarks) {
3379 int tickOffset = proxy()->pixelMetric(PM_SliderTickmarkOffset, slider, widget);
3380 int ticks = slider->tickPosition;
3381 int thickness = proxy()->pixelMetric(PM_SliderControlThickness, slider, widget);
3382 int len = proxy()->pixelMetric(PM_SliderLength, slider, widget);
3383 int available = proxy()->pixelMetric(PM_SliderSpaceAvailable, slider, widget);
3384 int interval = slider->tickInterval;
3385 if (interval <= 0) {
3386 interval = slider->singleStep;
3387 if (QStyle::sliderPositionFromValue(slider->minimum, slider->maximum, interval,
3388 available)
3389 - QStyle::sliderPositionFromValue(slider->minimum, slider->maximum,
3390 0, available) < 3)
3391 interval = slider->pageStep;
3392 }
3393 if (!interval)
3394 interval = 1;
3395 int fudge = len / 2;
3396 int pos;
3397 int bothOffset = (ticks & QSlider::TicksAbove && ticks & QSlider::TicksBelow) ? 1 : 0;
3398 painter->setPen(d->sliderTickColor);
3399 QVarLengthArray<QLine, 32> lines;
3400 int v = slider->minimum;
3401 while (v <= slider->maximum + 1) {
3402 if (v == slider->maximum + 1 && interval == 1)
3403 break;
3404 const int v_ = qMin(v, slider->maximum);
3405 int tickLength = (v_ == slider->minimum || v_ >= slider->maximum) ? 4 : 3;
3406 pos = QStyle::sliderPositionFromValue(slider->minimum, slider->maximum,
3407 v_, available) + fudge;
3408 if (slider->orientation == Qt::Horizontal) {
3409 if (ticks & QSlider::TicksAbove) {
3410 lines.append(QLine(pos, tickOffset - 1 - bothOffset,
3411 pos, tickOffset - 1 - bothOffset - tickLength));
3412 }
3413
3414 if (ticks & QSlider::TicksBelow) {
3415 lines.append(QLine(pos, tickOffset + thickness + bothOffset,
3416 pos, tickOffset + thickness + bothOffset + tickLength));
3417 }
3418 } else {
3419 if (ticks & QSlider::TicksAbove) {
3420 lines.append(QLine(tickOffset - 1 - bothOffset, pos,
3421 tickOffset - 1 - bothOffset - tickLength, pos));
3422 }
3423
3424 if (ticks & QSlider::TicksBelow) {
3425 lines.append(QLine(tickOffset + thickness + bothOffset, pos,
3426 tickOffset + thickness + bothOffset + tickLength, pos));
3427 }
3428 }
3429 // in the case where maximum is max int
3430 int nextInterval = v + interval;
3431 if (nextInterval < v)
3432 break;
3433 v = nextInterval;
3434 }
3435 if (!lines.isEmpty()) {
3436 painter->save();
3437 painter->translate(slrect.topLeft());
3438 painter->drawLines(lines.constData(), lines.size());
3439 painter->restore();
3440 }
3441 }
3442 if (sub & SC_SliderHandle) {
3443 theme.rect = proxy()->subControlRect(CC_Slider, option, SC_SliderHandle, widget);
3444 if (slider->orientation == Qt::Horizontal) {
3445 if (slider->tickPosition == QSlider::TicksAbove)
3446 partId = TKP_THUMBTOP;
3447 else if (slider->tickPosition == QSlider::TicksBelow)
3448 partId = TKP_THUMBBOTTOM;
3449 else
3450 partId = TKP_THUMB;
3451
3452 if (!(slider->state & State_Enabled))
3453 stateId = TUS_DISABLED;
3454 else if (slider->activeSubControls & SC_SliderHandle && (slider->state & State_Sunken))
3455 stateId = TUS_PRESSED;
3456 else if (slider->activeSubControls & SC_SliderHandle && (slider->state & State_MouseOver))
3457 stateId = TUS_HOT;
3458 else if (flags & State_HasFocus)
3459 stateId = TUS_FOCUSED;
3460 else
3461 stateId = TUS_NORMAL;
3462 } else {
3463 if (slider->tickPosition == QSlider::TicksLeft)
3464 partId = TKP_THUMBLEFT;
3465 else if (slider->tickPosition == QSlider::TicksRight)
3466 partId = TKP_THUMBRIGHT;
3467 else
3468 partId = TKP_THUMBVERT;
3469
3470 if (!(slider->state & State_Enabled))
3471 stateId = TUVS_DISABLED;
3472 else if (slider->activeSubControls & SC_SliderHandle && (slider->state & State_Sunken))
3473 stateId = TUVS_PRESSED;
3474 else if (slider->activeSubControls & SC_SliderHandle && (slider->state & State_MouseOver))
3475 stateId = TUVS_HOT;
3476 else if (flags & State_HasFocus)
3477 stateId = TUVS_FOCUSED;
3478 else
3479 stateId = TUVS_NORMAL;
3480 }
3481 theme.partId = partId;
3482 theme.stateId = stateId;
3483 d->drawBackground(theme);
3484 }
3485 if (slider->state & State_HasFocus) {
3486 QStyleOptionFocusRect fropt;
3487 fropt.QStyleOption::operator=(*slider);
3488 fropt.rect = subElementRect(SE_SliderFocusRect, slider, widget);
3489 proxy()->drawPrimitive(PE_FrameFocusRect, &fropt, painter, widget);
3490 }
3491 }
3492 break;
3493#endif
3494
3495#if QT_CONFIG(toolbutton)
3496 case CC_ToolButton:
3497 if (const auto *toolbutton = qstyleoption_cast<const QStyleOptionToolButton *>(option)) {
3498 QRect button, menuarea;
3499 button = proxy()->subControlRect(control, toolbutton, SC_ToolButton, widget);
3500 menuarea = proxy()->subControlRect(control, toolbutton, SC_ToolButtonMenu, widget);
3501
3502 State bflags = toolbutton->state & ~State_Sunken;
3503 State mflags = bflags;
3504 bool autoRaise = flags & State_AutoRaise;
3505 if (autoRaise) {
3506 if (!(bflags & State_MouseOver) || !(bflags & State_Enabled))
3507 bflags &= ~State_Raised;
3508 }
3509
3510 if (toolbutton->state & State_Sunken) {
3511 if (toolbutton->activeSubControls & SC_ToolButton) {
3512 bflags |= State_Sunken;
3513 mflags |= State_MouseOver | State_Sunken;
3514 } else if (toolbutton->activeSubControls & SC_ToolButtonMenu) {
3515 mflags |= State_Sunken;
3516 bflags |= State_MouseOver;
3517 }
3518 }
3519
3520 QStyleOption tool = *toolbutton;
3521 if (toolbutton->subControls & SC_ToolButton) {
3522 if (flags & (State_Sunken | State_On | State_Raised) || !autoRaise) {
3523 if (toolbutton->features & QStyleOptionToolButton::MenuButtonPopup && autoRaise) {
3524 QWindowsThemeData theme(widget, painter, QWindowsVistaStylePrivate::ToolBarTheme);
3525 theme.partId = TP_SPLITBUTTON;
3526 theme.rect = button;
3527 if (!(bflags & State_Enabled))
3528 stateId = TS_DISABLED;
3529 else if (bflags & State_Sunken)
3530 stateId = TS_PRESSED;
3531 else if (bflags & State_MouseOver || !(flags & State_AutoRaise))
3532 stateId = flags & State_On ? TS_HOTCHECKED : TS_HOT;
3533 else if (bflags & State_On)
3534 stateId = TS_CHECKED;
3535 else
3536 stateId = TS_NORMAL;
3537 if (option->direction == Qt::RightToLeft)
3538 theme.mirrorHorizontally = true;
3539 theme.stateId = stateId;
3540 d->drawBackground(theme);
3541 } else {
3542 tool.rect = option->rect;
3543 tool.state = bflags;
3544 if (autoRaise) // for tool bars
3545 proxy()->drawPrimitive(PE_PanelButtonTool, &tool, painter, widget);
3546 else
3547 proxy()->drawPrimitive(PE_PanelButtonBevel, &tool, painter, widget);
3548 }
3549 }
3550 }
3551
3552 if (toolbutton->state & State_HasFocus) {
3553 QStyleOptionFocusRect fr;
3554 fr.QStyleOption::operator=(*toolbutton);
3555 fr.rect.adjust(3, 3, -3, -3);
3556 if (toolbutton->features & QStyleOptionToolButton::MenuButtonPopup)
3557 fr.rect.adjust(0, 0, -proxy()->pixelMetric(QStyle::PM_MenuButtonIndicator,
3558 toolbutton, widget), 0);
3559 proxy()->drawPrimitive(PE_FrameFocusRect, &fr, painter, widget);
3560 }
3561 QStyleOptionToolButton label = *toolbutton;
3562 label.state = bflags;
3563 int fw = 2;
3564 if (!autoRaise)
3565 label.state &= ~State_Sunken;
3566 label.rect = button.adjusted(fw, fw, -fw, -fw);
3567 proxy()->drawControl(CE_ToolButtonLabel, &label, painter, widget);
3568
3569 if (toolbutton->subControls & SC_ToolButtonMenu) {
3570 tool.rect = menuarea;
3571 tool.state = mflags;
3572 if (autoRaise) {
3573 proxy()->drawPrimitive(PE_IndicatorButtonDropDown, &tool, painter, widget);
3574 } else {
3575 tool.state = mflags;
3576 menuarea.adjust(-2, 0, 0, 0);
3577 // Draw menu button
3578 if ((bflags & State_Sunken) != (mflags & State_Sunken)){
3579 painter->save();
3580 painter->setClipRect(menuarea);
3581 tool.rect = option->rect;
3582 proxy()->drawPrimitive(PE_PanelButtonBevel, &tool, painter, nullptr);
3583 painter->restore();
3584 }
3585 // Draw arrow
3586 painter->save();
3587 painter->setPen(option->palette.dark().color());
3588 painter->drawLine(menuarea.left(), menuarea.top() + 3,
3589 menuarea.left(), menuarea.bottom() - 3);
3590 painter->setPen(option->palette.light().color());
3591 painter->drawLine(menuarea.left() - 1, menuarea.top() + 3,
3592 menuarea.left() - 1, menuarea.bottom() - 3);
3593
3594 tool.rect = menuarea.adjusted(2, 3, -2, -1);
3595 proxy()->drawPrimitive(PE_IndicatorArrowDown, &tool, painter, widget);
3596 painter->restore();
3597 }
3598 } else if (toolbutton->features & QStyleOptionToolButton::HasMenu) {
3599 int mbi = proxy()->pixelMetric(PM_MenuButtonIndicator, toolbutton, widget);
3600 QRect ir = toolbutton->rect;
3601 QStyleOptionToolButton newBtn = *toolbutton;
3602 newBtn.rect = QRect(ir.right() + 4 - mbi, ir.height() - mbi + 4, mbi - 5, mbi - 5);
3603 proxy()->drawPrimitive(PE_IndicatorArrowDown, &newBtn, painter, widget);
3604 }
3605 }
3606 break;
3607#endif // QT_CONFIG(toolbutton)
3608
3609 case CC_TitleBar:
3610 if (const auto *tb = qstyleoption_cast<const QStyleOptionTitleBar *>(option)) {
3611 const qreal factor = QWindowsStylePrivate::nativeMetricScaleFactor(widget);
3612 bool isActive = tb->titleBarState & QStyle::State_Active;
3613 QWindowsThemeData theme(widget, painter, QWindowsVistaStylePrivate::WindowTheme);
3614 if (sub & SC_TitleBarLabel) {
3615 partId = (tb->titleBarState & Qt::WindowMinimized) ? WP_MINCAPTION : WP_CAPTION;
3616 theme.rect = option->rect;
3617 if (widget && !widget->isEnabled())
3618 stateId = CS_DISABLED;
3619 else if (isActive)
3620 stateId = CS_ACTIVE;
3621 else
3622 stateId = CS_INACTIVE;
3623
3624 theme.partId = partId;
3625 theme.stateId = stateId;
3626 d->drawBackground(theme);
3627
3628 QRect ir = proxy()->subControlRect(CC_TitleBar, tb, SC_TitleBarLabel, widget);
3629
3630 int result = TST_NONE;
3631 GetThemeEnumValue(theme.handle(), WP_CAPTION, isActive ? CS_ACTIVE : CS_INACTIVE, TMT_TEXTSHADOWTYPE, &result);
3632 if (result != TST_NONE) {
3633 COLORREF textShadowRef;
3634 GetThemeColor(theme.handle(), WP_CAPTION, isActive ? CS_ACTIVE : CS_INACTIVE, TMT_TEXTSHADOWCOLOR, &textShadowRef);
3635 QColor textShadow = qRgb(GetRValue(textShadowRef), GetGValue(textShadowRef), GetBValue(textShadowRef));
3636 painter->setPen(textShadow);
3637 painter->drawText(int(ir.x() + 3 * factor), int(ir.y() + 2 * factor),
3638 int(ir.width() - 1 * factor), ir.height(),
3639 Qt::AlignLeft | Qt::AlignVCenter | Qt::TextSingleLine, tb->text);
3640 }
3641 COLORREF captionText = GetSysColor(isActive ? COLOR_CAPTIONTEXT : COLOR_INACTIVECAPTIONTEXT);
3642 QColor textColor = qRgb(GetRValue(captionText), GetGValue(captionText), GetBValue(captionText));
3643 painter->setPen(textColor);
3644 painter->drawText(int(ir.x() + 2 * factor), int(ir.y() + 1 * factor),
3645 int(ir.width() - 2 * factor), ir.height(),
3646 Qt::AlignLeft | Qt::AlignVCenter | Qt::TextSingleLine, tb->text);
3647 }
3648 if (sub & SC_TitleBarSysMenu && tb->titleBarFlags & Qt::WindowSystemMenuHint) {
3649 theme.rect = proxy()->subControlRect(CC_TitleBar, option, SC_TitleBarSysMenu, widget);
3650 partId = WP_SYSBUTTON;
3651 if ((widget && !widget->isEnabled()) || !isActive)
3652 stateId = SBS_DISABLED;
3653 else if (option->activeSubControls == SC_TitleBarSysMenu && (option->state & State_Sunken))
3654 stateId = SBS_PUSHED;
3655 else if (option->activeSubControls == SC_TitleBarSysMenu && (option->state & State_MouseOver))
3656 stateId = SBS_HOT;
3657 else
3658 stateId = SBS_NORMAL;
3659 if (!tb->icon.isNull()) {
3660 tb->icon.paint(painter, theme.rect);
3661 } else {
3662 theme.partId = partId;
3663 theme.stateId = stateId;
3664 if (theme.size().isEmpty()) {
3665 const auto extent = proxy()->pixelMetric(PM_SmallIconSize, tb, widget);
3666 const auto dpr = QStyleHelper::getDpr(widget);
3667 const auto icon = proxy()->standardIcon(SP_TitleBarMenuButton, tb, widget);
3668 const auto pm = icon.pixmap(QSize(extent, extent), dpr);
3669 drawItemPixmap(painter, theme.rect, Qt::AlignCenter, pm);
3670 } else {
3671 d->drawBackground(theme);
3672 }
3673 }
3674 }
3675
3676 if (sub & SC_TitleBarMinButton && tb->titleBarFlags & Qt::WindowMinimizeButtonHint
3677 && !(tb->titleBarState & Qt::WindowMinimized)) {
3678 populateTitleBarButtonTheme(proxy(), widget, option, SC_TitleBarMinButton, isActive, WP_MINBUTTON, &theme);
3679 d->drawBackground(theme);
3680 }
3681 if (sub & SC_TitleBarMaxButton && tb->titleBarFlags & Qt::WindowMaximizeButtonHint
3682 && !(tb->titleBarState & Qt::WindowMaximized)) {
3683 populateTitleBarButtonTheme(proxy(), widget, option, SC_TitleBarMaxButton, isActive, WP_MAXBUTTON, &theme);
3684 d->drawBackground(theme);
3685 }
3686 if (sub & SC_TitleBarContextHelpButton
3687 && tb->titleBarFlags & Qt::WindowContextHelpButtonHint) {
3688 populateTitleBarButtonTheme(proxy(), widget, option, SC_TitleBarContextHelpButton, isActive, WP_HELPBUTTON, &theme);
3689 d->drawBackground(theme);
3690 }
3691 bool drawNormalButton = (sub & SC_TitleBarNormalButton)
3692 && (((tb->titleBarFlags & Qt::WindowMinimizeButtonHint)
3693 && (tb->titleBarState & Qt::WindowMinimized))
3694 || ((tb->titleBarFlags & Qt::WindowMaximizeButtonHint)
3695 && (tb->titleBarState & Qt::WindowMaximized)));
3696 if (drawNormalButton) {
3697 populateTitleBarButtonTheme(proxy(), widget, option, SC_TitleBarNormalButton, isActive, WP_RESTOREBUTTON, &theme);
3698 d->drawBackground(theme);
3699 }
3700 if (sub & SC_TitleBarShadeButton && tb->titleBarFlags & Qt::WindowShadeButtonHint
3701 && !(tb->titleBarState & Qt::WindowMinimized)) {
3702 populateTitleBarButtonTheme(proxy(), widget, option, SC_TitleBarShadeButton, isActive, WP_MINBUTTON, &theme);
3703 d->drawBackground(theme);
3704 }
3705 if (sub & SC_TitleBarUnshadeButton && tb->titleBarFlags & Qt::WindowShadeButtonHint
3706 && tb->titleBarState & Qt::WindowMinimized) {
3707 populateTitleBarButtonTheme(proxy(), widget, option, SC_TitleBarUnshadeButton, isActive, WP_RESTOREBUTTON, &theme);
3708 d->drawBackground(theme);
3709 }
3710 if (sub & SC_TitleBarCloseButton && tb->titleBarFlags & Qt::WindowSystemMenuHint) {
3711 populateTitleBarButtonTheme(proxy(), widget, option, SC_TitleBarCloseButton, isActive, WP_CLOSEBUTTON, &theme);
3712 d->drawBackground(theme);
3713 }
3714 }
3715 break;
3716
3717#if QT_CONFIG(mdiarea)
3718 case CC_MdiControls: {
3719 QWindowsThemeData theme(widget, painter, QWindowsVistaStylePrivate::WindowTheme, WP_MDICLOSEBUTTON, CBS_NORMAL);
3720 if (Q_UNLIKELY(!theme.isValid()))
3721 return;
3722
3723 if (option->subControls.testFlag(SC_MdiCloseButton)) {
3724 populateMdiButtonTheme(proxy(), widget, option, SC_MdiCloseButton, WP_MDICLOSEBUTTON, &theme);
3725 d->drawBackground(theme, mdiButtonCorrectionFactor(theme, widget));
3726 }
3727 if (option->subControls.testFlag(SC_MdiNormalButton)) {
3728 populateMdiButtonTheme(proxy(), widget, option, SC_MdiNormalButton, WP_MDIRESTOREBUTTON, &theme);
3729 d->drawBackground(theme, mdiButtonCorrectionFactor(theme, widget));
3730 }
3731 if (option->subControls.testFlag(QStyle::SC_MdiMinButton)) {
3732 populateMdiButtonTheme(proxy(), widget, option, SC_MdiMinButton, WP_MDIMINBUTTON, &theme);
3733 d->drawBackground(theme, mdiButtonCorrectionFactor(theme, widget));
3734 }
3735 break;
3736 }
3737#endif // QT_CONFIG(mdiarea)
3738
3739#if QT_CONFIG(dial)
3740 case CC_Dial:
3741 if (const auto *dial = qstyleoption_cast<const QStyleOptionSlider *>(option))
3742 QStyleHelper::drawDial(dial, painter);
3743 break;
3744#endif // QT_CONFIG(dial)
3745
3746 case CC_ComboBox:
3747 if (const QStyleOptionComboBox *cmb = qstyleoption_cast<const QStyleOptionComboBox *>(option)) {
3748 if (cmb->editable) {
3749 if (sub & SC_ComboBoxEditField) {
3750 partId = EP_EDITBORDER_NOSCROLL;
3751 if (!(flags & State_Enabled))
3752 stateId = ETS_DISABLED;
3753 else if (flags & State_MouseOver)
3754 stateId = ETS_HOT;
3755 else if (flags & State_HasFocus)
3756 stateId = ETS_FOCUSED;
3757 else
3758 stateId = ETS_NORMAL;
3759
3760 QWindowsThemeData theme(widget, painter,
3761 QWindowsVistaStylePrivate::EditTheme,
3762 partId, stateId, option->rect);
3763
3764 d->drawBackground(theme);
3765 }
3766 if (sub & SC_ComboBoxArrow) {
3767 QRect subRect = proxy()->subControlRect(CC_ComboBox, option, SC_ComboBoxArrow, widget);
3768 QWindowsThemeData theme(widget, painter, QWindowsVistaStylePrivate::ComboboxTheme);
3769 theme.rect = subRect;
3770 partId = option->direction == Qt::RightToLeft ? CP_DROPDOWNBUTTONLEFT : CP_DROPDOWNBUTTONRIGHT;
3771
3772 if (!(cmb->state & State_Enabled))
3773 stateId = CBXS_DISABLED;
3774 else if (cmb->state & State_Sunken || cmb->state & State_On)
3775 stateId = CBXS_PRESSED;
3776 else if (cmb->state & State_MouseOver && option->activeSubControls & SC_ComboBoxArrow)
3777 stateId = CBXS_HOT;
3778 else
3779 stateId = CBXS_NORMAL;
3780
3781 theme.partId = partId;
3782 theme.stateId = stateId;
3783 d->drawBackground(theme);
3784 }
3785
3786 } else {
3787 if (sub & SC_ComboBoxFrame) {
3788 QWindowsThemeData theme(widget, painter, QWindowsVistaStylePrivate::ComboboxTheme);
3789 theme.rect = option->rect;
3790 theme.partId = CP_READONLY;
3791 if (!(cmb->state & State_Enabled))
3792 theme.stateId = CBXS_DISABLED;
3793 else if (cmb->state & State_Sunken || cmb->state & State_On)
3794 theme.stateId = CBXS_PRESSED;
3795 else if (cmb->state & State_MouseOver)
3796 theme.stateId = CBXS_HOT;
3797 else
3798 theme.stateId = CBXS_NORMAL;
3799 d->drawBackground(theme);
3800 }
3801 if (sub & SC_ComboBoxArrow) {
3802 QWindowsThemeData theme(widget, painter, QWindowsVistaStylePrivate::ComboboxTheme);
3803 theme.rect = proxy()->subControlRect(CC_ComboBox, option, SC_ComboBoxArrow, widget);
3804 theme.partId = option->direction == Qt::RightToLeft ? CP_DROPDOWNBUTTONLEFT : CP_DROPDOWNBUTTONRIGHT;
3805 if (!(cmb->state & State_Enabled))
3806 theme.stateId = CBXS_DISABLED;
3807 else
3808 theme.stateId = CBXS_NORMAL;
3809 d->drawBackground(theme);
3810 }
3811 if ((sub & SC_ComboBoxEditField) && (flags & State_HasFocus)) {
3812 QStyleOptionFocusRect fropt;
3813 fropt.QStyleOption::operator=(*cmb);
3814 fropt.rect = proxy()->subControlRect(CC_ComboBox, option, SC_ComboBoxEditField, widget);
3815 proxy()->drawPrimitive(PE_FrameFocusRect, &fropt, painter, widget);
3816 }
3817 }
3818 }
3819 break;
3820
3821 case CC_ScrollBar:
3822 if (const QStyleOptionSlider *scrollbar = qstyleoption_cast<const QStyleOptionSlider *>(option)) {
3823 QWindowsThemeData theme(widget, painter, QWindowsVistaStylePrivate::ScrollBarTheme);
3824 bool maxedOut = (scrollbar->maximum == scrollbar->minimum);
3825 if (maxedOut)
3826 flags &= ~State_Enabled;
3827
3828 bool isHorz = flags & State_Horizontal;
3829 bool isRTL = option->direction == Qt::RightToLeft;
3830 if (sub & SC_ScrollBarAddLine) {
3831 theme.rect = proxy()->subControlRect(CC_ScrollBar, option, SC_ScrollBarAddLine, widget);
3832 partId = SBP_ARROWBTN;
3833 if (!(flags & State_Enabled))
3834 stateId = (isHorz ? (isRTL ? ABS_LEFTDISABLED : ABS_RIGHTDISABLED) : ABS_DOWNDISABLED);
3835 else if (scrollbar->activeSubControls & SC_ScrollBarAddLine && (scrollbar->state & State_Sunken))
3836 stateId = (isHorz ? (isRTL ? ABS_LEFTPRESSED : ABS_RIGHTPRESSED) : ABS_DOWNPRESSED);
3837 else if (scrollbar->activeSubControls & SC_ScrollBarAddLine && (scrollbar->state & State_MouseOver))
3838 stateId = (isHorz ? (isRTL ? ABS_LEFTHOT : ABS_RIGHTHOT) : ABS_DOWNHOT);
3839 else if (scrollbar->state & State_MouseOver)
3840 stateId = (isHorz ? (isRTL ? ABS_LEFTHOVER : ABS_RIGHTHOVER) : ABS_DOWNHOVER);
3841 else
3842 stateId = (isHorz ? (isRTL ? ABS_LEFTNORMAL : ABS_RIGHTNORMAL) : ABS_DOWNNORMAL);
3843 theme.partId = partId;
3844 theme.stateId = stateId;
3845 d->drawBackground(theme);
3846 }
3847 if (sub & SC_ScrollBarSubLine) {
3848 theme.rect = proxy()->subControlRect(CC_ScrollBar, option, SC_ScrollBarSubLine, widget);
3849 partId = SBP_ARROWBTN;
3850 if (!(flags & State_Enabled))
3851 stateId = (isHorz ? (isRTL ? ABS_RIGHTDISABLED : ABS_LEFTDISABLED) : ABS_UPDISABLED);
3852 else if (scrollbar->activeSubControls & SC_ScrollBarSubLine && (scrollbar->state & State_Sunken))
3853 stateId = (isHorz ? (isRTL ? ABS_RIGHTPRESSED : ABS_LEFTPRESSED) : ABS_UPPRESSED);
3854 else if (scrollbar->activeSubControls & SC_ScrollBarSubLine && (scrollbar->state & State_MouseOver))
3855 stateId = (isHorz ? (isRTL ? ABS_RIGHTHOT : ABS_LEFTHOT) : ABS_UPHOT);
3856 else if (scrollbar->state & State_MouseOver)
3857 stateId = (isHorz ? (isRTL ? ABS_RIGHTHOVER : ABS_LEFTHOVER) : ABS_UPHOVER);
3858 else
3859 stateId = (isHorz ? (isRTL ? ABS_RIGHTNORMAL : ABS_LEFTNORMAL) : ABS_UPNORMAL);
3860 theme.partId = partId;
3861 theme.stateId = stateId;
3862 d->drawBackground(theme);
3863 }
3864 if (maxedOut) {
3865 theme.rect = proxy()->subControlRect(CC_ScrollBar, option, SC_ScrollBarSlider, widget);
3866 theme.rect = theme.rect.united(proxy()->subControlRect(CC_ScrollBar, option, SC_ScrollBarSubPage, widget));
3867 theme.rect = theme.rect.united(proxy()->subControlRect(CC_ScrollBar, option, SC_ScrollBarAddPage, widget));
3868 partId = flags & State_Horizontal ? SBP_LOWERTRACKHORZ : SBP_LOWERTRACKVERT;
3869 stateId = SCRBS_DISABLED;
3870 theme.partId = partId;
3871 theme.stateId = stateId;
3872 d->drawBackground(theme);
3873 } else {
3874 if (sub & SC_ScrollBarSubPage) {
3875 theme.rect = proxy()->subControlRect(CC_ScrollBar, option, SC_ScrollBarSubPage, widget);
3876 partId = flags & State_Horizontal ? SBP_UPPERTRACKHORZ : SBP_UPPERTRACKVERT;
3877 if (!(flags & State_Enabled))
3878 stateId = SCRBS_DISABLED;
3879 else if (scrollbar->activeSubControls & SC_ScrollBarSubPage && (scrollbar->state & State_Sunken))
3880 stateId = SCRBS_PRESSED;
3881 else if (scrollbar->activeSubControls & SC_ScrollBarSubPage && (scrollbar->state & State_MouseOver))
3882 stateId = SCRBS_HOT;
3883 else
3884 stateId = SCRBS_NORMAL;
3885 theme.partId = partId;
3886 theme.stateId = stateId;
3887 d->drawBackground(theme);
3888 }
3889 if (sub & SC_ScrollBarAddPage) {
3890 theme.rect = proxy()->subControlRect(CC_ScrollBar, option, SC_ScrollBarAddPage, widget);
3891 partId = flags & State_Horizontal ? SBP_LOWERTRACKHORZ : SBP_LOWERTRACKVERT;
3892 if (!(flags & State_Enabled))
3893 stateId = SCRBS_DISABLED;
3894 else if (scrollbar->activeSubControls & SC_ScrollBarAddPage && (scrollbar->state & State_Sunken))
3895 stateId = SCRBS_PRESSED;
3896 else if (scrollbar->activeSubControls & SC_ScrollBarAddPage && (scrollbar->state & State_MouseOver))
3897 stateId = SCRBS_HOT;
3898 else
3899 stateId = SCRBS_NORMAL;
3900 theme.partId = partId;
3901 theme.stateId = stateId;
3902 d->drawBackground(theme);
3903 }
3904 if (sub & SC_ScrollBarSlider) {
3905 theme.rect = proxy()->subControlRect(CC_ScrollBar, option, SC_ScrollBarSlider, widget);
3906 if (!(flags & State_Enabled))
3907 stateId = SCRBS_DISABLED;
3908 else if (scrollbar->activeSubControls & SC_ScrollBarSlider && (scrollbar->state & State_Sunken))
3909 stateId = SCRBS_PRESSED;
3910 else if (scrollbar->activeSubControls & SC_ScrollBarSlider && (scrollbar->state & State_MouseOver))
3911 stateId = SCRBS_HOT;
3912 else if (option->state & State_MouseOver)
3913 stateId = SCRBS_HOVER;
3914 else
3915 stateId = SCRBS_NORMAL;
3916
3917 // Draw handle
3918 theme.partId = flags & State_Horizontal ? SBP_THUMBBTNHORZ : SBP_THUMBBTNVERT;
3919 theme.stateId = stateId;
3920 d->drawBackground(theme);
3921 }
3922 }
3923 }
3924 break;
3925
3926#if QT_CONFIG(spinbox)
3927 case CC_SpinBox:
3928 if (const QStyleOptionSpinBox *sb = qstyleoption_cast<const QStyleOptionSpinBox *>(option)) {
3929 QWindowsThemeData theme(widget, painter, QWindowsVistaStylePrivate::SpinTheme);
3930 if (sb->frame && (sub & SC_SpinBoxFrame)) {
3931 partId = EP_EDITBORDER_NOSCROLL;
3932 if (!(flags & State_Enabled))
3933 stateId = ETS_DISABLED;
3934 else if (flags & State_MouseOver)
3935 stateId = ETS_HOT;
3936 else if (flags & State_HasFocus)
3937 stateId = ETS_SELECTED;
3938 else
3939 stateId = ETS_NORMAL;
3940
3941 QWindowsThemeData ftheme(widget, painter,
3942 QWindowsVistaStylePrivate::EditTheme,
3943 partId, stateId, option->rect);
3944 // The spinbox in Windows QStyle is drawn with frameless QLineEdit inside it
3945 // That however breaks with QtQuickControls where this results in transparent
3946 // spinbox background, so if there's no "widget" passed (QtQuickControls case),
3947 // let ftheme.noContent be false, which fixes the spinbox rendering in QQC
3948 ftheme.noContent = (widget != nullptr);
3949 d->drawBackground(ftheme);
3950 }
3951 if (sub & SC_SpinBoxUp) {
3952 theme.rect = proxy()->subControlRect(CC_SpinBox, option, SC_SpinBoxUp, widget);
3953 partId = SPNP_UP;
3954 if (!(sb->stepEnabled & QAbstractSpinBox::StepUpEnabled) || !(flags & State_Enabled))
3955 stateId = UPS_DISABLED;
3956 else if (sb->activeSubControls == SC_SpinBoxUp && (sb->state & State_Sunken))
3957 stateId = UPS_PRESSED;
3958 else if (sb->activeSubControls == SC_SpinBoxUp && (sb->state & State_MouseOver))
3959 stateId = UPS_HOT;
3960 else
3961 stateId = UPS_NORMAL;
3962 theme.partId = partId;
3963 theme.stateId = stateId;
3964 d->drawBackground(theme);
3965 }
3966 if (sub & SC_SpinBoxDown) {
3967 theme.rect = proxy()->subControlRect(CC_SpinBox, option, SC_SpinBoxDown, widget);
3968 partId = SPNP_DOWN;
3969 if (!(sb->stepEnabled & QAbstractSpinBox::StepDownEnabled) || !(flags & State_Enabled))
3970 stateId = DNS_DISABLED;
3971 else if (sb->activeSubControls == SC_SpinBoxDown && (sb->state & State_Sunken))
3972 stateId = DNS_PRESSED;
3973 else if (sb->activeSubControls == SC_SpinBoxDown && (sb->state & State_MouseOver))
3974 stateId = DNS_HOT;
3975 else
3976 stateId = DNS_NORMAL;
3977 theme.partId = partId;
3978 theme.stateId = stateId;
3979 d->drawBackground(theme);
3980 }
3981 }
3982 break;
3983#endif // QT_CONFIG(spinbox)
3984
3985 default:
3986 QWindowsStyle::drawComplexControl(control, option, painter, widget);
3987 break;
3988 }
3989}
3990
3991/*!
3992 \internal
3993 */
3994QSize QWindowsVistaStyle::sizeFromContents(ContentsType type, const QStyleOption *option,
3995 const QSize &size, const QWidget *widget) const
3996{
3997 if (!QWindowsVistaStylePrivate::useVista())
3998 return QWindowsStyle::sizeFromContents(type, option, size, widget);
3999
4000 QSize contentSize(size);
4001
4002 switch (type) {
4003 case CT_LineEdit:
4004 case CT_ComboBox: {
4005 QWindowsThemeData buttontheme(widget, nullptr, QWindowsVistaStylePrivate::ButtonTheme, BP_PUSHBUTTON, PBS_NORMAL);
4006 if (buttontheme.isValid()) {
4007 const qreal factor = QWindowsStylePrivate::nativeMetricScaleFactor(widget);
4008 const QMarginsF borderSize = buttontheme.margins() * factor;
4009 if (!borderSize.isNull()) {
4010 const qreal margin = qreal(2) * factor;
4011 contentSize.rwidth() += qRound(borderSize.left() + borderSize.right() - margin);
4012 contentSize.rheight() += int(borderSize.bottom() + borderSize.top() - margin
4013 + qreal(1) / factor - 1);
4014 }
4015 const int textMargins = 2*(proxy()->pixelMetric(PM_FocusFrameHMargin, option) + 1);
4016 contentSize += QSize(qMax(pixelMetric(QStyle::PM_ScrollBarExtent, option, widget)
4017 + textMargins, 23), 0); //arrow button
4018 }
4019 break;
4020 }
4021
4022 case CT_TabWidget:
4023 contentSize += QSize(6, 6);
4024 break;
4025
4026 case CT_Menu:
4027 contentSize += QSize(1, 0);
4028 break;
4029
4030#if QT_CONFIG(menubar)
4031 case CT_MenuBarItem:
4032 if (!contentSize.isEmpty())
4033 contentSize += QSize(windowsItemHMargin * 5 + 1, 5);
4034 break;
4035#endif
4036
4037 case CT_MenuItem: {
4038 contentSize = QWindowsStyle::sizeFromContents(type, option, size, widget);
4039 QWindowsThemeData theme(widget, nullptr,
4040 QWindowsVistaStylePrivate::MenuTheme,
4041 MENU_POPUPCHECKBACKGROUND, MBI_HOT);
4042 QWindowsThemeData themeSize = theme;
4043 themeSize.partId = MENU_POPUPCHECK;
4044 themeSize.stateId = 0;
4045 const QSizeF size = themeSize.size() * QWindowsStylePrivate::nativeMetricScaleFactor(widget);
4046 const QMarginsF margins = themeSize.margins() * QWindowsStylePrivate::nativeMetricScaleFactor(widget);
4047 int minimumHeight = qMax(qRound(size.height() + margins.bottom() + margins.top()), contentSize.height());
4048 contentSize.rwidth() += qRound(size.width() + margins.left() + margins.right());
4049 if (const QStyleOptionMenuItem *menuitem = qstyleoption_cast<const QStyleOptionMenuItem *>(option)) {
4050 if (menuitem->menuItemType != QStyleOptionMenuItem::Separator)
4051 contentSize.setHeight(minimumHeight);
4052 }
4053 break;
4054 }
4055
4056 case CT_MdiControls: {
4057 contentSize.setHeight(int(QStyleHelper::dpiScaled(19, option)));
4058 int width = 54;
4059 if (const auto *styleOpt = qstyleoption_cast<const QStyleOptionComplex *>(option)) {
4060 width = 0;
4061 if (styleOpt->subControls & SC_MdiMinButton)
4062 width += 17 + 1;
4063 if (styleOpt->subControls & SC_MdiNormalButton)
4064 width += 17 + 1;
4065 if (styleOpt->subControls & SC_MdiCloseButton)
4066 width += 17 + 1;
4067 }
4068 contentSize.setWidth(int(QStyleHelper::dpiScaled(width, option)));
4069 break;
4070 }
4071
4072 case CT_ItemViewItem:
4073 contentSize = QWindowsStyle::sizeFromContents(type, option, size, widget);
4074 contentSize.rheight() += 2;
4075 break;
4076
4077 case CT_SpinBox: {
4078 //Spinbox adds frame twice
4079 contentSize = QWindowsStyle::sizeFromContents(type, option, size, widget);
4080 int border = proxy()->pixelMetric(PM_SpinBoxFrameWidth, option, widget);
4081 contentSize -= QSize(2*border, 2*border);
4082 break;
4083 }
4084
4085 case CT_HeaderSection:
4086 // When there is a sort indicator it adds to the width but it is shown
4087 // above the text natively and not on the side
4088 if (QStyleOptionHeader *hdr = qstyleoption_cast<QStyleOptionHeader *>(const_cast<QStyleOption *>(option))) {
4089 QStyleOptionHeader::SortIndicator sortInd = hdr->sortIndicator;
4090 hdr->sortIndicator = QStyleOptionHeader::None;
4091 contentSize = QWindowsStyle::sizeFromContents(type, hdr, size, widget);
4092 hdr->sortIndicator = sortInd;
4093 }
4094 break;
4095
4096 default:
4097 contentSize = QWindowsStyle::sizeFromContents(type, option, size, widget);
4098 break;
4099 }
4100
4101 return contentSize;
4102}
4103
4104/*!
4105 \internal
4106 */
4107QRect QWindowsVistaStyle::subElementRect(SubElement element, const QStyleOption *option, const QWidget *widget) const
4108{
4109 if (!QWindowsVistaStylePrivate::useVista())
4110 return QWindowsStyle::subElementRect(element, option, widget);
4111
4112 QRect rect(option->rect);
4113
4114 switch (element) {
4115 case SE_PushButtonContents:
4116 if (const QStyleOptionButton *btn = qstyleoption_cast<const QStyleOptionButton *>(option)) {
4117 MARGINS borderSize;
4118 const HTHEME theme = OpenThemeData(widget ? QWindowsVistaStylePrivate::winId(widget) : nullptr, L"Button");
4119 if (theme) {
4120 int stateId = PBS_NORMAL;
4121 if (!(option->state & State_Enabled))
4122 stateId = PBS_DISABLED;
4123 else if (option->state & State_Sunken)
4124 stateId = PBS_PRESSED;
4125 else if (option->state & State_MouseOver)
4126 stateId = PBS_HOT;
4127 else if (btn->features & QStyleOptionButton::DefaultButton)
4128 stateId = PBS_DEFAULTED;
4129
4130 int border = proxy()->pixelMetric(PM_DefaultFrameWidth, btn, widget);
4131 rect = option->rect.adjusted(border, border, -border, -border);
4132
4133 if (SUCCEEDED(GetThemeMargins(theme, nullptr, BP_PUSHBUTTON, stateId, TMT_CONTENTMARGINS, nullptr, &borderSize))) {
4134 rect.adjust(borderSize.cxLeftWidth, borderSize.cyTopHeight,
4135 -borderSize.cxRightWidth, -borderSize.cyBottomHeight);
4136 rect = visualRect(option->direction, option->rect, rect);
4137 }
4138 }
4139 }
4140 break;
4141
4142 case SE_DockWidgetCloseButton:
4143 case SE_DockWidgetFloatButton:
4144 rect = QWindowsStyle::subElementRect(element, option, widget);
4145 return rect.translated(0, 1);
4146
4147 case SE_TabWidgetTabContents:
4148 rect = QWindowsStyle::subElementRect(element, option, widget);
4149#if QT_CONFIG(tabwidget)
4150 if (qstyleoption_cast<const QStyleOptionTabWidgetFrame *>(option)) {
4151 rect = QWindowsStyle::subElementRect(element, option, widget);
4152 if (const QTabWidget *tabWidget = qobject_cast<const QTabWidget *>(widget)) {
4153 if (tabWidget->documentMode())
4154 break;
4155 rect.adjust(0, 0, -2, -2);
4156 }
4157 }
4158#endif // QT_CONFIG(tabwidget)
4159 break;
4160
4161 case SE_TabWidgetTabBar: {
4162 rect = QWindowsStyle::subElementRect(element, option, widget);
4163#if QT_CONFIG(tabwidget)
4164 const auto *twfOption = qstyleoption_cast<const QStyleOptionTabWidgetFrame *>(option);
4165 if (twfOption && twfOption->direction == Qt::RightToLeft
4166 && (twfOption->shape == QTabBar::RoundedNorth
4167 || twfOption->shape == QTabBar::RoundedSouth))
4168 {
4169 QStyleOptionTab otherOption;
4170 otherOption.shape = (twfOption->shape == QTabBar::RoundedNorth
4171 ? QTabBar::RoundedEast : QTabBar::RoundedSouth);
4172 int overlap = proxy()->pixelMetric(PM_TabBarBaseOverlap, &otherOption, widget);
4173 int borderThickness = proxy()->pixelMetric(PM_DefaultFrameWidth, option, widget);
4174 rect.adjust(-overlap + borderThickness, 0, -overlap + borderThickness, 0);
4175 }
4176#endif // QT_CONFIG(tabwidget)
4177 break;
4178 }
4179
4180 case SE_HeaderArrow: {
4181 rect = QWindowsStyle::subElementRect(element, option, widget);
4182 QRect r = rect;
4183 int h = option->rect.height();
4184 int w = option->rect.width();
4185 int x = option->rect.x();
4186 int y = option->rect.y();
4187 int margin = proxy()->pixelMetric(QStyle::PM_HeaderMargin, option, widget);
4188
4189 QWindowsThemeData theme(widget, nullptr,
4190 QWindowsVistaStylePrivate::HeaderTheme,
4191 HP_HEADERSORTARROW, HSAS_SORTEDDOWN, option->rect);
4192
4193 int arrowWidth = 13;
4194 int arrowHeight = 5;
4195 if (theme.isValid()) {
4196 const QSizeF size = theme.size() * QWindowsStylePrivate::nativeMetricScaleFactor(widget);
4197 if (!size.isEmpty()) {
4198 arrowWidth = qRound(size.width());
4199 arrowHeight = qRound(size.height());
4200 }
4201 }
4202 if (option->state & State_Horizontal) {
4203 r.setRect(x + w/2 - arrowWidth/2, y , arrowWidth, arrowHeight);
4204 } else {
4205 int vert_size = w / 2;
4206 r.setRect(x + 5, y + h - margin * 2 - vert_size,
4207 w - margin * 2 - 5, vert_size);
4208 }
4209 rect = visualRect(option->direction, option->rect, r);
4210 break;
4211 }
4212
4213 case SE_HeaderLabel: {
4214 rect = QWindowsStyle::subElementRect(element, option, widget);
4215 int margin = proxy()->pixelMetric(QStyle::PM_HeaderMargin, option, widget);
4216 QRect r = option->rect;
4217 r.setRect(option->rect.x() + margin, option->rect.y() + margin,
4218 option->rect.width() - margin * 2, option->rect.height() - margin * 2);
4219 if (const QStyleOptionHeader *header = qstyleoption_cast<const QStyleOptionHeader *>(option)) {
4220 // Subtract width needed for arrow, if there is one
4221 if (header->sortIndicator != QStyleOptionHeader::None) {
4222 if (!(option->state & State_Horizontal)) //horizontal arrows are positioned on top
4223 r.setHeight(r.height() - (option->rect.width() / 2) - (margin * 2));
4224 }
4225 }
4226 rect = visualRect(option->direction, option->rect, r);
4227 break;
4228 }
4229
4230 case SE_ProgressBarContents:
4231 rect = QCommonStyle::subElementRect(SE_ProgressBarGroove, option, widget);
4232 break;
4233
4234 case SE_ItemViewItemFocusRect:
4235 rect = QWindowsStyle::subElementRect(element, option, widget);
4236 if (const QStyleOptionViewItem *vopt = qstyleoption_cast<const QStyleOptionViewItem *>(option)) {
4237 QRect textRect = subElementRect(QStyle::SE_ItemViewItemText, option, widget);
4238 QRect displayRect = subElementRect(QStyle::SE_ItemViewItemDecoration, option, widget);
4239 if (!vopt->icon.isNull())
4240 rect = textRect.united(displayRect);
4241 else
4242 rect = textRect;
4243 rect = rect.adjusted(1, 0, -1, 0);
4244 }
4245 break;
4246
4247 default:
4248 rect = QWindowsStyle::subElementRect(element, option, widget);
4249 break;
4250 }
4251
4252 return rect;
4253}
4254
4255/*!
4256 \internal
4257 */
4258QRect QWindowsVistaStyle::subControlRect(ComplexControl control, const QStyleOptionComplex *option,
4259 SubControl subControl, const QWidget *widget) const
4260{
4261 if (!QWindowsVistaStylePrivate::useVista())
4262 return QWindowsStyle::subControlRect(control, option, subControl, widget);
4263
4264 QRect rect;
4265
4266 switch (control) {
4267#if QT_CONFIG(combobox)
4268 case CC_ComboBox:
4269 if (const auto *cb = qstyleoption_cast<const QStyleOptionComboBox *>(option)) {
4270 const int x = cb->rect.x(), y = cb->rect.y(), wi = cb->rect.width(), he = cb->rect.height();
4271 const int margin = cb->frame ? 3 : 0;
4272 const int bmarg = cb->frame ? 2 : 0;
4273 const int arrowWidth = qRound(QStyleHelper::dpiScaled(16, option));
4274 const int arrowButtonWidth = bmarg + arrowWidth;
4275 const int xpos = x + wi - arrowButtonWidth;
4276
4277 switch (subControl) {
4278 case SC_ComboBoxFrame:
4279 case SC_ComboBoxListBoxPopup:
4280 rect = cb->rect;
4281 break;
4282
4283 case SC_ComboBoxArrow: {
4284 rect.setRect(xpos, y , arrowButtonWidth, he);
4285 }
4286 break;
4287
4288 case SC_ComboBoxEditField: {
4289 rect.setRect(x + margin, y + margin, wi - 2 * margin - arrowWidth, he - 2 * margin);
4290 }
4291 break;
4292
4293 default:
4294 break;
4295 }
4296 }
4297 break;
4298#endif // QT_CONFIG(combobox)
4299
4300 case CC_TitleBar:
4301 if (const QStyleOptionTitleBar *tb = qstyleoption_cast<const QStyleOptionTitleBar *>(option)) {
4302 if (!buttonVisible(subControl, tb))
4303 return rect;
4304 const qreal factor = QWindowsStylePrivate::nativeMetricScaleFactor(widget);
4305 const bool isToolTitle = false;
4306 const int height = tb->rect.height();
4307 const int width = tb->rect.width();
4308
4309 const int buttonMargin = int(QStyleHelper::dpiScaled(4, option));
4310 int buttonHeight = qRound(qreal(GetSystemMetrics(SM_CYSIZE)) * factor)
4311 - buttonMargin;
4312 const int buttonWidth =
4313 qRound(qreal(GetSystemMetrics(SM_CXSIZE)) * factor - QStyleHelper::dpiScaled(4, option));
4314
4315 const int frameWidth = proxy()->pixelMetric(PM_MdiSubWindowFrameWidth, option, widget);
4316 const bool sysmenuHint = (tb->titleBarFlags & Qt::WindowSystemMenuHint) != 0;
4317 const bool minimizeHint = (tb->titleBarFlags & Qt::WindowMinimizeButtonHint) != 0;
4318 const bool maximizeHint = (tb->titleBarFlags & Qt::WindowMaximizeButtonHint) != 0;
4319 const bool contextHint = (tb->titleBarFlags & Qt::WindowContextHelpButtonHint) != 0;
4320 const bool shadeHint = (tb->titleBarFlags & Qt::WindowShadeButtonHint) != 0;
4321
4322 bool isMinimized = tb->titleBarState & Qt::WindowMinimized;
4323 bool isMaximized = tb->titleBarState & Qt::WindowMaximized;
4324 int offset = 0;
4325 const int delta = buttonWidth + 2;
4326 int controlTop = option->rect.bottom() - buttonHeight - 2;
4327
4328 switch (subControl) {
4329 case SC_TitleBarLabel: {
4330 rect = QRect(frameWidth, 0, width - (buttonWidth + frameWidth + 10), height);
4331 if (isToolTitle) {
4332 if (sysmenuHint) {
4333 rect.adjust(0, 0, int(-buttonWidth - 3 * factor), 0);
4334 }
4335 if (minimizeHint || maximizeHint)
4336 rect.adjust(0, 0, int(-buttonWidth - 2 * factor), 0);
4337 } else {
4338 if (sysmenuHint) {
4339 const int leftOffset = int(height - 8 * factor);
4340 rect.adjust(leftOffset, 0, 0, int(4 * factor));
4341 }
4342 if (minimizeHint)
4343 rect.adjust(0, 0, int(-buttonWidth - 2 * factor), 0);
4344 if (maximizeHint)
4345 rect.adjust(0, 0, int(-buttonWidth - 2 * factor), 0);
4346 if (contextHint)
4347 rect.adjust(0, 0, int(-buttonWidth - 2 * factor), 0);
4348 if (shadeHint)
4349 rect.adjust(0, 0, int(-buttonWidth - 2 * factor), 0);
4350 }
4351 rect.translate(0, int(2 * factor));
4352 }
4353 break;
4354
4355 case SC_TitleBarContextHelpButton:
4356 if (tb->titleBarFlags & Qt::WindowContextHelpButtonHint)
4357 offset += delta;
4358 Q_FALLTHROUGH();
4359 case SC_TitleBarMinButton:
4360 if (!isMinimized && (tb->titleBarFlags & Qt::WindowMinimizeButtonHint))
4361 offset += delta;
4362 else if (subControl == SC_TitleBarMinButton)
4363 break;
4364 Q_FALLTHROUGH();
4365 case SC_TitleBarNormalButton:
4366 if (isMinimized && (tb->titleBarFlags & Qt::WindowMinimizeButtonHint))
4367 offset += delta;
4368 else if (isMaximized && (tb->titleBarFlags & Qt::WindowMaximizeButtonHint))
4369 offset += delta;
4370 else if (subControl == SC_TitleBarNormalButton)
4371 break;
4372 Q_FALLTHROUGH();
4373 case SC_TitleBarMaxButton:
4374 if (!isMaximized && (tb->titleBarFlags & Qt::WindowMaximizeButtonHint))
4375 offset += delta;
4376 else if (subControl == SC_TitleBarMaxButton)
4377 break;
4378 Q_FALLTHROUGH();
4379 case SC_TitleBarShadeButton:
4380 if (!isMinimized && (tb->titleBarFlags & Qt::WindowShadeButtonHint))
4381 offset += delta;
4382 else if (subControl == SC_TitleBarShadeButton)
4383 break;
4384 Q_FALLTHROUGH();
4385 case SC_TitleBarUnshadeButton:
4386 if (isMinimized && (tb->titleBarFlags & Qt::WindowShadeButtonHint))
4387 offset += delta;
4388 else if (subControl == SC_TitleBarUnshadeButton)
4389 break;
4390 Q_FALLTHROUGH();
4391 case SC_TitleBarCloseButton:
4392 if (tb->titleBarFlags & Qt::WindowSystemMenuHint)
4393 offset += delta;
4394 else if (subControl == SC_TitleBarCloseButton)
4395 break;
4396
4397 rect.setRect(width - offset - controlTop + 1, controlTop,
4398 buttonWidth, buttonHeight);
4399 break;
4400
4401 case SC_TitleBarSysMenu: {
4402 const int controlTop = int(6 * factor);
4403 const int controlHeight = int(height - controlTop - 3 * factor);
4404 int iconExtent = proxy()->pixelMetric(PM_SmallIconSize, option);
4405 QSize iconSize = tb->icon.actualSize(QSize(iconExtent, iconExtent));
4406 if (tb->icon.isNull())
4407 iconSize = QSize(controlHeight, controlHeight);
4408 int hPad = (controlHeight - iconSize.height())/2;
4409 int vPad = (controlHeight - iconSize.width())/2;
4410 rect = QRect(frameWidth + hPad, controlTop + vPad, iconSize.width(), iconSize.height());
4411 rect.translate(0, int(3 * factor));
4412 }
4413 break;
4414
4415 default:
4416 break;
4417 }
4418 }
4419 break;
4420
4421#if QT_CONFIG(mdiarea)
4422 case CC_MdiControls: {
4423 int numSubControls = 0;
4424 if (option->subControls & SC_MdiCloseButton)
4425 ++numSubControls;
4426 if (option->subControls & SC_MdiMinButton)
4427 ++numSubControls;
4428 if (option->subControls & SC_MdiNormalButton)
4429 ++numSubControls;
4430 if (numSubControls == 0)
4431 break;
4432
4433 int buttonWidth = option->rect.width() / numSubControls;
4434 int offset = 0;
4435
4436 switch (subControl) {
4437 case SC_MdiCloseButton:
4438 // Only one sub control, no offset needed.
4439 if (numSubControls == 1)
4440 break;
4441 offset += buttonWidth;
4442 Q_FALLTHROUGH();
4443 case SC_MdiNormalButton:
4444 // No offset needed if
4445 // 1) There's only one sub control
4446 // 2) We have a close button and a normal button (offset already added in SC_MdiClose)
4447 if (numSubControls == 1 || (numSubControls == 2 && !(option->subControls & SC_MdiMinButton)))
4448 break;
4449 if (option->subControls & SC_MdiNormalButton)
4450 offset += buttonWidth;
4451 break;
4452 default:
4453 break;
4454 }
4455
4456 rect = QRect(offset, 0, buttonWidth, option->rect.height());
4457 break;
4458 }
4459#endif // QT_CONFIG(mdiarea)
4460
4461 default:
4462 return QWindowsStyle::subControlRect(control, option, subControl, widget);
4463 }
4464
4465 return visualRect(option->direction, option->rect, rect);
4466}
4467
4468/*!
4469 \internal
4470 */
4471QStyle::SubControl QWindowsVistaStyle::hitTestComplexControl(ComplexControl control, const QStyleOptionComplex *option,
4472 const QPoint &pos, const QWidget *widget) const
4473{
4474 return QWindowsStyle::hitTestComplexControl(control, option, pos, widget);
4475}
4476
4477/*!
4478 \internal
4479 */
4480int QWindowsVistaStyle::pixelMetric(PixelMetric metric, const QStyleOption *option, const QWidget *widget) const
4481{
4482 if (!QWindowsVistaStylePrivate::useVista())
4483 return QWindowsStyle::pixelMetric(metric, option, widget);
4484
4485 int ret = QWindowsVistaStylePrivate::fixedPixelMetric(metric);
4486 if (ret != QWindowsStylePrivate::InvalidMetric)
4487 return int(QStyleHelper::dpiScaled(ret, option));
4488
4489 int res = QWindowsVistaStylePrivate::pixelMetricFromSystemDp(metric, option, widget);
4490 if (res != QWindowsStylePrivate::InvalidMetric)
4491 return qRound(qreal(res) * QWindowsStylePrivate::nativeMetricScaleFactor(widget));
4492
4493 res = 0;
4494
4495 switch (metric) {
4496 case PM_MenuBarPanelWidth:
4497 case PM_ButtonDefaultIndicator:
4498 res = 0;
4499 break;
4500
4501 case PM_DefaultFrameWidth:
4502 res = qobject_cast<const QListView*>(widget) ? 2 : 1;
4503 break;
4504 case PM_MenuPanelWidth:
4505 case PM_SpinBoxFrameWidth:
4506 res = 1;
4507 break;
4508
4509 case PM_TabBarTabOverlap:
4510 case PM_MenuHMargin:
4511 case PM_MenuVMargin:
4512 res = 2;
4513 break;
4514
4515#if QT_CONFIG(tabbar)
4516 case PM_TabBarBaseOverlap:
4517 if (const auto *tab = qstyleoption_cast<const QStyleOptionTab *>(option)) {
4518 switch (tab->shape) {
4519 case QTabBar::RoundedNorth:
4520 case QTabBar::TriangularNorth:
4521 case QTabBar::RoundedWest:
4522 case QTabBar::TriangularWest:
4523 res = 1;
4524 break;
4525 case QTabBar::RoundedSouth:
4526 case QTabBar::TriangularSouth:
4527 res = 2;
4528 break;
4529 case QTabBar::RoundedEast:
4530 case QTabBar::TriangularEast:
4531 res = 3;
4532 break;
4533 }
4534 }
4535 break;
4536#endif // QT_CONFIG(tabbar)
4537
4538 case PM_SplitterWidth:
4539 res = QStyleHelper::dpiScaled(5., option);
4540 break;
4541
4542 case PM_MdiSubWindowMinimizedWidth:
4543 res = 160;
4544 break;
4545
4546#if QT_CONFIG(toolbar)
4547 case PM_ToolBarHandleExtent:
4548 res = int(QStyleHelper::dpiScaled(8., option));
4549 break;
4550
4551#endif // QT_CONFIG(toolbar)
4552 case PM_DockWidgetSeparatorExtent:
4553 case PM_DockWidgetTitleMargin:
4554 res = int(QStyleHelper::dpiScaled(4., option));
4555 break;
4556
4557#if QT_CONFIG(toolbutton)
4558 case PM_ButtonShiftHorizontal:
4559 case PM_ButtonShiftVertical:
4560 res = qstyleoption_cast<const QStyleOptionToolButton *>(option) ? 1 : 0;
4561 break;
4562#endif // QT_CONFIG(toolbutton)
4563
4564 default:
4565 res = QWindowsStyle::pixelMetric(metric, option, widget);
4566 }
4567
4568 return res;
4569}
4570
4571/*!
4572 \internal
4573 */
4574void QWindowsVistaStyle::polish(QWidget *widget)
4575{
4576 QWindowsStyle::polish(widget);
4577 if (false
4578#if QT_CONFIG(abstractbutton)
4579 || qobject_cast<QAbstractButton*>(widget)
4580#endif // QT_CONFIG(abstractbutton)
4581#if QT_CONFIG(toolbutton)
4582 || qobject_cast<QToolButton*>(widget)
4583#endif // QT_CONFIG(toolbutton)
4584#if QT_CONFIG(tabbar)
4585 || qobject_cast<QTabBar*>(widget)
4586#endif // QT_CONFIG(tabbar)
4587#if QT_CONFIG(combobox)
4588 || qobject_cast<QComboBox*>(widget)
4589#endif // QT_CONFIG(combobox)
4590 || qobject_cast<QScrollBar*>(widget)
4591 || qobject_cast<QSlider*>(widget)
4592 || qobject_cast<QHeaderView*>(widget)
4593#if QT_CONFIG(spinbox)
4594 || qobject_cast<QAbstractSpinBox*>(widget)
4595 || qobject_cast<QSpinBox*>(widget)
4596#endif // QT_CONFIG(spinbox)
4597#if QT_CONFIG(lineedit)
4598 || qobject_cast<QLineEdit*>(widget)
4599#endif // QT_CONFIG(lineedit)
4600 || qobject_cast<QGroupBox*>(widget)
4601 ) {
4602 widget->setAttribute(Qt::WA_Hover);
4603 } else if (QTreeView *tree = qobject_cast<QTreeView *> (widget)) {
4604 tree->viewport()->setAttribute(Qt::WA_Hover);
4605 } else if (QListView *list = qobject_cast<QListView *> (widget)) {
4606 list->viewport()->setAttribute(Qt::WA_Hover);
4607 }
4608
4609 if (!QWindowsVistaStylePrivate::useVista())
4610 return;
4611
4612#if QT_CONFIG(rubberband)
4613 if (qobject_cast<QRubberBand*>(widget))
4614 widget->setWindowOpacity(0.6);
4615 else
4616#endif
4617#if QT_CONFIG(commandlinkbutton)
4618 if (qobject_cast<QCommandLinkButton*>(widget)) {
4619 widget->setProperty("_qt_usingVistaStyle", true);
4620 QFont buttonFont = widget->font();
4621 buttonFont.setFamilies(QStringList{QLatin1String("Segoe UI")});
4622 widget->setFont(buttonFont);
4623 QPalette pal = widget->palette();
4624 pal.setColor(QPalette::Active, QPalette::ButtonText, QColor(21, 28, 85));
4625 pal.setColor(QPalette::Active, QPalette::BrightText, QColor(7, 64, 229));
4626 widget->setPalette(pal);
4627 } else
4628#endif // QT_CONFIG(commandlinkbutton)
4629 if (qobject_cast<const QTipLabel *>(widget)) {
4630 //note that since tooltips are not reused
4631 //we do not have to care about unpolishing
4632 widget->setContentsMargins(3, 0, 4, 0);
4633 COLORREF bgRef;
4634 HTHEME theme = OpenThemeData(widget ? QWindowsVistaStylePrivate::winId(widget) : nullptr, L"TOOLTIP");
4635 if (theme && SUCCEEDED(GetThemeColor(theme, TTP_STANDARD, TTSS_NORMAL, TMT_TEXTCOLOR, &bgRef))) {
4636 QColor textColor = QColor::fromRgb(bgRef);
4637 QPalette pal;
4638 pal.setColor(QPalette::All, QPalette::ToolTipText, textColor);
4639 pal.setResolveMask(0);
4640 widget->setPalette(pal);
4641 }
4642 } else if (qobject_cast<QMessageBox *> (widget)) {
4643 widget->setAttribute(Qt::WA_StyledBackground);
4644#if QT_CONFIG(dialogbuttonbox)
4645 QDialogButtonBox *buttonBox = widget->findChild<QDialogButtonBox *>(QLatin1String("qt_msgbox_buttonbox"));
4646 if (buttonBox)
4647 buttonBox->setContentsMargins(0, 9, 0, 0);
4648#endif
4649 }
4650}
4651
4652/*!
4653 \internal
4654 */
4655void QWindowsVistaStyle::unpolish(QWidget *widget)
4656{
4657 Q_D(QWindowsVistaStyle);
4658
4659#if QT_CONFIG(rubberband)
4660 if (qobject_cast<QRubberBand*>(widget))
4661 widget->setWindowOpacity(1.0);
4662#endif
4663
4664 // Unpolish of widgets is the first thing that
4665 // happens when a theme changes, or the theme
4666 // engine is turned off. So we detect it here.
4668 bool newState = QWindowsVistaStylePrivate::useVista(true);
4669 if ((oldState != newState) && newState) {
4670 d->cleanup(true);
4671 d->init(true);
4672 } else {
4673 // Cleanup handle map, if just changing style,
4674 // or turning it on. In both cases the values
4675 // already in the map might be old (other style).
4676 d->cleanupHandleMap();
4677 }
4678 if (false
4679 #if QT_CONFIG(abstractbutton)
4680 || qobject_cast<QAbstractButton*>(widget)
4681 #endif
4682 #if QT_CONFIG(toolbutton)
4683 || qobject_cast<QToolButton*>(widget)
4684 #endif // QT_CONFIG(toolbutton)
4685 #if QT_CONFIG(tabbar)
4686 || qobject_cast<QTabBar*>(widget)
4687 #endif // QT_CONFIG(tabbar)
4688 #if QT_CONFIG(combobox)
4689 || qobject_cast<QComboBox*>(widget)
4690 #endif // QT_CONFIG(combobox)
4691 || qobject_cast<QScrollBar*>(widget)
4692 || qobject_cast<QSlider*>(widget)
4693 || qobject_cast<QHeaderView*>(widget)
4694 #if QT_CONFIG(spinbox)
4695 || qobject_cast<QAbstractSpinBox*>(widget)
4696 || qobject_cast<QSpinBox*>(widget)
4697 #endif // QT_CONFIG(spinbox)
4698 ) {
4699 widget->setAttribute(Qt::WA_Hover, false);
4700 }
4701
4702 QWindowsStyle::unpolish(widget);
4703
4704 d->stopAnimation(widget);
4705
4706#if QT_CONFIG(lineedit)
4707 if (qobject_cast<QLineEdit*>(widget))
4708 widget->setAttribute(Qt::WA_Hover, false);
4709 else {
4710#endif // QT_CONFIG(lineedit)
4711 if (qobject_cast<QGroupBox*>(widget))
4712 widget->setAttribute(Qt::WA_Hover, false);
4713 else if (qobject_cast<QMessageBox *> (widget)) {
4714 widget->setAttribute(Qt::WA_StyledBackground, false);
4715#if QT_CONFIG(dialogbuttonbox)
4716 QDialogButtonBox *buttonBox = widget->findChild<QDialogButtonBox *>(QLatin1String("qt_msgbox_buttonbox"));
4717 if (buttonBox)
4718 buttonBox->setContentsMargins(0, 0, 0, 0);
4719#endif
4720 }
4721 else if (QTreeView *tree = qobject_cast<QTreeView *> (widget)) {
4722 tree->viewport()->setAttribute(Qt::WA_Hover, false);
4723 }
4724#if QT_CONFIG(commandlinkbutton)
4725 else if (qobject_cast<QCommandLinkButton*>(widget)) {
4726 QFont font = QApplication::font("QCommandLinkButton");
4727 QFont widgetFont = widget->font();
4728 widgetFont.setFamilies(font.families()); //Only family set by polish
4729 widget->setFont(widgetFont);
4730 }
4731#endif // QT_CONFIG(commandlinkbutton)
4732 }
4733}
4734
4735/*!
4736 \internal
4737 */
4738void QWindowsVistaStyle::polish(QPalette &pal)
4739{
4740 Q_D(QWindowsVistaStyle);
4741
4742 if (QGuiApplication::styleHints()->colorScheme() == Qt::ColorScheme::Dark) {
4743 // System runs in dark mode, but the Vista style cannot use a dark palette.
4744 // Overwrite with the light system palette.
4745 using QWindowsApplication = QNativeInterface::Private::QWindowsApplication;
4746 if (auto nativeWindowsApp = dynamic_cast<QWindowsApplication *>(QGuiApplicationPrivate::platformIntegration()))
4747 nativeWindowsApp->populateLightSystemPalette(pal);
4748 }
4749
4750 QPixmapCache::clear();
4751 d->alphaCache.clear();
4752
4753 if (!d->hasInitColors) {
4754 // Get text color for group box labels
4755 QWindowsThemeData theme(nullptr, nullptr, QWindowsVistaStylePrivate::ButtonTheme, 0, 0);
4756 COLORREF cref;
4757 GetThemeColor(theme.handle(), BP_GROUPBOX, GBS_NORMAL, TMT_TEXTCOLOR, &cref);
4758 d->groupBoxTextColor = qRgb(GetRValue(cref), GetGValue(cref), GetBValue(cref));
4759 GetThemeColor(theme.handle(), BP_GROUPBOX, GBS_DISABLED, TMT_TEXTCOLOR, &cref);
4760 d->groupBoxTextColorDisabled = qRgb(GetRValue(cref), GetGValue(cref), GetBValue(cref));
4761 //Work around Windows API returning the same color for enabled and disabled group boxes
4762 if (d->groupBoxTextColor == d->groupBoxTextColorDisabled)
4763 d->groupBoxTextColorDisabled = pal.color(QPalette::Disabled, QPalette::ButtonText).rgb();
4764 // Where does this color come from?
4765 //GetThemeColor(theme.handle(), TKP_TICS, TSS_NORMAL, TMT_COLOR, &cref);
4766 d->sliderTickColor = qRgb(165, 162, 148);
4767 d->hasInitColors = true;
4768 }
4769
4770 QWindowsStyle::polish(pal);
4771 pal.setBrush(QPalette::AlternateBase, pal.base().color().darker(104));
4772}
4773
4774/*!
4775 \internal
4776 */
4777void QWindowsVistaStyle::polish(QApplication *app)
4778{
4779 // Override windows theme palettes to light
4780 if (qApp->styleHints()->colorScheme() == Qt::ColorScheme::Dark) {
4781 static constexpr const char* themedWidgets[] = {
4782 "QToolButton",
4783 "QAbstractButton",
4784 "QCheckBox",
4785 "QRadioButton",
4786 "QHeaderView",
4787 "QAbstractItemView",
4788 "QMessageBoxLabel",
4789 "QTabBar",
4790 "QLabel",
4791 "QGroupBox",
4792 "QMenu",
4793 "QMenuBar",
4794 "QTextEdit",
4795 "QTextControl",
4796 "QLineEdit"
4797 };
4798 for (const auto& themedWidget : std::as_const(themedWidgets)) {
4799 auto defaultResolveMask = QApplication::palette().resolveMask();
4800 auto widgetResolveMask = QApplication::palette(themedWidget).resolveMask();
4801 if (widgetResolveMask != defaultResolveMask)
4802 QApplication::setPalette(QApplication::palette(), themedWidget);
4803 }
4804 }
4805
4806 QWindowsStyle::polish(app);
4807}
4808
4809void QWindowsVistaStyle::unpolish(QApplication *app)
4810{
4811 QWindowsStyle::unpolish(app);
4812 Q_D(QWindowsVistaStyle);
4813 d->hasInitColors = false;
4814}
4815
4816/*!
4817 \internal
4818 */
4819QPixmap QWindowsVistaStyle::standardPixmap(StandardPixmap standardPixmap, const QStyleOption *option,
4820 const QWidget *widget) const
4821{
4823 return QWindowsStyle::standardPixmap(standardPixmap, option, widget);
4824 }
4825
4826 switch (standardPixmap) {
4827 case SP_TitleBarMinButton:
4828 case SP_TitleBarMaxButton:
4829 case SP_TitleBarCloseButton:
4830 case SP_TitleBarNormalButton: {
4831 QWindowsThemeData theme(widget, nullptr, QWindowsVistaStylePrivate::WindowTheme, WP_SMALLCLOSEBUTTON, CBS_NORMAL);
4832 if (theme.isValid()) {
4833 const QSize size = (theme.size() * QWindowsStylePrivate::nativeMetricScaleFactor(widget)).toSize();
4834 return standardIcon(standardPixmap, option, widget).pixmap(size);
4835 }
4836 break;
4837 }
4838
4839 default:
4840 break;
4841 }
4842
4843 return QWindowsStyle::standardPixmap(standardPixmap, option, widget);
4844}
4845
4846/*!
4847\reimp
4848*/
4849QIcon QWindowsVistaStyle::standardIcon(StandardPixmap standardIcon,
4850 const QStyleOption *option,
4851 const QWidget *widget) const
4852{
4854 return QWindowsStyle::standardIcon(standardIcon, option, widget);
4855 }
4856
4857 auto *d = const_cast<QWindowsVistaStylePrivate*>(d_func());
4858
4859 switch (standardIcon) {
4860 case SP_TitleBarMinButton:
4861 if (d->m_titleBarMinIcon.isNull())
4862 d->m_titleBarMinIcon = QIcon(new WinFontIconEngine(QChar(0xE921), d->m_assetFont));
4863 return d->m_titleBarMinIcon;
4864
4865 case SP_TitleBarMaxButton:
4866 if (d->m_titleBarMaxIcon.isNull())
4867 d->m_titleBarMaxIcon = QIcon(new WinFontIconEngine(QChar(0xE922), d->m_assetFont));
4868 return d->m_titleBarMaxIcon;
4869
4870 case SP_TitleBarCloseButton:
4871 if (d->m_titleBarCloseIcon.isNull())
4872 d->m_titleBarCloseIcon = QIcon(new WinFontIconEngine(QChar(0xE8BB), d->m_assetFont));
4873 return d->m_titleBarCloseIcon;
4874
4875 case SP_TitleBarNormalButton:
4876 if (d->m_titleBarNormalIcon.isNull())
4877 d->m_titleBarNormalIcon = QIcon(new WinFontIconEngine(QChar(0xE923), d->m_assetFont));
4878 return d->m_titleBarNormalIcon;
4879
4880 case SP_CommandLink: {
4881 QWindowsThemeData theme(nullptr, nullptr, QWindowsVistaStylePrivate::ButtonTheme,
4883 if (theme.isValid()) {
4884 const QSize size = theme.size().toSize();
4885 QIcon linkGlyph;
4886 QPixmap pm(size);
4887 pm.fill(Qt::transparent);
4888 QPainter p(&pm);
4889 theme.painter = &p;
4890 theme.rect = QRect(QPoint(0, 0), size);
4891 d->drawBackground(theme);
4892 linkGlyph.addPixmap(pm, QIcon::Normal, QIcon::Off); // Normal
4893 pm.fill(Qt::transparent);
4894
4895 theme.stateId = CMDLGS_PRESSED;
4896 d->drawBackground(theme);
4897 linkGlyph.addPixmap(pm, QIcon::Normal, QIcon::On); // Pressed
4898 pm.fill(Qt::transparent);
4899
4900 theme.stateId = CMDLGS_HOT;
4901 d->drawBackground(theme);
4902 linkGlyph.addPixmap(pm, QIcon::Active, QIcon::Off); // Hover
4903 pm.fill(Qt::transparent);
4904
4905 theme.stateId = CMDLGS_DISABLED;
4906 d->drawBackground(theme);
4907 linkGlyph.addPixmap(pm, QIcon::Disabled, QIcon::Off); // Disabled
4908 return linkGlyph;
4909 }
4910 break;
4911 }
4912
4913 default:
4914 break;
4915 }
4916
4917 return QWindowsStyle::standardIcon(standardIcon, option, widget);
4918}
4919
4920WinFontIconEngine::WinFontIconEngine(const QString &glyph, const QFont &font)
4921 : QFontIconEngine({}, font)
4922 , m_font(font)
4923 , m_glyph(glyph)
4924{
4925}
4926
4927QString WinFontIconEngine::key() const
4928{
4929 return "WinFontIconEngine"_L1;
4930}
4931
4933{
4934 return new WinFontIconEngine(*this);
4935}
4936
4938{
4939 return m_glyph;
4940}
4941
4942void WinFontIconEngine::setScale(double scale)
4943{
4944 m_scale = scale;
4945}
4946
4947void WinFontIconEngine::paint(QPainter *painter, const QRect &rect, QIcon::Mode mode,
4948 QIcon::State)
4949{
4950 // we can't use QFontIconEngine because
4951 // - the text is drawn too large
4952 // - the palette from the widget is not used
4953 const QPaintDevice *paintDevice = painter->device();
4954 const bool isWidget = paintDevice->devType() == QInternal::Widget;
4955 const auto palette = isWidget ? static_cast<const QWidget *>(paintDevice)->palette()
4956 : qApp->palette();
4957 QColor color = Qt::black;
4958 switch (mode) {
4959 case QIcon::Active:
4960 color = palette.color(QPalette::Active, QPalette::Text);
4961 break;
4962 case QIcon::Normal:
4963 color = palette.color(QPalette::Active, QPalette::Text);
4964 break;
4965 case QIcon::Disabled:
4966 color = palette.color(QPalette::Disabled, QPalette::Text);
4967 break;
4968 case QIcon::Selected:
4969 color = palette.color(QPalette::Active, QPalette::HighlightedText);
4970 break;
4971 }
4972 QFont renderFont(m_font);
4973 renderFont.setPixelSize(rect.height() * m_scale);
4974 painter->save();
4975 painter->setFont(renderFont);
4976 painter->setPen(color);
4977 painter->drawText(rect, Qt::AlignCenter, m_glyph);
4978 painter->restore();
4979}
4980
4981QT_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)