mirror of
https://github.com/microsoft/PowerToys.git
synced 2026-04-05 10:46:33 +02:00
Immersive dark mode + Theme Listener (#18315)
* C++ impl of immersive dark mode * Stop using the hardcoded value. * Conjured up theme listener based on registry. * Update MainWindow.xaml.cpp * Update expect.txt * Moved themehelpers to the common themes lib. * Ported theme helpers back to .NET * Update expect.txt * Updated C# Theme Listening logic to mimic the one from Windows Community Toolkit. * Replaced unmanaged code for RegisterForImmersiveDarkMode with unmanaged ThemeListener class. * Fix upstream changes * Update ThemeListener.h * Update ThemeListener.h * Proper formatting * Added handler to Keyboard Manager. * Update EditKeyboardWindow.cpp * Added dwmapi.lib to runner, removed condition from additional dependencies. * Update PowerRenameUI.vcxproj * Added new deps for ManagedCommon to Product.wxs * Crude attempts and understanding installer * Removed Microsoft.Win32.Registry.dll from product.wxs. * Updated dictionary * Renamed ThemeListener class file for consistency, removed unused CheckImmersiveDarkMode in theme_helpers. * Update Themes.vcxproj * Update theme_listener.cpp * Removed SupportsImmersiveDarkMode version check * Removed SupportsImmersiveDarkMode version check * Whoops * Update expect.txt
This commit is contained in:
@@ -17,6 +17,8 @@
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.Win32.Registry" Version="5.0.0" />
|
||||
<PackageReference Include="System.Management" Version="6.0.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -14,4 +14,10 @@ namespace ManagedCommon
|
||||
HighContrastBlack,
|
||||
HighContrastWhite,
|
||||
}
|
||||
|
||||
public enum AppTheme
|
||||
{
|
||||
Dark = 0,
|
||||
Light = 1,
|
||||
}
|
||||
}
|
||||
|
||||
37
src/common/ManagedCommon/ThemeHelpers.cs
Normal file
37
src/common/ManagedCommon/ThemeHelpers.cs
Normal file
@@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
62
src/common/ManagedCommon/ThemeListener.cs
Normal file
62
src/common/ManagedCommon/ThemeListener.cs
Normal file
@@ -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
|
||||
{
|
||||
/// <summary>
|
||||
/// The Delegate for a ThemeChanged Event.
|
||||
/// </summary>
|
||||
/// <param name="sender">Sender ThemeListener</param>
|
||||
public delegate void ThemeChangedEvent(ThemeListener sender);
|
||||
|
||||
public class ThemeListener : IDisposable
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the App Theme.
|
||||
/// </summary>
|
||||
public AppTheme AppTheme { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// An event that fires if the Theme changes.
|
||||
/// </summary>
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -31,10 +31,14 @@
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="icon_helpers.h" />
|
||||
<ClInclude Include="theme_listener.h" />
|
||||
<ClInclude Include="theme_helpers.h" />
|
||||
<ClInclude Include="windows_colors.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="icon_helpers.cpp" />
|
||||
<ClCompile Include="theme_listener.cpp" />
|
||||
<ClCompile Include="theme_helpers.cpp" />
|
||||
<ClCompile Include="windows_colors.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
|
||||
43
src/common/Themes/theme_helpers.cpp
Normal file
43
src/common/Themes/theme_helpers.cpp
Normal file
@@ -0,0 +1,43 @@
|
||||
// Port Based on https://stackoverflow.com/a/62811758/5001796
|
||||
#include "theme_helpers.h"
|
||||
#include "dwmapi.h"
|
||||
#include <windows.h>
|
||||
#include <vector>
|
||||
|
||||
#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<char>(4);
|
||||
auto cbData = static_cast<DWORD>(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));
|
||||
}
|
||||
14
src/common/Themes/theme_helpers.h
Normal file
14
src/common/Themes/theme_helpers.h
Normal file
@@ -0,0 +1,14 @@
|
||||
#pragma once
|
||||
#include <windows.h>
|
||||
|
||||
enum class AppTheme
|
||||
{
|
||||
Dark = 0,
|
||||
Light = 1
|
||||
};
|
||||
|
||||
struct ThemeHelpers
|
||||
{
|
||||
static AppTheme GetAppTheme();
|
||||
static void ThemeHelpers::SetImmersiveDarkMode(HWND window, bool enabled);
|
||||
};
|
||||
57
src/common/Themes/theme_listener.cpp
Normal file
57
src/common/Themes/theme_listener.cpp
Normal file
@@ -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]();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
34
src/common/Themes/theme_listener.h
Normal file
34
src/common/Themes/theme_listener.h
Normal file
@@ -0,0 +1,34 @@
|
||||
#include "theme_helpers.h"
|
||||
#include "dwmapi.h"
|
||||
#include <windows.h>
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
|
||||
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<THEME_HANDLE> handles;
|
||||
};
|
||||
@@ -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()
|
||||
|
||||
@@ -102,7 +102,7 @@
|
||||
<AdditionalIncludeDirectories>./;$(SolutionDir)src\modules\;$(SolutionDir)src\modules\KeyboardManager\KeyboardManagerEditorLibrary\;$(SolutionDir)src\common\Display;$(SolutionDir)src\common\inc;$(SolutionDir)src\common\Telemetry;$(SolutionDir)src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<AdditionalDependencies>Display.lib;shcore.lib;Dbghelp.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalDependencies>Display.lib;shcore.lib;Dbghelp.lib;dwmapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalLibraryDirectories>$(SolutionDir)$(Platform)\$(ConfigurationName);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
@@ -113,7 +113,7 @@
|
||||
<Link>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<AdditionalDependencies>Display.lib;shcore.lib;Dbghelp.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalDependencies>Display.lib;shcore.lib;Dbghelp.lib;dwmapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalLibraryDirectories>$(SolutionDir)$(Platform)\$(ConfigurationName);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
#include "UIHelpers.h"
|
||||
#include "ShortcutErrorType.h"
|
||||
#include "EditorConstants.h"
|
||||
#include <common/Themes/theme_listener.h>
|
||||
|
||||
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<bool> OrphanKeysConfirmationDialog(
|
||||
KBMEditor::KeyboardManagerState& state,
|
||||
const std::vector<DWORD>& 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<std::vector<std::unique_ptr<SingleKeyRemapControl>>> 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;
|
||||
|
||||
@@ -21,6 +21,8 @@
|
||||
#include "microsoft.ui.xaml.window.h"
|
||||
#include <winrt/Microsoft.UI.Interop.h>
|
||||
#include <winrt/Microsoft.UI.Windowing.h>
|
||||
#include <common/Themes/theme_helpers.h>
|
||||
#include <common/Themes/theme_listener.h>
|
||||
|
||||
using namespace winrt;
|
||||
using namespace Windows::UI::Xaml;
|
||||
@@ -35,15 +37,31 @@ HINSTANCE g_hostHInst;
|
||||
|
||||
extern std::vector<std::wstring> 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);
|
||||
|
||||
@@ -69,6 +69,9 @@
|
||||
<ClCompile>
|
||||
<PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<AdditionalDependencies>kernel32.lib;user32.lib;dwmapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)'=='Release'">
|
||||
<ClCompile>
|
||||
@@ -77,6 +80,7 @@
|
||||
<Link>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<AdditionalDependencies>kernel32.lib;user32.lib;dwmapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
@@ -161,6 +165,9 @@
|
||||
<ProjectReference Include="..\..\..\common\logger\logger.vcxproj">
|
||||
<Project>{d9b8fc84-322a-4f9f-bbb9-20915c47ddfd}</Project>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\..\..\common\Themes\Themes.vcxproj">
|
||||
<Project>{98537082-0fdb-40de-abd8-0dc5a4269bab}</Project>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\..\..\common\version\version.vcxproj">
|
||||
<Project>{cc6e41ac-8174-4e8a-8d22-85dd7f4851df}</Project>
|
||||
</ProjectReference>
|
||||
|
||||
@@ -39,7 +39,7 @@
|
||||
<Link>
|
||||
<UACExecutionLevel>AsInvoker</UACExecutionLevel>
|
||||
<OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
|
||||
<AdditionalDependencies>Shcore.lib;gdiplus.lib;Msi.lib;WindowsApp.lib;taskschd.lib;Rstrtmgr.lib;Shlwapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalDependencies>Shcore.lib;gdiplus.lib;Msi.lib;WindowsApp.lib;taskschd.lib;Rstrtmgr.lib;Shlwapi.lib;dwmapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
</Link>
|
||||
<Manifest>
|
||||
<EnableDpiAwareness>false</EnableDpiAwareness>
|
||||
|
||||
@@ -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<GeneralSettings>.GetInstance(settingsUtils).SettingsConfig.Theme.ToUpper(CultureInfo.InvariantCulture);
|
||||
}
|
||||
|
||||
public static bool IsDarkTheme()
|
||||
{
|
||||
var selectedTheme = SettingsRepository<GeneralSettings>.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()
|
||||
{
|
||||
|
||||
@@ -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
|
||||
/// </summary>
|
||||
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();
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -78,6 +78,7 @@ namespace Microsoft.PowerToys.Settings.UI.Views
|
||||
break;
|
||||
}
|
||||
|
||||
App.HandleThemeChange();
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user