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