1 /****************************************************************************
2 **
3 ** Copyright (C) 2016 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of the plugins of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see https://www.qt.io/terms-conditions. For further
15 ** information use the contact form at https://www.qt.io/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 3 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL3 included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 3 requirements
23 ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24 **
25 ** GNU General Public License Usage
26 ** Alternatively, this file may be used under the terms of the GNU
27 ** General Public License version 2.0 or (at your option) the GNU General
28 ** Public license version 3 or any later version approved by the KDE Free
29 ** Qt Foundation. The licenses are as published by the Free Software
30 ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31 ** included in the packaging of this file. Please review the following
32 ** information to ensure the GNU General Public License requirements will
33 ** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34 ** https://www.gnu.org/licenses/gpl-3.0.html.
35 **
36 ** $QT_END_LICENSE$
37 **
38 ****************************************************************************/
39 
40 // SHSTOCKICONINFO is only available since Vista
41 #if _WIN32_WINNT < 0x0601
42 #  undef _WIN32_WINNT
43 #  define _WIN32_WINNT 0x0601
44 #endif
45 
46 #include "qwindowstheme.h"
47 #include "qwindowsmenu.h"
48 #include "qwindowsdialoghelpers.h"
49 #include "qwindowscontext.h"
50 #include "qwindowsintegration.h"
51 #if QT_CONFIG(systemtrayicon)
52 #  include "qwindowssystemtrayicon.h"
53 #endif
54 #include "qwindowsscreen.h"
55 #include "qt_windows.h"
56 #include <commctrl.h>
57 #include <objbase.h>
58 #ifndef Q_CC_MINGW
59 #  include <commoncontrols.h>
60 #endif
61 #include <shellapi.h>
62 
63 #include <QtCore/qvariant.h>
64 #include <QtCore/qcoreapplication.h>
65 #include <QtCore/qdebug.h>
66 #include <QtCore/qtextstream.h>
67 #include <QtCore/qoperatingsystemversion.h>
68 #include <QtCore/qsysinfo.h>
69 #include <QtCore/qcache.h>
70 #include <QtCore/qthread.h>
71 #include <QtCore/qmutex.h>
72 #include <QtCore/qwaitcondition.h>
73 #include <QtGui/qcolor.h>
74 #include <QtGui/qpalette.h>
75 #include <QtGui/qguiapplication.h>
76 #include <QtGui/qpainter.h>
77 #include <QtGui/qpixmapcache.h>
78 #include <qpa/qwindowsysteminterface.h>
79 #include <QtThemeSupport/private/qabstractfileiconengine_p.h>
80 #include <QtFontDatabaseSupport/private/qwindowsfontdatabase_p.h>
81 #include <private/qhighdpiscaling_p.h>
82 #include <private/qsystemlibrary_p.h>
83 #include <private/qwinregistry_p.h>
84 
85 #include <algorithm>
86 
87 #if defined(__IImageList_INTERFACE_DEFINED__) && defined(__IID_DEFINED__)
88 #  define USE_IIMAGELIST
89 #endif
90 
91 QT_BEGIN_NAMESPACE
92 
COLORREFToQColor(COLORREF cr)93 static inline QColor COLORREFToQColor(COLORREF cr)
94 {
95     return QColor(GetRValue(cr), GetGValue(cr), GetBValue(cr));
96 }
97 
operator <<(QTextStream & str,const QColor & c)98 static inline QTextStream& operator<<(QTextStream &str, const QColor &c)
99 {
100     str.setIntegerBase(16);
101     str.setFieldWidth(2);
102     str.setPadChar(u'0');
103     str << " rgb: #" << c.red()  << c.green() << c.blue();
104     str.setIntegerBase(10);
105     str.setFieldWidth(0);
106     return str;
107 }
108 
booleanSystemParametersInfo(UINT what,bool defaultValue)109 static inline bool booleanSystemParametersInfo(UINT what, bool defaultValue)
110 {
111     BOOL result;
112     if (SystemParametersInfo(what, 0, &result, 0))
113         return result != FALSE;
114     return defaultValue;
115 }
116 
dWordSystemParametersInfo(UINT what,DWORD defaultValue)117 static inline DWORD dWordSystemParametersInfo(UINT what, DWORD defaultValue)
118 {
119     DWORD result;
120     if (SystemParametersInfo(what, 0, &result, 0))
121         return result;
122     return defaultValue;
123 }
124 
mixColors(const QColor & c1,const QColor & c2)125 static inline QColor mixColors(const QColor &c1, const QColor &c2)
126 {
127     return {(c1.red() + c2.red()) / 2,
128             (c1.green() + c2.green()) / 2,
129             (c1.blue() + c2.blue()) / 2};
130 }
131 
getSysColor(int index)132 static inline QColor getSysColor(int index)
133 {
134     return COLORREFToQColor(GetSysColor(index));
135 }
136 
137 // QTBUG-48823/Windows 10: SHGetFileInfo() (as called by item views on file system
138 // models has been observed to trigger a WM_PAINT on the mainwindow. Suppress the
139 // behavior by running it in a thread.
140 
141 struct QShGetFileInfoParams
142 {
QShGetFileInfoParamsQShGetFileInfoParams143     QShGetFileInfoParams(const QString &fn, DWORD a, SHFILEINFO *i, UINT f, bool *r)
144         : fileName(fn), attributes(a), flags(f), info(i), result(r)
145     { }
146 
147     const QString &fileName;
148     const DWORD attributes;
149     const UINT flags;
150     SHFILEINFO *const info;
151     bool *const result;
152 };
153 
154 class QShGetFileInfoThread : public QThread
155 {
156 public:
QShGetFileInfoThread()157     explicit QShGetFileInfoThread()
158         : QThread(), m_params(nullptr)
159     {
160         connect(this, &QThread::finished, this, &QObject::deleteLater);
161     }
162 
run()163     void run() override
164     {
165         m_init = CoInitializeEx(nullptr, COINIT_MULTITHREADED);
166 
167         QMutexLocker readyLocker(&m_readyMutex);
168         while (!m_cancelled.loadRelaxed()) {
169             if (!m_params && !m_cancelled.loadRelaxed()
170                 && !m_readyCondition.wait(&m_readyMutex, QDeadlineTimer(1000ll)))
171                 continue;
172 
173             if (m_params) {
174                 const QString fileName = m_params->fileName;
175                 SHFILEINFO info;
176                 const bool result = SHGetFileInfo(reinterpret_cast<const wchar_t *>(fileName.utf16()),
177                                                   m_params->attributes, &info, sizeof(SHFILEINFO),
178                                                   m_params->flags);
179                 m_doneMutex.lock();
180                 if (!m_cancelled.loadRelaxed()) {
181                     *m_params->result = result;
182                     memcpy(m_params->info, &info, sizeof(SHFILEINFO));
183                 }
184                 m_params = nullptr;
185 
186                 m_doneCondition.wakeAll();
187                 m_doneMutex.unlock();
188             }
189         }
190 
191         if (m_init != S_FALSE)
192             CoUninitialize();
193     }
194 
runWithParams(QShGetFileInfoParams * params,qint64 timeOutMSecs)195     bool runWithParams(QShGetFileInfoParams *params, qint64 timeOutMSecs)
196     {
197         QMutexLocker doneLocker(&m_doneMutex);
198 
199         m_readyMutex.lock();
200         m_params = params;
201         m_readyCondition.wakeAll();
202         m_readyMutex.unlock();
203 
204         return m_doneCondition.wait(&m_doneMutex, QDeadlineTimer(timeOutMSecs));
205     }
206 
cancel()207     void cancel()
208     {
209         QMutexLocker doneLocker(&m_doneMutex);
210         m_cancelled.storeRelaxed(1);
211         m_readyCondition.wakeAll();
212     }
213 
214 private:
215     HRESULT m_init;
216     QShGetFileInfoParams *m_params;
217     QAtomicInt m_cancelled;
218     QWaitCondition m_readyCondition;
219     QWaitCondition m_doneCondition;
220     QMutex m_readyMutex;
221     QMutex m_doneMutex;
222 };
223 
shGetFileInfoBackground(const QString & fileName,DWORD attributes,SHFILEINFO * info,UINT flags,qint64 timeOutMSecs=5000)224 static bool shGetFileInfoBackground(const QString &fileName, DWORD attributes,
225                                     SHFILEINFO *info, UINT flags,
226                                     qint64 timeOutMSecs = 5000)
227 {
228     static QShGetFileInfoThread *getFileInfoThread = nullptr;
229     if (!getFileInfoThread) {
230         getFileInfoThread = new QShGetFileInfoThread;
231         getFileInfoThread->start();
232     }
233 
234     bool result = false;
235     QShGetFileInfoParams params(fileName, attributes, info, flags, &result);
236     if (!getFileInfoThread->runWithParams(&params, timeOutMSecs)) {
237         // Cancel and reset getFileInfoThread. It'll
238         // be reinitialized the next time we get called.
239         getFileInfoThread->cancel();
240         getFileInfoThread = nullptr;
241         qWarning().noquote() << "SHGetFileInfo() timed out for " << fileName;
242         return false;
243     }
244     return result;
245 }
246 
247 // Dark Mode constants
248 enum DarkModeColors : QRgb  {
249     darkModeBtnHighlightRgb = 0xc0c0c0,
250     darkModeBtnShadowRgb = 0x808080,
251     darkModeHighlightRgb = 0x0055ff, // deviating from 0x800080
252     darkModeMenuHighlightRgb = darkModeHighlightRgb
253 };
254 
255 // from QStyle::standardPalette
standardPalette()256 static inline QPalette standardPalette()
257 {
258     QColor backgroundColor(0xd4, 0xd0, 0xc8); // win 2000 grey
259     QColor lightColor(backgroundColor.lighter());
260     QColor darkColor(backgroundColor.darker());
261     const QBrush darkBrush(darkColor);
262     QColor midColor(Qt::gray);
263     QPalette palette(Qt::black, backgroundColor, lightColor, darkColor,
264                      midColor, Qt::black, Qt::white);
265     palette.setBrush(QPalette::Disabled, QPalette::WindowText, darkBrush);
266     palette.setBrush(QPalette::Disabled, QPalette::Text, darkBrush);
267     palette.setBrush(QPalette::Disabled, QPalette::ButtonText, darkBrush);
268     palette.setBrush(QPalette::Disabled, QPalette::Base, QBrush(backgroundColor));
269     return palette;
270 }
271 
populateLightSystemBasePalette(QPalette & result)272 static void populateLightSystemBasePalette(QPalette &result)
273 {
274     result.setColor(QPalette::WindowText, getSysColor(COLOR_WINDOWTEXT));
275     const QColor btnFace = getSysColor(COLOR_BTNFACE);
276     result.setColor(QPalette::Button, btnFace);
277     const QColor btnHighlight = getSysColor(COLOR_BTNHIGHLIGHT);
278     result.setColor(QPalette::Light, btnHighlight);
279     result.setColor(QPalette::Dark, getSysColor(COLOR_BTNSHADOW));
280     result.setColor(QPalette::Mid, result.button().color().darker(150));
281     result.setColor(QPalette::Text, getSysColor(COLOR_WINDOWTEXT));
282     result.setColor(QPalette::BrightText, btnHighlight);
283     result.setColor(QPalette::Base, getSysColor(COLOR_WINDOW));
284     result.setColor(QPalette::Window, btnFace);
285     result.setColor(QPalette::ButtonText, getSysColor(COLOR_BTNTEXT));
286     result.setColor(QPalette::Midlight, getSysColor(COLOR_3DLIGHT));
287     result.setColor(QPalette::Shadow, getSysColor(COLOR_3DDKSHADOW));
288     result.setColor(QPalette::Highlight, getSysColor(COLOR_HIGHLIGHT));
289     result.setColor(QPalette::HighlightedText, getSysColor(COLOR_HIGHLIGHTTEXT));
290 }
291 
populateDarkSystemBasePalette(QPalette & result)292 static void populateDarkSystemBasePalette(QPalette &result)
293 {
294     const QColor darkModeWindowText = Qt::white;
295     result.setColor(QPalette::WindowText, darkModeWindowText);
296     const QColor darkModebtnFace = Qt::black;
297     result.setColor(QPalette::Button, darkModebtnFace);
298     const QColor btnHighlight = QColor(darkModeBtnHighlightRgb);
299     result.setColor(QPalette::Light, btnHighlight);
300     result.setColor(QPalette::Dark, QColor(darkModeBtnShadowRgb));
301     result.setColor(QPalette::Mid, result.button().color().darker(150));
302     result.setColor(QPalette::Text, darkModeWindowText);
303     result.setColor(QPalette::BrightText, btnHighlight);
304     result.setColor(QPalette::Base, darkModebtnFace);
305     result.setColor(QPalette::Window, darkModebtnFace);
306     result.setColor(QPalette::ButtonText, darkModeWindowText);
307     result.setColor(QPalette::Midlight, darkModeWindowText);
308     result.setColor(QPalette::Shadow, darkModeWindowText);
309     result.setColor(QPalette::Highlight, QColor(darkModeHighlightRgb));
310     result.setColor(QPalette::HighlightedText, darkModeWindowText);
311 }
312 
systemPalette(bool light)313 static QPalette systemPalette(bool light)
314 {
315     QPalette result = standardPalette();
316     if (light)
317         populateLightSystemBasePalette(result);
318     else
319         populateDarkSystemBasePalette(result);
320 
321     result.setColor(QPalette::Link, Qt::blue);
322     result.setColor(QPalette::LinkVisited, Qt::magenta);
323     result.setColor(QPalette::Inactive, QPalette::Button, result.button().color());
324     result.setColor(QPalette::Inactive, QPalette::Window, result.window().color());
325     result.setColor(QPalette::Inactive, QPalette::Light, result.light().color());
326     result.setColor(QPalette::Inactive, QPalette::Dark, result.dark().color());
327 
328     if (result.midlight() == result.button())
329         result.setColor(QPalette::Midlight, result.button().color().lighter(110));
330     if (result.window() != result.base()) {
331         result.setColor(QPalette::Inactive, QPalette::Highlight, result.color(QPalette::Inactive, QPalette::Window));
332         result.setColor(QPalette::Inactive, QPalette::HighlightedText, result.color(QPalette::Inactive, QPalette::Text));
333     }
334 
335     const QColor disabled =
336         mixColors(result.windowText().color(), result.button().color());
337 
338     result.setColorGroup(QPalette::Disabled, result.windowText(), result.button(),
339                          result.light(), result.dark(), result.mid(),
340                          result.text(), result.brightText(), result.base(),
341                          result.window());
342     result.setColor(QPalette::Disabled, QPalette::WindowText, disabled);
343     result.setColor(QPalette::Disabled, QPalette::Text, disabled);
344     result.setColor(QPalette::Disabled, QPalette::ButtonText, disabled);
345     result.setColor(QPalette::Disabled, QPalette::Highlight,
346                     light ? getSysColor(COLOR_HIGHLIGHT) : QColor(darkModeHighlightRgb));
347     result.setColor(QPalette::Disabled, QPalette::HighlightedText,
348                     light ? getSysColor(COLOR_HIGHLIGHTTEXT) : QColor(Qt::white));
349     result.setColor(QPalette::Disabled, QPalette::Base,
350                     result.window().color());
351     return result;
352 }
353 
toolTipPalette(const QPalette & systemPalette,bool light)354 static inline QPalette toolTipPalette(const QPalette &systemPalette, bool light)
355 {
356     QPalette result(systemPalette);
357     const QColor tipBgColor = light ? getSysColor(COLOR_INFOBK) : QColor(Qt::black);
358     const QColor tipTextColor = light ? getSysColor(COLOR_INFOTEXT) : QColor(Qt::white);
359 
360     result.setColor(QPalette::All, QPalette::Button, tipBgColor);
361     result.setColor(QPalette::All, QPalette::Window, tipBgColor);
362     result.setColor(QPalette::All, QPalette::Text, tipTextColor);
363     result.setColor(QPalette::All, QPalette::WindowText, tipTextColor);
364     result.setColor(QPalette::All, QPalette::ButtonText, tipTextColor);
365     result.setColor(QPalette::All, QPalette::Button, tipBgColor);
366     result.setColor(QPalette::All, QPalette::Window, tipBgColor);
367     result.setColor(QPalette::All, QPalette::Text, tipTextColor);
368     result.setColor(QPalette::All, QPalette::WindowText, tipTextColor);
369     result.setColor(QPalette::All, QPalette::ButtonText, tipTextColor);
370     result.setColor(QPalette::All, QPalette::ToolTipBase, tipBgColor);
371     result.setColor(QPalette::All, QPalette::ToolTipText, tipTextColor);
372     const QColor disabled =
373         mixColors(result.windowText().color(), result.button().color());
374     result.setColor(QPalette::Disabled, QPalette::WindowText, disabled);
375     result.setColor(QPalette::Disabled, QPalette::Text, disabled);
376     result.setColor(QPalette::Disabled, QPalette::ToolTipText, disabled);
377     result.setColor(QPalette::Disabled, QPalette::Base, Qt::white);
378     result.setColor(QPalette::Disabled, QPalette::BrightText, Qt::white);
379     result.setColor(QPalette::Disabled, QPalette::ToolTipBase, Qt::white);
380     return result;
381 }
382 
menuPalette(const QPalette & systemPalette,bool light)383 static inline QPalette menuPalette(const QPalette &systemPalette, bool light)
384 {
385     QPalette result(systemPalette);
386     const QColor menuColor = light ? getSysColor(COLOR_MENU) : QColor(Qt::black);
387     const QColor menuTextColor = light ? getSysColor(COLOR_MENUTEXT) : QColor(Qt::white);
388     const QColor disabled = light
389         ? getSysColor(COLOR_GRAYTEXT) : QColor(darkModeBtnHighlightRgb);
390     // we might need a special color group for the result.
391     result.setColor(QPalette::Active, QPalette::Button, menuColor);
392     result.setColor(QPalette::Active, QPalette::Text, menuTextColor);
393     result.setColor(QPalette::Active, QPalette::WindowText, menuTextColor);
394     result.setColor(QPalette::Active, QPalette::ButtonText, menuTextColor);
395     result.setColor(QPalette::Disabled, QPalette::WindowText, disabled);
396     result.setColor(QPalette::Disabled, QPalette::Text, disabled);
397     const bool isFlat = booleanSystemParametersInfo(SPI_GETFLATMENU, false);
398     const QColor highlightColor = light
399         ? (getSysColor(isFlat ? COLOR_MENUHILIGHT : COLOR_HIGHLIGHT))
400         : QColor(darkModeMenuHighlightRgb);
401     result.setColor(QPalette::Disabled, QPalette::Highlight, highlightColor);
402     result.setColor(QPalette::Disabled, QPalette::HighlightedText, disabled);
403     result.setColor(QPalette::Disabled, QPalette::Button,
404                     result.color(QPalette::Active, QPalette::Button));
405     result.setColor(QPalette::Inactive, QPalette::Button,
406                     result.color(QPalette::Active, QPalette::Button));
407     result.setColor(QPalette::Inactive, QPalette::Text,
408                     result.color(QPalette::Active, QPalette::Text));
409     result.setColor(QPalette::Inactive, QPalette::WindowText,
410                     result.color(QPalette::Active, QPalette::WindowText));
411     result.setColor(QPalette::Inactive, QPalette::ButtonText,
412                     result.color(QPalette::Active, QPalette::ButtonText));
413     result.setColor(QPalette::Inactive, QPalette::Highlight,
414                     result.color(QPalette::Active, QPalette::Highlight));
415     result.setColor(QPalette::Inactive, QPalette::HighlightedText,
416                     result.color(QPalette::Active, QPalette::HighlightedText));
417     result.setColor(QPalette::Inactive, QPalette::ButtonText,
418                     systemPalette.color(QPalette::Inactive, QPalette::Dark));
419     return result;
420 }
421 
menuBarPalette(const QPalette & menuPalette,bool light)422 static inline QPalette *menuBarPalette(const QPalette &menuPalette, bool light)
423 {
424     QPalette *result = nullptr;
425     if (booleanSystemParametersInfo(SPI_GETFLATMENU, false)) {
426         result = new QPalette(menuPalette);
427         const QColor menubar(light ? getSysColor(COLOR_MENUBAR) : QColor(Qt::black));
428         result->setColor(QPalette::Active, QPalette::Button, menubar);
429         result->setColor(QPalette::Disabled, QPalette::Button, menubar);
430         result->setColor(QPalette::Inactive, QPalette::Button, menubar);
431     }
432     return result;
433 }
434 
435 const char *QWindowsTheme::name = "windows";
436 QWindowsTheme *QWindowsTheme::m_instance = nullptr;
437 
QWindowsTheme()438 QWindowsTheme::QWindowsTheme()
439 {
440     m_instance = this;
441     std::fill(m_fonts, m_fonts + NFonts, nullptr);
442     std::fill(m_palettes, m_palettes + NPalettes, nullptr);
443     refresh();
444     refreshIconPixmapSizes();
445 }
446 
~QWindowsTheme()447 QWindowsTheme::~QWindowsTheme()
448 {
449     clearPalettes();
450     clearFonts();
451     m_instance = nullptr;
452 }
453 
iconThemeSearchPaths()454 static inline QStringList iconThemeSearchPaths()
455 {
456     const QFileInfo appDir(QCoreApplication::applicationDirPath() + QLatin1String("/icons"));
457     return appDir.isDir() ? QStringList(appDir.absoluteFilePath()) : QStringList();
458 }
459 
styleNames()460 static inline QStringList styleNames()
461 {
462     return { QStringLiteral("WindowsVista"), QStringLiteral("Windows") };
463 }
464 
uiEffects()465 static inline int uiEffects()
466 {
467     int result = QPlatformTheme::HoverEffect;
468     if (booleanSystemParametersInfo(SPI_GETUIEFFECTS, false))
469         result |= QPlatformTheme::GeneralUiEffect;
470     if (booleanSystemParametersInfo(SPI_GETMENUANIMATION, false))
471         result |= QPlatformTheme::AnimateMenuUiEffect;
472     if (booleanSystemParametersInfo(SPI_GETMENUFADE, false))
473         result |= QPlatformTheme::FadeMenuUiEffect;
474     if (booleanSystemParametersInfo(SPI_GETCOMBOBOXANIMATION, false))
475         result |= QPlatformTheme::AnimateComboUiEffect;
476     if (booleanSystemParametersInfo(SPI_GETTOOLTIPANIMATION, false))
477         result |= QPlatformTheme::AnimateTooltipUiEffect;
478     return result;
479 }
480 
themeHint(ThemeHint hint) const481 QVariant QWindowsTheme::themeHint(ThemeHint hint) const
482 {
483     switch (hint) {
484     case UseFullScreenForPopupMenu:
485         return QVariant(true);
486     case DialogButtonBoxLayout:
487         return QVariant(QPlatformDialogHelper::WinLayout);
488     case IconThemeSearchPaths:
489         return QVariant(iconThemeSearchPaths());
490     case StyleNames:
491         return QVariant(styleNames());
492     case TextCursorWidth:
493         return QVariant(int(dWordSystemParametersInfo(SPI_GETCARETWIDTH, 1u)));
494     case DropShadow:
495         return QVariant(booleanSystemParametersInfo(SPI_GETDROPSHADOW, false));
496     case MaximumScrollBarDragDistance:
497         return QVariant(qRound(qreal(QWindowsContext::instance()->defaultDPI()) * 1.375));
498     case KeyboardScheme:
499         return QVariant(int(WindowsKeyboardScheme));
500     case UiEffects:
501         return QVariant(uiEffects());
502     case IconPixmapSizes:
503         return QVariant::fromValue(m_fileIconSizes);
504     case DialogSnapToDefaultButton:
505         return QVariant(booleanSystemParametersInfo(SPI_GETSNAPTODEFBUTTON, false));
506     case ContextMenuOnMouseRelease:
507         return QVariant(true);
508     case WheelScrollLines: {
509         int result = 3;
510         const DWORD scrollLines = dWordSystemParametersInfo(SPI_GETWHEELSCROLLLINES, DWORD(result));
511         if (scrollLines != DWORD(-1)) // Special value meaning "scroll one screen", unimplemented in Qt.
512             result = int(scrollLines);
513         return QVariant(result);
514     }
515     case MouseDoubleClickDistance:
516         return GetSystemMetrics(SM_CXDOUBLECLK);
517     default:
518         break;
519     }
520     return QPlatformTheme::themeHint(hint);
521 }
522 
clearPalettes()523 void QWindowsTheme::clearPalettes()
524 {
525     qDeleteAll(m_palettes, m_palettes + NPalettes);
526     std::fill(m_palettes, m_palettes + NPalettes, nullptr);
527 }
528 
refreshPalettes()529 void QWindowsTheme::refreshPalettes()
530 {
531 
532     if (!QGuiApplication::desktopSettingsAware())
533         return;
534     const bool light =
535         !QWindowsContext::isDarkMode()
536         || (QWindowsIntegration::instance()->options() & QWindowsIntegration::DarkModeStyle) == 0;
537     m_palettes[SystemPalette] = new QPalette(systemPalette(light));
538     m_palettes[ToolTipPalette] = new QPalette(toolTipPalette(*m_palettes[SystemPalette], light));
539     m_palettes[MenuPalette] = new QPalette(menuPalette(*m_palettes[SystemPalette], light));
540     m_palettes[MenuBarPalette] = menuBarPalette(*m_palettes[MenuPalette], light);
541     if (!light) {
542         m_palettes[ButtonPalette] = new QPalette(*m_palettes[SystemPalette]);
543         m_palettes[ButtonPalette]->setColor(QPalette::Button, QColor(0x666666u));
544         const QColor checkBoxBlue(0x0078d7u);
545         const QColor white(Qt::white);
546         m_palettes[CheckBoxPalette] = new QPalette(*m_palettes[SystemPalette]);
547         m_palettes[CheckBoxPalette]->setColor(QPalette::Window, checkBoxBlue);
548         m_palettes[CheckBoxPalette]->setColor(QPalette::Base, checkBoxBlue);
549         m_palettes[CheckBoxPalette]->setColor(QPalette::Button, checkBoxBlue);
550         m_palettes[CheckBoxPalette]->setColor(QPalette::ButtonText, white);
551         m_palettes[RadioButtonPalette] = new QPalette(*m_palettes[CheckBoxPalette]);
552 
553     }
554 }
555 
clearFonts()556 void QWindowsTheme::clearFonts()
557 {
558     qDeleteAll(m_fonts, m_fonts + NFonts);
559     std::fill(m_fonts, m_fonts + NFonts, nullptr);
560 }
561 
refresh()562 void QWindowsTheme::refresh()
563 {
564     refreshPalettes();
565     refreshFonts();
566 }
567 
568 #ifndef QT_NO_DEBUG_STREAM
569 QDebug operator<<(QDebug d, const LOGFONT &lf); // in platformsupport
570 
operator <<(QDebug d,const NONCLIENTMETRICS & m)571 QDebug operator<<(QDebug d, const NONCLIENTMETRICS &m)
572 {
573     QDebugStateSaver saver(d);
574     d.nospace();
575     d.noquote();
576     d << "NONCLIENTMETRICS(iMenu=" << m.iMenuWidth  << 'x' << m.iMenuHeight
577       << ", lfCaptionFont=" << m.lfCaptionFont << ", lfSmCaptionFont="
578       << m.lfSmCaptionFont << ", lfMenuFont=" << m.lfMenuFont
579       <<  ", lfMessageFont=" << m.lfMessageFont << ", lfStatusFont="
580       << m.lfStatusFont << ')';
581     return d;
582 }
583 #endif // QT_NO_DEBUG_STREAM
584 
refreshFonts()585 void QWindowsTheme::refreshFonts()
586 {
587     clearFonts();
588     if (!QGuiApplication::desktopSettingsAware())
589         return;
590     NONCLIENTMETRICS ncm;
591     auto screenManager = QWindowsContext::instance()->screenManager();
592     QWindowsContext::nonClientMetricsForScreen(&ncm, screenManager.screens().value(0));
593     qCDebug(lcQpaWindows) << __FUNCTION__ << ncm;
594     const QFont menuFont = QWindowsFontDatabase::LOGFONT_to_QFont(ncm.lfMenuFont);
595     const QFont messageBoxFont = QWindowsFontDatabase::LOGFONT_to_QFont(ncm.lfMessageFont);
596     const QFont statusFont = QWindowsFontDatabase::LOGFONT_to_QFont(ncm.lfStatusFont);
597     const QFont titleFont = QWindowsFontDatabase::LOGFONT_to_QFont(ncm.lfCaptionFont);
598     QFont fixedFont(QStringLiteral("Courier New"), messageBoxFont.pointSize());
599     fixedFont.setStyleHint(QFont::TypeWriter);
600 
601     LOGFONT lfIconTitleFont;
602     SystemParametersInfo(SPI_GETICONTITLELOGFONT, sizeof(lfIconTitleFont), &lfIconTitleFont, 0);
603     const QFont iconTitleFont = QWindowsFontDatabase::LOGFONT_to_QFont(lfIconTitleFont);
604 
605     m_fonts[SystemFont] = new QFont(QWindowsFontDatabase::systemDefaultFont());
606     m_fonts[MenuFont] = new QFont(menuFont);
607     m_fonts[MenuBarFont] = new QFont(menuFont);
608     m_fonts[MessageBoxFont] = new QFont(messageBoxFont);
609     m_fonts[TipLabelFont] = new QFont(statusFont);
610     m_fonts[StatusBarFont] = new QFont(statusFont);
611     m_fonts[MdiSubWindowTitleFont] = new QFont(titleFont);
612     m_fonts[DockWidgetTitleFont] = new QFont(titleFont);
613     m_fonts[ItemViewFont] = new QFont(iconTitleFont);
614     m_fonts[FixedFont] = new QFont(fixedFont);
615 }
616 
617 enum FileIconSize {
618     // Standard icons obtainable via shGetFileInfo(), SHGFI_SMALLICON, SHGFI_LARGEICON
619     SmallFileIcon, LargeFileIcon,
620     // Larger icons obtainable via SHGetImageList()
621     ExtraLargeFileIcon,
622     JumboFileIcon, // Vista onwards
623     FileIconSizeCount
624 };
625 
usePlatformNativeDialog(DialogType type) const626 bool QWindowsTheme::usePlatformNativeDialog(DialogType type) const
627 {
628     return QWindowsDialogs::useHelper(type);
629 }
630 
createPlatformDialogHelper(DialogType type) const631 QPlatformDialogHelper *QWindowsTheme::createPlatformDialogHelper(DialogType type) const
632 {
633     return QWindowsDialogs::createHelper(type);
634 }
635 
636 #if QT_CONFIG(systemtrayicon)
createPlatformSystemTrayIcon() const637 QPlatformSystemTrayIcon *QWindowsTheme::createPlatformSystemTrayIcon() const
638 {
639     return new QWindowsSystemTrayIcon;
640 }
641 #endif
642 
windowsThemeChanged(QWindow * window)643 void QWindowsTheme::windowsThemeChanged(QWindow * window)
644 {
645     refresh();
646     QWindowSystemInterface::handleThemeChange(window);
647 }
648 
649 static int fileIconSizes[FileIconSizeCount];
650 
refreshIconPixmapSizes()651 void QWindowsTheme::refreshIconPixmapSizes()
652 {
653     // Standard sizes: 16, 32, 48, 256
654     fileIconSizes[SmallFileIcon] = GetSystemMetrics(SM_CXSMICON); // corresponds to SHGFI_SMALLICON);
655     fileIconSizes[LargeFileIcon] = GetSystemMetrics(SM_CXICON); // corresponds to SHGFI_LARGEICON
656     fileIconSizes[ExtraLargeFileIcon] =
657         fileIconSizes[LargeFileIcon] + fileIconSizes[LargeFileIcon] / 2;
658     fileIconSizes[JumboFileIcon] = 8 * fileIconSizes[LargeFileIcon]; // empirical, has not been observed to work
659 
660 #ifdef USE_IIMAGELIST
661     int *availEnd = fileIconSizes + JumboFileIcon + 1;
662 #else
663     int *availEnd = fileIconSizes + LargeFileIcon + 1;
664 #endif // USE_IIMAGELIST
665     m_fileIconSizes = QAbstractFileIconEngine::toSizeList(fileIconSizes, availEnd);
666     qCDebug(lcQpaWindows) << __FUNCTION__ << m_fileIconSizes;
667 }
668 
669 // Defined in qpixmap_win.cpp
670 Q_GUI_EXPORT QPixmap qt_pixmapFromWinHICON(HICON icon);
671 
loadIconFromShell32(int resourceId,QSizeF size)672 static QPixmap loadIconFromShell32(int resourceId, QSizeF size)
673 {
674     if (const HMODULE hmod = QSystemLibrary::load(L"shell32")) {
675         auto iconHandle =
676             static_cast<HICON>(LoadImage(hmod, MAKEINTRESOURCE(resourceId),
677                                          IMAGE_ICON, int(size.width()), int(size.height()), 0));
678         if (iconHandle) {
679             QPixmap iconpixmap = qt_pixmapFromWinHICON(iconHandle);
680             DestroyIcon(iconHandle);
681             return iconpixmap;
682         }
683     }
684     return QPixmap();
685 }
686 
standardPixmap(StandardPixmap sp,const QSizeF & pixmapSize) const687 QPixmap QWindowsTheme::standardPixmap(StandardPixmap sp, const QSizeF &pixmapSize) const
688 {
689     int resourceId = -1;
690     SHSTOCKICONID stockId = SIID_INVALID;
691     UINT stockFlags = 0;
692     LPCTSTR iconName = nullptr;
693     switch (sp) {
694     case DriveCDIcon:
695         stockId = SIID_DRIVECD;
696         resourceId = 12;
697         break;
698     case DriveDVDIcon:
699         stockId = SIID_DRIVEDVD;
700         resourceId = 12;
701         break;
702     case DriveNetIcon:
703         stockId = SIID_DRIVENET;
704         resourceId = 10;
705         break;
706     case DriveHDIcon:
707         stockId = SIID_DRIVEFIXED;
708         resourceId = 9;
709         break;
710     case DriveFDIcon:
711         stockId = SIID_DRIVE35;
712         resourceId = 7;
713         break;
714     case FileLinkIcon:
715         stockFlags = SHGSI_LINKOVERLAY;
716         Q_FALLTHROUGH();
717     case FileIcon:
718         stockId = SIID_DOCNOASSOC;
719         resourceId = 1;
720         break;
721     case DirLinkIcon:
722         stockFlags = SHGSI_LINKOVERLAY;
723         Q_FALLTHROUGH();
724     case DirClosedIcon:
725     case DirIcon:
726         stockId = SIID_FOLDER;
727         resourceId = 4;
728         break;
729     case DesktopIcon:
730         resourceId = 35;
731         break;
732     case ComputerIcon:
733         resourceId = 16;
734         break;
735     case DirLinkOpenIcon:
736         stockFlags = SHGSI_LINKOVERLAY;
737         Q_FALLTHROUGH();
738     case DirOpenIcon:
739         stockId = SIID_FOLDEROPEN;
740         resourceId = 5;
741         break;
742     case FileDialogNewFolder:
743         stockId = SIID_FOLDER;
744         resourceId = 319;
745         break;
746     case DirHomeIcon:
747         resourceId = 235;
748         break;
749     case TrashIcon:
750         stockId = SIID_RECYCLER;
751         resourceId = 191;
752         break;
753     case MessageBoxInformation:
754         stockId = SIID_INFO;
755         iconName = IDI_INFORMATION;
756         break;
757     case MessageBoxWarning:
758         stockId = SIID_WARNING;
759         iconName = IDI_WARNING;
760         break;
761     case MessageBoxCritical:
762         stockId = SIID_ERROR;
763         iconName = IDI_ERROR;
764         break;
765     case MessageBoxQuestion:
766         stockId = SIID_HELP;
767         iconName = IDI_QUESTION;
768         break;
769     case VistaShield:
770         stockId = SIID_SHIELD;
771         break;
772     default:
773         break;
774     }
775 
776     if (stockId != SIID_INVALID) {
777         QPixmap pixmap;
778         SHSTOCKICONINFO iconInfo;
779         memset(&iconInfo, 0, sizeof(iconInfo));
780         iconInfo.cbSize = sizeof(iconInfo);
781         stockFlags |= (pixmapSize.width() > 16 ? SHGFI_LARGEICON : SHGFI_SMALLICON);
782         if (SHGetStockIconInfo(stockId, SHGFI_ICON | stockFlags, &iconInfo) == S_OK) {
783             pixmap = qt_pixmapFromWinHICON(iconInfo.hIcon);
784             DestroyIcon(iconInfo.hIcon);
785             return pixmap;
786         }
787     }
788 
789     if (resourceId != -1) {
790         QPixmap pixmap = loadIconFromShell32(resourceId, pixmapSize);
791         if (!pixmap.isNull()) {
792             if (sp == FileLinkIcon || sp == DirLinkIcon || sp == DirLinkOpenIcon) {
793                 QPainter painter(&pixmap);
794                 QPixmap link = loadIconFromShell32(30, pixmapSize);
795                 painter.drawPixmap(0, 0, int(pixmapSize.width()), int(pixmapSize.height()), link);
796             }
797             return pixmap;
798         }
799     }
800 
801     if (iconName) {
802         HICON iconHandle = LoadIcon(nullptr, iconName);
803         QPixmap pixmap = qt_pixmapFromWinHICON(iconHandle);
804         DestroyIcon(iconHandle);
805         if (!pixmap.isNull())
806             return pixmap;
807     }
808 
809     return QPlatformTheme::standardPixmap(sp, pixmapSize);
810 }
811 
812 enum { // Shell image list ids
813     sHIL_EXTRALARGE = 0x2, // 48x48 or user-defined
814     sHIL_JUMBO = 0x4 // 256x256 (Vista or later)
815 };
816 
dirIconPixmapCacheKey(int iIcon,int iconSize,int imageListSize)817 static QString dirIconPixmapCacheKey(int iIcon, int iconSize, int imageListSize)
818 {
819     QString key = QLatin1String("qt_dir_") + QString::number(iIcon);
820     if (iconSize == SHGFI_LARGEICON)
821         key += u'l';
822     switch (imageListSize) {
823     case sHIL_EXTRALARGE:
824         key += u'e';
825         break;
826     case sHIL_JUMBO:
827         key += u'j';
828         break;
829     }
830     return key;
831 }
832 
833 template <typename T>
834 class FakePointer
835 {
836 public:
837 
838     Q_STATIC_ASSERT_X(sizeof(T) <= sizeof(void *), "FakePointers can only go that far.");
839 
create(T thing)840     static FakePointer *create(T thing)
841     {
842         return reinterpret_cast<FakePointer *>(qintptr(thing));
843     }
844 
operator *() const845     T operator * () const
846     {
847         return T(qintptr(this));
848     }
849 
operator delete(void *)850     void operator delete (void *) {}
851 };
852 
853 // Shell image list helper functions.
854 
pixmapFromShellImageList(int iImageList,const SHFILEINFO & info)855 static QPixmap pixmapFromShellImageList(int iImageList, const SHFILEINFO &info)
856 {
857     QPixmap result;
858 #ifdef USE_IIMAGELIST
859     // For MinGW:
860     static const IID iID_IImageList = {0x46eb5926, 0x582e, 0x4017, {0x9f, 0xdf, 0xe8, 0x99, 0x8d, 0xaa, 0x9, 0x50}};
861 
862     IImageList *imageList = nullptr;
863     HRESULT hr = SHGetImageList(iImageList, iID_IImageList, reinterpret_cast<void **>(&imageList));
864     if (hr != S_OK)
865         return result;
866     HICON hIcon;
867     hr = imageList->GetIcon(info.iIcon, ILD_TRANSPARENT, &hIcon);
868     if (hr == S_OK) {
869         result = qt_pixmapFromWinHICON(hIcon);
870         DestroyIcon(hIcon);
871     }
872     imageList->Release();
873 #else
874     Q_UNUSED(iImageList)
875     Q_UNUSED(info)
876 #endif // USE_IIMAGELIST
877     return result;
878 }
879 
880 class QWindowsFileIconEngine : public QAbstractFileIconEngine
881 {
882 public:
QWindowsFileIconEngine(const QFileInfo & info,QPlatformTheme::IconOptions opts)883     explicit QWindowsFileIconEngine(const QFileInfo &info, QPlatformTheme::IconOptions opts) :
884         QAbstractFileIconEngine(info, opts) {}
885 
availableSizes(QIcon::Mode=QIcon::Normal,QIcon::State=QIcon::Off) const886     QList<QSize> availableSizes(QIcon::Mode = QIcon::Normal, QIcon::State = QIcon::Off) const override
887     { return QWindowsTheme::instance()->availableFileIconSizes(); }
888 
889 protected:
890     QString cacheKey() const override;
891     QPixmap filePixmap(const QSize &size, QIcon::Mode mode, QIcon::State) override;
892 };
893 
cacheKey() const894 QString QWindowsFileIconEngine::cacheKey() const
895 {
896     // Cache directories unless custom or drives, which have custom icons depending on type
897     if ((options() & QPlatformTheme::DontUseCustomDirectoryIcons) && fileInfo().isDir() && !fileInfo().isRoot())
898         return QStringLiteral("qt_/directory/");
899     if (!fileInfo().isFile())
900         return QString();
901     // Return "" for .exe, .lnk and .ico extensions.
902     // It is faster to just look at the file extensions;
903     // avoiding slow QFileInfo::isExecutable() (QTBUG-13182)
904     QString suffix = fileInfo().suffix();
905     if (!suffix.compare(u"exe", Qt::CaseInsensitive)
906         || !suffix.compare(u"lnk", Qt::CaseInsensitive)
907         || !suffix.compare(u"ico", Qt::CaseInsensitive)) {
908         return QString();
909     }
910     return QLatin1String("qt_.")
911         + (suffix.isEmpty() ? fileInfo().fileName() : std::move(suffix).toUpper()); // handle "Makefile"                                    ;)
912 }
913 
filePixmap(const QSize & size,QIcon::Mode,QIcon::State)914 QPixmap QWindowsFileIconEngine::filePixmap(const QSize &size, QIcon::Mode, QIcon::State)
915 {
916     /* We don't use the variable, but by storing it statically, we
917      * ensure CoInitialize is only called once. */
918     static HRESULT comInit = CoInitialize(nullptr);
919     Q_UNUSED(comInit);
920 
921     static QCache<QString, FakePointer<int> > dirIconEntryCache(1000);
922     static QMutex mx;
923     static int defaultFolderIIcon = -1;
924     const bool useDefaultFolderIcon = options() & QPlatformTheme::DontUseCustomDirectoryIcons;
925 
926     QPixmap pixmap;
927     const QString filePath = QDir::toNativeSeparators(fileInfo().filePath());
928     const int width = int(size.width());
929     const int iconSize = width > fileIconSizes[SmallFileIcon] ? SHGFI_LARGEICON : SHGFI_SMALLICON;
930     const int requestedImageListSize =
931 #ifdef USE_IIMAGELIST
932         width > fileIconSizes[ExtraLargeFileIcon]
933             ? sHIL_JUMBO
934             : (width > fileIconSizes[LargeFileIcon] ? sHIL_EXTRALARGE : 0);
935 #else
936         0;
937 #endif // !USE_IIMAGELIST
938     bool cacheableDirIcon = fileInfo().isDir() && !fileInfo().isRoot();
939     if (cacheableDirIcon) {
940         QMutexLocker locker(&mx);
941         int iIcon = (useDefaultFolderIcon && defaultFolderIIcon >= 0) ? defaultFolderIIcon
942                                                                       : **dirIconEntryCache.object(filePath);
943         if (iIcon) {
944             QPixmapCache::find(dirIconPixmapCacheKey(iIcon, iconSize, requestedImageListSize),
945                                &pixmap);
946             if (pixmap.isNull()) // Let's keep both caches in sync
947                 dirIconEntryCache.remove(filePath);
948             else
949                 return pixmap;
950         }
951     }
952 
953     SHFILEINFO info;
954     unsigned int flags = SHGFI_ICON | iconSize | SHGFI_SYSICONINDEX | SHGFI_ADDOVERLAYS | SHGFI_OVERLAYINDEX;
955     DWORD attributes = 0;
956     QString path = filePath;
957     if (cacheableDirIcon && useDefaultFolderIcon) {
958         flags |= SHGFI_USEFILEATTRIBUTES;
959         attributes |= FILE_ATTRIBUTE_DIRECTORY;
960         path = QStringLiteral("dummy");
961     } else if (!fileInfo().exists()) {
962         flags |= SHGFI_USEFILEATTRIBUTES;
963         attributes |= FILE_ATTRIBUTE_NORMAL;
964     }
965     const bool val = shGetFileInfoBackground(path, attributes, &info, flags);
966 
967     // Even if GetFileInfo returns a valid result, hIcon can be empty in some cases
968     if (val && info.hIcon) {
969         QString key;
970         if (cacheableDirIcon) {
971             if (useDefaultFolderIcon && defaultFolderIIcon < 0)
972                 defaultFolderIIcon = info.iIcon;
973 
974             //using the unique icon index provided by windows save us from duplicate keys
975             key = dirIconPixmapCacheKey(info.iIcon, iconSize, requestedImageListSize);
976             QPixmapCache::find(key, &pixmap);
977             if (!pixmap.isNull()) {
978                 QMutexLocker locker(&mx);
979                 dirIconEntryCache.insert(filePath, FakePointer<int>::create(info.iIcon));
980             }
981         }
982 
983         if (pixmap.isNull()) {
984             if (requestedImageListSize) {
985                 pixmap = pixmapFromShellImageList(requestedImageListSize, info);
986                 if (pixmap.isNull() && requestedImageListSize == sHIL_JUMBO)
987                     pixmap = pixmapFromShellImageList(sHIL_EXTRALARGE, info);
988             }
989             if (pixmap.isNull())
990                 pixmap = qt_pixmapFromWinHICON(info.hIcon);
991             if (!pixmap.isNull()) {
992                 if (cacheableDirIcon) {
993                     QMutexLocker locker(&mx);
994                     QPixmapCache::insert(key, pixmap);
995                     dirIconEntryCache.insert(filePath, FakePointer<int>::create(info.iIcon));
996                 }
997             } else {
998                 qWarning("QWindowsTheme::fileIconPixmap() no icon found");
999             }
1000         }
1001         DestroyIcon(info.hIcon);
1002     }
1003 
1004     return pixmap;
1005 }
1006 
fileIcon(const QFileInfo & fileInfo,QPlatformTheme::IconOptions iconOptions) const1007 QIcon QWindowsTheme::fileIcon(const QFileInfo &fileInfo, QPlatformTheme::IconOptions iconOptions) const
1008 {
1009     return QIcon(new QWindowsFileIconEngine(fileInfo, iconOptions));
1010 }
1011 
doUseNativeMenus()1012 static inline bool doUseNativeMenus()
1013 {
1014     const unsigned options = QWindowsIntegration::instance()->options();
1015     if ((options & QWindowsIntegration::NoNativeMenus) != 0)
1016         return false;
1017     if ((options & QWindowsIntegration::AlwaysUseNativeMenus) != 0)
1018         return true;
1019     // "Auto" mode: For non-widget or Quick Controls 2 applications
1020     if (!QCoreApplication::instance()->inherits("QApplication"))
1021         return true;
1022     const QWindowList &topLevels = QGuiApplication::topLevelWindows();
1023     for (const QWindow *t : topLevels) {
1024         if (t->inherits("QQuickApplicationWindow"))
1025             return true;
1026     }
1027     return false;
1028 }
1029 
useNativeMenus()1030 bool QWindowsTheme::useNativeMenus()
1031 {
1032     static const bool result = doUseNativeMenus();
1033     return result;
1034 }
1035 
queryDarkMode()1036 bool QWindowsTheme::queryDarkMode()
1037 {
1038     if (QOperatingSystemVersion::current()
1039         < QOperatingSystemVersion(QOperatingSystemVersion::Windows, 10, 0, 17763)
1040         || queryHighContrast()) {
1041         return false;
1042     }
1043     const auto setting = QWinRegistryKey(HKEY_CURRENT_USER, LR"(Software\Microsoft\Windows\CurrentVersion\Themes\Personalize)")
1044                          .dwordValue(L"AppsUseLightTheme");
1045     return setting.second && setting.first == 0;
1046 }
1047 
queryHighContrast()1048 bool QWindowsTheme::queryHighContrast()
1049 {
1050     return booleanSystemParametersInfo(SPI_GETHIGHCONTRAST, false);
1051 }
1052 
createPlatformMenuItem() const1053 QPlatformMenuItem *QWindowsTheme::createPlatformMenuItem() const
1054 {
1055     qCDebug(lcQpaMenus) << __FUNCTION__;
1056     return QWindowsTheme::useNativeMenus() ? new QWindowsMenuItem : nullptr;
1057 }
1058 
createPlatformMenu() const1059 QPlatformMenu *QWindowsTheme::createPlatformMenu() const
1060 {
1061     qCDebug(lcQpaMenus) << __FUNCTION__;
1062     // We create a popup menu here, since it will likely be used as context
1063     // menu. Submenus should be created the factory functions of
1064     // QPlatformMenu/Bar. Note though that Quick Controls 1 will use this
1065     // function for submenus as well, but this has been found to work.
1066     return QWindowsTheme::useNativeMenus() ? new QWindowsPopupMenu : nullptr;
1067 }
1068 
createPlatformMenuBar() const1069 QPlatformMenuBar *QWindowsTheme::createPlatformMenuBar() const
1070 {
1071     qCDebug(lcQpaMenus) << __FUNCTION__;
1072     return QWindowsTheme::useNativeMenus() ? new QWindowsMenuBar : nullptr;
1073 }
1074 
showPlatformMenuBar()1075 void QWindowsTheme::showPlatformMenuBar()
1076 {
1077     qCDebug(lcQpaMenus) << __FUNCTION__;
1078 }
1079 
1080 QT_END_NAMESPACE
1081