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(¶ms, 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