mirror of
https://github.com/microsoft/PowerToys.git
synced 2026-07-02 08:28:55 +02:00
Compare commits
1 Commits
powerscrip
...
dev/crutka
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
49b3795f1a |
@@ -30,6 +30,7 @@
|
||||
</ClCompile>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="dark_menu.h" />
|
||||
<ClInclude Include="icon_helpers.h" />
|
||||
<ClInclude Include="theme_listener.h" />
|
||||
<ClInclude Include="theme_helpers.h" />
|
||||
|
||||
97
src/common/Themes/dark_menu.h
Normal file
97
src/common/Themes/dark_menu.h
Normal file
@@ -0,0 +1,97 @@
|
||||
// Theme-aware rendering for classic Win32 popup menus (HMENU / TrackPopupMenu).
|
||||
//
|
||||
// Lets a Win32 tray/popup menu follow the OS light/dark theme. It puts the
|
||||
// process into the matching app theme via uxtheme's preferred-app-mode entry
|
||||
// points -- the same mechanism the OS uses to render dark menus, as already
|
||||
// used by ZoomIt and File Explorer -- and then the system draws the real
|
||||
// themed menu. Native keyboard, accessibility, checkmarks, separators and DPI
|
||||
// are all preserved; only the colors change.
|
||||
//
|
||||
// Theme detection reads the documented AppsUseLightTheme value, fresh on each
|
||||
// call, so a live light<->dark switch is reflected without restarting.
|
||||
//
|
||||
// Drop-in for any PowerToys system-tray utility:
|
||||
//
|
||||
// theme::dark_menu::SetAppMode(theme::dark_menu::IsSystemDarkMode());
|
||||
// TrackPopupMenu(menu, ...);
|
||||
#pragma once
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
namespace theme::dark_menu
|
||||
{
|
||||
namespace details
|
||||
{
|
||||
// uxtheme preferred-app-mode values (order matters).
|
||||
enum class PreferredAppMode
|
||||
{
|
||||
Default = 0,
|
||||
AllowDark,
|
||||
ForceDark,
|
||||
ForceLight,
|
||||
Max
|
||||
};
|
||||
|
||||
using SetPreferredAppModeFn = PreferredAppMode(WINAPI*)(PreferredAppMode);
|
||||
using FlushMenuThemesFn = void(WINAPI*)();
|
||||
|
||||
struct Ordinals
|
||||
{
|
||||
SetPreferredAppModeFn setPreferredAppMode = nullptr;
|
||||
FlushMenuThemesFn flushMenuThemes = nullptr;
|
||||
};
|
||||
|
||||
// Resolved once per process: an inline function's local static is a single
|
||||
// shared instance across all translation units that include this header.
|
||||
inline const Ordinals& GetOrdinals() noexcept
|
||||
{
|
||||
static const Ordinals ordinals = []() noexcept {
|
||||
Ordinals result{};
|
||||
HMODULE uxtheme = GetModuleHandleW(L"uxtheme.dll");
|
||||
if (uxtheme == nullptr)
|
||||
{
|
||||
uxtheme = LoadLibraryExW(L"uxtheme.dll", nullptr, LOAD_LIBRARY_SEARCH_SYSTEM32);
|
||||
}
|
||||
if (uxtheme != nullptr)
|
||||
{
|
||||
// Ordinal 135 = SetPreferredAppMode, ordinal 136 = FlushMenuThemes.
|
||||
result.setPreferredAppMode = reinterpret_cast<SetPreferredAppModeFn>(
|
||||
GetProcAddress(uxtheme, MAKEINTRESOURCEA(135)));
|
||||
result.flushMenuThemes = reinterpret_cast<FlushMenuThemesFn>(
|
||||
GetProcAddress(uxtheme, MAKEINTRESOURCEA(136)));
|
||||
}
|
||||
return result;
|
||||
}();
|
||||
return ordinals;
|
||||
}
|
||||
}
|
||||
|
||||
// True if the user's app theme is dark, via the documented AppsUseLightTheme value.
|
||||
inline bool IsSystemDarkMode() noexcept
|
||||
{
|
||||
DWORD value = 1; // default to light if the value is missing
|
||||
DWORD size = sizeof(value);
|
||||
RegGetValueW(HKEY_CURRENT_USER,
|
||||
L"Software\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize",
|
||||
L"AppsUseLightTheme", RRF_RT_REG_DWORD, nullptr, &value, &size);
|
||||
return value == 0;
|
||||
}
|
||||
|
||||
// Make this process's classic popup menus render dark or light. Cheap and
|
||||
// idempotent -- call right before TrackPopupMenu so the menu always matches
|
||||
// the current theme, including after a live theme switch. No-op if those
|
||||
// entry points are unavailable.
|
||||
inline void SetAppMode(bool dark) noexcept
|
||||
{
|
||||
const details::Ordinals& ordinals = details::GetOrdinals();
|
||||
if (ordinals.setPreferredAppMode != nullptr)
|
||||
{
|
||||
ordinals.setPreferredAppMode(dark ? details::PreferredAppMode::ForceDark
|
||||
: details::PreferredAppMode::ForceLight);
|
||||
}
|
||||
if (ordinals.flushMenuThemes != nullptr)
|
||||
{
|
||||
ordinals.flushMenuThemes();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -34,6 +34,7 @@
|
||||
#include <common/utils/logger_helper.h>
|
||||
#include <common/utils/winapi_error.h>
|
||||
#include <common/utils/gpo.h>
|
||||
#include <common/Themes/dark_menu.h>
|
||||
#include <array>
|
||||
#include <vector>
|
||||
#endif // __ZOOMIT_POWERTOYS__
|
||||
@@ -10163,8 +10164,23 @@ LRESULT APIENTRY MainWndProc(
|
||||
InsertMenu( hPopupMenu, 0, MF_BYPOSITION|MF_SEPARATOR, 0, NULL );
|
||||
InsertMenu( hPopupMenu, 0, MF_BYPOSITION, IDC_OPTIONS, L"&Options" );
|
||||
}
|
||||
// Apply dark mode theme to the menu
|
||||
// Make the popup theme-aware (dark/light).
|
||||
#ifdef __ZOOMIT_POWERTOYS__
|
||||
// Shared common helper: the OS renders the dark/light menu (native
|
||||
// chrome, keyboard, accessibility). Reusable by other Win32 modules
|
||||
// such as the runner tray menu.
|
||||
//
|
||||
// Detect the theme fresh at open time (respecting ZoomIt's theme
|
||||
// override) so a live light<->dark switch is reflected without a
|
||||
// restart. IsDarkModeEnabled() uses uxtheme's cached state, which a
|
||||
// runtime theme switch may not refresh, whereas the AppsUseLightTheme
|
||||
// registry value (read by IsSystemDarkMode) is always current.
|
||||
const bool useDarkMenu = ( g_ThemeOverride == 1 ) ||
|
||||
( g_ThemeOverride != 0 && theme::dark_menu::IsSystemDarkMode() );
|
||||
theme::dark_menu::SetAppMode( useDarkMenu );
|
||||
#else
|
||||
ApplyDarkModeToMenu( hPopupMenu );
|
||||
#endif
|
||||
TrackPopupMenu( hPopupMenu, 0, pt.x , pt.y, 0, hWnd, NULL );
|
||||
DestroyMenu( hPopupMenu );
|
||||
break;
|
||||
|
||||
Reference in New Issue
Block a user