committed by
GitHub
870 changed files with 24585 additions and 19604 deletions
@ -1,7 +1,7 @@ |
|||||
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> |
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> |
||||
<ItemGroup> |
<ItemGroup> |
||||
<PackageReference Include="HarfBuzzSharp" Version="2.8.2-preview.171" /> |
<PackageReference Include="HarfBuzzSharp" Version="2.8.2-preview.178" /> |
||||
<PackageReference Condition="'$(IncludeLinuxSkia)' == 'true'" Include="HarfBuzzSharp.NativeAssets.Linux" Version="2.8.2-preview.171" /> |
<PackageReference Condition="'$(IncludeLinuxSkia)' == 'true'" Include="HarfBuzzSharp.NativeAssets.Linux" Version="2.8.2-preview.178" /> |
||||
<PackageReference Condition="'$(IncludeWasmSkia)' == 'true'" Include="HarfBuzzSharp.NativeAssets.WebAssembly" Version="2.8.2-preview.171"/> |
<PackageReference Condition="'$(IncludeWasmSkia)' == 'true'" Include="HarfBuzzSharp.NativeAssets.WebAssembly" Version="2.8.2-preview.178"/> |
||||
</ItemGroup> |
</ItemGroup> |
||||
</Project> |
</Project> |
||||
|
|||||
@ -1,35 +0,0 @@ |
|||||
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> |
|
||||
|
|
||||
<!-- Ensure that code generator is actually built --> |
|
||||
<ItemGroup> |
|
||||
<ProjectReference Include="$(MSBuildThisFileDirectory)\..\src\tools\MicroComGenerator\MicroComGenerator.csproj"> |
|
||||
<ReferenceOutputAssembly>false</ReferenceOutputAssembly> |
|
||||
<ExcludeAssets>all</ExcludeAssets> |
|
||||
<SkipGetTargetFrameworkProperties>true</SkipGetTargetFrameworkProperties> |
|
||||
<SetTargetFramework>TargetFramework=net6.0</SetTargetFramework> |
|
||||
</ProjectReference> |
|
||||
</ItemGroup> |
|
||||
|
|
||||
<Target Name="GenerateAvaloniaNativeComInterop" |
|
||||
BeforeTargets="CoreCompile" |
|
||||
DependsOnTargets="ResolveReferences" |
|
||||
Inputs="@(AvnComIdl);$(MSBuildThisFileDirectory)../src/tools/MicroComGenerator/**/*.cs" |
|
||||
Outputs="%(AvnComIdl.OutputFile)"> |
|
||||
<Message Importance="high" Text="Generating file %(AvnComIdl.OutputFile) from @(AvnComIdl)" /> |
|
||||
<Exec Command="dotnet "$(MSBuildThisFileDirectory)../src/tools/MicroComGenerator/bin/$(Configuration)/net6.0/MicroComGenerator.dll" -i @(AvnComIdl) --cs %(AvnComIdl.OutputFile)" |
|
||||
LogStandardErrorAsError="true" /> |
|
||||
<ItemGroup> |
|
||||
<!-- Remove and re-add generated file, this is needed for the clean build --> |
|
||||
<Compile Remove="%(AvnComIdl.OutputFile)"/> |
|
||||
<Compile Include="%(AvnComIdl.OutputFile)"/> |
|
||||
</ItemGroup> |
|
||||
</Target> |
|
||||
<ItemGroup> |
|
||||
<UpToDateCheckInput Include="@(AvnComIdl)"/> |
|
||||
<UpToDateCheckInput Include="$(MSBuildThisFileDirectory)/../src/tools/MicroComGenerator/**/*.cs"/> |
|
||||
</ItemGroup> |
|
||||
<PropertyGroup> |
|
||||
<_AvaloniaPatchComInterop>true</_AvaloniaPatchComInterop> |
|
||||
</PropertyGroup> |
|
||||
<Import Project="$(MSBuildThisFileDirectory)/BuildTargets.targets" /> |
|
||||
</Project> |
|
||||
@ -1,7 +1,7 @@ |
|||||
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> |
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> |
||||
<ItemGroup> |
<ItemGroup> |
||||
<PackageReference Include="SkiaSharp" Version="2.88.0-preview.171" /> |
<PackageReference Include="SkiaSharp" Version="2.88.0-preview.178" /> |
||||
<PackageReference Condition="'$(IncludeLinuxSkia)' == 'true'" Include="SkiaSharp.NativeAssets.Linux" Version="2.88.0-preview.171" /> |
<PackageReference Condition="'$(IncludeLinuxSkia)' == 'true'" Include="SkiaSharp.NativeAssets.Linux" Version="2.88.0-preview.178" /> |
||||
<PackageReference Condition="'$(IncludeWasmSkia)' == 'true'" Include="SkiaSharp.NativeAssets.WebAssembly" Version="2.88.0-preview.171"/> |
<PackageReference Condition="'$(IncludeWasmSkia)' == 'true'" Include="SkiaSharp.NativeAssets.WebAssembly" Version="2.88.0-preview.178"/> |
||||
</ItemGroup> |
</ItemGroup> |
||||
</Project> |
</Project> |
||||
|
|||||
@ -1,14 +1,14 @@ |
|||||
using System.IO; |
using System.IO; |
||||
using MicroComGenerator; |
using MicroCom.CodeGenerator; |
||||
using Nuke.Common; |
using Nuke.Common; |
||||
|
|
||||
partial class Build : NukeBuild |
partial class Build : NukeBuild |
||||
{ |
{ |
||||
Target GenerateCppHeaders => _ => _.Executes(() => |
Target GenerateCppHeaders => _ => _.Executes(() => |
||||
{ |
{ |
||||
var text = File.ReadAllText(RootDirectory / "src" / "Avalonia.Native" / "avn.idl"); |
var file = MicroComCodeGenerator.Parse( |
||||
var ast = AstParser.Parse(text); |
File.ReadAllText(RootDirectory / "src" / "Avalonia.Native" / "avn.idl")); |
||||
File.WriteAllText(RootDirectory / "native" / "Avalonia.Native" / "inc" / "avalonia-native.h", |
File.WriteAllText(RootDirectory / "native" / "Avalonia.Native" / "inc" / "avalonia-native.h", |
||||
CppGen.GenerateCpp(ast)); |
file.GenerateCppHeader()); |
||||
}); |
}); |
||||
} |
} |
||||
@ -1,77 +1,95 @@ |
|||||
<UserControl xmlns="https://github.com/avaloniaui" |
<UserControl |
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" |
x:Class="ControlCatalog.Pages.ComboBoxPage" |
||||
x:Class="ControlCatalog.Pages.ComboBoxPage" |
xmlns="https://github.com/avaloniaui" |
||||
xmlns:sys="using:System" |
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" |
||||
xmlns:col="using:System.Collections"> |
xmlns:col="using:System.Collections" |
||||
<StackPanel Orientation="Vertical" Spacing="4"> |
xmlns:sys="using:System"> |
||||
<TextBlock Classes="h2">A drop-down list.</TextBlock> |
<StackPanel Orientation="Vertical" Spacing="4"> |
||||
|
<TextBlock Classes="h2">A drop-down list.</TextBlock> |
||||
|
|
||||
<WrapPanel HorizontalAlignment="Center" Margin="0 16 0 0" |
<StackPanel |
||||
MaxWidth="660"> |
Margin="0,16,0,0" |
||||
<WrapPanel.Styles> |
HorizontalAlignment="Center" |
||||
<Style Selector="ComboBox"> |
Orientation="Horizontal" |
||||
<Setter Property="Width" Value="250" /> |
Spacing="8"> |
||||
<Setter Property="Margin" Value="10" /> |
<WrapPanel |
||||
</Style> |
MaxWidth="660" |
||||
</WrapPanel.Styles> |
Margin="0,16,0,0" |
||||
<ComboBox PlaceholderText="Pick an Item"> |
HorizontalAlignment="Center"> |
||||
<ComboBoxItem>Inline Items</ComboBoxItem> |
<WrapPanel.Styles> |
||||
<ComboBoxItem>Inline Item 2</ComboBoxItem> |
<Style Selector="ComboBox"> |
||||
<ComboBoxItem>Inline Item 3</ComboBoxItem> |
<Setter Property="Width" Value="250" /> |
||||
<ComboBoxItem>Inline Item 4</ComboBoxItem> |
<Setter Property="Margin" Value="10" /> |
||||
</ComboBox> |
</Style> |
||||
|
</WrapPanel.Styles> |
||||
|
|
||||
<ComboBox> |
<ComboBox PlaceholderText="Pick an Item" WrapSelection="{Binding WrapSelection}"> |
||||
<ComboBox.Items> |
<ComboBoxItem>Inline Items</ComboBoxItem> |
||||
<col:ArrayList> |
<ComboBoxItem>Inline Item 2</ComboBoxItem> |
||||
<x:Null /> |
<ComboBoxItem>Inline Item 3</ComboBoxItem> |
||||
<sys:String>Hello</sys:String> |
<ComboBoxItem>Inline Item 4</ComboBoxItem> |
||||
<sys:String>World</sys:String> |
</ComboBox> |
||||
</col:ArrayList> |
|
||||
</ComboBox.Items> |
|
||||
<ComboBox.ItemTemplate> |
|
||||
<DataTemplate> |
|
||||
<Panel> |
|
||||
<TextBlock Text="{Binding}" /> |
|
||||
<TextBlock Text="Null object" IsVisible="{Binding Converter={x:Static ObjectConverters.IsNull}}" /> |
|
||||
</Panel> |
|
||||
</DataTemplate> |
|
||||
</ComboBox.ItemTemplate> |
|
||||
</ComboBox> |
|
||||
|
|
||||
<ComboBox SelectedIndex="0"> |
<ComboBox WrapSelection="{Binding WrapSelection}"> |
||||
<ComboBoxItem> |
<ComboBox.Items> |
||||
<Panel> |
<col:ArrayList> |
||||
<Rectangle Fill="{DynamicResource SystemAccentColor}"/> |
<x:Null /> |
||||
<TextBlock Margin="8">Control Items</TextBlock> |
<sys:String>Hello</sys:String> |
||||
</Panel> |
<sys:String>World</sys:String> |
||||
</ComboBoxItem> |
</col:ArrayList> |
||||
<ComboBoxItem> |
</ComboBox.Items> |
||||
<Ellipse Width="50" Height="50" Fill="Yellow"/> |
<ComboBox.ItemTemplate> |
||||
</ComboBoxItem> |
<DataTemplate> |
||||
<ComboBoxItem> |
<Panel> |
||||
<TextBox Text="TextBox"/> |
<TextBlock Text="{Binding}" /> |
||||
</ComboBoxItem> |
<TextBlock IsVisible="{Binding Converter={x:Static ObjectConverters.IsNull}}" Text="Null object" /> |
||||
</ComboBox> |
</Panel> |
||||
|
</DataTemplate> |
||||
|
</ComboBox.ItemTemplate> |
||||
|
</ComboBox> |
||||
|
|
||||
<ComboBox x:Name="fontComboBox" SelectedIndex="0"> |
<ComboBox SelectedIndex="0" WrapSelection="{Binding WrapSelection}"> |
||||
<ComboBox.ItemTemplate> |
<ComboBoxItem> |
||||
<DataTemplate> |
<Panel> |
||||
<TextBlock Text="{Binding Name}" FontFamily="{Binding}" /> |
<Rectangle Fill="{DynamicResource SystemAccentColor}" /> |
||||
</DataTemplate> |
<TextBlock Margin="8">Control Items</TextBlock> |
||||
</ComboBox.ItemTemplate> |
</Panel> |
||||
</ComboBox> |
</ComboBoxItem> |
||||
|
<ComboBoxItem> |
||||
<ComboBox PlaceholderText="Pick an Item"> |
<Ellipse |
||||
<ComboBoxItem>Inline Items</ComboBoxItem> |
Width="50" |
||||
<ComboBoxItem>Inline Item 2</ComboBoxItem> |
Height="50" |
||||
<ComboBoxItem>Inline Item 3</ComboBoxItem> |
Fill="Yellow" /> |
||||
<ComboBoxItem>Inline Item 4</ComboBoxItem> |
</ComboBoxItem> |
||||
<DataValidationErrors.Error> |
<ComboBoxItem> |
||||
<sys:Exception /> |
<TextBox Text="TextBox" /> |
||||
</DataValidationErrors.Error> |
</ComboBoxItem> |
||||
</ComboBox> |
</ComboBox> |
||||
</WrapPanel> |
|
||||
|
|
||||
</StackPanel> |
<ComboBox |
||||
|
x:Name="fontComboBox" |
||||
|
SelectedIndex="0" |
||||
|
WrapSelection="{Binding WrapSelection}"> |
||||
|
<ComboBox.ItemTemplate> |
||||
|
<DataTemplate> |
||||
|
<TextBlock FontFamily="{Binding}" Text="{Binding Name}" /> |
||||
|
</DataTemplate> |
||||
|
</ComboBox.ItemTemplate> |
||||
|
</ComboBox> |
||||
|
|
||||
|
<ComboBox PlaceholderText="Pick an Item" WrapSelection="{Binding WrapSelection}"> |
||||
|
<ComboBoxItem>Inline Items</ComboBoxItem> |
||||
|
<ComboBoxItem>Inline Item 2</ComboBoxItem> |
||||
|
<ComboBoxItem>Inline Item 3</ComboBoxItem> |
||||
|
<ComboBoxItem>Inline Item 4</ComboBoxItem> |
||||
|
<DataValidationErrors.Error> |
||||
|
<sys:Exception /> |
||||
|
</DataValidationErrors.Error> |
||||
|
</ComboBox> |
||||
|
</WrapPanel> |
||||
|
|
||||
|
<CheckBox IsChecked="{Binding WrapSelection}">WrapSelection</CheckBox> |
||||
|
|
||||
|
</StackPanel> |
||||
|
</StackPanel> |
||||
</UserControl> |
</UserControl> |
||||
|
|||||
@ -0,0 +1,21 @@ |
|||||
|
using System; |
||||
|
using System.Collections.ObjectModel; |
||||
|
using System.Linq; |
||||
|
using System.Reactive; |
||||
|
using Avalonia.Controls; |
||||
|
using Avalonia.Controls.Selection; |
||||
|
using MiniMvvm; |
||||
|
|
||||
|
namespace ControlCatalog.ViewModels |
||||
|
{ |
||||
|
public class ComboBoxPageViewModel : ViewModelBase |
||||
|
{ |
||||
|
private bool _wrapSelection; |
||||
|
|
||||
|
public bool WrapSelection |
||||
|
{ |
||||
|
get => _wrapSelection; |
||||
|
set => this.RaiseAndSetIfChanged(ref _wrapSelection, value); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -1,35 +1,18 @@ |
|||||
using System; |
|
||||
using System.Reactive.Linq; |
|
||||
using Avalonia; |
|
||||
using Avalonia.Animation; |
|
||||
using Avalonia.Controls; |
using Avalonia.Controls; |
||||
using Avalonia.Data; |
|
||||
using Avalonia.Markup.Xaml; |
using Avalonia.Markup.Xaml; |
||||
using Avalonia.Media; |
|
||||
|
|
||||
namespace RenderDemo.Pages |
namespace RenderDemo.Pages |
||||
{ |
{ |
||||
public class ClippingPage : UserControl |
public class ClippingPage : UserControl |
||||
{ |
{ |
||||
private Geometry _clip; |
|
||||
|
|
||||
public ClippingPage() |
public ClippingPage() |
||||
{ |
{ |
||||
InitializeComponent(); |
InitializeComponent(); |
||||
WireUpCheckbox(); |
|
||||
} |
} |
||||
|
|
||||
private void InitializeComponent() |
private void InitializeComponent() |
||||
{ |
{ |
||||
AvaloniaXamlLoader.Load(this); |
AvaloniaXamlLoader.Load(this); |
||||
} |
} |
||||
|
|
||||
private void WireUpCheckbox() |
|
||||
{ |
|
||||
var useMask = this.FindControl<CheckBox>("useMask"); |
|
||||
var clipped = this.FindControl<Border>("clipped"); |
|
||||
_clip = clipped.Clip; |
|
||||
useMask.Click += (s, e) => clipped.Clip = clipped.Clip == null ? _clip : null; |
|
||||
} |
|
||||
} |
} |
||||
} |
} |
||||
|
|||||
@ -0,0 +1,7 @@ |
|||||
|
<UserControl xmlns="https://github.com/avaloniaui" |
||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" |
||||
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" |
||||
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" |
||||
|
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" |
||||
|
x:Class="RenderDemo.Pages.FormattedTextPage"> |
||||
|
</UserControl> |
||||
@ -0,0 +1,60 @@ |
|||||
|
using System.Globalization; |
||||
|
using Avalonia; |
||||
|
using Avalonia.Controls; |
||||
|
using Avalonia.Markup.Xaml; |
||||
|
using Avalonia.Media; |
||||
|
|
||||
|
namespace RenderDemo.Pages |
||||
|
{ |
||||
|
public class FormattedTextPage : UserControl |
||||
|
{ |
||||
|
public FormattedTextPage() |
||||
|
{ |
||||
|
this.InitializeComponent(); |
||||
|
} |
||||
|
|
||||
|
private void InitializeComponent() |
||||
|
{ |
||||
|
AvaloniaXamlLoader.Load(this); |
||||
|
} |
||||
|
|
||||
|
public override void Render(DrawingContext context) |
||||
|
{ |
||||
|
const string testString = "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor"; |
||||
|
|
||||
|
// Create the initial formatted text string.
|
||||
|
var formattedText = new FormattedText( |
||||
|
testString, |
||||
|
CultureInfo.GetCultureInfo("en-us"), |
||||
|
FlowDirection.LeftToRight, |
||||
|
new Typeface("Verdana"), |
||||
|
32, |
||||
|
Brushes.Black) { MaxTextWidth = 300, MaxTextHeight = 240 }; |
||||
|
|
||||
|
// Set a maximum width and height. If the text overflows these values, an ellipsis "..." appears.
|
||||
|
|
||||
|
// Use a larger font size beginning at the first (zero-based) character and continuing for 5 characters.
|
||||
|
// The font size is calculated in terms of points -- not as device-independent pixels.
|
||||
|
formattedText.SetFontSize(36 * (96.0 / 72.0), 0, 5); |
||||
|
|
||||
|
// Use a Bold font weight beginning at the 6th character and continuing for 11 characters.
|
||||
|
formattedText.SetFontWeight(FontWeight.Bold, 6, 11); |
||||
|
|
||||
|
var gradient = new LinearGradientBrush |
||||
|
{ |
||||
|
GradientStops = |
||||
|
new GradientStops { new GradientStop(Colors.Orange, 0), new GradientStop(Colors.Teal, 1) }, |
||||
|
StartPoint = new RelativePoint(0,0, RelativeUnit.Relative), |
||||
|
EndPoint = new RelativePoint(0,1, RelativeUnit.Relative) |
||||
|
}; |
||||
|
|
||||
|
// Use a linear gradient brush beginning at the 6th character and continuing for 11 characters.
|
||||
|
formattedText.SetForegroundBrush(gradient, 6, 11); |
||||
|
|
||||
|
// Use an Italic font style beginning at the 28th character and continuing for 28 characters.
|
||||
|
formattedText.SetFontStyle(FontStyle.Italic, 28, 28); |
||||
|
|
||||
|
context.DrawText(formattedText, new Point(10, 0)); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -1,18 +0,0 @@ |
|||||
using Avalonia.Platform; |
|
||||
|
|
||||
namespace Avalonia.Shared.PlatformSupport |
|
||||
{ |
|
||||
internal partial class StandardRuntimePlatform |
|
||||
{ |
|
||||
public RuntimePlatformInfo GetRuntimeInfo() => new RuntimePlatformInfo |
|
||||
{ |
|
||||
IsCoreClr = false, |
|
||||
IsDesktop = false, |
|
||||
IsMobile = true, |
|
||||
IsDotNetFramework = false, |
|
||||
IsMono = true, |
|
||||
IsUnix = true, |
|
||||
OperatingSystem = OperatingSystemType.Android |
|
||||
}; |
|
||||
} |
|
||||
} |
|
||||
@ -0,0 +1,56 @@ |
|||||
|
using System; |
||||
|
using System.IO; |
||||
|
using Avalonia.Platform; |
||||
|
|
||||
|
namespace Avalonia.Android |
||||
|
{ |
||||
|
class WindowingPlatformStub : IWindowingPlatform |
||||
|
{ |
||||
|
public IWindowImpl CreateWindow() => throw new NotSupportedException(); |
||||
|
|
||||
|
public IWindowImpl CreateEmbeddableWindow() => throw new NotSupportedException(); |
||||
|
|
||||
|
public ITrayIconImpl CreateTrayIcon() => null; |
||||
|
} |
||||
|
|
||||
|
class PlatformIconLoaderStub : IPlatformIconLoader |
||||
|
{ |
||||
|
public IWindowIconImpl LoadIcon(IBitmapImpl bitmap) |
||||
|
{ |
||||
|
using (var stream = new MemoryStream()) |
||||
|
{ |
||||
|
bitmap.Save(stream); |
||||
|
return LoadIcon(stream); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public IWindowIconImpl LoadIcon(Stream stream) |
||||
|
{ |
||||
|
var ms = new MemoryStream(); |
||||
|
stream.CopyTo(ms); |
||||
|
return new IconStub(ms); |
||||
|
} |
||||
|
|
||||
|
public IWindowIconImpl LoadIcon(string fileName) |
||||
|
{ |
||||
|
using (var file = File.Open(fileName, FileMode.Open)) |
||||
|
return LoadIcon(file); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public class IconStub : IWindowIconImpl |
||||
|
{ |
||||
|
private readonly MemoryStream _ms; |
||||
|
|
||||
|
public IconStub(MemoryStream stream) |
||||
|
{ |
||||
|
_ms = stream; |
||||
|
} |
||||
|
|
||||
|
public void Save(Stream outputStream) |
||||
|
{ |
||||
|
_ms.Position = 0; |
||||
|
_ms.CopyTo(outputStream); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -1,10 +1,14 @@ |
|||||
<Project Sdk="Microsoft.NET.Sdk"> |
<Project Sdk="Microsoft.NET.Sdk"> |
||||
<PropertyGroup> |
<PropertyGroup> |
||||
<TargetFrameworks>netstandard2.0;net6.0</TargetFrameworks> |
<TargetFrameworks>net6.0;netstandard2.0</TargetFrameworks> |
||||
</PropertyGroup> |
</PropertyGroup> |
||||
|
<ItemGroup> |
||||
|
<Compile Include="..\Avalonia.Base\Metadata\NullableAttributes.cs" Link="NullableAttributes.cs" /> |
||||
|
</ItemGroup> |
||||
<ItemGroup> |
<ItemGroup> |
||||
<ProjectReference Include="..\Avalonia.Base\Avalonia.Base.csproj" /> |
<ProjectReference Include="..\Avalonia.Base\Avalonia.Base.csproj" /> |
||||
</ItemGroup> |
</ItemGroup> |
||||
<Import Project="..\..\build\Rx.props" /> |
<Import Project="..\..\build\Rx.props" /> |
||||
<Import Project="..\..\build\ApiDiff.props" /> |
<Import Project="..\..\build\ApiDiff.props" /> |
||||
|
<Import Project="..\..\build\NullableEnable.props" /> |
||||
</Project> |
</Project> |
||||
|
|||||
@ -0,0 +1,3 @@ |
|||||
|
Compat issues with assembly Avalonia.Base: |
||||
|
InterfacesShouldHaveSameMembers : Interface member 'public void Avalonia.Threading.IDispatcher.Post<T>(System.Action<T>, T, Avalonia.Threading.DispatcherPriority)' is present in the implementation but not in the contract. |
||||
|
Total Issues: 1 |
||||
@ -0,0 +1,12 @@ |
|||||
|
using System; |
||||
|
|
||||
|
namespace Avalonia.Utilities; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Defines a listener to a event subscribed vis the <see cref="WeakEvent{TTarget, TEventArgs}"/>.
|
||||
|
/// </summary>
|
||||
|
/// <typeparam name="TEventArgs">The type of the event arguments.</typeparam>
|
||||
|
public interface IWeakEventSubscriber<in TEventArgs> where TEventArgs : EventArgs |
||||
|
{ |
||||
|
void OnEvent(object? sender, WeakEvent ev, TEventArgs e); |
||||
|
} |
||||
@ -0,0 +1,187 @@ |
|||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Linq; |
||||
|
using System.Reflection; |
||||
|
using System.Runtime.CompilerServices; |
||||
|
using Avalonia.Threading; |
||||
|
|
||||
|
namespace Avalonia.Utilities; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Manages subscriptions to events using weak listeners.
|
||||
|
/// </summary>
|
||||
|
public class WeakEvent<TSender, TEventArgs> : WeakEvent where TEventArgs : EventArgs where TSender : class |
||||
|
{ |
||||
|
private readonly Func<TSender, EventHandler<TEventArgs>, Action> _subscribe; |
||||
|
|
||||
|
readonly ConditionalWeakTable<object, Subscription> _subscriptions = new(); |
||||
|
|
||||
|
internal WeakEvent( |
||||
|
Action<TSender, EventHandler<TEventArgs>> subscribe, |
||||
|
Action<TSender, EventHandler<TEventArgs>> unsubscribe) |
||||
|
{ |
||||
|
_subscribe = (t, s) => |
||||
|
{ |
||||
|
subscribe(t, s); |
||||
|
return () => unsubscribe(t, s); |
||||
|
}; |
||||
|
} |
||||
|
|
||||
|
internal WeakEvent(Func<TSender, EventHandler<TEventArgs>, Action> subscribe) |
||||
|
{ |
||||
|
_subscribe = subscribe; |
||||
|
} |
||||
|
|
||||
|
public void Subscribe(TSender target, IWeakEventSubscriber<TEventArgs> subscriber) |
||||
|
{ |
||||
|
if (!_subscriptions.TryGetValue(target, out var subscription)) |
||||
|
_subscriptions.Add(target, subscription = new Subscription(this, target)); |
||||
|
subscription.Add(new WeakReference<IWeakEventSubscriber<TEventArgs>>(subscriber)); |
||||
|
} |
||||
|
|
||||
|
public void Unsubscribe(TSender target, IWeakEventSubscriber<TEventArgs> subscriber) |
||||
|
{ |
||||
|
if (_subscriptions.TryGetValue(target, out var subscription)) |
||||
|
subscription.Remove(subscriber); |
||||
|
} |
||||
|
|
||||
|
private class Subscription |
||||
|
{ |
||||
|
private readonly WeakEvent<TSender, TEventArgs> _ev; |
||||
|
private readonly TSender _target; |
||||
|
private readonly Action _compact; |
||||
|
|
||||
|
private WeakReference<IWeakEventSubscriber<TEventArgs>>?[] _data = |
||||
|
new WeakReference<IWeakEventSubscriber<TEventArgs>>[16]; |
||||
|
private int _count; |
||||
|
private readonly Action _unsubscribe; |
||||
|
private bool _compactScheduled; |
||||
|
|
||||
|
public Subscription(WeakEvent<TSender, TEventArgs> ev, TSender target) |
||||
|
{ |
||||
|
_ev = ev; |
||||
|
_target = target; |
||||
|
_compact = Compact; |
||||
|
_unsubscribe = ev._subscribe(target, OnEvent); |
||||
|
} |
||||
|
|
||||
|
void Destroy() |
||||
|
{ |
||||
|
_unsubscribe(); |
||||
|
_ev._subscriptions.Remove(_target); |
||||
|
} |
||||
|
|
||||
|
public void Add(WeakReference<IWeakEventSubscriber<TEventArgs>> s) |
||||
|
{ |
||||
|
if (_count == _data.Length) |
||||
|
{ |
||||
|
//Extend capacity
|
||||
|
var extendedData = new WeakReference<IWeakEventSubscriber<TEventArgs>>?[_data.Length * 2]; |
||||
|
Array.Copy(_data, extendedData, _data.Length); |
||||
|
_data = extendedData; |
||||
|
} |
||||
|
|
||||
|
_data[_count] = s; |
||||
|
_count++; |
||||
|
} |
||||
|
|
||||
|
public void Remove(IWeakEventSubscriber<TEventArgs> s) |
||||
|
{ |
||||
|
var removed = false; |
||||
|
|
||||
|
for (int c = 0; c < _count; ++c) |
||||
|
{ |
||||
|
var reference = _data[c]; |
||||
|
|
||||
|
if (reference != null && reference.TryGetTarget(out var instance) && instance == s) |
||||
|
{ |
||||
|
_data[c] = null; |
||||
|
removed = true; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
if (removed) |
||||
|
{ |
||||
|
ScheduleCompact(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
void ScheduleCompact() |
||||
|
{ |
||||
|
if(_compactScheduled) |
||||
|
return; |
||||
|
_compactScheduled = true; |
||||
|
Dispatcher.UIThread.Post(_compact, DispatcherPriority.Background); |
||||
|
} |
||||
|
|
||||
|
void Compact() |
||||
|
{ |
||||
|
_compactScheduled = false; |
||||
|
int empty = -1; |
||||
|
for (var c = 0; c < _count; c++) |
||||
|
{ |
||||
|
var r = _data[c]; |
||||
|
//Mark current index as first empty
|
||||
|
if (r == null && empty == -1) |
||||
|
empty = c; |
||||
|
//If current element isn't null and we have an empty one
|
||||
|
if (r != null && empty != -1) |
||||
|
{ |
||||
|
_data[c] = null; |
||||
|
_data[empty] = r; |
||||
|
empty++; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
if (empty != -1) |
||||
|
_count = empty; |
||||
|
if (_count == 0) |
||||
|
Destroy(); |
||||
|
} |
||||
|
|
||||
|
void OnEvent(object? sender, TEventArgs eventArgs) |
||||
|
{ |
||||
|
var needCompact = false; |
||||
|
for (var c = 0; c < _count; c++) |
||||
|
{ |
||||
|
var r = _data[c]; |
||||
|
if (r?.TryGetTarget(out var sub) == true) |
||||
|
sub!.OnEvent(_target, _ev, eventArgs); |
||||
|
else |
||||
|
needCompact = true; |
||||
|
} |
||||
|
|
||||
|
if (needCompact) |
||||
|
ScheduleCompact(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
} |
||||
|
|
||||
|
public class WeakEvent |
||||
|
{ |
||||
|
public static WeakEvent<TSender, TEventArgs> Register<TSender, TEventArgs>( |
||||
|
Action<TSender, EventHandler<TEventArgs>> subscribe, |
||||
|
Action<TSender, EventHandler<TEventArgs>> unsubscribe) where TSender : class where TEventArgs : EventArgs |
||||
|
{ |
||||
|
return new WeakEvent<TSender, TEventArgs>(subscribe, unsubscribe); |
||||
|
} |
||||
|
|
||||
|
public static WeakEvent<TSender, TEventArgs> Register<TSender, TEventArgs>( |
||||
|
Func<TSender, EventHandler<TEventArgs>, Action> subscribe) where TSender : class where TEventArgs : EventArgs |
||||
|
{ |
||||
|
return new WeakEvent<TSender, TEventArgs>(subscribe); |
||||
|
} |
||||
|
|
||||
|
public static WeakEvent<TSender, EventArgs> Register<TSender>( |
||||
|
Action<TSender, EventHandler> subscribe, |
||||
|
Action<TSender, EventHandler> unsubscribe) where TSender : class |
||||
|
{ |
||||
|
return Register<TSender, EventArgs>((s, h) => |
||||
|
{ |
||||
|
EventHandler handler = (_, e) => h(s, e); |
||||
|
subscribe(s, handler); |
||||
|
return () => unsubscribe(s, handler); |
||||
|
}); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,40 @@ |
|||||
|
using System; |
||||
|
using System.Collections.Specialized; |
||||
|
using System.ComponentModel; |
||||
|
using System.Windows.Input; |
||||
|
|
||||
|
namespace Avalonia.Utilities; |
||||
|
|
||||
|
public class WeakEvents |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Represents CollectionChanged event from <see cref="INotifyCollectionChanged"/>
|
||||
|
/// </summary>
|
||||
|
public static readonly WeakEvent<INotifyCollectionChanged, NotifyCollectionChangedEventArgs> |
||||
|
CollectionChanged = WeakEvent.Register<INotifyCollectionChanged, NotifyCollectionChangedEventArgs>( |
||||
|
(c, s) => |
||||
|
{ |
||||
|
NotifyCollectionChangedEventHandler handler = (_, e) => s(c, e); |
||||
|
c.CollectionChanged += handler; |
||||
|
return () => c.CollectionChanged -= handler; |
||||
|
}); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Represents PropertyChanged event from <see cref="INotifyPropertyChanged"/>
|
||||
|
/// </summary>
|
||||
|
public static readonly WeakEvent<INotifyPropertyChanged, PropertyChangedEventArgs> |
||||
|
PropertyChanged = WeakEvent.Register<INotifyPropertyChanged, PropertyChangedEventArgs>( |
||||
|
(s, h) => |
||||
|
{ |
||||
|
PropertyChangedEventHandler handler = (_, e) => h(s, e); |
||||
|
s.PropertyChanged += handler; |
||||
|
return () => s.PropertyChanged -= handler; |
||||
|
}); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Represents CanExecuteChanged event from <see cref="ICommand"/>
|
||||
|
/// </summary>
|
||||
|
public static readonly WeakEvent<ICommand, EventArgs> CommandCanExecuteChanged = |
||||
|
WeakEvent.Register<ICommand>((s, h) => s.CanExecuteChanged += h, |
||||
|
(s, h) => s.CanExecuteChanged -= h); |
||||
|
} |
||||
Some files were not shown because too many files changed in this diff
Loading…
Reference in new issue