Skip to content

Commit 4d606b3

Browse files
committed
implemented light/dark icon set with small/big icon differences
1 parent b426236 commit 4d606b3

File tree

5 files changed

+49
-15
lines changed

5 files changed

+49
-15
lines changed

App/App.xaml.cs

+2
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
using Serilog;
2323
using LaunchActivatedEventArgs = Microsoft.UI.Xaml.LaunchActivatedEventArgs;
2424
using Microsoft.Windows.AppNotifications;
25+
using Coder.Desktop.App.Controls;
2526

2627
namespace Coder.Desktop.App;
2728

@@ -106,6 +107,7 @@ public async Task ExitApplication()
106107
{
107108
_logger.LogDebug("exiting app");
108109
_handleWindowClosed = false;
110+
TitleBarIcon.DisposeIconsManager();
109111
Exit();
110112
var syncController = _services.GetRequiredService<ISyncSessionController>();
111113
await syncController.DisposeAsync();

App/Controls/TitleBarIcon.cs

+41-13
Original file line numberDiff line numberDiff line change
@@ -2,46 +2,74 @@
22
using Microsoft.UI;
33
using Microsoft.UI.Windowing;
44
using Microsoft.UI.Xaml;
5+
using Microsoft.UI.Xaml.Controls.Primitives;
56
using WinRT.Interop;
67

78
namespace Coder.Desktop.App.Controls
89
{
910
public static class TitleBarIcon
1011
{
11-
public static void InjectIcon(Window window)
12-
{
13-
var hwnd = WindowNative.GetWindowHandle(window);
14-
var windowId = Win32Interop.GetWindowIdFromWindow(hwnd);
15-
AppWindow.GetFromWindowId(windowId).SetIcon("coder.ico");
16-
}
12+
private static readonly Lazy<IconsManager> _iconsManager = new(() => new IconsManager());
1713

1814
public static void SetTitlebarIcon(Window window)
1915
{
2016
var hwnd = WindowNative.GetWindowHandle(window);
17+
var theme = window.Content is FrameworkElement fe
18+
? fe.ActualTheme
19+
: ElementTheme.Default;
20+
_iconsManager.Value.SetTitlebarIcon(hwnd, theme == ElementTheme.Dark);
21+
}
2122

22-
string iconPathDark = "Assets/coder_icon_32_dark.ico";
23-
string iconPathLight = "Assets/coder_icon_32_light.ico";
23+
public static void DisposeIconsManager()
24+
{
25+
_iconsManager.Value.Dispose();
26+
}
27+
}
2428

25-
var hIconDark = PInvoke.LoadImage(
29+
#pragma warning disable CsWinRT1028 // Class does not need to be partial, it's an SDK bug:
30+
public class IconsManager : IDisposable
31+
#pragma warning restore CsWinRT1028 // Class is not marked partial
32+
{
33+
private nint hIconDark;
34+
private nint hIconLight;
35+
private const string iconPathDark = "Assets/coder_icon_32_dark.ico";
36+
private const string iconPathLight = "Assets/coder_icon_32_light.ico";
37+
public IconsManager() {
38+
hIconDark = PInvoke.LoadImage(
2639
IntPtr.Zero,
2740
iconPathDark,
2841
PInvoke.IMAGE_ICON,
2942
0,
3043
0,
3144
PInvoke.LR_LOADFROMFILE
3245
);
33-
34-
var hIconLight = PInvoke.LoadImage(
46+
hIconLight = PInvoke.LoadImage(
3547
IntPtr.Zero,
3648
iconPathLight,
3749
PInvoke.IMAGE_ICON,
3850
0,
3951
0,
4052
PInvoke.LR_LOADFROMFILE
4153
);
54+
}
4255

43-
PInvoke.SendMessage(hwnd, PInvoke.WM_SETICON, (IntPtr)PInvoke.ICON_SMALL, hIconDark);
44-
PInvoke.SendMessage(hwnd, PInvoke.WM_SETICON, (IntPtr)PInvoke.ICON_BIG, hIconLight);
56+
public void SetTitlebarIcon(nint windowHandle, bool isDarkTheme)
57+
{
58+
PInvoke.SendMessage(windowHandle, PInvoke.WM_SETICON, (IntPtr)PInvoke.ICON_SMALL, isDarkTheme ? hIconDark : hIconLight);
59+
PInvoke.SendMessage(windowHandle, PInvoke.WM_SETICON, (IntPtr)PInvoke.ICON_BIG, isDarkTheme ? hIconLight : hIconDark);
60+
}
61+
62+
public void Dispose()
63+
{
64+
if (hIconDark != IntPtr.Zero)
65+
{
66+
PInvoke.DestroyIcon(hIconDark);
67+
}
68+
if (hIconLight != IntPtr.Zero)
69+
{
70+
PInvoke.DestroyIcon(hIconLight);
71+
}
72+
GC.SuppressFinalize(this);
4573
}
4674
}
4775
}

App/Views/DirectoryPickerWindow.xaml.cs

+3
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
using Microsoft.UI.Xaml.Media;
99
using WinRT.Interop;
1010
using WinUIEx;
11+
using Coder.Desktop.App.Controls;
1112

1213
namespace Coder.Desktop.App.Views;
1314

@@ -16,6 +17,8 @@ public sealed partial class DirectoryPickerWindow : WindowEx
1617
public DirectoryPickerWindow(DirectoryPickerViewModel viewModel)
1718
{
1819
InitializeComponent();
20+
TitleBarIcon.SetTitlebarIcon(this);
21+
1922
SystemBackdrop = new DesktopAcrylicBackdrop();
2023

2124
viewModel.Initialize(this, DispatcherQueue);

App/Views/FileSyncListWindow.xaml.cs

+2-2
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,13 @@ public FileSyncListWindow(FileSyncListViewModel viewModel)
2020
{
2121
ViewModel = viewModel;
2222
InitializeComponent();
23+
TitleBarIcon.SetTitlebarIcon(this);
24+
2325
SystemBackdrop = new DesktopAcrylicBackdrop();
2426

2527
ViewModel.Initialize(this, DispatcherQueue);
2628
RootFrame.Content = new FileSyncListMainPage(ViewModel);
2729

28-
TitleBarIcon.SetTitlebarIcon(this);
29-
3030
this.CenterOnScreen();
3131
}
3232

App/Views/SignInWindow.xaml.cs

+1
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ public sealed partial class SignInWindow : Window
2222
public SignInWindow(SignInViewModel viewModel)
2323
{
2424
InitializeComponent();
25+
TitleBarIcon.SetTitlebarIcon(this);
2526
SystemBackdrop = new DesktopAcrylicBackdrop();
2627
RootFrame.SizeChanged += RootFrame_SizeChanged;
2728

0 commit comments

Comments
 (0)