Merge pull request #324 from DubyaDude/master

Light/Dark themer added for Winform title bar
This commit is contained in:
Natsumi
2022-03-27 03:05:00 +13:00
committed by GitHub
7 changed files with 151 additions and 3 deletions
+5
View File
@@ -415,6 +415,11 @@ namespace VRCX
return System.Globalization.CultureInfo.CurrentCulture.ToString();
}
public void ChangeTheme(int value)
{
WinformThemer.SetGlobalTheme(value);
}
public void SetStartup(bool enabled)
{
try
+1 -1
View File
@@ -13,7 +13,7 @@ using CefSharp.WinForms;
namespace VRCX
{
public partial class MainForm : Form
public partial class MainForm : WinformBase
{
public static MainForm Instance;
public ChromiumWebBrowser Browser;
+5 -1
View File
@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
@@ -119,6 +119,10 @@
<Compile Include="VRCXStorage.cs" />
<Compile Include="JsonSerializer.cs" />
<Compile Include="WinApi.cs" />
<Compile Include="WinformBase.cs">
<SubType>Form</SubType>
</Compile>
<Compile Include="WinformThemer.cs" />
<EmbeddedResource Include="VRForm.resx">
<DependentUpon>VRForm.cs</DependentUpon>
</EmbeddedResource>
+1 -1
View File
@@ -10,7 +10,7 @@ using CefSharp.WinForms;
namespace VRCX
{
public partial class VRForm : Form
public partial class VRForm : WinformBase
{
public static VRForm Instance;
private ChromiumWebBrowser _browser1;
+19
View File
@@ -0,0 +1,19 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace VRCX
{
public class WinformBase : Form
{
protected override void OnHandleCreated(EventArgs e)
{
WinformThemer.SetThemeToGlobal(this);
base.OnHandleCreated(e);
}
}
}
+110
View File
@@ -0,0 +1,110 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace VRCX
{
//Based off DWMWA_USE_IMMERSIVE_DARK_MODE, documentation: https://docs.microsoft.com/en-us/windows/win32/api/dwmapi/ne-dwmapi-dwmwindowattribute
//dwAttribute was 19 before Windows 20H1, 20 after Windows 20H1
internal static class WinformThemer
{
/// <summary>
/// Private holder of current theme
/// </summary>
private static int currentTheme = 0;
/// <summary>
/// Sets the global theme of the app
/// Light = 0
/// Dark = 1
/// </summary>
public static void SetGlobalTheme(int theme)
{
currentTheme = theme;
//Make a seperate list for all current forms (causes issues otherwise)
List<Form> forms = new List<Form>();
foreach(Form form in Application.OpenForms)
{
forms.Add(form);
}
SetThemeToGlobal(forms);
}
/// <summary>
/// Gets the global theme of the app
/// Light = 0
/// Dark = 1
/// </summary>
public static int GetGlobalTheme() => currentTheme;
/// <summary>
/// Set given form to the current global theme
/// </summary>
/// <param name="form"></param>
public static void SetThemeToGlobal(Form form)
{
SetThemeToGlobal(new List<Form>() { form });
}
/// <summary>
/// Set a list of given forms to the current global theme
/// </summary>
/// <param name="forms"></param>
public static void SetThemeToGlobal(List<Form> forms)
{
//For each form, set the theme, then move focus onto it to force refresh
foreach(Form form in forms)
{
//Set the theme of the window
SetThemeToGlobal(form.Handle);
//Change opacity to foce full redraw
form.Opacity = 0.99999;
form.Opacity = 1;
}
}
private static void SetThemeToGlobal(IntPtr handle)
{
if (GetTheme(handle) != currentTheme)
{
if (PInvoke.DwmSetWindowAttribute(handle, 19, new[] { currentTheme }, 4) != 0)
PInvoke.DwmSetWindowAttribute(handle, 20, new[] { currentTheme }, 4);
}
}
private static int GetTheme(IntPtr handle)
{
//Allocate needed memory
IntPtr curThemePtr = Marshal.AllocHGlobal(4);
//See what window state it currently is
if (PInvoke.DwmGetWindowAttribute(handle, 19, curThemePtr, 4) != 0)
PInvoke.DwmGetWindowAttribute(handle, 20, curThemePtr, 4);
//Read current theme (light = 0, dark = 1)
int theme = Marshal.ReadInt32(curThemePtr);
//Free previously allocated
Marshal.FreeHGlobal(curThemePtr);
return theme;
}
internal static class PInvoke
{
[DllImport("DwmApi")]
internal static extern int DwmSetWindowAttribute(IntPtr hwnd, int dwAttribute, int[] pvAttribute, int cbAttribute);
[DllImport("DwmApi")]
internal static extern int DwmGetWindowAttribute(IntPtr hwnd, int dwAttribute, IntPtr pvAttribute, int cbAttribute);
}
}
}
+10
View File
@@ -11397,8 +11397,18 @@ speechSynthesis.getVoices();
$app.watch.isDarkMode = function () {
configRepository.setBool('isDarkMode', this.isDarkMode);
$appDarkStyle.disabled = this.isDarkMode === false;
if (this.isDarkMode) {
AppApi.ChangeTheme(1);
} else {
AppApi.ChangeTheme(0);
}
this.updateVRConfigVars();
};
if ($app.data.isDarkMode) {
AppApi.ChangeTheme(1);
} else {
AppApi.ChangeTheme(0);
}
window
.matchMedia('(prefers-color-scheme: dark)')
.addEventListener('change', (e) => {