Skip to content

Commit

Permalink
feat(theme): Limited support for FrameworkElement.RequestedTheme
Browse files Browse the repository at this point in the history
Setting FrameworkElement.RequestedTheme will apply the nominated RequestedTheme to the entire application. This does not allow setting a different RequestedTheme for individual pages or controls, but does support the use case of overriding the system theme while the application is running.
  • Loading branch information
davidjohnoliver committed Aug 4, 2020
1 parent 293dfe8 commit b543db1
Show file tree
Hide file tree
Showing 8 changed files with 105 additions and 21 deletions.
4 changes: 2 additions & 2 deletions src/Uno.UI.Tests/Windows_UI_Xaml/Given_ResourceDictionary.cs
Original file line number Diff line number Diff line change
Expand Up @@ -221,13 +221,13 @@ public void When_Has_Multiple_Themes()
#if !NETFX_CORE //Not legal on UWP to change theme after app has launched
try
{
Application.Current.SetRequestedTheme(ApplicationTheme.Dark);
Application.Current.SetExplicitRequestedTheme(ApplicationTheme.Dark);
var retrieved2 = rd["Blu"];
Assert.AreEqual(Colors.DarkBlue, ((SolidColorBrush)retrieved2).Color);
}
finally
{
Application.Current.SetRequestedTheme(ApplicationTheme.Light);
Application.Current.SetExplicitRequestedTheme(ApplicationTheme.Light);
}
#endif
}
Expand Down
24 changes: 23 additions & 1 deletion src/Uno.UI.Tests/Windows_UI_Xaml/Given_ThemeResource.cs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,28 @@ public async Task When_Themed_Color_Theme_Changed()
Assert.AreEqual(Colors.DarkBlue, (page.TestBorder.Background as SolidColorBrush).Color);
}

[TestMethod]
public void When_Element_Theme_Changed()
{
var page = new ThemeResource_Themed_Color_Page();
var app = UnitTestsApp.App.EnsureApplication();
app.HostView.Children.Add(page);

Assert.AreEqual(Colors.LightBlue, (page.TestBorder.Background as SolidColorBrush).Color);

page.RequestedTheme = ElementTheme.Dark;
Assert.AreEqual(Colors.DarkBlue, (page.TestBorder.Background as SolidColorBrush).Color);

page.RequestedTheme = ElementTheme.Light;
Assert.AreEqual(Colors.LightBlue, (page.TestBorder.Background as SolidColorBrush).Color);

page.RequestedTheme = ElementTheme.Dark;
Assert.AreEqual(Colors.DarkBlue, (page.TestBorder.Background as SolidColorBrush).Color);

page.RequestedTheme = ElementTheme.Default;
Assert.AreEqual(Colors.LightBlue, (page.TestBorder.Background as SolidColorBrush).Color);
}

[TestMethod]
public async Task When_Visual_States_Keyframe_Theme_Changed_Reapplied()
{
Expand Down Expand Up @@ -471,7 +493,7 @@ private static async Task<bool> SwapSystemTheme()

await _swapTask;
#else
Application.Current.SetRequestedTheme(targetTheme);
Application.Current.SetExplicitRequestedTheme(targetTheme);
#endif
Assert.AreEqual(targetTheme, Application.Current.RequestedTheme);
return true;
Expand Down
22 changes: 22 additions & 0 deletions src/Uno.UI/Extensions/ElementThemeExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Uno.Extensions;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;

namespace Uno.UI.Extensions
{
internal static class ElementThemeExtensions
{
public static ApplicationTheme? ToApplicationThemeOrDefault(this ElementTheme elementTheme)
=> elementTheme switch
{
ElementTheme.Default => null,
ElementTheme.Light => ApplicationTheme.Light,
ElementTheme.Dark => ApplicationTheme.Dark,
_ => throw new ArgumentException()
};
}
}
20 changes: 10 additions & 10 deletions src/Uno.UI/Generated/3.0.0.0/Windows.UI.Xaml/ElementTheme.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,21 @@
#pragma warning disable 114 // new keyword hiding
namespace Windows.UI.Xaml
{
#if __ANDROID__ || __IOS__ || NET461 || __WASM__ || __SKIA__ || __NETSTD_REFERENCE__ || __MACOS__
#if __ANDROID__ || __IOS__ || NET461 || __WASM__ || __SKIA__ || __NETSTD_REFERENCE__ || __MACOS__
#if false
#if false
[global::Uno.NotImplemented]
#endif
#endif
public enum ElementTheme
{
#if __ANDROID__ || __IOS__ || NET461 || __WASM__ || __SKIA__ || __NETSTD_REFERENCE__ || __MACOS__
#if false
Default,
#endif
#if __ANDROID__ || __IOS__ || NET461 || __WASM__ || __SKIA__ || __NETSTD_REFERENCE__ || __MACOS__
#endif
#if false
Light,
#endif
#if __ANDROID__ || __IOS__ || NET461 || __WASM__ || __SKIA__ || __NETSTD_REFERENCE__ || __MACOS__
#endif
#if false
Dark,
#endif
#endif
}
#endif
#endif
}
Original file line number Diff line number Diff line change
Expand Up @@ -179,8 +179,8 @@ public double Height
}
}
#endif
#if __ANDROID__ || __IOS__ || NET461 || __WASM__ || __SKIA__ || __NETSTD_REFERENCE__ || __MACOS__
[global::Uno.NotImplemented("__ANDROID__", "__IOS__", "NET461", "__WASM__", "__SKIA__", "__NETSTD_REFERENCE__", "__MACOS__")]
#if false
[global::Uno.NotImplemented]
public global::Windows.UI.Xaml.ElementTheme RequestedTheme
{
get
Expand Down Expand Up @@ -416,8 +416,7 @@ public bool AllowFocusOnInteraction
typeof(global::Windows.UI.Xaml.FrameworkElement),
new FrameworkPropertyMetadata(default(double)));
#endif
#if __ANDROID__ || __IOS__ || NET461 || __WASM__ || __SKIA__ || __NETSTD_REFERENCE__ || __MACOS__
[global::Uno.NotImplemented("__ANDROID__", "__IOS__", "NET461", "__WASM__", "__SKIA__", "__NETSTD_REFERENCE__", "__MACOS__")]
#if false
public static global::Windows.UI.Xaml.DependencyProperty RequestedThemeProperty { get; } =
Windows.UI.Xaml.DependencyProperty.Register(
nameof(RequestedTheme), typeof(global::Windows.UI.Xaml.ElementTheme),
Expand Down
14 changes: 10 additions & 4 deletions src/Uno.UI/UI/Xaml/Application.cs
Original file line number Diff line number Diff line change
Expand Up @@ -77,12 +77,18 @@ public ApplicationTheme RequestedTheme
{
throw new NotSupportedException("Operation not supported");
}
// this flag makes sure the app will not respond to OS events
_themeSetExplicitly = true;
SetRequestedTheme(value);
SetExplicitRequestedTheme(value);
}
}

internal void SetExplicitRequestedTheme(ApplicationTheme? explicitTheme)
{
// this flag makes sure the app will not respond to OS events
_themeSetExplicitly = explicitTheme.HasValue;
var theme = explicitTheme ?? GetDefaultSystemTheme();
SetRequestedTheme(theme);
}

public ResourceDictionary Resources { get; } = new ResourceDictionary();

#pragma warning disable CS0067 // The event is never used
Expand Down Expand Up @@ -174,7 +180,7 @@ internal void RaiseWindowCreated(Window window)
OnWindowCreated(new WindowCreatedEventArgs(window));
}

internal void SetRequestedTheme(ApplicationTheme requestedTheme)
private void SetRequestedTheme(ApplicationTheme requestedTheme)
{
if (requestedTheme != _requestedTheme)
{
Expand Down
9 changes: 9 additions & 0 deletions src/Uno.UI/UI/Xaml/ElementTheme.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
namespace Windows.UI.Xaml
{
public enum ElementTheme
{
Default,
Light,
Dark,
}
}
26 changes: 26 additions & 0 deletions src/Uno.UI/UI/Xaml/FrameworkElement.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
using System.ComponentModel;
using Uno.UI.DataBinding;
using Uno.UI.Xaml;
using Uno.UI.Extensions;
#if XAMARIN_ANDROID
using View = Android.Views.View;
#elif XAMARIN_IOS_UNIFIED
Expand Down Expand Up @@ -378,6 +379,31 @@ private void UpdateActiveStyle()

private Style ResolveImplicitStyle() => (this as IDependencyObjectStoreProvider).Store.GetImplicitStyle();

#region Requested theme dependency property

public ElementTheme RequestedTheme
{
get => (ElementTheme)GetValue(RequestedThemeProperty);
set => SetValue(RequestedThemeProperty, value);
}

public static DependencyProperty RequestedThemeProperty { get; } =
DependencyProperty.Register(
nameof(RequestedTheme),
typeof(ElementTheme),
typeof(FrameworkElement),
new PropertyMetadata(
ElementTheme.Default,
(o, e) => ((FrameworkElement)o).OnRequestedThemeChanged((ElementTheme)e.OldValue, (ElementTheme)e.NewValue)));

private void OnRequestedThemeChanged(ElementTheme oldValue, ElementTheme newValue)
// This is an ultra-naive implementation... but nonetheless enables the common use case of overriding the system theme for
// the entire visual tree (since Application.RequestedTheme cannot be set after launch)
=> Application.Current.SetExplicitRequestedTheme(newValue.ToApplicationThemeOrDefault());


#endregion

/// <summary>
/// Replace previous style with new style, at nominated precedence. This method is called separately for the user-determined
/// 'active style' and for the baked-in 'default style'.
Expand Down

0 comments on commit b543db1

Please sign in to comment.