Browse Source

allow setting ThemeVariant to Default to reset theme to system on android

pull/16340/head
Emmanuel Hansen 2 years ago
parent
commit
91348e2242
  1. 2
      samples/SafeAreaDemo/App.xaml
  2. 1
      samples/SafeAreaDemo/Views/MainView.xaml
  3. 51
      src/Android/Avalonia.Android/AvaloniaView.cs
  4. 5
      src/Android/Avalonia.Android/Platform/AndroidInsetsManager.cs
  5. 8
      src/Android/Avalonia.Android/Platform/AndroidPlatformSettings.cs
  6. 17
      src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs
  7. 6
      src/Avalonia.Base/Styling/ThemeVariant.cs
  8. 14
      src/Avalonia.Controls/Application.cs
  9. 2
      src/Avalonia.Controls/Embedding/Offscreen/OffscreenTopLevelImpl.cs
  10. 2
      src/Avalonia.Controls/Platform/ITopLevelImpl.cs
  11. 5
      src/Avalonia.Controls/ThemeVariantScope.cs
  12. 17
      src/Avalonia.Controls/TopLevel.cs
  13. 2
      src/Avalonia.DesignerSupport/Remote/Stubs.cs
  14. 2
      src/Avalonia.Native/TopLevelImpl.cs
  15. 4
      src/Avalonia.Native/WindowImplBase.cs
  16. 2
      src/Avalonia.X11/X11Window.cs
  17. 2
      src/Browser/Avalonia.Browser/BrowserTopLevelImpl.cs
  18. 2
      src/Headless/Avalonia.Headless/HeadlessWindowImpl.cs
  19. 2
      src/Linux/Avalonia.LinuxFramebuffer/FramebufferToplevelImpl.cs
  20. 4
      src/Windows/Avalonia.Win32/WindowImpl.cs
  21. 2
      src/iOS/Avalonia.iOS/AvaloniaView.cs
  22. 2
      tests/Avalonia.UnitTests/CompositorTestServices.cs

2
samples/SafeAreaDemo/App.xaml

@ -2,7 +2,7 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:SafeAreaDemo"
x:Class="SafeAreaDemo.App"
RequestedThemeVariant="Light">
RequestedThemeVariant="Default">
<Application.DataTemplates>
<local:ViewLocator/>
</Application.DataTemplates>

1
samples/SafeAreaDemo/Views/MainView.xaml

@ -8,7 +8,6 @@
d:DesignHeight="450"
x:Class="SafeAreaDemo.Views.MainView"
x:DataType="vm:MainViewModel"
Background="#ccc"
TopLevel.AutoSafeAreaPadding="{Binding AutoSafeAreaPadding, Mode=TwoWay}">
<Grid HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"

51
src/Android/Avalonia.Android/AvaloniaView.cs

@ -17,21 +17,18 @@ namespace Avalonia.Android
{
public class AvaloniaView : FrameLayout
{
private EmbeddableControlRoot _root;
private EmbeddableControlRoot? _root;
private readonly ViewImpl _view;
private IDisposable? _timerSubscription;
private object? _content;
public AvaloniaView(Context context) : base(context)
{
_view = new ViewImpl(this);
AddView(_view.View);
_root = new EmbeddableControlRoot(_view);
_root.Prepare();
this.SetBackgroundColor(global::Android.Graphics.Color.Transparent);
OnConfigurationChanged();
}
internal TopLevelImpl TopLevelImpl => _view;
@ -39,15 +36,42 @@ namespace Avalonia.Android
public object? Content
{
get { return _root.Content; }
set { _root.Content = value; }
get { return _root?.Content; }
set
{
_content = null;
if (_root != null)
_root.Content = value;
else
{
_content = value;
}
}
}
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
}
protected override void OnDetachedFromWindow()
{
base.OnDetachedFromWindow();
_root?.Dispose();
_root = null!;
_root = null;
}
protected override void OnAttachedToWindow()
{
_root = new EmbeddableControlRoot(_view);
_root.Prepare();
if(_content != null)
{
_root.Content = _content;
}
SendConfigurationChanged(Context?.Resources?.Configuration);
base.OnAttachedToWindow();
}
public override bool DispatchKeyEvent(KeyEvent? e)
@ -70,6 +94,8 @@ namespace Avalonia.Android
internal void OnVisibilityChanged(bool isVisible)
{
if (_root == null)
return;
if (isVisible && _timerSubscription == null)
{
if (AvaloniaLocator.Current.GetService<IRenderTimer>() is ChoreographerTimer timer)
@ -95,16 +121,17 @@ namespace Avalonia.Android
protected override void OnConfigurationChanged(Configuration? newConfig)
{
base.OnConfigurationChanged(newConfig);
OnConfigurationChanged();
SendConfigurationChanged(newConfig ?? Context?.Resources?.Configuration);
}
private void OnConfigurationChanged()
private void SendConfigurationChanged(Configuration? newConfig)
{
if (Context is { } context)
_view?.InsetsManager?.SetDefaultSystemLightMode(!(newConfig?.UiMode.HasFlag(UiMode.NightYes) ?? false));
if (Context is { } context && newConfig is { } config)
{
var settings =
AvaloniaLocator.Current.GetRequiredService<IPlatformSettings>() as AndroidPlatformSettings;
settings?.OnViewConfigurationChanged(context);
settings?.OnViewConfigurationChanged(context, config);
}
}

5
src/Android/Avalonia.Android/Platform/AndroidInsetsManager.cs

@ -166,6 +166,11 @@ namespace Avalonia.Android.Platform
return insets;
}
internal void SetDefaultSystemLightMode(bool islightMode)
{
_isDefaultSystemBarLightTheme = islightMode;
}
private void NotifySafeAreaChanged(Thickness safeAreaPadding)
{
Dispatcher.UIThread.Send(_ => SafeAreaChanged?.Invoke(this, new SafeAreaChangedArgs(safeAreaPadding)));

8
src/Android/Avalonia.Android/Platform/AndroidPlatformSettings.cs

@ -22,14 +22,14 @@ internal class AndroidPlatformSettings : DefaultPlatformSettings
return _latestValues;
}
internal void OnViewConfigurationChanged(Context context)
internal void OnViewConfigurationChanged(Context context, Configuration configuration)
{
if (context.Resources?.Configuration is null)
if (context.Resources is null)
{
return;
}
var systemTheme = (context.Resources.Configuration.UiMode & UiMode.NightMask) switch
PlatformThemeVariant systemTheme = (configuration.UiMode & UiMode.NightMask) switch
{
UiMode.NightYes => PlatformThemeVariant.Dark,
UiMode.NightNo => PlatformThemeVariant.Light,
@ -71,7 +71,7 @@ internal class AndroidPlatformSettings : DefaultPlatformSettings
}
finally
{
array.Recycle();
array.Recycle();
}
}
}

17
src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs

@ -282,9 +282,9 @@ namespace Avalonia.Android.Platform.SkiaPlatform
}
}
public void SetFrameThemeVariant(PlatformThemeVariant themeVariant)
public void SetFrameThemeVariant(PlatformThemeVariant? themeVariant)
{
if(_insetsManager != null)
if (_insetsManager != null)
{
_insetsManager.SystemBarTheme = themeVariant switch
{
@ -294,7 +294,16 @@ namespace Avalonia.Android.Platform.SkiaPlatform
};
}
AppCompatDelegate.DefaultNightMode = themeVariant == PlatformThemeVariant.Light ? AppCompatDelegate.ModeNightNo : AppCompatDelegate.ModeNightYes;
AppCompatDelegate.DefaultNightMode = themeVariant == null ? AppCompatDelegate.ModeNightFollowSystem :
themeVariant == PlatformThemeVariant.Light ? AppCompatDelegate.ModeNightNo : AppCompatDelegate.ModeNightYes;
if (AppCompatDelegate.DefaultNightMode == AppCompatDelegate.ModeNightFollowSystem && _view.Context is { } context
&& context.Resources?.Configuration is { } config)
{
var settings =
AvaloniaLocator.Current.GetRequiredService<IPlatformSettings>() as AndroidPlatformSettings;
settings?.OnViewConfigurationChanged(context, config);
}
}
public AcrylicPlatformCompensationLevels AcrylicCompensationLevels => new AcrylicPlatformCompensationLevels(1, 1, 1);
@ -305,6 +314,8 @@ namespace Avalonia.Android.Platform.SkiaPlatform
public double Scaling => RenderScaling;
internal AndroidInsetsManager? InsetsManager => _insetsManager;
public void SetTransparencyLevelHint(IReadOnlyList<WindowTransparencyLevel> transparencyLevels)
{
if (_view.Context is not AvaloniaMainActivity activity)

6
src/Avalonia.Base/Styling/ThemeVariant.cs

@ -100,11 +100,11 @@ public sealed record ThemeVariant
{
PlatformThemeVariant.Light => Light,
PlatformThemeVariant.Dark => Dark,
_ => throw new ArgumentOutOfRangeException(nameof(themeVariant), themeVariant, null)
_ => Default,
};
}
public static explicit operator PlatformThemeVariant?(ThemeVariant themeVariant)
public static explicit operator PlatformThemeVariant?(ThemeVariant? themeVariant)
{
if (themeVariant == Light)
{
@ -114,7 +114,7 @@ public sealed record ThemeVariant
{
return PlatformThemeVariant.Dark;
}
else if (themeVariant.InheritVariant is { } inheritVariant)
else if (themeVariant?.InheritVariant is { } inheritVariant)
{
return (PlatformThemeVariant?)inheritVariant;
}

14
src/Avalonia.Controls/Application.cs

@ -68,6 +68,11 @@ namespace Avalonia
/// <inheritdoc/>
public event EventHandler? ActualThemeVariantChanged;
static Application()
{
RequestedThemeVariantProperty.OverrideDefaultValue<Application>(ThemeVariant.Default);
}
/// <summary>
/// Creates an instance of the <see cref="Application"/> class.
/// </summary>
@ -364,20 +369,19 @@ namespace Avalonia
if (change.Property == RequestedThemeVariantProperty)
{
if (change.GetNewValue<ThemeVariant>() is {} themeVariant && themeVariant != ThemeVariant.Default)
if (change.GetNewValue<ThemeVariant>() is { } themeVariant)
SetValue(ActualThemeVariantProperty, themeVariant);
else
ClearValue(ActualThemeVariantProperty);
}
else if (change.Property == ActualThemeVariantProperty)
{
ActualThemeVariantChanged?.Invoke(this, EventArgs.Empty);
}
}
private void OnColorValuesChanged(object? sender, PlatformColorValues e)
{
SetValue(ActualThemeVariantProperty, (ThemeVariant)e.ThemeVariant, BindingPriority.Template);
if ((RequestedThemeVariant ?? ThemeVariant.Default) == ThemeVariant.Default)
SetCurrentValue(ActualThemeVariantProperty, (ThemeVariant)e.ThemeVariant);
}
}
}

2
src/Avalonia.Controls/Embedding/Offscreen/OffscreenTopLevelImpl.cs

@ -61,7 +61,7 @@ namespace Avalonia.Controls.Embedding.Offscreen
public Action<WindowTransparencyLevel>? TransparencyLevelChanged { get; set; }
public void SetFrameThemeVariant(PlatformThemeVariant themeVariant) { }
public void SetFrameThemeVariant(PlatformThemeVariant? themeVariant) { }
public AcrylicPlatformCompensationLevels AcrylicCompensationLevels { get; } = new AcrylicPlatformCompensationLevels(1, 1, 1);

2
src/Avalonia.Controls/Platform/ITopLevelImpl.cs

@ -136,6 +136,6 @@ namespace Avalonia.Platform
/// Sets the <see cref="PlatformThemeVariant"/> on the frame if it should be dark or light.
/// Also applies for the mobile status bar.
/// </summary>
void SetFrameThemeVariant(PlatformThemeVariant themeVariant);
void SetFrameThemeVariant(PlatformThemeVariant? themeVariant);
}
}

5
src/Avalonia.Controls/ThemeVariantScope.cs

@ -15,6 +15,11 @@ namespace Avalonia.Controls
public static readonly StyledProperty<ThemeVariant?> RequestedThemeVariantProperty =
ThemeVariant.RequestedThemeVariantProperty.AddOwner<ThemeVariantScope>();
static ThemeVariantScope()
{
RequestedThemeVariantProperty.OverrideDefaultValue<ThemeVariantScope>(ThemeVariant.Default);
}
/// <summary>
/// Gets or sets the UI theme variant that is used by the control (and its child elements) for resource determination.
/// The UI theme you specify with ThemeVariant can override the app-level ThemeVariant.

17
src/Avalonia.Controls/TopLevel.cs

@ -233,11 +233,6 @@ namespace Avalonia.Controls
impl.TransparencyLevelChanged = HandleTransparencyLevelChanged;
CreatePlatformImplBinding(TransparencyLevelHintProperty, hint => PlatformImpl.SetTransparencyLevelHint(hint ?? Array.Empty<WindowTransparencyLevel>()));
CreatePlatformImplBinding(ActualThemeVariantProperty, variant =>
{
variant ??= ThemeVariant.Default;
PlatformImpl?.SetFrameThemeVariant((PlatformThemeVariant?)variant ?? PlatformThemeVariant.Light);
});
_keyboardNavigationHandler?.SetOwner(this);
_accessKeyHandler?.SetOwner(this);
@ -252,6 +247,16 @@ namespace Avalonia.Controls
SetValue(ActualThemeVariantProperty, _applicationThemeHost.ActualThemeVariant, BindingPriority.Template);
_applicationThemeHost.ActualThemeVariantChanged += GlobalActualThemeVariantChanged;
}
CreatePlatformImplBinding(ActualThemeVariantProperty, variant =>
{
if(_applicationThemeHost is AvaloniaObject element)
{
if (element.GetValue(ThemeVariantScope.RequestedThemeVariantProperty) is { } requestedThemeVariant)
variant = requestedThemeVariant == ThemeVariant.Default ? ThemeVariant.Default : variant;
}
variant ??= ThemeVariant.Default;
PlatformImpl?.SetFrameThemeVariant((PlatformThemeVariant?)variant);
});
ClientSize = impl.ClientSize;
@ -878,7 +883,7 @@ namespace Avalonia.Controls
private void GlobalActualThemeVariantChanged(object? sender, EventArgs e)
{
SetValue(ActualThemeVariantProperty, ((IThemeVariantHost)sender!).ActualThemeVariant, BindingPriority.Template);
SetCurrentValue(ActualThemeVariantProperty, ((IThemeVariantHost)sender!).ActualThemeVariant);
}
private void SceneInvalidated(object? sender, SceneInvalidatedEventArgs e)

2
src/Avalonia.DesignerSupport/Remote/Stubs.cs

@ -194,7 +194,7 @@ namespace Avalonia.DesignerSupport.Remote
public bool NeedsManagedDecorations => false;
public void SetFrameThemeVariant(PlatformThemeVariant themeVariant) { }
public void SetFrameThemeVariant(PlatformThemeVariant? themeVariant) { }
public AcrylicPlatformCompensationLevels AcrylicCompensationLevels { get; } = new AcrylicPlatformCompensationLevels(1, 1, 1);
public object TryGetFeature(Type featureType) => null;

2
src/Avalonia.Native/TopLevelImpl.cs

@ -148,7 +148,7 @@ internal class TopLevelImpl : ITopLevelImpl, IFramebufferPlatformSurface
}
public AcrylicPlatformCompensationLevels AcrylicCompensationLevels { get; } = new AcrylicPlatformCompensationLevels(1, 0, 0);
public virtual void SetFrameThemeVariant(PlatformThemeVariant themeVariant)
public virtual void SetFrameThemeVariant(PlatformThemeVariant? themeVariant)
{
//noop
}

4
src/Avalonia.Native/WindowImplBase.cs

@ -70,9 +70,9 @@ namespace Avalonia.Native
Native?.Resize(clientSize.Width, clientSize.Height, (AvnPlatformResizeReason)reason);
}
public override void SetFrameThemeVariant(PlatformThemeVariant themeVariant)
public override void SetFrameThemeVariant(PlatformThemeVariant? themeVariant)
{
Native?.SetFrameThemeVariant((AvnPlatformThemeVariant)themeVariant);
Native?.SetFrameThemeVariant((AvnPlatformThemeVariant)(themeVariant ?? PlatformThemeVariant.Light));
}
public override void Dispose()

2
src/Avalonia.X11/X11Window.cs

@ -1440,7 +1440,7 @@ namespace Avalonia.X11
public WindowTransparencyLevel TransparencyLevel =>
_transparencyHelper?.CurrentLevel ?? WindowTransparencyLevel.None;
public void SetFrameThemeVariant(PlatformThemeVariant themeVariant) { }
public void SetFrameThemeVariant(PlatformThemeVariant? themeVariant) { }
public AcrylicPlatformCompensationLevels AcrylicCompensationLevels { get; } = new AcrylicPlatformCompensationLevels(1, 0.8, 0.8);

2
src/Browser/Avalonia.Browser/BrowserTopLevelImpl.cs

@ -141,7 +141,7 @@ namespace Avalonia.Browser
public Action? LostFocus { get; set; }
public WindowTransparencyLevel TransparencyLevel => WindowTransparencyLevel.None;
public void SetFrameThemeVariant(PlatformThemeVariant themeVariant)
public void SetFrameThemeVariant(PlatformThemeVariant? themeVariant)
{
// not in the standard, but we potentially can use "apple-mobile-web-app-status-bar-style" for iOS and "theme-color" for android.
}

2
src/Headless/Avalonia.Headless/HeadlessWindowImpl.cs

@ -429,7 +429,7 @@ namespace Avalonia.Headless
}
public void SetFrameThemeVariant(PlatformThemeVariant themeVariant)
public void SetFrameThemeVariant(PlatformThemeVariant? themeVariant)
{
}

2
src/Linux/Avalonia.LinuxFramebuffer/FramebufferToplevelImpl.cs

@ -78,7 +78,7 @@ using Avalonia.Rendering.Composition;
public WindowTransparencyLevel TransparencyLevel => WindowTransparencyLevel.None;
public void SetFrameThemeVariant(PlatformThemeVariant themeVariant) { }
public void SetFrameThemeVariant(PlatformThemeVariant? themeVariant) { }
public AcrylicPlatformCompensationLevels AcrylicCompensationLevels { get; } = new AcrylicPlatformCompensationLevels(1, 1, 1);
public object TryGetFeature(Type featureType) => null;

4
src/Windows/Avalonia.Win32/WindowImpl.cs

@ -894,9 +894,9 @@ namespace Avalonia.Win32
}
}
public unsafe void SetFrameThemeVariant(PlatformThemeVariant themeVariant)
public unsafe void SetFrameThemeVariant(PlatformThemeVariant? themeVariant)
{
_currentThemeVariant = themeVariant;
_currentThemeVariant = themeVariant ?? PlatformThemeVariant.Light;
if (Win32Platform.WindowsVersion.Build >= 22000)
{
var pvUseBackdropBrush = themeVariant == PlatformThemeVariant.Dark ? 1 : 0;

2
src/iOS/Avalonia.iOS/AvaloniaView.cs

@ -238,7 +238,7 @@ namespace Avalonia.iOS
public WindowTransparencyLevel TransparencyLevel => WindowTransparencyLevel.None;
public void SetFrameThemeVariant(PlatformThemeVariant themeVariant)
public void SetFrameThemeVariant(PlatformThemeVariant? themeVariant)
{
#if !TVOS
// TODO adjust status bar depending on full screen mode.

2
tests/Avalonia.UnitTests/CompositorTestServices.cs

@ -211,7 +211,7 @@ public class CompositorTestServices : IDisposable
public WindowTransparencyLevel TransparencyLevel => WindowTransparencyLevel.None;
public void SetFrameThemeVariant(PlatformThemeVariant themeVariant)
public void SetFrameThemeVariant(PlatformThemeVariant? themeVariant)
{
}

Loading…
Cancel
Save