diff options
Diffstat (limited to 'src/plugins/platforms/windows/qwindowstheme.cpp')
-rw-r--r-- | src/plugins/platforms/windows/qwindowstheme.cpp | 512 |
1 files changed, 282 insertions, 230 deletions
diff --git a/src/plugins/platforms/windows/qwindowstheme.cpp b/src/plugins/platforms/windows/qwindowstheme.cpp index 2d97b718ba..b6017c7692 100644 --- a/src/plugins/platforms/windows/qwindowstheme.cpp +++ b/src/plugins/platforms/windows/qwindowstheme.cpp @@ -7,26 +7,29 @@ #include "qwindowsmenu.h" #include "qwindowsdialoghelpers.h" #include "qwindowscontext.h" +#include "qwindowsiconengine.h" #include "qwindowsintegration.h" #if QT_CONFIG(systemtrayicon) # include "qwindowssystemtrayicon.h" #endif #include "qwindowsscreen.h" +#include "qwindowswindow.h" #include <commctrl.h> #include <objbase.h> -#ifndef Q_CC_MINGW -# include <commoncontrols.h> -#endif +#include <commoncontrols.h> #include <shellapi.h> +#include <QtCore/qapplicationstatic.h> #include <QtCore/qvariant.h> #include <QtCore/qcoreapplication.h> #include <QtCore/qdebug.h> #include <QtCore/qsysinfo.h> #include <QtCore/qcache.h> #include <QtCore/qthread.h> +#include <QtCore/qqueue.h> #include <QtCore/qmutex.h> #include <QtCore/qwaitcondition.h> +#include <QtCore/qoperatingsystemversion.h> #include <QtGui/qcolor.h> #include <QtGui/qpalette.h> #include <QtGui/qguiapplication.h> @@ -48,10 +51,6 @@ # include <winrt/Windows.UI.ViewManagement.h> #endif // QT_CONFIG(cpp_winrt) -#if defined(__IImageList_INTERFACE_DEFINED__) && defined(__IID_DEFINED__) -# define USE_IIMAGELIST -#endif - QT_BEGIN_NAMESPACE using namespace Qt::StringLiterals; @@ -79,11 +78,11 @@ static inline QColor mixColors(const QColor &c1, const QColor &c2) (c1.blue() + c2.blue()) / 2}; } -static inline QColor getSysColor(int index) -{ - COLORREF cr = GetSysColor(index); - return QColor(GetRValue(cr), GetGValue(cr), GetBValue(cr)); -} +enum AccentColorLevel { + AccentColorDarkest, + AccentColorNormal, + AccentColorLightest +}; #if QT_CONFIG(cpp_winrt) static constexpr QColor getSysColor(winrt::Windows::UI::Color &&color) @@ -92,111 +91,146 @@ static constexpr QColor getSysColor(winrt::Windows::UI::Color &&color) } #endif -// QTBUG-48823/Windows 10: SHGetFileInfo() (as called by item views on file system -// models has been observed to trigger a WM_PAINT on the mainwindow. Suppress the -// behavior by running it in a thread. +[[maybe_unused]] [[nodiscard]] static inline QColor qt_accentColor(AccentColorLevel level) +{ +#if QT_CONFIG(cpp_winrt) + using namespace winrt::Windows::UI::ViewManagement; + const auto settings = UISettings(); + const QColor accent = getSysColor(settings.GetColorValue(UIColorType::Accent)); + const QColor accentLight = getSysColor(settings.GetColorValue(UIColorType::AccentLight1)); + const QColor accentDarkest = getSysColor(settings.GetColorValue(UIColorType::AccentDark3)); +#else + const QWinRegistryKey registry(HKEY_CURRENT_USER, LR"(Software\Microsoft\Windows\DWM)"); + if (!registry.isValid()) + return {}; + const QVariant value = registry.value(L"AccentColor"); + if (!value.isValid()) + return {}; + // The retrieved value is in the #AABBGGRR format, we need to + // convert it to the #AARRGGBB format which Qt expects. + const QColor abgr = QColor::fromRgba(qvariant_cast<DWORD>(value)); + if (!abgr.isValid()) + return {}; + const QColor accent = QColor::fromRgb(abgr.blue(), abgr.green(), abgr.red(), abgr.alpha()); + const QColor accentLight = accent.lighter(120); + const QColor accentDarkest = accent.darker(120 * 120 * 120); +#endif + if (level == AccentColorDarkest) + return accentDarkest; + else if (level == AccentColorLightest) + return accentLight; + return accent; +} -struct QShGetFileInfoParams +static inline QColor getSysColor(int index) { - QShGetFileInfoParams(const QString &fn, DWORD a, SHFILEINFO *i, UINT f, bool *r) - : fileName(fn), attributes(a), flags(f), info(i), result(r) - { } - - const QString &fileName; - const DWORD attributes; - const UINT flags; - SHFILEINFO *const info; - bool *const result; -}; + COLORREF cr = GetSysColor(index); + return QColor(GetRValue(cr), GetGValue(cr), GetBValue(cr)); +} +// QTBUG-48823/Windows 10: SHGetFileInfo() (as called by item views on file system +// models has been observed to trigger a WM_PAINT on the mainwindow. Suppress the +// behavior by running it in a thread. class QShGetFileInfoThread : public QThread { public: - explicit QShGetFileInfoThread() - : QThread(), m_params(nullptr) + struct Task + { + Task(const QString &fn, DWORD a, UINT f) + : fileName(fn), attributes(a), flags(f) + {} + Q_DISABLE_COPY(Task) + ~Task() + { + DestroyIcon(hIcon); + hIcon = 0; + } + // Request + const QString fileName; + const DWORD attributes; + const UINT flags; + // Result + HICON hIcon = 0; + int iIcon = -1; + bool finished = false; + bool resultValid() const { return hIcon != 0 && iIcon >= 0 && finished; } + }; + + QShGetFileInfoThread() + : QThread() { - connect(this, &QThread::finished, this, &QObject::deleteLater); + start(); + } + + ~QShGetFileInfoThread() + { + cancel(); + wait(); + } + + QSharedPointer<Task> getNextTask() + { + QMutexLocker l(&m_waitForTaskMutex); + while (!isInterruptionRequested()) { + if (!m_taskQueue.isEmpty()) + return m_taskQueue.dequeue(); + m_waitForTaskCondition.wait(&m_waitForTaskMutex); + } + return nullptr; } void run() override { QComHelper comHelper(COINIT_MULTITHREADED); - QMutexLocker readyLocker(&m_readyMutex); - while (!m_cancelled.loadRelaxed()) { - if (!m_params && !m_cancelled.loadRelaxed() - && !m_readyCondition.wait(&m_readyMutex, QDeadlineTimer(1000ll))) - continue; - - if (m_params) { - const QString fileName = m_params->fileName; + while (!isInterruptionRequested()) { + auto task = getNextTask(); + if (task) { SHFILEINFO info; - const bool result = SHGetFileInfo(reinterpret_cast<const wchar_t *>(fileName.utf16()), - m_params->attributes, &info, sizeof(SHFILEINFO), - m_params->flags); - m_doneMutex.lock(); - if (!m_cancelled.loadRelaxed()) { - *m_params->result = result; - memcpy(m_params->info, &info, sizeof(SHFILEINFO)); + const bool result = SHGetFileInfo(reinterpret_cast<const wchar_t *>(task->fileName.utf16()), + task->attributes, &info, sizeof(SHFILEINFO), + task->flags); + if (result) { + task->hIcon = info.hIcon; + task->iIcon = info.iIcon; } - m_params = nullptr; - + task->finished = true; m_doneCondition.wakeAll(); - m_doneMutex.unlock(); } } } - bool runWithParams(QShGetFileInfoParams *params, qint64 timeOutMSecs) + void runWithParams(const QSharedPointer<Task> &task, + std::chrono::milliseconds timeout = std::chrono::milliseconds(5000)) { - QMutexLocker doneLocker(&m_doneMutex); - - m_readyMutex.lock(); - m_params = params; - m_readyCondition.wakeAll(); - m_readyMutex.unlock(); + { + QMutexLocker l(&m_waitForTaskMutex); + m_taskQueue.enqueue(task); + m_waitForTaskCondition.wakeAll(); + } - return m_doneCondition.wait(&m_doneMutex, QDeadlineTimer(timeOutMSecs)); + QMutexLocker doneLocker(&m_doneMutex); + while (!task->finished && !isInterruptionRequested()) { + if (!m_doneCondition.wait(&m_doneMutex, QDeadlineTimer(timeout))) + return; + } } void cancel() { - QMutexLocker doneLocker(&m_doneMutex); - m_cancelled.storeRelaxed(1); - m_readyCondition.wakeAll(); + requestInterruption(); + m_doneCondition.wakeAll(); + m_waitForTaskCondition.wakeAll(); } private: - QShGetFileInfoParams *m_params; - QAtomicInt m_cancelled; - QWaitCondition m_readyCondition; + QQueue<QSharedPointer<Task>> m_taskQueue; QWaitCondition m_doneCondition; - QMutex m_readyMutex; + QWaitCondition m_waitForTaskCondition; QMutex m_doneMutex; + QMutex m_waitForTaskMutex; }; - -static bool shGetFileInfoBackground(const QString &fileName, DWORD attributes, - SHFILEINFO *info, UINT flags, - qint64 timeOutMSecs = 5000) -{ - static QShGetFileInfoThread *getFileInfoThread = nullptr; - if (!getFileInfoThread) { - getFileInfoThread = new QShGetFileInfoThread; - getFileInfoThread->start(); - } - - bool result = false; - QShGetFileInfoParams params(fileName, attributes, info, flags, &result); - if (!getFileInfoThread->runWithParams(¶ms, timeOutMSecs)) { - // Cancel and reset getFileInfoThread. It'll - // be reinitialized the next time we get called. - getFileInfoThread->cancel(); - getFileInfoThread = nullptr; - qWarning().noquote() << "SHGetFileInfo() timed out for " << fileName; - return false; - } - return result; -} +Q_APPLICATION_STATIC(QShGetFileInfoThread, s_shGetFileInfoThread) // from QStyle::standardPalette static inline QPalette standardPalette() @@ -221,22 +255,6 @@ static QColor placeHolderColor(QColor textColor) return textColor; } -[[maybe_unused]] [[nodiscard]] static inline QColor qt_accentColor() -{ - const QWinRegistryKey registry(HKEY_CURRENT_USER, LR"(Software\Microsoft\Windows\DWM)"); - if (!registry.isValid()) - return {}; - const QVariant value = registry.value(L"AccentColor"); - if (!value.isValid()) - return {}; - // The retrieved value is in the #AABBGGRR format, we need to - // convert it to the #AARRGGBB format which Qt expects. - const QColor abgr = QColor::fromRgba(qvariant_cast<DWORD>(value)); - if (!abgr.isValid()) - return {}; - return QColor::fromRgb(abgr.blue(), abgr.green(), abgr.red(), abgr.alpha()); -} - /* This is used when the theme is light mode, and when the theme is dark but the application doesn't support dark mode. In the latter case, we need to check. @@ -246,17 +264,8 @@ void QWindowsTheme::populateLightSystemBasePalette(QPalette &result) const QColor background = getSysColor(COLOR_BTNFACE); const QColor textColor = getSysColor(COLOR_WINDOWTEXT); -#if QT_CONFIG(cpp_winrt) - // respect the Windows 11 accent color - using namespace winrt::Windows::UI::ViewManagement; - const auto settings = UISettings(); - - const QColor accent = getSysColor(settings.GetColorValue(UIColorType::Accent)); - const QColor accentDarkest = getSysColor(settings.GetColorValue(UIColorType::AccentDark3)); -#else - const QColor accent = qt_accentColor(); - const QColor accentDarkest = accent.darker(120 * 120 * 120); -#endif + const QColor accent = qt_accentColor(AccentColorNormal); + const QColor accentDarkest = qt_accentColor(AccentColorDarkest); const QColor linkColor = accent; const QColor btnFace = background; @@ -277,7 +286,7 @@ void QWindowsTheme::populateLightSystemBasePalette(QPalette &result) result.setColor(QPalette::Midlight, getSysColor(COLOR_3DLIGHT)); result.setColor(QPalette::Shadow, getSysColor(COLOR_3DDKSHADOW)); result.setColor(QPalette::HighlightedText, getSysColor(COLOR_HIGHLIGHTTEXT)); - result.setColor(QPalette::AccentColor, accent); + result.setColor(QPalette::Accent, accent); result.setColor(QPalette::Link, linkColor); result.setColor(QPalette::LinkVisited, accentDarkest); @@ -290,8 +299,11 @@ void QWindowsTheme::populateLightSystemBasePalette(QPalette &result) result.setColor(QPalette::Midlight, result.button().color().lighter(110)); } -static void populateDarkSystemBasePalette(QPalette &result) +void QWindowsTheme::populateDarkSystemBasePalette(QPalette &result) { + QColor foreground, background, + accent, accentDark, accentDarker, accentDarkest, + accentLight, accentLighter, accentLightest; #if QT_CONFIG(cpp_winrt) using namespace winrt::Windows::UI::ViewManagement; const auto settings = UISettings(); @@ -299,32 +311,37 @@ static void populateDarkSystemBasePalette(QPalette &result) // We have to craft a palette from these colors. The settings.UIElementColor(UIElementType) API // returns the old system colors, not the dark mode colors. If the background is black (which it // usually), then override it with a dark gray instead so that we can go up and down the lightness. - const QColor foreground = getSysColor(settings.GetColorValue(UIColorType::Foreground)); - const QColor background = [&settings]() -> QColor { - auto systemBackground = getSysColor(settings.GetColorValue(UIColorType::Background)); - if (systemBackground == Qt::black) - systemBackground = QColor(0x1E, 0x1E, 0x1E); - return systemBackground; - }(); - - const QColor accent = getSysColor(settings.GetColorValue(UIColorType::Accent)); - const QColor accentDark = getSysColor(settings.GetColorValue(UIColorType::AccentDark1)); - const QColor accentDarker = getSysColor(settings.GetColorValue(UIColorType::AccentDark2)); - const QColor accentDarkest = getSysColor(settings.GetColorValue(UIColorType::AccentDark3)); - const QColor accentLight = getSysColor(settings.GetColorValue(UIColorType::AccentLight1)); - const QColor accentLighter = getSysColor(settings.GetColorValue(UIColorType::AccentLight2)); - const QColor accentLightest = getSysColor(settings.GetColorValue(UIColorType::AccentLight3)); -#else - const QColor foreground = Qt::white; - const QColor background = QColor(0x1E, 0x1E, 0x1E); - const QColor accent = qt_accentColor(); - const QColor accentDark = accent.darker(120); - const QColor accentDarker = accentDark.darker(120); - const QColor accentDarkest = accentDarker.darker(120); - const QColor accentLight = accent.lighter(120); - const QColor accentLighter = accentLight.lighter(120); - const QColor accentLightest = accentLighter.lighter(120); + if (QWindowsTheme::queryColorScheme() == Qt::ColorScheme::Dark) { + // the system is actually running in dark mode, so UISettings will give us dark colors + foreground = getSysColor(settings.GetColorValue(UIColorType::Foreground)); + background = [&settings]() -> QColor { + auto systemBackground = getSysColor(settings.GetColorValue(UIColorType::Background)); + if (systemBackground == Qt::black) + systemBackground = QColor(0x1E, 0x1E, 0x1E); + return systemBackground; + }(); + + accent = getSysColor(settings.GetColorValue(UIColorType::Accent)); + accentDark = getSysColor(settings.GetColorValue(UIColorType::AccentDark1)); + accentDarker = getSysColor(settings.GetColorValue(UIColorType::AccentDark2)); + accentDarkest = getSysColor(settings.GetColorValue(UIColorType::AccentDark3)); + accentLight = getSysColor(settings.GetColorValue(UIColorType::AccentLight1)); + accentLighter = getSysColor(settings.GetColorValue(UIColorType::AccentLight2)); + accentLightest = getSysColor(settings.GetColorValue(UIColorType::AccentLight3)); + } else #endif + { + // If the system is running in light mode, then we need to make up our own dark palette + foreground = Qt::white; + background = QColor(0x1E, 0x1E, 0x1E); + accent = qt_accentColor(AccentColorNormal); + accentDark = accent.darker(120); + accentDarker = accentDark.darker(120); + accentDarkest = accentDarker.darker(120); + accentLight = accent.lighter(120); + accentLighter = accentLight.lighter(120); + accentLightest = accentLighter.lighter(120); + } const QColor linkColor = accent; const QColor buttonColor = background.lighter(200); @@ -351,40 +368,7 @@ static void populateDarkSystemBasePalette(QPalette &result) result.setColor(QPalette::All, QPalette::ToolTipBase, buttonColor); result.setColor(QPalette::All, QPalette::ToolTipText, foreground.darker(120)); result.setColor(QPalette::All, QPalette::PlaceholderText, placeHolderColor(foreground)); - result.setColor(QPalette::All, QPalette::AccentColor, accent); -} - -static QPalette systemPalette(bool light) -{ - QPalette result = standardPalette(); - if (light) - QWindowsTheme::populateLightSystemBasePalette(result); - else - populateDarkSystemBasePalette(result); - - if (result.window() != result.base()) { - result.setColor(QPalette::Inactive, QPalette::Highlight, - result.color(QPalette::Inactive, QPalette::Window)); - result.setColor(QPalette::Inactive, QPalette::HighlightedText, - result.color(QPalette::Inactive, QPalette::Text)); - result.setColor(QPalette::Inactive, QPalette::AccentColor, - result.color(QPalette::Inactive, QPalette::Window)); - } - - const QColor disabled = mixColors(result.windowText().color(), result.button().color()); - - result.setColorGroup(QPalette::Disabled, result.windowText(), result.button(), - result.light(), result.dark(), result.mid(), - result.text(), result.brightText(), result.base(), - result.window()); - result.setColor(QPalette::Disabled, QPalette::WindowText, disabled); - result.setColor(QPalette::Disabled, QPalette::Text, disabled); - result.setColor(QPalette::Disabled, QPalette::ButtonText, disabled); - result.setColor(QPalette::Disabled, QPalette::Highlight, result.color(QPalette::Highlight)); - result.setColor(QPalette::Disabled, QPalette::HighlightedText, result.color(QPalette::HighlightedText)); - result.setColor(QPalette::Disabled, QPalette::AccentColor, disabled); - result.setColor(QPalette::Disabled, QPalette::Base, result.window().color()); - return result; + result.setColor(QPalette::All, QPalette::Accent, accent); } static inline QPalette toolTipPalette(const QPalette &systemPalette, bool light) @@ -476,6 +460,7 @@ QWindowsTheme *QWindowsTheme::m_instance = nullptr; QWindowsTheme::QWindowsTheme() { m_instance = this; + s_colorScheme = QWindowsTheme::queryColorScheme(); std::fill(m_fonts, m_fonts + NFonts, nullptr); std::fill(m_palettes, m_palettes + NPalettes, nullptr); refresh(); @@ -497,7 +482,10 @@ static inline QStringList iconThemeSearchPaths() static inline QStringList styleNames() { - return { QStringLiteral("WindowsVista"), QStringLiteral("Windows") }; + QStringList styles = { QStringLiteral("WindowsVista"), QStringLiteral("Windows") }; + if (QOperatingSystemVersion::current() >= QOperatingSystemVersion::Windows11) + styles.prepend(QStringLiteral("Windows11")); + return styles; } static inline int uiEffects() @@ -562,7 +550,43 @@ QVariant QWindowsTheme::themeHint(ThemeHint hint) const Qt::ColorScheme QWindowsTheme::colorScheme() const { - return QWindowsContext::isDarkMode() ? Qt::ColorScheme::Dark : Qt::ColorScheme::Light; + return QWindowsTheme::effectiveColorScheme(); +} + +Qt::ColorScheme QWindowsTheme::effectiveColorScheme() +{ + if (queryHighContrast()) + return Qt::ColorScheme::Unknown; + if (s_colorSchemeOverride != Qt::ColorScheme::Unknown) + return s_colorSchemeOverride; + if (s_colorScheme != Qt::ColorScheme::Unknown) + return s_colorScheme; + return queryColorScheme(); +} + +void QWindowsTheme::requestColorScheme(Qt::ColorScheme scheme) +{ + s_colorSchemeOverride = scheme; + handleSettingsChanged(); +} + +void QWindowsTheme::handleSettingsChanged() +{ + const auto oldColorScheme = s_colorScheme; + s_colorScheme = Qt::ColorScheme::Unknown; // make effectiveColorScheme() query registry + const auto newColorScheme = effectiveColorScheme(); + const bool colorSchemeChanged = newColorScheme != oldColorScheme; + s_colorScheme = newColorScheme; + auto integration = QWindowsIntegration::instance(); + integration->updateApplicationBadge(); + if (integration->darkModeHandling().testFlag(QWindowsApplication::DarkModeStyle)) { + QWindowsTheme::instance()->refresh(); + QWindowSystemInterface::handleThemeChange<QWindowSystemInterface::SynchronousDelivery>(); + } + if (colorSchemeChanged) { + for (QWindowsWindow *w : std::as_const(QWindowsContext::instance()->windows())) + w->setDarkBorder(s_colorScheme == Qt::ColorScheme::Dark); + } } void QWindowsTheme::clearPalettes() @@ -576,33 +600,63 @@ void QWindowsTheme::refreshPalettes() if (!QGuiApplication::desktopSettingsAware()) return; const bool light = - !QWindowsContext::isDarkMode() + effectiveColorScheme() != Qt::ColorScheme::Dark || !QWindowsIntegration::instance()->darkModeHandling().testFlag(QWindowsApplication::DarkModeStyle); clearPalettes(); - m_palettes[SystemPalette] = new QPalette(systemPalette(light)); + m_palettes[SystemPalette] = new QPalette(QWindowsTheme::systemPalette(s_colorScheme)); m_palettes[ToolTipPalette] = new QPalette(toolTipPalette(*m_palettes[SystemPalette], light)); m_palettes[MenuPalette] = new QPalette(menuPalette(*m_palettes[SystemPalette], light)); m_palettes[MenuBarPalette] = menuBarPalette(*m_palettes[MenuPalette], light); if (!light) { -#if QT_CONFIG(cpp_winrt) - using namespace winrt::Windows::UI::ViewManagement; - const auto settings = UISettings(); - const QColor accent = getSysColor(settings.GetColorValue(UIColorType::Accent)); - const QColor accentLight = getSysColor(settings.GetColorValue(UIColorType::AccentLight1)); - const QColor accentDarkest = getSysColor(settings.GetColorValue(UIColorType::AccentDark3)); -#else - const QColor accent = qt_accentColor(); - const QColor accentLight = accent.lighter(120); - const QColor accentDarkest = accent.darker(120 * 120 * 120); -#endif m_palettes[CheckBoxPalette] = new QPalette(*m_palettes[SystemPalette]); - m_palettes[CheckBoxPalette]->setColor(QPalette::Active, QPalette::Base, accent); - m_palettes[CheckBoxPalette]->setColor(QPalette::Active, QPalette::Button, accentLight); - m_palettes[CheckBoxPalette]->setColor(QPalette::Inactive, QPalette::Base, accentDarkest); + m_palettes[CheckBoxPalette]->setColor(QPalette::Active, QPalette::Base, qt_accentColor(AccentColorNormal)); + m_palettes[CheckBoxPalette]->setColor(QPalette::Active, QPalette::Button, qt_accentColor(AccentColorLightest)); + m_palettes[CheckBoxPalette]->setColor(QPalette::Inactive, QPalette::Base, qt_accentColor(AccentColorDarkest)); m_palettes[RadioButtonPalette] = new QPalette(*m_palettes[CheckBoxPalette]); } } +QPalette QWindowsTheme::systemPalette(Qt::ColorScheme colorScheme) +{ + QPalette result = standardPalette(); + + switch (colorScheme) { + case Qt::ColorScheme::Unknown: + // when a high-contrast theme is active or when we fail to read, assume light + Q_FALLTHROUGH(); + case Qt::ColorScheme::Light: + populateLightSystemBasePalette(result); + break; + case Qt::ColorScheme::Dark: + populateDarkSystemBasePalette(result); + break; + } + + if (result.window() != result.base()) { + result.setColor(QPalette::Inactive, QPalette::Highlight, + result.color(QPalette::Inactive, QPalette::Window)); + result.setColor(QPalette::Inactive, QPalette::HighlightedText, + result.color(QPalette::Inactive, QPalette::Text)); + result.setColor(QPalette::Inactive, QPalette::Accent, + result.color(QPalette::Inactive, QPalette::Window)); + } + + const QColor disabled = mixColors(result.windowText().color(), result.button().color()); + + result.setColorGroup(QPalette::Disabled, result.windowText(), result.button(), + result.light(), result.dark(), result.mid(), + result.text(), result.brightText(), result.base(), + result.window()); + result.setColor(QPalette::Disabled, QPalette::WindowText, disabled); + result.setColor(QPalette::Disabled, QPalette::Text, disabled); + result.setColor(QPalette::Disabled, QPalette::ButtonText, disabled); + result.setColor(QPalette::Disabled, QPalette::Highlight, result.color(QPalette::Highlight)); + result.setColor(QPalette::Disabled, QPalette::HighlightedText, result.color(QPalette::HighlightedText)); + result.setColor(QPalette::Disabled, QPalette::Accent, disabled); + result.setColor(QPalette::Disabled, QPalette::Base, result.window().color()); + return result; +} + void QWindowsTheme::clearFonts() { qDeleteAll(m_fonts, m_fonts + NFonts); @@ -714,11 +768,7 @@ void QWindowsTheme::refreshIconPixmapSizes() fileIconSizes[LargeFileIcon] + fileIconSizes[LargeFileIcon] / 2; fileIconSizes[JumboFileIcon] = 8 * fileIconSizes[LargeFileIcon]; // empirical, has not been observed to work -#ifdef USE_IIMAGELIST int *availEnd = fileIconSizes + JumboFileIcon + 1; -#else - int *availEnd = fileIconSizes + LargeFileIcon + 1; -#endif // USE_IIMAGELIST m_fileIconSizes = QAbstractFileIconEngine::toSizeList(fileIconSizes, availEnd); qCDebug(lcQpaWindow) << __FUNCTION__ << m_fileIconSizes; } @@ -831,15 +881,18 @@ QPixmap QWindowsTheme::standardPixmap(StandardPixmap sp, const QSizeF &pixmapSiz } if (stockId != SIID_INVALID) { - QPixmap pixmap; SHSTOCKICONINFO iconInfo; memset(&iconInfo, 0, sizeof(iconInfo)); iconInfo.cbSize = sizeof(iconInfo); - stockFlags |= (pixmapSize.width() > 16 ? SHGFI_LARGEICON : SHGFI_SMALLICON); - if (SHGetStockIconInfo(stockId, SHGFI_ICON | stockFlags, &iconInfo) == S_OK) { - pixmap = qt_pixmapFromWinHICON(iconInfo.hIcon); - DestroyIcon(iconInfo.hIcon); - return pixmap; + stockFlags |= SHGSI_ICONLOCATION; + if (SHGetStockIconInfo(stockId, stockFlags, &iconInfo) == S_OK) { + const auto iconSize = pixmapSize.width(); + HICON icon; + if (SHDefExtractIcon(iconInfo.szPath, iconInfo.iIcon, 0, &icon, nullptr, iconSize) == S_OK) { + QPixmap pixmap = qt_pixmapFromWinHICON(icon); + DestroyIcon(icon); + return pixmap; + } } } @@ -909,10 +962,9 @@ public: // Shell image list helper functions. -static QPixmap pixmapFromShellImageList(int iImageList, const SHFILEINFO &info) +static QPixmap pixmapFromShellImageList(int iImageList, int iIcon) { QPixmap result; -#ifdef USE_IIMAGELIST // For MinGW: static const IID iID_IImageList = {0x46eb5926, 0x582e, 0x4017, {0x9f, 0xdf, 0xe8, 0x99, 0x8d, 0xaa, 0x9, 0x50}}; @@ -921,16 +973,12 @@ static QPixmap pixmapFromShellImageList(int iImageList, const SHFILEINFO &info) if (hr != S_OK) return result; HICON hIcon; - hr = imageList->GetIcon(info.iIcon, ILD_TRANSPARENT, &hIcon); + hr = imageList->GetIcon(iIcon, ILD_TRANSPARENT, &hIcon); if (hr == S_OK) { result = qt_pixmapFromWinHICON(hIcon); DestroyIcon(hIcon); } imageList->Release(); -#else - Q_UNUSED(iImageList); - Q_UNUSED(info); -#endif // USE_IIMAGELIST return result; } @@ -982,13 +1030,9 @@ QPixmap QWindowsFileIconEngine::filePixmap(const QSize &size, QIcon::Mode, QIcon const int width = int(size.width()); const int iconSize = width > fileIconSizes[SmallFileIcon] ? SHGFI_LARGEICON : SHGFI_SMALLICON; const int requestedImageListSize = -#ifdef USE_IIMAGELIST width > fileIconSizes[ExtraLargeFileIcon] ? sHIL_JUMBO : (width > fileIconSizes[LargeFileIcon] ? sHIL_EXTRALARGE : 0); -#else - 0; -#endif // !USE_IIMAGELIST bool cacheableDirIcon = fileInfo().isDir() && !fileInfo().isRoot(); if (cacheableDirIcon) { QMutexLocker locker(&mx); @@ -1004,7 +1048,6 @@ QPixmap QWindowsFileIconEngine::filePixmap(const QSize &size, QIcon::Mode, QIcon } } - SHFILEINFO info; unsigned int flags = SHGFI_ICON | iconSize | SHGFI_SYSICONINDEX | SHGFI_ADDOVERLAYS | SHGFI_OVERLAYINDEX; DWORD attributes = 0; QString path = filePath; @@ -1016,43 +1059,43 @@ QPixmap QWindowsFileIconEngine::filePixmap(const QSize &size, QIcon::Mode, QIcon flags |= SHGFI_USEFILEATTRIBUTES; attributes |= FILE_ATTRIBUTE_NORMAL; } - const bool val = shGetFileInfoBackground(path, attributes, &info, flags); - + auto task = QSharedPointer<QShGetFileInfoThread::Task>( + new QShGetFileInfoThread::Task(path, attributes, flags)); + s_shGetFileInfoThread()->runWithParams(task); // Even if GetFileInfo returns a valid result, hIcon can be empty in some cases - if (val && info.hIcon) { + if (task->resultValid()) { QString key; if (cacheableDirIcon) { if (useDefaultFolderIcon && defaultFolderIIcon < 0) - defaultFolderIIcon = info.iIcon; + defaultFolderIIcon = task->iIcon; //using the unique icon index provided by windows save us from duplicate keys - key = dirIconPixmapCacheKey(info.iIcon, iconSize, requestedImageListSize); + key = dirIconPixmapCacheKey(task->iIcon, iconSize, requestedImageListSize); QPixmapCache::find(key, &pixmap); if (!pixmap.isNull()) { QMutexLocker locker(&mx); - dirIconEntryCache.insert(filePath, FakePointer<int>::create(info.iIcon)); + dirIconEntryCache.insert(filePath, FakePointer<int>::create(task->iIcon)); } } if (pixmap.isNull()) { if (requestedImageListSize) { - pixmap = pixmapFromShellImageList(requestedImageListSize, info); + pixmap = pixmapFromShellImageList(requestedImageListSize, task->iIcon); if (pixmap.isNull() && requestedImageListSize == sHIL_JUMBO) - pixmap = pixmapFromShellImageList(sHIL_EXTRALARGE, info); + pixmap = pixmapFromShellImageList(sHIL_EXTRALARGE, task->iIcon); } if (pixmap.isNull()) - pixmap = qt_pixmapFromWinHICON(info.hIcon); + pixmap = qt_pixmapFromWinHICON(task->hIcon); if (!pixmap.isNull()) { if (cacheableDirIcon) { QMutexLocker locker(&mx); QPixmapCache::insert(key, pixmap); - dirIconEntryCache.insert(filePath, FakePointer<int>::create(info.iIcon)); + dirIconEntryCache.insert(filePath, FakePointer<int>::create(task->iIcon)); } } else { qWarning("QWindowsTheme::fileIconPixmap() no icon found"); } } - DestroyIcon(info.hIcon); } return pixmap; @@ -1063,6 +1106,11 @@ QIcon QWindowsTheme::fileIcon(const QFileInfo &fileInfo, QPlatformTheme::IconOpt return QIcon(new QWindowsFileIconEngine(fileInfo, iconOptions)); } +QIconEngine *QWindowsTheme::createIconEngine(const QString &iconName) const +{ + return new QWindowsIconEngine(iconName); +} + static inline bool doUseNativeMenus() { const unsigned options = QWindowsIntegration::instance()->options(); @@ -1087,19 +1135,23 @@ bool QWindowsTheme::useNativeMenus() return result; } -bool QWindowsTheme::queryDarkMode() +Qt::ColorScheme QWindowsTheme::queryColorScheme() { - if (queryHighContrast()) { - return false; - } + if (queryHighContrast()) + return Qt::ColorScheme::Unknown; + const auto setting = QWinRegistryKey(HKEY_CURRENT_USER, LR"(Software\Microsoft\Windows\CurrentVersion\Themes\Personalize)") .dwordValue(L"AppsUseLightTheme"); - return setting.second && setting.first == 0; + return setting.second && setting.first == 0 ? Qt::ColorScheme::Dark : Qt::ColorScheme::Light; } bool QWindowsTheme::queryHighContrast() { - return booleanSystemParametersInfo(SPI_GETHIGHCONTRAST, false); + HIGHCONTRAST hcf = {}; + hcf.cbSize = static_cast<UINT>(sizeof(HIGHCONTRAST)); + if (SystemParametersInfo(SPI_GETHIGHCONTRAST, hcf.cbSize, &hcf, FALSE)) + return hcf.dwFlags & HCF_HIGHCONTRASTON; + return false; } QPlatformMenuItem *QWindowsTheme::createPlatformMenuItem() const |