diff --git a/.github/actions/spell-check/expect.txt b/.github/actions/spell-check/expect.txt
index 130f458b55..068c343369 100644
--- a/.github/actions/spell-check/expect.txt
+++ b/.github/actions/spell-check/expect.txt
@@ -147,7 +147,6 @@ betadele
betsegaw
BGR
bgra
-BGSOUNDS
bhid
Bicubic
bigbar
@@ -245,7 +244,6 @@ CLASSNOTAVAILABLE
clickable
clickonce
CLIENTEDGE
-CLIENTPULL
clientside
CLIPCHILDREN
Clipperton
@@ -452,19 +450,14 @@ dimm
directshow
dirs
DISABLEASACTIONKEY
-dispid
-DISPIDAMBIENTDLCONTROL
DISPLAYCHANGE
DISPLAYCONFIG
displayname
divyan
-DLACTIVEXCTLS
-DLCONTROL
Dlg
DLGFRAME
DLGMODALFRAME
dlib
-DLIMAGES
dllexport
dllhost
dllimport
@@ -476,7 +469,6 @@ doctype
DONOTROUND
DONTVALIDATEPATH
dotnet
-DOWNLOADONLY
DPICHANGED
DPIs
DPolicy
@@ -536,6 +528,7 @@ Emoji
emptyrecyclebin
ENABLEDPOPUP
endforeach
+endian
endif
endl
endpointvolume
@@ -547,7 +540,6 @@ enum
EOAC
eol
epicgames
-epo
Eqn
ERASEBKGND
EREOF
@@ -595,7 +587,6 @@ fallthrough
fancyzones
FANCYZONESDRAWLAYOUTTEST
FANCYZONESEDITOR
-FANCYZONESWINDOWSTYLES
Farbraum
Faroe
FARPROC
@@ -634,11 +625,9 @@ FOFX
FOLDERID
folderpath
FORCEMINIMIZE
-FORCEOFFLINE
foreach
formatetc
FRAMECHANGED
-FRAMEDOWNLOAD
franky
frankychen
Froml
@@ -681,7 +670,6 @@ globals
GNumber
google
GPTR
-gsuberland
gtm
gui
guiddef
@@ -718,7 +706,6 @@ helptext
Heure
HEVC
hfile
-HFONT
hglobal
hhk
HHmmss
@@ -778,8 +765,8 @@ hstring
hsv
htcfreek
HTHUMBNAIL
-Htmdid
HTTRANSPARENT
+HValue
Hvci
hwb
HWINEVENTHOOK
@@ -813,10 +800,8 @@ ICompositor
ICONERROR
IContext
ICONWARNING
-ICore
ICreate
IData
-IDCANCEL
IDD
IDelayed
IDesktop
@@ -827,7 +812,6 @@ IDispatcher
IDisposable
idl
IDLIST
-IDOK
IDOn
IDR
IDrive
@@ -895,7 +879,6 @@ INFOEXAMPLE
Infotip
ingbuffer
inheritdoc
-INITDIALOG
initguid
Inkscape
Inlines
@@ -958,7 +941,6 @@ IPublic
IQuery
IRead
IReference
-IReflect
IRegistered
IRegistration
IRegistry
@@ -1006,9 +988,7 @@ IWbem
IWeb
IWIC
iwindow
-IWindows
IWork
-IXaml
IXml
IYUV
IZone
@@ -1066,7 +1046,6 @@ Kyrgyzstan
Kyzylorda
LAlt
Lambson
-lamotile
langword
Lastdevice
Latn
@@ -1153,6 +1132,7 @@ LPSAFEARRAY
LPSTR
lpsz
lpt
+LPTHREAD
LPTOP
lptpm
LPTSTR
@@ -1225,7 +1205,6 @@ MENUBREAK
MENUITEMINFO
MENUITEMINFOW
messageboxes
-METACHARSET
Metadatas
metafile
mfapi
@@ -1252,7 +1231,6 @@ miniz
minlevel
MINORVERSION
Miracast
-mirophone
Mishkeegogamang
mjpg
mkd
@@ -1296,7 +1274,6 @@ msdata
MSDN
msedge
MSGFLT
-mshtmdid
MSIFASTINSTALL
MSIHANDLE
msiquery
@@ -1362,10 +1339,8 @@ netcore
netcoreapp
netcpl
netframework
-NETFX
netsetup
netsh
-netstandard
Neue
newcolor
newdev
@@ -1443,7 +1418,6 @@ NUMLOCK
NUMPAD
Nunavut
Nusa
-Nvidia
nwc
NWSE
Objbase
@@ -1483,6 +1457,7 @@ ostream
ostringstream
OSVERSIONINFOEX
OSVERSIONINFOEXW
+OSVERSIONINFOW
osvi
otating
OUTOFCONTEXT
@@ -1566,7 +1541,6 @@ Pohnpei
popup
POPUPWINDOW
posix
-Postion
powercfg
powerlauncher
powerpreview
@@ -1596,7 +1570,6 @@ Prefixer
Preinstalled
preload
PREMULTIPLIED
-preperty
prevhost
previewer
PREVIEWHANDLERFRAMEINFO
@@ -1623,6 +1596,7 @@ PROPBAG
PROPERTYKEY
propkey
propvarutil
+PRTL
prvpane
psapi
PSECURITY
@@ -1824,7 +1798,6 @@ SENDCHANGE
sendvirtualinput
serializationexception
serializer
-serizalization
serverside
SETCONTEXT
setcursor
@@ -1839,8 +1812,6 @@ SETTEXT
SETTINGCHANGE
settingsheader
settingshotkeycontrol
-settingsv
-Setttings
SETWORKAREA
sfgao
SFGAOF
@@ -1920,7 +1891,6 @@ somil
Soref
SOURCECLIENTAREAONLY
SOURCEHEADER
-sourceid
sourcesdirectory
spam
spdisp
@@ -2076,7 +2046,6 @@ Tenggara
testcase
testhost
testprocess
-testtrocess
testzones
TEXCOORD
textblock
@@ -2100,7 +2069,6 @@ TMPVAR
TNP
todo
toggleswitch
-Toolchain
toolkitcontrols
toolkitconverters
Toolset
@@ -2227,7 +2195,6 @@ vcruntime
vcvars
VDesktop
vdi
-VDId
vec
VERBSONLY
VERBW
@@ -2385,6 +2352,7 @@ wpf
wpr
wprintf
wprp
+wql
WQL
wregex
WResize
@@ -2410,8 +2378,6 @@ Wwan
Wwanpp
xamarin
XAttribute
-xbf
-XBind
Xbox
XBUTTON
XBUTTONDBLCLK
@@ -2422,7 +2388,6 @@ XDocument
XElement
XFile
XIncrement
-XInstance
XLoc
XNamespace
XOffset
diff --git a/installer/MSIX/PackagingLayout.xml b/installer/MSIX/PackagingLayout.xml
index 419b388f3c..31d1d1128b 100644
--- a/installer/MSIX/PackagingLayout.xml
+++ b/installer/MSIX/PackagingLayout.xml
@@ -25,6 +25,7 @@
+
@@ -33,6 +34,7 @@
+
diff --git a/installer/PowerToysSetup/Product.wxs b/installer/PowerToysSetup/Product.wxs
index 0a863871fe..8450cd3427 100644
--- a/installer/PowerToysSetup/Product.wxs
+++ b/installer/PowerToysSetup/Product.wxs
@@ -40,13 +40,13 @@
-->
-
+
-
+
-
+
-
+
@@ -872,7 +872,7 @@
-
+
@@ -892,7 +892,7 @@
-
+
@@ -1731,7 +1731,7 @@
-
+
diff --git a/src/common/ManagedCommon/ManagedCommon.csproj b/src/common/ManagedCommon/ManagedCommon.csproj
index 6ddd715395..30dd826a0f 100644
--- a/src/common/ManagedCommon/ManagedCommon.csproj
+++ b/src/common/ManagedCommon/ManagedCommon.csproj
@@ -17,6 +17,8 @@
all
runtime; build; native; contentfiles; analyzers; buildtransitive
+
+
diff --git a/src/common/ManagedCommon/Theme.cs b/src/common/ManagedCommon/Theme.cs
index 4e0cda2f3b..3726050b5b 100644
--- a/src/common/ManagedCommon/Theme.cs
+++ b/src/common/ManagedCommon/Theme.cs
@@ -14,4 +14,10 @@ namespace ManagedCommon
HighContrastBlack,
HighContrastWhite,
}
+
+ public enum AppTheme
+ {
+ Dark = 0,
+ Light = 1,
+ }
}
diff --git a/src/common/ManagedCommon/ThemeHelpers.cs b/src/common/ManagedCommon/ThemeHelpers.cs
new file mode 100644
index 0000000000..2bc6f50602
--- /dev/null
+++ b/src/common/ManagedCommon/ThemeHelpers.cs
@@ -0,0 +1,37 @@
+// Copyright (c) Microsoft Corporation
+// The Microsoft Corporation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using System.Linq;
+using System.Runtime.InteropServices;
+using Microsoft.Win32;
+
+namespace ManagedCommon
+{
+ // Based on https://stackoverflow.com/a/62811758/5001796
+ public static class ThemeHelpers
+ {
+ [DllImport("dwmapi.dll")]
+ private static extern int DwmSetWindowAttribute(IntPtr hwnd, int attr, ref int attrValue, int attrSize);
+
+ internal const string HKeyRoot = "HKEY_CURRENT_USER";
+ internal const string HkeyWindowsTheme = @"SOFTWARE\Microsoft\Windows\CurrentVersion\Themes";
+ internal const string HkeyWindowsPersonalizeTheme = $@"{HkeyWindowsTheme}\Personalize";
+ internal const string HValueAppTheme = "AppsUseLightTheme";
+ internal const int DWMWAImmersiveDarkMode = 20;
+
+ // based on https://stackoverflow.com/questions/51334674/how-to-detect-windows-10-light-dark-mode-in-win32-application
+ public static AppTheme GetAppTheme()
+ {
+ int value = (int)Registry.GetValue($"{HKeyRoot}\\{HkeyWindowsPersonalizeTheme}", HValueAppTheme, 1);
+ return (AppTheme)value;
+ }
+
+ public static void SetImmersiveDarkMode(IntPtr window, bool enabled)
+ {
+ int useImmersiveDarkMode = enabled ? 1 : 0;
+ _ = DwmSetWindowAttribute(window, DWMWAImmersiveDarkMode, ref useImmersiveDarkMode, sizeof(int));
+ }
+ }
+}
diff --git a/src/common/ManagedCommon/ThemeListener.cs b/src/common/ManagedCommon/ThemeListener.cs
new file mode 100644
index 0000000000..4e74754581
--- /dev/null
+++ b/src/common/ManagedCommon/ThemeListener.cs
@@ -0,0 +1,62 @@
+// Copyright (c) Microsoft Corporation
+// The Microsoft Corporation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using System.Management;
+using System.Security.Principal;
+
+namespace ManagedCommon
+{
+ ///
+ /// The Delegate for a ThemeChanged Event.
+ ///
+ /// Sender ThemeListener
+ public delegate void ThemeChangedEvent(ThemeListener sender);
+
+ public class ThemeListener : IDisposable
+ {
+ ///
+ /// Gets the App Theme.
+ ///
+ public AppTheme AppTheme { get; private set; }
+
+ ///
+ /// An event that fires if the Theme changes.
+ ///
+ public event ThemeChangedEvent ThemeChanged;
+
+ private readonly ManagementEventWatcher watcher;
+
+ public ThemeListener()
+ {
+ var currentUser = WindowsIdentity.GetCurrent();
+ var query = new WqlEventQuery(
+ $"SELECT * FROM RegistryValueChangeEvent WHERE Hive='HKEY_USERS' AND " +
+ $"KeyPath='{currentUser.User.Value}\\\\{ThemeHelpers.HkeyWindowsPersonalizeTheme.Replace("\\", "\\\\")}' AND ValueName='{ThemeHelpers.HValueAppTheme}'");
+ watcher = new ManagementEventWatcher(query);
+ watcher.EventArrived += Watcher_EventArrived;
+ watcher.Start();
+
+ AppTheme = ThemeHelpers.GetAppTheme();
+ }
+
+ private void Watcher_EventArrived(object sender, EventArrivedEventArgs e)
+ {
+ var appTheme = ThemeHelpers.GetAppTheme();
+
+ if (appTheme != AppTheme)
+ {
+ AppTheme = appTheme;
+
+ ThemeChanged?.Invoke(this);
+ }
+ }
+
+ public void Dispose()
+ {
+ watcher.Dispose();
+ GC.SuppressFinalize(this);
+ }
+ }
+}
diff --git a/src/common/Themes/Themes.vcxproj b/src/common/Themes/Themes.vcxproj
index b9335299b1..30a529c62e 100644
--- a/src/common/Themes/Themes.vcxproj
+++ b/src/common/Themes/Themes.vcxproj
@@ -31,10 +31,14 @@
+
+
+
+
diff --git a/src/common/Themes/theme_helpers.cpp b/src/common/Themes/theme_helpers.cpp
new file mode 100644
index 0000000000..280eda9ded
--- /dev/null
+++ b/src/common/Themes/theme_helpers.cpp
@@ -0,0 +1,43 @@
+// Port Based on https://stackoverflow.com/a/62811758/5001796
+#include "theme_helpers.h"
+#include "dwmapi.h"
+#include
+#include
+
+#define DWMWA_USE_IMMERSIVE_DARK_MODE 20
+#define HKEY_WINDOWS_THEME L"Software\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize"
+
+// based on https://stackoverflow.com/questions/51334674/how-to-detect-windows-10-light-dark-mode-in-win32-application
+AppTheme ThemeHelpers::GetAppTheme()
+{
+ // The value is expected to be a REG_DWORD, which is a signed 32-bit little-endian
+ auto buffer = std::vector(4);
+ auto cbData = static_cast(buffer.size() * sizeof(char));
+ auto res = RegGetValueW(
+ HKEY_CURRENT_USER,
+ HKEY_WINDOWS_THEME,
+ L"AppsUseLightTheme",
+ RRF_RT_REG_DWORD, // expected value type
+ nullptr,
+ buffer.data(),
+ &cbData);
+
+ if (res != ERROR_SUCCESS)
+ {
+ return AppTheme::Light;
+ }
+
+ // convert bytes written to our buffer to an int, assuming little-endian
+ auto i = int(buffer[3] << 24 |
+ buffer[2] << 16 |
+ buffer[1] << 8 |
+ buffer[0]);
+
+ return AppTheme(i);
+}
+
+void ThemeHelpers::SetImmersiveDarkMode(HWND window, bool enabled)
+{
+ int useImmersiveDarkMode = enabled ? 1 : 0;
+ DwmSetWindowAttribute(window, DWMWA_USE_IMMERSIVE_DARK_MODE, &useImmersiveDarkMode, sizeof(useImmersiveDarkMode));
+}
diff --git a/src/common/Themes/theme_helpers.h b/src/common/Themes/theme_helpers.h
new file mode 100644
index 0000000000..026a971988
--- /dev/null
+++ b/src/common/Themes/theme_helpers.h
@@ -0,0 +1,14 @@
+#pragma once
+#include
+
+enum class AppTheme
+{
+ Dark = 0,
+ Light = 1
+};
+
+struct ThemeHelpers
+{
+ static AppTheme GetAppTheme();
+ static void ThemeHelpers::SetImmersiveDarkMode(HWND window, bool enabled);
+};
\ No newline at end of file
diff --git a/src/common/Themes/theme_listener.cpp b/src/common/Themes/theme_listener.cpp
new file mode 100644
index 0000000000..4728b049e0
--- /dev/null
+++ b/src/common/Themes/theme_listener.cpp
@@ -0,0 +1,57 @@
+#include "theme_listener.h"
+
+#define HKEY_WINDOWS_THEME L"Software\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize"
+
+DWORD WINAPI _checkTheme(LPVOID lpParam)
+{
+ auto listener = (ThemeListener*)lpParam;
+ listener->CheckTheme();
+ return 0;
+}
+
+void ThemeListener::AddChangedHandler(THEME_HANDLE handle)
+{
+ handles.push_back(handle);
+}
+
+void ThemeListener::DelChangedHandler(THEME_HANDLE handle)
+{
+ auto it = std::find(handles.begin(), handles.end(), handle);
+ handles.erase(it);
+}
+
+void ThemeListener::CheckTheme()
+{
+ HANDLE hEvent;
+ HKEY hKey;
+
+ // Open the Key to listen
+ RegOpenKeyEx(HKEY_CURRENT_USER, HKEY_WINDOWS_THEME, 0, KEY_NOTIFY, &hKey);
+
+ while (true)
+ {
+ // Create an event.
+ hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
+ if (hEvent != 0)
+ {
+ // Watch the registry key for a change of value.
+ RegNotifyChangeKeyValue(hKey,
+ TRUE,
+ REG_NOTIFY_CHANGE_LAST_SET,
+ hEvent,
+ TRUE);
+
+ WaitForSingleObject(hEvent, INFINITE);
+
+ auto _theme = ThemeHelpers::GetAppTheme();
+ if (AppTheme != _theme)
+ {
+ AppTheme = _theme;
+ for (int i = 0; i < handles.size(); i++)
+ {
+ handles[i]();
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/common/Themes/theme_listener.h b/src/common/Themes/theme_listener.h
new file mode 100644
index 0000000000..d9c01925cf
--- /dev/null
+++ b/src/common/Themes/theme_listener.h
@@ -0,0 +1,34 @@
+#include "theme_helpers.h"
+#include "dwmapi.h"
+#include
+#include
+#include
+
+typedef void (*THEME_HANDLE)();
+DWORD WINAPI _checkTheme(LPVOID lpParam);
+
+#pragma once
+class ThemeListener
+{
+public:
+ ThemeListener()
+ {
+ AppTheme = ThemeHelpers::GetAppTheme();
+ dwThreadHandle = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)_checkTheme, this, 0, &dwThreadId);
+ }
+ ~ThemeListener()
+ {
+ CloseHandle(dwThreadHandle);
+ dwThreadId = 0;
+ }
+
+ AppTheme AppTheme;
+ void ThemeListener::AddChangedHandler(THEME_HANDLE handle);
+ void ThemeListener::DelChangedHandler(THEME_HANDLE handle);
+ void CheckTheme();
+
+private:
+ HANDLE dwThreadHandle;
+ DWORD dwThreadId;
+ std::vector handles;
+};
\ No newline at end of file
diff --git a/src/common/Themes/windows_colors.cpp b/src/common/Themes/windows_colors.cpp
index 962c2acf4c..a30329c181 100644
--- a/src/common/Themes/windows_colors.cpp
+++ b/src/common/Themes/windows_colors.cpp
@@ -1,4 +1,5 @@
#include "windows_colors.h"
+#include "theme_helpers.h"
DWORD WindowsColors::rgb_color(DWORD abgr_color)
{
@@ -65,7 +66,7 @@ WindowsColors::Color WindowsColors::get_background_color()
bool WindowsColors::is_dark_mode()
{
- return rgb_color(get_background_color()) == 0;
+ return ThemeHelpers::GetAppTheme() == AppTheme::Dark;
}
bool WindowsColors::update()
diff --git a/src/modules/keyboardmanager/KeyboardManagerEditor/KeyboardManagerEditor.vcxproj b/src/modules/keyboardmanager/KeyboardManagerEditor/KeyboardManagerEditor.vcxproj
index 93758a4a27..2edf88a3dd 100644
--- a/src/modules/keyboardmanager/KeyboardManagerEditor/KeyboardManagerEditor.vcxproj
+++ b/src/modules/keyboardmanager/KeyboardManagerEditor/KeyboardManagerEditor.vcxproj
@@ -102,7 +102,7 @@
./;$(SolutionDir)src\modules\;$(SolutionDir)src\modules\KeyboardManager\KeyboardManagerEditorLibrary\;$(SolutionDir)src\common\Display;$(SolutionDir)src\common\inc;$(SolutionDir)src\common\Telemetry;$(SolutionDir)src;%(AdditionalIncludeDirectories)
- Display.lib;shcore.lib;Dbghelp.lib;%(AdditionalDependencies)
+ Display.lib;shcore.lib;Dbghelp.lib;dwmapi.lib;%(AdditionalDependencies)
$(SolutionDir)$(Platform)\$(ConfigurationName);%(AdditionalLibraryDirectories)
@@ -113,7 +113,7 @@
true
true
- Display.lib;shcore.lib;Dbghelp.lib;%(AdditionalDependencies)
+ Display.lib;shcore.lib;Dbghelp.lib;dwmapi.lib;%(AdditionalDependencies)
$(SolutionDir)$(Platform)\$(ConfigurationName);%(AdditionalLibraryDirectories)
diff --git a/src/modules/keyboardmanager/KeyboardManagerEditorLibrary/EditKeyboardWindow.cpp b/src/modules/keyboardmanager/KeyboardManagerEditorLibrary/EditKeyboardWindow.cpp
index d2fa577f4c..2ef8c732f9 100644
--- a/src/modules/keyboardmanager/KeyboardManagerEditorLibrary/EditKeyboardWindow.cpp
+++ b/src/modules/keyboardmanager/KeyboardManagerEditorLibrary/EditKeyboardWindow.cpp
@@ -22,6 +22,7 @@
#include "UIHelpers.h"
#include "ShortcutErrorType.h"
#include "EditorConstants.h"
+#include
using namespace winrt::Windows::Foundation;
@@ -42,6 +43,20 @@ std::mutex editKeyboardWindowMutex;
// Stores a pointer to the Xaml Bridge object so that it can be accessed from the window procedure
static XamlBridge* xamlBridgePtr = nullptr;
+// Theming
+ThemeListener theme_listener{};
+
+void handleTheme()
+{
+ auto theme = theme_listener.AppTheme;
+ auto isDark = theme == AppTheme::Dark;
+ Logger::info(L"Theme is now {}", isDark ? L"Dark" : L"Light");
+ if (hwndEditKeyboardNativeWindow != nullptr)
+ {
+ ThemeHelpers::SetImmersiveDarkMode(hwndEditKeyboardNativeWindow, isDark);
+ }
+}
+
static IAsyncOperation OrphanKeysConfirmationDialog(
KBMEditor::KeyboardManagerState& state,
const std::vector& keys,
@@ -130,7 +145,7 @@ inline void CreateEditKeyboardWindowImpl(HINSTANCE hInst, KBMEditor::KeyboardMan
48,
48,
LR_DEFAULTCOLOR);
-
+
if (RegisterClassEx(&windowClass) == NULL)
{
MessageBox(NULL, GET_RESOURCE_STRING(IDS_REGISTERCLASSFAILED_ERRORMESSAGE).c_str(), GET_RESOURCE_STRING(IDS_REGISTERCLASSFAILED_ERRORTITLE).c_str(), NULL);
@@ -149,7 +164,7 @@ inline void CreateEditKeyboardWindowImpl(HINSTANCE hInst, KBMEditor::KeyboardMan
DPIAware::ConvertByCursorPosition(windowWidth, windowHeight);
DPIAware::GetScreenDPIForCursor(g_currentDPI);
-
+
// Window Creation
HWND _hWndEditKeyboardWindow = CreateWindow(
szWindowClass,
@@ -163,7 +178,7 @@ inline void CreateEditKeyboardWindowImpl(HINSTANCE hInst, KBMEditor::KeyboardMan
NULL,
hInst,
NULL);
-
+
if (_hWndEditKeyboardWindow == NULL)
{
MessageBox(NULL, GET_RESOURCE_STRING(IDS_CREATEWINDOWFAILED_ERRORMESSAGE).c_str(), GET_RESOURCE_STRING(IDS_CREATEWINDOWFAILED_ERRORTITLE).c_str(), NULL);
@@ -181,12 +196,15 @@ inline void CreateEditKeyboardWindowImpl(HINSTANCE hInst, KBMEditor::KeyboardMan
hwndEditKeyboardNativeWindow = _hWndEditKeyboardWindow;
hwndLock.unlock();
+ handleTheme();
+ theme_listener.AddChangedHandler(handleTheme);
+
// Create the xaml bridge object
XamlBridge xamlBridge(_hWndEditKeyboardWindow);
// DesktopSource needs to be declared before the RelativePanel xamlContainer object to avoid errors
winrt::Windows::UI::Xaml::Hosting::DesktopWindowXamlSource desktopSource;
-
+
// Create the desktop window xaml source object and set its content
hWndXamlIslandEditKeyboardWindow = xamlBridge.InitDesktopWindowsXamlSource(desktopSource);
@@ -253,10 +271,10 @@ inline void CreateEditKeyboardWindowImpl(HINSTANCE hInst, KBMEditor::KeyboardMan
SingleKeyRemapControl::keyboardManagerState = &keyboardManagerState;
KeyDropDownControl::keyboardManagerState = &keyboardManagerState;
KeyDropDownControl::mappingConfiguration = &mappingConfiguration;
-
+
// Clear the single key remap buffer
SingleKeyRemapControl::singleKeyRemapBuffer.clear();
-
+
// Vector to store dynamically allocated control objects to avoid early destruction
std::vector>> keyboardRemapControlObjects;
@@ -378,6 +396,7 @@ inline void CreateEditKeyboardWindowImpl(HINSTANCE hInst, KBMEditor::KeyboardMan
xamlBridgePtr = nullptr;
hWndXamlIslandEditKeyboardWindow = nullptr;
hwndLock.lock();
+ theme_listener.DelChangedHandler(handleTheme);
hwndEditKeyboardNativeWindow = nullptr;
keyboardManagerState.ResetUIState();
keyboardManagerState.ClearRegisteredKeyDelays();
@@ -450,8 +469,7 @@ LRESULT CALLBACK EditKeyboardWindowProc(HWND hWnd, UINT messageCode, WPARAM wPar
rect->top,
rect->right - rect->left,
rect->bottom - rect->top,
- SWP_NOZORDER | SWP_NOACTIVATE
- );
+ SWP_NOZORDER | SWP_NOACTIVATE);
Logger::trace(L"WM_DPICHANGED: new dpi {} rect {} {} ", newDPI, rect->right - rect->left, rect->bottom - rect->top);
}
@@ -487,7 +505,7 @@ bool CheckEditKeyboardWindowActive()
{
ShowWindow(hwndEditKeyboardNativeWindow, SW_RESTORE);
}
-
+
// If there is an already existing window no need to create a new open bring it on foreground.
SetForegroundWindow(hwndEditKeyboardNativeWindow);
result = true;
diff --git a/src/modules/powerrename/PowerRenameUILib/MainWindow.xaml.cpp b/src/modules/powerrename/PowerRenameUILib/MainWindow.xaml.cpp
index 09fe0055a2..273638c61e 100644
--- a/src/modules/powerrename/PowerRenameUILib/MainWindow.xaml.cpp
+++ b/src/modules/powerrename/PowerRenameUILib/MainWindow.xaml.cpp
@@ -21,6 +21,8 @@
#include "microsoft.ui.xaml.window.h"
#include
#include
+#include
+#include
using namespace winrt;
using namespace Windows::UI::Xaml;
@@ -35,15 +37,31 @@ HINSTANCE g_hostHInst;
extern std::vector g_files;
+// Theming
+ThemeListener theme_listener{};
+HWND CurrentWindow;
+
+void handleTheme() {
+ auto theme = theme_listener.AppTheme;
+ auto isDark = theme == AppTheme::Dark;
+ Logger::info(L"Theme is now {}", isDark ? L"Dark" : L"Light");
+ ThemeHelpers::SetImmersiveDarkMode(CurrentWindow, isDark);
+}
+
namespace winrt::PowerRenameUI::implementation
{
MainWindow::MainWindow() :
m_instance{ nullptr }, m_allSelected{ true }, m_managerEvents{ this }
{
-
auto windowNative{ this->try_as<::IWindowNative>() };
winrt::check_bool(windowNative);
windowNative->get_WindowHandle(&m_window);
+ CurrentWindow = m_window;
+
+ // Attach theme handling
+ theme_listener.AddChangedHandler(handleTheme);
+ handleTheme();
+
Microsoft::UI::WindowId windowId =
Microsoft::UI::GetWindowIdFromWindow(m_window);
@@ -69,7 +87,6 @@ namespace winrt::PowerRenameUI::implementation
}
}
-
Microsoft::UI::Windowing::AppWindow appWindow =
Microsoft::UI::Windowing::AppWindow::GetFromWindowId(windowId);
appWindow.SetIcon(PowerRenameUIIco);
diff --git a/src/modules/powerrename/PowerRenameUILib/PowerRenameUI.vcxproj b/src/modules/powerrename/PowerRenameUILib/PowerRenameUI.vcxproj
index 97d78f39d4..4bb7d8663a 100644
--- a/src/modules/powerrename/PowerRenameUILib/PowerRenameUI.vcxproj
+++ b/src/modules/powerrename/PowerRenameUILib/PowerRenameUI.vcxproj
@@ -69,6 +69,9 @@
_DEBUG;%(PreprocessorDefinitions)
+
+ kernel32.lib;user32.lib;dwmapi.lib;%(AdditionalDependencies)
+
@@ -77,6 +80,7 @@
true
true
+ kernel32.lib;user32.lib;dwmapi.lib;%(AdditionalDependencies)
@@ -161,6 +165,9 @@
{d9b8fc84-322a-4f9f-bbb9-20915c47ddfd}
+
+ {98537082-0fdb-40de-abd8-0dc5a4269bab}
+
{cc6e41ac-8174-4e8a-8d22-85dd7f4851df}
diff --git a/src/runner/runner.vcxproj b/src/runner/runner.vcxproj
index 3ddc45fae8..6d89ca5d71 100644
--- a/src/runner/runner.vcxproj
+++ b/src/runner/runner.vcxproj
@@ -39,7 +39,7 @@
AsInvoker
$(OutDir)$(TargetName)$(TargetExt)
- Shcore.lib;gdiplus.lib;Msi.lib;WindowsApp.lib;taskschd.lib;Rstrtmgr.lib;Shlwapi.lib;%(AdditionalDependencies)
+ Shcore.lib;gdiplus.lib;Msi.lib;WindowsApp.lib;taskschd.lib;Rstrtmgr.lib;Shlwapi.lib;dwmapi.lib;%(AdditionalDependencies)
false
diff --git a/src/settings-ui/Settings.UI/App.xaml.cs b/src/settings-ui/Settings.UI/App.xaml.cs
index 26a4702a20..310181053b 100644
--- a/src/settings-ui/Settings.UI/App.xaml.cs
+++ b/src/settings-ui/Settings.UI/App.xaml.cs
@@ -15,7 +15,6 @@ using Microsoft.PowerToys.Settings.UI.Library.Telemetry.Events;
using Microsoft.PowerToys.Telemetry;
using Microsoft.UI.Xaml;
using Windows.UI.Popups;
-using Windows.UI.ViewManagement;
using WinRT.Interop;
namespace Microsoft.PowerToys.Settings.UI
@@ -73,7 +72,7 @@ namespace Microsoft.PowerToys.Settings.UI
{
if (settingsWindow == null)
{
- settingsWindow = new MainWindow();
+ settingsWindow = new MainWindow(IsDarkTheme());
}
settingsWindow.Activate();
@@ -88,6 +87,7 @@ namespace Microsoft.PowerToys.Settings.UI
protected override void OnLaunched(Microsoft.UI.Xaml.LaunchActivatedEventArgs args)
{
var cmdArgs = Environment.GetCommandLineArgs();
+ var isDark = IsDarkTheme();
if (cmdArgs != null && cmdArgs.Length >= RequiredArgumentsQty)
{
@@ -143,7 +143,7 @@ namespace Microsoft.PowerToys.Settings.UI
if (!ShowOobe && !ShowScoobe)
{
- settingsWindow = new MainWindow();
+ settingsWindow = new MainWindow(isDark);
settingsWindow.Activate();
settingsWindow.NavigateToSection(StartupPage);
}
@@ -152,19 +152,19 @@ namespace Microsoft.PowerToys.Settings.UI
// Create the Settings window hidden so that it's fully initialized and
// it will be ready to receive the notification if the user opens
// the Settings from the tray icon.
- settingsWindow = new MainWindow(true);
+ settingsWindow = new MainWindow(isDark, true);
if (ShowOobe)
{
PowerToysTelemetry.Log.WriteEvent(new OobeStartedEvent());
- OobeWindow oobeWindow = new OobeWindow(OOBE.Enums.PowerToysModules.Overview);
+ OobeWindow oobeWindow = new OobeWindow(OOBE.Enums.PowerToysModules.Overview, isDark);
oobeWindow.Activate();
SetOobeWindow(oobeWindow);
}
else if (ShowScoobe)
{
PowerToysTelemetry.Log.WriteEvent(new ScoobeStartedEvent());
- OobeWindow scoobeWindow = new OobeWindow(OOBE.Enums.PowerToysModules.WhatsNew);
+ OobeWindow scoobeWindow = new OobeWindow(OOBE.Enums.PowerToysModules.WhatsNew, isDark);
scoobeWindow.Activate();
SetOobeWindow(scoobeWindow);
}
@@ -174,7 +174,7 @@ namespace Microsoft.PowerToys.Settings.UI
{
// For debugging purposes
// Window is also needed to show MessageDialog
- settingsWindow = new MainWindow();
+ settingsWindow = new MainWindow(isDark);
settingsWindow.Activate();
ShowMessageDialog("The application cannot be run as a standalone process. Please start the application through the runner.", "Forbidden");
}
@@ -203,18 +203,50 @@ namespace Microsoft.PowerToys.Settings.UI
return ipcmanager;
}
+ public static string SelectedTheme()
+ {
+ return SettingsRepository.GetInstance(settingsUtils).SettingsConfig.Theme.ToUpper(CultureInfo.InvariantCulture);
+ }
+
public static bool IsDarkTheme()
{
- var selectedTheme = SettingsRepository.GetInstance(settingsUtils).SettingsConfig.Theme.ToUpper(CultureInfo.InvariantCulture);
- var defaultTheme = new UISettings();
- var uiTheme = defaultTheme.GetColorValue(UIColorType.Background).ToString(System.Globalization.CultureInfo.InvariantCulture);
- return selectedTheme == "DARK" || (selectedTheme == "SYSTEM" && uiTheme == "#FF000000");
+ var selectedTheme = SelectedTheme();
+ return selectedTheme == "DARK" || (selectedTheme == "SYSTEM" && ThemeHelpers.GetAppTheme() == AppTheme.Dark);
+ }
+
+ public static void HandleThemeChange()
+ {
+ var isDark = IsDarkTheme();
+ if (settingsWindow != null)
+ {
+ var hWnd = WinRT.Interop.WindowNative.GetWindowHandle(settingsWindow);
+ ThemeHelpers.SetImmersiveDarkMode(hWnd, isDark);
+ }
+
+ if (oobeWindow != null)
+ {
+ var hWnd = WinRT.Interop.WindowNative.GetWindowHandle(oobeWindow);
+ ThemeHelpers.SetImmersiveDarkMode(hWnd, isDark);
+ }
+
+ var selectedTheme = SelectedTheme();
+ if (selectedTheme == "SYSTEM")
+ {
+ themeListener = new ThemeListener();
+ themeListener.ThemeChanged += (_) => HandleThemeChange();
+ }
+ else if (themeListener != null)
+ {
+ themeListener.Dispose();
+ themeListener = null;
+ }
}
private static ISettingsUtils settingsUtils = new SettingsUtils();
private static MainWindow settingsWindow;
private static OobeWindow oobeWindow;
+ private static ThemeListener themeListener;
public static void ClearSettingsWindow()
{
diff --git a/src/settings-ui/Settings.UI/MainWindow.xaml.cs b/src/settings-ui/Settings.UI/MainWindow.xaml.cs
index b7e111c022..8723a01110 100644
--- a/src/settings-ui/Settings.UI/MainWindow.xaml.cs
+++ b/src/settings-ui/Settings.UI/MainWindow.xaml.cs
@@ -3,7 +3,7 @@
// See the LICENSE file in the project root for more information.
using System;
-using System.Drawing;
+using ManagedCommon;
using Microsoft.PowerLauncher.Telemetry;
using Microsoft.PowerToys.Settings.UI.Helpers;
using Microsoft.PowerToys.Settings.UI.Library.Utilities;
@@ -22,7 +22,7 @@ namespace Microsoft.PowerToys.Settings.UI
///
public sealed partial class MainWindow : Window
{
- public MainWindow(bool createHidden = false)
+ public MainWindow(bool isDark, bool createHidden = false)
{
var bootTime = new System.Diagnostics.Stopwatch();
bootTime.Start();
@@ -36,6 +36,12 @@ namespace Microsoft.PowerToys.Settings.UI
AppWindow appWindow = AppWindow.GetFromWindowId(windowId);
appWindow.SetIcon("icon.ico");
+ // Passed by parameter, as it needs to be evaluated ASAP, otherwise there is a white flash
+ if (isDark)
+ {
+ ThemeHelpers.SetImmersiveDarkMode(hWnd, isDark);
+ }
+
var placement = Utils.DeserializePlacementOrDefault(hWnd);
if (createHidden)
{
@@ -72,7 +78,7 @@ namespace Microsoft.PowerToys.Settings.UI
{
if (App.GetOobeWindow() == null)
{
- App.SetOobeWindow(new OobeWindow(Microsoft.PowerToys.Settings.UI.OOBE.Enums.PowerToysModules.Overview));
+ App.SetOobeWindow(new OobeWindow(Microsoft.PowerToys.Settings.UI.OOBE.Enums.PowerToysModules.Overview, App.IsDarkTheme()));
}
App.GetOobeWindow().Activate();
diff --git a/src/settings-ui/Settings.UI/OobeWindow.xaml.cs b/src/settings-ui/Settings.UI/OobeWindow.xaml.cs
index 8fa07ea7ea..713e06be3d 100644
--- a/src/settings-ui/Settings.UI/OobeWindow.xaml.cs
+++ b/src/settings-ui/Settings.UI/OobeWindow.xaml.cs
@@ -4,6 +4,7 @@
using System;
using interop;
+using ManagedCommon;
using Microsoft.PowerToys.Settings.UI.Helpers;
using Microsoft.PowerToys.Settings.UI.OOBE.Enums;
using Microsoft.PowerToys.Settings.UI.OOBE.Views;
@@ -30,7 +31,7 @@ namespace Microsoft.PowerToys.Settings.UI
private IntPtr _hWnd;
private AppWindow _appWindow;
- public OobeWindow(PowerToysModules initialModule)
+ public OobeWindow(PowerToysModules initialModule, bool isDark)
{
this.InitializeComponent();
@@ -40,6 +41,12 @@ namespace Microsoft.PowerToys.Settings.UI
_appWindow = AppWindow.GetFromWindowId(_windowId);
_appWindow.SetIcon("icon.ico");
+ // Passed by parameter, as it needs to be evaluated ASAP, otherwise there is a white flash
+ if (isDark)
+ {
+ ThemeHelpers.SetImmersiveDarkMode(_hWnd, isDark);
+ }
+
OverlappedPresenter presenter = _appWindow.Presenter as OverlappedPresenter;
presenter.IsMinimizable = false;
presenter.IsMaximizable = false;
diff --git a/src/settings-ui/Settings.UI/Views/GeneralPage.xaml.cs b/src/settings-ui/Settings.UI/Views/GeneralPage.xaml.cs
index 3b74e1ac6b..eb0c40ebfd 100644
--- a/src/settings-ui/Settings.UI/Views/GeneralPage.xaml.cs
+++ b/src/settings-ui/Settings.UI/Views/GeneralPage.xaml.cs
@@ -78,6 +78,7 @@ namespace Microsoft.PowerToys.Settings.UI.Views
break;
}
+ App.HandleThemeChange();
return 0;
}