win32: add support for dark theme menu

Signed-off-by: Yuhang Zhao <2546789017@qq.com>
This commit is contained in:
Yuhang Zhao 2022-10-11 16:17:04 +08:00
parent fcf51d6d73
commit b9f5cf79c0
12 changed files with 419 additions and 105 deletions

View File

@ -134,7 +134,7 @@ void Dialog::setupUi()
// with making the window un-resizable: we still want the window be able to resize
// programatically, but we also want the user not able to resize the window manually.
// So apparently we can't use QWidget::setFixedWidth/Height/Size() here.
FramelessWidgetsHelperPrivate::get(helper)->setProperty(FRAMELESSHELPER_BYTEARRAY_LITERAL("FRAMELESSHELPER_DONT_OVERRIDE_CURSOR"), true);
FramelessWidgetsHelperPrivate::get(helper)->setProperty(kDontOverrideCursorVar, true);
connect(helper, &FramelessWidgetsHelper::ready, this, [this, helper](){
const QScopedPointer<QSettings> settings(appConfigFile());
const QByteArray data = settings->value(kIniKeyPath).toByteArray();

View File

@ -58,8 +58,8 @@
# define _WIN32_WINNT_WIN10 0x0A00
#endif
#ifndef NTDDI_WIN10_CO
# define NTDDI_WIN10_CO 0x0A00000B
#ifndef NTDDI_WIN10_NI
# define NTDDI_WIN10_NI 0x0A00000C
#endif
#ifndef WINVER
@ -71,7 +71,7 @@
#endif
#ifndef NTDDI_VERSION
# define NTDDI_VERSION NTDDI_WIN10_CO
# define NTDDI_VERSION NTDDI_WIN10_NI
#endif
#include <windows.h>
@ -375,9 +375,6 @@ using PWINDOWCOMPOSITIONATTRIBDATA = WINDOWCOMPOSITIONATTRIBDATA *;
using NPWINDOWCOMPOSITIONATTRIBDATA = WINDOWCOMPOSITIONATTRIBDATA NEAR *;
using LPWINDOWCOMPOSITIONATTRIBDATA = WINDOWCOMPOSITIONATTRIBDATA FAR *;
using GetWindowCompositionAttributePtr = BOOL(WINAPI *)(HWND, PWINDOWCOMPOSITIONATTRIBDATA);
using SetWindowCompositionAttributePtr = BOOL(WINAPI *)(HWND, PWINDOWCOMPOSITIONATTRIBDATA);
using _WINDOWTHEMEATTRIBUTETYPE = enum _WINDOWTHEMEATTRIBUTETYPE
{
_WTA_NONCLIENT = 1
@ -390,6 +387,37 @@ using WTA_OPTIONS2 = struct WTA_OPTIONS2
};
using PWTA_OPTIONS2 = WTA_OPTIONS2 *;
using IMMERSIVE_HC_CACHE_MODE = enum IMMERSIVE_HC_CACHE_MODE
{
IHCM_USE_CACHED_VALUE = 0,
IHCM_REFRESH = 1
};
using PREFERRED_APP_MODE = enum PREFERRED_APP_MODE
{
PAM_DEFAULT = 0,
PAM_ALLOW_DARK = 1,
PAM_FORCE_DARK = 2,
PAM_FORCE_LIGHT = 3,
PAM_MAX = 4
};
using GetWindowCompositionAttributePtr = BOOL(WINAPI *)(HWND, PWINDOWCOMPOSITIONATTRIBDATA);
using SetWindowCompositionAttributePtr = BOOL(WINAPI *)(HWND, PWINDOWCOMPOSITIONATTRIBDATA);
// Win10 1809 (10.0.17763)
using ShouldAppsUseDarkModePtr = BOOL(WINAPI *)(VOID); // Ordinal 132
using AllowDarkModeForWindowPtr = BOOL(WINAPI *)(HWND, BOOL); // Ordinal 133
using AllowDarkModeForAppPtr = BOOL(WINAPI *)(BOOL); // Ordinal 135
using FlushMenuThemesPtr = VOID(WINAPI *)(VOID); // Ordinal 136
using RefreshImmersiveColorPolicyStatePtr = VOID(WINAPI *)(VOID); // Ordinal 104
using IsDarkModeAllowedForWindowPtr = BOOL(WINAPI *)(HWND); // Ordinal 137
using GetIsImmersiveColorUsingHighContrastPtr = BOOL(WINAPI *)(IMMERSIVE_HC_CACHE_MODE); // Ordinal 106
using OpenNcThemeDataPtr = HTHEME(WINAPI *)(HWND, LPCWSTR); // Ordinal 49
// Win10 1903 (10.0.18362)
using ShouldSystemUseDarkModePtr = BOOL(WINAPI *)(VOID); // Ordinal 138
using SetPreferredAppModePtr = PREFERRED_APP_MODE(WINAPI *)(PREFERRED_APP_MODE); // Ordinal 135
using IsDarkModeAllowedForAppPtr = BOOL(WINAPI *)(VOID); // Ordinal 139
EXTERN_C_START
DECLSPEC_IMPORT MMRESULT WINAPI

View File

@ -235,6 +235,11 @@ Q_NAMESPACE_EXPORT(FRAMELESSHELPER_CORE_API)
[[maybe_unused]] inline Q_CONSTEXPR2 const QColor kDefaultSystemButtonBackgroundColor = {204, 204, 204}; // #CCCCCC
[[maybe_unused]] inline Q_CONSTEXPR2 const QColor kDefaultSystemCloseButtonBackgroundColor = {232, 17, 35}; // #E81123
[[maybe_unused]] inline const QByteArray kDontOverrideCursorVar
= FRAMELESSHELPER_BYTEARRAY_LITERAL("FRAMELESSHELPER_DONT_OVERRIDE_CURSOR");
[[maybe_unused]] inline const QByteArray kDontToggleMaximizeVar
= FRAMELESSHELPER_BYTEARRAY_LITERAL("FRAMELESSHELPER_DONT_TOGGLE_MAXIMIZE");
enum class Option
{
UseCrossPlatformQtImplementation = 0,
@ -373,10 +378,10 @@ Q_ENUM_NS(RegistryRootKey)
enum class WindowEdge
{
Unspecified = 0x00000000,
Left = 0x00000002,
Top = 0x00000004,
Right = 0x00000008,
Bottom = 0x00000010
Left = 0x00000001,
Top = 0x00000002,
Right = 0x00000004,
Bottom = 0x00000008
};
Q_ENUM_NS(WindowEdge)
Q_DECLARE_FLAGS(WindowEdges, WindowEdge)

View File

@ -41,6 +41,7 @@ public:
Q_NODISCARD static SysApiLoader *instance();
Q_NODISCARD static QFunctionPointer resolve(const QString &library, const char *function);
Q_NODISCARD static QFunctionPointer resolve(const QString &library, const QByteArray &function);
Q_NODISCARD static QFunctionPointer resolve(const QString &library, const QString &function);

View File

@ -96,7 +96,6 @@ FRAMELESSHELPER_CORE_API void showSystemMenu(
[[nodiscard]] FRAMELESSHELPER_CORE_API quint32 getTitleBarHeight(const WId windowId, const bool scaled);
[[nodiscard]] FRAMELESSHELPER_CORE_API quint32 getFrameBorderThickness(const WId windowId,
const bool scaled);
FRAMELESSHELPER_CORE_API void updateWindowFrameBorderColor(const WId windowId, const bool dark);
FRAMELESSHELPER_CORE_API void maybeFixupQtInternals(const WId windowId);
[[nodiscard]] FRAMELESSHELPER_CORE_API bool isWindowFrameBorderVisible();
[[nodiscard]] FRAMELESSHELPER_CORE_API bool isFrameBorderColorized();
@ -115,6 +114,7 @@ FRAMELESSHELPER_CORE_API void forceSquareCornersForWindow(const WId windowId, co
FRAMELESSHELPER_CORE_API void disableOriginalTitleBarFunctionalities
(const WId windowId, const bool disable = true);
FRAMELESSHELPER_CORE_API void setQtDarkModeAwareEnabled(const bool enable);
FRAMELESSHELPER_CORE_API void refreshWin32ThemeResources(const WId windowId, const bool dark);
#endif // Q_OS_WINDOWS
#ifdef Q_OS_LINUX

View File

@ -40,10 +40,10 @@ function(setup_compile_params arg_target)
)
if(WIN32) # Needed by both MSVC and MinGW
set(_WIN32_WINNT_WIN10 0x0A00)
set(NTDDI_WIN10_CO 0x0A00000B)
set(NTDDI_WIN10_NI 0x0A00000C)
target_compile_definitions(${arg_target} PRIVATE
WINVER=${_WIN32_WINNT_WIN10} _WIN32_WINNT=${_WIN32_WINNT_WIN10}
_WIN32_IE=${_WIN32_WINNT_WIN10} NTDDI_VERSION=${NTDDI_WIN10_CO}
_WIN32_IE=${_WIN32_WINNT_WIN10} NTDDI_VERSION=${NTDDI_WIN10_NI}
)
endif()
if(MSVC)

View File

@ -41,9 +41,6 @@ Q_LOGGING_CATEGORY(lcFramelessHelperQt, "wangwenx190.framelesshelper.core.impl.q
using namespace Global;
FRAMELESSHELPER_BYTEARRAY_CONSTANT2(DontOverrideCursorVar, "FRAMELESSHELPER_DONT_OVERRIDE_CURSOR")
FRAMELESSHELPER_BYTEARRAY_CONSTANT2(DontToggleMaximizeVar, "FRAMELESSHELPER_DONT_TOGGLE_MAXIMIZE")
struct QtHelperData
{
SystemParameters params = {};

View File

@ -72,8 +72,6 @@ FRAMELESSHELPER_STRING_CONSTANT(SetWindowPos)
FRAMELESSHELPER_STRING_CONSTANT(TrackMouseEvent)
FRAMELESSHELPER_STRING_CONSTANT(FindWindowW)
FRAMELESSHELPER_STRING_CONSTANT(UnregisterClassW)
FRAMELESSHELPER_BYTEARRAY_CONSTANT2(DontOverrideCursorVar, "FRAMELESSHELPER_DONT_OVERRIDE_CURSOR")
FRAMELESSHELPER_BYTEARRAY_CONSTANT2(DontToggleMaximizeVar, "FRAMELESSHELPER_DONT_TOGGLE_MAXIMIZE")
FRAMELESSHELPER_STRING_CONSTANT(DestroyWindow)
[[maybe_unused]] static constexpr const char kFallbackTitleBarErrorMessage[] =
"FramelessHelper is unable to create the fallback title bar window, and thus the snap layout feature will be disabled"
@ -524,15 +522,15 @@ void FramelessHelperWin::addWindow(const SystemParameters &params)
if (WindowsVersionHelper::isWin10RS1OrGreater()) {
// Tell DWM we may need dark theme non-client area (title bar & frame border).
FramelessHelper::Core::setApplicationOSThemeAware();
const bool dark = Utils::shouldAppsUseDarkMode();
Utils::updateWindowFrameBorderColor(windowId, dark);
if (WindowsVersionHelper::isWin10RS5OrGreater()) {
const bool dark = Utils::shouldAppsUseDarkMode();
static const bool isQtQuickApplication = (params.getCurrentApplicationType() == ApplicationType::Quick);
if (isQtQuickApplication) {
// Tell UXTheme we may need dark theme controls.
// Causes some QtWidgets paint incorrectly, so only apply to Qt Quick applications.
Utils::updateGlobalWin32ControlsTheme(windowId, dark);
}
Utils::refreshWin32ThemeResources(windowId, dark);
if (WindowsVersionHelper::isWin11OrGreater()) {
const FramelessConfig * const config = FramelessConfig::instance();
// Set the frame corner style, only Win11 provides official public API to do it.
@ -1199,16 +1197,14 @@ bool FramelessHelperWin::nativeEventFilter(const QByteArray &eventType, void *me
if ((wParam == 0) && (lParam != 0) // lParam sometimes may be NULL.
&& (std::wcscmp(reinterpret_cast<LPCWSTR>(lParam), kThemeSettingChangeEventName) == 0)) {
systemThemeChanged = true;
const bool dark = Utils::shouldAppsUseDarkMode();
#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
Utils::updateWindowFrameBorderColor(windowId, dark);
#endif
if (WindowsVersionHelper::isWin10RS5OrGreater()) {
const bool dark = Utils::shouldAppsUseDarkMode();
static const bool isQtQuickApplication = (data.params.getCurrentApplicationType() == ApplicationType::Quick);
if (isQtQuickApplication) {
// Causes some QtWidgets paint incorrectly, so only apply to Qt Quick applications.
Utils::updateGlobalWin32ControlsTheme(windowId, dark);
}
Utils::refreshWin32ThemeResources(windowId, dark);
}
}
}

View File

@ -50,6 +50,20 @@ SysApiLoader *SysApiLoader::instance()
return g_sysApiLoader();
}
QFunctionPointer SysApiLoader::resolve(const QString &library, const char *function)
{
Q_ASSERT(!library.isEmpty());
Q_ASSERT(function);
if (library.isEmpty() || !function) {
return nullptr;
}
#ifdef Q_OS_WINDOWS
return QSystemLibrary::resolve(library, function);
#else
return QLibrary::resolve(library, function.constData());
#endif
}
QFunctionPointer SysApiLoader::resolve(const QString &library, const QByteArray &function)
{
Q_ASSERT(!library.isEmpty());
@ -57,11 +71,7 @@ QFunctionPointer SysApiLoader::resolve(const QString &library, const QByteArray
if (library.isEmpty() || function.isEmpty()) {
return nullptr;
}
#ifdef Q_OS_WINDOWS
return QSystemLibrary::resolve(library, function.constData());
#else
return QLibrary::resolve(library, function.constData());
#endif
return SysApiLoader::resolve(library, function.constData());
}
QFunctionPointer SysApiLoader::resolve(const QString &library, const QString &function)

View File

@ -23,6 +23,9 @@
*/
#include "utils.h"
#ifdef Q_OS_WINDOWS
# include "winverhelper_p.h"
#endif
#include <QtGui/qwindow.h>
#include <QtGui/qscreen.h>
#include <QtGui/qguiapplication.h>
@ -140,8 +143,7 @@ QString Utils::getSystemButtonIconCode(const SystemButtonType button)
// Windows 11: Segoe Fluent Icons (https://docs.microsoft.com/en-us/windows/apps/design/style/segoe-fluent-icons-font)
// Windows 10: Segoe MDL2 Assets (https://docs.microsoft.com/en-us/windows/apps/design/style/segoe-ui-symbol-font)
// Windows 7~8.1: Micon (http://xtoolkit.github.io/Micon/)
static const bool isWin10OrGreater = isWindowsVersionOrGreater(WindowsVersion::_10_1507);
if (isWin10OrGreater) {
if (WindowsVersionHelper::isWin10OrGreater()) {
return QChar(icon.segoe);
}
#endif

View File

@ -42,9 +42,48 @@
#include "sysapiloader_p.h"
#include "registrykey_p.h"
#include "winverhelper_p.h"
#include <uxtheme.h>
#include <d2d1.h>
EXTERN_C [[nodiscard]] FRAMELESSHELPER_CORE_API BOOL WINAPI
GetWindowCompositionAttribute(const HWND hWnd, PWINDOWCOMPOSITIONATTRIBDATA pvData)
{
Q_ASSERT(hWnd);
Q_ASSERT(pvData);
if (!hWnd || !pvData) {
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
FRAMELESSHELPER_USE_NAMESPACE
FRAMELESSHELPER_STRING_CONSTANT(user32)
FRAMELESSHELPER_STRING_CONSTANT(GetWindowCompositionAttribute)
const auto loader = SysApiLoader::instance();
if (!loader->isAvailable(kuser32, kGetWindowCompositionAttribute)) {
SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
return FALSE;
}
return (loader->get<GetWindowCompositionAttributePtr>(kGetWindowCompositionAttribute))(hWnd, pvData);
}
EXTERN_C [[nodiscard]] FRAMELESSHELPER_CORE_API BOOL WINAPI
SetWindowCompositionAttribute(const HWND hWnd, PWINDOWCOMPOSITIONATTRIBDATA pvData)
{
Q_ASSERT(hWnd);
Q_ASSERT(pvData);
if (!hWnd || !pvData) {
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
FRAMELESSHELPER_USE_NAMESPACE
FRAMELESSHELPER_STRING_CONSTANT(user32)
FRAMELESSHELPER_STRING_CONSTANT(SetWindowCompositionAttribute)
const auto loader = SysApiLoader::instance();
if (!loader->isAvailable(kuser32, kSetWindowCompositionAttribute)) {
SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
return FALSE;
}
return (loader->get<SetWindowCompositionAttributePtr>(kSetWindowCompositionAttribute))(hWnd, pvData);
}
EXTERN_C [[nodiscard]] FRAMELESSHELPER_CORE_API HRESULT WINAPI
SetWindowThemeAttribute2(const HWND hWnd, const _WINDOWTHEMEATTRIBUTETYPE attrib,
PVOID pvData, const DWORD cbData
@ -55,13 +94,14 @@ SetWindowThemeAttribute2(const HWND hWnd, const _WINDOWTHEMEATTRIBUTETYPE attrib
if (!hWnd || !pvData) {
return E_INVALIDARG;
}
FRAMELESSHELPER_USE_NAMESPACE
FRAMELESSHELPER_STRING_CONSTANT(uxtheme)
FRAMELESSHELPER_STRING_CONSTANT(SetWindowThemeAttribute)
const auto loader = FRAMELESSHELPER_PREPEND_NAMESPACE(SysApiLoader)::instance();
const auto loader = SysApiLoader::instance();
if (!loader->isAvailable(kuxtheme, kSetWindowThemeAttribute)) {
return E_NOTIMPL;
}
return (loader->get<decltype(&::SetWindowThemeAttribute2)>(kSetWindowThemeAttribute))(hWnd, attrib, pvData, cbData);
return (loader->get<decltype(&SetWindowThemeAttribute2)>(kSetWindowThemeAttribute))(hWnd, attrib, pvData, cbData);
}
EXTERN_C [[nodiscard]] FRAMELESSHELPER_CORE_API HRESULT WINAPI
@ -73,6 +113,187 @@ SetWindowThemeNonClientAttributes2(const HWND hWnd, const DWORD dwMask, const DW
return SetWindowThemeAttribute2(hWnd, _WTA_NONCLIENT, &options, sizeof(options));
}
EXTERN_C [[nodiscard]] FRAMELESSHELPER_CORE_API BOOL WINAPI
ShouldAppsUseDarkMode(VOID)
{
FRAMELESSHELPER_USE_NAMESPACE
FRAMELESSHELPER_STRING_CONSTANT(uxtheme)
static const auto pShouldAppsUseDarkMode
= reinterpret_cast<ShouldAppsUseDarkModePtr>(
SysApiLoader::resolve(kuxtheme, MAKEINTRESOURCEA(132)));
if (!pShouldAppsUseDarkMode) {
SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
return FALSE;
}
return pShouldAppsUseDarkMode();
}
EXTERN_C [[nodiscard]] FRAMELESSHELPER_CORE_API BOOL WINAPI
AllowDarkModeForWindow(const HWND hWnd, const BOOL bAllow)
{
Q_ASSERT(hWnd);
if (!hWnd) {
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
FRAMELESSHELPER_USE_NAMESPACE
FRAMELESSHELPER_STRING_CONSTANT(uxtheme)
static const auto pAllowDarkModeForWindow
= reinterpret_cast<AllowDarkModeForWindowPtr>(
SysApiLoader::resolve(kuxtheme, MAKEINTRESOURCEA(133)));
if (!pAllowDarkModeForWindow) {
SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
return FALSE;
}
return pAllowDarkModeForWindow(hWnd, bAllow);
}
EXTERN_C [[nodiscard]] FRAMELESSHELPER_CORE_API BOOL WINAPI
AllowDarkModeForApp(const BOOL bAllow)
{
FRAMELESSHELPER_USE_NAMESPACE
FRAMELESSHELPER_STRING_CONSTANT(uxtheme)
static const auto pAllowDarkModeForApp
= reinterpret_cast<AllowDarkModeForAppPtr>(
SysApiLoader::resolve(kuxtheme, MAKEINTRESOURCEA(135)));
if (!pAllowDarkModeForApp) {
SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
return FALSE;
}
return pAllowDarkModeForApp(bAllow);
}
EXTERN_C FRAMELESSHELPER_CORE_API VOID WINAPI
FlushMenuThemes(VOID)
{
FRAMELESSHELPER_USE_NAMESPACE
FRAMELESSHELPER_STRING_CONSTANT(uxtheme)
static const auto pFlushMenuThemes
= reinterpret_cast<FlushMenuThemesPtr>(
SysApiLoader::resolve(kuxtheme, MAKEINTRESOURCEA(136)));
if (!pFlushMenuThemes) {
SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
return;
}
pFlushMenuThemes();
}
EXTERN_C FRAMELESSHELPER_CORE_API VOID WINAPI
RefreshImmersiveColorPolicyState(VOID)
{
FRAMELESSHELPER_USE_NAMESPACE
FRAMELESSHELPER_STRING_CONSTANT(uxtheme)
static const auto pRefreshImmersiveColorPolicyState
= reinterpret_cast<RefreshImmersiveColorPolicyStatePtr>(
SysApiLoader::resolve(kuxtheme, MAKEINTRESOURCEA(104)));
if (!pRefreshImmersiveColorPolicyState) {
SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
return;
}
pRefreshImmersiveColorPolicyState();
}
EXTERN_C [[nodiscard]] FRAMELESSHELPER_CORE_API BOOL WINAPI
IsDarkModeAllowedForWindow(const HWND hWnd)
{
Q_ASSERT(hWnd);
if (!hWnd) {
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
FRAMELESSHELPER_USE_NAMESPACE
FRAMELESSHELPER_STRING_CONSTANT(uxtheme)
static const auto pIsDarkModeAllowedForWindow
= reinterpret_cast<IsDarkModeAllowedForWindowPtr>(
SysApiLoader::resolve(kuxtheme, MAKEINTRESOURCEA(137)));
if (!pIsDarkModeAllowedForWindow) {
SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
return FALSE;
}
return pIsDarkModeAllowedForWindow(hWnd);
}
EXTERN_C [[nodiscard]] FRAMELESSHELPER_CORE_API BOOL WINAPI
GetIsImmersiveColorUsingHighContrast(const IMMERSIVE_HC_CACHE_MODE mode)
{
FRAMELESSHELPER_USE_NAMESPACE
FRAMELESSHELPER_STRING_CONSTANT(uxtheme)
static const auto pGetIsImmersiveColorUsingHighContrast
= reinterpret_cast<GetIsImmersiveColorUsingHighContrastPtr>(
SysApiLoader::resolve(kuxtheme, MAKEINTRESOURCEA(106)));
if (!pGetIsImmersiveColorUsingHighContrast) {
SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
return FALSE;
}
return pGetIsImmersiveColorUsingHighContrast(mode);
}
EXTERN_C [[nodiscard]] FRAMELESSHELPER_CORE_API HTHEME WINAPI
OpenNcThemeData(const HWND hWnd, LPCWSTR pszClassList)
{
Q_ASSERT(hWnd);
Q_ASSERT(pszClassList);
if (!hWnd || !pszClassList) {
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
FRAMELESSHELPER_USE_NAMESPACE
FRAMELESSHELPER_STRING_CONSTANT(uxtheme)
static const auto pOpenNcThemeData
= reinterpret_cast<OpenNcThemeDataPtr>(
SysApiLoader::resolve(kuxtheme, MAKEINTRESOURCEA(49)));
if (!pOpenNcThemeData) {
SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
return FALSE;
}
return pOpenNcThemeData(hWnd, pszClassList);
}
EXTERN_C [[nodiscard]] FRAMELESSHELPER_CORE_API BOOL WINAPI
ShouldSystemUseDarkMode(VOID)
{
FRAMELESSHELPER_USE_NAMESPACE
FRAMELESSHELPER_STRING_CONSTANT(uxtheme)
static const auto pShouldSystemUseDarkMode
= reinterpret_cast<ShouldSystemUseDarkModePtr>(
SysApiLoader::resolve(kuxtheme, MAKEINTRESOURCEA(138)));
if (!pShouldSystemUseDarkMode) {
SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
return FALSE;
}
return pShouldSystemUseDarkMode();
}
EXTERN_C [[nodiscard]] FRAMELESSHELPER_CORE_API PREFERRED_APP_MODE WINAPI
SetPreferredAppMode(const PREFERRED_APP_MODE mode)
{
FRAMELESSHELPER_USE_NAMESPACE
FRAMELESSHELPER_STRING_CONSTANT(uxtheme)
static const auto pSetPreferredAppMode
= reinterpret_cast<SetPreferredAppModePtr>(
SysApiLoader::resolve(kuxtheme, MAKEINTRESOURCEA(135)));
if (!pSetPreferredAppMode) {
SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
return PAM_MAX;
}
return pSetPreferredAppMode(mode);
}
EXTERN_C [[nodiscard]] FRAMELESSHELPER_CORE_API BOOL WINAPI
IsDarkModeAllowedForApp(VOID)
{
FRAMELESSHELPER_USE_NAMESPACE
FRAMELESSHELPER_STRING_CONSTANT(uxtheme)
static const auto pIsDarkModeAllowedForApp
= reinterpret_cast<IsDarkModeAllowedForAppPtr>(
SysApiLoader::resolve(kuxtheme, MAKEINTRESOURCEA(139)));
if (!pIsDarkModeAllowedForApp) {
SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
return FALSE;
}
return pIsDarkModeAllowedForApp();
}
Q_DECLARE_METATYPE(QMargins)
FRAMELESSHELPER_BEGIN_NAMESPACE
@ -169,6 +390,11 @@ FRAMELESSHELPER_STRING_CONSTANT(DeleteDC)
FRAMELESSHELPER_STRING_CONSTANT(d2d1)
FRAMELESSHELPER_STRING_CONSTANT(D2D1CreateFactory)
FRAMELESSHELPER_STRING_CONSTANT(ReloadSystemMetrics)
FRAMELESSHELPER_STRING_CONSTANT(SetPreferredAppMode)
FRAMELESSHELPER_STRING_CONSTANT(AllowDarkModeForApp)
FRAMELESSHELPER_STRING_CONSTANT(AllowDarkModeForWindow)
FRAMELESSHELPER_STRING_CONSTANT(FlushMenuThemes)
FRAMELESSHELPER_STRING_CONSTANT(RefreshImmersiveColorPolicyState)
struct Win32UtilsHelperData
{
@ -474,8 +700,7 @@ bool Utils::isWindowsVersionOrGreater(const WindowsVersion version)
bool Utils::isDwmCompositionEnabled()
{
// DWM composition is always enabled and can't be disabled since Windows 8.
static const bool isWin8OrGreater = isWindowsVersionOrGreater(WindowsVersion::_8);
if (isWin8OrGreater) {
if (WindowsVersionHelper::isWin8OrGreater()) {
return true;
}
const auto resultFromRegistry = []() -> bool {
@ -627,8 +852,7 @@ QColor Utils::getDwmColorizationColor()
DwmColorizationArea Utils::getDwmColorizationArea()
{
// It's a Win10 only feature. (TO BE VERIFIED)
static const bool isWin10OrGreater = isWindowsVersionOrGreater(WindowsVersion::_10_1507);
if (!isWin10OrGreater) {
if (!WindowsVersionHelper::isWin10OrGreater()) {
return DwmColorizationArea::None_;
}
const RegistryKey themeRegistry(RegistryRootKey::CurrentUser, personalizeRegistryKey());
@ -1035,8 +1259,7 @@ quint32 Utils::getFrameBorderThickness(const WId windowId, const bool scaled)
return 0;
}
// There's no window frame border before Windows 10.
static const bool isWin10OrGreater = isWindowsVersionOrGreater(WindowsVersion::_10_1507);
if (!isWin10OrGreater) {
if (!WindowsVersionHelper::isWin10OrGreater()) {
return 0;
}
if (!API_DWM_AVAILABLE(DwmGetWindowAttribute)) {
@ -1061,8 +1284,7 @@ QColor Utils::getFrameBorderColor(const bool active)
{
// There's no window frame border before Windows 10.
// So we just return a default value which is based on most window managers.
static const bool isWin10OrGreater = isWindowsVersionOrGreater(WindowsVersion::_10_1507);
if (!isWin10OrGreater) {
if (!WindowsVersionHelper::isWin10OrGreater()) {
return (active ? kDefaultBlackColor : kDefaultDarkGrayColor);
}
const bool dark = shouldAppsUseDarkMode();
@ -1077,30 +1299,6 @@ QColor Utils::getFrameBorderColor(const bool active)
}
}
void Utils::updateWindowFrameBorderColor(const WId windowId, const bool dark)
{
Q_ASSERT(windowId);
if (!windowId) {
return;
}
// There's no global dark theme before Win10 1607.
static const bool isWin10RS1OrGreater = isWindowsVersionOrGreater(WindowsVersion::_10_1607);
if (!isWin10RS1OrGreater) {
return;
}
if (!API_DWM_AVAILABLE(DwmSetWindowAttribute)) {
return;
}
const auto hwnd = reinterpret_cast<HWND>(windowId);
static const bool isWin1020H1OrGreater = isWindowsVersionOrGreater(WindowsVersion::_10_2004);
const DWORD mode = (isWin1020H1OrGreater ? _DWMWA_USE_IMMERSIVE_DARK_MODE : _DWMWA_USE_IMMERSIVE_DARK_MODE_BEFORE_20H1);
const BOOL value = (dark ? TRUE : FALSE);
const HRESULT hr = API_CALL_FUNCTION(DwmSetWindowAttribute, hwnd, mode, &value, sizeof(value));
if (FAILED(hr)) {
WARNING << __getSystemErrorMessage(kDwmSetWindowAttribute, hr);
}
}
void Utils::maybeFixupQtInternals(const WId windowId)
{
Q_ASSERT(windowId);
@ -1216,8 +1414,7 @@ bool Utils::isWindowFrameBorderVisible()
if (config->isSet(Option::ForceHideWindowFrameBorder)) {
return false;
}
static const bool isWin10OrGreater = isWindowsVersionOrGreater(WindowsVersion::_10_1507);
return isWin10OrGreater;
return WindowsVersionHelper::isWin10OrGreater();
}();
return result;
}
@ -1225,8 +1422,7 @@ bool Utils::isWindowFrameBorderVisible()
bool Utils::isTitleBarColorized()
{
// CHECK: is it supported on win7?
static const bool isWin10OrGreater = isWindowsVersionOrGreater(WindowsVersion::_10_1507);
if (!isWin10OrGreater) {
if (!WindowsVersionHelper::isWin10OrGreater()) {
return false;
}
const DwmColorizationArea area = getDwmColorizationArea();
@ -1407,8 +1603,7 @@ SystemTheme Utils::getSystemTheme()
if (isHighContrastModeEnabled()) {
return SystemTheme::HighContrast;
}
static const bool isWin10RS1OrGreater = isWindowsVersionOrGreater(WindowsVersion::_10_1607);
if (isWin10RS1OrGreater && shouldAppsUseDarkMode()) {
if (WindowsVersionHelper::isWin10RS1OrGreater() && shouldAppsUseDarkMode()) {
return SystemTheme::Dark;
}
return SystemTheme::Light;
@ -1421,8 +1616,7 @@ void Utils::updateGlobalWin32ControlsTheme(const WId windowId, const bool dark)
return;
}
// There's no global dark theme for common Win32 controls before Win10 1809.
static const bool isWin10RS5OrGreater = isWindowsVersionOrGreater(WindowsVersion::_10_1809);
if (!isWin10RS5OrGreater) {
if (!WindowsVersionHelper::isWin10RS5OrGreater()) {
return;
}
if (!API_THEME_AVAILABLE(SetWindowTheme)) {
@ -1439,8 +1633,7 @@ void Utils::updateGlobalWin32ControlsTheme(const WId windowId, const bool dark)
bool Utils::shouldAppsUseDarkMode_windows()
{
// The global dark mode was first introduced in Windows 10 1607.
static const bool isWin10RS1OrGreater = isWindowsVersionOrGreater(WindowsVersion::_10_1607);
if (!isWin10RS1OrGreater) {
if (!WindowsVersionHelper::isWin10RS1OrGreater()) {
return false;
}
#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
@ -1486,8 +1679,7 @@ void Utils::forceSquareCornersForWindow(const WId windowId, const bool force)
return;
}
// We cannot change the window corner style until Windows 11.
static const bool isWin11OrGreater = isWindowsVersionOrGreater(WindowsVersion::_11_21H2);
if (!isWin11OrGreater) {
if (!WindowsVersionHelper::isWin11OrGreater()) {
return;
}
if (!API_DWM_AVAILABLE(DwmSetWindowAttribute)) {
@ -1508,44 +1700,34 @@ bool Utils::setBlurBehindWindowEnabled(const WId windowId, const BlurMode mode,
return false;
}
const auto hwnd = reinterpret_cast<HWND>(windowId);
static const bool isWin8OrGreater = isWindowsVersionOrGreater(WindowsVersion::_8);
if (isWin8OrGreater) {
if (!API_USER_AVAILABLE(SetWindowCompositionAttribute)) {
return false;
}
if (WindowsVersionHelper::isWin8OrGreater()) {
if (!API_DWM_AVAILABLE(DwmSetWindowAttribute)) {
return false;
}
if (!API_DWM_AVAILABLE(DwmExtendFrameIntoClientArea)) {
return false;
}
const auto pSetWindowCompositionAttribute =
reinterpret_cast<SetWindowCompositionAttributePtr>(
SysApiLoader::instance()->get(kSetWindowCompositionAttribute));
static const bool isWin1122H2OrGreater = isWindowsVersionOrGreater(WindowsVersion::_11_22H2);
static const bool isWin11OrGreater = isWindowsVersionOrGreater(WindowsVersion::_11_21H2);
static const bool isWin10OrGreater = isWindowsVersionOrGreater(WindowsVersion::_10_1507);
const BlurMode blurMode = [mode]() -> BlurMode {
if ((mode == BlurMode::Disable) || (mode == BlurMode::Windows_Aero)) {
return mode;
}
if ((mode == BlurMode::Windows_Mica) && !isWin11OrGreater) {
if ((mode == BlurMode::Windows_Mica) && !WindowsVersionHelper::isWin11OrGreater()) {
WARNING << "The Mica material is not supported on your system, fallback to the Acrylic blur instead...";
if (isWin10OrGreater) {
if (WindowsVersionHelper::isWin10OrGreater()) {
return BlurMode::Windows_Acrylic;
}
WARNING << "The Acrylic blur is not supported on your system, fallback to the traditional DWM blur instead...";
return BlurMode::Windows_Aero;
}
if ((mode == BlurMode::Windows_Acrylic) && !isWin10OrGreater) {
if ((mode == BlurMode::Windows_Acrylic) && !WindowsVersionHelper::isWin10OrGreater()) {
WARNING << "The Acrylic blur is not supported on your system, fallback to the traditional DWM blur instead...";
return BlurMode::Windows_Aero;
}
if (mode == BlurMode::Default) {
if (isWin11OrGreater) {
if (WindowsVersionHelper::isWin11OrGreater()) {
return BlurMode::Windows_Mica;
}
if (isWin10OrGreater) {
if (WindowsVersionHelper::isWin10OrGreater()) {
return BlurMode::Windows_Acrylic;
}
return BlurMode::Windows_Aero;
@ -1554,7 +1736,7 @@ bool Utils::setBlurBehindWindowEnabled(const WId windowId, const BlurMode mode,
return mode;
}();
if (blurMode == BlurMode::Disable) {
if (isWin1122H2OrGreater) {
if (WindowsVersionHelper::isWin1122H2OrGreater()) {
const _DWM_SYSTEMBACKDROP_TYPE dwmsbt = _DWMSBT_NONE;
const HRESULT hr = API_CALL_FUNCTION(DwmSetWindowAttribute,
hwnd, _DWMWA_SYSTEMBACKDROP_TYPE, &dwmsbt, sizeof(dwmsbt));
@ -1562,7 +1744,7 @@ bool Utils::setBlurBehindWindowEnabled(const WId windowId, const BlurMode mode,
WARNING << __getSystemErrorMessage(kDwmSetWindowAttribute, hr);
}
}
if (isWin11OrGreater) {
if (WindowsVersionHelper::isWin11OrGreater()) {
const BOOL enable = FALSE;
HRESULT hr = API_CALL_FUNCTION(DwmSetWindowAttribute,
hwnd, _DWMWA_MICA_EFFECT, &enable, sizeof(enable));
@ -1583,7 +1765,7 @@ bool Utils::setBlurBehindWindowEnabled(const WId windowId, const BlurMode mode,
wcad.Attrib = WCA_ACCENT_POLICY;
wcad.pvData = &policy;
wcad.cbData = sizeof(policy);
if (pSetWindowCompositionAttribute(hwnd, &wcad) == FALSE) {
if (SetWindowCompositionAttribute(hwnd, &wcad) == FALSE) {
WARNING << getSystemErrorMessage(kSetWindowCompositionAttribute);
}
}
@ -1597,7 +1779,7 @@ bool Utils::setBlurBehindWindowEnabled(const WId windowId, const BlurMode mode,
const MARGINS margins = {-1, -1, -1, -1};
HRESULT hr = API_CALL_FUNCTION(DwmExtendFrameIntoClientArea, hwnd, &margins);
if (SUCCEEDED(hr)) {
if (isWin1122H2OrGreater) {
if (WindowsVersionHelper::isWin1122H2OrGreater()) {
const _DWM_SYSTEMBACKDROP_TYPE dwmsbt = _DWMSBT_MAINWINDOW; // Mica
hr = API_CALL_FUNCTION(DwmSetWindowAttribute, hwnd,
_DWMWA_SYSTEMBACKDROP_TYPE, &dwmsbt, sizeof(dwmsbt));
@ -1646,8 +1828,8 @@ bool Utils::setBlurBehindWindowEnabled(const WId windowId, const BlurMode mode,
wcad.Attrib = WCA_ACCENT_POLICY;
wcad.pvData = &policy;
wcad.cbData = sizeof(policy);
if (pSetWindowCompositionAttribute(hwnd, &wcad) != FALSE) {
if (!isWin11OrGreater) {
if (SetWindowCompositionAttribute(hwnd, &wcad) != FALSE) {
if (!WindowsVersionHelper::isWin11OrGreater()) {
DEBUG << "Enabling the Acrylic blur for Win32 windows on Windows 10 "
"is very buggy. The only recommended way by Microsoft is to "
"use the XAML Island technology or use pure UWP instead. If "
@ -1749,8 +1931,7 @@ bool Utils::isBlurBehindWindowSupported()
if (FramelessConfig::instance()->isSet(Option::ForceNonNativeBackgroundBlur)) {
return false;
}
static const bool isWin11OrGreater = isWindowsVersionOrGreater(WindowsVersion::_11_21H2);
return isWin11OrGreater;
return WindowsVersionHelper::isWin11OrGreater();
}();
return result;
}
@ -1817,4 +1998,98 @@ void Utils::registerThemeChangeNotification()
// top level windows by default.
}
void Utils::refreshWin32ThemeResources(const WId windowId, const bool dark)
{
Q_ASSERT(windowId);
if (!windowId) {
return;
}
// We have no way to adjust such things until Win10 1809.
if (!WindowsVersionHelper::isWin10RS5OrGreater()) {
return;
}
#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
if (!API_DWM_AVAILABLE(DwmSetWindowAttribute)) {
return;
}
#endif
const auto hWnd = reinterpret_cast<HWND>(windowId);
#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
const DWORD borderFlag = (WindowsVersionHelper::isWin1020H1OrGreater()
? _DWMWA_USE_IMMERSIVE_DARK_MODE : _DWMWA_USE_IMMERSIVE_DARK_MODE_BEFORE_20H1);
#endif
const PREFERRED_APP_MODE appMode = (dark ? PAM_ALLOW_DARK : PAM_DEFAULT);
const BOOL darkFlag = (dark ? TRUE : FALSE);
WINDOWCOMPOSITIONATTRIBDATA wcad;
SecureZeroMemory(&wcad, sizeof(wcad));
wcad.Attrib = WCA_USEDARKMODECOLORS;
wcad.pvData = const_cast<BOOL *>(&darkFlag);
wcad.cbData = sizeof(darkFlag);
if (dark) {
if (WindowsVersionHelper::isWin1019H1OrGreater()) {
if (SetPreferredAppMode(appMode) == PAM_MAX) {
WARNING << getSystemErrorMessage(kSetPreferredAppMode);
}
} else {
if (AllowDarkModeForApp(darkFlag) == FALSE) {
WARNING << getSystemErrorMessage(kAllowDarkModeForApp);
}
}
if (AllowDarkModeForWindow(hWnd, darkFlag) == FALSE) {
WARNING << getSystemErrorMessage(kAllowDarkModeForWindow);
}
if (SetWindowCompositionAttribute(hWnd, &wcad) == FALSE) {
WARNING << getSystemErrorMessage(kSetWindowCompositionAttribute);
}
#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
const HRESULT hr = API_CALL_FUNCTION(DwmSetWindowAttribute, hWnd, borderFlag, &darkFlag, sizeof(darkFlag));
if (FAILED(hr)) {
WARNING << __getSystemErrorMessage(kDwmSetWindowAttribute, hr);
}
#endif
SetLastError(ERROR_SUCCESS);
FlushMenuThemes();
if (GetLastError() != ERROR_SUCCESS) {
WARNING << getSystemErrorMessage(kFlushMenuThemes);
}
SetLastError(ERROR_SUCCESS);
RefreshImmersiveColorPolicyState();
if (GetLastError() != ERROR_SUCCESS) {
WARNING << getSystemErrorMessage(kRefreshImmersiveColorPolicyState);
}
} else {
if (AllowDarkModeForWindow(hWnd, darkFlag) == FALSE) {
WARNING << getSystemErrorMessage(kAllowDarkModeForWindow);
}
if (SetWindowCompositionAttribute(hWnd, &wcad) == FALSE) {
WARNING << getSystemErrorMessage(kSetWindowCompositionAttribute);
}
#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
const HRESULT hr = API_CALL_FUNCTION(DwmSetWindowAttribute, hWnd, borderFlag, &darkFlag, sizeof(darkFlag));
if (FAILED(hr)) {
WARNING << __getSystemErrorMessage(kDwmSetWindowAttribute, hr);
}
#endif
SetLastError(ERROR_SUCCESS);
FlushMenuThemes();
if (GetLastError() != ERROR_SUCCESS) {
WARNING << getSystemErrorMessage(kFlushMenuThemes);
}
SetLastError(ERROR_SUCCESS);
RefreshImmersiveColorPolicyState();
if (GetLastError() != ERROR_SUCCESS) {
WARNING << getSystemErrorMessage(kRefreshImmersiveColorPolicyState);
}
if (WindowsVersionHelper::isWin1019H1OrGreater()) {
if (SetPreferredAppMode(appMode) == PAM_MAX) {
WARNING << getSystemErrorMessage(kSetPreferredAppMode);
}
} else {
if (AllowDarkModeForApp(darkFlag) == FALSE) {
WARNING << getSystemErrorMessage(kAllowDarkModeForApp);
}
}
}
}
FRAMELESSHELPER_END_NAMESPACE

View File

@ -29,14 +29,14 @@ target_sources(${PROJECT_NAME} PRIVATE
)
set(_WIN32_WINNT_WIN10 0x0A00)
set(NTDDI_WIN10_CO 0x0A00000B)
set(NTDDI_WIN10_NI 0x0A00000C)
target_compile_definitions(${PROJECT_NAME} PRIVATE
_CRT_NON_CONFORMING_SWPRINTFS _CRT_SECURE_NO_WARNINGS
_CRT_SECURE_NO_DEPRECATE _CRT_NONSTDC_NO_WARNINGS
_CRT_NONSTDC_NO_DEPRECATE _ENABLE_EXTENDED_ALIGNED_STORAGE
NOMINMAX UNICODE _UNICODE WIN32_LEAN_AND_MEAN WINRT_LEAN_AND_MEAN
WINVER=${_WIN32_WINNT_WIN10} _WIN32_WINNT=${_WIN32_WINNT_WIN10}
_WIN32_IE=${_WIN32_WINNT_WIN10} NTDDI_VERSION=${NTDDI_WIN10_CO}
_WIN32_IE=${_WIN32_WINNT_WIN10} NTDDI_VERSION=${NTDDI_WIN10_NI}
)
target_compile_options(${PROJECT_NAME} PRIVATE