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