浏览代码

Merge pull request #9658 from AvaloniaUI/feature/transparent-web-canvas

Feature/transparent web canvas
Max Katz 2 年之前
父节点
当前提交
e77e41544c
共有 24 个文件被更改,包括 141 次插入48 次删除
  1. 1 1
      samples/ControlCatalog.Android/MainActivity.cs
  2. 4 0
      samples/ControlCatalog.Android/Resources/values/styles.xml
  3. 2 0
      samples/ControlCatalog.Android/SplashActivity.cs
  4. 13 0
      samples/ControlCatalog.Browser/Properties/launchSettings.json
  5. 1 2
      samples/ControlCatalog/MainView.xaml
  6. 16 7
      samples/ControlCatalog/MainView.xaml.cs
  7. 0 1
      samples/ControlCatalog/MainWindow.xaml
  8. 0 7
      samples/ControlCatalog/Pages/WindowCustomizationsPage.xaml
  9. 0 7
      samples/ControlCatalog/ViewModels/MainWindowViewModel.cs
  10. 2 0
      src/Android/Avalonia.Android/AvaloniaView.cs
  11. 1 0
      src/Android/Avalonia.Android/Platform/SkiaPlatform/InvalidationAwareSurfaceView.cs
  12. 83 2
      src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs
  13. 1 0
      src/Browser/Avalonia.Browser.Blazor/Avalonia.Browser.Blazor.csproj
  14. 2 1
      src/Browser/Avalonia.Browser.Blazor/AvaloniaView.cs
  15. 0 1
      src/Browser/Avalonia.Browser.Blazor/BlazorSingleViewLifetime.cs
  16. 0 1
      src/Browser/Avalonia.Browser/AvaloniaView.cs
  17. 0 2
      src/Browser/Avalonia.Browser/BrowserSingleViewLifetime.cs
  18. 9 3
      src/Browser/Avalonia.Browser/BrowserTopLevelImpl.cs
  19. 0 1
      src/Browser/Avalonia.Browser/Interop/CanvasHelper.cs
  20. 6 8
      src/Browser/Avalonia.Browser/Skia/BrowserSkiaGpuRenderTarget.cs
  21. 0 1
      src/Browser/Avalonia.Browser/Storage/BlobReadableStream.cs
  22. 0 1
      src/Browser/Avalonia.Browser/Storage/BrowserStorageProvider.cs
  23. 0 1
      src/Browser/Avalonia.Browser/Storage/WriteableStream.cs
  24. 0 1
      src/Browser/Avalonia.Browser/webapp/modules/avalonia/dom.ts

+ 1 - 1
samples/ControlCatalog.Android/MainActivity.cs

@@ -5,7 +5,7 @@ using Avalonia.Android;
 
 namespace ControlCatalog.Android
 {
-    [Activity(Label = "ControlCatalog.Android", Theme = "@style/MyTheme.NoActionBar", Icon = "@drawable/icon", LaunchMode = LaunchMode.SingleTop, ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.ScreenSize)]
+    [Activity(Label = "ControlCatalog.Android", Theme = "@style/MyTheme.Main", Icon = "@drawable/icon", LaunchMode = LaunchMode.SingleTop, ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.ScreenSize)]
     public class MainActivity : AvaloniaMainActivity
     {
     }

+ 4 - 0
samples/ControlCatalog.Android/Resources/values/styles.xml

@@ -14,4 +14,8 @@
     <item name="android:windowContentOverlay">@null</item>
   </style>
 
+  <style name="MyTheme.Main" parent ="MyTheme.NoActionBar">
+    <item name="android:windowIsTranslucent">true</item>
+  </style>
+
 </resources>

+ 2 - 0
samples/ControlCatalog.Android/SplashActivity.cs

@@ -28,6 +28,8 @@ namespace ControlCatalog.Android
             base.OnResume();
 
             StartActivity(new Intent(Application.Context, typeof(MainActivity)));
+
+            Finish();
         }
     }
 }

+ 13 - 0
samples/ControlCatalog.Browser/Properties/launchSettings.json

@@ -0,0 +1,13 @@
+{
+  "profiles": {
+    "ControlCatalog.Browser": {
+      "commandName": "Project",
+      "launchBrowser": true,
+      "environmentVariables": {
+        "ASPNETCORE_ENVIRONMENT": "Development"
+      },
+      "applicationUrl": "https://localhost:5001;http://localhost:5000",
+      "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/debug?browser={browserInspectUri}"
+    }
+  }
+}

+ 1 - 2
samples/ControlCatalog/MainView.xaml

@@ -209,8 +209,7 @@
               </ComboBox.Items>
             </ComboBox>
             <ComboBox x:Name="TransparencyLevels"
-                      HorizontalAlignment="Stretch"
-                      SelectedIndex="{Binding TransparencyLevel}">
+                      HorizontalAlignment="Stretch">
               <ComboBox.Items>
                 <WindowTransparencyLevel>None</WindowTransparencyLevel>
                 <WindowTransparencyLevel>Transparent</WindowTransparencyLevel>

+ 16 - 7
samples/ControlCatalog/MainView.xaml.cs

@@ -6,6 +6,7 @@ using Avalonia.Controls.ApplicationLifetimes;
 using Avalonia.Markup.Xaml;
 using Avalonia.Media;
 using Avalonia.Media.Immutable;
+using Avalonia.VisualTree;
 using ControlCatalog.Models;
 using ControlCatalog.Pages;
 
@@ -59,17 +60,25 @@ namespace ControlCatalog
             };
 
             var transparencyLevels = this.Get<ComboBox>("TransparencyLevels");
-            IDisposable? backgroundSetter = null, paneBackgroundSetter = null;
+            IDisposable? topLevelBackgroundSideSetter = null, sideBarBackgroundSetter = null, paneBackgroundSetter = null;
             transparencyLevels.SelectionChanged += (sender, e) =>
             {
-                backgroundSetter?.Dispose();
+                topLevelBackgroundSideSetter?.Dispose();
+                sideBarBackgroundSetter?.Dispose();
                 paneBackgroundSetter?.Dispose();
-                if (transparencyLevels.SelectedItem is WindowTransparencyLevel selected
-                    && selected != WindowTransparencyLevel.None)
+                if (transparencyLevels.SelectedItem is WindowTransparencyLevel selected)
                 {
-                    var semiTransparentBrush = new ImmutableSolidColorBrush(Colors.Gray, 0.5);
-                    backgroundSetter = sideBar.SetValue(BackgroundProperty, semiTransparentBrush, Avalonia.Data.BindingPriority.Style);
-                    paneBackgroundSetter = sideBar.SetValue(SplitView.PaneBackgroundProperty, semiTransparentBrush, Avalonia.Data.BindingPriority.Style);
+                    var topLevel = (TopLevel)this.GetVisualRoot()!;
+                    topLevel.TransparencyLevelHint = selected;
+
+                    if (selected != WindowTransparencyLevel.None)
+                    {
+                        var transparentBrush = new ImmutableSolidColorBrush(Colors.White, 0);
+                        var semiTransparentBrush = new ImmutableSolidColorBrush(Colors.Gray, 0.2);
+                        topLevelBackgroundSideSetter = topLevel.SetValue(BackgroundProperty, transparentBrush, Avalonia.Data.BindingPriority.Style);
+                        sideBarBackgroundSetter = sideBar.SetValue(BackgroundProperty, semiTransparentBrush, Avalonia.Data.BindingPriority.Style);
+                        paneBackgroundSetter = sideBar.SetValue(SplitView.PaneBackgroundProperty, semiTransparentBrush, Avalonia.Data.BindingPriority.Style);
+                    }
                 }
             };
         }

+ 0 - 1
samples/ControlCatalog/MainWindow.xaml

@@ -10,7 +10,6 @@
         ExtendClientAreaToDecorationsHint="{Binding ExtendClientAreaEnabled}"
         ExtendClientAreaChromeHints="{Binding ChromeHints}"
         ExtendClientAreaTitleBarHeightHint="{Binding TitleBarHeight}"
-        TransparencyLevelHint="{Binding TransparencyLevel}"        
         x:Name="MainWindow"
         Background="Transparent"
         x:Class="ControlCatalog.MainWindow" WindowState="{Binding WindowState, Mode=TwoWay}"

+ 0 - 7
samples/ControlCatalog/Pages/WindowCustomizationsPage.xaml

@@ -11,12 +11,5 @@
     <CheckBox Content="Title Bar" IsChecked="{Binding SystemTitleBarEnabled}" />    
     <CheckBox Content="Prefer System Chrome" IsChecked="{Binding PreferSystemChromeEnabled}" />
     <Slider Minimum="-1" Maximum="200" Value="{Binding TitleBarHeight}" />
-    <ComboBox x:Name="TransparencyLevels" SelectedIndex="{Binding TransparencyLevel}">
-      <ComboBoxItem>None</ComboBoxItem>
-      <ComboBoxItem>Transparent</ComboBoxItem>
-      <ComboBoxItem>Blur</ComboBoxItem>
-      <ComboBoxItem>AcrylicBlur</ComboBoxItem>
-      <ComboBoxItem>Mica</ComboBoxItem>
-    </ComboBox>
   </StackPanel>
 </UserControl>

+ 0 - 7
samples/ControlCatalog/ViewModels/MainWindowViewModel.cs

@@ -17,7 +17,6 @@ namespace ControlCatalog.ViewModels
         private bool _isMenuItemChecked = true;
         private WindowState _windowState;
         private WindowState[] _windowStates = Array.Empty<WindowState>();
-        private int _transparencyLevel;
         private ExtendClientAreaChromeHints _chromeHints = ExtendClientAreaChromeHints.PreferSystemChrome;
         private bool _extendClientAreaEnabled;
         private bool _systemTitleBarEnabled;
@@ -77,12 +76,6 @@ namespace ControlCatalog.ViewModels
             TitleBarHeight = -1;
         }        
 
-        public int TransparencyLevel
-        {
-            get { return _transparencyLevel; }
-            set { this.RaiseAndSetIfChanged(ref _transparencyLevel, value); }
-        }        
-
         public ExtendClientAreaChromeHints ChromeHints
         {
             get { return _chromeHints; }

+ 2 - 0
src/Android/Avalonia.Android/AvaloniaView.cs

@@ -24,6 +24,8 @@ namespace Avalonia.Android
 
             _root = new EmbeddableControlRoot(_view);
             _root.Prepare();
+
+            this.SetBackgroundColor(global::Android.Graphics.Color.Transparent);
         }
 
         internal TopLevelImpl TopLevelImpl => _view;

+ 1 - 0
src/Android/Avalonia.Android/Platform/SkiaPlatform/InvalidationAwareSurfaceView.cs

@@ -22,6 +22,7 @@ namespace Avalonia.Android
         public InvalidationAwareSurfaceView(Context context) : base(context)
         {
             Holder.AddCallback(this);
+            Holder.SetFormat(global::Android.Graphics.Format.Transparent);
             _handler = new Handler(context.MainLooper);
         }
 

+ 83 - 2
src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs

@@ -26,6 +26,7 @@ using Avalonia.Rendering.Composition;
 using Java.Lang;
 using Math = System.Math;
 using AndroidRect = Android.Graphics.Rect;
+using Android.Graphics.Drawables;
 
 namespace Avalonia.Android.Platform.SkiaPlatform
 {
@@ -283,7 +284,7 @@ namespace Avalonia.Android.Platform.SkiaPlatform
         public Action LostFocus { get; set; }
         public Action<WindowTransparencyLevel> TransparencyLevelChanged { get; set; }
 
-        public WindowTransparencyLevel TransparencyLevel => WindowTransparencyLevel.None;
+        public WindowTransparencyLevel TransparencyLevel { get; private set; }
 
         public AcrylicPlatformCompensationLevels AcrylicCompensationLevels => new AcrylicPlatformCompensationLevels(1, 1, 1);
 
@@ -301,7 +302,87 @@ namespace Avalonia.Android.Platform.SkiaPlatform
 
         public void SetTransparencyLevelHint(WindowTransparencyLevel transparencyLevel)
         {
-            throw new NotImplementedException();
+            if (TransparencyLevel != transparencyLevel)
+            {
+                bool isBelowR = Build.VERSION.SdkInt < BuildVersionCodes.R;
+                bool isAboveR = Build.VERSION.SdkInt > BuildVersionCodes.R;
+                if (_view.Context is AvaloniaMainActivity activity)
+                {
+                    switch (transparencyLevel)
+                    {
+                        case WindowTransparencyLevel.AcrylicBlur:
+                        case WindowTransparencyLevel.ForceAcrylicBlur:
+                        case WindowTransparencyLevel.Mica:
+                        case WindowTransparencyLevel.None:
+                            if (!isBelowR)
+                            {
+                                activity.SetTranslucent(false);
+                            }
+                            if (isAboveR)
+                            {
+                                activity.Window?.ClearFlags(WindowManagerFlags.BlurBehind);
+
+                                var attr = activity.Window?.Attributes;
+                                if (attr != null)
+                                {
+                                    attr.BlurBehindRadius = 0;
+
+                                    activity.Window.Attributes = attr;
+                                }
+                            }
+                            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);
+
+                                var attr = activity.Window?.Attributes;
+                                if (attr != null)
+                                {
+                                    attr.BlurBehindRadius = 0;
+
+                                    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);
+
+                                var attr = activity.Window?.Attributes;
+                                if (attr != null)
+                                {
+                                    attr.BlurBehindRadius = 120;
+
+                                    activity.Window.Attributes = attr;
+                                }
+                                activity.Window.SetBackgroundDrawable(new ColorDrawable(Color.Transparent));
+                            }
+                            else
+                            {
+                                activity.Window?.ClearFlags(WindowManagerFlags.BlurBehind);
+                                activity.Window.SetBackgroundDrawable(new ColorDrawable(Color.White));
+
+                                return;
+                            }
+                            break;
+                    }
+                    TransparencyLevel = transparencyLevel;
+                }
+            }
         }
     }
 

+ 1 - 0
src/Browser/Avalonia.Browser.Blazor/Avalonia.Browser.Blazor.csproj

@@ -7,6 +7,7 @@
   </PropertyGroup>
 
   <ItemGroup>
+    <SupportedPlatform Remove="@(SupportedPlatform)" />
     <SupportedPlatform Include="browser" />
   </ItemGroup>
 

+ 2 - 1
src/Browser/Avalonia.Browser.Blazor/AvaloniaView.cs

@@ -8,9 +8,10 @@ using Microsoft.AspNetCore.Components;
 using Microsoft.AspNetCore.Components.Rendering;
 using BrowserView = Avalonia.Browser.AvaloniaView;
 
+[assembly: SupportedOSPlatform("browser")]
+
 namespace Avalonia.Browser.Blazor;
 
-[SupportedOSPlatform("browser")]
 public class AvaloniaView : ComponentBase
 {
     private Browser.AvaloniaView? _browserView;

+ 0 - 1
src/Browser/Avalonia.Browser.Blazor/BlazorSingleViewLifetime.cs

@@ -5,7 +5,6 @@ using Avalonia.Controls.ApplicationLifetimes;
 
 namespace Avalonia.Browser.Blazor;
 
-[SupportedOSPlatform("browser")]
 public static class WebAppBuilder
 {
     public static AppBuilder SetupWithSingleViewLifetime(

+ 0 - 1
src/Browser/Avalonia.Browser/AvaloniaView.cs

@@ -18,7 +18,6 @@ using SkiaSharp;
 
 namespace Avalonia.Browser
 {
-    [System.Runtime.Versioning.SupportedOSPlatform("browser")] // gets rid of callsite warnings
     public partial class AvaloniaView : ITextInputMethodImpl
     {
         private static readonly PooledList<RawPointerPoint> s_intermediatePointsPooledList = new(ClearMode.Never);

+ 0 - 2
src/Browser/Avalonia.Browser/BrowserSingleViewLifetime.cs

@@ -5,7 +5,6 @@ using System.Runtime.Versioning;
 
 namespace Avalonia.Browser;
 
-[SupportedOSPlatform("browser")]
 public class BrowserSingleViewLifetime : ISingleViewApplicationLifetime
 {
     public AvaloniaView? View;
@@ -22,7 +21,6 @@ public class BrowserPlatformOptions
     public Func<string, string> FrameworkAssetPathResolver { get; set; } = new(fileName => $"./{fileName}");
 }
 
-[SupportedOSPlatform("browser")]
 public static class WebAppBuilder
 {
     public static AppBuilder SetupBrowserApp(

+ 9 - 3
src/Browser/Avalonia.Browser/BrowserTopLevelImpl.cs

@@ -2,6 +2,7 @@ using System;
 using System.Collections.Generic;
 using System.Diagnostics;
 using System.Linq;
+using System.Runtime.Versioning;
 using Avalonia.Browser.Skia;
 using Avalonia.Browser.Storage;
 using Avalonia.Controls;
@@ -14,9 +15,10 @@ using Avalonia.Platform.Storage;
 using Avalonia.Rendering;
 using Avalonia.Rendering.Composition;
 
+[assembly: SupportedOSPlatform("browser")]
+
 namespace Avalonia.Browser
 {
-    [System.Runtime.Versioning.SupportedOSPlatform("browser")] // gets rid of callsite warnings
     internal class BrowserTopLevelImpl : ITopLevelImplWithTextInputMethod, ITopLevelImplWithNativeControlHost, ITopLevelImplWithStorageProvider
     {
         private Size _clientSize;
@@ -201,7 +203,11 @@ namespace Avalonia.Browser
 
         public void SetTransparencyLevelHint(WindowTransparencyLevel transparencyLevel)
         {
-
+            if (transparencyLevel == WindowTransparencyLevel.None
+                || transparencyLevel == WindowTransparencyLevel.Transparent)
+            {
+                TransparencyLevel = transparencyLevel;
+            }
         }
 
         public Size ClientSize => _clientSize;
@@ -221,7 +227,7 @@ namespace Avalonia.Browser
         public IMouseDevice MouseDevice { get; } = new MouseDevice();
 
         public IKeyboardDevice KeyboardDevice { get; } = BrowserWindowingPlatform.Keyboard;
-        public WindowTransparencyLevel TransparencyLevel { get; }
+        public WindowTransparencyLevel TransparencyLevel { get; private set; }
         public AcrylicPlatformCompensationLevels AcrylicCompensationLevels { get; }
 
         public ITextInputMethodImpl TextInputMethod => _avaloniaView;

+ 0 - 1
src/Browser/Avalonia.Browser/Interop/CanvasHelper.cs

@@ -6,7 +6,6 @@ namespace Avalonia.Browser.Interop;
 
 internal record GLInfo(int ContextId, uint FboId, int Stencils, int Samples, int Depth);
 
-[System.Runtime.Versioning.SupportedOSPlatform("browser")] // gets rid of callsite warnings
 internal static partial class CanvasHelper
 {
 

+ 6 - 8
src/Browser/Avalonia.Browser/Skia/BrowserSkiaGpuRenderTarget.cs

@@ -14,14 +14,12 @@ namespace Avalonia.Browser.Skia
             _size = browserSkiaSurface.Size;
 
             var glFbInfo = new GRGlFramebufferInfo(browserSkiaSurface.GlInfo.FboId, browserSkiaSurface.ColorType.ToGlSizedFormat());
-            {
-                _browserSkiaSurface = browserSkiaSurface;
-                _renderTarget = new GRBackendRenderTarget(
-                    (int)(browserSkiaSurface.Size.Width * browserSkiaSurface.Scaling),
-                    (int)(browserSkiaSurface.Size.Height * browserSkiaSurface.Scaling),
-                    browserSkiaSurface.GlInfo.Samples,
-                    browserSkiaSurface.GlInfo.Stencils, glFbInfo);
-            }
+            _browserSkiaSurface = browserSkiaSurface;
+            _renderTarget = new GRBackendRenderTarget(
+                (int)(browserSkiaSurface.Size.Width * browserSkiaSurface.Scaling),
+                (int)(browserSkiaSurface.Size.Height * browserSkiaSurface.Scaling),
+                browserSkiaSurface.GlInfo.Samples,
+                browserSkiaSurface.GlInfo.Stencils, glFbInfo);
         }
 
         public void Dispose()

+ 0 - 1
src/Browser/Avalonia.Browser/Storage/BlobReadableStream.cs

@@ -7,7 +7,6 @@ using Avalonia.Browser.Interop;
 
 namespace Avalonia.Browser.Storage;
 
-[System.Runtime.Versioning.SupportedOSPlatform("browser")]
 internal class BlobReadableStream : Stream
 {
     private JSObject? _jSReference;

+ 0 - 1
src/Browser/Avalonia.Browser/Storage/BrowserStorageProvider.cs

@@ -13,7 +13,6 @@ namespace Avalonia.Browser.Storage;
 
 internal record FilePickerAcceptType(string Description, IReadOnlyDictionary<string, IReadOnlyList<string>> Accept);
 
-[SupportedOSPlatform("browser")]
 internal class BrowserStorageProvider : IStorageProvider
 {
     internal const string PickerCancelMessage = "The user aborted a request";

+ 0 - 1
src/Browser/Avalonia.Browser/Storage/WriteableStream.cs

@@ -7,7 +7,6 @@ using Avalonia.Browser.Interop;
 
 namespace Avalonia.Browser.Storage;
 
-[System.Runtime.Versioning.SupportedOSPlatform("browser")]
 // Loose wrapper implementaion of a stream on top of FileAPI FileSystemWritableFileStream
 internal sealed class WriteableStream : Stream
 {

+ 0 - 1
src/Browser/Avalonia.Browser/webapp/modules/avalonia/dom.ts

@@ -17,7 +17,6 @@ export class AvaloniaDOM {
         const canvas = document.createElement("canvas");
         canvas.id = `canvas${randomIdPart}`;
         canvas.classList.add("avalonia-canvas");
-        canvas.style.backgroundColor = "#ccc";
         canvas.style.width = "100%";
         canvas.style.position = "absolute";