Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support dark theme on title bar #6

Open
wants to merge 9 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 55 additions & 0 deletions skiffWindowsApp/Skiff Desktop/MainWindow.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
using System.Windows.Media;
using System.Windows.Interop;
using System.Windows.Media.Imaging;
using System.Runtime.InteropServices;
using Skiff_Desktop.Utilities;

namespace Skiff_Desktop
{
Expand All @@ -32,6 +34,48 @@ public partial class MainWindow : Window
private MessageProcessor _messageProcessor;
private PreferencesController _preferencesController;

#region Dark Theme

private ThemeWatcher _themeWatcher;

[DllImport("dwmapi.dll", PreserveSig = true)]
static extern int DwmSetWindowAttribute(IntPtr hwnd, int attr, ref int attrValue, int attrSize);
const int DWMWA_CAPTION_COLOR = 35;
Comment on lines +41 to +43
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Opinion: Ideally, rather than write PInvokes by hand, we would wire up CsWin32 here.


private void SetDarkMode(bool darkMode)
{
WindowInteropHelper hwnd = new WindowInteropHelper(this);
if (hwnd.Handle == IntPtr.Zero)
return;

// Match sidebar color
int color = darkMode ? 0x1A1A1A : 0xF5F5F5;
try
{
DwmSetWindowAttribute(
hwnd.Handle,
DWMWA_CAPTION_COLOR,
ref color, Marshal.SizeOf(color)
);
}
catch (EntryPointNotFoundException)
{
// Not supported (old Windows version?)
}
}

public void SetTheme(string? theme)
{
bool darkMode = false;
if (theme == null || theme.Contains("system"))
darkMode = _themeWatcher.GetCurrentTheme() == ThemeMode.Dark;
else
darkMode = (theme.Contains("dark"));

SetDarkMode(darkMode);
}

#endregion

public MainWindow()
{
Expand Down Expand Up @@ -150,6 +194,17 @@ private async Task InitializeBrowser()
WebView2.CoreWebView2.Settings.IsGeneralAutofillEnabled = true;
WebView2.CoreWebView2.Settings.IsStatusBarEnabled = false;

// Watch for system theme changes
_themeWatcher = new ThemeWatcher();
_themeWatcher.ThemeChanged += (sender, theme) => Dispatcher.Invoke(() =>
{
// Update WebView default theme
WebView2.CoreWebView2.Profile.PreferredColorScheme =
(theme == ThemeMode.Dark) ? CoreWebView2PreferredColorScheme.Dark : CoreWebView2PreferredColorScheme.Light;

});
_themeWatcher.Start();

// this is needed to allow the webview to communicate with the app
// right now, only for sending notifications
WebView2.CoreWebView2.WebMessageReceived += _messageProcessor.CoreWebView2_WebMessageReceived;
Expand Down
13 changes: 13 additions & 0 deletions skiffWindowsApp/Skiff Desktop/MessageProcessor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ public enum MessageTypes
{
newMessageNotifications,
unreadMailCount,
theme,
}

internal class MessageProcessor
Expand Down Expand Up @@ -56,6 +57,12 @@ internal void CoreWebView2_WebMessageReceived(object sender, CoreWebView2WebMess
_mainWindow.UpdateUnreadCount(counterPayload.UnreadCount);
break;

case MessageTypes.theme:
var themePayload = JsonSerializer.Deserialize<ThemeChange>(receivedMessage.Data.ToString());
Debug.WriteLine($"Updating theme: {themePayload.Theme}.");
_mainWindow.SetTheme(themePayload.Theme);
break;

default:
Debug.WriteLine("Message type is not ‘newMessageNotifications’. Skipping.");
break;
Expand Down Expand Up @@ -98,6 +105,12 @@ public class UnreadCountDataWrapper
public int UnreadCount { get; set; }
}

public class ThemeChange
{
[JsonPropertyName("theme")]
public string Theme { get; set; }
}

#endregion
}
}
1 change: 1 addition & 0 deletions skiffWindowsApp/Skiff Desktop/Skiff Desktop.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@

<ItemGroup>
<PackageReference Include="Microsoft.Web.WebView2" Version="1.0.1988-prerelease" />
<PackageReference Include="System.Management" Version="7.0.2" />
<PackageReference Include="ToastNotifications" Version="2.5.1" />
<PackageReference Include="ToastNotifications.Messages" Version="2.5.1" />
</ItemGroup>
Expand Down
88 changes: 88 additions & 0 deletions skiffWindowsApp/Skiff Desktop/Utilities/ThemeWatcher.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
using Microsoft.Win32;
using System;
using System.Diagnostics;
using System.Globalization;
using System.Management;
using System.Security.Principal;

namespace Skiff_Desktop.Utilities
{
public enum ThemeMode
{
Light = 0,
Dark = 1,
}

public class ThemeWatcher
{
public ThemeMode Theme
{
get => _theme;
private set {
_theme = value;
ThemeChanged?.Invoke(this, value);
}
}
public event EventHandler<ThemeMode>? ThemeChanged;

private const string RegistryPath = @"Software\Microsoft\Windows\CurrentVersion\Themes\Personalize";
private const string RegistryValueName = "AppsUseLightTheme";
private ManagementEventWatcher? watcher;
private ThemeMode _theme;

public void Start()
{
var currentUser = WindowsIdentity.GetCurrent();

// Based on https://stackoverflow.com/a/69604613
string query = string.Format(
CultureInfo.InvariantCulture,
@"SELECT * FROM RegistryValueChangeEvent WHERE Hive = 'HKEY_USERS' AND KeyPath = '{0}\\{1}' AND ValueName = '{2}'",
currentUser.User?.Value,
RegistryPath.Replace(@"\", @"\\"),
RegistryValueName);

try
{
Theme = GetCurrentTheme();

// Listen for events
watcher = new ManagementEventWatcher(query);
watcher.EventArrived += (sender, e) =>
{
Theme = GetCurrentTheme();
Debug.WriteLine("System theme changed: " + Theme);
};
watcher.Start();
}
catch (Exception)
{
// This can fail on Windows 7
Theme = ThemeMode.Light;
}
}

public ThemeMode GetCurrentTheme()
{
ThemeMode theme = ThemeMode.Light;

try
{
using (RegistryKey? key = Registry.CurrentUser?.OpenSubKey(RegistryPath))
{
object? value = key?.GetValue(RegistryValueName);
if (value == null)
return ThemeMode.Light;

theme = (int)value > 0 ? ThemeMode.Light : ThemeMode.Dark;
}

return theme;
}
catch (Exception)
{
return theme;
}
}
}
}