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