Browse Source

Replace WindowTransparencyLevel enum with list of structs

The transparency level is not yet communicated to the backends though.

Co-Authored-By: Max Katz <maxkatz6@outlook.com>
pull/11509/head
Steven Kirk 3 years ago
parent
commit
b2d7805060
  1. 2
      samples/ControlCatalog/MainView.xaml.cs
  2. 2
      samples/IntegrationTestApp/MainWindow.axaml.cs
  3. 118
      src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs
  4. 22
      src/Avalonia.Controls/ExperimentalAcrylicBorder.cs
  5. 38
      src/Avalonia.Controls/TopLevel.cs
  6. 78
      src/Avalonia.Controls/WindowTransparencyLevel.cs
  7. 6
      src/Avalonia.Native/WindowImplBase.cs
  8. 8
      src/Avalonia.X11/TransparencyHelper.cs
  9. 16
      src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlLanguageParseIntrinsics.cs
  10. 4
      src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlWellKnownTypes.cs
  11. 2
      src/Windows/Avalonia.Win32/TrayIconImpl.cs
  12. 54
      src/Windows/Avalonia.Win32/WindowImpl.cs
  13. 26
      tests/Avalonia.Markup.Xaml.UnitTests/Xaml/WindowTests.cs

2
samples/ControlCatalog/MainView.xaml.cs

@ -83,7 +83,7 @@ namespace ControlCatalog
if (transparencyLevels.SelectedItem is WindowTransparencyLevel selected)
{
var topLevel = (TopLevel)this.GetVisualRoot()!;
topLevel.TransparencyLevelHint = selected;
topLevel.TransparencyLevelHint = new[] { selected };
if (selected != WindowTransparencyLevel.None)
{

2
samples/IntegrationTestApp/MainWindow.axaml.cs

@ -136,7 +136,7 @@ namespace IntegrationTestApp
Name = "TransparentWindow",
SystemDecorations = SystemDecorations.None,
Background = Brushes.Transparent,
TransparencyLevelHint = WindowTransparencyLevel.Transparent,
TransparencyLevelHint = new[] { WindowTransparencyLevel.Transparent },
WindowStartupLocation = WindowStartupLocation.CenterOwner,
Width = 200,
Height = 200,

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

@ -307,78 +307,80 @@ namespace Avalonia.Android.Platform.SkiaPlatform
bool isAboveR = Build.VERSION.SdkInt > BuildVersionCodes.R;
if (_view.Context is AvaloniaMainActivity activity)
{
switch (transparencyLevel)
if (transparencyLevel == WindowTransparencyLevel.AcrylicBlur ||
transparencyLevel == WindowTransparencyLevel.Mica ||
transparencyLevel == WindowTransparencyLevel.None)
{
case WindowTransparencyLevel.AcrylicBlur:
case WindowTransparencyLevel.ForceAcrylicBlur:
case WindowTransparencyLevel.Mica:
case WindowTransparencyLevel.None:
if (!isBelowR)
if (!isBelowR)
{
activity.SetTranslucent(false);
}
if (isAboveR)
{
activity.Window?.ClearFlags(WindowManagerFlags.BlurBehind);
var attr = activity.Window?.Attributes;
if (attr != null)
{
activity.SetTranslucent(false);
}
if (isAboveR)
{
activity.Window?.ClearFlags(WindowManagerFlags.BlurBehind);
var attr = activity.Window?.Attributes;
if (attr != null)
{
attr.BlurBehindRadius = 0;
attr.BlurBehindRadius = 0;
activity.Window.Attributes = attr;
}
activity.Window.Attributes = attr;
}
activity.Window.SetBackgroundDrawable(new ColorDrawable(Color.White));
}
activity.Window.SetBackgroundDrawable(new ColorDrawable(Color.White));
if(transparencyLevel != WindowTransparencyLevel.None)
{
return;
}
break;
case WindowTransparencyLevel.Transparent:
if (!isBelowR)
{
activity.SetTranslucent(true);
}
if (isAboveR)
{
activity.Window?.ClearFlags(WindowManagerFlags.BlurBehind);
if (transparencyLevel != WindowTransparencyLevel.None)
{
return;
}
}
var attr = activity.Window?.Attributes;
if (attr != null)
{
attr.BlurBehindRadius = 0;
if (transparencyLevel == WindowTransparencyLevel.Transparent)
{
if (!isBelowR)
{
activity.SetTranslucent(true);
}
if (isAboveR)
{
activity.Window?.ClearFlags(WindowManagerFlags.BlurBehind);
var attr = activity.Window?.Attributes;
if (attr != null)
{
attr.BlurBehindRadius = 0;
activity.Window.Attributes = attr;
}
activity.Window.Attributes = attr;
}
activity.Window.SetBackgroundDrawable(new ColorDrawable(Color.Transparent));
break;
case WindowTransparencyLevel.Blur:
if (isAboveR)
{
activity.SetTranslucent(true);
activity.Window?.AddFlags(WindowManagerFlags.BlurBehind);
}
activity.Window.SetBackgroundDrawable(new ColorDrawable(Color.Transparent));
}
var attr = activity.Window?.Attributes;
if (attr != null)
{
attr.BlurBehindRadius = 120;
if (transparencyLevel == WindowTransparencyLevel.Blur)
{
if (isAboveR)
{
activity.SetTranslucent(true);
activity.Window?.AddFlags(WindowManagerFlags.BlurBehind);
activity.Window.Attributes = attr;
}
activity.Window.SetBackgroundDrawable(new ColorDrawable(Color.Transparent));
}
else
var attr = activity.Window?.Attributes;
if (attr != null)
{
activity.Window?.ClearFlags(WindowManagerFlags.BlurBehind);
activity.Window.SetBackgroundDrawable(new ColorDrawable(Color.White));
attr.BlurBehindRadius = 120;
return;
activity.Window.Attributes = attr;
}
break;
activity.Window.SetBackgroundDrawable(new ColorDrawable(Color.Transparent));
}
else
{
activity.Window?.ClearFlags(WindowManagerFlags.BlurBehind);
activity.Window.SetBackgroundDrawable(new ColorDrawable(Color.White));
return;
}
}
TransparencyLevel = transparencyLevel;
}
}

22
src/Avalonia.Controls/ExperimentalAcrylicBorder.cs

@ -56,22 +56,12 @@ namespace Avalonia.Controls
{
if (tl.PlatformImpl is null)
return;
switch (x)
{
case WindowTransparencyLevel.Transparent:
case WindowTransparencyLevel.None:
Material.PlatformTransparencyCompensationLevel = tl.PlatformImpl.AcrylicCompensationLevels.TransparentLevel;
break;
case WindowTransparencyLevel.Blur:
Material.PlatformTransparencyCompensationLevel = tl.PlatformImpl.AcrylicCompensationLevels.BlurLevel;
break;
case WindowTransparencyLevel.AcrylicBlur:
Material.PlatformTransparencyCompensationLevel = tl.PlatformImpl.AcrylicCompensationLevels.AcrylicBlurLevel;
break;
}
if (x == WindowTransparencyLevel.Transparent || x == WindowTransparencyLevel.None)
Material.PlatformTransparencyCompensationLevel = tl.PlatformImpl.AcrylicCompensationLevels.TransparentLevel;
else if (x == WindowTransparencyLevel.Blur)
Material.PlatformTransparencyCompensationLevel = tl.PlatformImpl.AcrylicCompensationLevels.BlurLevel;
else if (x == WindowTransparencyLevel.AcrylicBlur)
Material.PlatformTransparencyCompensationLevel = tl.PlatformImpl.AcrylicCompensationLevels.AcrylicBlurLevel;
});
UpdateMaterialSubscription();
}

38
src/Avalonia.Controls/TopLevel.cs

@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using Avalonia.Reactive;
@ -22,6 +23,7 @@ using Avalonia.Utilities;
using Avalonia.Input.Platform;
using System.Linq;
using System.Threading.Tasks;
using Avalonia.Metadata;
namespace Avalonia.Controls
{
@ -64,8 +66,8 @@ namespace Avalonia.Controls
/// <summary>
/// Defines the <see cref="TransparencyLevelHint"/> property.
/// </summary>
public static readonly StyledProperty<WindowTransparencyLevel> TransparencyLevelHintProperty =
AvaloniaProperty.Register<TopLevel, WindowTransparencyLevel>(nameof(TransparencyLevelHint), WindowTransparencyLevel.None);
public static readonly StyledProperty<IReadOnlyList<WindowTransparencyLevel>> TransparencyLevelHintProperty =
AvaloniaProperty.Register<TopLevel, IReadOnlyList<WindowTransparencyLevel>>(nameof(TransparencyLevelHint), Array.Empty<WindowTransparencyLevel>());
/// <summary>
/// Defines the <see cref="ActualTransparencyLevel"/> property.
@ -172,7 +174,7 @@ namespace Avalonia.Controls
PlatformImpl = impl ?? throw new InvalidOperationException(
"Could not create window implementation: maybe no windowing subsystem was initialized?");
_actualTransparencyLevel = PlatformImpl.TransparencyLevel;
_actualTransparencyLevel = PlatformImpl.TransparencyLevel;
dependencyResolver ??= AvaloniaLocator.Current;
@ -310,8 +312,11 @@ namespace Avalonia.Controls
/// <summary>
/// Gets or sets the <see cref="WindowTransparencyLevel"/> that the TopLevel should use when possible.
/// Accepts multiple values which are applied in a fallback order.
/// For instance, with "Mica, Blur" Mica will be applied only on platforms where it is possible,
/// and Blur will be used on the rest of them. Default value is an empty array or "None".
/// </summary>
public WindowTransparencyLevel TransparencyLevelHint
public IReadOnlyList<WindowTransparencyLevel> TransparencyLevelHint
{
get { return GetValue(TransparencyLevelHintProperty); }
set { SetValue(TransparencyLevelHintProperty, value); }
@ -518,8 +523,8 @@ namespace Avalonia.Controls
{
if (PlatformImpl != null)
{
PlatformImpl.SetTransparencyLevelHint(change.GetNewValue<WindowTransparencyLevel>());
HandleTransparencyLevelChanged(PlatformImpl.TransparencyLevel);
////PlatformImpl.SetTransparencyLevelHint(
//// change.GetNewValue<IReadOnlyList<WindowTransparencyLevel>>() ?? Array.Empty<WindowTransparencyLevel>());
}
}
else if (change.Property == ActualThemeVariantProperty)
@ -610,27 +615,11 @@ namespace Avalonia.Controls
ScalingChanged?.Invoke(this, EventArgs.Empty);
}
private static bool TransparencyLevelsMatch (WindowTransparencyLevel requested, WindowTransparencyLevel received)
{
if(requested == received)
{
return true;
}
else if(requested >= WindowTransparencyLevel.Blur && received >= WindowTransparencyLevel.Blur)
{
return true;
}
return false;
}
private void HandleTransparencyLevelChanged(WindowTransparencyLevel transparencyLevel)
{
if(_transparencyFallbackBorder != null)
if (_transparencyFallbackBorder != null)
{
if(transparencyLevel == WindowTransparencyLevel.None ||
TransparencyLevelHint == WindowTransparencyLevel.None ||
!TransparencyLevelsMatch(TransparencyLevelHint, transparencyLevel))
if (transparencyLevel == WindowTransparencyLevel.None)
{
_transparencyFallbackBorder.Background = TransparencyBackgroundFallback;
}
@ -660,7 +649,6 @@ namespace Avalonia.Controls
return;
_transparencyFallbackBorder = e.NameScope.Find<Border>("PART_TransparencyFallback");
HandleTransparencyLevelChanged(PlatformImpl.TransparencyLevel);
}

78
src/Avalonia.Controls/WindowTransparencyLevel.cs

@ -1,35 +1,51 @@
namespace Avalonia.Controls
using System.Collections.Generic;
using System.Collections.ObjectModel;
namespace Avalonia.Controls;
public readonly record struct WindowTransparencyLevel
{
public enum WindowTransparencyLevel
private readonly string _value;
private WindowTransparencyLevel(string value)
{
/// <summary>
/// The window background is Black where nothing is drawn in the window.
/// </summary>
None,
/// <summary>
/// The window background is Transparent where nothing is drawn in the window.
/// </summary>
Transparent,
/// <summary>
/// The window background is a blur-behind where nothing is drawn in the window.
/// </summary>
Blur,
/// <summary>
/// The window background is a blur-behind with a high blur radius. This level may fallback to Blur.
/// </summary>
AcrylicBlur,
/// <summary>
/// Force acrylic on some incompatible versions of Windows 10.
/// </summary>
ForceAcrylicBlur,
/// <summary>
/// The window background is based on desktop wallpaper tint with a blur. This will only work on Windows 11
/// </summary>
Mica
_value = value;
}
/// <summary>
/// The window background is Black where nothing is drawn in the window.
/// </summary>
public static WindowTransparencyLevel None { get; } = new(nameof(None));
/// <summary>
/// The window background is Transparent where nothing is drawn in the window.
/// </summary>
public static WindowTransparencyLevel Transparent { get; } = new(nameof(Transparent));
/// <summary>
/// The window background is a blur-behind where nothing is drawn in the window.
/// </summary>
public static WindowTransparencyLevel Blur { get; } = new(nameof(Blur));
/// <summary>
/// The window background is a blur-behind with a high blur radius. This level may fallback to Blur.
/// </summary>
public static WindowTransparencyLevel AcrylicBlur { get; } = new(nameof(AcrylicBlur));
/// <summary>
/// The window background is based on desktop wallpaper tint with a blur. This will only work on Windows 11
/// </summary>
public static WindowTransparencyLevel Mica { get; } = new(nameof(Mica));
public override string ToString()
{
return _value;
}
}
public class WindowTransparencyLevelCollection : ReadOnlyCollection<WindowTransparencyLevel>
{
public WindowTransparencyLevelCollection(IList<WindowTransparencyLevel> list) : base(list)
{
}
}

6
src/Avalonia.Native/WindowImplBase.cs

@ -489,8 +489,12 @@ namespace Avalonia.Native
{
if (TransparencyLevel != transparencyLevel)
{
if (transparencyLevel > WindowTransparencyLevel.Transparent)
if (transparencyLevel == WindowTransparencyLevel.Blur ||
transparencyLevel == WindowTransparencyLevel.AcrylicBlur ||
transparencyLevel == WindowTransparencyLevel.Mica)
{
transparencyLevel = WindowTransparencyLevel.AcrylicBlur;
}
TransparencyLevel = transparencyLevel;

8
src/Avalonia.X11/TransparencyHelper.cs

@ -41,7 +41,11 @@ namespace Avalonia.X11
private WindowTransparencyLevel UpdateAtomsAndGetTransparency()
{
if (_requestedLevel >= WindowTransparencyLevel.Blur)
var blur = _requestedLevel == WindowTransparencyLevel.Blur ||
_requestedLevel == WindowTransparencyLevel.AcrylicBlur ||
_requestedLevel == WindowTransparencyLevel.Mica;
if (blur)
{
if (!_blurAtomsAreSet)
{
@ -62,7 +66,7 @@ namespace Avalonia.X11
if (!_globals.IsCompositionEnabled)
return WindowTransparencyLevel.None;
if (_requestedLevel >= WindowTransparencyLevel.Blur && CanBlur)
if (blur && CanBlur)
return WindowTransparencyLevel.Blur;
return WindowTransparencyLevel.Transparent;
}

16
src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlLanguageParseIntrinsics.cs

@ -274,6 +274,19 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions
}
}
if (type.Equals(types.WindowTransparencyLevel))
{
foreach (var property in types.WindowTransparencyLevel.Properties)
{
if (property.PropertyType == types.WindowTransparencyLevel && property.Name.Equals(text, StringComparison.OrdinalIgnoreCase))
{
result = new XamlStaticOrTargetedReturnMethodCallNode(node, property.Getter, Enumerable.Empty<IXamlAstValueNode>());
return true;
}
}
}
if (type.Equals(types.Uri))
{
var uriText = text.Trim();
@ -385,7 +398,8 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions
result = new AvaloniaXamlIlArrayConstantAstNode(node, elementType.MakeArrayType(1), elementType, nodes);
return true;
}
else if (type == context.Configuration.WellKnownTypes.IListOfT.MakeGenericType(elementType))
else if (type == context.Configuration.WellKnownTypes.IListOfT.MakeGenericType(elementType) ||
type == types.IReadOnlyListOfT.MakeGenericType(elementType))
{
var listType = context.Configuration.WellKnownTypes.IListOfT.MakeGenericType(elementType);
result = new AvaloniaXamlIlArrayConstantAstNode(node, listType, elementType, nodes);

4
src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlWellKnownTypes.cs

@ -117,6 +117,8 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
public IXamlConstructor UriConstructor { get; }
public IXamlType Style { get; }
public IXamlType ControlTheme { get; }
public IXamlType WindowTransparencyLevel { get; }
public IXamlType IReadOnlyListOfT { get; }
public AvaloniaXamlIlWellKnownTypes(TransformerConfiguration cfg)
{
@ -199,6 +201,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
FontFamily = cfg.TypeSystem.GetType("Avalonia.Media.FontFamily");
FontFamilyConstructorUriName = FontFamily.GetConstructor(new List<IXamlType> { Uri, XamlIlTypes.String });
ThemeVariant = cfg.TypeSystem.GetType("Avalonia.Styling.ThemeVariant");
WindowTransparencyLevel = cfg.TypeSystem.GetType("Avalonia.Controls.WindowTransparencyLevel");
(IXamlType, IXamlConstructor) GetNumericTypeInfo(string name, IXamlType componentType, int componentCount)
{
@ -260,6 +263,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
UriConstructor = Uri.GetConstructor(new List<IXamlType>() { cfg.WellKnownTypes.String, UriKind });
Style = cfg.TypeSystem.GetType("Avalonia.Styling.Style");
ControlTheme = cfg.TypeSystem.GetType("Avalonia.Styling.ControlTheme");
IReadOnlyListOfT = cfg.TypeSystem.GetType("System.Collections.Generic.IReadOnlyList`1");
}
}

2
src/Windows/Avalonia.Win32/TrayIconImpl.cs

@ -147,7 +147,7 @@ namespace Avalonia.Win32
SystemDecorations = SystemDecorations.None,
SizeToContent = SizeToContent.WidthAndHeight,
Background = null,
TransparencyLevelHint = WindowTransparencyLevel.Transparent,
TransparencyLevelHint = new[] { WindowTransparencyLevel.Transparent },
Content = new TrayIconMenuFlyoutPresenter()
{
ItemsSource = menuItems

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

@ -435,7 +435,9 @@ namespace Avalonia.Win32
Marshal.FreeHGlobal(accentPtr);
if (transparencyLevel >= WindowTransparencyLevel.Blur)
if (transparencyLevel == WindowTransparencyLevel.Blur ||
transparencyLevel == WindowTransparencyLevel.AcrylicBlur ||
transparencyLevel == WindowTransparencyLevel.Mica)
{
Win7EnableBlur(transparencyLevel);
}
@ -447,13 +449,14 @@ namespace Avalonia.Win32
{
if (_isUsingComposition)
{
var effect = transparencyLevel switch
{
WindowTransparencyLevel.Mica => BlurEffect.Mica,
WindowTransparencyLevel.AcrylicBlur => BlurEffect.Acrylic,
WindowTransparencyLevel.Blur => BlurEffect.Acrylic,
_ => BlurEffect.None
};
BlurEffect effect;
if (transparencyLevel == WindowTransparencyLevel.Mica)
effect = BlurEffect.Mica;
else if (transparencyLevel == WindowTransparencyLevel.AcrylicBlur)
effect = BlurEffect.Acrylic;
else
effect = BlurEffect.None;
if (Win32Platform.WindowsVersion >= WinUiCompositionShared.MinHostBackdropVersion)
{
@ -485,27 +488,22 @@ namespace Avalonia.Win32
transparencyLevel = WindowTransparencyLevel.Blur;
}
switch (transparencyLevel)
if (transparencyLevel == WindowTransparencyLevel.Transparent)
{
accent.AccentState = AccentState.ACCENT_ENABLE_TRANSPARENTGRADIENT;
}
else if (transparencyLevel == WindowTransparencyLevel.Blur)
{
accent.AccentState = AccentState.ACCENT_ENABLE_BLURBEHIND;
}
else if (transparencyLevel == WindowTransparencyLevel.AcrylicBlur || transparencyLevel == WindowTransparencyLevel.Mica)
{
accent.AccentState = AccentState.ACCENT_ENABLE_ACRYLIC;
transparencyLevel = WindowTransparencyLevel.AcrylicBlur;
}
else
{
default:
case WindowTransparencyLevel.None:
accent.AccentState = AccentState.ACCENT_DISABLED;
break;
case WindowTransparencyLevel.Transparent:
accent.AccentState = AccentState.ACCENT_ENABLE_TRANSPARENTGRADIENT;
break;
case WindowTransparencyLevel.Blur:
accent.AccentState = AccentState.ACCENT_ENABLE_BLURBEHIND;
break;
case WindowTransparencyLevel.AcrylicBlur:
case WindowTransparencyLevel.ForceAcrylicBlur: // hack-force acrylic.
case WindowTransparencyLevel.Mica:
accent.AccentState = AccentState.ACCENT_ENABLE_ACRYLIC;
transparencyLevel = WindowTransparencyLevel.AcrylicBlur;
break;
accent.AccentState = AccentState.ACCENT_DISABLED;
}
accent.AccentFlags = 2;

26
tests/Avalonia.Markup.Xaml.UnitTests/Xaml/WindowTests.cs

@ -0,0 +1,26 @@
using Avalonia.Controls;
using Avalonia.UnitTests;
using Xunit;
namespace Avalonia.Markup.Xaml.UnitTests.Xaml
{
public class WindowTests : XamlTestBase
{
[Fact]
public void Can_Specify_TransparencyLevelHint()
{
using var app = UnitTestApplication.Start(TestServices.MockWindowingPlatform);
var xaml = @"<Window xmlns='https://github.com/avaloniaui' TransparencyLevelHint='Blur,Transparent,None'/>";
var target = AvaloniaRuntimeXamlLoader.Parse<Window>(xaml);
Assert.Equal(
new[]
{
WindowTransparencyLevel.Blur,
WindowTransparencyLevel.Transparent,
WindowTransparencyLevel.None,
}, target.TransparencyLevelHint);
}
}
}
Loading…
Cancel
Save