committed by
GitHub
159 changed files with 1016 additions and 4142 deletions
@ -0,0 +1,5 @@ |
|||||
|
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> |
||||
|
<PropertyGroup> |
||||
|
<EnableNETAnalyzers>true</EnableNETAnalyzers> |
||||
|
</PropertyGroup> |
||||
|
</Project> |
||||
@ -0,0 +1,17 @@ |
|||||
|
using Avalonia; |
||||
|
using Avalonia.Web.Blazor; |
||||
|
|
||||
|
namespace ControlCatalog.Blazor.Web; |
||||
|
|
||||
|
public partial class App |
||||
|
{ |
||||
|
protected override void OnParametersSet() |
||||
|
{ |
||||
|
AppBuilder.Configure<ControlCatalog.App>() |
||||
|
.UseBlazor() |
||||
|
// .With(new SkiaOptions { CustomGpuFactory = null }) // uncomment to disable GPU/GL rendering
|
||||
|
.SetupWithSingleViewLifetime(); |
||||
|
|
||||
|
base.OnParametersSet(); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,29 @@ |
|||||
|
<Project Sdk="Microsoft.NET.Sdk.BlazorWebAssembly"> |
||||
|
<PropertyGroup> |
||||
|
<TargetFramework>net7.0</TargetFramework> |
||||
|
<RuntimeIdentifier>browser-wasm</RuntimeIdentifier> |
||||
|
<Nullable>enable</Nullable> |
||||
|
<EmccTotalMemory>16777216</EmccTotalMemory> |
||||
|
<BlazorEnableTimeZoneSupport>false</BlazorEnableTimeZoneSupport> |
||||
|
<BlazorWebAssemblyPreserveCollationData>false</BlazorWebAssemblyPreserveCollationData> |
||||
|
</PropertyGroup> |
||||
|
|
||||
|
<ItemGroup> |
||||
|
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="7.0.0-rc.1.22427.2" /> |
||||
|
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="7.0.0-rc.1.22427.2" PrivateAssets="all" /> |
||||
|
</ItemGroup> |
||||
|
|
||||
|
<ItemGroup> |
||||
|
<ProjectReference Include="..\..\src\Skia\Avalonia.Skia\Avalonia.Skia.csproj" /> |
||||
|
<ProjectReference Include="..\..\src\Web\Avalonia.Web.Blazor\Avalonia.Web.Blazor.csproj" /> |
||||
|
<ProjectReference Include="..\ControlCatalog\ControlCatalog.csproj" /> |
||||
|
</ItemGroup> |
||||
|
|
||||
|
<Import Project="..\..\build\ReferenceCoreLibraries.props" /> |
||||
|
<Import Project="..\..\build\BuildTargets.targets" /> |
||||
|
|
||||
|
<Import Project="..\..\src\Web\Avalonia.Web\Avalonia.Web.props" /> |
||||
|
<Import Project="..\..\src\Web\Avalonia.Web\Avalonia.Web.targets" /> |
||||
|
|
||||
|
</Project> |
||||
|
|
||||
@ -0,0 +1,29 @@ |
|||||
|
using System; |
||||
|
using System.Net.Http; |
||||
|
using System.Threading.Tasks; |
||||
|
using Microsoft.AspNetCore.Components.WebAssembly.Hosting; |
||||
|
using Microsoft.Extensions.DependencyInjection; |
||||
|
using ControlCatalog.Blazor.Web; |
||||
|
|
||||
|
public class Program |
||||
|
{ |
||||
|
public static async Task Main(string[] args) |
||||
|
{ |
||||
|
await CreateHostBuilder(args).Build().RunAsync(); |
||||
|
} |
||||
|
|
||||
|
public static WebAssemblyHostBuilder CreateHostBuilder(string[] args) |
||||
|
{ |
||||
|
var builder = WebAssemblyHostBuilder.CreateDefault(args); |
||||
|
|
||||
|
builder.RootComponents.Add<App>("#app"); |
||||
|
|
||||
|
builder.Services.AddScoped(sp => new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) }); |
||||
|
|
||||
|
return builder; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
|
Before Width: | Height: | Size: 172 KiB After Width: | Height: | Size: 172 KiB |
@ -1,20 +0,0 @@ |
|||||
using Avalonia; |
|
||||
using Avalonia.Web.Blazor; |
|
||||
|
|
||||
namespace ControlCatalog.Web; |
|
||||
|
|
||||
public partial class App |
|
||||
{ |
|
||||
protected override void OnParametersSet() |
|
||||
{ |
|
||||
WebAppBuilder.Configure<ControlCatalog.App>() |
|
||||
.AfterSetup(_ => |
|
||||
{ |
|
||||
ControlCatalog.Pages.EmbedSample.Implementation = new EmbedSampleWeb(); |
|
||||
}) |
|
||||
.With(new SkiaOptions { CustomGpuFactory = null }) // uncomment to disable GPU/GL rendering
|
|
||||
.SetupWithSingleViewLifetime(); |
|
||||
|
|
||||
base.OnParametersSet(); |
|
||||
} |
|
||||
} |
|
||||
@ -1,34 +1,42 @@ |
|||||
using System; |
using System; |
||||
|
using System.Runtime.InteropServices.JavaScript; |
||||
using Avalonia; |
|
||||
using Avalonia.Platform; |
using Avalonia.Platform; |
||||
using Avalonia.Web.Blazor; |
using Avalonia.Web; |
||||
|
|
||||
using ControlCatalog.Pages; |
using ControlCatalog.Pages; |
||||
|
|
||||
using Microsoft.JSInterop; |
|
||||
|
|
||||
namespace ControlCatalog.Web; |
namespace ControlCatalog.Web; |
||||
|
|
||||
public class EmbedSampleWeb : INativeDemoControl |
public class EmbedSampleWeb : INativeDemoControl |
||||
{ |
{ |
||||
public IPlatformHandle CreateControl(bool isSecond, IPlatformHandle parent, Func<IPlatformHandle> createDefault) |
public IPlatformHandle CreateControl(bool isSecond, IPlatformHandle parent, Func<IPlatformHandle> createDefault) |
||||
{ |
{ |
||||
var runtime = AvaloniaLocator.Current.GetRequiredService<IJSInProcessRuntime>(); |
|
||||
|
|
||||
if (isSecond) |
if (isSecond) |
||||
{ |
{ |
||||
var iframe = runtime.Invoke<IJSInProcessObjectReference>("document.createElement", "iframe"); |
var iframe = EmbedInterop.CreateElement("iframe"); |
||||
iframe.InvokeVoid("setAttribute", "src", "https://www.youtube.com/embed/kZCIporjJ70"); |
iframe.SetProperty("src", "https://www.youtube.com/embed/kZCIporjJ70"); |
||||
|
|
||||
return new JSObjectControlHandle(iframe); |
return new JSObjectControlHandle(iframe); |
||||
} |
} |
||||
else |
else |
||||
{ |
{ |
||||
// window.createAppButton source is defined in "app.js" file.
|
var defaultHandle = (JSObjectControlHandle)createDefault(); |
||||
var button = runtime.Invoke<IJSInProcessObjectReference>("window.createAppButton"); |
|
||||
|
_ = JSHost.ImportAsync("embed.js", "./embed.js").ContinueWith(_ => |
||||
|
{ |
||||
|
EmbedInterop.AddAppButton(defaultHandle.Object); |
||||
|
}); |
||||
|
|
||||
return new JSObjectControlHandle(button); |
return defaultHandle; |
||||
} |
} |
||||
} |
} |
||||
} |
} |
||||
|
|
||||
|
internal static partial class EmbedInterop |
||||
|
{ |
||||
|
[JSImport("globalThis.document.createElement")] |
||||
|
public static partial JSObject CreateElement(string tagName); |
||||
|
|
||||
|
[JSImport("addAppButton", "embed.js")] |
||||
|
public static partial void AddAppButton(JSObject parentObject); |
||||
|
} |
||||
|
|||||
|
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.2 KiB |
@ -0,0 +1,6 @@ |
|||||
|
<linker> |
||||
|
<assembly fullname="ControlCatalog" preserve="All" /> |
||||
|
<assembly fullname="ControlCatalog.Web" preserve="All" /> |
||||
|
<assembly fullname="Avalonia.Themes.Fluent" preserve="All" /> |
||||
|
<assembly fullname="Avalonia.Themes.Simple" preserve="All" /> |
||||
|
</linker> |
||||
|
Before Width: | Height: | Size: 172 KiB After Width: | Height: | Size: 172 KiB |
@ -1,105 +0,0 @@ |
|||||
using Android.OS; |
|
||||
using AndroidX.AppCompat.App; |
|
||||
using Android.Content.Res; |
|
||||
using AndroidX.Lifecycle; |
|
||||
using Avalonia.Controls.ApplicationLifetimes; |
|
||||
using Avalonia.Controls; |
|
||||
using Android.Runtime; |
|
||||
using Android.App; |
|
||||
using Android.Content; |
|
||||
using System; |
|
||||
|
|
||||
namespace Avalonia.Android |
|
||||
{ |
|
||||
public abstract class AvaloniaActivity : AppCompatActivity |
|
||||
{ |
|
||||
internal class SingleViewLifetime : ISingleViewApplicationLifetime |
|
||||
{ |
|
||||
public AvaloniaView View { get; internal set; } |
|
||||
|
|
||||
public Control MainView |
|
||||
{ |
|
||||
get => (Control)View.Content; |
|
||||
set => View.Content = value; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
internal Action<int, Result, Intent> ActivityResult; |
|
||||
internal AvaloniaView View; |
|
||||
internal AvaloniaViewModel _viewModel; |
|
||||
|
|
||||
protected abstract AppBuilder CreateAppBuilder(); |
|
||||
|
|
||||
protected override void OnCreate(Bundle savedInstanceState) |
|
||||
{ |
|
||||
var builder = CreateAppBuilder(); |
|
||||
|
|
||||
|
|
||||
var lifetime = new SingleViewLifetime(); |
|
||||
|
|
||||
builder.AfterSetup(x => |
|
||||
{ |
|
||||
_viewModel = new ViewModelProvider(this).Get(Java.Lang.Class.FromType(typeof(AvaloniaViewModel))) as AvaloniaViewModel; |
|
||||
|
|
||||
View = new AvaloniaView(this); |
|
||||
if (_viewModel.Content != null) |
|
||||
{ |
|
||||
View.Content = _viewModel.Content; |
|
||||
} |
|
||||
|
|
||||
SetContentView(View); |
|
||||
lifetime.View = View; |
|
||||
|
|
||||
View.Prepare(); |
|
||||
}); |
|
||||
|
|
||||
builder.SetupWithLifetime(lifetime); |
|
||||
|
|
||||
base.OnCreate(savedInstanceState); |
|
||||
} |
|
||||
public object Content |
|
||||
{ |
|
||||
get |
|
||||
{ |
|
||||
return _viewModel.Content; |
|
||||
} |
|
||||
set |
|
||||
{ |
|
||||
_viewModel.Content = value; |
|
||||
if (View != null) |
|
||||
View.Content = value; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
public override void OnConfigurationChanged(Configuration newConfig) |
|
||||
{ |
|
||||
base.OnConfigurationChanged(newConfig); |
|
||||
} |
|
||||
|
|
||||
protected override void OnDestroy() |
|
||||
{ |
|
||||
View.Content = null; |
|
||||
|
|
||||
base.OnDestroy(); |
|
||||
} |
|
||||
|
|
||||
protected override void OnActivityResult(int requestCode, [GeneratedEnum] Result resultCode, Intent data) |
|
||||
{ |
|
||||
base.OnActivityResult(requestCode, resultCode, data); |
|
||||
|
|
||||
ActivityResult?.Invoke(requestCode, resultCode, data); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
public abstract class AvaloniaActivity<TApp> : AvaloniaActivity where TApp : Application, new() |
|
||||
{ |
|
||||
protected virtual AppBuilder CustomizeAppBuilder(AppBuilder builder) => builder.UseAndroid(); |
|
||||
|
|
||||
protected override AppBuilder CreateAppBuilder() |
|
||||
{ |
|
||||
var builder = AppBuilder.Configure<TApp>(); |
|
||||
|
|
||||
return CustomizeAppBuilder(builder); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -0,0 +1,72 @@ |
|||||
|
using System; |
||||
|
using Android.App; |
||||
|
using Android.Content; |
||||
|
using Android.Content.Res; |
||||
|
using Android.OS; |
||||
|
using Android.Runtime; |
||||
|
using AndroidX.AppCompat.App; |
||||
|
using AndroidX.Lifecycle; |
||||
|
|
||||
|
namespace Avalonia.Android |
||||
|
{ |
||||
|
public abstract class AvaloniaMainActivity : AppCompatActivity |
||||
|
{ |
||||
|
internal static object ViewContent; |
||||
|
|
||||
|
internal Action<int, Result, Intent> ActivityResult; |
||||
|
internal AvaloniaView View; |
||||
|
|
||||
|
protected override void OnCreate(Bundle savedInstanceState) |
||||
|
{ |
||||
|
View = new AvaloniaView(this); |
||||
|
if (ViewContent != null) |
||||
|
{ |
||||
|
View.Content = ViewContent; |
||||
|
} |
||||
|
|
||||
|
View.Prepare(); |
||||
|
|
||||
|
if (Avalonia.Application.Current.ApplicationLifetime is SingleViewLifetime lifetime) |
||||
|
{ |
||||
|
lifetime.View = View; |
||||
|
} |
||||
|
|
||||
|
base.OnCreate(savedInstanceState); |
||||
|
|
||||
|
SetContentView(View); |
||||
|
} |
||||
|
|
||||
|
public object Content |
||||
|
{ |
||||
|
get |
||||
|
{ |
||||
|
return ViewContent; |
||||
|
} |
||||
|
set |
||||
|
{ |
||||
|
ViewContent = value; |
||||
|
if (View != null) |
||||
|
View.Content = value; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public override void OnConfigurationChanged(Configuration newConfig) |
||||
|
{ |
||||
|
base.OnConfigurationChanged(newConfig); |
||||
|
} |
||||
|
|
||||
|
protected override void OnDestroy() |
||||
|
{ |
||||
|
View.Content = null; |
||||
|
|
||||
|
base.OnDestroy(); |
||||
|
} |
||||
|
|
||||
|
protected override void OnActivityResult(int requestCode, [GeneratedEnum] Result resultCode, Intent data) |
||||
|
{ |
||||
|
base.OnActivityResult(requestCode, resultCode, data); |
||||
|
|
||||
|
ActivityResult?.Invoke(requestCode, resultCode, data); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,34 @@ |
|||||
|
using Android.OS; |
||||
|
using AndroidX.AppCompat.App; |
||||
|
using AndroidX.Lifecycle; |
||||
|
|
||||
|
namespace Avalonia.Android |
||||
|
{ |
||||
|
public abstract class AvaloniaSplashActivity : AppCompatActivity |
||||
|
{ |
||||
|
protected abstract AppBuilder CreateAppBuilder(); |
||||
|
|
||||
|
protected override void OnCreate(Bundle? savedInstanceState) |
||||
|
{ |
||||
|
base.OnCreate(savedInstanceState); |
||||
|
|
||||
|
var builder = CreateAppBuilder(); |
||||
|
|
||||
|
var lifetime = new SingleViewLifetime(); |
||||
|
|
||||
|
builder.SetupWithLifetime(lifetime); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public abstract class AvaloniaSplashActivity<TApp> : AvaloniaSplashActivity where TApp : Application, new() |
||||
|
{ |
||||
|
protected virtual AppBuilder CustomizeAppBuilder(AppBuilder builder) => builder.UseAndroid(); |
||||
|
|
||||
|
protected override AppBuilder CreateAppBuilder() |
||||
|
{ |
||||
|
var builder = AppBuilder.Configure<TApp>(); |
||||
|
|
||||
|
return CustomizeAppBuilder(builder); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -1,11 +0,0 @@ |
|||||
using System; |
|
||||
using System.Collections.Generic; |
|
||||
using System.Text; |
|
||||
|
|
||||
namespace Avalonia.Android |
|
||||
{ |
|
||||
internal class AvaloniaViewModel : AndroidX.Lifecycle.ViewModel |
|
||||
{ |
|
||||
public object Content { get; set; } |
|
||||
} |
|
||||
} |
|
||||
@ -0,0 +1,26 @@ |
|||||
|
using Avalonia.Controls; |
||||
|
using Avalonia.Controls.ApplicationLifetimes; |
||||
|
|
||||
|
namespace Avalonia.Android |
||||
|
{ |
||||
|
internal class SingleViewLifetime : ISingleViewApplicationLifetime |
||||
|
{ |
||||
|
private AvaloniaView _view; |
||||
|
|
||||
|
public AvaloniaView View |
||||
|
{ |
||||
|
get => _view; internal set |
||||
|
{ |
||||
|
if (_view != null) |
||||
|
{ |
||||
|
_view.Content = null; |
||||
|
_view.Dispose(); |
||||
|
} |
||||
|
_view = value; |
||||
|
_view.Content = MainView; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public Control MainView { get; set; } |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,359 @@ |
|||||
|
using Avalonia.Media; |
||||
|
using Avalonia.Utilities; |
||||
|
|
||||
|
namespace Avalonia.Controls |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Implements a reduced version of the 2014 Material Design color palette.
|
||||
|
/// </summary>
|
||||
|
/// <remarks>
|
||||
|
/// This palette is based on the one outlined here:
|
||||
|
///
|
||||
|
/// https://material.io/design/color/the-color-system.html#tools-for-picking-colors
|
||||
|
///
|
||||
|
/// In order to make the palette uniform and rectangular the following
|
||||
|
/// alterations were made:
|
||||
|
///
|
||||
|
/// 1. The A100-A700 shades of each color are excluded.
|
||||
|
/// These shades do not exist for all colors (brown/gray).
|
||||
|
/// 2. Black/White are stand-alone and are also excluded.
|
||||
|
///
|
||||
|
/// </remarks>
|
||||
|
public class MaterialColorPalette : IColorPalette |
||||
|
{ |
||||
|
// See: https://material.io/design/color/the-color-system.html#tools-for-picking-colors
|
||||
|
// This is a reduced palette for uniformity
|
||||
|
private static Color[,]? _colorChart = null; |
||||
|
private static int _colorChartColorCount = 0; |
||||
|
private static int _colorChartShadeCount = 0; |
||||
|
private static object _colorChartMutex = new object(); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Initializes all color chart colors.
|
||||
|
/// </summary>
|
||||
|
/// <remarks>
|
||||
|
/// This is pulled out separately to lazy load for performance.
|
||||
|
/// If no material color palette is ever used, no colors will be created.
|
||||
|
/// </remarks>
|
||||
|
private void InitColorChart() |
||||
|
{ |
||||
|
lock (_colorChartMutex) |
||||
|
{ |
||||
|
_colorChart = new Color[,] |
||||
|
{ |
||||
|
// Red
|
||||
|
{ |
||||
|
Color.FromArgb(0xFF, 0xFF, 0xEB, 0xEE), |
||||
|
Color.FromArgb(0xFF, 0xFF, 0xCD, 0xD2), |
||||
|
Color.FromArgb(0xFF, 0xEF, 0x9A, 0x9A), |
||||
|
Color.FromArgb(0xFF, 0xE5, 0x73, 0x73), |
||||
|
Color.FromArgb(0xFF, 0xEF, 0x53, 0x50), |
||||
|
Color.FromArgb(0xFF, 0xF4, 0x43, 0x36), |
||||
|
Color.FromArgb(0xFF, 0xE5, 0x39, 0x35), |
||||
|
Color.FromArgb(0xFF, 0xD3, 0x2F, 0x2F), |
||||
|
Color.FromArgb(0xFF, 0xC6, 0x28, 0x28), |
||||
|
Color.FromArgb(0xFF, 0xB7, 0x1C, 0x1C), |
||||
|
}, |
||||
|
|
||||
|
// Pink
|
||||
|
{ |
||||
|
Color.FromArgb(0xFF, 0xFC, 0xE4, 0xEC), |
||||
|
Color.FromArgb(0xFF, 0xF8, 0xBB, 0xD0), |
||||
|
Color.FromArgb(0xFF, 0xF4, 0x8F, 0xB1), |
||||
|
Color.FromArgb(0xFF, 0xF0, 0x62, 0x92), |
||||
|
Color.FromArgb(0xFF, 0xEC, 0x40, 0x7A), |
||||
|
Color.FromArgb(0xFF, 0xE9, 0x1E, 0x63), |
||||
|
Color.FromArgb(0xFF, 0xD8, 0x1B, 0x60), |
||||
|
Color.FromArgb(0xFF, 0xC2, 0x18, 0x5B), |
||||
|
Color.FromArgb(0xFF, 0xAD, 0x14, 0x57), |
||||
|
Color.FromArgb(0xFF, 0x88, 0x0E, 0x4F), |
||||
|
}, |
||||
|
|
||||
|
// Purple
|
||||
|
{ |
||||
|
Color.FromArgb(0xFF, 0xF3, 0xE5, 0xF5), |
||||
|
Color.FromArgb(0xFF, 0xE1, 0xBE, 0xE7), |
||||
|
Color.FromArgb(0xFF, 0xCE, 0x93, 0xD8), |
||||
|
Color.FromArgb(0xFF, 0xBA, 0x68, 0xC8), |
||||
|
Color.FromArgb(0xFF, 0xAB, 0x47, 0xBC), |
||||
|
Color.FromArgb(0xFF, 0x9C, 0x27, 0xB0), |
||||
|
Color.FromArgb(0xFF, 0x8E, 0x24, 0xAA), |
||||
|
Color.FromArgb(0xFF, 0x7B, 0x1F, 0xA2), |
||||
|
Color.FromArgb(0xFF, 0x6A, 0x1B, 0x9A), |
||||
|
Color.FromArgb(0xFF, 0x4A, 0x14, 0x8C), |
||||
|
}, |
||||
|
|
||||
|
// Deep Purple
|
||||
|
{ |
||||
|
Color.FromArgb(0xFF, 0xED, 0xE7, 0xF6), |
||||
|
Color.FromArgb(0xFF, 0xD1, 0xC4, 0xE9), |
||||
|
Color.FromArgb(0xFF, 0xB3, 0x9D, 0xDB), |
||||
|
Color.FromArgb(0xFF, 0x95, 0x75, 0xCD), |
||||
|
Color.FromArgb(0xFF, 0x7E, 0x57, 0xC2), |
||||
|
Color.FromArgb(0xFF, 0x67, 0x3A, 0xB7), |
||||
|
Color.FromArgb(0xFF, 0x5E, 0x35, 0xB1), |
||||
|
Color.FromArgb(0xFF, 0x51, 0x2D, 0xA8), |
||||
|
Color.FromArgb(0xFF, 0x45, 0x27, 0xA0), |
||||
|
Color.FromArgb(0xFF, 0x31, 0x1B, 0x92), |
||||
|
}, |
||||
|
|
||||
|
// Indigo
|
||||
|
{ |
||||
|
Color.FromArgb(0xFF, 0xE8, 0xEA, 0xF6), |
||||
|
Color.FromArgb(0xFF, 0xC5, 0xCA, 0xE9), |
||||
|
Color.FromArgb(0xFF, 0x9F, 0xA8, 0xDA), |
||||
|
Color.FromArgb(0xFF, 0x79, 0x86, 0xCB), |
||||
|
Color.FromArgb(0xFF, 0x5C, 0x6B, 0xC0), |
||||
|
Color.FromArgb(0xFF, 0x3F, 0x51, 0xB5), |
||||
|
Color.FromArgb(0xFF, 0x39, 0x49, 0xAB), |
||||
|
Color.FromArgb(0xFF, 0x30, 0x3F, 0x9F), |
||||
|
Color.FromArgb(0xFF, 0x28, 0x35, 0x93), |
||||
|
Color.FromArgb(0xFF, 0x1A, 0x23, 0x7E), |
||||
|
}, |
||||
|
|
||||
|
// Blue
|
||||
|
{ |
||||
|
Color.FromArgb(0xFF, 0xE3, 0xF2, 0xFD), |
||||
|
Color.FromArgb(0xFF, 0xBB, 0xDE, 0xFB), |
||||
|
Color.FromArgb(0xFF, 0x90, 0xCA, 0xF9), |
||||
|
Color.FromArgb(0xFF, 0x64, 0xB5, 0xF6), |
||||
|
Color.FromArgb(0xFF, 0x42, 0xA5, 0xF5), |
||||
|
Color.FromArgb(0xFF, 0x21, 0x96, 0xF3), |
||||
|
Color.FromArgb(0xFF, 0x1E, 0x88, 0xE5), |
||||
|
Color.FromArgb(0xFF, 0x19, 0x76, 0xD2), |
||||
|
Color.FromArgb(0xFF, 0x15, 0x65, 0xC0), |
||||
|
Color.FromArgb(0xFF, 0x0D, 0x47, 0xA1), |
||||
|
}, |
||||
|
|
||||
|
// Light Blue
|
||||
|
{ |
||||
|
Color.FromArgb(0xFF, 0xE1, 0xF5, 0xFE), |
||||
|
Color.FromArgb(0xFF, 0xB3, 0xE5, 0xFC), |
||||
|
Color.FromArgb(0xFF, 0x81, 0xD4, 0xFA), |
||||
|
Color.FromArgb(0xFF, 0x4F, 0xC3, 0xF7), |
||||
|
Color.FromArgb(0xFF, 0x29, 0xB6, 0xF6), |
||||
|
Color.FromArgb(0xFF, 0x03, 0xA9, 0xF4), |
||||
|
Color.FromArgb(0xFF, 0x03, 0x9B, 0xE5), |
||||
|
Color.FromArgb(0xFF, 0x02, 0x88, 0xD1), |
||||
|
Color.FromArgb(0xFF, 0x02, 0x77, 0xBD), |
||||
|
Color.FromArgb(0xFF, 0x01, 0x57, 0x9B), |
||||
|
}, |
||||
|
|
||||
|
// Cyan
|
||||
|
{ |
||||
|
Color.FromArgb(0xFF, 0xE0, 0xF7, 0xFA), |
||||
|
Color.FromArgb(0xFF, 0xB2, 0xEB, 0xF2), |
||||
|
Color.FromArgb(0xFF, 0x80, 0xDE, 0xEA), |
||||
|
Color.FromArgb(0xFF, 0x4D, 0xD0, 0xE1), |
||||
|
Color.FromArgb(0xFF, 0x26, 0xC6, 0xDA), |
||||
|
Color.FromArgb(0xFF, 0x00, 0xBC, 0xD4), |
||||
|
Color.FromArgb(0xFF, 0x00, 0xAC, 0xC1), |
||||
|
Color.FromArgb(0xFF, 0x00, 0x97, 0xA7), |
||||
|
Color.FromArgb(0xFF, 0x00, 0x83, 0x8F), |
||||
|
Color.FromArgb(0xFF, 0x00, 0x60, 0x64), |
||||
|
}, |
||||
|
|
||||
|
// Teal
|
||||
|
{ |
||||
|
Color.FromArgb(0xFF, 0xE0, 0xF2, 0xF1), |
||||
|
Color.FromArgb(0xFF, 0xB2, 0xDF, 0xDB), |
||||
|
Color.FromArgb(0xFF, 0x80, 0xCB, 0xC4), |
||||
|
Color.FromArgb(0xFF, 0x4D, 0xB6, 0xAC), |
||||
|
Color.FromArgb(0xFF, 0x26, 0xA6, 0x9A), |
||||
|
Color.FromArgb(0xFF, 0x00, 0x96, 0x88), |
||||
|
Color.FromArgb(0xFF, 0x00, 0x89, 0x7B), |
||||
|
Color.FromArgb(0xFF, 0x00, 0x79, 0x6B), |
||||
|
Color.FromArgb(0xFF, 0x00, 0x69, 0x5C), |
||||
|
Color.FromArgb(0xFF, 0x00, 0x4D, 0x40), |
||||
|
}, |
||||
|
|
||||
|
// Green
|
||||
|
{ |
||||
|
Color.FromArgb(0xFF, 0xE8, 0xF5, 0xE9), |
||||
|
Color.FromArgb(0xFF, 0xC8, 0xE6, 0xC9), |
||||
|
Color.FromArgb(0xFF, 0xA5, 0xD6, 0xA7), |
||||
|
Color.FromArgb(0xFF, 0x81, 0xC7, 0x84), |
||||
|
Color.FromArgb(0xFF, 0x66, 0xBB, 0x6A), |
||||
|
Color.FromArgb(0xFF, 0x4C, 0xAF, 0x50), |
||||
|
Color.FromArgb(0xFF, 0x43, 0xA0, 0x47), |
||||
|
Color.FromArgb(0xFF, 0x38, 0x8E, 0x3C), |
||||
|
Color.FromArgb(0xFF, 0x2E, 0x7D, 0x32), |
||||
|
Color.FromArgb(0xFF, 0x1B, 0x5E, 0x20), |
||||
|
}, |
||||
|
|
||||
|
// Light Green
|
||||
|
{ |
||||
|
Color.FromArgb(0xFF, 0xF1, 0xF8, 0xE9), |
||||
|
Color.FromArgb(0xFF, 0xDC, 0xED, 0xC8), |
||||
|
Color.FromArgb(0xFF, 0xC5, 0xE1, 0xA5), |
||||
|
Color.FromArgb(0xFF, 0xAE, 0xD5, 0x81), |
||||
|
Color.FromArgb(0xFF, 0x9C, 0xCC, 0x65), |
||||
|
Color.FromArgb(0xFF, 0x8B, 0xC3, 0x4A), |
||||
|
Color.FromArgb(0xFF, 0x7C, 0xB3, 0x42), |
||||
|
Color.FromArgb(0xFF, 0x68, 0x9F, 0x38), |
||||
|
Color.FromArgb(0xFF, 0x55, 0x8B, 0x2F), |
||||
|
Color.FromArgb(0xFF, 0x33, 0x69, 0x1E), |
||||
|
}, |
||||
|
|
||||
|
// Lime
|
||||
|
{ |
||||
|
Color.FromArgb(0xFF, 0xF9, 0xFB, 0xE7), |
||||
|
Color.FromArgb(0xFF, 0xF0, 0xF4, 0xC3), |
||||
|
Color.FromArgb(0xFF, 0xE6, 0xEE, 0x9C), |
||||
|
Color.FromArgb(0xFF, 0xDC, 0xE7, 0x75), |
||||
|
Color.FromArgb(0xFF, 0xD4, 0xE1, 0x57), |
||||
|
Color.FromArgb(0xFF, 0xCD, 0xDC, 0x39), |
||||
|
Color.FromArgb(0xFF, 0xC0, 0xCA, 0x33), |
||||
|
Color.FromArgb(0xFF, 0xAF, 0xB4, 0x2B), |
||||
|
Color.FromArgb(0xFF, 0x9E, 0x9D, 0x24), |
||||
|
Color.FromArgb(0xFF, 0x82, 0x77, 0x17), |
||||
|
}, |
||||
|
|
||||
|
// Yellow
|
||||
|
{ |
||||
|
Color.FromArgb(0xFF, 0xFF, 0xFD, 0xE7), |
||||
|
Color.FromArgb(0xFF, 0xFF, 0xF9, 0xC4), |
||||
|
Color.FromArgb(0xFF, 0xFF, 0xF5, 0x9D), |
||||
|
Color.FromArgb(0xFF, 0xFF, 0xF1, 0x76), |
||||
|
Color.FromArgb(0xFF, 0xFF, 0xEE, 0x58), |
||||
|
Color.FromArgb(0xFF, 0xFF, 0xEB, 0x3B), |
||||
|
Color.FromArgb(0xFF, 0xFD, 0xD8, 0x35), |
||||
|
Color.FromArgb(0xFF, 0xFB, 0xC0, 0x2D), |
||||
|
Color.FromArgb(0xFF, 0xF9, 0xA8, 0x25), |
||||
|
Color.FromArgb(0xFF, 0xF5, 0x7F, 0x17), |
||||
|
}, |
||||
|
|
||||
|
// Amber
|
||||
|
{ |
||||
|
Color.FromArgb(0xFF, 0xFF, 0xF8, 0xE1), |
||||
|
Color.FromArgb(0xFF, 0xFF, 0xEC, 0xB3), |
||||
|
Color.FromArgb(0xFF, 0xFF, 0xE0, 0x82), |
||||
|
Color.FromArgb(0xFF, 0xFF, 0xD5, 0x4F), |
||||
|
Color.FromArgb(0xFF, 0xFF, 0xCA, 0x28), |
||||
|
Color.FromArgb(0xFF, 0xFF, 0xC1, 0x07), |
||||
|
Color.FromArgb(0xFF, 0xFF, 0xB3, 0x00), |
||||
|
Color.FromArgb(0xFF, 0xFF, 0xA0, 0x00), |
||||
|
Color.FromArgb(0xFF, 0xFF, 0x8F, 0x00), |
||||
|
Color.FromArgb(0xFF, 0xFF, 0x6F, 0x00), |
||||
|
}, |
||||
|
|
||||
|
// Orange
|
||||
|
{ |
||||
|
Color.FromArgb(0xFF, 0xFF, 0xF3, 0xE0), |
||||
|
Color.FromArgb(0xFF, 0xFF, 0xE0, 0xB2), |
||||
|
Color.FromArgb(0xFF, 0xFF, 0xCC, 0x80), |
||||
|
Color.FromArgb(0xFF, 0xFF, 0xB7, 0x4D), |
||||
|
Color.FromArgb(0xFF, 0xFF, 0xA7, 0x26), |
||||
|
Color.FromArgb(0xFF, 0xFF, 0x98, 0x00), |
||||
|
Color.FromArgb(0xFF, 0xFB, 0x8C, 0x00), |
||||
|
Color.FromArgb(0xFF, 0xF5, 0x7C, 0x00), |
||||
|
Color.FromArgb(0xFF, 0xEF, 0x6C, 0x00), |
||||
|
Color.FromArgb(0xFF, 0xE6, 0x51, 0x00), |
||||
|
}, |
||||
|
|
||||
|
// Deep Orange
|
||||
|
{ |
||||
|
Color.FromArgb(0xFF, 0xFB, 0xE9, 0xE7), |
||||
|
Color.FromArgb(0xFF, 0xFF, 0xCC, 0xBC), |
||||
|
Color.FromArgb(0xFF, 0xFF, 0xAB, 0x91), |
||||
|
Color.FromArgb(0xFF, 0xFF, 0x8A, 0x65), |
||||
|
Color.FromArgb(0xFF, 0xFF, 0x70, 0x43), |
||||
|
Color.FromArgb(0xFF, 0xFF, 0x57, 0x22), |
||||
|
Color.FromArgb(0xFF, 0xF4, 0x51, 0x1E), |
||||
|
Color.FromArgb(0xFF, 0xE6, 0x4A, 0x19), |
||||
|
Color.FromArgb(0xFF, 0xD8, 0x43, 0x15), |
||||
|
Color.FromArgb(0xFF, 0xBF, 0x36, 0x0C), |
||||
|
}, |
||||
|
|
||||
|
// Brown
|
||||
|
{ |
||||
|
Color.FromArgb(0xFF, 0xEF, 0xEB, 0xE9), |
||||
|
Color.FromArgb(0xFF, 0xD7, 0xCC, 0xC8), |
||||
|
Color.FromArgb(0xFF, 0xBC, 0xAA, 0xA4), |
||||
|
Color.FromArgb(0xFF, 0xA1, 0x88, 0x7F), |
||||
|
Color.FromArgb(0xFF, 0x8D, 0x6E, 0x63), |
||||
|
Color.FromArgb(0xFF, 0x79, 0x55, 0x48), |
||||
|
Color.FromArgb(0xFF, 0x6D, 0x4C, 0x41), |
||||
|
Color.FromArgb(0xFF, 0x5D, 0x40, 0x37), |
||||
|
Color.FromArgb(0xFF, 0x4E, 0x34, 0x2E), |
||||
|
Color.FromArgb(0xFF, 0x3E, 0x27, 0x23), |
||||
|
}, |
||||
|
|
||||
|
// Gray
|
||||
|
{ |
||||
|
Color.FromArgb(0xFF, 0xFA, 0xFA, 0xFA), |
||||
|
Color.FromArgb(0xFF, 0xF5, 0xF5, 0xF5), |
||||
|
Color.FromArgb(0xFF, 0xEE, 0xEE, 0xEE), |
||||
|
Color.FromArgb(0xFF, 0xE0, 0xE0, 0xE0), |
||||
|
Color.FromArgb(0xFF, 0xBD, 0xBD, 0xBD), |
||||
|
Color.FromArgb(0xFF, 0x9E, 0x9E, 0x9E), |
||||
|
Color.FromArgb(0xFF, 0x75, 0x75, 0x75), |
||||
|
Color.FromArgb(0xFF, 0x61, 0x61, 0x61), |
||||
|
Color.FromArgb(0xFF, 0x42, 0x42, 0x42), |
||||
|
Color.FromArgb(0xFF, 0x21, 0x21, 0x21), |
||||
|
}, |
||||
|
|
||||
|
// Blue Gray
|
||||
|
{ |
||||
|
Color.FromArgb(0xFF, 0xEC, 0xEF, 0xF1), |
||||
|
Color.FromArgb(0xFF, 0xCF, 0xD8, 0xDC), |
||||
|
Color.FromArgb(0xFF, 0xB0, 0xBE, 0xC5), |
||||
|
Color.FromArgb(0xFF, 0x90, 0xA4, 0xAE), |
||||
|
Color.FromArgb(0xFF, 0x78, 0x90, 0x9C), |
||||
|
Color.FromArgb(0xFF, 0x60, 0x7D, 0x8B), |
||||
|
Color.FromArgb(0xFF, 0x54, 0x6E, 0x7A), |
||||
|
Color.FromArgb(0xFF, 0x45, 0x5A, 0x64), |
||||
|
Color.FromArgb(0xFF, 0x37, 0x47, 0x4F), |
||||
|
Color.FromArgb(0xFF, 0x26, 0x32, 0x38), |
||||
|
}, |
||||
|
}; |
||||
|
|
||||
|
_colorChartColorCount = _colorChart.GetLength(0); |
||||
|
_colorChartShadeCount = _colorChart.GetLength(1); |
||||
|
} |
||||
|
|
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
public int ColorCount |
||||
|
{ |
||||
|
get |
||||
|
{ |
||||
|
if (_colorChart == null) |
||||
|
{ |
||||
|
InitColorChart(); |
||||
|
} |
||||
|
|
||||
|
return _colorChartColorCount; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
public int ShadeCount |
||||
|
{ |
||||
|
get |
||||
|
{ |
||||
|
if (_colorChart == null) |
||||
|
{ |
||||
|
InitColorChart(); |
||||
|
} |
||||
|
|
||||
|
return _colorChartShadeCount; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
public Color GetColor(int colorIndex, int shadeIndex) |
||||
|
{ |
||||
|
if (_colorChart == null) |
||||
|
{ |
||||
|
InitColorChart(); |
||||
|
} |
||||
|
|
||||
|
return _colorChart![ |
||||
|
MathUtilities.Clamp(colorIndex, 0, _colorChartColorCount - 1), |
||||
|
MathUtilities.Clamp(shadeIndex, 0, _colorChartShadeCount - 1)]; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -1,7 +0,0 @@ |
|||||
<Project> |
|
||||
<PropertyGroup> |
|
||||
<EmccTotalMemory>16777216</EmccTotalMemory> |
|
||||
<BlazorEnableTimeZoneSupport>false</BlazorEnableTimeZoneSupport> |
|
||||
<BlazorWebAssemblyPreserveCollationData>false</BlazorWebAssemblyPreserveCollationData> |
|
||||
</PropertyGroup> |
|
||||
</Project> |
|
||||
@ -1,53 +1,40 @@ |
|||||
<Project Sdk="Microsoft.NET.Sdk.Razor"> |
<Project Sdk="Microsoft.NET.Sdk.Razor"> |
||||
|
|
||||
<PropertyGroup> |
<PropertyGroup> |
||||
<TargetFramework>net6.0</TargetFramework> |
<TargetFramework>net7.0</TargetFramework> |
||||
<ImplicitUsings>enable</ImplicitUsings> |
|
||||
<PackageId>Avalonia.Web.Blazor</PackageId> |
<PackageId>Avalonia.Web.Blazor</PackageId> |
||||
<LangVersion>preview</LangVersion> |
<ResolveStaticWebAssetsInputsDependsOn>_IncludeGeneratedAvaloniaStaticFiles;$(ResolveStaticWebAssetsInputsDependsOn)</ResolveStaticWebAssetsInputsDependsOn> |
||||
<MSBuildEnableWorkloadResolver>false</MSBuildEnableWorkloadResolver> |
|
||||
<StaticWebAssetsDisableProjectBuildPropsFileGeneration>true</StaticWebAssetsDisableProjectBuildPropsFileGeneration> |
|
||||
</PropertyGroup> |
</PropertyGroup> |
||||
|
|
||||
<ItemGroup> |
<ItemGroup> |
||||
<SupportedPlatform Include="browser" /> |
<SupportedPlatform Include="browser" /> |
||||
</ItemGroup> |
</ItemGroup> |
||||
|
|
||||
<Import Project="..\..\..\build\BuildTargets.targets" /> |
|
||||
<Import Project="..\..\..\build\SkiaSharp.props" /> |
|
||||
<Import Project="..\..\..\build\HarfBuzzSharp.props" /> |
|
||||
<Import Project="..\..\..\build\NullableEnable.props" /> |
<Import Project="..\..\..\build\NullableEnable.props" /> |
||||
|
|
||||
<ItemGroup> |
<ItemGroup> |
||||
<Content Include="*.props"> |
<PackageReference Include="Microsoft.AspNetCore.Components.Web" Version="7.0.0-*" /> |
||||
<Pack>true</Pack> |
|
||||
<PackagePath>build\</PackagePath> |
|
||||
</Content> |
|
||||
<Content Include="*.targets"> |
|
||||
<Pack>true</Pack> |
|
||||
<PackagePath>build\;buildTransitive\</PackagePath> |
|
||||
</Content> |
|
||||
</ItemGroup> |
</ItemGroup> |
||||
|
|
||||
<ItemGroup> |
<ItemGroup> |
||||
<PackageReference Include="Microsoft.AspNetCore.Components.Web" Version="6.0.8" /> |
<ProjectReference Include="..\Avalonia.Web\Avalonia.Web.csproj" /> |
||||
</ItemGroup> |
|
||||
|
|
||||
<ItemGroup> |
|
||||
<ProjectReference Include="..\..\Avalonia.Base\Avalonia.Base.csproj" /> |
|
||||
<ProjectReference Include="..\..\Skia\Avalonia.Skia\Avalonia.Skia.csproj" /> |
|
||||
</ItemGroup> |
|
||||
|
|
||||
<ItemGroup> |
|
||||
<Folder Include="wwwroot\" /> |
|
||||
</ItemGroup> |
</ItemGroup> |
||||
|
|
||||
<Target Name="NpmInstall" Inputs="webapp/package.json" Outputs="webapp/node_modules/.install-stamp"> |
<Target Name="_IncludeGeneratedAvaloniaStaticFiles"> |
||||
<Exec Command="npm install" WorkingDirectory="webapp" /> |
<ItemGroup> |
||||
<!-- Write the stamp file, so incremental builds work --> |
<_AvaloniaWebAssets Include="$(MSBuildThisFileDirectory)../Avalonia.Web/wwwroot/**/*.*" /> |
||||
<Touch Files="webapp/node_modules/.install-stamp" AlwaysCreate="true" /> |
</ItemGroup> |
||||
</Target> |
<DefineStaticWebAssets SourceId="$(PackageId)" |
||||
<Target Name="NpmRunBuild" DependsOnTargets="NpmInstall" BeforeTargets="BeforeBuild"> |
SourceType="Computed" |
||||
<Exec Command="npm run build" WorkingDirectory="webapp" /> |
AssetKind="All" |
||||
|
AssetRole="Primary" |
||||
|
CopyToOutputDirectory="PreserveNewest" |
||||
|
CopyToPublishDirectory="PreserveNewest" |
||||
|
ContentRoot="$(MSBuildThisFileDirectory)../Avalonia.Web/wwwroot/" |
||||
|
BasePath="_content\$(PackageId)" |
||||
|
CandidateAssets="@(_AvaloniaWebAssets)" |
||||
|
RelativePathFilter="**.js"> |
||||
|
<Output TaskParameter="Assets" ItemName="StaticWebAsset" /> |
||||
|
</DefineStaticWebAssets> |
||||
</Target> |
</Target> |
||||
</Project> |
</Project> |
||||
|
|||||
@ -1,4 +0,0 @@ |
|||||
<Project> |
|
||||
<Import Project="Microsoft.AspNetCore.StaticWebAssets.props" /> |
|
||||
<Import Project="Avalonia.Web.Blazor.CompilationTuning.props" /> |
|
||||
</Project> |
|
||||
@ -1,6 +0,0 @@ |
|||||
<Project> |
|
||||
<ItemGroup> |
|
||||
<NativeFileReference Include="$(HarfBuzzSharpStaticLibraryPath)\2.0.23\libHarfBuzzSharp.a" /> |
|
||||
<NativeFileReference Include="$(SkiaSharpStaticLibraryPath)\2.0.23\libSkiaSharp.a" /> |
|
||||
</ItemGroup> |
|
||||
</Project> |
|
||||
@ -1,20 +0,0 @@ |
|||||
using Avalonia.Controls; |
|
||||
using Avalonia.Platform; |
|
||||
|
|
||||
namespace Avalonia.Web.Blazor |
|
||||
{ |
|
||||
public class AvaloniaBlazorAppBuilder : AppBuilderBase<AvaloniaBlazorAppBuilder> |
|
||||
{ |
|
||||
public AvaloniaBlazorAppBuilder(IRuntimePlatform platform, Action<AvaloniaBlazorAppBuilder> platformServices) |
|
||||
: base(platform, platformServices) |
|
||||
{ |
|
||||
} |
|
||||
|
|
||||
public AvaloniaBlazorAppBuilder() |
|
||||
: base(new StandardRuntimePlatform(), |
|
||||
builder => StandardRuntimePlatformServices.Register(builder.ApplicationType!.Assembly)) |
|
||||
{ |
|
||||
UseWindowingSubsystem(BlazorWindowingPlatform.Register); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -0,0 +1,44 @@ |
|||||
|
using System.Runtime.InteropServices.JavaScript; |
||||
|
using System.Runtime.Versioning; |
||||
|
using System.Threading.Tasks; |
||||
|
using System; |
||||
|
using Avalonia.Controls.ApplicationLifetimes; |
||||
|
using Microsoft.AspNetCore.Components; |
||||
|
using Microsoft.AspNetCore.Components.Rendering; |
||||
|
using BrowserView = Avalonia.Web.AvaloniaView; |
||||
|
|
||||
|
namespace Avalonia.Web.Blazor; |
||||
|
|
||||
|
[SupportedOSPlatform("browser")] |
||||
|
public class AvaloniaView : ComponentBase |
||||
|
{ |
||||
|
private BrowserView? _browserView; |
||||
|
private readonly string _containerId; |
||||
|
|
||||
|
public AvaloniaView() |
||||
|
{ |
||||
|
_containerId = "av_container_" + Guid.NewGuid(); |
||||
|
} |
||||
|
|
||||
|
protected override void BuildRenderTree(RenderTreeBuilder builder) |
||||
|
{ |
||||
|
builder.OpenElement(0, "div"); |
||||
|
builder.AddAttribute(1, "id", _containerId); |
||||
|
builder.AddAttribute(2, "style", "width:100vw;height:100vh"); |
||||
|
builder.CloseElement(); |
||||
|
} |
||||
|
|
||||
|
protected override async Task OnInitializedAsync() |
||||
|
{ |
||||
|
if (OperatingSystem.IsBrowser()) |
||||
|
{ |
||||
|
await Avalonia.Web.Interop.AvaloniaModule.ImportMain(); |
||||
|
|
||||
|
_browserView = new BrowserView(_containerId); |
||||
|
if (Application.Current?.ApplicationLifetime is ISingleViewApplicationLifetime lifetime) |
||||
|
{ |
||||
|
_browserView.Content = lifetime.MainView; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -1,67 +0,0 @@ |
|||||
<div id="container" @ref="_containerElement" |
|
||||
class="avalonia-container" |
|
||||
tabindex="0" oncontextmenu="return false;" |
|
||||
@onwheel="OnWheel" |
|
||||
@onkeydown="OnKeyDown" |
|
||||
@onkeyup="OnKeyUp" |
|
||||
@onkeyup:preventDefault="@KeyPreventDefault" |
|
||||
@onkeydown:preventDefault="@KeyPreventDefault" |
|
||||
@onpointerdown="OnPointerDown" |
|
||||
@onpointerup="OnPointerUp" |
|
||||
@onpointermove="OnPointerMove" |
|
||||
@onpointercancel="OnPointerCancel" |
|
||||
@onfocus="OnFocus"> |
|
||||
|
|
||||
<canvas id="htmlCanvas" @ref="_htmlCanvas" @attributes="AdditionalAttributes"/> |
|
||||
|
|
||||
<div id="nativeControlsContainer" @ref="_nativeControlsContainer" /> |
|
||||
|
|
||||
<input id="inputElement" @ref="_inputElement" type="text" |
|
||||
spellcheck="false" onpaste="return false;" |
|
||||
oncopy="return false;" |
|
||||
oncut="return false;" |
|
||||
autocapitalize="none"/> |
|
||||
</div> |
|
||||
|
|
||||
<style> |
|
||||
#container{ |
|
||||
touch-action: none; |
|
||||
} |
|
||||
#htmlCanvas { |
|
||||
opacity: 1; |
|
||||
background-color: #ccc; |
|
||||
position: fixed; |
|
||||
width: 100vw; |
|
||||
height: 100vh; |
|
||||
top: 0px; |
|
||||
left: 0px; |
|
||||
z-index: 500; |
|
||||
} |
|
||||
|
|
||||
#nativeControlsContainer { |
|
||||
position: fixed; |
|
||||
width: 100vw; |
|
||||
height: 100vh; |
|
||||
top: 0px; |
|
||||
left: 0px; |
|
||||
z-index: 700; |
|
||||
} |
|
||||
|
|
||||
#inputElement { |
|
||||
padding: 0; |
|
||||
margin: 0; |
|
||||
position: absolute; |
|
||||
height: 20px; |
|
||||
z-index: 1000; |
|
||||
overflow: hidden; |
|
||||
caret-color: transparent; |
|
||||
border-top-style: hidden; |
|
||||
border-bottom-style: hidden; |
|
||||
border-right-style: hidden; |
|
||||
border-left-style: hidden; |
|
||||
outline: none; |
|
||||
background: transparent; |
|
||||
color: transparent; |
|
||||
} |
|
||||
|
|
||||
</style> |
|
||||
@ -1,500 +0,0 @@ |
|||||
using System.Diagnostics; |
|
||||
using Avalonia.Controls.ApplicationLifetimes; |
|
||||
using Avalonia.Controls.Embedding; |
|
||||
using Avalonia.Controls.Platform; |
|
||||
using Avalonia.Input; |
|
||||
using Avalonia.Input.Raw; |
|
||||
using Avalonia.Input.TextInput; |
|
||||
using Avalonia.Platform.Storage; |
|
||||
using Avalonia.Rendering; |
|
||||
using Avalonia.Rendering.Composition; |
|
||||
using Avalonia.Web.Blazor.Interop; |
|
||||
using Avalonia.Web.Blazor.Interop.Storage; |
|
||||
|
|
||||
using Microsoft.AspNetCore.Components; |
|
||||
using Microsoft.AspNetCore.Components.Forms; |
|
||||
using Microsoft.AspNetCore.Components.Web; |
|
||||
using Microsoft.JSInterop; |
|
||||
|
|
||||
using SkiaSharp; |
|
||||
using HTMLPointerEventArgs = Microsoft.AspNetCore.Components.Web.PointerEventArgs; |
|
||||
|
|
||||
namespace Avalonia.Web.Blazor |
|
||||
{ |
|
||||
public partial class AvaloniaView : ITextInputMethodImpl |
|
||||
{ |
|
||||
private readonly RazorViewTopLevelImpl _topLevelImpl; |
|
||||
private EmbeddableControlRoot _topLevel; |
|
||||
|
|
||||
// Interop
|
|
||||
private SKHtmlCanvasInterop? _interop = null; |
|
||||
private SizeWatcherInterop? _sizeWatcher = null; |
|
||||
private DpiWatcherInterop? _dpiWatcher = null; |
|
||||
private SKHtmlCanvasInterop.GLInfo? _jsGlInfo = null; |
|
||||
private AvaloniaModule? _avaloniaModule = null; |
|
||||
private InputHelperInterop? _inputHelper = null; |
|
||||
private FocusHelperInterop? _canvasHelper = null; |
|
||||
private FocusHelperInterop? _containerHelper = null; |
|
||||
private NativeControlHostInterop? _nativeControlHost = null; |
|
||||
private StorageProviderInterop? _storageProvider = null; |
|
||||
private ElementReference _htmlCanvas; |
|
||||
private ElementReference _inputElement; |
|
||||
private ElementReference _containerElement; |
|
||||
private ElementReference _nativeControlsContainer; |
|
||||
private double _dpi = 1; |
|
||||
private SKSize _canvasSize = new (100, 100); |
|
||||
|
|
||||
private GRContext? _context; |
|
||||
private GRGlInterface? _glInterface; |
|
||||
private const SKColorType ColorType = SKColorType.Rgba8888; |
|
||||
|
|
||||
private bool _useGL; |
|
||||
private bool _inputElementFocused; |
|
||||
|
|
||||
private ITextInputMethodClient? _client; |
|
||||
|
|
||||
|
|
||||
[Inject] private IJSRuntime Js { get; set; } = null!; |
|
||||
|
|
||||
public AvaloniaView() |
|
||||
{ |
|
||||
_topLevelImpl = new RazorViewTopLevelImpl(this); |
|
||||
|
|
||||
_topLevel = new EmbeddableControlRoot(_topLevelImpl); |
|
||||
|
|
||||
if (Application.Current?.ApplicationLifetime is ISingleViewApplicationLifetime lifetime) |
|
||||
{ |
|
||||
_topLevel.Content = lifetime.MainView; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
public bool KeyPreventDefault { get; set; } |
|
||||
|
|
||||
public ITextInputMethodClient? Client => _client; |
|
||||
|
|
||||
public bool IsActive => _client != null; |
|
||||
|
|
||||
public bool IsComposing { get; private set; } |
|
||||
|
|
||||
internal INativeControlHostImpl GetNativeControlHostImpl() |
|
||||
{ |
|
||||
return _nativeControlHost ?? throw new InvalidOperationException("Blazor View wasn't initialized yet"); |
|
||||
} |
|
||||
|
|
||||
internal IStorageProvider GetStorageProvider() |
|
||||
{ |
|
||||
return _storageProvider ?? throw new InvalidOperationException("Blazor View wasn't initialized yet"); |
|
||||
} |
|
||||
|
|
||||
private void OnPointerCancel(HTMLPointerEventArgs e) |
|
||||
{ |
|
||||
if (e.PointerType == "touch") |
|
||||
{ |
|
||||
_topLevelImpl.RawPointerEvent(RawPointerEventType.TouchCancel, e.PointerType, GetPointFromEventArgs(e), |
|
||||
GetModifiers(e), e.PointerId); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
private void OnPointerMove(HTMLPointerEventArgs e) |
|
||||
{ |
|
||||
var type = e.PointerType switch |
|
||||
{ |
|
||||
"touch" => RawPointerEventType.TouchUpdate, |
|
||||
_ => RawPointerEventType.Move |
|
||||
}; |
|
||||
|
|
||||
_topLevelImpl.RawPointerEvent(type, e.PointerType, GetPointFromEventArgs(e), GetModifiers(e), e.PointerId); |
|
||||
} |
|
||||
|
|
||||
private void OnPointerUp(HTMLPointerEventArgs e) |
|
||||
{ |
|
||||
var type = e.PointerType switch |
|
||||
{ |
|
||||
"touch" => RawPointerEventType.TouchEnd, |
|
||||
_ => e.Button switch |
|
||||
{ |
|
||||
0 => RawPointerEventType.LeftButtonUp, |
|
||||
1 => RawPointerEventType.MiddleButtonUp, |
|
||||
2 => RawPointerEventType.RightButtonUp, |
|
||||
3 => RawPointerEventType.XButton1Up, |
|
||||
4 => RawPointerEventType.XButton2Up, |
|
||||
// 5 => Pen eraser button,
|
|
||||
_ => RawPointerEventType.Move |
|
||||
} |
|
||||
}; |
|
||||
|
|
||||
_topLevelImpl.RawPointerEvent(type, e.PointerType, GetPointFromEventArgs(e), GetModifiers(e), e.PointerId); |
|
||||
} |
|
||||
|
|
||||
private void OnPointerDown(HTMLPointerEventArgs e) |
|
||||
{ |
|
||||
var type = e.PointerType switch |
|
||||
{ |
|
||||
"touch" => RawPointerEventType.TouchBegin, |
|
||||
_ => e.Button switch |
|
||||
{ |
|
||||
0 => RawPointerEventType.LeftButtonDown, |
|
||||
1 => RawPointerEventType.MiddleButtonDown, |
|
||||
2 => RawPointerEventType.RightButtonDown, |
|
||||
3 => RawPointerEventType.XButton1Down, |
|
||||
4 => RawPointerEventType.XButton2Down, |
|
||||
// 5 => Pen eraser button,
|
|
||||
_ => RawPointerEventType.Move |
|
||||
} |
|
||||
}; |
|
||||
|
|
||||
_topLevelImpl.RawPointerEvent(type, e.PointerType, GetPointFromEventArgs(e), GetModifiers(e), e.PointerId); |
|
||||
} |
|
||||
|
|
||||
private static RawPointerPoint GetPointFromEventArgs(HTMLPointerEventArgs args) |
|
||||
{ |
|
||||
return new RawPointerPoint |
|
||||
{ |
|
||||
Position = new Point(args.ClientX, args.ClientY), |
|
||||
Pressure = args.Pressure, |
|
||||
XTilt = args.TiltX, |
|
||||
YTilt = args.TiltY |
|
||||
// Twist = args.Twist - read from JS code directly when
|
|
||||
}; |
|
||||
} |
|
||||
|
|
||||
private void OnWheel(WheelEventArgs e) |
|
||||
{ |
|
||||
_topLevelImpl.RawMouseWheelEvent( new Point(e.ClientX, e.ClientY), |
|
||||
new Vector(-(e.DeltaX / 50), -(e.DeltaY / 50)), GetModifiers(e)); |
|
||||
} |
|
||||
|
|
||||
private static RawInputModifiers GetModifiers(MouseEventArgs e) |
|
||||
{ |
|
||||
var modifiers = RawInputModifiers.None; |
|
||||
|
|
||||
if (e.CtrlKey) |
|
||||
modifiers |= RawInputModifiers.Control; |
|
||||
if (e.AltKey) |
|
||||
modifiers |= RawInputModifiers.Alt; |
|
||||
if (e.ShiftKey) |
|
||||
modifiers |= RawInputModifiers.Shift; |
|
||||
if (e.MetaKey) |
|
||||
modifiers |= RawInputModifiers.Meta; |
|
||||
|
|
||||
if ((e.Buttons & 1L) == 1) |
|
||||
modifiers |= RawInputModifiers.LeftMouseButton; |
|
||||
|
|
||||
if ((e.Buttons & 2L) == 2) |
|
||||
modifiers |= e.Type == "pen" ? RawInputModifiers.PenBarrelButton : RawInputModifiers.RightMouseButton; |
|
||||
|
|
||||
if ((e.Buttons & 4L) == 4) |
|
||||
modifiers |= RawInputModifiers.MiddleMouseButton; |
|
||||
|
|
||||
if ((e.Buttons & 8L) == 8) |
|
||||
modifiers |= RawInputModifiers.XButton1MouseButton; |
|
||||
|
|
||||
if ((e.Buttons & 16L) == 16) |
|
||||
modifiers |= RawInputModifiers.XButton2MouseButton; |
|
||||
|
|
||||
if ((e.Buttons & 32L) == 32) |
|
||||
modifiers |= RawInputModifiers.PenEraser; |
|
||||
|
|
||||
return modifiers; |
|
||||
} |
|
||||
|
|
||||
private static RawInputModifiers GetModifiers(KeyboardEventArgs e) |
|
||||
{ |
|
||||
var modifiers = RawInputModifiers.None; |
|
||||
|
|
||||
if (e.CtrlKey) |
|
||||
modifiers |= RawInputModifiers.Control; |
|
||||
if (e.AltKey) |
|
||||
modifiers |= RawInputModifiers.Alt; |
|
||||
if (e.ShiftKey) |
|
||||
modifiers |= RawInputModifiers.Shift; |
|
||||
if (e.MetaKey) |
|
||||
modifiers |= RawInputModifiers.Meta; |
|
||||
|
|
||||
return modifiers; |
|
||||
} |
|
||||
|
|
||||
private void OnKeyDown(KeyboardEventArgs e) |
|
||||
{ |
|
||||
KeyPreventDefault = _topLevelImpl.RawKeyboardEvent(RawKeyEventType.KeyDown, e.Code, e.Key, GetModifiers(e)); |
|
||||
} |
|
||||
|
|
||||
private void OnKeyUp(KeyboardEventArgs e) |
|
||||
{ |
|
||||
KeyPreventDefault = _topLevelImpl.RawKeyboardEvent(RawKeyEventType.KeyUp, e.Code, e.Key, GetModifiers(e)); |
|
||||
} |
|
||||
|
|
||||
private void OnFocus(FocusEventArgs e) |
|
||||
{ |
|
||||
// if focus has unexpectedly moved from the input element to the container element,
|
|
||||
// shift it back to the input element
|
|
||||
if (_inputElementFocused && _inputHelper is not null) |
|
||||
{ |
|
||||
_inputHelper.Focus(); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
[Parameter(CaptureUnmatchedValues = true)] |
|
||||
public IReadOnlyDictionary<string, object>? AdditionalAttributes { get; set; } |
|
||||
|
|
||||
protected override async Task OnAfterRenderAsync(bool firstRender) |
|
||||
{ |
|
||||
if (firstRender) |
|
||||
{ |
|
||||
AvaloniaLocator.CurrentMutable.Bind<IJSInProcessRuntime>().ToConstant((IJSInProcessRuntime)Js); |
|
||||
|
|
||||
_avaloniaModule = await AvaloniaModule.ImportAsync(Js); |
|
||||
|
|
||||
_canvasHelper = new FocusHelperInterop(_avaloniaModule, _htmlCanvas); |
|
||||
_containerHelper = new FocusHelperInterop(_avaloniaModule, _containerElement); |
|
||||
_inputHelper = new InputHelperInterop(_avaloniaModule, _inputElement); |
|
||||
|
|
||||
_inputHelper.CompositionEvent += InputHelperOnCompositionEvent; |
|
||||
_inputHelper.InputEvent += InputHelperOnInputEvent; |
|
||||
|
|
||||
HideIme(); |
|
||||
_canvasHelper.SetCursor("default"); |
|
||||
_topLevelImpl.SetCssCursor = x => |
|
||||
{ |
|
||||
_inputHelper.SetCursor(x); //macOS
|
|
||||
_canvasHelper.SetCursor(x); //windows
|
|
||||
}; |
|
||||
|
|
||||
_nativeControlHost = new NativeControlHostInterop(_avaloniaModule, _nativeControlsContainer); |
|
||||
_storageProvider = await StorageProviderInterop.ImportAsync(Js); |
|
||||
|
|
||||
Console.WriteLine("starting html canvas setup"); |
|
||||
_interop = new SKHtmlCanvasInterop(_avaloniaModule, _htmlCanvas, OnRenderFrame); |
|
||||
|
|
||||
Console.WriteLine("Interop created"); |
|
||||
|
|
||||
var skiaOptions = AvaloniaLocator.Current.GetService<SkiaOptions>(); |
|
||||
_useGL = skiaOptions?.CustomGpuFactory != null; |
|
||||
|
|
||||
if (_useGL) |
|
||||
{ |
|
||||
_jsGlInfo = _interop.InitGL(); |
|
||||
Console.WriteLine("jsglinfo created - init gl"); |
|
||||
} |
|
||||
else |
|
||||
{ |
|
||||
var rasterInitialized = _interop.InitRaster(); |
|
||||
Console.WriteLine("raster initialized: {0}", rasterInitialized); |
|
||||
} |
|
||||
|
|
||||
if (_useGL) |
|
||||
{ |
|
||||
// create the SkiaSharp context
|
|
||||
if (_context == null) |
|
||||
{ |
|
||||
_glInterface = GRGlInterface.Create(); |
|
||||
_context = GRContext.CreateGl(_glInterface); |
|
||||
|
|
||||
|
|
||||
// bump the default resource cache limit
|
|
||||
_context.SetResourceCacheLimit(skiaOptions?.MaxGpuResourceSizeBytes ?? 32 * 1024 * 1024); |
|
||||
Console.WriteLine("glcontext created and resource limit set"); |
|
||||
} |
|
||||
|
|
||||
_topLevelImpl.SetSurface(_context, _jsGlInfo!, ColorType, |
|
||||
new PixelSize((int)_canvasSize.Width, (int)_canvasSize.Height), _dpi); |
|
||||
} |
|
||||
else |
|
||||
{ |
|
||||
_topLevelImpl.SetSurface(ColorType, |
|
||||
new PixelSize((int)_canvasSize.Width, (int)_canvasSize.Height), _dpi, _interop.PutImageData); |
|
||||
} |
|
||||
|
|
||||
_interop.SetCanvasSize((int)(_canvasSize.Width * _dpi), (int)(_canvasSize.Height * _dpi)); |
|
||||
|
|
||||
Threading.Dispatcher.UIThread.Post(async () => |
|
||||
{ |
|
||||
_interop.RequestAnimationFrame(true); |
|
||||
|
|
||||
_sizeWatcher = new SizeWatcherInterop(_avaloniaModule, _htmlCanvas, OnSizeChanged); |
|
||||
_dpiWatcher = new DpiWatcherInterop(_avaloniaModule, OnDpiChanged); |
|
||||
|
|
||||
_sizeWatcher.Start(); |
|
||||
_topLevel.Prepare(); |
|
||||
|
|
||||
_topLevel.Renderer.Start(); |
|
||||
}); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
private void InputHelperOnInputEvent(object? sender, WebInputEventArgs e) |
|
||||
{ |
|
||||
if (IsComposing) |
|
||||
{ |
|
||||
return; |
|
||||
} |
|
||||
|
|
||||
_topLevelImpl.RawTextEvent(e.Data); |
|
||||
|
|
||||
e.Handled = true; |
|
||||
} |
|
||||
|
|
||||
private void InputHelperOnCompositionEvent(object? sender, WebCompositionEventArgs e) |
|
||||
{ |
|
||||
if(_client == null) |
|
||||
{ |
|
||||
return; |
|
||||
} |
|
||||
|
|
||||
switch (e.Type) |
|
||||
{ |
|
||||
case WebCompositionEventArgs.WebCompositionEventType.Start: |
|
||||
_client.SetPreeditText(null); |
|
||||
IsComposing = true; |
|
||||
break; |
|
||||
case WebCompositionEventArgs.WebCompositionEventType.Update: |
|
||||
_client.SetPreeditText(e.Data); |
|
||||
break; |
|
||||
case WebCompositionEventArgs.WebCompositionEventType.End: |
|
||||
IsComposing = false; |
|
||||
_client.SetPreeditText(null); |
|
||||
_topLevelImpl.RawTextEvent(e.Data); |
|
||||
break; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
private void OnRenderFrame() |
|
||||
{ |
|
||||
if (_useGL && (_jsGlInfo == null)) |
|
||||
{ |
|
||||
Console.WriteLine("nothing to render"); |
|
||||
return; |
|
||||
} |
|
||||
if (_canvasSize.Width <= 0 || _canvasSize.Height <= 0 || _dpi <= 0) |
|
||||
{ |
|
||||
Console.WriteLine("nothing to render"); |
|
||||
return; |
|
||||
} |
|
||||
|
|
||||
ManualTriggerRenderTimer.Instance.RaiseTick(); |
|
||||
} |
|
||||
|
|
||||
public void Dispose() |
|
||||
{ |
|
||||
_dpiWatcher?.Unsubscribe(OnDpiChanged); |
|
||||
_sizeWatcher?.Dispose(); |
|
||||
_interop?.Dispose(); |
|
||||
} |
|
||||
|
|
||||
private void ForceBlit() |
|
||||
{ |
|
||||
// Note: this is technically a hack, but it's a kinda unique use case when
|
|
||||
// we want to blit the previous frame
|
|
||||
// renderer doesn't have much control over the render target
|
|
||||
// we render on the UI thread
|
|
||||
// We also don't want to have it as a meaningful public API.
|
|
||||
// Therefore we have InternalsVisibleTo hack here.
|
|
||||
|
|
||||
if (_topLevel.Renderer is CompositingRenderer dr) |
|
||||
{ |
|
||||
dr.CompositionTarget.ImmediateUIThreadRender(); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
private void OnDpiChanged(double newDpi) |
|
||||
{ |
|
||||
if (Math.Abs(_dpi - newDpi) > 0.0001) |
|
||||
{ |
|
||||
_dpi = newDpi; |
|
||||
|
|
||||
_interop!.SetCanvasSize((int)(_canvasSize.Width * _dpi), (int)(_canvasSize.Height * _dpi)); |
|
||||
|
|
||||
_topLevelImpl.SetClientSize(_canvasSize, _dpi); |
|
||||
|
|
||||
ForceBlit(); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
private void OnSizeChanged(SKSize newSize) |
|
||||
{ |
|
||||
if (_canvasSize != newSize) |
|
||||
{ |
|
||||
_canvasSize = newSize; |
|
||||
|
|
||||
_interop!.SetCanvasSize((int)(_canvasSize.Width * _dpi), (int)(_canvasSize.Height * _dpi)); |
|
||||
|
|
||||
_topLevelImpl.SetClientSize(_canvasSize, _dpi); |
|
||||
|
|
||||
ForceBlit(); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
private void HideIme() |
|
||||
{ |
|
||||
_inputHelper?.Hide(); |
|
||||
_containerHelper?.Focus(); |
|
||||
} |
|
||||
|
|
||||
public void SetClient(ITextInputMethodClient? client) |
|
||||
{ |
|
||||
if (_inputHelper is null) |
|
||||
{ |
|
||||
return; |
|
||||
} |
|
||||
|
|
||||
if(_client != null) |
|
||||
{ |
|
||||
_client.SurroundingTextChanged -= SurroundingTextChanged; |
|
||||
} |
|
||||
|
|
||||
if(client != null) |
|
||||
{ |
|
||||
client.SurroundingTextChanged += SurroundingTextChanged; |
|
||||
} |
|
||||
|
|
||||
_inputHelper.Clear(); |
|
||||
|
|
||||
_client = client; |
|
||||
|
|
||||
if (IsActive && _client != null) |
|
||||
{ |
|
||||
_inputHelper.Show(); |
|
||||
_inputElementFocused = true; |
|
||||
_inputHelper.Focus(); |
|
||||
|
|
||||
var surroundingText = _client.SurroundingText; |
|
||||
|
|
||||
_inputHelper?.SetSurroundingText(surroundingText.Text, surroundingText.AnchorOffset, surroundingText.CursorOffset); |
|
||||
} |
|
||||
else |
|
||||
{ |
|
||||
_inputElementFocused = false; |
|
||||
HideIme(); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
private void SurroundingTextChanged(object? sender, EventArgs e) |
|
||||
{ |
|
||||
if(_client != null) |
|
||||
{ |
|
||||
var surroundingText = _client.SurroundingText; |
|
||||
|
|
||||
_inputHelper?.SetSurroundingText(surroundingText.Text, surroundingText.AnchorOffset, surroundingText.CursorOffset); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
public void SetCursorRect(Rect rect) |
|
||||
{ |
|
||||
_inputHelper?.Focus(); |
|
||||
var bounds = new PixelRect((int)rect.X, (int) rect.Y, (int) rect.Width, (int) rect.Height); |
|
||||
|
|
||||
_inputHelper?.SetBounds(bounds, _client?.SurroundingText.CursorOffset ?? 0); |
|
||||
_inputHelper?.Focus(); |
|
||||
} |
|
||||
|
|
||||
public void SetOptions(TextInputOptions options) |
|
||||
{ |
|
||||
} |
|
||||
|
|
||||
public void Reset() |
|
||||
{ |
|
||||
_inputHelper?.Clear(); |
|
||||
_inputHelper?.SetSurroundingText("", 0, 0); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,31 +1,39 @@ |
|||||
using Avalonia.Controls; |
using System.Runtime.Versioning; |
||||
|
|
||||
|
using Avalonia.Controls; |
||||
using Avalonia.Controls.ApplicationLifetimes; |
using Avalonia.Controls.ApplicationLifetimes; |
||||
using Avalonia.Media; |
|
||||
|
|
||||
namespace Avalonia.Web.Blazor |
namespace Avalonia.Web.Blazor; |
||||
|
|
||||
|
[SupportedOSPlatform("browser")] |
||||
|
public static class WebAppBuilder |
||||
{ |
{ |
||||
public class BlazorSingleViewLifetime : ISingleViewApplicationLifetime |
public static T SetupWithSingleViewLifetime<T>( |
||||
|
this T builder) |
||||
|
where T : AppBuilderBase<T>, new() |
||||
{ |
{ |
||||
public Control? MainView { get; set; } |
return builder.SetupWithLifetime(new BlazorSingleViewLifetime()); |
||||
} |
} |
||||
|
|
||||
public static class WebAppBuilder |
public static T UseBlazor<T>(this T builder) where T : AppBuilderBase<T>, new() |
||||
{ |
{ |
||||
public static T SetupWithSingleViewLifetime<T>( |
return builder |
||||
this T builder) |
.UseBrowser() |
||||
where T : AppBuilderBase<T>, new() |
.With(new BrowserPlatformOptions |
||||
{ |
{ |
||||
return builder.SetupWithLifetime(new BlazorSingleViewLifetime()); |
FrameworkAssetPathResolver = new(filePath => $"/_content/Avalonia.Web.Blazor/{filePath}") |
||||
} |
}); |
||||
|
} |
||||
|
|
||||
public static AvaloniaBlazorAppBuilder Configure<TApp>() |
public static AppBuilder Configure<TApp>() |
||||
where TApp : Application, new() |
where TApp : Application, new() |
||||
{ |
{ |
||||
var builder = AvaloniaBlazorAppBuilder.Configure<TApp>() |
return AppBuilder.Configure<TApp>() |
||||
.UseSkia() |
.UseBlazor(); |
||||
.With(new SkiaOptions { CustomGpuFactory = () => new BlazorSkiaGpu() }); |
} |
||||
|
|
||||
return builder; |
internal class BlazorSingleViewLifetime : ISingleViewApplicationLifetime |
||||
} |
{ |
||||
|
public Control? MainView { get; set; } |
||||
} |
} |
||||
} |
} |
||||
|
|||||
@ -1,25 +0,0 @@ |
|||||
using Avalonia.Skia; |
|
||||
|
|
||||
namespace Avalonia.Web.Blazor |
|
||||
{ |
|
||||
public class BlazorSkiaGpu : ISkiaGpu |
|
||||
{ |
|
||||
public ISkiaGpuRenderTarget? TryCreateRenderTarget(IEnumerable<object> surfaces) |
|
||||
{ |
|
||||
foreach (var surface in surfaces) |
|
||||
{ |
|
||||
if (surface is BlazorSkiaSurface blazorSkiaSurface) |
|
||||
{ |
|
||||
return new BlazorSkiaGpuRenderTarget(blazorSkiaSurface); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
return null; |
|
||||
} |
|
||||
|
|
||||
public ISkiaSurface? TryCreateSurface(PixelSize size, ISkiaGpuRenderSession session) |
|
||||
{ |
|
||||
return null; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,37 +0,0 @@ |
|||||
using Avalonia.Skia; |
|
||||
using SkiaSharp; |
|
||||
|
|
||||
namespace Avalonia.Web.Blazor |
|
||||
{ |
|
||||
internal class BlazorSkiaGpuRenderSession : ISkiaGpuRenderSession |
|
||||
{ |
|
||||
private readonly SKSurface _surface; |
|
||||
|
|
||||
|
|
||||
public BlazorSkiaGpuRenderSession(BlazorSkiaSurface blazorSkiaSurface, GRBackendRenderTarget renderTarget) |
|
||||
{ |
|
||||
_surface = SKSurface.Create(blazorSkiaSurface.Context, renderTarget, blazorSkiaSurface.Origin, blazorSkiaSurface.ColorType); |
|
||||
|
|
||||
GrContext = blazorSkiaSurface.Context; |
|
||||
|
|
||||
ScaleFactor = blazorSkiaSurface.Scaling; |
|
||||
|
|
||||
SurfaceOrigin = blazorSkiaSurface.Origin; |
|
||||
} |
|
||||
|
|
||||
public void Dispose() |
|
||||
{ |
|
||||
_surface.Flush(); |
|
||||
|
|
||||
_surface.Dispose(); |
|
||||
} |
|
||||
|
|
||||
public GRContext GrContext { get; } |
|
||||
|
|
||||
public SKSurface SkSurface => _surface; |
|
||||
|
|
||||
public double ScaleFactor { get; } |
|
||||
|
|
||||
public GRSurfaceOrigin SurfaceOrigin { get; } |
|
||||
} |
|
||||
} |
|
||||
@ -1,39 +0,0 @@ |
|||||
using Avalonia.Skia; |
|
||||
using SkiaSharp; |
|
||||
|
|
||||
namespace Avalonia.Web.Blazor |
|
||||
{ |
|
||||
internal class BlazorSkiaGpuRenderTarget : ISkiaGpuRenderTarget |
|
||||
{ |
|
||||
private readonly GRBackendRenderTarget _renderTarget; |
|
||||
private readonly BlazorSkiaSurface _blazorSkiaSurface; |
|
||||
private readonly PixelSize _size; |
|
||||
|
|
||||
public BlazorSkiaGpuRenderTarget(BlazorSkiaSurface blazorSkiaSurface) |
|
||||
{ |
|
||||
_size = blazorSkiaSurface.Size; |
|
||||
|
|
||||
var glFbInfo = new GRGlFramebufferInfo(blazorSkiaSurface.GlInfo.FboId, blazorSkiaSurface.ColorType.ToGlSizedFormat()); |
|
||||
{ |
|
||||
_blazorSkiaSurface = blazorSkiaSurface; |
|
||||
_renderTarget = new GRBackendRenderTarget( |
|
||||
(int)(blazorSkiaSurface.Size.Width * blazorSkiaSurface.Scaling), |
|
||||
(int)(blazorSkiaSurface.Size.Height * blazorSkiaSurface.Scaling), |
|
||||
blazorSkiaSurface.GlInfo.Samples, |
|
||||
blazorSkiaSurface.GlInfo.Stencils, glFbInfo); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
public void Dispose() |
|
||||
{ |
|
||||
_renderTarget.Dispose(); |
|
||||
} |
|
||||
|
|
||||
public ISkiaGpuRenderSession BeginRenderingSession() |
|
||||
{ |
|
||||
return new BlazorSkiaGpuRenderSession(_blazorSkiaSurface, _renderTarget); |
|
||||
} |
|
||||
|
|
||||
public bool IsCorrupted => _blazorSkiaSurface.Size != _size; |
|
||||
} |
|
||||
} |
|
||||
@ -1,87 +0,0 @@ |
|||||
using System.Runtime.InteropServices; |
|
||||
using Avalonia.Controls.Platform.Surfaces; |
|
||||
using Avalonia.Platform; |
|
||||
using Avalonia.Skia; |
|
||||
using SkiaSharp; |
|
||||
|
|
||||
namespace Avalonia.Web.Blazor |
|
||||
{ |
|
||||
internal class BlazorSkiaRasterSurface : IBlazorSkiaSurface, IFramebufferPlatformSurface, IDisposable |
|
||||
{ |
|
||||
public SKColorType ColorType { get; set; } |
|
||||
|
|
||||
public PixelSize Size { get; set; } |
|
||||
|
|
||||
public double Scaling { get; set; } |
|
||||
|
|
||||
private FramebufferData? _fbData; |
|
||||
private readonly Action<IntPtr, SKSizeI> _blitCallback; |
|
||||
private readonly Action _onDisposeAction; |
|
||||
|
|
||||
public BlazorSkiaRasterSurface( |
|
||||
SKColorType colorType, PixelSize size, double scaling, Action<IntPtr, SKSizeI> blitCallback) |
|
||||
{ |
|
||||
ColorType = colorType; |
|
||||
Size = size; |
|
||||
Scaling = scaling; |
|
||||
_blitCallback = blitCallback; |
|
||||
_onDisposeAction = Blit; |
|
||||
} |
|
||||
|
|
||||
public void Dispose() |
|
||||
{ |
|
||||
_fbData?.Dispose(); |
|
||||
_fbData = null; |
|
||||
} |
|
||||
|
|
||||
public ILockedFramebuffer Lock() |
|
||||
{ |
|
||||
var bytesPerPixel = 4; // TODO: derive from ColorType
|
|
||||
var dpi = Scaling * 96.0; |
|
||||
var width = (int)(Size.Width * Scaling); |
|
||||
var height = (int)(Size.Height * Scaling); |
|
||||
|
|
||||
if (_fbData is null || _fbData?.Size.Width != width || _fbData?.Size.Height != height) |
|
||||
{ |
|
||||
_fbData?.Dispose(); |
|
||||
_fbData = new FramebufferData(width, height, bytesPerPixel); |
|
||||
} |
|
||||
|
|
||||
var pixelFormat = ColorType.ToPixelFormat(); |
|
||||
var data = _fbData.Value; |
|
||||
return new LockedFramebuffer( |
|
||||
data.Address, data.Size, data.RowBytes, |
|
||||
new Vector(dpi, dpi), pixelFormat, _onDisposeAction); |
|
||||
} |
|
||||
|
|
||||
private void Blit() |
|
||||
{ |
|
||||
if (_fbData != null) |
|
||||
{ |
|
||||
var data = _fbData.Value; |
|
||||
_blitCallback(data.Address, new SKSizeI(data.Size.Width, data.Size.Height)); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
private readonly struct FramebufferData |
|
||||
{ |
|
||||
public PixelSize Size { get; } |
|
||||
|
|
||||
public int RowBytes { get; } |
|
||||
|
|
||||
public IntPtr Address { get; } |
|
||||
|
|
||||
public FramebufferData(int width, int height, int bytesPerPixel) |
|
||||
{ |
|
||||
Size = new PixelSize(width, height); |
|
||||
RowBytes = width * bytesPerPixel; |
|
||||
Address = Marshal.AllocHGlobal(width * height * bytesPerPixel); |
|
||||
} |
|
||||
|
|
||||
public void Dispose() |
|
||||
{ |
|
||||
Marshal.FreeHGlobal(Address); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
Some files were not shown because too many files changed in this diff
Loading…
Reference in new issue