committed by
GitHub
119 changed files with 1450 additions and 815 deletions
@ -0,0 +1,27 @@ |
|||||
|
namespace Avalonia.Layout; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Provides access to layout information of a control.
|
||||
|
/// </summary>
|
||||
|
public static class LayoutInformation |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Gets the available size constraint passed in the previous layout pass.
|
||||
|
/// </summary>
|
||||
|
/// <param name="control">The control.</param>
|
||||
|
/// <returns>Previous control measure constraint, if any.</returns>
|
||||
|
public static Size? GetPreviousMeasureConstraint(Layoutable control) |
||||
|
{ |
||||
|
return control.PreviousMeasure; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the control bounds used in the previous layout arrange pass.
|
||||
|
/// </summary>
|
||||
|
/// <param name="control">The control.</param>
|
||||
|
/// <returns>Previous control arrange bounds, if any.</returns>
|
||||
|
public static Rect? GetPreviousArrangeBounds(Layoutable control) |
||||
|
{ |
||||
|
return control.PreviousArrange; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,22 @@ |
|||||
|
using Avalonia.Automation.Peers; |
||||
|
|
||||
|
namespace Avalonia.Controls.Automation.Peers |
||||
|
{ |
||||
|
public class SliderAutomationPeer : RangeBaseAutomationPeer |
||||
|
{ |
||||
|
public SliderAutomationPeer(Slider owner) : base(owner) |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
override protected string GetClassNameCore() |
||||
|
{ |
||||
|
return "Slider"; |
||||
|
} |
||||
|
|
||||
|
override protected AutomationControlType GetAutomationControlTypeCore() |
||||
|
{ |
||||
|
return AutomationControlType.Slider; |
||||
|
} |
||||
|
|
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,83 @@ |
|||||
|
using System; |
||||
|
using System.Threading.Tasks; |
||||
|
using Avalonia.Browser.Interop; |
||||
|
|
||||
|
namespace Avalonia.Browser; |
||||
|
|
||||
|
public class BrowserPlatformOptions |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Defines paths where avalonia modules and service locator should be resolved.
|
||||
|
/// If null, default path resolved depending on the backend (browser or blazor) is used.
|
||||
|
/// </summary>
|
||||
|
public Func<string, string>? FrameworkAssetPathResolver { get; set; } |
||||
|
} |
||||
|
|
||||
|
public static class BrowserAppBuilder |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Configures browser backend, loads avalonia javascript modules and creates a single view lifetime from the passed <see cref="mainDivId"/> parameter.
|
||||
|
/// </summary>
|
||||
|
/// <param name="builder">Application builder.</param>
|
||||
|
/// <param name="mainDivId">ID of the html element where avalonia content should be rendered.</param>
|
||||
|
/// <param name="options">Browser backend specific options.</param>
|
||||
|
public static async Task StartBrowserAppAsync(this AppBuilder builder, string mainDivId, BrowserPlatformOptions? options = null) |
||||
|
{ |
||||
|
if (mainDivId is null) |
||||
|
{ |
||||
|
throw new ArgumentNullException(nameof(mainDivId)); |
||||
|
} |
||||
|
|
||||
|
builder = await PreSetupBrowser(builder, options); |
||||
|
|
||||
|
var lifetime = new BrowserSingleViewLifetime(); |
||||
|
builder |
||||
|
.AfterSetup(_ => |
||||
|
{ |
||||
|
lifetime.View = new AvaloniaView(mainDivId); |
||||
|
}) |
||||
|
.SetupWithLifetime(lifetime); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Loads avalonia javascript modules and configures browser backend.
|
||||
|
/// </summary>
|
||||
|
/// <param name="builder">Application builder.</param>
|
||||
|
/// <param name="options">Browser backend specific options.</param>
|
||||
|
/// <remarks>
|
||||
|
/// This method doesn't creates any avalonia views to be rendered. To do so create an <see cref="AvaloniaView"/> object.
|
||||
|
/// Alternatively, you can call <see cref="StartBrowserAppAsync"/> method instead of <see cref="SetupBrowserAppAsync"/>.
|
||||
|
/// </remarks>
|
||||
|
public static async Task SetupBrowserAppAsync(this AppBuilder builder, BrowserPlatformOptions? options = null) |
||||
|
{ |
||||
|
builder = await PreSetupBrowser(builder, options); |
||||
|
|
||||
|
builder |
||||
|
.SetupWithoutStarting(); |
||||
|
} |
||||
|
|
||||
|
internal static async Task<AppBuilder> PreSetupBrowser(AppBuilder builder, BrowserPlatformOptions? options) |
||||
|
{ |
||||
|
options ??= new BrowserPlatformOptions(); |
||||
|
options.FrameworkAssetPathResolver ??= fileName => $"./{fileName}"; |
||||
|
|
||||
|
AvaloniaLocator.CurrentMutable.Bind<BrowserPlatformOptions>().ToConstant(options); |
||||
|
|
||||
|
await AvaloniaModule.ImportMain(); |
||||
|
|
||||
|
if (builder.WindowingSubsystemInitializer is null) |
||||
|
{ |
||||
|
builder = builder.UseBrowser(); |
||||
|
} |
||||
|
|
||||
|
return builder; |
||||
|
} |
||||
|
|
||||
|
public static AppBuilder UseBrowser( |
||||
|
this AppBuilder builder) |
||||
|
{ |
||||
|
return builder |
||||
|
.UseWindowingSubsystem(BrowserWindowingPlatform.Register) |
||||
|
.UseSkia(); |
||||
|
} |
||||
|
} |
||||
@ -1,47 +1,36 @@ |
|||||
using System; |
using System; |
||||
|
using System.Diagnostics.CodeAnalysis; |
||||
using Avalonia.Controls; |
using Avalonia.Controls; |
||||
using Avalonia.Controls.ApplicationLifetimes; |
using Avalonia.Controls.ApplicationLifetimes; |
||||
using System.Runtime.Versioning; |
using System.Runtime.Versioning; |
||||
|
using Avalonia.Browser; |
||||
|
|
||||
namespace Avalonia.Browser; |
namespace Avalonia; |
||||
|
|
||||
public class BrowserSingleViewLifetime : ISingleViewApplicationLifetime |
internal class BrowserSingleViewLifetime : ISingleViewApplicationLifetime |
||||
{ |
{ |
||||
public AvaloniaView? View; |
public AvaloniaView? View; |
||||
|
|
||||
public Control? MainView |
public Control? MainView |
||||
{ |
{ |
||||
get => View!.Content; |
get |
||||
set => View!.Content = value; |
{ |
||||
} |
EnsureView(); |
||||
} |
return View.Content; |
||||
|
} |
||||
public class BrowserPlatformOptions |
set |
||||
{ |
{ |
||||
public Func<string, string> FrameworkAssetPathResolver { get; set; } = new(fileName => $"./{fileName}"); |
EnsureView(); |
||||
} |
View.Content = value; |
||||
|
} |
||||
public static class WebAppBuilder |
|
||||
{ |
|
||||
public static AppBuilder SetupBrowserApp( |
|
||||
this AppBuilder builder, string mainDivId) |
|
||||
{ |
|
||||
var lifetime = new BrowserSingleViewLifetime(); |
|
||||
|
|
||||
return builder |
|
||||
.UseBrowser() |
|
||||
.AfterSetup(b => |
|
||||
{ |
|
||||
lifetime.View = new AvaloniaView(mainDivId); |
|
||||
}) |
|
||||
.SetupWithLifetime(lifetime); |
|
||||
} |
} |
||||
|
|
||||
public static AppBuilder UseBrowser( |
[MemberNotNull(nameof(View))] |
||||
this AppBuilder builder) |
private void EnsureView() |
||||
{ |
{ |
||||
return builder |
if (View is null) |
||||
.UseWindowingSubsystem(BrowserWindowingPlatform.Register) |
{ |
||||
.UseSkia(); |
throw new InvalidOperationException("Browser lifetime was not initialized. Make sure AppBuilder.StartBrowserApp was called."); |
||||
|
} |
||||
} |
} |
||||
} |
} |
||||
|
|||||
@ -0,0 +1,72 @@ |
|||||
|
using System; |
||||
|
using System.Collections.Concurrent; |
||||
|
using SkiaSharp; |
||||
|
|
||||
|
namespace Avalonia.Skia |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Cache base for Skia objects.
|
||||
|
/// </summary>
|
||||
|
internal abstract class SKCacheBase<TCachedItem, TCache> |
||||
|
where TCachedItem : IDisposable, new() |
||||
|
where TCache : new() |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Bag to hold the cached items.
|
||||
|
/// </summary>
|
||||
|
protected readonly ConcurrentBag<TCachedItem> Cache; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Shared cache.
|
||||
|
/// </summary>
|
||||
|
public static readonly TCache Shared = new TCache(); |
||||
|
|
||||
|
protected SKCacheBase() |
||||
|
{ |
||||
|
Cache = new ConcurrentBag<TCachedItem>(); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets a cached item for usage.
|
||||
|
/// </summary>
|
||||
|
/// <remarks>
|
||||
|
/// If there is a available item in the cache, the cached item will be returned..
|
||||
|
/// Otherwise a new cached item will be created.
|
||||
|
/// </remarks>
|
||||
|
/// <returns></returns>
|
||||
|
public TCachedItem Get() |
||||
|
{ |
||||
|
if (!Cache.TryTake(out var item)) |
||||
|
{ |
||||
|
item = new TCachedItem(); |
||||
|
} |
||||
|
|
||||
|
return item; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Returns the item for reuse later.
|
||||
|
/// </summary>
|
||||
|
/// <remarks>
|
||||
|
/// Do not use the item further.
|
||||
|
/// Do not return the same item multiple times as that will break the cache.
|
||||
|
/// </remarks>
|
||||
|
/// <param name="item"></param>
|
||||
|
public void Return(TCachedItem item) |
||||
|
{ |
||||
|
Cache.Add(item); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Clears and disposes all cached items.
|
||||
|
/// </summary>
|
||||
|
public void Clear() |
||||
|
{ |
||||
|
while (Cache.TryTake(out var item)) |
||||
|
{ |
||||
|
item.Dispose(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,13 @@ |
|||||
|
using System.Collections.Concurrent; |
||||
|
using SkiaSharp; |
||||
|
|
||||
|
namespace Avalonia.Skia |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Cache for SKFonts.
|
||||
|
/// </summary>
|
||||
|
internal class SKFontCache : SKCacheBase<SKFont, SKFontCache> |
||||
|
{ |
||||
|
|
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,99 @@ |
|||||
|
using System.Buffers; |
||||
|
using System.Collections.Concurrent; |
||||
|
using System.Collections.Generic; |
||||
|
using SkiaSharp; |
||||
|
|
||||
|
namespace Avalonia.Skia |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Cache for SKRoundRectCache.
|
||||
|
/// </summary>
|
||||
|
internal class SKRoundRectCache : SKCacheBase<SKRoundRect, SKRoundRectCache> |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Cache for points to use for setting the radii.
|
||||
|
/// </summary>
|
||||
|
private readonly ConcurrentBag<SKPoint[]> _radiiCache = new(); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets a cached SKRoundRect and sets it with the passed rectangle and Radii.
|
||||
|
/// </summary>
|
||||
|
/// <param name="rectangle">Rectangle size to set the cached rectangle to.</param>
|
||||
|
/// <param name="roundedRect">Rounded rectangle to copy the radii from.</param>
|
||||
|
/// <returns>Configured rounded rectangle</returns>
|
||||
|
public SKRoundRect GetAndSetRadii(in SKRect rectangle, in RoundedRect roundedRect) |
||||
|
{ |
||||
|
if (!Cache.TryTake(out var item)) |
||||
|
{ |
||||
|
item = new SKRoundRect(); |
||||
|
} |
||||
|
|
||||
|
// Try and acquire a cached point array.
|
||||
|
if (!_radiiCache.TryTake(out var skArray)) |
||||
|
{ |
||||
|
skArray = new SKPoint[4]; |
||||
|
} |
||||
|
|
||||
|
skArray[0].X = (float)roundedRect.RadiiTopLeft.X; |
||||
|
skArray[0].Y = (float)roundedRect.RadiiTopLeft.Y; |
||||
|
skArray[1].X = (float)roundedRect.RadiiTopRight.X; |
||||
|
skArray[1].Y = (float)roundedRect.RadiiTopRight.Y; |
||||
|
skArray[2].X = (float)roundedRect.RadiiBottomRight.X; |
||||
|
skArray[2].Y = (float)roundedRect.RadiiBottomRight.Y; |
||||
|
skArray[3].X = (float)roundedRect.RadiiBottomLeft.X; |
||||
|
skArray[3].Y = (float)roundedRect.RadiiBottomLeft.Y; |
||||
|
|
||||
|
item.SetRectRadii(rectangle, skArray); |
||||
|
|
||||
|
// Add the array back to the cache.
|
||||
|
_radiiCache.Add(skArray); |
||||
|
|
||||
|
return item; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets a cached SKRoundRect and sets it with the passed rectangle and Radii.
|
||||
|
/// </summary>
|
||||
|
/// <param name="rectangle">Rectangle size to set the cached rectangle to.</param>
|
||||
|
/// <param name="radii">point array of radii.</param>
|
||||
|
/// <returns>Configured rounded rectangle</returns>
|
||||
|
public SKRoundRect GetAndSetRadii(in SKRect rectangle, in SKPoint[] radii) |
||||
|
{ |
||||
|
if (!Cache.TryTake(out var item)) |
||||
|
{ |
||||
|
item = new SKRoundRect(); |
||||
|
} |
||||
|
|
||||
|
item.SetRectRadii(rectangle, radii); |
||||
|
|
||||
|
return item; |
||||
|
} |
||||
|
/// <summary>
|
||||
|
/// Returns a SKPaint and resets it for reuse later.
|
||||
|
/// </summary>
|
||||
|
/// <remarks>
|
||||
|
/// Do not use the rect further.
|
||||
|
/// Do not return the same rect multiple times as that will break the cache.
|
||||
|
/// Uses SKRoundRect.SetEmpty(); for reuse later.
|
||||
|
/// </remarks>
|
||||
|
/// <param name="rect">Rectangle to reset</param>
|
||||
|
public void ReturnReset(SKRoundRect rect) |
||||
|
{ |
||||
|
rect.SetEmpty(); |
||||
|
Cache.Add(rect); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Clears and disposes all cached items.
|
||||
|
/// </summary>
|
||||
|
public new void Clear() |
||||
|
{ |
||||
|
base.Clear(); |
||||
|
|
||||
|
// Clear out the cache of SKPoint arrays.
|
||||
|
while (_radiiCache.TryTake(out var item)) |
||||
|
{ |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,13 @@ |
|||||
|
using System.Collections.Concurrent; |
||||
|
using SkiaSharp; |
||||
|
|
||||
|
namespace Avalonia.Skia |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Cache for SKTextBlobBuilder.
|
||||
|
/// </summary>
|
||||
|
internal class SKTextBlobBuilderCache : SKCacheBase<SKTextBlobBuilder, SKTextBlobBuilderCache> |
||||
|
{ |
||||
|
|
||||
|
} |
||||
|
} |
||||
Some files were not shown because too many files changed in this diff
Loading…
Reference in new issue