committed by
GitHub
186 changed files with 13955 additions and 8350 deletions
@ -1,6 +1,6 @@ |
|||
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> |
|||
<ItemGroup> |
|||
<PackageReference Include="HarfBuzzSharp" Version="2.6.1.5" /> |
|||
<PackageReference Condition="'$(IncludeLinuxSkia)' == 'true'" Include="HarfBuzzSharp.NativeAssets.Linux" Version="2.6.1.5" /> |
|||
<PackageReference Include="HarfBuzzSharp" Version="2.6.1.6" /> |
|||
<PackageReference Condition="'$(IncludeLinuxSkia)' == 'true'" Include="HarfBuzzSharp.NativeAssets.Linux" Version="2.6.1.6" /> |
|||
</ItemGroup> |
|||
</Project> |
|||
|
|||
@ -1,6 +1,6 @@ |
|||
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> |
|||
<ItemGroup> |
|||
<PackageReference Include="SkiaSharp" Version="2.80.1" /> |
|||
<PackageReference Condition="'$(IncludeLinuxSkia)' == 'true'" Include="SkiaSharp.NativeAssets.Linux" Version="2.80.0" /> |
|||
<PackageReference Include="SkiaSharp" Version="2.80.2-preview.33" /> |
|||
<PackageReference Condition="'$(IncludeLinuxSkia)' == 'true'" Include="SkiaSharp.NativeAssets.Linux" Version="2.80.2-preview.33" /> |
|||
</ItemGroup> |
|||
</Project> |
|||
|
|||
@ -1,5 +0,0 @@ |
|||
<Project> |
|||
<PropertyGroup Condition="'$(iOSRoslynPathHackRequired)' == 'true'"> |
|||
<CscToolPath>$(MSBuildToolsPath)\..\Roslyn</CscToolPath> |
|||
</PropertyGroup> |
|||
</Project> |
|||
@ -1,10 +1,10 @@ |
|||
{ |
|||
"sdk": { |
|||
"version": "3.1.101" |
|||
"version": "3.1.401" |
|||
}, |
|||
"msbuild-sdks": { |
|||
"Microsoft.Build.Traversal": "1.0.43", |
|||
"MSBuild.Sdk.Extras": "2.0.46", |
|||
"MSBuild.Sdk.Extras": "2.0.54", |
|||
"AggregatePackage.NuGet.Sdk" : "0.1.12" |
|||
} |
|||
} |
|||
|
|||
@ -1,6 +0,0 @@ |
|||
Compat issues with assembly Avalonia.Base: |
|||
MembersMustExist : Member 'public void Avalonia.DirectProperty<TOwner, TValue>..ctor(System.String, System.Func<TOwner, TValue>, System.Action<TOwner, TValue>, Avalonia.DirectPropertyMetadata<TValue>, System.Boolean)' does not exist in the implementation but it does exist in the contract. |
|||
MembersMustExist : Member 'protected void Avalonia.DirectPropertyBase<TValue>..ctor(Avalonia.AvaloniaProperty, System.Type, Avalonia.PropertyMetadata, System.Boolean)' does not exist in the implementation but it does exist in the contract. |
|||
MembersMustExist : Member 'protected void Avalonia.DirectPropertyBase<TValue>..ctor(System.String, System.Type, Avalonia.PropertyMetadata, System.Boolean)' does not exist in the implementation but it does exist in the contract. |
|||
MembersMustExist : Member 'public System.Boolean Avalonia.DirectPropertyBase<TValue>.IsDataValidationEnabled.get()' does not exist in the implementation but it does exist in the contract. |
|||
Total Issues: 4 |
|||
@ -1,41 +0,0 @@ |
|||
using System; |
|||
using System.Globalization; |
|||
using System.Reflection; |
|||
using System.Windows.Input; |
|||
using Avalonia.Utilities; |
|||
|
|||
namespace Avalonia.Data.Converters |
|||
{ |
|||
class AlwaysEnabledDelegateCommand : ICommand |
|||
{ |
|||
private readonly Delegate action; |
|||
|
|||
private ParameterInfo parameterInfo; |
|||
|
|||
public AlwaysEnabledDelegateCommand(Delegate action) |
|||
{ |
|||
this.action = action; |
|||
var parameters = action.Method.GetParameters(); |
|||
parameterInfo = parameters.Length == 0 ? null : parameters[0]; |
|||
} |
|||
|
|||
#pragma warning disable 0067
|
|||
public event EventHandler CanExecuteChanged; |
|||
#pragma warning restore 0067
|
|||
|
|||
public bool CanExecute(object parameter) => true; |
|||
|
|||
public void Execute(object parameter) |
|||
{ |
|||
if (parameterInfo == null) |
|||
{ |
|||
action.DynamicInvoke(); |
|||
} |
|||
else |
|||
{ |
|||
TypeUtilities.TryConvert(parameterInfo.ParameterType, parameter, CultureInfo.CurrentCulture, out object convertedParameter); |
|||
action.DynamicInvoke(convertedParameter); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,230 @@ |
|||
using System; |
|||
using System.ComponentModel; |
|||
using System.Globalization; |
|||
using System.Linq; |
|||
using System.Linq.Expressions; |
|||
using System.Reflection; |
|||
using System.Windows.Input; |
|||
using Avalonia.Utilities; |
|||
|
|||
namespace Avalonia.Data.Converters |
|||
{ |
|||
class MethodToCommandConverter : ICommand |
|||
{ |
|||
readonly static Func<object, bool> AlwaysEnabled = (_) => true; |
|||
readonly static MethodInfo tryConvert = typeof(TypeUtilities) |
|||
.GetMethod(nameof(TypeUtilities.TryConvert), BindingFlags.Public | BindingFlags.Static); |
|||
readonly static PropertyInfo currentCulture = typeof(CultureInfo) |
|||
.GetProperty(nameof(CultureInfo.CurrentCulture), BindingFlags.Public | BindingFlags.Static); |
|||
readonly Func<object, bool> canExecute; |
|||
readonly Action<object> execute; |
|||
readonly WeakPropertyChangedProxy weakPropertyChanged; |
|||
readonly PropertyChangedEventHandler propertyChangedEventHandler; |
|||
readonly string[] dependencyProperties; |
|||
|
|||
public MethodToCommandConverter(Delegate action) |
|||
{ |
|||
var target = action.Target; |
|||
var canExecuteMethodName = "Can" + action.Method.Name; |
|||
var parameters = action.Method.GetParameters(); |
|||
var parameterInfo = parameters.Length == 0 ? null : parameters[0].ParameterType; |
|||
|
|||
if (parameterInfo == null) |
|||
{ |
|||
execute = CreateExecute(target, action.Method); |
|||
} |
|||
else |
|||
{ |
|||
execute = CreateExecute(target, action.Method, parameterInfo); |
|||
} |
|||
|
|||
var canExecuteMethod = action.Method.DeclaringType.GetRuntimeMethods() |
|||
.FirstOrDefault(m => m.Name == canExecuteMethodName |
|||
&& m.GetParameters().Length == 1 |
|||
&& m.GetParameters()[0].ParameterType == typeof(object)); |
|||
if (canExecuteMethod == null) |
|||
{ |
|||
canExecute = AlwaysEnabled; |
|||
} |
|||
else |
|||
{ |
|||
canExecute = CreateCanExecute(target, canExecuteMethod); |
|||
dependencyProperties = canExecuteMethod |
|||
.GetCustomAttributes(typeof(Metadata.DependsOnAttribute), true) |
|||
.OfType<Metadata.DependsOnAttribute>() |
|||
.Select(x => x.Name) |
|||
.ToArray(); |
|||
if (dependencyProperties.Any() |
|||
&& target is INotifyPropertyChanged inpc) |
|||
{ |
|||
propertyChangedEventHandler = OnPropertyChanged; |
|||
weakPropertyChanged = new WeakPropertyChangedProxy(inpc, propertyChangedEventHandler); |
|||
} |
|||
} |
|||
} |
|||
|
|||
void OnPropertyChanged(object sender,PropertyChangedEventArgs args) |
|||
{ |
|||
if (string.IsNullOrWhiteSpace(args.PropertyName) |
|||
|| dependencyProperties?.Contains(args.PropertyName) == true) |
|||
{ |
|||
Threading.Dispatcher.UIThread.Post(() => CanExecuteChanged?.Invoke(this, EventArgs.Empty) |
|||
, Threading.DispatcherPriority.Input); |
|||
} |
|||
} |
|||
|
|||
#pragma warning disable 0067
|
|||
public event EventHandler CanExecuteChanged; |
|||
#pragma warning restore 0067
|
|||
|
|||
public bool CanExecute(object parameter) => canExecute(parameter); |
|||
|
|||
public void Execute(object parameter) => execute(parameter); |
|||
|
|||
|
|||
static Action<object> CreateExecute(object target |
|||
, System.Reflection.MethodInfo method) |
|||
{ |
|||
|
|||
var parameter = Expression.Parameter(typeof(object), "parameter"); |
|||
|
|||
var instance = Expression.Convert |
|||
( |
|||
Expression.Constant(target), |
|||
method.DeclaringType |
|||
); |
|||
|
|||
|
|||
var call = Expression.Call |
|||
( |
|||
instance, |
|||
method |
|||
); |
|||
|
|||
|
|||
return Expression |
|||
.Lambda<Action<object>>(call, parameter) |
|||
.Compile(); |
|||
} |
|||
|
|||
static Action<object> CreateExecute(object target |
|||
, System.Reflection.MethodInfo method |
|||
, Type parameterType) |
|||
{ |
|||
|
|||
var parameter = Expression.Parameter(typeof(object), "parameter"); |
|||
|
|||
var instance = Expression.Convert |
|||
( |
|||
Expression.Constant(target), |
|||
method.DeclaringType |
|||
); |
|||
|
|||
Expression body; |
|||
|
|||
if (parameterType == typeof(object)) |
|||
{ |
|||
body = Expression.Call(instance, |
|||
method, |
|||
parameter |
|||
); |
|||
} |
|||
else |
|||
{ |
|||
var arg0 = Expression.Variable(typeof(object), "argX"); |
|||
var convertCall = Expression.Call(tryConvert, |
|||
Expression.Constant(parameterType), |
|||
parameter, |
|||
Expression.Property(null, currentCulture), |
|||
arg0 |
|||
); |
|||
|
|||
var call = Expression.Call(instance, |
|||
method, |
|||
Expression.Convert(arg0, parameterType) |
|||
); |
|||
body = Expression.Block(new[] { arg0 }, |
|||
convertCall, |
|||
call |
|||
); |
|||
|
|||
} |
|||
Action<object> action = null; |
|||
try |
|||
{ |
|||
action = Expression |
|||
.Lambda<Action<object>>(body, parameter) |
|||
.Compile(); |
|||
} |
|||
catch (Exception ex) |
|||
{ |
|||
throw ex; |
|||
} |
|||
return action; |
|||
} |
|||
|
|||
static Func<object, bool> CreateCanExecute(object target |
|||
, System.Reflection.MethodInfo method) |
|||
{ |
|||
var parameter = Expression.Parameter(typeof(object), "parameter"); |
|||
var instance = Expression.Convert |
|||
( |
|||
Expression.Constant(target), |
|||
method.DeclaringType |
|||
); |
|||
var call = Expression.Call |
|||
( |
|||
instance, |
|||
method, |
|||
parameter |
|||
); |
|||
return Expression |
|||
.Lambda<Func<object, bool>>(call, parameter) |
|||
.Compile(); |
|||
} |
|||
|
|||
|
|||
internal class WeakPropertyChangedProxy |
|||
{ |
|||
readonly WeakReference<PropertyChangedEventHandler> _listener = new WeakReference<PropertyChangedEventHandler>(null); |
|||
readonly PropertyChangedEventHandler _handler; |
|||
internal WeakReference<INotifyPropertyChanged> Source { get; } = new WeakReference<INotifyPropertyChanged>(null); |
|||
|
|||
public WeakPropertyChangedProxy() |
|||
{ |
|||
_handler = new PropertyChangedEventHandler(OnPropertyChanged); |
|||
} |
|||
|
|||
public WeakPropertyChangedProxy(INotifyPropertyChanged source, PropertyChangedEventHandler listener) : this() |
|||
{ |
|||
SubscribeTo(source, listener); |
|||
} |
|||
|
|||
public void SubscribeTo(INotifyPropertyChanged source, PropertyChangedEventHandler listener) |
|||
{ |
|||
source.PropertyChanged += _handler; |
|||
|
|||
Source.SetTarget(source); |
|||
_listener.SetTarget(listener); |
|||
} |
|||
|
|||
public void Unsubscribe() |
|||
{ |
|||
if (Source.TryGetTarget(out INotifyPropertyChanged source) && source != null) |
|||
source.PropertyChanged -= _handler; |
|||
|
|||
Source.SetTarget(null); |
|||
_listener.SetTarget(null); |
|||
} |
|||
|
|||
void OnPropertyChanged(object sender, PropertyChangedEventArgs e) |
|||
{ |
|||
if (_listener.TryGetTarget(out var handler) && handler != null) |
|||
handler(sender, e); |
|||
else |
|||
Unsubscribe(); |
|||
} |
|||
|
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,645 @@ |
|||
<Styles xmlns="https://github.com/avaloniaui" |
|||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"> |
|||
<Styles.Resources> |
|||
<Thickness x:Key="DataGridTextColumnCellTextBlockMargin">12,0,12,0</Thickness> |
|||
|
|||
<x:Double x:Key="ListAccentLowOpacity">0.6</x:Double> |
|||
<x:Double x:Key="ListAccentMediumOpacity">0.8</x:Double> |
|||
|
|||
<StreamGeometry x:Key="DataGridSortIconAscendingPath">M1875 1011l-787 787v-1798h-128v1798l-787 -787l-90 90l941 941l941 -941z</StreamGeometry> |
|||
<StreamGeometry x:Key="DataGridSortIconDescendingPath">M1965 947l-941 -941l-941 941l90 90l787 -787v1798h128v-1798l787 787z</StreamGeometry> |
|||
<StreamGeometry x:Key="DataGridRowGroupHeaderIconClosedPath">M515 93l930 931l-930 931l90 90l1022 -1021l-1022 -1021z</StreamGeometry> |
|||
<StreamGeometry x:Key="DataGridRowGroupHeaderIconOpenedPath">M1939 1581l90 -90l-1005 -1005l-1005 1005l90 90l915 -915z</StreamGeometry> |
|||
|
|||
<SolidColorBrush x:Key="DataGridGridLinesBrush" |
|||
Color="{StaticResource SystemBaseMediumLowColor}" |
|||
Opacity="0.4" /> |
|||
<SolidColorBrush x:Key="DataGridDropLocationIndicatorBackground" |
|||
Color="#3F4346" /> |
|||
<SolidColorBrush x:Key="DataGridDisabledVisualElementBackground" |
|||
Color="#8CFFFFFF" /> |
|||
<SolidColorBrush x:Key="DataGridFillerGridLinesBrush" |
|||
Color="Transparent" /> |
|||
<SolidColorBrush x:Key="DataGridCurrencyVisualPrimaryBrush" |
|||
Color="Transparent" /> |
|||
<StaticResource x:Key="DataGridColumnHeaderBackgroundColor" |
|||
ResourceKey="SystemAltHighColor" /> |
|||
<SolidColorBrush x:Key="DataGridColumnHeaderBackgroundBrush" |
|||
Color="{StaticResource DataGridColumnHeaderBackgroundColor}" /> |
|||
<StaticResource x:Key="DataGridScrollBarsSeparatorBackground" |
|||
ResourceKey="SystemChromeLowColor" /> |
|||
<StaticResource x:Key="DataGridColumnHeaderForegroundBrush" |
|||
ResourceKey="SystemControlForegroundBaseMediumBrush" /> |
|||
<StaticResource x:Key="DataGridColumnHeaderHoveredBackgroundColor" |
|||
ResourceKey="SystemListLowColor" /> |
|||
<StaticResource x:Key="DataGridColumnHeaderPressedBackgroundColor" |
|||
ResourceKey="SystemListMediumColor" /> |
|||
<StaticResource x:Key="DataGridColumnHeaderDraggedBackgroundBrush" |
|||
ResourceKey="SystemControlBackgroundChromeMediumLowBrush" /> |
|||
<StaticResource x:Key="DataGridColumnHeaderPointerOverBrush" |
|||
ResourceKey="SystemControlHighlightListLowBrush" /> |
|||
<StaticResource x:Key="DataGridColumnHeaderPressedBrush" |
|||
ResourceKey="SystemControlHighlightListMediumBrush" /> |
|||
<StaticResource x:Key="DataGridDetailsPresenterBackgroundBrush" |
|||
ResourceKey="SystemControlBackgroundChromeMediumLowBrush" /> |
|||
<StaticResource x:Key="DataGridFillerColumnGridLinesBrush" |
|||
ResourceKey="DataGridFillerGridLinesBrush" /> |
|||
<StaticResource x:Key="DataGridRowSelectedBackgroundColor" |
|||
ResourceKey="SystemAccentColor" /> |
|||
<StaticResource x:Key="DataGridRowSelectedBackgroundOpacity" |
|||
ResourceKey="ListAccentLowOpacity" /> |
|||
<StaticResource x:Key="DataGridRowSelectedHoveredBackgroundColor" |
|||
ResourceKey="SystemAccentColor" /> |
|||
<StaticResource x:Key="DataGridRowSelectedHoveredBackgroundOpacity" |
|||
ResourceKey="ListAccentMediumOpacity" /> |
|||
<StaticResource x:Key="DataGridRowSelectedUnfocusedBackgroundColor" |
|||
ResourceKey="SystemAccentColor" /> |
|||
<StaticResource x:Key="DataGridRowSelectedUnfocusedBackgroundOpacity" |
|||
ResourceKey="ListAccentLowOpacity" /> |
|||
<StaticResource x:Key="DataGridRowSelectedHoveredUnfocusedBackgroundColor" |
|||
ResourceKey="SystemAccentColor" /> |
|||
<StaticResource x:Key="DataGridRowSelectedHoveredUnfocusedBackgroundOpacity" |
|||
ResourceKey="ListAccentMediumOpacity" /> |
|||
<StaticResource x:Key="DataGridRowHeaderForegroundBrush" |
|||
ResourceKey="SystemControlForegroundBaseMediumBrush" /> |
|||
<StaticResource x:Key="DataGridRowHeaderBackgroundBrush" |
|||
ResourceKey="SystemControlBackgroundAltHighBrush" /> |
|||
<StaticResource x:Key="DataGridRowGroupHeaderBackgroundBrush" |
|||
ResourceKey="SystemControlBackgroundChromeMediumBrush" /> |
|||
<StaticResource x:Key="DataGridRowGroupHeaderHoveredBackgroundBrush" |
|||
ResourceKey="SystemControlBackgroundListLowBrush" /> |
|||
<StaticResource x:Key="DataGridRowGroupHeaderPressedBackgroundBrush" |
|||
ResourceKey="SystemControlBackgroundListMediumBrush" /> |
|||
<StaticResource x:Key="DataGridRowGroupHeaderForegroundBrush" |
|||
ResourceKey="SystemControlForegroundBaseHighBrush" /> |
|||
<StaticResource x:Key="DataGridRowInvalidBrush" |
|||
ResourceKey="SystemErrorTextColor" /> |
|||
<StaticResource x:Key="DataGridCellBackgroundBrush" |
|||
ResourceKey="SystemControlTransparentBrush" /> |
|||
<StaticResource x:Key="DataGridCellFocusVisualPrimaryBrush" |
|||
ResourceKey="SystemControlFocusVisualPrimaryBrush" /> |
|||
<StaticResource x:Key="DataGridCellFocusVisualSecondaryBrush" |
|||
ResourceKey="SystemControlFocusVisualSecondaryBrush" /> |
|||
<StaticResource x:Key="DataGridCellInvalidBrush" |
|||
ResourceKey="SystemErrorTextColor" /> |
|||
</Styles.Resources> |
|||
|
|||
<Style Selector="DataGridCell"> |
|||
<Setter Property="Background" Value="{DynamicResource DataGridCellBackgroundBrush}" /> |
|||
<Setter Property="HorizontalContentAlignment" Value="Stretch" /> |
|||
<Setter Property="VerticalContentAlignment" Value="Stretch" /> |
|||
<Setter Property="FontSize" Value="15" /> |
|||
<Setter Property="MinHeight" Value="32" /> |
|||
<Setter Property="Focusable" Value="False" /> |
|||
<Setter Property="Template"> |
|||
<ControlTemplate> |
|||
<Grid x:Name="PART_CellRoot" |
|||
ColumnDefinitions="*,Auto" |
|||
Background="{TemplateBinding Background}"> |
|||
|
|||
<Rectangle x:Name="CurrencyVisual" |
|||
HorizontalAlignment="Stretch" |
|||
VerticalAlignment="Stretch" |
|||
Fill="Transparent" |
|||
IsHitTestVisible="False" |
|||
Stroke="{DynamicResource DataGridCurrencyVisualPrimaryBrush}" |
|||
StrokeThickness="1" /> |
|||
<Grid x:Name="FocusVisual" |
|||
IsHitTestVisible="False"> |
|||
<Rectangle HorizontalAlignment="Stretch" |
|||
VerticalAlignment="Stretch" |
|||
Fill="Transparent" |
|||
IsHitTestVisible="False" |
|||
Stroke="{DynamicResource DataGridCellFocusVisualPrimaryBrush}" |
|||
StrokeThickness="2" /> |
|||
<Rectangle Margin="2" |
|||
HorizontalAlignment="Stretch" |
|||
VerticalAlignment="Stretch" |
|||
Fill="Transparent" |
|||
IsHitTestVisible="False" |
|||
Stroke="{DynamicResource DataGridCellFocusVisualSecondaryBrush}" |
|||
StrokeThickness="1" /> |
|||
</Grid> |
|||
|
|||
<ContentPresenter ContentTemplate="{TemplateBinding ContentTemplate}" |
|||
Content="{TemplateBinding Content}" |
|||
Margin="{TemplateBinding Padding}" |
|||
TextBlock.Foreground="{TemplateBinding Foreground}" |
|||
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" |
|||
VerticalAlignment="{TemplateBinding VerticalContentAlignment}" /> |
|||
|
|||
<Rectangle x:Name="InvalidVisualElement" |
|||
HorizontalAlignment="Stretch" |
|||
VerticalAlignment="Stretch" |
|||
IsHitTestVisible="False" |
|||
Stroke="{DynamicResource DataGridCellInvalidBrush}" |
|||
StrokeThickness="1" /> |
|||
|
|||
<Rectangle Name="PART_RightGridLine" |
|||
Grid.Column="1" |
|||
VerticalAlignment="Stretch" |
|||
Width="1" |
|||
Fill="{DynamicResource DataGridFillerColumnGridLinesBrush}" /> |
|||
</Grid> |
|||
</ControlTemplate> |
|||
</Setter> |
|||
</Style> |
|||
|
|||
<Style Selector="DataGridCell /template/ Rectangle#CurrencyVisual"> |
|||
<Setter Property="IsVisible" Value="False" /> |
|||
</Style> |
|||
<Style Selector="DataGridCell /template/ Grid#FocusVisual"> |
|||
<Setter Property="IsVisible" Value="False" /> |
|||
</Style> |
|||
<Style Selector="DataGridCell:current /template/ Rectangle#CurrencyVisual"> |
|||
<Setter Property="IsVisible" Value="True" /> |
|||
</Style> |
|||
<Style Selector="DataGrid:focus DataGridCell:current /template/ Grid#FocusVisual"> |
|||
<Setter Property="IsVisible" Value="True" /> |
|||
</Style> |
|||
<Style Selector="DataGridCell /template/ Rectangle#InvalidVisualElement"> |
|||
<Setter Property="IsVisible" Value="False" /> |
|||
</Style> |
|||
<Style Selector="DataGridCell:invalid /template/ Rectangle#InvalidVisualElement"> |
|||
<Setter Property="IsVisible" Value="True" /> |
|||
</Style> |
|||
|
|||
<Style Selector="DataGridColumnHeader"> |
|||
<Setter Property="Foreground" Value="{DynamicResource DataGridColumnHeaderForegroundBrush}" /> |
|||
<Setter Property="Background" Value="{DynamicResource DataGridColumnHeaderBackgroundBrush}" /> |
|||
<Setter Property="HorizontalContentAlignment" Value="Stretch" /> |
|||
<Setter Property="VerticalContentAlignment" Value="Center" /> |
|||
<Setter Property="Focusable" Value="False" /> |
|||
<Setter Property="SeparatorBrush" Value="{DynamicResource DataGridGridLinesBrush}" /> |
|||
<Setter Property="Padding" Value="12,0,0,0" /> |
|||
<Setter Property="FontSize" Value="12" /> |
|||
<Setter Property="MinHeight" Value="32" /> |
|||
<Setter Property="Template"> |
|||
<ControlTemplate> |
|||
<Grid Name="PART_ColumnHeaderRoot" |
|||
ColumnDefinitions="*,Auto" |
|||
Background="{TemplateBinding Background}"> |
|||
|
|||
<Grid HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" |
|||
VerticalAlignment="{TemplateBinding VerticalContentAlignment}" |
|||
Margin="{TemplateBinding Padding}"> |
|||
<Grid.ColumnDefinitions> |
|||
<ColumnDefinition Width="*" /> |
|||
<ColumnDefinition MinWidth="32" |
|||
Width="Auto" /> |
|||
</Grid.ColumnDefinitions> |
|||
|
|||
<ContentPresenter Content="{TemplateBinding Content}" /> |
|||
|
|||
<Path Name="SortIcon" |
|||
Grid.Column="1" |
|||
Fill="{TemplateBinding Foreground}" |
|||
HorizontalAlignment="Center" |
|||
VerticalAlignment="Center" |
|||
Stretch="Uniform" |
|||
Height="12" /> |
|||
</Grid> |
|||
|
|||
<Rectangle Name="VerticalSeparator" |
|||
Grid.Column="1" |
|||
Width="1" |
|||
VerticalAlignment="Stretch" |
|||
Fill="{TemplateBinding SeparatorBrush}" |
|||
IsVisible="{TemplateBinding AreSeparatorsVisible}" /> |
|||
|
|||
<Grid x:Name="FocusVisual" |
|||
IsHitTestVisible="False"> |
|||
<Rectangle x:Name="FocusVisualPrimary" |
|||
HorizontalAlignment="Stretch" |
|||
VerticalAlignment="Stretch" |
|||
Fill="Transparent" |
|||
IsHitTestVisible="False" |
|||
Stroke="{DynamicResource DataGridCellFocusVisualPrimaryBrush}" |
|||
StrokeThickness="2" /> |
|||
<Rectangle x:Name="FocusVisualSecondary" |
|||
Margin="2" |
|||
HorizontalAlignment="Stretch" |
|||
VerticalAlignment="Stretch" |
|||
Fill="Transparent" |
|||
IsHitTestVisible="False" |
|||
Stroke="{DynamicResource DataGridCellFocusVisualSecondaryBrush}" |
|||
StrokeThickness="1" /> |
|||
</Grid> |
|||
</Grid> |
|||
</ControlTemplate> |
|||
</Setter> |
|||
</Style> |
|||
|
|||
<Style Selector="DataGridColumnHeader /template/ Grid#FocusVisual"> |
|||
<Setter Property="IsVisible" Value="False" /> |
|||
</Style> |
|||
<Style Selector="DataGridColumnHeader:focus-visible /template/ Grid#FocusVisual"> |
|||
<Setter Property="IsVisible" Value="True" /> |
|||
</Style> |
|||
|
|||
<Style Selector="DataGridColumnHeader:pointerover /template/ Grid#PART_ColumnHeaderRoot"> |
|||
<Setter Property="Background" Value="{DynamicResource DataGridColumnHeaderHoveredBackgroundColor}" /> |
|||
</Style> |
|||
<Style Selector="DataGridColumnHeader:pressed /template/ Grid#PART_ColumnHeaderRoot"> |
|||
<Setter Property="Background" Value="{DynamicResource DataGridColumnHeaderPressedBackgroundColor}" /> |
|||
</Style> |
|||
|
|||
<Style Selector="DataGridColumnHeader:dragIndicator"> |
|||
<Setter Property="Opacity" Value="0.5" /> |
|||
</Style> |
|||
|
|||
<Style Selector="DataGridColumnHeader /template/ Path#SortIcon"> |
|||
<Setter Property="IsVisible" Value="False" /> |
|||
</Style> |
|||
|
|||
<Style Selector="DataGridColumnHeader:sortascending /template/ Path#SortIcon"> |
|||
<Setter Property="IsVisible" Value="True" /> |
|||
<Setter Property="Data" Value="{StaticResource DataGridSortIconAscendingPath}" /> |
|||
</Style> |
|||
|
|||
<Style Selector="DataGridColumnHeader:sortdescending /template/ Path#SortIcon"> |
|||
<Setter Property="IsVisible" Value="True" /> |
|||
<Setter Property="Data" Value="{StaticResource DataGridSortIconDescendingPath}" /> |
|||
</Style> |
|||
|
|||
<Style Selector="DataGridRow"> |
|||
<Setter Property="Focusable" Value="False" /> |
|||
<Setter Property="Template"> |
|||
<ControlTemplate> |
|||
<DataGridFrozenGrid Name="PART_Root" |
|||
RowDefinitions="*,Auto,Auto" |
|||
ColumnDefinitions="Auto,*"> |
|||
|
|||
<Rectangle Name="BackgroundRectangle" |
|||
Grid.RowSpan="2" |
|||
Grid.ColumnSpan="2" /> |
|||
<Rectangle x:Name="InvalidVisualElement" |
|||
Grid.ColumnSpan="2" |
|||
Fill="{DynamicResource DataGridRowInvalidBrush}" /> |
|||
|
|||
<DataGridRowHeader Name="PART_RowHeader" |
|||
Grid.RowSpan="3" |
|||
DataGridFrozenGrid.IsFrozen="True" /> |
|||
<DataGridCellsPresenter Name="PART_CellsPresenter" |
|||
Grid.Column="1" |
|||
DataGridFrozenGrid.IsFrozen="True" /> |
|||
<DataGridDetailsPresenter Name="PART_DetailsPresenter" |
|||
Grid.Row="1" |
|||
Grid.Column="1" |
|||
Background="{DynamicResource DataGridDetailsPresenterBackgroundBrush}" /> |
|||
<Rectangle Name="PART_BottomGridLine" |
|||
Grid.Row="2" |
|||
Grid.Column="1" |
|||
HorizontalAlignment="Stretch" |
|||
Height="1" /> |
|||
|
|||
</DataGridFrozenGrid> |
|||
</ControlTemplate> |
|||
</Setter> |
|||
</Style> |
|||
|
|||
<Style Selector="DataGridRow /template/ Rectangle#InvalidVisualElement"> |
|||
<Setter Property="Opacity" Value="0" /> |
|||
</Style> |
|||
<Style Selector="DataGridRow:invalid /template/ Rectangle#InvalidVisualElement"> |
|||
<Setter Property="Opacity" Value="0.4" /> |
|||
</Style> |
|||
<Style Selector="DataGridRow:invalid /template/ Rectangle#BackgroundRectangle"> |
|||
<Setter Property="Opacity" Value="0" /> |
|||
</Style> |
|||
|
|||
<Style Selector="DataGridRow /template/ Rectangle#BackgroundRectangle"> |
|||
<Setter Property="Fill" Value="{DynamicResource SystemControlTransparentBrush}" /> |
|||
</Style> |
|||
<Style Selector="DataGridRow:pointerover /template/ Rectangle#BackgroundRectangle"> |
|||
<Setter Property="Fill" Value="{DynamicResource SystemListLowColor}" /> |
|||
</Style> |
|||
<Style Selector="DataGridRow:selected /template/ Rectangle#BackgroundRectangle"> |
|||
<Setter Property="Fill" Value="{DynamicResource DataGridRowSelectedUnfocusedBackgroundColor}" /> |
|||
<Setter Property="Opacity" Value="{DynamicResource DataGridRowSelectedUnfocusedBackgroundOpacity}" /> |
|||
</Style> |
|||
<Style Selector="DataGridRow:selected:pointerover /template/ Rectangle#BackgroundRectangle"> |
|||
<Setter Property="Fill" Value="{DynamicResource DataGridRowSelectedHoveredUnfocusedBackgroundColor}" /> |
|||
<Setter Property="Opacity" Value="{DynamicResource DataGridRowSelectedHoveredUnfocusedBackgroundOpacity}" /> |
|||
</Style> |
|||
<Style Selector="DataGridRow:selected:focus /template/ Rectangle#BackgroundRectangle"> |
|||
<Setter Property="Fill" Value="{DynamicResource DataGridRowSelectedBackgroundColor}" /> |
|||
<Setter Property="Opacity" Value="{DynamicResource DataGridRowSelectedBackgroundOpacity}" /> |
|||
</Style> |
|||
<Style Selector="DataGridRow:selected:pointerover:focus /template/ Rectangle#BackgroundRectangle"> |
|||
<Setter Property="Fill" Value="{DynamicResource DataGridRowSelectedHoveredBackgroundColor}" /> |
|||
<Setter Property="Opacity" Value="{DynamicResource DataGridRowSelectedHoveredBackgroundOpacity}" /> |
|||
</Style> |
|||
|
|||
<Style Selector="DataGridRowHeader"> |
|||
<Setter Property="Background" Value="{DynamicResource DataGridRowHeaderBackgroundBrush}" /> |
|||
<Setter Property="Focusable" Value="False" /> |
|||
<Setter Property="SeparatorBrush" Value="{DynamicResource DataGridGridLinesBrush}" /> |
|||
<Setter Property="AreSeparatorsVisible" Value="False" /> |
|||
<Setter Property="Template"> |
|||
<ControlTemplate> |
|||
<Grid x:Name="PART_Root" |
|||
RowDefinitions="*,*,Auto" |
|||
ColumnDefinitions="Auto,*"> |
|||
<Border Grid.RowSpan="3" |
|||
Grid.ColumnSpan="2" |
|||
BorderBrush="{TemplateBinding SeparatorBrush}" |
|||
BorderThickness="0,0,1,0"> |
|||
<Grid Background="{TemplateBinding Background}"> |
|||
<Rectangle x:Name="RowInvalidVisualElement" |
|||
Fill="{DynamicResource DataGridRowInvalidBrush}" |
|||
Stretch="Fill" /> |
|||
<Rectangle x:Name="BackgroundRectangle" |
|||
Stretch="Fill" /> |
|||
</Grid> |
|||
</Border> |
|||
<Rectangle x:Name="HorizontalSeparator" |
|||
Grid.Row="2" |
|||
Grid.ColumnSpan="2" |
|||
Height="1" |
|||
Margin="1,0,1,0" |
|||
HorizontalAlignment="Stretch" |
|||
Fill="{TemplateBinding SeparatorBrush}" |
|||
IsVisible="{TemplateBinding AreSeparatorsVisible}" /> |
|||
|
|||
<ContentPresenter Grid.RowSpan="2" |
|||
Grid.Column="1" |
|||
HorizontalAlignment="Center" |
|||
VerticalAlignment="Center" |
|||
Content="{TemplateBinding Content}" /> |
|||
</Grid> |
|||
</ControlTemplate> |
|||
</Setter> |
|||
</Style> |
|||
|
|||
<Style Selector="DataGridRowHeader /template/ Rectangle#RowInvalidVisualElement"> |
|||
<Setter Property="Opacity" Value="0" /> |
|||
</Style> |
|||
<Style Selector="DataGridRowHeader:invalid /template/ Rectangle#RowInvalidVisualElement"> |
|||
<Setter Property="Opacity" Value="0.4" /> |
|||
</Style> |
|||
<Style Selector="DataGridRowHeader:invalid /template/ Rectangle#BackgroundRectangle"> |
|||
<Setter Property="Opacity" Value="0" /> |
|||
</Style> |
|||
|
|||
<Style Selector="DataGridRowHeader /template/ Rectangle#BackgroundRectangle"> |
|||
<Setter Property="Fill" Value="{DynamicResource SystemControlTransparentBrush}" /> |
|||
</Style> |
|||
<Style Selector="DataGridRow:pointerover DataGridRowHeader /template/ Rectangle#BackgroundRectangle"> |
|||
<Setter Property="Fill" Value="{DynamicResource SystemListLowColor}" /> |
|||
</Style> |
|||
<Style Selector="DataGridRowHeader:selected /template/ Rectangle#BackgroundRectangle"> |
|||
<Setter Property="Fill" Value="{DynamicResource DataGridRowSelectedUnfocusedBackgroundColor}" /> |
|||
<Setter Property="Opacity" Value="{DynamicResource DataGridRowSelectedUnfocusedBackgroundOpacity}" /> |
|||
</Style> |
|||
<Style Selector="DataGridRow:pointerover DataGridRowHeader:selected /template/ Rectangle#BackgroundRectangle"> |
|||
<Setter Property="Fill" Value="{DynamicResource DataGridRowSelectedHoveredUnfocusedBackgroundColor}" /> |
|||
<Setter Property="Opacity" Value="{DynamicResource DataGridRowSelectedHoveredUnfocusedBackgroundOpacity}" /> |
|||
</Style> |
|||
<Style Selector="DataGridRowHeader:selected:focus /template/ Rectangle#BackgroundRectangle"> |
|||
<Setter Property="Fill" Value="{DynamicResource DataGridRowSelectedBackgroundColor}" /> |
|||
<Setter Property="Opacity" Value="{DynamicResource DataGridRowSelectedBackgroundOpacity}" /> |
|||
</Style> |
|||
<Style Selector="DataGridRow:pointerover DataGridRowHeader:selected:focus /template/ Rectangle#BackgroundRectangle"> |
|||
<Setter Property="Fill" Value="{DynamicResource DataGridRowSelectedHoveredBackgroundColor}" /> |
|||
<Setter Property="Opacity" Value="{DynamicResource DataGridRowSelectedHoveredBackgroundOpacity}" /> |
|||
</Style> |
|||
|
|||
<Style Selector="DataGridRowGroupHeader"> |
|||
<Setter Property="Focusable" Value="False" /> |
|||
<Setter Property="Foreground" Value="{DynamicResource DataGridRowGroupHeaderForegroundBrush}" /> |
|||
<Setter Property="Background" Value="{DynamicResource DataGridRowGroupHeaderBackgroundBrush}" /> |
|||
<Setter Property="FontSize" Value="15" /> |
|||
<Setter Property="MinHeight" Value="32" /> |
|||
<Setter Property="Template"> |
|||
<ControlTemplate> |
|||
<DataGridFrozenGrid Name="PART_Root" |
|||
MinHeight="{TemplateBinding MinHeight}" |
|||
ColumnDefinitions="Auto,Auto,Auto,Auto,*" |
|||
RowDefinitions="*,Auto"> |
|||
|
|||
<Rectangle Name="IndentSpacer" |
|||
Grid.Column="1" /> |
|||
<ToggleButton Name="ExpanderButton" |
|||
Grid.Column="2" |
|||
Width="12" |
|||
Height="12" |
|||
Margin="12,0,0,0" |
|||
Background="{TemplateBinding Background}" |
|||
Foreground="{TemplateBinding Foreground}" |
|||
Focusable="False" /> |
|||
|
|||
<StackPanel Grid.Column="3" |
|||
Orientation="Horizontal" |
|||
VerticalAlignment="Center" |
|||
Margin="12,0,0,0"> |
|||
<TextBlock Name="PropertyNameElement" |
|||
Margin="4,0,0,0" |
|||
IsVisible="{TemplateBinding IsPropertyNameVisible}" |
|||
Foreground="{TemplateBinding Foreground}" /> |
|||
<TextBlock Margin="4,0,0,0" |
|||
Text="{Binding Key}" |
|||
Foreground="{TemplateBinding Foreground}" /> |
|||
<TextBlock Name="ItemCountElement" |
|||
Margin="4,0,0,0" |
|||
IsVisible="{TemplateBinding IsItemCountVisible}" |
|||
Foreground="{TemplateBinding Foreground}" /> |
|||
</StackPanel> |
|||
|
|||
<Rectangle x:Name="CurrencyVisual" |
|||
Grid.ColumnSpan="5" |
|||
HorizontalAlignment="Stretch" |
|||
VerticalAlignment="Stretch" |
|||
Fill="Transparent" |
|||
IsHitTestVisible="False" |
|||
Stroke="{DynamicResource DataGridCurrencyVisualPrimaryBrush}" |
|||
StrokeThickness="1" /> |
|||
<Grid x:Name="FocusVisual" |
|||
Grid.ColumnSpan="5" |
|||
IsHitTestVisible="False"> |
|||
<Rectangle HorizontalAlignment="Stretch" |
|||
VerticalAlignment="Stretch" |
|||
Fill="Transparent" |
|||
IsHitTestVisible="False" |
|||
Stroke="{DynamicResource DataGridCellFocusVisualPrimaryBrush}" |
|||
StrokeThickness="2" /> |
|||
<Rectangle Margin="2" |
|||
HorizontalAlignment="Stretch" |
|||
VerticalAlignment="Stretch" |
|||
Fill="Transparent" |
|||
IsHitTestVisible="False" |
|||
Stroke="{DynamicResource DataGridCellFocusVisualSecondaryBrush}" |
|||
StrokeThickness="1" /> |
|||
</Grid> |
|||
|
|||
<DataGridRowHeader Name="PART_RowHeader" |
|||
Grid.RowSpan="2" |
|||
DataGridFrozenGrid.IsFrozen="True" /> |
|||
|
|||
<Rectangle x:Name="PART_BottomGridLine" |
|||
Grid.Row="1" |
|||
Grid.ColumnSpan="5" |
|||
Height="1" /> |
|||
</DataGridFrozenGrid> |
|||
</ControlTemplate> |
|||
</Setter> |
|||
</Style> |
|||
|
|||
<Style Selector="DataGridRowGroupHeader /template/ ToggleButton#ExpanderButton"> |
|||
<Setter Property="Template"> |
|||
<ControlTemplate> |
|||
<Border Grid.Column="0" |
|||
Width="12" |
|||
Height="12" |
|||
Background="Transparent" |
|||
HorizontalAlignment="Center" |
|||
VerticalAlignment="Center"> |
|||
<Path Fill="{TemplateBinding Foreground}" |
|||
HorizontalAlignment="Right" |
|||
VerticalAlignment="Center" |
|||
Stretch="Uniform" /> |
|||
</Border> |
|||
</ControlTemplate> |
|||
</Setter> |
|||
</Style> |
|||
|
|||
<Style Selector="DataGridRowGroupHeader /template/ ToggleButton#ExpanderButton /template/ Path"> |
|||
<Setter Property="Data" Value="{StaticResource DataGridRowGroupHeaderIconOpenedPath}" /> |
|||
<Setter Property="Stretch" Value="Uniform" /> |
|||
</Style> |
|||
|
|||
<Style Selector="DataGridRowGroupHeader /template/ ToggleButton#ExpanderButton:checked /template/ Path"> |
|||
<Setter Property="Data" Value="{StaticResource DataGridRowGroupHeaderIconClosedPath}" /> |
|||
<Setter Property="Stretch" Value="UniformToFill" /> |
|||
</Style> |
|||
|
|||
<Style Selector="DataGridRowGroupHeader /template/ DataGridFrozenGrid#PART_Root"> |
|||
<Setter Property="Background" Value="{Binding $parent[DataGridRowGroupHeader].Background}" /> |
|||
</Style> |
|||
<Style Selector="DataGridRowGroupHeader:pointerover /template/ DataGridFrozenGrid#PART_Root"> |
|||
<Setter Property="Background" Value="{DynamicResource DataGridRowGroupHeaderHoveredBackgroundBrush}" /> |
|||
</Style> |
|||
<Style Selector="DataGridRowGroupHeader:pressed /template/ DataGridFrozenGrid#PART_Root"> |
|||
<Setter Property="Background" Value="{DynamicResource DataGridRowGroupHeaderPressedBackgroundBrush}" /> |
|||
</Style> |
|||
|
|||
<Style Selector="DataGridRowGroupHeader /template/ Rectangle#CurrencyVisual"> |
|||
<Setter Property="IsVisible" Value="False" /> |
|||
</Style> |
|||
<Style Selector="DataGridRowGroupHeader /template/ Grid#FocusVisual"> |
|||
<Setter Property="IsVisible" Value="False" /> |
|||
</Style> |
|||
<Style Selector="DataGridRowGroupHeader:current /template/ Rectangle#CurrencyVisual"> |
|||
<Setter Property="IsVisible" Value="True" /> |
|||
</Style> |
|||
<Style Selector="DataGrid:focus DataGridRowGroupHeader:current /template/ Grid#FocusVisual"> |
|||
<Setter Property="IsVisible" Value="True" /> |
|||
</Style> |
|||
|
|||
<Style Selector="DataGrid"> |
|||
<Setter Property="RowBackground" Value="Transparent" /> |
|||
<Setter Property="AlternatingRowBackground" Value="Transparent" /> |
|||
<Setter Property="HeadersVisibility" Value="Column" /> |
|||
<Setter Property="HorizontalScrollBarVisibility" Value="Auto" /> |
|||
<Setter Property="VerticalScrollBarVisibility" Value="Auto" /> |
|||
<Setter Property="SelectionMode" Value="Extended" /> |
|||
<Setter Property="GridLinesVisibility" Value="None" /> |
|||
<Setter Property="HorizontalGridLinesBrush" Value="{DynamicResource DataGridGridLinesBrush}" /> |
|||
<Setter Property="VerticalGridLinesBrush" Value="{DynamicResource DataGridGridLinesBrush}" /> |
|||
<Setter Property="DropLocationIndicatorTemplate"> |
|||
<Template> |
|||
<Rectangle Fill="{DynamicResource DataGridDropLocationIndicatorBackground}" |
|||
Width="2" /> |
|||
</Template> |
|||
</Setter> |
|||
<Setter Property="Template"> |
|||
<ControlTemplate> |
|||
<Border Background="{TemplateBinding Background}" |
|||
BorderThickness="{TemplateBinding BorderThickness}" |
|||
BorderBrush="{TemplateBinding BorderBrush}"> |
|||
<Grid RowDefinitions="Auto,*,Auto,Auto" |
|||
ColumnDefinitions="Auto,*,Auto"> |
|||
<Grid.Resources> |
|||
<ControlTemplate x:Key="TopLeftHeaderTemplate" |
|||
TargetType="DataGridColumnHeader"> |
|||
<Grid x:Name="TopLeftHeaderRoot" |
|||
RowDefinitions="*,*,Auto"> |
|||
<Border Grid.RowSpan="2" |
|||
BorderThickness="0,0,1,0" |
|||
BorderBrush="{DynamicResource DataGridGridLinesBrush}" /> |
|||
<Rectangle Grid.RowSpan="2" |
|||
VerticalAlignment="Bottom" |
|||
StrokeThickness="1" |
|||
Height="1" |
|||
Fill="{DynamicResource DataGridGridLinesBrush}" /> |
|||
</Grid> |
|||
</ControlTemplate> |
|||
<ControlTemplate x:Key="TopRightHeaderTemplate" |
|||
TargetType="DataGridColumnHeader"> |
|||
<Grid x:Name="RootElement" /> |
|||
</ControlTemplate> |
|||
</Grid.Resources> |
|||
|
|||
<DataGridColumnHeader Name="PART_TopLeftCornerHeader" |
|||
Template="{StaticResource TopLeftHeaderTemplate}" /> |
|||
<DataGridColumnHeadersPresenter Name="PART_ColumnHeadersPresenter" |
|||
Grid.Column="1" |
|||
Grid.ColumnSpan="2" /> |
|||
<!--<DataGridColumnHeader Name="PART_TopRightCornerHeader" |
|||
Grid.Column="2" |
|||
Template="{StaticResource TopRightHeaderTemplate}" />--> |
|||
<!--<Rectangle Name="PART_ColumnHeadersAndRowsSeparator" |
|||
Grid.ColumnSpan="3" |
|||
VerticalAlignment="Bottom" |
|||
StrokeThickness="1" |
|||
Height="1" |
|||
Fill="{DynamicResource DataGridGridLinesBrush}" />--> |
|||
<Border Name="PART_ColumnHeadersAndRowsSeparator" |
|||
Grid.ColumnSpan="3" |
|||
Height="2" |
|||
VerticalAlignment="Bottom" |
|||
BorderThickness="0,0,0,1" |
|||
BorderBrush="{DynamicResource DataGridGridLinesBrush}" /> |
|||
|
|||
<DataGridRowsPresenter Name="PART_RowsPresenter" |
|||
Grid.Row="1" |
|||
Grid.RowSpan="2" |
|||
Grid.ColumnSpan="3" /> |
|||
<Rectangle Name="PART_BottomRightCorner" |
|||
Fill="{DynamicResource DataGridScrollBarsSeparatorBackground}" |
|||
Grid.Column="2" |
|||
Grid.Row="2" /> |
|||
<!--<Rectangle Name="BottomLeftCorner" |
|||
Fill="{DynamicResource DataGridScrollBarsSeparatorBackground}" |
|||
Grid.Row="2" |
|||
Grid.ColumnSpan="2" />--> |
|||
<ScrollBar Name="PART_VerticalScrollbar" |
|||
Orientation="Vertical" |
|||
Grid.Column="2" |
|||
Grid.Row="1" |
|||
Width="{DynamicResource ScrollBarSize}" /> |
|||
|
|||
<Grid Grid.Column="1" |
|||
Grid.Row="2" |
|||
ColumnDefinitions="Auto,*"> |
|||
<Rectangle Name="PART_FrozenColumnScrollBarSpacer" /> |
|||
<ScrollBar Name="PART_HorizontalScrollbar" |
|||
Grid.Column="1" |
|||
Orientation="Horizontal" |
|||
Height="{DynamicResource ScrollBarSize}" /> |
|||
</Grid> |
|||
<Border x:Name="PART_DisabledVisualElement" |
|||
Grid.ColumnSpan="3" |
|||
Grid.RowSpan="4" |
|||
IsHitTestVisible="False" |
|||
HorizontalAlignment="Stretch" |
|||
VerticalAlignment="Stretch" |
|||
CornerRadius="2" |
|||
Background="{DynamicResource DataGridDisabledVisualElementBackground}" |
|||
IsVisible="{Binding !$parent[DataGrid].IsEnabled}" /> |
|||
</Grid> |
|||
</Border> |
|||
</ControlTemplate> |
|||
</Setter> |
|||
</Style> |
|||
</Styles> |
|||
@ -1,7 +1,18 @@ |
|||
Compat issues with assembly Avalonia.Controls: |
|||
MembersMustExist : Member 'protected void Avalonia.Controls.ComboBox.PopupClosedOverride(Avalonia.Controls.Primitives.PopupClosedEventArgs)' does not exist in the implementation but it does exist in the contract. |
|||
MembersMustExist : Member 'public Avalonia.StyledProperty<System.Boolean> Avalonia.StyledProperty<System.Boolean> Avalonia.Controls.Primitives.Popup.StaysOpenProperty' does not exist in the implementation but it does exist in the contract. |
|||
MembersMustExist : Member 'public void Avalonia.Controls.Primitives.Popup.add_Closed(System.EventHandler<Avalonia.Controls.Primitives.PopupClosedEventArgs>)' does not exist in the implementation but it does exist in the contract. |
|||
MembersMustExist : Member 'public void Avalonia.Controls.Primitives.Popup.remove_Closed(System.EventHandler<Avalonia.Controls.Primitives.PopupClosedEventArgs>)' does not exist in the implementation but it does exist in the contract. |
|||
TypesMustExist : Type 'Avalonia.Controls.Primitives.PopupClosedEventArgs' does not exist in the implementation but it does exist in the contract. |
|||
Total Issues: 5 |
|||
TypesMustExist : Type 'Avalonia.Controls.IndexPath' does not exist in the implementation but it does exist in the contract. |
|||
TypesMustExist : Type 'Avalonia.Controls.ISelectedItemInfo' does not exist in the implementation but it does exist in the contract. |
|||
TypesMustExist : Type 'Avalonia.Controls.ISelectionModel' does not exist in the implementation but it does exist in the contract. |
|||
MembersMustExist : Member 'public Avalonia.DirectProperty<Avalonia.Controls.Primitives.SelectingItemsControl, Avalonia.Controls.ISelectionModel> Avalonia.DirectProperty<Avalonia.Controls.Primitives.SelectingItemsControl, Avalonia.Controls.ISelectionModel> Avalonia.Controls.ListBox.SelectionProperty' does not exist in the implementation but it does exist in the contract. |
|||
MembersMustExist : Member 'public Avalonia.Controls.ISelectionModel Avalonia.Controls.ListBox.Selection.get()' does not exist in the implementation but it does exist in the contract. |
|||
MembersMustExist : Member 'public void Avalonia.Controls.ListBox.Selection.set(Avalonia.Controls.ISelectionModel)' does not exist in the implementation but it does exist in the contract. |
|||
TypesMustExist : Type 'Avalonia.Controls.SelectionModel' does not exist in the implementation but it does exist in the contract. |
|||
TypesMustExist : Type 'Avalonia.Controls.SelectionModelChildrenRequestedEventArgs' does not exist in the implementation but it does exist in the contract. |
|||
TypesMustExist : Type 'Avalonia.Controls.SelectionModelSelectionChangedEventArgs' does not exist in the implementation but it does exist in the contract. |
|||
MembersMustExist : Member 'public Avalonia.DirectProperty<Avalonia.Controls.TreeView, Avalonia.Controls.ISelectionModel> Avalonia.DirectProperty<Avalonia.Controls.TreeView, Avalonia.Controls.ISelectionModel> Avalonia.Controls.TreeView.SelectionProperty' does not exist in the implementation but it does exist in the contract. |
|||
MembersMustExist : Member 'public Avalonia.Interactivity.RoutedEvent<Avalonia.Controls.SelectionChangedEventArgs> Avalonia.Interactivity.RoutedEvent<Avalonia.Controls.SelectionChangedEventArgs> Avalonia.Controls.TreeView.SelectionChangedEvent' does not exist in the implementation but it does exist in the contract. |
|||
MembersMustExist : Member 'public Avalonia.Controls.ISelectionModel Avalonia.Controls.TreeView.Selection.get()' does not exist in the implementation but it does exist in the contract. |
|||
MembersMustExist : Member 'public void Avalonia.Controls.TreeView.Selection.set(Avalonia.Controls.ISelectionModel)' does not exist in the implementation but it does exist in the contract. |
|||
MembersMustExist : Member 'public Avalonia.DirectProperty<Avalonia.Controls.Primitives.SelectingItemsControl, Avalonia.Controls.ISelectionModel> Avalonia.DirectProperty<Avalonia.Controls.Primitives.SelectingItemsControl, Avalonia.Controls.ISelectionModel> Avalonia.Controls.Primitives.SelectingItemsControl.SelectionProperty' does not exist in the implementation but it does exist in the contract. |
|||
MembersMustExist : Member 'protected Avalonia.Controls.ISelectionModel Avalonia.Controls.Primitives.SelectingItemsControl.Selection.get()' does not exist in the implementation but it does exist in the contract. |
|||
MembersMustExist : Member 'protected void Avalonia.Controls.Primitives.SelectingItemsControl.Selection.set(Avalonia.Controls.ISelectionModel)' does not exist in the implementation but it does exist in the contract. |
|||
Total Issues: 16 |
|||
|
|||
@ -1,249 +0,0 @@ |
|||
// This source file is adapted from the WinUI project.
|
|||
// (https://github.com/microsoft/microsoft-ui-xaml)
|
|||
//
|
|||
// Licensed to The Avalonia Project under MIT License, courtesy of The .NET Foundation.
|
|||
|
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.ComponentModel; |
|||
|
|||
namespace Avalonia.Controls |
|||
{ |
|||
/// <summary>
|
|||
/// Holds the selected items for a control.
|
|||
/// </summary>
|
|||
public interface ISelectionModel : INotifyPropertyChanged |
|||
{ |
|||
/// <summary>
|
|||
/// Gets or sets the anchor index.
|
|||
/// </summary>
|
|||
IndexPath AnchorIndex { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// Gets or set the index of the first selected item.
|
|||
/// </summary>
|
|||
IndexPath SelectedIndex { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// Gets or set the indexes of the selected items.
|
|||
/// </summary>
|
|||
IReadOnlyList<IndexPath> SelectedIndices { get; } |
|||
|
|||
/// <summary>
|
|||
/// Gets the first selected item.
|
|||
/// </summary>
|
|||
object SelectedItem { get; } |
|||
|
|||
/// <summary>
|
|||
/// Gets the selected items.
|
|||
/// </summary>
|
|||
IReadOnlyList<object> SelectedItems { get; } |
|||
|
|||
/// <summary>
|
|||
/// Gets a value indicating whether the model represents a single or multiple selection.
|
|||
/// </summary>
|
|||
bool SingleSelect { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// Gets a value indicating whether to always keep an item selected where possible.
|
|||
/// </summary>
|
|||
bool AutoSelect { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets the collection that contains the items that can be selected.
|
|||
/// </summary>
|
|||
object Source { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// Raised when the children of a selection are required.
|
|||
/// </summary>
|
|||
event EventHandler<SelectionModelChildrenRequestedEventArgs> ChildrenRequested; |
|||
|
|||
/// <summary>
|
|||
/// Raised when the selection has changed.
|
|||
/// </summary>
|
|||
event EventHandler<SelectionModelSelectionChangedEventArgs> SelectionChanged; |
|||
|
|||
/// <summary>
|
|||
/// Clears the selection.
|
|||
/// </summary>
|
|||
void ClearSelection(); |
|||
|
|||
/// <summary>
|
|||
/// Deselects an item.
|
|||
/// </summary>
|
|||
/// <param name="index">The index of the item.</param>
|
|||
void Deselect(int index); |
|||
|
|||
/// <summary>
|
|||
/// Deselects an item.
|
|||
/// </summary>
|
|||
/// <param name="groupIndex">The index of the item group.</param>
|
|||
/// <param name="itemIndex">The index of the item in the group.</param>
|
|||
void Deselect(int groupIndex, int itemIndex); |
|||
|
|||
/// <summary>
|
|||
/// Deselects an item.
|
|||
/// </summary>
|
|||
/// <param name="index">The index of the item.</param>
|
|||
void DeselectAt(IndexPath index); |
|||
|
|||
/// <summary>
|
|||
/// Deselects a range of items.
|
|||
/// </summary>
|
|||
/// <param name="start">The start index of the range.</param>
|
|||
/// <param name="end">The end index of the range.</param>
|
|||
void DeselectRange(IndexPath start, IndexPath end); |
|||
|
|||
/// <summary>
|
|||
/// Deselects a range of items, starting at <see cref="AnchorIndex"/>.
|
|||
/// </summary>
|
|||
/// <param name="index">The end index of the range.</param>
|
|||
void DeselectRangeFromAnchor(int index); |
|||
|
|||
/// <summary>
|
|||
/// Deselects a range of items, starting at <see cref="AnchorIndex"/>.
|
|||
/// </summary>
|
|||
/// <param name="endGroupIndex">
|
|||
/// The index of the item group that represents the end of the selection.
|
|||
/// </param>
|
|||
/// <param name="endItemIndex">
|
|||
/// The index of the item in the group that represents the end of the selection.
|
|||
/// </param>
|
|||
void DeselectRangeFromAnchor(int endGroupIndex, int endItemIndex); |
|||
|
|||
/// <summary>
|
|||
/// Deselects a range of items, starting at <see cref="AnchorIndex"/>.
|
|||
/// </summary>
|
|||
/// <param name="index">The end index of the range.</param>
|
|||
void DeselectRangeFromAnchorTo(IndexPath index); |
|||
|
|||
/// <summary>
|
|||
/// Disposes the object and clears the selection.
|
|||
/// </summary>
|
|||
void Dispose(); |
|||
|
|||
/// <summary>
|
|||
/// Checks whether an item is selected.
|
|||
/// </summary>
|
|||
/// <param name="index">The index of the item</param>
|
|||
bool IsSelected(int index); |
|||
|
|||
/// <summary>
|
|||
/// Checks whether an item is selected.
|
|||
/// </summary>
|
|||
/// <param name="groupIndex">The index of the item group.</param>
|
|||
/// <param name="itemIndex">The index of the item in the group.</param>
|
|||
bool IsSelected(int groupIndex, int itemIndex); |
|||
|
|||
/// <summary>
|
|||
/// Checks whether an item is selected.
|
|||
/// </summary>
|
|||
/// <param name="index">The index of the item</param>
|
|||
public bool IsSelectedAt(IndexPath index); |
|||
|
|||
/// <summary>
|
|||
/// Checks whether an item or its descendents are selected.
|
|||
/// </summary>
|
|||
/// <param name="index">The index of the item</param>
|
|||
/// <returns>
|
|||
/// True if the item and all its descendents are selected, false if the item and all its
|
|||
/// descendents are deselected, or null if a combination of selected and deselected.
|
|||
/// </returns>
|
|||
bool? IsSelectedWithPartial(int index); |
|||
|
|||
/// <summary>
|
|||
/// Checks whether an item or its descendents are selected.
|
|||
/// </summary>
|
|||
/// <param name="groupIndex">The index of the item group.</param>
|
|||
/// <param name="itemIndex">The index of the item in the group.</param>
|
|||
/// <returns>
|
|||
/// True if the item and all its descendents are selected, false if the item and all its
|
|||
/// descendents are deselected, or null if a combination of selected and deselected.
|
|||
/// </returns>
|
|||
bool? IsSelectedWithPartial(int groupIndex, int itemIndex); |
|||
|
|||
/// <summary>
|
|||
/// Checks whether an item or its descendents are selected.
|
|||
/// </summary>
|
|||
/// <param name="index">The index of the item</param>
|
|||
/// <returns>
|
|||
/// True if the item and all its descendents are selected, false if the item and all its
|
|||
/// descendents are deselected, or null if a combination of selected and deselected.
|
|||
/// </returns>
|
|||
bool? IsSelectedWithPartialAt(IndexPath index); |
|||
|
|||
/// <summary>
|
|||
/// Selects an item.
|
|||
/// </summary>
|
|||
/// <param name="index">The index of the item</param>
|
|||
void Select(int index); |
|||
|
|||
/// <summary>
|
|||
/// Selects an item.
|
|||
/// </summary>
|
|||
/// <param name="groupIndex">The index of the item group.</param>
|
|||
/// <param name="itemIndex">The index of the item in the group.</param>
|
|||
void Select(int groupIndex, int itemIndex); |
|||
|
|||
/// <summary>
|
|||
/// Selects an item.
|
|||
/// </summary>
|
|||
/// <param name="index">The index of the item</param>
|
|||
void SelectAt(IndexPath index); |
|||
|
|||
/// <summary>
|
|||
/// Selects all items.
|
|||
/// </summary>
|
|||
void SelectAll(); |
|||
|
|||
/// <summary>
|
|||
/// Selects a range of items.
|
|||
/// </summary>
|
|||
/// <param name="start">The start index of the range.</param>
|
|||
/// <param name="end">The end index of the range.</param>
|
|||
void SelectRange(IndexPath start, IndexPath end); |
|||
|
|||
/// <summary>
|
|||
/// Selects a range of items, starting at <see cref="AnchorIndex"/>.
|
|||
/// </summary>
|
|||
/// <param name="index">The end index of the range.</param>
|
|||
void SelectRangeFromAnchor(int index); |
|||
|
|||
/// <summary>
|
|||
/// Selects a range of items, starting at <see cref="AnchorIndex"/>.
|
|||
/// </summary>
|
|||
/// <param name="endGroupIndex">
|
|||
/// The index of the item group that represents the end of the selection.
|
|||
/// </param>
|
|||
/// <param name="endItemIndex">
|
|||
/// The index of the item in the group that represents the end of the selection.
|
|||
/// </param>
|
|||
void SelectRangeFromAnchor(int endGroupIndex, int endItemIndex); |
|||
|
|||
/// <summary>
|
|||
/// Selects a range of items, starting at <see cref="AnchorIndex"/>.
|
|||
/// </summary>
|
|||
/// <param name="index">The end index of the range.</param>
|
|||
void SelectRangeFromAnchorTo(IndexPath index); |
|||
|
|||
/// <summary>
|
|||
/// Sets the <see cref="AnchorIndex"/>.
|
|||
/// </summary>
|
|||
/// <param name="index">The anchor index.</param>
|
|||
void SetAnchorIndex(int index); |
|||
|
|||
/// <summary>
|
|||
/// Sets the <see cref="AnchorIndex"/>.
|
|||
/// </summary>
|
|||
/// <param name="groupIndex">The index of the item group.</param>
|
|||
/// <param name="index">The index of the item in the group.</param>
|
|||
void SetAnchorIndex(int groupIndex, int index); |
|||
|
|||
/// <summary>
|
|||
/// Begins a batch update of the selection.
|
|||
/// </summary>
|
|||
/// <returns>An <see cref="IDisposable"/> that finishes the batch update.</returns>
|
|||
IDisposable Update(); |
|||
} |
|||
} |
|||
@ -1,200 +0,0 @@ |
|||
// This source file is adapted from the WinUI project.
|
|||
// (https://github.com/microsoft/microsoft-ui-xaml)
|
|||
//
|
|||
// Licensed to The Avalonia Project under MIT License, courtesy of The .NET Foundation.
|
|||
|
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
|
|||
#nullable enable |
|||
|
|||
namespace Avalonia.Controls |
|||
{ |
|||
public readonly struct IndexPath : IComparable<IndexPath>, IEquatable<IndexPath> |
|||
{ |
|||
public static readonly IndexPath Unselected = default; |
|||
|
|||
private readonly int _index; |
|||
private readonly int[]? _path; |
|||
|
|||
public IndexPath(int index) |
|||
{ |
|||
_index = index + 1; |
|||
_path = null; |
|||
} |
|||
|
|||
public IndexPath(int groupIndex, int itemIndex) |
|||
{ |
|||
_index = 0; |
|||
_path = new[] { groupIndex, itemIndex }; |
|||
} |
|||
|
|||
public IndexPath(IEnumerable<int>? indices) |
|||
{ |
|||
if (indices != null) |
|||
{ |
|||
_index = 0; |
|||
_path = indices.ToArray(); |
|||
} |
|||
else |
|||
{ |
|||
_index = 0; |
|||
_path = null; |
|||
} |
|||
} |
|||
|
|||
private IndexPath(int[] basePath, int index) |
|||
{ |
|||
basePath = basePath ?? throw new ArgumentNullException(nameof(basePath)); |
|||
|
|||
_index = 0; |
|||
_path = new int[basePath.Length + 1]; |
|||
Array.Copy(basePath, _path, basePath.Length); |
|||
_path[basePath.Length] = index; |
|||
} |
|||
|
|||
public int GetSize() => _path?.Length ?? (_index == 0 ? 0 : 1); |
|||
|
|||
public int GetAt(int index) |
|||
{ |
|||
if (index >= GetSize()) |
|||
{ |
|||
throw new IndexOutOfRangeException(); |
|||
} |
|||
|
|||
return _path?[index] ?? (_index - 1); |
|||
} |
|||
|
|||
public int CompareTo(IndexPath other) |
|||
{ |
|||
var rhsPath = other; |
|||
int compareResult = 0; |
|||
int lhsCount = GetSize(); |
|||
int rhsCount = rhsPath.GetSize(); |
|||
|
|||
if (lhsCount == 0 || rhsCount == 0) |
|||
{ |
|||
// one of the paths are empty, compare based on size
|
|||
compareResult = (lhsCount - rhsCount); |
|||
} |
|||
else |
|||
{ |
|||
// both paths are non-empty, but can be of different size
|
|||
for (int i = 0; i < Math.Min(lhsCount, rhsCount); i++) |
|||
{ |
|||
if (GetAt(i) < rhsPath.GetAt(i)) |
|||
{ |
|||
compareResult = -1; |
|||
break; |
|||
} |
|||
else if (GetAt(i) > rhsPath.GetAt(i)) |
|||
{ |
|||
compareResult = 1; |
|||
break; |
|||
} |
|||
} |
|||
|
|||
// if both match upto min(lhsCount, rhsCount), compare based on size
|
|||
compareResult = compareResult == 0 ? (lhsCount - rhsCount) : compareResult; |
|||
} |
|||
|
|||
if (compareResult != 0) |
|||
{ |
|||
compareResult = compareResult > 0 ? 1 : -1; |
|||
} |
|||
|
|||
return compareResult; |
|||
} |
|||
|
|||
public IndexPath CloneWithChildIndex(int childIndex) |
|||
{ |
|||
if (_path != null) |
|||
{ |
|||
return new IndexPath(_path, childIndex); |
|||
} |
|||
else if (_index != 0) |
|||
{ |
|||
return new IndexPath(_index - 1, childIndex); |
|||
} |
|||
else |
|||
{ |
|||
return new IndexPath(childIndex); |
|||
} |
|||
} |
|||
|
|||
public bool IsAncestorOf(in IndexPath other) |
|||
{ |
|||
if (other.GetSize() <= GetSize()) |
|||
{ |
|||
return false; |
|||
} |
|||
|
|||
var size = GetSize(); |
|||
|
|||
for (int i = 0; i < size; i++) |
|||
{ |
|||
if (GetAt(i) != other.GetAt(i)) |
|||
{ |
|||
return false; |
|||
} |
|||
} |
|||
|
|||
return true; |
|||
} |
|||
|
|||
public override string ToString() |
|||
{ |
|||
if (_path != null) |
|||
{ |
|||
return "R" + string.Join(".", _path); |
|||
} |
|||
else if (_index != 0) |
|||
{ |
|||
return "R" + (_index - 1); |
|||
} |
|||
else |
|||
{ |
|||
return "R"; |
|||
} |
|||
} |
|||
|
|||
public static IndexPath CreateFrom(int index) => new IndexPath(index); |
|||
|
|||
public static IndexPath CreateFrom(int groupIndex, int itemIndex) => new IndexPath(groupIndex, itemIndex); |
|||
|
|||
public static IndexPath CreateFromIndices(IList<int> indices) => new IndexPath(indices); |
|||
|
|||
public override bool Equals(object obj) => obj is IndexPath other && Equals(other); |
|||
|
|||
public bool Equals(IndexPath other) => CompareTo(other) == 0; |
|||
|
|||
public override int GetHashCode() |
|||
{ |
|||
var hashCode = -504981047; |
|||
|
|||
if (_path != null) |
|||
{ |
|||
foreach (var i in _path) |
|||
{ |
|||
hashCode = hashCode * -1521134295 + i.GetHashCode(); |
|||
} |
|||
} |
|||
else |
|||
{ |
|||
hashCode = hashCode * -1521134295 + _index.GetHashCode(); |
|||
} |
|||
|
|||
return hashCode; |
|||
} |
|||
|
|||
public static bool operator <(IndexPath x, IndexPath y) { return x.CompareTo(y) < 0; } |
|||
public static bool operator >(IndexPath x, IndexPath y) { return x.CompareTo(y) > 0; } |
|||
public static bool operator <=(IndexPath x, IndexPath y) { return x.CompareTo(y) <= 0; } |
|||
public static bool operator >=(IndexPath x, IndexPath y) { return x.CompareTo(y) >= 0; } |
|||
public static bool operator ==(IndexPath x, IndexPath y) { return x.CompareTo(y) == 0; } |
|||
public static bool operator !=(IndexPath x, IndexPath y) { return x.CompareTo(y) != 0; } |
|||
public static bool operator ==(IndexPath? x, IndexPath? y) { return (x ?? default).CompareTo(y ?? default) == 0; } |
|||
public static bool operator !=(IndexPath? x, IndexPath? y) { return (x ?? default).CompareTo(y ?? default) != 0; } |
|||
} |
|||
} |
|||
@ -1,49 +0,0 @@ |
|||
// This source file is adapted from the WinUI project.
|
|||
// (https://github.com/microsoft/microsoft-ui-xaml)
|
|||
//
|
|||
// Licensed to The Avalonia Project under MIT License, courtesy of The .NET Foundation.
|
|||
|
|||
using System; |
|||
using System.Collections; |
|||
using System.Collections.Generic; |
|||
|
|||
#nullable enable |
|||
|
|||
namespace Avalonia.Controls |
|||
{ |
|||
public interface ISelectedItemInfo |
|||
{ |
|||
public IndexPath Path { get; } |
|||
} |
|||
|
|||
internal class SelectedItems<TValue, Tinfo> : IReadOnlyList<TValue> |
|||
where Tinfo : ISelectedItemInfo |
|||
{ |
|||
private readonly List<Tinfo> _infos; |
|||
private readonly Func<List<Tinfo>, int, TValue> _getAtImpl; |
|||
|
|||
public SelectedItems( |
|||
List<Tinfo> infos, |
|||
int count, |
|||
Func<List<Tinfo>, int, TValue> getAtImpl) |
|||
{ |
|||
_infos = infos; |
|||
_getAtImpl = getAtImpl; |
|||
Count = count; |
|||
} |
|||
|
|||
public TValue this[int index] => _getAtImpl(_infos, index); |
|||
|
|||
public int Count { get; } |
|||
|
|||
public IEnumerator<TValue> GetEnumerator() |
|||
{ |
|||
for (var i = 0; i < Count; ++i) |
|||
{ |
|||
yield return this[i]; |
|||
} |
|||
} |
|||
|
|||
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); |
|||
} |
|||
} |
|||
@ -0,0 +1,66 @@ |
|||
using System; |
|||
using System.Collections; |
|||
using System.Collections.Generic; |
|||
using System.ComponentModel; |
|||
|
|||
#nullable enable |
|||
|
|||
namespace Avalonia.Controls.Selection |
|||
{ |
|||
public interface ISelectionModel : INotifyPropertyChanged |
|||
{ |
|||
IEnumerable? Source { get; set; } |
|||
bool SingleSelect { get; set; } |
|||
int SelectedIndex { get; set; } |
|||
IReadOnlyList<int> SelectedIndexes { get; } |
|||
object? SelectedItem { get; set; } |
|||
IReadOnlyList<object?> SelectedItems { get; } |
|||
int AnchorIndex { get; set; } |
|||
int Count { get; } |
|||
|
|||
public event EventHandler<SelectionModelIndexesChangedEventArgs>? IndexesChanged; |
|||
public event EventHandler<SelectionModelSelectionChangedEventArgs>? SelectionChanged; |
|||
public event EventHandler? LostSelection; |
|||
public event EventHandler? SourceReset; |
|||
|
|||
public void BeginBatchUpdate(); |
|||
public void EndBatchUpdate(); |
|||
bool IsSelected(int index); |
|||
void Select(int index); |
|||
void Deselect(int index); |
|||
void SelectRange(int start, int end); |
|||
void DeselectRange(int start, int end); |
|||
void SelectAll(); |
|||
void Clear(); |
|||
} |
|||
|
|||
public static class SelectionModelExtensions |
|||
{ |
|||
public static IDisposable BatchUpdate(this ISelectionModel model) |
|||
{ |
|||
return new BatchUpdateOperation(model); |
|||
} |
|||
|
|||
public struct BatchUpdateOperation : IDisposable |
|||
{ |
|||
private readonly ISelectionModel _owner; |
|||
private bool _isDisposed; |
|||
|
|||
public BatchUpdateOperation(ISelectionModel owner) |
|||
{ |
|||
_owner = owner; |
|||
_isDisposed = false; |
|||
owner.BeginBatchUpdate(); |
|||
} |
|||
|
|||
public void Dispose() |
|||
{ |
|||
if (!_isDisposed) |
|||
{ |
|||
_owner?.EndBatchUpdate(); |
|||
_isDisposed = true; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,82 @@ |
|||
using System; |
|||
using System.Collections; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
|
|||
#nullable enable |
|||
|
|||
namespace Avalonia.Controls.Selection |
|||
{ |
|||
internal class SelectedIndexes<T> : IReadOnlyList<int> |
|||
{ |
|||
private readonly SelectionModel<T>? _owner; |
|||
private readonly IReadOnlyList<IndexRange>? _ranges; |
|||
|
|||
public SelectedIndexes(SelectionModel<T> owner) => _owner = owner; |
|||
public SelectedIndexes(IReadOnlyList<IndexRange> ranges) => _ranges = ranges; |
|||
|
|||
public int this[int index] |
|||
{ |
|||
get |
|||
{ |
|||
if (index >= Count) |
|||
{ |
|||
throw new IndexOutOfRangeException("The index was out of range."); |
|||
} |
|||
|
|||
if (_owner?.SingleSelect == true) |
|||
{ |
|||
return _owner.SelectedIndex; |
|||
} |
|||
else |
|||
{ |
|||
return IndexRange.GetAt(Ranges!, index); |
|||
} |
|||
} |
|||
} |
|||
|
|||
public int Count |
|||
{ |
|||
get |
|||
{ |
|||
if (_owner?.SingleSelect == true) |
|||
{ |
|||
return _owner.SelectedIndex == -1 ? 0 : 1; |
|||
} |
|||
else |
|||
{ |
|||
return IndexRange.GetCount(Ranges!); |
|||
} |
|||
} |
|||
} |
|||
|
|||
private IReadOnlyList<IndexRange> Ranges => _ranges ?? _owner!.Ranges!; |
|||
|
|||
public IEnumerator<int> GetEnumerator() |
|||
{ |
|||
IEnumerator<int> SingleSelect() |
|||
{ |
|||
if (_owner.SelectedIndex >= 0) |
|||
{ |
|||
yield return _owner.SelectedIndex; |
|||
} |
|||
} |
|||
|
|||
if (_owner?.SingleSelect == true) |
|||
{ |
|||
return SingleSelect(); |
|||
} |
|||
else |
|||
{ |
|||
return IndexRange.EnumerateIndices(Ranges).GetEnumerator(); |
|||
} |
|||
} |
|||
|
|||
public static SelectedIndexes<T>? Create(IReadOnlyList<IndexRange>? ranges) |
|||
{ |
|||
return ranges is object ? new SelectedIndexes<T>(ranges) : null; |
|||
} |
|||
|
|||
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); |
|||
} |
|||
} |
|||
@ -0,0 +1,121 @@ |
|||
using System; |
|||
using System.Collections; |
|||
using System.Collections.Generic; |
|||
using System.Diagnostics.CodeAnalysis; |
|||
|
|||
#nullable enable |
|||
|
|||
namespace Avalonia.Controls.Selection |
|||
{ |
|||
internal class SelectedItems<T> : IReadOnlyList<T> |
|||
{ |
|||
private readonly SelectionModel<T>? _owner; |
|||
private readonly ItemsSourceView<T>? _items; |
|||
private readonly IReadOnlyList<IndexRange>? _ranges; |
|||
|
|||
public SelectedItems(SelectionModel<T> owner) => _owner = owner; |
|||
|
|||
public SelectedItems(IReadOnlyList<IndexRange> ranges, ItemsSourceView<T>? items) |
|||
{ |
|||
_ranges = ranges ?? throw new ArgumentNullException(nameof(ranges)); |
|||
_items = items; |
|||
} |
|||
|
|||
[MaybeNull] |
|||
public T this[int index] |
|||
{ |
|||
#pragma warning disable CS8766
|
|||
get |
|||
#pragma warning restore CS8766
|
|||
{ |
|||
if (index >= Count) |
|||
{ |
|||
throw new IndexOutOfRangeException("The index was out of range."); |
|||
} |
|||
|
|||
if (_owner?.SingleSelect == true) |
|||
{ |
|||
return _owner.SelectedItem; |
|||
} |
|||
else if (Items is object) |
|||
{ |
|||
return Items[index]; |
|||
} |
|||
else |
|||
{ |
|||
return default; |
|||
} |
|||
} |
|||
} |
|||
|
|||
public int Count |
|||
{ |
|||
get |
|||
{ |
|||
if (_owner?.SingleSelect == true) |
|||
{ |
|||
return _owner.SelectedIndex == -1 ? 0 : 1; |
|||
} |
|||
else |
|||
{ |
|||
return Ranges is object ? IndexRange.GetCount(Ranges) : 0; |
|||
} |
|||
} |
|||
} |
|||
|
|||
private ItemsSourceView<T>? Items => _items ?? _owner?.ItemsView; |
|||
private IReadOnlyList<IndexRange>? Ranges => _ranges ?? _owner!.Ranges; |
|||
|
|||
public IEnumerator<T> GetEnumerator() |
|||
{ |
|||
if (_owner?.SingleSelect == true) |
|||
{ |
|||
if (_owner.SelectedIndex >= 0) |
|||
{ |
|||
#pragma warning disable CS8603
|
|||
yield return _owner.SelectedItem; |
|||
#pragma warning restore CS8603
|
|||
} |
|||
} |
|||
else |
|||
{ |
|||
var items = Items; |
|||
|
|||
foreach (var range in Ranges!) |
|||
{ |
|||
for (var i = range.Begin; i <= range.End; ++i) |
|||
{ |
|||
#pragma warning disable CS8603
|
|||
yield return items is object ? items[i] : default; |
|||
#pragma warning restore CS8603
|
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); |
|||
|
|||
public static SelectedItems<T>? Create( |
|||
IReadOnlyList<IndexRange>? ranges, |
|||
ItemsSourceView<T>? items) |
|||
{ |
|||
return ranges is object ? new SelectedItems<T>(ranges, items) : null; |
|||
} |
|||
|
|||
public class Untyped : IReadOnlyList<object?> |
|||
{ |
|||
private readonly IReadOnlyList<T> _source; |
|||
public Untyped(IReadOnlyList<T> source) => _source = source; |
|||
public object? this[int index] => _source[index]; |
|||
public int Count => _source.Count; |
|||
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); |
|||
public IEnumerator<object?> GetEnumerator() |
|||
{ |
|||
foreach (var i in _source) |
|||
{ |
|||
yield return i; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,749 @@ |
|||
using System; |
|||
using System.Collections; |
|||
using System.Collections.Generic; |
|||
using System.Collections.Specialized; |
|||
using System.ComponentModel; |
|||
using System.Diagnostics.CodeAnalysis; |
|||
using System.Linq; |
|||
|
|||
#nullable enable |
|||
|
|||
namespace Avalonia.Controls.Selection |
|||
{ |
|||
public class SelectionModel<T> : SelectionNodeBase<T>, ISelectionModel |
|||
{ |
|||
private bool _singleSelect = true; |
|||
private int _anchorIndex = -1; |
|||
private int _selectedIndex = -1; |
|||
private Operation? _operation; |
|||
private SelectedIndexes<T>? _selectedIndexes; |
|||
private SelectedItems<T>? _selectedItems; |
|||
private SelectedItems<T>.Untyped? _selectedItemsUntyped; |
|||
private EventHandler<SelectionModelSelectionChangedEventArgs>? _untypedSelectionChanged; |
|||
[AllowNull] private T _initSelectedItem = default; |
|||
private bool _hasInitSelectedItem; |
|||
|
|||
public SelectionModel() |
|||
{ |
|||
} |
|||
|
|||
public SelectionModel(IEnumerable<T>? source) |
|||
{ |
|||
Source = source; |
|||
} |
|||
|
|||
public new IEnumerable<T>? Source |
|||
{ |
|||
get => base.Source as IEnumerable<T>; |
|||
set => SetSource(value); |
|||
} |
|||
|
|||
public bool SingleSelect |
|||
{ |
|||
get => _singleSelect; |
|||
set |
|||
{ |
|||
if (_singleSelect != value) |
|||
{ |
|||
if (value == true) |
|||
{ |
|||
using var update = BatchUpdate(); |
|||
var selectedIndex = SelectedIndex; |
|||
Clear(); |
|||
SelectedIndex = selectedIndex; |
|||
} |
|||
|
|||
_singleSelect = value; |
|||
RangesEnabled = !value; |
|||
|
|||
if (RangesEnabled && _selectedIndex >= 0) |
|||
{ |
|||
CommitSelect(new IndexRange(_selectedIndex)); |
|||
} |
|||
|
|||
RaisePropertyChanged(nameof(SingleSelect)); |
|||
} |
|||
} |
|||
} |
|||
|
|||
public int SelectedIndex |
|||
{ |
|||
get => _selectedIndex; |
|||
set |
|||
{ |
|||
using var update = BatchUpdate(); |
|||
Clear(); |
|||
Select(value); |
|||
} |
|||
} |
|||
|
|||
public IReadOnlyList<int> SelectedIndexes => _selectedIndexes ??= new SelectedIndexes<T>(this); |
|||
|
|||
[MaybeNull, AllowNull] |
|||
public T SelectedItem |
|||
{ |
|||
get => ItemsView is object ? GetItemAt(_selectedIndex) : _initSelectedItem; |
|||
set |
|||
{ |
|||
if (ItemsView is object) |
|||
{ |
|||
SelectedIndex = ItemsView.IndexOf(value!); |
|||
} |
|||
else |
|||
{ |
|||
Clear(); |
|||
_initSelectedItem = value; |
|||
_hasInitSelectedItem = true; |
|||
} |
|||
} |
|||
} |
|||
|
|||
public IReadOnlyList<T> SelectedItems |
|||
{ |
|||
get |
|||
{ |
|||
if (ItemsView is null && _hasInitSelectedItem) |
|||
{ |
|||
return new[] { _initSelectedItem }; |
|||
} |
|||
|
|||
return _selectedItems ??= new SelectedItems<T>(this); |
|||
} |
|||
} |
|||
|
|||
public int AnchorIndex |
|||
{ |
|||
get => _anchorIndex; |
|||
set |
|||
{ |
|||
using var update = BatchUpdate(); |
|||
var index = CoerceIndex(value); |
|||
update.Operation.AnchorIndex = index; |
|||
} |
|||
} |
|||
|
|||
public int Count |
|||
{ |
|||
get |
|||
{ |
|||
if (SingleSelect) |
|||
{ |
|||
return _selectedIndex >= 0 ? 1 : 0; |
|||
} |
|||
else |
|||
{ |
|||
return IndexRange.GetCount(Ranges); |
|||
} |
|||
} |
|||
} |
|||
|
|||
IEnumerable? ISelectionModel.Source |
|||
{ |
|||
get => Source; |
|||
set => SetSource(value); |
|||
} |
|||
|
|||
object? ISelectionModel.SelectedItem |
|||
{ |
|||
get => SelectedItem; |
|||
set |
|||
{ |
|||
if (value is T t) |
|||
{ |
|||
SelectedItem = t; |
|||
} |
|||
else |
|||
{ |
|||
SelectedIndex = -1; |
|||
} |
|||
} |
|||
|
|||
} |
|||
|
|||
IReadOnlyList<object?> ISelectionModel.SelectedItems |
|||
{ |
|||
get => _selectedItemsUntyped ??= new SelectedItems<T>.Untyped(SelectedItems); |
|||
} |
|||
|
|||
public event EventHandler<SelectionModelIndexesChangedEventArgs>? IndexesChanged; |
|||
public event EventHandler<SelectionModelSelectionChangedEventArgs<T>>? SelectionChanged; |
|||
public event EventHandler? LostSelection; |
|||
public event EventHandler? SourceReset; |
|||
public event PropertyChangedEventHandler? PropertyChanged; |
|||
|
|||
event EventHandler<SelectionModelSelectionChangedEventArgs>? ISelectionModel.SelectionChanged |
|||
{ |
|||
add => _untypedSelectionChanged += value; |
|||
remove => _untypedSelectionChanged -= value; |
|||
} |
|||
|
|||
public BatchUpdateOperation BatchUpdate() => new BatchUpdateOperation(this); |
|||
|
|||
public void BeginBatchUpdate() |
|||
{ |
|||
_operation ??= new Operation(this); |
|||
++_operation.UpdateCount; |
|||
} |
|||
|
|||
public void EndBatchUpdate() |
|||
{ |
|||
if (_operation is null || _operation.UpdateCount == 0) |
|||
{ |
|||
throw new InvalidOperationException("No batch update in progress."); |
|||
} |
|||
|
|||
if (--_operation.UpdateCount == 0) |
|||
{ |
|||
// If the collection is currently changing, commit the update when the
|
|||
// collection change finishes.
|
|||
if (!IsSourceCollectionChanging) |
|||
{ |
|||
CommitOperation(_operation); |
|||
} |
|||
} |
|||
} |
|||
|
|||
public bool IsSelected(int index) |
|||
{ |
|||
if (index < 0) |
|||
{ |
|||
return false; |
|||
} |
|||
else if (SingleSelect) |
|||
{ |
|||
return _selectedIndex == index; |
|||
} |
|||
else |
|||
{ |
|||
return IndexRange.Contains(Ranges, index); |
|||
} |
|||
} |
|||
|
|||
public void Select(int index) => SelectRange(index, index, false, true); |
|||
|
|||
public void Deselect(int index) => DeselectRange(index, index); |
|||
|
|||
public void SelectRange(int start, int end) => SelectRange(start, end, false, false); |
|||
|
|||
public void DeselectRange(int start, int end) |
|||
{ |
|||
using var update = BatchUpdate(); |
|||
var o = update.Operation; |
|||
var range = CoerceRange(start, end); |
|||
|
|||
if (range.Begin == -1) |
|||
{ |
|||
return; |
|||
} |
|||
|
|||
if (RangesEnabled) |
|||
{ |
|||
var selected = Ranges.ToList(); |
|||
var deselected = new List<IndexRange>(); |
|||
var operationDeselected = new List<IndexRange>(); |
|||
|
|||
o.DeselectedRanges ??= new List<IndexRange>(); |
|||
IndexRange.Remove(o.SelectedRanges, range, operationDeselected); |
|||
IndexRange.Remove(selected, range, deselected); |
|||
IndexRange.Add(o.DeselectedRanges, deselected); |
|||
|
|||
if (IndexRange.Contains(deselected, o.SelectedIndex) || |
|||
IndexRange.Contains(operationDeselected, o.SelectedIndex)) |
|||
{ |
|||
o.SelectedIndex = GetFirstSelectedIndexFromRanges(except: deselected); |
|||
} |
|||
} |
|||
else if(range.Contains(_selectedIndex)) |
|||
{ |
|||
o.SelectedIndex = -1; |
|||
} |
|||
|
|||
_initSelectedItem = default; |
|||
_hasInitSelectedItem = false; |
|||
} |
|||
|
|||
public void SelectAll() => SelectRange(0, int.MaxValue); |
|||
public void Clear() => DeselectRange(0, int.MaxValue); |
|||
|
|||
protected void RaisePropertyChanged(string propertyName) |
|||
{ |
|||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); |
|||
} |
|||
|
|||
private void SetSource(IEnumerable? value) |
|||
{ |
|||
if (base.Source != value) |
|||
{ |
|||
if (_operation is object) |
|||
{ |
|||
throw new InvalidOperationException("Cannot change source while update is in progress."); |
|||
} |
|||
|
|||
if (base.Source is object && value is object) |
|||
{ |
|||
using var update = BatchUpdate(); |
|||
update.Operation.SkipLostSelection = true; |
|||
Clear(); |
|||
} |
|||
|
|||
base.Source = value; |
|||
|
|||
using (var update = BatchUpdate()) |
|||
{ |
|||
update.Operation.IsSourceUpdate = true; |
|||
|
|||
if (_hasInitSelectedItem) |
|||
{ |
|||
SelectedItem = _initSelectedItem; |
|||
_initSelectedItem = default; |
|||
_hasInitSelectedItem = false; |
|||
} |
|||
else |
|||
{ |
|||
TrimInvalidSelections(update.Operation); |
|||
} |
|||
|
|||
RaisePropertyChanged(nameof(Source)); |
|||
} |
|||
} |
|||
} |
|||
|
|||
private protected override void OnIndexesChanged(int shiftIndex, int shiftDelta) |
|||
{ |
|||
IndexesChanged?.Invoke(this, new SelectionModelIndexesChangedEventArgs(shiftIndex, shiftDelta)); |
|||
} |
|||
|
|||
private protected override void OnSourceReset() |
|||
{ |
|||
_selectedIndex = _anchorIndex = -1; |
|||
CommitDeselect(new IndexRange(0, int.MaxValue)); |
|||
|
|||
if (SourceReset is object) |
|||
{ |
|||
SourceReset.Invoke(this, EventArgs.Empty); |
|||
} |
|||
else |
|||
{ |
|||
//Logger.TryGet(LogEventLevel.Warning, LogArea.Control)?.Log(
|
|||
// this,
|
|||
// "SelectionModel received Reset but no SourceReset handler was registered to handle it. " +
|
|||
// "Selection may be out of sync.",
|
|||
// typeof(SelectionModel));
|
|||
} |
|||
} |
|||
|
|||
private protected override void OnSelectionChanged(IReadOnlyList<T> deselectedItems) |
|||
{ |
|||
// Note: We're *not* putting this in a using scope. A collection update is still in progress
|
|||
// so the operation won't get commited by normal means: we have to commit it manually.
|
|||
var update = BatchUpdate(); |
|||
|
|||
update.Operation.DeselectedItems = deselectedItems; |
|||
|
|||
if (_selectedIndex == -1 && LostSelection is object) |
|||
{ |
|||
LostSelection(this, EventArgs.Empty); |
|||
} |
|||
|
|||
CommitOperation(update.Operation); |
|||
} |
|||
|
|||
private protected override CollectionChangeState OnItemsAdded(int index, IList items) |
|||
{ |
|||
var count = items.Count; |
|||
var shifted = SelectedIndex >= index; |
|||
var shiftCount = shifted ? count : 0; |
|||
|
|||
_selectedIndex += shiftCount; |
|||
_anchorIndex += shiftCount; |
|||
|
|||
var baseResult = base.OnItemsAdded(index, items); |
|||
shifted |= baseResult.ShiftDelta != 0; |
|||
|
|||
return new CollectionChangeState |
|||
{ |
|||
ShiftIndex = index, |
|||
ShiftDelta = shifted ? count : 0, |
|||
}; |
|||
} |
|||
|
|||
private protected override CollectionChangeState OnItemsRemoved(int index, IList items) |
|||
{ |
|||
var count = items.Count; |
|||
var removedRange = new IndexRange(index, index + count - 1); |
|||
var shifted = false; |
|||
List<T>? removed; |
|||
|
|||
var baseResult = base.OnItemsRemoved(index, items); |
|||
shifted |= baseResult.ShiftDelta != 0; |
|||
removed = baseResult.RemovedItems; |
|||
|
|||
if (removedRange.Contains(SelectedIndex)) |
|||
{ |
|||
if (SingleSelect) |
|||
{ |
|||
#pragma warning disable CS8604
|
|||
removed = new List<T> { (T)items[SelectedIndex - index] }; |
|||
#pragma warning restore CS8604
|
|||
} |
|||
|
|||
_selectedIndex = GetFirstSelectedIndexFromRanges(); |
|||
} |
|||
else if (SelectedIndex >= index) |
|||
{ |
|||
_selectedIndex -= count; |
|||
shifted = true; |
|||
} |
|||
|
|||
if (removedRange.Contains(AnchorIndex)) |
|||
{ |
|||
_anchorIndex = GetFirstSelectedIndexFromRanges(); |
|||
} |
|||
else if (AnchorIndex >= index) |
|||
{ |
|||
_anchorIndex -= count; |
|||
shifted = true; |
|||
} |
|||
|
|||
return new CollectionChangeState |
|||
{ |
|||
ShiftIndex = index, |
|||
ShiftDelta = shifted ? -count : 0, |
|||
RemovedItems = removed, |
|||
}; |
|||
} |
|||
|
|||
private protected override void OnSourceCollectionChanged(NotifyCollectionChangedEventArgs e) |
|||
{ |
|||
if (_operation?.UpdateCount > 0) |
|||
{ |
|||
throw new InvalidOperationException("Source collection was modified during selection update."); |
|||
} |
|||
|
|||
var oldAnchorIndex = _anchorIndex; |
|||
var oldSelectedIndex = _selectedIndex; |
|||
|
|||
base.OnSourceCollectionChanged(e); |
|||
|
|||
if (oldSelectedIndex != _selectedIndex) |
|||
{ |
|||
RaisePropertyChanged(nameof(SelectedIndex)); |
|||
} |
|||
|
|||
if (oldAnchorIndex != _anchorIndex) |
|||
{ |
|||
RaisePropertyChanged(nameof(AnchorIndex)); |
|||
} |
|||
} |
|||
|
|||
private protected override bool IsValidCollectionChange(NotifyCollectionChangedEventArgs e) |
|||
{ |
|||
if (!base.IsValidCollectionChange(e)) |
|||
{ |
|||
return false; |
|||
} |
|||
|
|||
if (ItemsView is object && e.Action == NotifyCollectionChangedAction.Add) |
|||
{ |
|||
if (e.NewStartingIndex <= _selectedIndex) |
|||
{ |
|||
return _selectedIndex + e.NewItems.Count < ItemsView.Count; |
|||
} |
|||
|
|||
if (e.NewStartingIndex <= _anchorIndex) |
|||
{ |
|||
return _anchorIndex + e.NewItems.Count < ItemsView.Count; |
|||
} |
|||
} |
|||
|
|||
return true; |
|||
} |
|||
|
|||
protected override void OnSourceCollectionChangeFinished() |
|||
{ |
|||
if (_operation is object) |
|||
{ |
|||
CommitOperation(_operation); |
|||
} |
|||
} |
|||
|
|||
private int GetFirstSelectedIndexFromRanges(List<IndexRange>? except = null) |
|||
{ |
|||
if (RangesEnabled) |
|||
{ |
|||
var count = IndexRange.GetCount(Ranges); |
|||
var index = 0; |
|||
|
|||
while (index < count) |
|||
{ |
|||
var result = IndexRange.GetAt(Ranges, index++); |
|||
|
|||
if (!IndexRange.Contains(except, result)) |
|||
{ |
|||
return result; |
|||
} |
|||
} |
|||
} |
|||
|
|||
return -1; |
|||
} |
|||
|
|||
private void SelectRange( |
|||
int start, |
|||
int end, |
|||
bool forceSelectedIndex, |
|||
bool forceAnchorIndex) |
|||
{ |
|||
if (SingleSelect && start != end) |
|||
{ |
|||
throw new InvalidOperationException("Cannot select range with single selection."); |
|||
} |
|||
|
|||
var range = CoerceRange(start, end); |
|||
|
|||
if (range.Begin == -1) |
|||
{ |
|||
return; |
|||
} |
|||
|
|||
using var update = BatchUpdate(); |
|||
var o = update.Operation; |
|||
var selected = new List<IndexRange>(); |
|||
|
|||
if (RangesEnabled) |
|||
{ |
|||
o.SelectedRanges ??= new List<IndexRange>(); |
|||
IndexRange.Remove(o.DeselectedRanges, range); |
|||
IndexRange.Add(o.SelectedRanges, range); |
|||
IndexRange.Remove(o.SelectedRanges, Ranges); |
|||
|
|||
if (o.SelectedIndex == -1 || forceSelectedIndex) |
|||
{ |
|||
o.SelectedIndex = range.Begin; |
|||
} |
|||
|
|||
if (o.AnchorIndex == -1 || forceAnchorIndex) |
|||
{ |
|||
o.AnchorIndex = range.Begin; |
|||
} |
|||
} |
|||
else |
|||
{ |
|||
o.SelectedIndex = o.AnchorIndex = start; |
|||
} |
|||
|
|||
_initSelectedItem = default; |
|||
_hasInitSelectedItem = false; |
|||
} |
|||
|
|||
[return: MaybeNull] |
|||
private T GetItemAt(int index) |
|||
{ |
|||
if (ItemsView is null || index < 0 || index >= ItemsView.Count) |
|||
{ |
|||
return default; |
|||
} |
|||
|
|||
return ItemsView[index]; |
|||
} |
|||
|
|||
private int CoerceIndex(int index) |
|||
{ |
|||
index = Math.Max(index, -1); |
|||
|
|||
if (ItemsView is object && index >= ItemsView.Count) |
|||
{ |
|||
index = -1; |
|||
} |
|||
|
|||
return index; |
|||
} |
|||
|
|||
private IndexRange CoerceRange(int start, int end) |
|||
{ |
|||
var max = ItemsView is object ? ItemsView.Count - 1 : int.MaxValue; |
|||
|
|||
if (start > max || (start < 0 && end < 0)) |
|||
{ |
|||
return new IndexRange(-1); |
|||
} |
|||
|
|||
start = Math.Max(start, 0); |
|||
end = Math.Min(end, max); |
|||
|
|||
return new IndexRange(start, end); |
|||
} |
|||
|
|||
private void TrimInvalidSelections(Operation operation) |
|||
{ |
|||
if (ItemsView is null) |
|||
{ |
|||
return; |
|||
} |
|||
|
|||
var max = ItemsView.Count - 1; |
|||
|
|||
if (operation.SelectedIndex > max) |
|||
{ |
|||
operation.SelectedIndex = GetFirstSelectedIndexFromRanges(); |
|||
} |
|||
|
|||
if (operation.AnchorIndex > max) |
|||
{ |
|||
operation.AnchorIndex = GetFirstSelectedIndexFromRanges(); |
|||
} |
|||
|
|||
if (RangesEnabled && Ranges.Count > 0) |
|||
{ |
|||
var selected = Ranges.ToList(); |
|||
|
|||
if (max < 0) |
|||
{ |
|||
operation.DeselectedRanges = selected; |
|||
} |
|||
else |
|||
{ |
|||
var valid = new IndexRange(0, max); |
|||
var removed = new List<IndexRange>(); |
|||
IndexRange.Intersect(selected, valid, removed); |
|||
operation.DeselectedRanges = removed; |
|||
} |
|||
} |
|||
} |
|||
|
|||
private void CommitOperation(Operation operation) |
|||
{ |
|||
try |
|||
{ |
|||
var oldAnchorIndex = _anchorIndex; |
|||
var oldSelectedIndex = _selectedIndex; |
|||
var indexesChanged = false; |
|||
|
|||
if (operation.SelectedIndex == -1 && LostSelection is object && !operation.SkipLostSelection) |
|||
{ |
|||
operation.UpdateCount++; |
|||
LostSelection?.Invoke(this, EventArgs.Empty); |
|||
} |
|||
|
|||
_selectedIndex = operation.SelectedIndex; |
|||
_anchorIndex = operation.AnchorIndex; |
|||
|
|||
if (operation.SelectedRanges is object) |
|||
{ |
|||
indexesChanged |= CommitSelect(operation.SelectedRanges) > 0; |
|||
} |
|||
|
|||
if (operation.DeselectedRanges is object) |
|||
{ |
|||
indexesChanged |= CommitDeselect(operation.DeselectedRanges) > 0; |
|||
} |
|||
|
|||
if (SelectionChanged is object || _untypedSelectionChanged is object) |
|||
{ |
|||
IReadOnlyList<IndexRange>? deselected = operation.DeselectedRanges; |
|||
IReadOnlyList<IndexRange>? selected = operation.SelectedRanges; |
|||
|
|||
if (SingleSelect && oldSelectedIndex != _selectedIndex) |
|||
{ |
|||
if (oldSelectedIndex != -1) |
|||
{ |
|||
deselected = new[] { new IndexRange(oldSelectedIndex) }; |
|||
} |
|||
|
|||
if (_selectedIndex != -1) |
|||
{ |
|||
selected = new[] { new IndexRange(_selectedIndex) }; |
|||
} |
|||
} |
|||
|
|||
if (deselected?.Count > 0 || selected?.Count > 0 || operation.DeselectedItems is object) |
|||
{ |
|||
// If the operation was caused by Source being updated, then use a null source
|
|||
// so that the items will appear as nulls.
|
|||
var deselectedSource = operation.IsSourceUpdate ? null : ItemsView; |
|||
|
|||
// If the operation contains DeselectedItems then we're notifying a source
|
|||
// CollectionChanged event. LostFocus may have caused another item to have been
|
|||
// selected, but it can't have caused a deselection (as it was called due to
|
|||
// selection being lost) so we're ok to discard `deselected` here.
|
|||
var deselectedItems = operation.DeselectedItems ?? |
|||
SelectedItems<T>.Create(deselected, deselectedSource); |
|||
|
|||
var e = new SelectionModelSelectionChangedEventArgs<T>( |
|||
SelectedIndexes<T>.Create(deselected), |
|||
SelectedIndexes<T>.Create(selected), |
|||
deselectedItems, |
|||
SelectedItems<T>.Create(selected, ItemsView)); |
|||
SelectionChanged?.Invoke(this, e); |
|||
_untypedSelectionChanged?.Invoke(this, e); |
|||
} |
|||
} |
|||
|
|||
if (oldSelectedIndex != _selectedIndex) |
|||
{ |
|||
indexesChanged = true; |
|||
RaisePropertyChanged(nameof(SelectedIndex)); |
|||
RaisePropertyChanged(nameof(SelectedItem)); |
|||
} |
|||
|
|||
if (oldAnchorIndex != _anchorIndex) |
|||
{ |
|||
indexesChanged = true; |
|||
RaisePropertyChanged(nameof(AnchorIndex)); |
|||
} |
|||
|
|||
if (indexesChanged) |
|||
{ |
|||
RaisePropertyChanged(nameof(SelectedIndexes)); |
|||
RaisePropertyChanged(nameof(SelectedItems)); |
|||
} |
|||
} |
|||
finally |
|||
{ |
|||
_operation = null; |
|||
} |
|||
} |
|||
|
|||
public struct BatchUpdateOperation : IDisposable |
|||
{ |
|||
private readonly SelectionModel<T> _owner; |
|||
private bool _isDisposed; |
|||
|
|||
public BatchUpdateOperation(SelectionModel<T> owner) |
|||
{ |
|||
_owner = owner; |
|||
_isDisposed = false; |
|||
owner.BeginBatchUpdate(); |
|||
} |
|||
|
|||
internal Operation Operation => _owner._operation!; |
|||
|
|||
public void Dispose() |
|||
{ |
|||
if (!_isDisposed) |
|||
{ |
|||
_owner?.EndBatchUpdate(); |
|||
_isDisposed = true; |
|||
} |
|||
} |
|||
} |
|||
|
|||
internal class Operation |
|||
{ |
|||
public Operation(SelectionModel<T> owner) |
|||
{ |
|||
AnchorIndex = owner.AnchorIndex; |
|||
SelectedIndex = owner.SelectedIndex; |
|||
} |
|||
|
|||
public int UpdateCount { get; set; } |
|||
public bool IsSourceUpdate { get; set; } |
|||
public bool SkipLostSelection { get; set; } |
|||
public int AnchorIndex { get; set; } |
|||
public int SelectedIndex { get; set; } |
|||
public List<IndexRange>? SelectedRanges { get; set; } |
|||
public List<IndexRange>? DeselectedRanges { get; set; } |
|||
public IReadOnlyList<T>? DeselectedItems { get; set; } |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,18 @@ |
|||
using System; |
|||
|
|||
#nullable enable |
|||
|
|||
namespace Avalonia.Controls.Selection |
|||
{ |
|||
public class SelectionModelIndexesChangedEventArgs : EventArgs |
|||
{ |
|||
public SelectionModelIndexesChangedEventArgs(int startIndex, int delta) |
|||
{ |
|||
StartIndex = startIndex; |
|||
Delta = delta; |
|||
} |
|||
|
|||
public int StartIndex { get; } |
|||
public int Delta { get; } |
|||
} |
|||
} |
|||
@ -0,0 +1,85 @@ |
|||
using System; |
|||
using System.Collections; |
|||
using System.Collections.Generic; |
|||
using Avalonia.Controls.Selection; |
|||
|
|||
#nullable enable |
|||
|
|||
namespace Avalonia.Controls.Selection |
|||
{ |
|||
public abstract class SelectionModelSelectionChangedEventArgs : EventArgs |
|||
{ |
|||
/// <summary>
|
|||
/// Gets the indexes of the items that were removed from the selection.
|
|||
/// </summary>
|
|||
public abstract IReadOnlyList<int> DeselectedIndexes { get; } |
|||
|
|||
/// <summary>
|
|||
/// Gets the indexes of the items that were added to the selection.
|
|||
/// </summary>
|
|||
public abstract IReadOnlyList<int> SelectedIndexes { get; } |
|||
|
|||
/// <summary>
|
|||
/// Gets the items that were removed from the selection.
|
|||
/// </summary>
|
|||
public IReadOnlyList<object?> DeselectedItems => GetUntypedDeselectedItems(); |
|||
|
|||
/// <summary>
|
|||
/// Gets the items that were added to the selection.
|
|||
/// </summary>
|
|||
public IReadOnlyList<object?> SelectedItems => GetUntypedSelectedItems(); |
|||
|
|||
protected abstract IReadOnlyList<object?> GetUntypedDeselectedItems(); |
|||
protected abstract IReadOnlyList<object?> GetUntypedSelectedItems(); |
|||
} |
|||
|
|||
public class SelectionModelSelectionChangedEventArgs<T> : SelectionModelSelectionChangedEventArgs |
|||
{ |
|||
private IReadOnlyList<object?>? _deselectedItems; |
|||
private IReadOnlyList<object?>? _selectedItems; |
|||
|
|||
public SelectionModelSelectionChangedEventArgs( |
|||
IReadOnlyList<int>? deselectedIndices = null, |
|||
IReadOnlyList<int>? selectedIndices = null, |
|||
IReadOnlyList<T>? deselectedItems = null, |
|||
IReadOnlyList<T>? selectedItems = null) |
|||
{ |
|||
DeselectedIndexes = deselectedIndices ?? Array.Empty<int>(); |
|||
SelectedIndexes = selectedIndices ?? Array.Empty<int>(); |
|||
DeselectedItems = deselectedItems ?? Array.Empty<T>(); |
|||
SelectedItems = selectedItems ?? Array.Empty<T>(); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets the indexes of the items that were removed from the selection.
|
|||
/// </summary>
|
|||
public override IReadOnlyList<int> DeselectedIndexes { get; } |
|||
|
|||
/// <summary>
|
|||
/// Gets the indexes of the items that were added to the selection.
|
|||
/// </summary>
|
|||
public override IReadOnlyList<int> SelectedIndexes { get; } |
|||
|
|||
/// <summary>
|
|||
/// Gets the items that were removed from the selection.
|
|||
/// </summary>
|
|||
public new IReadOnlyList<T> DeselectedItems { get; } |
|||
|
|||
/// <summary>
|
|||
/// Gets the items that were added to the selection.
|
|||
/// </summary>
|
|||
public new IReadOnlyList<T> SelectedItems { get; } |
|||
|
|||
protected override IReadOnlyList<object?> GetUntypedDeselectedItems() |
|||
{ |
|||
return _deselectedItems ??= (DeselectedItems as IReadOnlyList<object?>) ?? |
|||
new SelectedItems<T>.Untyped(DeselectedItems); |
|||
} |
|||
|
|||
protected override IReadOnlyList<object?> GetUntypedSelectedItems() |
|||
{ |
|||
return _selectedItems ??= (SelectedItems as IReadOnlyList<object?>) ?? |
|||
new SelectedItems<T>.Untyped(SelectedItems); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,320 @@ |
|||
using System; |
|||
using System.Collections; |
|||
using System.Collections.Generic; |
|||
using System.Collections.Specialized; |
|||
using System.Linq; |
|||
using Avalonia.Controls.Utils; |
|||
|
|||
#nullable enable |
|||
|
|||
namespace Avalonia.Controls.Selection |
|||
{ |
|||
public abstract class SelectionNodeBase<T> : ICollectionChangedListener |
|||
{ |
|||
private IEnumerable? _source; |
|||
private bool _rangesEnabled; |
|||
private List<IndexRange>? _ranges; |
|||
private int _collectionChanging; |
|||
|
|||
protected IEnumerable? Source |
|||
{ |
|||
get => _source; |
|||
set |
|||
{ |
|||
if (_source != value) |
|||
{ |
|||
ItemsView?.RemoveListener(this); |
|||
_source = value; |
|||
ItemsView = value is object ? ItemsSourceView<T>.GetOrCreate(value) : null; |
|||
ItemsView?.AddListener(this); |
|||
} |
|||
} |
|||
} |
|||
|
|||
protected bool IsSourceCollectionChanging => _collectionChanging > 0; |
|||
|
|||
protected bool RangesEnabled |
|||
{ |
|||
get => _rangesEnabled; |
|||
set |
|||
{ |
|||
if (_rangesEnabled != value) |
|||
{ |
|||
_rangesEnabled = value; |
|||
|
|||
if (!_rangesEnabled) |
|||
{ |
|||
_ranges = null; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
internal ItemsSourceView<T>? ItemsView { get; set; } |
|||
|
|||
internal IReadOnlyList<IndexRange> Ranges |
|||
{ |
|||
get |
|||
{ |
|||
if (!RangesEnabled) |
|||
{ |
|||
throw new InvalidOperationException("Ranges not enabled."); |
|||
} |
|||
|
|||
return _ranges ??= new List<IndexRange>(); |
|||
} |
|||
} |
|||
|
|||
void ICollectionChangedListener.PreChanged(INotifyCollectionChanged sender, NotifyCollectionChangedEventArgs e) |
|||
{ |
|||
++_collectionChanging; |
|||
} |
|||
|
|||
void ICollectionChangedListener.Changed(INotifyCollectionChanged sender, NotifyCollectionChangedEventArgs e) |
|||
{ |
|||
OnSourceCollectionChanged(e); |
|||
} |
|||
|
|||
void ICollectionChangedListener.PostChanged(INotifyCollectionChanged sender, NotifyCollectionChangedEventArgs e) |
|||
{ |
|||
if (--_collectionChanging == 0) |
|||
{ |
|||
OnSourceCollectionChangeFinished(); |
|||
} |
|||
} |
|||
|
|||
protected abstract void OnSourceCollectionChangeFinished(); |
|||
|
|||
private protected abstract void OnIndexesChanged(int shiftIndex, int shiftDelta); |
|||
|
|||
private protected abstract void OnSourceReset(); |
|||
|
|||
private protected abstract void OnSelectionChanged(IReadOnlyList<T> deselectedItems); |
|||
|
|||
private protected int CommitSelect(IndexRange range) |
|||
{ |
|||
if (RangesEnabled) |
|||
{ |
|||
_ranges ??= new List<IndexRange>(); |
|||
return IndexRange.Add(_ranges, range); |
|||
} |
|||
|
|||
return 0; |
|||
} |
|||
|
|||
private protected int CommitSelect(IReadOnlyList<IndexRange> ranges) |
|||
{ |
|||
if (RangesEnabled) |
|||
{ |
|||
_ranges ??= new List<IndexRange>(); |
|||
return IndexRange.Add(_ranges, ranges); |
|||
} |
|||
|
|||
return 0; |
|||
} |
|||
|
|||
private protected int CommitDeselect(IndexRange range) |
|||
{ |
|||
if (RangesEnabled) |
|||
{ |
|||
_ranges ??= new List<IndexRange>(); |
|||
return IndexRange.Remove(_ranges, range); |
|||
} |
|||
|
|||
return 0; |
|||
} |
|||
|
|||
private protected int CommitDeselect(IReadOnlyList<IndexRange> ranges) |
|||
{ |
|||
if (RangesEnabled && _ranges is object) |
|||
{ |
|||
return IndexRange.Remove(_ranges, ranges); |
|||
} |
|||
|
|||
return 0; |
|||
} |
|||
|
|||
private protected virtual CollectionChangeState OnItemsAdded(int index, IList items) |
|||
{ |
|||
var count = items.Count; |
|||
var shifted = false; |
|||
|
|||
if (_ranges is object) |
|||
{ |
|||
List<IndexRange>? toAdd = null; |
|||
|
|||
for (var i = 0; i < Ranges!.Count; ++i) |
|||
{ |
|||
var range = Ranges[i]; |
|||
|
|||
// The range is after the inserted items, need to shift the range right
|
|||
if (range.End >= index) |
|||
{ |
|||
int begin = range.Begin; |
|||
|
|||
// If the index left of newIndex is inside the range,
|
|||
// Split the range and remember the left piece to add later
|
|||
if (range.Contains(index - 1)) |
|||
{ |
|||
range.Split(index - 1, out var before, out _); |
|||
(toAdd ??= new List<IndexRange>()).Add(before); |
|||
begin = index; |
|||
} |
|||
|
|||
// Shift the range to the right
|
|||
_ranges[i] = new IndexRange(begin + count, range.End + count); |
|||
shifted = true; |
|||
} |
|||
} |
|||
|
|||
if (toAdd is object) |
|||
{ |
|||
foreach (var range in toAdd) |
|||
{ |
|||
IndexRange.Add(_ranges, range); |
|||
} |
|||
} |
|||
} |
|||
|
|||
return new CollectionChangeState |
|||
{ |
|||
ShiftIndex = index, |
|||
ShiftDelta = shifted ? count : 0, |
|||
}; |
|||
} |
|||
|
|||
private protected virtual CollectionChangeState OnItemsRemoved(int index, IList items) |
|||
{ |
|||
var count = items.Count; |
|||
var removedRange = new IndexRange(index, index + count - 1); |
|||
bool shifted = false; |
|||
List<T>? removed = null; |
|||
|
|||
if (_ranges is object) |
|||
{ |
|||
var deselected = new List<IndexRange>(); |
|||
|
|||
if (IndexRange.Remove(_ranges, removedRange, deselected) > 0) |
|||
{ |
|||
removed = new List<T>(); |
|||
|
|||
foreach (var range in deselected) |
|||
{ |
|||
for (var i = range.Begin; i <= range.End; ++i) |
|||
{ |
|||
#pragma warning disable CS8604
|
|||
removed.Add((T)items[i - index]); |
|||
#pragma warning restore CS8604
|
|||
} |
|||
} |
|||
} |
|||
|
|||
for (var i = 0; i < Ranges!.Count; ++i) |
|||
{ |
|||
var existing = Ranges[i]; |
|||
|
|||
if (existing.End > removedRange.Begin) |
|||
{ |
|||
_ranges[i] = new IndexRange(existing.Begin - count, existing.End - count); |
|||
shifted = true; |
|||
} |
|||
} |
|||
} |
|||
|
|||
return new CollectionChangeState |
|||
{ |
|||
ShiftIndex = index, |
|||
ShiftDelta = shifted ? -count : 0, |
|||
RemovedItems = removed, |
|||
}; |
|||
} |
|||
|
|||
private protected virtual void OnSourceCollectionChanged(NotifyCollectionChangedEventArgs e) |
|||
{ |
|||
var shiftDelta = 0; |
|||
var shiftIndex = -1; |
|||
List<T>? removed = null; |
|||
|
|||
if (!IsValidCollectionChange(e)) |
|||
{ |
|||
return; |
|||
} |
|||
|
|||
switch (e.Action) |
|||
{ |
|||
case NotifyCollectionChangedAction.Add: |
|||
{ |
|||
var change = OnItemsAdded(e.NewStartingIndex, e.NewItems); |
|||
shiftIndex = change.ShiftIndex; |
|||
shiftDelta = change.ShiftDelta; |
|||
break; |
|||
} |
|||
case NotifyCollectionChangedAction.Remove: |
|||
{ |
|||
var change = OnItemsRemoved(e.OldStartingIndex, e.OldItems); |
|||
shiftIndex = change.ShiftIndex; |
|||
shiftDelta = change.ShiftDelta; |
|||
removed = change.RemovedItems; |
|||
break; |
|||
} |
|||
case NotifyCollectionChangedAction.Replace: |
|||
{ |
|||
var removeChange = OnItemsRemoved(e.OldStartingIndex, e.OldItems); |
|||
var addChange = OnItemsAdded(e.NewStartingIndex, e.NewItems); |
|||
shiftIndex = removeChange.ShiftIndex; |
|||
shiftDelta = removeChange.ShiftDelta + addChange.ShiftDelta; |
|||
removed = removeChange.RemovedItems; |
|||
} |
|||
break; |
|||
case NotifyCollectionChangedAction.Reset: |
|||
OnSourceReset(); |
|||
break; |
|||
} |
|||
|
|||
if (shiftDelta != 0) |
|||
{ |
|||
OnIndexesChanged(shiftIndex, shiftDelta); |
|||
} |
|||
|
|||
if (removed is object) |
|||
{ |
|||
OnSelectionChanged(removed); |
|||
} |
|||
} |
|||
|
|||
private protected virtual bool IsValidCollectionChange(NotifyCollectionChangedEventArgs e) |
|||
{ |
|||
// If the selection is modified in a CollectionChanged handler before the selection
|
|||
// model's CollectionChanged handler has had chance to run then we can end up with
|
|||
// a selected index that refers to the *new* state of the Source intermixed with
|
|||
// indexes that reference an old state of the source.
|
|||
//
|
|||
// There's not much we can do in this situation, so detect whether shifting the
|
|||
// current selected indexes would result in an invalid index in the source, and if
|
|||
// so bail.
|
|||
//
|
|||
// See unit test Handles_Selection_Made_In_CollectionChanged for more details.
|
|||
if (ItemsView is object && |
|||
RangesEnabled && |
|||
Ranges.Count > 0 && |
|||
e.Action == NotifyCollectionChangedAction.Add) |
|||
{ |
|||
var lastIndex = Ranges.Last().End; |
|||
|
|||
if (e.NewStartingIndex <= lastIndex) |
|||
{ |
|||
return lastIndex + e.NewItems.Count < ItemsView.Count; |
|||
} |
|||
} |
|||
|
|||
return true; |
|||
} |
|||
|
|||
private protected struct CollectionChangeState |
|||
{ |
|||
public int ShiftIndex; |
|||
public int ShiftDelta; |
|||
public List<T>? RemovedItems; |
|||
} |
|||
} |
|||
} |
|||
@ -1,894 +0,0 @@ |
|||
// This source file is adapted from the WinUI project.
|
|||
// (https://github.com/microsoft/microsoft-ui-xaml)
|
|||
//
|
|||
// Licensed to The Avalonia Project under MIT License, courtesy of The .NET Foundation.
|
|||
|
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.ComponentModel; |
|||
using System.Linq; |
|||
using System.Reactive.Linq; |
|||
using Avalonia.Controls.Utils; |
|||
|
|||
#nullable enable |
|||
|
|||
namespace Avalonia.Controls |
|||
{ |
|||
public class SelectionModel : ISelectionModel, IDisposable |
|||
{ |
|||
private readonly SelectionNode _rootNode; |
|||
private bool _singleSelect; |
|||
private bool _autoSelect; |
|||
private int _operationCount; |
|||
private IndexPath _oldAnchorIndex; |
|||
private IReadOnlyList<IndexPath>? _selectedIndicesCached; |
|||
private IReadOnlyList<object?>? _selectedItemsCached; |
|||
private SelectionModelChildrenRequestedEventArgs? _childrenRequestedEventArgs; |
|||
|
|||
public event EventHandler<SelectionModelChildrenRequestedEventArgs>? ChildrenRequested; |
|||
public event PropertyChangedEventHandler? PropertyChanged; |
|||
public event EventHandler<SelectionModelSelectionChangedEventArgs>? SelectionChanged; |
|||
|
|||
public SelectionModel() |
|||
{ |
|||
_rootNode = new SelectionNode(this, null); |
|||
SharedLeafNode = new SelectionNode(this, null); |
|||
} |
|||
|
|||
public object? Source |
|||
{ |
|||
get => _rootNode.Source; |
|||
set |
|||
{ |
|||
if (_rootNode.Source != value) |
|||
{ |
|||
var raiseChanged = _rootNode.Source == null && SelectedIndices.Count > 0; |
|||
|
|||
if (_rootNode.Source != null) |
|||
{ |
|||
// Temporarily prevent auto-select when switching source.
|
|||
var restoreAutoSelect = _autoSelect; |
|||
_autoSelect = false; |
|||
|
|||
try |
|||
{ |
|||
using (var operation = new Operation(this)) |
|||
{ |
|||
ClearSelection(resetAnchor: true); |
|||
} |
|||
} |
|||
finally |
|||
{ |
|||
_autoSelect = restoreAutoSelect; |
|||
} |
|||
} |
|||
|
|||
_rootNode.Source = value; |
|||
ApplyAutoSelect(true); |
|||
|
|||
RaisePropertyChanged("Source"); |
|||
|
|||
if (raiseChanged) |
|||
{ |
|||
var e = new SelectionModelSelectionChangedEventArgs( |
|||
null, |
|||
SelectedIndices, |
|||
null, |
|||
SelectedItems); |
|||
OnSelectionChanged(e); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
public bool SingleSelect |
|||
{ |
|||
get => _singleSelect; |
|||
set |
|||
{ |
|||
if (_singleSelect != value) |
|||
{ |
|||
_singleSelect = value; |
|||
var selectedIndices = SelectedIndices; |
|||
|
|||
if (value && selectedIndices != null && selectedIndices.Count > 0) |
|||
{ |
|||
using var operation = new Operation(this); |
|||
|
|||
// We want to be single select, so make sure there is only
|
|||
// one selected item.
|
|||
var firstSelectionIndexPath = selectedIndices[0]; |
|||
ClearSelection(resetAnchor: true); |
|||
SelectWithPathImpl(firstSelectionIndexPath, select: true); |
|||
SelectedIndex = firstSelectionIndexPath; |
|||
} |
|||
|
|||
RaisePropertyChanged("SingleSelect"); |
|||
} |
|||
} |
|||
} |
|||
|
|||
public bool RetainSelectionOnReset |
|||
{ |
|||
get => _rootNode.RetainSelectionOnReset; |
|||
set => _rootNode.RetainSelectionOnReset = value; |
|||
} |
|||
|
|||
public bool AutoSelect |
|||
{ |
|||
get => _autoSelect; |
|||
set |
|||
{ |
|||
if (_autoSelect != value) |
|||
{ |
|||
_autoSelect = value; |
|||
ApplyAutoSelect(true); |
|||
} |
|||
} |
|||
} |
|||
|
|||
public IndexPath AnchorIndex |
|||
{ |
|||
get |
|||
{ |
|||
IndexPath anchor = default; |
|||
|
|||
if (_rootNode.AnchorIndex >= 0) |
|||
{ |
|||
var path = new List<int>(); |
|||
SelectionNode? current = _rootNode; |
|||
|
|||
while (current?.AnchorIndex >= 0) |
|||
{ |
|||
path.Add(current.AnchorIndex); |
|||
current = current.GetAt(current.AnchorIndex, false, default); |
|||
} |
|||
|
|||
anchor = new IndexPath(path); |
|||
} |
|||
|
|||
return anchor; |
|||
} |
|||
set |
|||
{ |
|||
var oldValue = AnchorIndex; |
|||
|
|||
if (value != null) |
|||
{ |
|||
SelectionTreeHelper.TraverseIndexPath( |
|||
_rootNode, |
|||
value, |
|||
realizeChildren: true, |
|||
(currentNode, path, depth, childIndex) => currentNode.AnchorIndex = path.GetAt(depth)); |
|||
} |
|||
else |
|||
{ |
|||
_rootNode.AnchorIndex = -1; |
|||
} |
|||
|
|||
if (_operationCount == 0 && oldValue != AnchorIndex) |
|||
{ |
|||
RaisePropertyChanged("AnchorIndex"); |
|||
} |
|||
} |
|||
} |
|||
|
|||
public IndexPath SelectedIndex |
|||
{ |
|||
get |
|||
{ |
|||
IndexPath selectedIndex = default; |
|||
var selectedIndices = SelectedIndices; |
|||
|
|||
if (selectedIndices?.Count > 0) |
|||
{ |
|||
selectedIndex = selectedIndices[0]; |
|||
} |
|||
|
|||
return selectedIndex; |
|||
} |
|||
set |
|||
{ |
|||
if (!IsSelectedAt(value) || SelectedItems.Count > 1) |
|||
{ |
|||
using var operation = new Operation(this); |
|||
ClearSelection(resetAnchor: true); |
|||
SelectWithPathImpl(value, select: true); |
|||
} |
|||
} |
|||
} |
|||
|
|||
public object? SelectedItem |
|||
{ |
|||
get |
|||
{ |
|||
object? item = null; |
|||
var selectedItems = SelectedItems; |
|||
|
|||
if (selectedItems?.Count > 0) |
|||
{ |
|||
item = selectedItems[0]; |
|||
} |
|||
|
|||
return item; |
|||
} |
|||
} |
|||
|
|||
public IReadOnlyList<object?> SelectedItems |
|||
{ |
|||
get |
|||
{ |
|||
if (_selectedItemsCached == null) |
|||
{ |
|||
var selectedInfos = new List<SelectedItemInfo>(); |
|||
var count = 0; |
|||
|
|||
if (_rootNode.Source != null) |
|||
{ |
|||
SelectionTreeHelper.Traverse( |
|||
_rootNode, |
|||
realizeChildren: false, |
|||
currentInfo => |
|||
{ |
|||
if (currentInfo.Node.SelectedCount > 0) |
|||
{ |
|||
selectedInfos.Add(new SelectedItemInfo(currentInfo.Node, currentInfo.Path)); |
|||
count += currentInfo.Node.SelectedCount; |
|||
} |
|||
}); |
|||
} |
|||
|
|||
// Instead of creating a dumb vector that takes up the space for all the selected items,
|
|||
// we create a custom IReadOnlyList implementation that calls back using a delegate to find
|
|||
// the selected item at a particular index. This avoid having to create the storage and copying
|
|||
// needed in a dumb vector. This also allows us to expose a tree of selected nodes into an
|
|||
// easier to consume flat vector view of objects.
|
|||
var selectedItems = new SelectedItems<object?, SelectedItemInfo> ( |
|||
selectedInfos, |
|||
count, |
|||
(infos, index) => |
|||
{ |
|||
var currentIndex = 0; |
|||
object? item = null; |
|||
|
|||
foreach (var info in infos) |
|||
{ |
|||
var node = info.Node; |
|||
|
|||
if (node != null) |
|||
{ |
|||
var currentCount = node.SelectedCount; |
|||
|
|||
if (index >= currentIndex && index < currentIndex + currentCount) |
|||
{ |
|||
var targetIndex = node.SelectedIndices[index - currentIndex]; |
|||
item = node.ItemsSourceView!.GetAt(targetIndex); |
|||
break; |
|||
} |
|||
|
|||
currentIndex += currentCount; |
|||
} |
|||
else |
|||
{ |
|||
throw new InvalidOperationException( |
|||
"Selection has changed since SelectedItems property was read."); |
|||
} |
|||
} |
|||
|
|||
return item; |
|||
}); |
|||
|
|||
_selectedItemsCached = selectedItems; |
|||
} |
|||
|
|||
return _selectedItemsCached; |
|||
} |
|||
} |
|||
|
|||
public IReadOnlyList<IndexPath> SelectedIndices |
|||
{ |
|||
get |
|||
{ |
|||
if (_selectedIndicesCached == null) |
|||
{ |
|||
var selectedInfos = new List<SelectedItemInfo>(); |
|||
var count = 0; |
|||
|
|||
SelectionTreeHelper.Traverse( |
|||
_rootNode, |
|||
false, |
|||
currentInfo => |
|||
{ |
|||
if (currentInfo.Node.SelectedCount > 0) |
|||
{ |
|||
selectedInfos.Add(new SelectedItemInfo(currentInfo.Node, currentInfo.Path)); |
|||
count += currentInfo.Node.SelectedCount; |
|||
} |
|||
}); |
|||
|
|||
// Instead of creating a dumb vector that takes up the space for all the selected indices,
|
|||
// we create a custom VectorView implimentation that calls back using a delegate to find
|
|||
// the IndexPath at a particular index. This avoid having to create the storage and copying
|
|||
// needed in a dumb vector. This also allows us to expose a tree of selected nodes into an
|
|||
// easier to consume flat vector view of IndexPaths.
|
|||
var indices = new SelectedItems<IndexPath, SelectedItemInfo>( |
|||
selectedInfos, |
|||
count, |
|||
(infos, index) => // callback for GetAt(index)
|
|||
{ |
|||
var currentIndex = 0; |
|||
IndexPath path = default; |
|||
|
|||
foreach (var info in infos) |
|||
{ |
|||
var node = info.Node; |
|||
|
|||
if (node != null) |
|||
{ |
|||
var currentCount = node.SelectedCount; |
|||
if (index >= currentIndex && index < currentIndex + currentCount) |
|||
{ |
|||
int targetIndex = node.SelectedIndices[index - currentIndex]; |
|||
path = info.Path.CloneWithChildIndex(targetIndex); |
|||
break; |
|||
} |
|||
|
|||
currentIndex += currentCount; |
|||
} |
|||
else |
|||
{ |
|||
throw new InvalidOperationException( |
|||
"Selection has changed since SelectedIndices property was read."); |
|||
} |
|||
} |
|||
|
|||
return path; |
|||
}); |
|||
|
|||
_selectedIndicesCached = indices; |
|||
} |
|||
|
|||
return _selectedIndicesCached; |
|||
} |
|||
} |
|||
|
|||
internal SelectionNode SharedLeafNode { get; private set; } |
|||
|
|||
public void Dispose() |
|||
{ |
|||
ClearSelection(resetAnchor: false); |
|||
_rootNode.Cleanup(); |
|||
_rootNode.Dispose(); |
|||
_selectedIndicesCached = null; |
|||
_selectedItemsCached = null; |
|||
} |
|||
|
|||
public void SetAnchorIndex(int index) => AnchorIndex = new IndexPath(index); |
|||
|
|||
public void SetAnchorIndex(int groupIndex, int index) => AnchorIndex = new IndexPath(groupIndex, index); |
|||
|
|||
public void Select(int index) |
|||
{ |
|||
using var operation = new Operation(this); |
|||
SelectImpl(index, select: true); |
|||
} |
|||
|
|||
public void Select(int groupIndex, int itemIndex) |
|||
{ |
|||
using var operation = new Operation(this); |
|||
SelectWithGroupImpl(groupIndex, itemIndex, select: true); |
|||
} |
|||
|
|||
public void SelectAt(IndexPath index) |
|||
{ |
|||
using var operation = new Operation(this); |
|||
SelectWithPathImpl(index, select: true); |
|||
} |
|||
|
|||
public void Deselect(int index) |
|||
{ |
|||
using var operation = new Operation(this); |
|||
SelectImpl(index, select: false); |
|||
} |
|||
|
|||
public void Deselect(int groupIndex, int itemIndex) |
|||
{ |
|||
using var operation = new Operation(this); |
|||
SelectWithGroupImpl(groupIndex, itemIndex, select: false); |
|||
} |
|||
|
|||
public void DeselectAt(IndexPath index) |
|||
{ |
|||
using var operation = new Operation(this); |
|||
SelectWithPathImpl(index, select: false); |
|||
} |
|||
|
|||
public bool IsSelected(int index) => _rootNode.IsSelected(index); |
|||
|
|||
public bool IsSelected(int grouIndex, int itemIndex) |
|||
{ |
|||
return IsSelectedAt(new IndexPath(grouIndex, itemIndex)); |
|||
} |
|||
|
|||
public bool IsSelectedAt(IndexPath index) |
|||
{ |
|||
var path = index; |
|||
SelectionNode? node = _rootNode; |
|||
|
|||
for (int i = 0; i < path.GetSize() - 1; i++) |
|||
{ |
|||
var childIndex = path.GetAt(i); |
|||
node = node.GetAt(childIndex, false, default); |
|||
|
|||
if (node == null) |
|||
{ |
|||
return false; |
|||
} |
|||
} |
|||
|
|||
return node.IsSelected(index.GetAt(index.GetSize() - 1)); |
|||
} |
|||
|
|||
public bool? IsSelectedWithPartial(int index) |
|||
{ |
|||
if (index < 0) |
|||
{ |
|||
throw new ArgumentException("Index must be >= 0", nameof(index)); |
|||
} |
|||
|
|||
var isSelected = _rootNode.IsSelectedWithPartial(index); |
|||
return isSelected; |
|||
} |
|||
|
|||
public bool? IsSelectedWithPartial(int groupIndex, int itemIndex) |
|||
{ |
|||
if (groupIndex < 0) |
|||
{ |
|||
throw new ArgumentException("Group index must be >= 0", nameof(groupIndex)); |
|||
} |
|||
|
|||
if (itemIndex < 0) |
|||
{ |
|||
throw new ArgumentException("Item index must be >= 0", nameof(itemIndex)); |
|||
} |
|||
|
|||
var isSelected = (bool?)false; |
|||
var childNode = _rootNode.GetAt(groupIndex, false, default); |
|||
|
|||
if (childNode != null) |
|||
{ |
|||
isSelected = childNode.IsSelectedWithPartial(itemIndex); |
|||
} |
|||
|
|||
return isSelected; |
|||
} |
|||
|
|||
public bool? IsSelectedWithPartialAt(IndexPath index) |
|||
{ |
|||
var path = index; |
|||
var isRealized = true; |
|||
SelectionNode? node = _rootNode; |
|||
|
|||
for (int i = 0; i < path.GetSize() - 1; i++) |
|||
{ |
|||
var childIndex = path.GetAt(i); |
|||
node = node.GetAt(childIndex, false, default); |
|||
|
|||
if (node == null) |
|||
{ |
|||
isRealized = false; |
|||
break; |
|||
} |
|||
} |
|||
|
|||
var isSelected = (bool?)false; |
|||
|
|||
if (isRealized) |
|||
{ |
|||
var size = path.GetSize(); |
|||
if (size == 0) |
|||
{ |
|||
isSelected = SelectionNode.ConvertToNullableBool(node!.EvaluateIsSelectedBasedOnChildrenNodes()); |
|||
} |
|||
else |
|||
{ |
|||
isSelected = node!.IsSelectedWithPartial(path.GetAt(size - 1)); |
|||
} |
|||
} |
|||
|
|||
return isSelected; |
|||
} |
|||
|
|||
public void SelectRangeFromAnchor(int index) |
|||
{ |
|||
using var operation = new Operation(this); |
|||
SelectRangeFromAnchorImpl(index, select: true); |
|||
} |
|||
|
|||
public void SelectRangeFromAnchor(int endGroupIndex, int endItemIndex) |
|||
{ |
|||
using var operation = new Operation(this); |
|||
SelectRangeFromAnchorWithGroupImpl(endGroupIndex, endItemIndex, select: true); |
|||
} |
|||
|
|||
public void SelectRangeFromAnchorTo(IndexPath index) |
|||
{ |
|||
using var operation = new Operation(this); |
|||
SelectRangeImpl(AnchorIndex, index, select: true); |
|||
} |
|||
|
|||
public void DeselectRangeFromAnchor(int index) |
|||
{ |
|||
using var operation = new Operation(this); |
|||
SelectRangeFromAnchorImpl(index, select: false); |
|||
} |
|||
|
|||
public void DeselectRangeFromAnchor(int endGroupIndex, int endItemIndex) |
|||
{ |
|||
using var operation = new Operation(this); |
|||
SelectRangeFromAnchorWithGroupImpl(endGroupIndex, endItemIndex, false /* select */); |
|||
} |
|||
|
|||
public void DeselectRangeFromAnchorTo(IndexPath index) |
|||
{ |
|||
using var operation = new Operation(this); |
|||
SelectRangeImpl(AnchorIndex, index, select: false); |
|||
} |
|||
|
|||
public void SelectRange(IndexPath start, IndexPath end) |
|||
{ |
|||
using var operation = new Operation(this); |
|||
SelectRangeImpl(start, end, select: true); |
|||
} |
|||
|
|||
public void DeselectRange(IndexPath start, IndexPath end) |
|||
{ |
|||
using var operation = new Operation(this); |
|||
SelectRangeImpl(start, end, select: false); |
|||
} |
|||
|
|||
public void SelectAll() |
|||
{ |
|||
using var operation = new Operation(this); |
|||
|
|||
SelectionTreeHelper.Traverse( |
|||
_rootNode, |
|||
realizeChildren: true, |
|||
info => |
|||
{ |
|||
if (info.Node.DataCount > 0) |
|||
{ |
|||
info.Node.SelectAll(); |
|||
} |
|||
}); |
|||
} |
|||
|
|||
public void ClearSelection() |
|||
{ |
|||
using var operation = new Operation(this); |
|||
ClearSelection(resetAnchor: true); |
|||
} |
|||
|
|||
public IDisposable Update() => new Operation(this); |
|||
|
|||
protected void OnPropertyChanged(string propertyName) |
|||
{ |
|||
RaisePropertyChanged(propertyName); |
|||
} |
|||
|
|||
private void RaisePropertyChanged(string propertyName) |
|||
{ |
|||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); |
|||
} |
|||
|
|||
public void OnSelectionInvalidatedDueToCollectionChange( |
|||
bool selectionInvalidated, |
|||
IReadOnlyList<object?>? removedItems) |
|||
{ |
|||
SelectionModelSelectionChangedEventArgs? e = null; |
|||
|
|||
if (selectionInvalidated) |
|||
{ |
|||
e = new SelectionModelSelectionChangedEventArgs(null, null, removedItems, null); |
|||
} |
|||
|
|||
OnSelectionChanged(e); |
|||
ApplyAutoSelect(true); |
|||
} |
|||
|
|||
internal IObservable<object?>? ResolvePath( |
|||
object data, |
|||
IndexPath dataIndexPath, |
|||
IndexPath finalIndexPath) |
|||
{ |
|||
IObservable<object?>? resolved = null; |
|||
|
|||
// Raise ChildrenRequested event if there is a handler
|
|||
if (ChildrenRequested != null) |
|||
{ |
|||
if (_childrenRequestedEventArgs == null) |
|||
{ |
|||
_childrenRequestedEventArgs = new SelectionModelChildrenRequestedEventArgs( |
|||
data, |
|||
dataIndexPath, |
|||
finalIndexPath, |
|||
false); |
|||
} |
|||
else |
|||
{ |
|||
_childrenRequestedEventArgs.Initialize(data, dataIndexPath, finalIndexPath, false); |
|||
} |
|||
|
|||
ChildrenRequested(this, _childrenRequestedEventArgs); |
|||
resolved = _childrenRequestedEventArgs.Children; |
|||
|
|||
// Clear out the values in the args so that it cannot be used after the event handler call.
|
|||
_childrenRequestedEventArgs.Initialize(null, default, default, true); |
|||
} |
|||
|
|||
return resolved; |
|||
} |
|||
|
|||
private void ClearSelection(bool resetAnchor) |
|||
{ |
|||
SelectionTreeHelper.Traverse( |
|||
_rootNode, |
|||
realizeChildren: false, |
|||
info => info.Node.Clear()); |
|||
|
|||
if (resetAnchor) |
|||
{ |
|||
AnchorIndex = default; |
|||
} |
|||
|
|||
OnSelectionChanged(); |
|||
} |
|||
|
|||
private void OnSelectionChanged(SelectionModelSelectionChangedEventArgs? e = null) |
|||
{ |
|||
_selectedIndicesCached = null; |
|||
_selectedItemsCached = null; |
|||
|
|||
if (e != null) |
|||
{ |
|||
SelectionChanged?.Invoke(this, e); |
|||
|
|||
RaisePropertyChanged(nameof(SelectedIndex)); |
|||
RaisePropertyChanged(nameof(SelectedIndices)); |
|||
|
|||
if (_rootNode.Source != null) |
|||
{ |
|||
RaisePropertyChanged(nameof(SelectedItem)); |
|||
RaisePropertyChanged(nameof(SelectedItems)); |
|||
} |
|||
} |
|||
} |
|||
|
|||
private void SelectImpl(int index, bool select) |
|||
{ |
|||
if (_singleSelect) |
|||
{ |
|||
ClearSelection(resetAnchor: true); |
|||
} |
|||
|
|||
var selected = _rootNode.Select(index, select); |
|||
|
|||
if (selected) |
|||
{ |
|||
AnchorIndex = new IndexPath(index); |
|||
} |
|||
|
|||
OnSelectionChanged(); |
|||
} |
|||
|
|||
private void SelectWithGroupImpl(int groupIndex, int itemIndex, bool select) |
|||
{ |
|||
if (_singleSelect) |
|||
{ |
|||
ClearSelection(resetAnchor: true); |
|||
} |
|||
|
|||
var childNode = _rootNode.GetAt(groupIndex, true, new IndexPath(groupIndex, itemIndex)); |
|||
var selected = childNode!.Select(itemIndex, select); |
|||
|
|||
if (selected) |
|||
{ |
|||
AnchorIndex = new IndexPath(groupIndex, itemIndex); |
|||
} |
|||
|
|||
OnSelectionChanged(); |
|||
} |
|||
|
|||
private void SelectWithPathImpl(IndexPath index, bool select) |
|||
{ |
|||
bool selected = false; |
|||
|
|||
if (_singleSelect) |
|||
{ |
|||
ClearSelection(resetAnchor: true); |
|||
} |
|||
|
|||
SelectionTreeHelper.TraverseIndexPath( |
|||
_rootNode, |
|||
index, |
|||
true, |
|||
(currentNode, path, depth, childIndex) => |
|||
{ |
|||
if (depth == path.GetSize() - 1) |
|||
{ |
|||
selected = currentNode.Select(childIndex, select); |
|||
} |
|||
} |
|||
); |
|||
|
|||
if (selected) |
|||
{ |
|||
AnchorIndex = index; |
|||
} |
|||
|
|||
OnSelectionChanged(); |
|||
} |
|||
|
|||
private void SelectRangeFromAnchorImpl(int index, bool select) |
|||
{ |
|||
int anchorIndex = 0; |
|||
var anchor = AnchorIndex; |
|||
|
|||
if (anchor != null) |
|||
{ |
|||
anchorIndex = anchor.GetAt(0); |
|||
} |
|||
|
|||
_rootNode.SelectRange(new IndexRange(anchorIndex, index), select); |
|||
OnSelectionChanged(); |
|||
} |
|||
|
|||
private void SelectRangeFromAnchorWithGroupImpl(int endGroupIndex, int endItemIndex, bool select) |
|||
{ |
|||
var startGroupIndex = 0; |
|||
var startItemIndex = 0; |
|||
var anchorIndex = AnchorIndex; |
|||
|
|||
if (anchorIndex != null) |
|||
{ |
|||
startGroupIndex = anchorIndex.GetAt(0); |
|||
startItemIndex = anchorIndex.GetAt(1); |
|||
} |
|||
|
|||
// Make sure start > end
|
|||
if (startGroupIndex > endGroupIndex || |
|||
(startGroupIndex == endGroupIndex && startItemIndex > endItemIndex)) |
|||
{ |
|||
int temp = startGroupIndex; |
|||
startGroupIndex = endGroupIndex; |
|||
endGroupIndex = temp; |
|||
temp = startItemIndex; |
|||
startItemIndex = endItemIndex; |
|||
endItemIndex = temp; |
|||
} |
|||
|
|||
for (int groupIdx = startGroupIndex; groupIdx <= endGroupIndex; groupIdx++) |
|||
{ |
|||
var groupNode = _rootNode.GetAt(groupIdx, true, new IndexPath(endGroupIndex, endItemIndex))!; |
|||
int startIndex = groupIdx == startGroupIndex ? startItemIndex : 0; |
|||
int endIndex = groupIdx == endGroupIndex ? endItemIndex : groupNode.DataCount - 1; |
|||
groupNode.SelectRange(new IndexRange(startIndex, endIndex), select); |
|||
} |
|||
|
|||
OnSelectionChanged(); |
|||
} |
|||
|
|||
private void SelectRangeImpl(IndexPath start, IndexPath end, bool select) |
|||
{ |
|||
var winrtStart = start; |
|||
var winrtEnd = end; |
|||
|
|||
// Make sure start <= end
|
|||
if (winrtEnd.CompareTo(winrtStart) == -1) |
|||
{ |
|||
var temp = winrtStart; |
|||
winrtStart = winrtEnd; |
|||
winrtEnd = temp; |
|||
} |
|||
|
|||
// Note: Since we do not know the depth of the tree, we have to walk to each leaf
|
|||
SelectionTreeHelper.TraverseRangeRealizeChildren( |
|||
_rootNode, |
|||
winrtStart, |
|||
winrtEnd, |
|||
info => |
|||
{ |
|||
if (info.Path >= winrtStart && info.Path <= winrtEnd) |
|||
{ |
|||
info.ParentNode!.Select(info.Path.GetAt(info.Path.GetSize() - 1), select); |
|||
} |
|||
}); |
|||
|
|||
OnSelectionChanged(); |
|||
} |
|||
|
|||
private void BeginOperation() |
|||
{ |
|||
if (_operationCount++ == 0) |
|||
{ |
|||
_oldAnchorIndex = AnchorIndex; |
|||
_rootNode.BeginOperation(); |
|||
} |
|||
} |
|||
|
|||
private void EndOperation() |
|||
{ |
|||
if (_operationCount == 0) |
|||
{ |
|||
throw new AvaloniaInternalException("No selection operation in progress."); |
|||
} |
|||
|
|||
SelectionModelSelectionChangedEventArgs? e = null; |
|||
|
|||
if (--_operationCount == 0) |
|||
{ |
|||
ApplyAutoSelect(false); |
|||
|
|||
var changes = new List<SelectionNodeOperation>(); |
|||
_rootNode.EndOperation(changes); |
|||
|
|||
if (changes.Count > 0) |
|||
{ |
|||
var changeSet = new SelectionModelChangeSet(changes); |
|||
e = changeSet.CreateEventArgs(); |
|||
} |
|||
|
|||
OnSelectionChanged(e); |
|||
|
|||
if (_oldAnchorIndex != AnchorIndex) |
|||
{ |
|||
RaisePropertyChanged(nameof(AnchorIndex)); |
|||
} |
|||
|
|||
_rootNode.Cleanup(); |
|||
_oldAnchorIndex = default; |
|||
} |
|||
} |
|||
|
|||
private void ApplyAutoSelect(bool createOperation) |
|||
{ |
|||
if (AutoSelect) |
|||
{ |
|||
_selectedIndicesCached = null; |
|||
|
|||
if (SelectedIndex == default && _rootNode.ItemsSourceView?.Count > 0) |
|||
{ |
|||
if (createOperation) |
|||
{ |
|||
using var operation = new Operation(this); |
|||
SelectImpl(0, true); |
|||
} |
|||
else |
|||
{ |
|||
SelectImpl(0, true); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
internal class SelectedItemInfo : ISelectedItemInfo |
|||
{ |
|||
public SelectedItemInfo(SelectionNode node, IndexPath path) |
|||
{ |
|||
Node = node; |
|||
Path = path; |
|||
} |
|||
|
|||
public SelectionNode Node { get; } |
|||
public IndexPath Path { get; } |
|||
public int Count => Node.SelectedCount; |
|||
} |
|||
|
|||
private struct Operation : IDisposable |
|||
{ |
|||
private readonly SelectionModel _manager; |
|||
public Operation(SelectionModel manager) => (_manager = manager).BeginOperation(); |
|||
public void Dispose() => _manager.EndOperation(); |
|||
} |
|||
} |
|||
} |
|||
@ -1,170 +0,0 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
|
|||
#nullable enable |
|||
|
|||
namespace Avalonia.Controls |
|||
{ |
|||
internal class SelectionModelChangeSet |
|||
{ |
|||
private readonly List<SelectionNodeOperation> _changes; |
|||
|
|||
public SelectionModelChangeSet(List<SelectionNodeOperation> changes) |
|||
{ |
|||
_changes = changes; |
|||
} |
|||
|
|||
public SelectionModelSelectionChangedEventArgs CreateEventArgs() |
|||
{ |
|||
var deselectedIndexCount = 0; |
|||
var selectedIndexCount = 0; |
|||
var deselectedItemCount = 0; |
|||
var selectedItemCount = 0; |
|||
|
|||
foreach (var change in _changes) |
|||
{ |
|||
deselectedIndexCount += change.DeselectedCount; |
|||
selectedIndexCount += change.SelectedCount; |
|||
|
|||
if (change.Items != null) |
|||
{ |
|||
deselectedItemCount += change.DeselectedCount; |
|||
selectedItemCount += change.SelectedCount; |
|||
} |
|||
} |
|||
|
|||
var deselectedIndices = new SelectedItems<IndexPath, SelectionNodeOperation>( |
|||
_changes, |
|||
deselectedIndexCount, |
|||
GetDeselectedIndexAt); |
|||
var selectedIndices = new SelectedItems<IndexPath, SelectionNodeOperation>( |
|||
_changes, |
|||
selectedIndexCount, |
|||
GetSelectedIndexAt); |
|||
var deselectedItems = new SelectedItems<object?, SelectionNodeOperation>( |
|||
_changes, |
|||
deselectedItemCount, |
|||
GetDeselectedItemAt); |
|||
var selectedItems = new SelectedItems<object?, SelectionNodeOperation>( |
|||
_changes, |
|||
selectedItemCount, |
|||
GetSelectedItemAt); |
|||
|
|||
return new SelectionModelSelectionChangedEventArgs( |
|||
deselectedIndices, |
|||
selectedIndices, |
|||
deselectedItems, |
|||
selectedItems); |
|||
} |
|||
|
|||
private IndexPath GetDeselectedIndexAt( |
|||
List<SelectionNodeOperation> infos, |
|||
int index) |
|||
{ |
|||
static int GetCount(SelectionNodeOperation info) => info.DeselectedCount; |
|||
static List<IndexRange>? GetRanges(SelectionNodeOperation info) => info.DeselectedRanges; |
|||
return GetIndexAt(infos, index, x => GetCount(x), x => GetRanges(x)); |
|||
} |
|||
|
|||
private IndexPath GetSelectedIndexAt( |
|||
List<SelectionNodeOperation> infos, |
|||
int index) |
|||
{ |
|||
static int GetCount(SelectionNodeOperation info) => info.SelectedCount; |
|||
static List<IndexRange>? GetRanges(SelectionNodeOperation info) => info.SelectedRanges; |
|||
return GetIndexAt(infos, index, x => GetCount(x), x => GetRanges(x)); |
|||
} |
|||
|
|||
private object? GetDeselectedItemAt( |
|||
List<SelectionNodeOperation> infos, |
|||
int index) |
|||
{ |
|||
static int GetCount(SelectionNodeOperation info) => info.Items != null ? info.DeselectedCount : 0; |
|||
static List<IndexRange>? GetRanges(SelectionNodeOperation info) => info.DeselectedRanges; |
|||
return GetItemAt(infos, index, x => GetCount(x), x => GetRanges(x)); |
|||
} |
|||
|
|||
private object? GetSelectedItemAt( |
|||
List<SelectionNodeOperation> infos, |
|||
int index) |
|||
{ |
|||
static int GetCount(SelectionNodeOperation info) => info.Items != null ? info.SelectedCount : 0; |
|||
static List<IndexRange>? GetRanges(SelectionNodeOperation info) => info.SelectedRanges; |
|||
return GetItemAt(infos, index, x => GetCount(x), x => GetRanges(x)); |
|||
} |
|||
|
|||
private IndexPath GetIndexAt( |
|||
List<SelectionNodeOperation> infos, |
|||
int index, |
|||
Func<SelectionNodeOperation, int> getCount, |
|||
Func<SelectionNodeOperation, List<IndexRange>?> getRanges) |
|||
{ |
|||
var currentIndex = 0; |
|||
IndexPath path = default; |
|||
|
|||
foreach (var info in infos) |
|||
{ |
|||
var currentCount = getCount(info); |
|||
|
|||
if (index >= currentIndex && index < currentIndex + currentCount) |
|||
{ |
|||
int targetIndex = GetIndexAt(getRanges(info), index - currentIndex); |
|||
path = info.Path.CloneWithChildIndex(targetIndex); |
|||
break; |
|||
} |
|||
|
|||
currentIndex += currentCount; |
|||
} |
|||
|
|||
return path; |
|||
} |
|||
|
|||
private object? GetItemAt( |
|||
List<SelectionNodeOperation> infos, |
|||
int index, |
|||
Func<SelectionNodeOperation, int> getCount, |
|||
Func<SelectionNodeOperation, List<IndexRange>?> getRanges) |
|||
{ |
|||
var currentIndex = 0; |
|||
object? item = null; |
|||
|
|||
foreach (var info in infos) |
|||
{ |
|||
var currentCount = getCount(info); |
|||
|
|||
if (index >= currentIndex && index < currentIndex + currentCount) |
|||
{ |
|||
int targetIndex = GetIndexAt(getRanges(info), index - currentIndex); |
|||
item = info.Items?.Count > targetIndex ? info.Items?.GetAt(targetIndex) : null; |
|||
break; |
|||
} |
|||
|
|||
currentIndex += currentCount; |
|||
} |
|||
|
|||
return item; |
|||
} |
|||
|
|||
private int GetIndexAt(List<IndexRange>? ranges, int index) |
|||
{ |
|||
var currentIndex = 0; |
|||
|
|||
if (ranges != null) |
|||
{ |
|||
foreach (var range in ranges) |
|||
{ |
|||
var currentCount = (range.End - range.Begin) + 1; |
|||
|
|||
if (index >= currentIndex && index < currentIndex + currentCount) |
|||
{ |
|||
return range.Begin + (index - currentIndex); |
|||
} |
|||
|
|||
currentIndex += currentCount; |
|||
} |
|||
} |
|||
|
|||
throw new IndexOutOfRangeException(); |
|||
} |
|||
} |
|||
} |
|||
@ -1,103 +0,0 @@ |
|||
// This source file is adapted from the WinUI project.
|
|||
// (https://github.com/microsoft/microsoft-ui-xaml)
|
|||
//
|
|||
// Licensed to The Avalonia Project under MIT License, courtesy of The .NET Foundation.
|
|||
|
|||
using System; |
|||
|
|||
#nullable enable |
|||
|
|||
namespace Avalonia.Controls |
|||
{ |
|||
/// <summary>
|
|||
/// Provides data for the <see cref="SelectionModel.ChildrenRequested"/> event.
|
|||
/// </summary>
|
|||
public class SelectionModelChildrenRequestedEventArgs : EventArgs |
|||
{ |
|||
private object? _source; |
|||
private IndexPath _sourceIndexPath; |
|||
private IndexPath _finalIndexPath; |
|||
private bool _throwOnAccess; |
|||
|
|||
internal SelectionModelChildrenRequestedEventArgs( |
|||
object source, |
|||
IndexPath sourceIndexPath, |
|||
IndexPath finalIndexPath, |
|||
bool throwOnAccess) |
|||
{ |
|||
source = source ?? throw new ArgumentNullException(nameof(source)); |
|||
Initialize(source, sourceIndexPath, finalIndexPath, throwOnAccess); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets an observable which produces the children of the <see cref="Source"/>
|
|||
/// object.
|
|||
/// </summary>
|
|||
public IObservable<object?>? Children { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// Gets the object whose children are being requested.
|
|||
/// </summary>
|
|||
public object Source |
|||
{ |
|||
get |
|||
{ |
|||
if (_throwOnAccess) |
|||
{ |
|||
throw new ObjectDisposedException(nameof(SelectionModelChildrenRequestedEventArgs)); |
|||
} |
|||
|
|||
return _source!; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets the index of the object whose children are being requested.
|
|||
/// </summary>
|
|||
public IndexPath SourceIndex |
|||
{ |
|||
get |
|||
{ |
|||
if (_throwOnAccess) |
|||
{ |
|||
throw new ObjectDisposedException(nameof(SelectionModelChildrenRequestedEventArgs)); |
|||
} |
|||
|
|||
return _sourceIndexPath; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets the index of the final object which is being attempted to be retrieved.
|
|||
/// </summary>
|
|||
public IndexPath FinalIndex |
|||
{ |
|||
get |
|||
{ |
|||
if (_throwOnAccess) |
|||
{ |
|||
throw new ObjectDisposedException(nameof(SelectionModelChildrenRequestedEventArgs)); |
|||
} |
|||
|
|||
return _finalIndexPath; |
|||
} |
|||
} |
|||
|
|||
internal void Initialize( |
|||
object? source, |
|||
IndexPath sourceIndexPath, |
|||
IndexPath finalIndexPath, |
|||
bool throwOnAccess) |
|||
{ |
|||
if (!throwOnAccess && source == null) |
|||
{ |
|||
throw new ArgumentNullException(nameof(source)); |
|||
} |
|||
|
|||
_source = source; |
|||
_sourceIndexPath = sourceIndexPath; |
|||
_finalIndexPath = finalIndexPath; |
|||
_throwOnAccess = throwOnAccess; |
|||
} |
|||
} |
|||
} |
|||
@ -1,47 +0,0 @@ |
|||
// This source file is adapted from the WinUI project.
|
|||
// (https://github.com/microsoft/microsoft-ui-xaml)
|
|||
//
|
|||
// Licensed to The Avalonia Project under MIT License, courtesy of The .NET Foundation.
|
|||
|
|||
using System; |
|||
using System.Collections.Generic; |
|||
|
|||
#nullable enable |
|||
|
|||
namespace Avalonia.Controls |
|||
{ |
|||
public class SelectionModelSelectionChangedEventArgs : EventArgs |
|||
{ |
|||
public SelectionModelSelectionChangedEventArgs( |
|||
IReadOnlyList<IndexPath>? deselectedIndices, |
|||
IReadOnlyList<IndexPath>? selectedIndices, |
|||
IReadOnlyList<object?>? deselectedItems, |
|||
IReadOnlyList<object?>? selectedItems) |
|||
{ |
|||
DeselectedIndices = deselectedIndices ?? Array.Empty<IndexPath>(); |
|||
SelectedIndices = selectedIndices ?? Array.Empty<IndexPath>(); |
|||
DeselectedItems = deselectedItems ?? Array.Empty<object?>(); |
|||
SelectedItems= selectedItems ?? Array.Empty<object?>(); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets the indices of the items that were removed from the selection.
|
|||
/// </summary>
|
|||
public IReadOnlyList<IndexPath> DeselectedIndices { get; } |
|||
|
|||
/// <summary>
|
|||
/// Gets the indices of the items that were added to the selection.
|
|||
/// </summary>
|
|||
public IReadOnlyList<IndexPath> SelectedIndices { get; } |
|||
|
|||
/// <summary>
|
|||
/// Gets the items that were removed from the selection.
|
|||
/// </summary>
|
|||
public IReadOnlyList<object?> DeselectedItems { get; } |
|||
|
|||
/// <summary>
|
|||
/// Gets the items that were added to the selection.
|
|||
/// </summary>
|
|||
public IReadOnlyList<object?> SelectedItems { get; } |
|||
} |
|||
} |
|||
@ -1,971 +0,0 @@ |
|||
// This source file is adapted from the WinUI project.
|
|||
// (https://github.com/microsoft/microsoft-ui-xaml)
|
|||
//
|
|||
// Licensed to The Avalonia Project under MIT License, courtesy of The .NET Foundation.
|
|||
|
|||
using System; |
|||
using System.Collections; |
|||
using System.Collections.Generic; |
|||
using System.Collections.Specialized; |
|||
using System.Linq; |
|||
using Avalonia.Controls.Utils; |
|||
|
|||
#nullable enable |
|||
|
|||
namespace Avalonia.Controls |
|||
{ |
|||
/// <summary>
|
|||
/// Tracks nested selection.
|
|||
/// </summary>
|
|||
/// <remarks>
|
|||
/// SelectionNode is the internal tree data structure that we keep track of for selection in
|
|||
/// a nested scenario. This would map to one ItemsSourceView/Collection. This node reacts to
|
|||
/// collection changes and keeps the selected indices up to date. This can either be a leaf
|
|||
/// node or a non leaf node.
|
|||
/// </remarks>
|
|||
internal class SelectionNode : IDisposable |
|||
{ |
|||
private readonly SelectionModel _manager; |
|||
private readonly List<SelectionNode?> _childrenNodes = new List<SelectionNode?>(); |
|||
private readonly SelectionNode? _parent; |
|||
private readonly List<IndexRange> _selected = new List<IndexRange>(); |
|||
private readonly List<int> _selectedIndicesCached = new List<int>(); |
|||
private IDisposable? _childrenSubscription; |
|||
private SelectionNodeOperation? _operation; |
|||
private object? _source; |
|||
private bool _selectedIndicesCacheIsValid; |
|||
private bool _retainSelectionOnReset; |
|||
private List<object?>? _selectedItems; |
|||
|
|||
public SelectionNode(SelectionModel manager, SelectionNode? parent) |
|||
{ |
|||
_manager = manager; |
|||
_parent = parent; |
|||
} |
|||
|
|||
public int AnchorIndex { get; set; } = -1; |
|||
|
|||
public bool RetainSelectionOnReset |
|||
{ |
|||
get => _retainSelectionOnReset; |
|||
set |
|||
{ |
|||
if (_retainSelectionOnReset != value) |
|||
{ |
|||
_retainSelectionOnReset = value; |
|||
|
|||
if (_retainSelectionOnReset) |
|||
{ |
|||
_selectedItems = new List<object?>(); |
|||
PopulateSelectedItemsFromSelectedIndices(); |
|||
} |
|||
else |
|||
{ |
|||
_selectedItems = null; |
|||
} |
|||
|
|||
foreach (var child in _childrenNodes) |
|||
{ |
|||
if (child != null) |
|||
{ |
|||
child.RetainSelectionOnReset = value; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
public object? Source |
|||
{ |
|||
get => _source; |
|||
set |
|||
{ |
|||
if (_source != value) |
|||
{ |
|||
if (_source != null) |
|||
{ |
|||
ClearSelection(); |
|||
ClearChildNodes(); |
|||
UnhookCollectionChangedHandler(); |
|||
} |
|||
|
|||
_source = value; |
|||
|
|||
// Setup ItemsSourceView
|
|||
var newDataSource = value as ItemsSourceView; |
|||
|
|||
if (value != null && newDataSource == null) |
|||
{ |
|||
newDataSource = new ItemsSourceView((IEnumerable)value); |
|||
} |
|||
|
|||
ItemsSourceView = newDataSource; |
|||
|
|||
TrimInvalidSelections(); |
|||
PopulateSelectedItemsFromSelectedIndices(); |
|||
HookupCollectionChangedHandler(); |
|||
OnSelectionChanged(); |
|||
} |
|||
} |
|||
} |
|||
|
|||
private void TrimInvalidSelections() |
|||
{ |
|||
if (_selected == null || ItemsSourceView == null) |
|||
{ |
|||
return; |
|||
} |
|||
|
|||
var validRange = ItemsSourceView.Count > 0 ? new IndexRange(0, ItemsSourceView.Count - 1) : new IndexRange(-1, -1); |
|||
var removed = new List<IndexRange>(); |
|||
var removedCount = IndexRange.Intersect(_selected, validRange, removed); |
|||
|
|||
if (removedCount > 0) |
|||
{ |
|||
using var operation = _manager.Update(); |
|||
SelectedCount -= removedCount; |
|||
OnSelectionChanged(); |
|||
_operation!.Deselected(removed); |
|||
} |
|||
} |
|||
|
|||
public ItemsSourceView? ItemsSourceView { get; private set; } |
|||
public int DataCount => ItemsSourceView?.Count ?? 0; |
|||
public int ChildrenNodeCount => _childrenNodes.Count; |
|||
public int RealizedChildrenNodeCount { get; private set; } |
|||
|
|||
public IndexPath IndexPath |
|||
{ |
|||
get |
|||
{ |
|||
var path = new List<int>(); ; |
|||
var parent = _parent; |
|||
var child = this; |
|||
|
|||
while (parent != null) |
|||
{ |
|||
var childNodes = parent._childrenNodes; |
|||
var index = childNodes.IndexOf(child); |
|||
|
|||
// We are walking up to the parent, so the path will be backwards
|
|||
path.Insert(0, index); |
|||
child = parent; |
|||
parent = parent._parent; |
|||
} |
|||
|
|||
return new IndexPath(path); |
|||
} |
|||
} |
|||
|
|||
// For a genuine tree view, we dont know which node is leaf until we
|
|||
// actually walk to it, so currently the tree builds up to the leaf. I don't
|
|||
// create a bunch of leaf node instances - instead i use the same instance m_leafNode to avoid
|
|||
// an explosion of node objects. However, I'm still creating the m_childrenNodes
|
|||
// collection unfortunately.
|
|||
public SelectionNode? GetAt(int index, bool realizeChild, IndexPath finalIndexPath) |
|||
{ |
|||
SelectionNode? child = null; |
|||
|
|||
if (realizeChild) |
|||
{ |
|||
if (ItemsSourceView == null || index < 0 || index >= ItemsSourceView.Count) |
|||
{ |
|||
throw new IndexOutOfRangeException(); |
|||
} |
|||
|
|||
if (_childrenNodes.Count == 0) |
|||
{ |
|||
if (ItemsSourceView != null) |
|||
{ |
|||
for (int i = 0; i < ItemsSourceView.Count; i++) |
|||
{ |
|||
_childrenNodes.Add(null); |
|||
} |
|||
} |
|||
} |
|||
|
|||
if (_childrenNodes[index] == null) |
|||
{ |
|||
var childData = ItemsSourceView!.GetAt(index); |
|||
IObservable<object?>? resolver = null; |
|||
|
|||
if (childData != null) |
|||
{ |
|||
var childDataIndexPath = IndexPath.CloneWithChildIndex(index); |
|||
resolver = _manager.ResolvePath(childData, childDataIndexPath, finalIndexPath); |
|||
} |
|||
|
|||
if (resolver != null) |
|||
{ |
|||
child = new SelectionNode(_manager, parent: this); |
|||
child.SetChildrenObservable(resolver); |
|||
} |
|||
else if (childData is IEnumerable<object> || childData is IList) |
|||
{ |
|||
child = new SelectionNode(_manager, parent: this); |
|||
child.Source = childData; |
|||
} |
|||
else |
|||
{ |
|||
child = _manager.SharedLeafNode; |
|||
} |
|||
|
|||
if (_operation != null && child != _manager.SharedLeafNode) |
|||
{ |
|||
child.BeginOperation(); |
|||
} |
|||
|
|||
_childrenNodes[index] = child; |
|||
RealizedChildrenNodeCount++; |
|||
} |
|||
else |
|||
{ |
|||
child = _childrenNodes[index]; |
|||
} |
|||
} |
|||
else |
|||
{ |
|||
if (_childrenNodes.Count > 0) |
|||
{ |
|||
child = _childrenNodes[index]; |
|||
} |
|||
} |
|||
|
|||
return child; |
|||
} |
|||
|
|||
public void SetChildrenObservable(IObservable<object?> resolver) |
|||
{ |
|||
_childrenSubscription = resolver.Subscribe(x => |
|||
{ |
|||
if (Source != null) |
|||
{ |
|||
using (_manager.Update()) |
|||
{ |
|||
SelectionTreeHelper.Traverse( |
|||
this, |
|||
realizeChildren: false, |
|||
info => info.Node.Clear()); |
|||
} |
|||
} |
|||
|
|||
Source = x; |
|||
}); |
|||
} |
|||
|
|||
public int SelectedCount { get; private set; } |
|||
|
|||
public bool IsSelected(int index) |
|||
{ |
|||
var isSelected = false; |
|||
|
|||
foreach (var range in _selected) |
|||
{ |
|||
if (range.Contains(index)) |
|||
{ |
|||
isSelected = true; |
|||
break; |
|||
} |
|||
} |
|||
|
|||
return isSelected; |
|||
} |
|||
|
|||
// True -> Selected
|
|||
// False -> Not Selected
|
|||
// Null -> Some descendents are selected and some are not
|
|||
public bool? IsSelectedWithPartial() |
|||
{ |
|||
var isSelected = (bool?)false; |
|||
|
|||
if (_parent != null) |
|||
{ |
|||
var parentsChildren = _parent._childrenNodes; |
|||
|
|||
var myIndexInParent = parentsChildren.IndexOf(this); |
|||
|
|||
if (myIndexInParent != -1) |
|||
{ |
|||
isSelected = _parent.IsSelectedWithPartial(myIndexInParent); |
|||
} |
|||
} |
|||
|
|||
return isSelected; |
|||
} |
|||
|
|||
// True -> Selected
|
|||
// False -> Not Selected
|
|||
// Null -> Some descendents are selected and some are not
|
|||
public bool? IsSelectedWithPartial(int index) |
|||
{ |
|||
SelectionState selectionState; |
|||
|
|||
if (_childrenNodes.Count == 0 || // no nodes realized
|
|||
_childrenNodes.Count <= index || // target node is not realized
|
|||
_childrenNodes[index] == null || // target node is not realized
|
|||
_childrenNodes[index] == _manager.SharedLeafNode) // target node is a leaf node.
|
|||
{ |
|||
// Ask parent if the target node is selected.
|
|||
selectionState = IsSelected(index) ? SelectionState.Selected : SelectionState.NotSelected; |
|||
} |
|||
else |
|||
{ |
|||
// targetNode is the node representing the index. This node is the parent.
|
|||
// targetNode is a non-leaf node, containing one or many children nodes. Evaluate
|
|||
// based on children of targetNode.
|
|||
var targetNode = _childrenNodes[index]; |
|||
selectionState = targetNode!.EvaluateIsSelectedBasedOnChildrenNodes(); |
|||
} |
|||
|
|||
return ConvertToNullableBool(selectionState); |
|||
} |
|||
|
|||
public int SelectedIndex |
|||
{ |
|||
get => SelectedCount > 0 ? SelectedIndices[0] : -1; |
|||
set |
|||
{ |
|||
if (IsValidIndex(value) && (SelectedCount != 1 || !IsSelected(value))) |
|||
{ |
|||
ClearSelection(); |
|||
|
|||
if (value != -1) |
|||
{ |
|||
Select(value, true); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
public List<int> SelectedIndices |
|||
{ |
|||
get |
|||
{ |
|||
if (!_selectedIndicesCacheIsValid) |
|||
{ |
|||
_selectedIndicesCacheIsValid = true; |
|||
|
|||
foreach (var range in _selected) |
|||
{ |
|||
for (int index = range.Begin; index <= range.End; index++) |
|||
{ |
|||
// Avoid duplicates
|
|||
if (!_selectedIndicesCached.Contains(index)) |
|||
{ |
|||
_selectedIndicesCached.Add(index); |
|||
} |
|||
} |
|||
} |
|||
|
|||
// Sort the list for easy consumption
|
|||
_selectedIndicesCached.Sort(); |
|||
} |
|||
|
|||
return _selectedIndicesCached; |
|||
} |
|||
} |
|||
|
|||
public IEnumerable<object> SelectedItems |
|||
{ |
|||
get => SelectedIndices.Select(x => ItemsSourceView!.GetAt(x)); |
|||
} |
|||
|
|||
public void Dispose() |
|||
{ |
|||
_childrenSubscription?.Dispose(); |
|||
ItemsSourceView?.Dispose(); |
|||
ClearChildNodes(); |
|||
UnhookCollectionChangedHandler(); |
|||
} |
|||
|
|||
public void BeginOperation() |
|||
{ |
|||
if (_operation != null) |
|||
{ |
|||
throw new AvaloniaInternalException("Selection operation already in progress."); |
|||
} |
|||
|
|||
_operation = new SelectionNodeOperation(this); |
|||
|
|||
for (var i = 0; i < _childrenNodes.Count; ++i) |
|||
{ |
|||
var child = _childrenNodes[i]; |
|||
|
|||
if (child != null && child != _manager.SharedLeafNode) |
|||
{ |
|||
child.BeginOperation(); |
|||
} |
|||
} |
|||
} |
|||
|
|||
public void EndOperation(List<SelectionNodeOperation> changes) |
|||
{ |
|||
if (_operation == null) |
|||
{ |
|||
throw new AvaloniaInternalException("No selection operation in progress."); |
|||
} |
|||
|
|||
if (_operation.HasChanges) |
|||
{ |
|||
changes.Add(_operation); |
|||
} |
|||
|
|||
_operation = null; |
|||
|
|||
for (var i = 0; i < _childrenNodes.Count; ++i) |
|||
{ |
|||
var child = _childrenNodes[i]; |
|||
|
|||
if (child != null && child != _manager.SharedLeafNode) |
|||
{ |
|||
child.EndOperation(changes); |
|||
} |
|||
} |
|||
} |
|||
|
|||
public bool Cleanup() |
|||
{ |
|||
var result = SelectedCount == 0; |
|||
|
|||
for (var i = 0; i < _childrenNodes.Count; ++i) |
|||
{ |
|||
var child = _childrenNodes[i]; |
|||
|
|||
if (child != null) |
|||
{ |
|||
if (child.Cleanup()) |
|||
{ |
|||
child.Dispose(); |
|||
_childrenNodes[i] = null; |
|||
} |
|||
else |
|||
{ |
|||
result = false; |
|||
} |
|||
} |
|||
} |
|||
|
|||
return result; |
|||
} |
|||
|
|||
public bool Select(int index, bool select) |
|||
{ |
|||
return Select(index, select, raiseOnSelectionChanged: true); |
|||
} |
|||
|
|||
public bool ToggleSelect(int index) |
|||
{ |
|||
return Select(index, !IsSelected(index)); |
|||
} |
|||
|
|||
public void SelectAll() |
|||
{ |
|||
if (ItemsSourceView != null) |
|||
{ |
|||
var size = ItemsSourceView.Count; |
|||
|
|||
if (size > 0) |
|||
{ |
|||
SelectRange(new IndexRange(0, size - 1), select: true); |
|||
} |
|||
} |
|||
} |
|||
|
|||
public void Clear() => ClearSelection(); |
|||
|
|||
public bool SelectRange(IndexRange range, bool select) |
|||
{ |
|||
if (IsValidIndex(range.Begin) && IsValidIndex(range.End)) |
|||
{ |
|||
if (select) |
|||
{ |
|||
AddRange(range, raiseOnSelectionChanged: true); |
|||
} |
|||
else |
|||
{ |
|||
RemoveRange(range, raiseOnSelectionChanged: true); |
|||
} |
|||
|
|||
return true; |
|||
} |
|||
|
|||
return false; |
|||
} |
|||
|
|||
private void HookupCollectionChangedHandler() |
|||
{ |
|||
if (ItemsSourceView != null) |
|||
{ |
|||
ItemsSourceView.CollectionChanged += OnSourceListChanged; |
|||
} |
|||
} |
|||
|
|||
private void UnhookCollectionChangedHandler() |
|||
{ |
|||
if (ItemsSourceView != null) |
|||
{ |
|||
ItemsSourceView.CollectionChanged -= OnSourceListChanged; |
|||
} |
|||
} |
|||
|
|||
private bool IsValidIndex(int index) |
|||
{ |
|||
return ItemsSourceView == null || (index >= 0 && index < ItemsSourceView.Count); |
|||
} |
|||
|
|||
private void AddRange(IndexRange addRange, bool raiseOnSelectionChanged) |
|||
{ |
|||
var selected = new List<IndexRange>(); |
|||
|
|||
SelectedCount += IndexRange.Add(_selected, addRange, selected); |
|||
|
|||
if (selected.Count > 0) |
|||
{ |
|||
_operation?.Selected(selected); |
|||
|
|||
if (_selectedItems != null && ItemsSourceView != null) |
|||
{ |
|||
for (var i = addRange.Begin; i <= addRange.End; ++i) |
|||
{ |
|||
_selectedItems.Add(ItemsSourceView!.GetAt(i)); |
|||
} |
|||
} |
|||
|
|||
if (raiseOnSelectionChanged) |
|||
{ |
|||
OnSelectionChanged(); |
|||
} |
|||
} |
|||
} |
|||
|
|||
private void RemoveRange(IndexRange removeRange, bool raiseOnSelectionChanged) |
|||
{ |
|||
var removed = new List<IndexRange>(); |
|||
|
|||
SelectedCount -= IndexRange.Remove(_selected, removeRange, removed); |
|||
|
|||
if (removed.Count > 0) |
|||
{ |
|||
_operation?.Deselected(removed); |
|||
|
|||
if (_selectedItems != null) |
|||
{ |
|||
for (var i = removeRange.Begin; i <= removeRange.End; ++i) |
|||
{ |
|||
_selectedItems.Remove(ItemsSourceView!.GetAt(i)); |
|||
} |
|||
} |
|||
|
|||
if (raiseOnSelectionChanged) |
|||
{ |
|||
OnSelectionChanged(); |
|||
} |
|||
} |
|||
} |
|||
|
|||
private void ClearSelection() |
|||
{ |
|||
// Deselect all items
|
|||
if (_selected.Count > 0) |
|||
{ |
|||
_operation?.Deselected(_selected); |
|||
_selected.Clear(); |
|||
OnSelectionChanged(); |
|||
} |
|||
|
|||
_selectedItems?.Clear(); |
|||
SelectedCount = 0; |
|||
AnchorIndex = -1; |
|||
} |
|||
|
|||
private void ClearChildNodes() |
|||
{ |
|||
for (int i = 0; i < _childrenNodes.Count; i++) |
|||
{ |
|||
var child = _childrenNodes[i]; |
|||
|
|||
if (child != null && child != _manager.SharedLeafNode) |
|||
{ |
|||
child.Dispose(); |
|||
_childrenNodes[i] = null; |
|||
} |
|||
} |
|||
|
|||
RealizedChildrenNodeCount = 0; |
|||
} |
|||
|
|||
private bool Select(int index, bool select, bool raiseOnSelectionChanged) |
|||
{ |
|||
if (IsValidIndex(index)) |
|||
{ |
|||
// Ignore duplicate selection calls
|
|||
if (IsSelected(index) == select) |
|||
{ |
|||
return true; |
|||
} |
|||
|
|||
var range = new IndexRange(index, index); |
|||
|
|||
if (select) |
|||
{ |
|||
AddRange(range, raiseOnSelectionChanged); |
|||
} |
|||
else |
|||
{ |
|||
RemoveRange(range, raiseOnSelectionChanged); |
|||
} |
|||
|
|||
return true; |
|||
} |
|||
|
|||
return false; |
|||
} |
|||
|
|||
private void OnSourceListChanged(object dataSource, NotifyCollectionChangedEventArgs args) |
|||
{ |
|||
bool selectionInvalidated = false; |
|||
List<object?>? removed = null; |
|||
|
|||
switch (args.Action) |
|||
{ |
|||
case NotifyCollectionChangedAction.Add: |
|||
{ |
|||
selectionInvalidated = OnItemsAdded(args.NewStartingIndex, args.NewItems.Count); |
|||
break; |
|||
} |
|||
|
|||
case NotifyCollectionChangedAction.Remove: |
|||
{ |
|||
(selectionInvalidated, removed) = OnItemsRemoved(args.OldStartingIndex, args.OldItems); |
|||
break; |
|||
} |
|||
|
|||
case NotifyCollectionChangedAction.Reset: |
|||
{ |
|||
if (_selectedItems == null) |
|||
{ |
|||
ClearSelection(); |
|||
} |
|||
else |
|||
{ |
|||
removed = RecreateSelectionFromSelectedItems(); |
|||
} |
|||
|
|||
selectionInvalidated = true; |
|||
break; |
|||
} |
|||
|
|||
case NotifyCollectionChangedAction.Replace: |
|||
{ |
|||
(selectionInvalidated, removed) = OnItemsRemoved(args.OldStartingIndex, args.OldItems); |
|||
selectionInvalidated |= OnItemsAdded(args.NewStartingIndex, args.NewItems.Count); |
|||
break; |
|||
} |
|||
} |
|||
|
|||
if (selectionInvalidated) |
|||
{ |
|||
OnSelectionChanged(); |
|||
} |
|||
|
|||
_manager.OnSelectionInvalidatedDueToCollectionChange(selectionInvalidated, removed); |
|||
} |
|||
|
|||
private bool OnItemsAdded(int index, int count) |
|||
{ |
|||
var selectionInvalidated = false; |
|||
|
|||
// Update ranges for leaf items
|
|||
var toAdd = new List<IndexRange>(); |
|||
|
|||
for (int i = 0; i < _selected.Count; i++) |
|||
{ |
|||
var range = _selected[i]; |
|||
|
|||
// The range is after the inserted items, need to shift the range right
|
|||
if (range.End >= index) |
|||
{ |
|||
int begin = range.Begin; |
|||
|
|||
// If the index left of newIndex is inside the range,
|
|||
// Split the range and remember the left piece to add later
|
|||
if (range.Contains(index - 1)) |
|||
{ |
|||
range.Split(index - 1, out var before, out _); |
|||
toAdd.Add(before); |
|||
begin = index; |
|||
} |
|||
|
|||
// Shift the range to the right
|
|||
_selected[i] = new IndexRange(begin + count, range.End + count); |
|||
selectionInvalidated = true; |
|||
} |
|||
} |
|||
|
|||
// Add the left sides of the split ranges
|
|||
_selected.AddRange(toAdd); |
|||
|
|||
// Update for non-leaf if we are tracking non-leaf nodes
|
|||
if (_childrenNodes.Count > 0) |
|||
{ |
|||
selectionInvalidated = true; |
|||
for (int i = 0; i < count; i++) |
|||
{ |
|||
_childrenNodes.Insert(index, null); |
|||
} |
|||
} |
|||
|
|||
// Adjust the anchor
|
|||
if (AnchorIndex >= index) |
|||
{ |
|||
AnchorIndex += count; |
|||
} |
|||
|
|||
// Check if adding a node invalidated an ancestors
|
|||
// selection state. For example if parent was selected before
|
|||
// adding a new item makes the parent partially selected now.
|
|||
if (!selectionInvalidated) |
|||
{ |
|||
var parent = _parent; |
|||
|
|||
while (parent != null) |
|||
{ |
|||
var isSelected = parent.IsSelectedWithPartial(); |
|||
|
|||
// If a parent is selected, then it will become partially selected.
|
|||
// If it is not selected or partially selected - there is no change.
|
|||
if (isSelected == true) |
|||
{ |
|||
selectionInvalidated = true; |
|||
break; |
|||
} |
|||
|
|||
parent = parent._parent; |
|||
} |
|||
} |
|||
|
|||
return selectionInvalidated; |
|||
} |
|||
|
|||
private (bool, List<object?>) OnItemsRemoved(int index, IList items) |
|||
{ |
|||
var selectionInvalidated = false; |
|||
var removed = new List<object?>(); |
|||
var count = items.Count; |
|||
var isSelected = false; |
|||
|
|||
for (int i = 0; i <= count - 1; i++) |
|||
{ |
|||
if (IsSelected(index + i)) |
|||
{ |
|||
isSelected = true; |
|||
removed.Add(items[i]); |
|||
} |
|||
} |
|||
|
|||
if (isSelected) |
|||
{ |
|||
var removeRange = new IndexRange(index, index + count - 1); |
|||
SelectedCount -= IndexRange.Remove(_selected, removeRange); |
|||
selectionInvalidated = true; |
|||
|
|||
if (_selectedItems != null) |
|||
{ |
|||
foreach (var i in items) |
|||
{ |
|||
_selectedItems.Remove(i); |
|||
} |
|||
} |
|||
} |
|||
|
|||
for (int i = 0; i < _selected.Count; i++) |
|||
{ |
|||
var range = _selected[i]; |
|||
|
|||
// The range is after the removed items, need to shift the range left
|
|||
if (range.End > index) |
|||
{ |
|||
// Shift the range to the left
|
|||
_selected[i] = new IndexRange(range.Begin - count, range.End - count); |
|||
selectionInvalidated = true; |
|||
} |
|||
} |
|||
|
|||
// Update for non-leaf if we are tracking non-leaf nodes
|
|||
if (_childrenNodes.Count > 0) |
|||
{ |
|||
selectionInvalidated = true; |
|||
for (int i = 0; i < count; i++) |
|||
{ |
|||
if (_childrenNodes[index] != null) |
|||
{ |
|||
removed.AddRange(_childrenNodes[index]!.SelectedItems); |
|||
RealizedChildrenNodeCount--; |
|||
_childrenNodes[index]!.Dispose(); |
|||
} |
|||
_childrenNodes.RemoveAt(index); |
|||
} |
|||
} |
|||
|
|||
//Adjust the anchor
|
|||
if (AnchorIndex >= index) |
|||
{ |
|||
AnchorIndex -= count; |
|||
} |
|||
|
|||
return (selectionInvalidated, removed); |
|||
} |
|||
|
|||
private void OnSelectionChanged() |
|||
{ |
|||
_selectedIndicesCacheIsValid = false; |
|||
_selectedIndicesCached.Clear(); |
|||
} |
|||
|
|||
public static bool? ConvertToNullableBool(SelectionState isSelected) |
|||
{ |
|||
bool? result = null; // PartialySelected
|
|||
|
|||
if (isSelected == SelectionState.Selected) |
|||
{ |
|||
result = true; |
|||
} |
|||
else if (isSelected == SelectionState.NotSelected) |
|||
{ |
|||
result = false; |
|||
} |
|||
|
|||
return result; |
|||
} |
|||
|
|||
public SelectionState EvaluateIsSelectedBasedOnChildrenNodes() |
|||
{ |
|||
var selectionState = SelectionState.NotSelected; |
|||
int realizedChildrenNodeCount = RealizedChildrenNodeCount; |
|||
int selectedCount = SelectedCount; |
|||
|
|||
if (realizedChildrenNodeCount != 0 || selectedCount != 0) |
|||
{ |
|||
// There are realized children or some selected leaves.
|
|||
int dataCount = DataCount; |
|||
if (realizedChildrenNodeCount == 0 && selectedCount > 0) |
|||
{ |
|||
// All nodes are leaves under it - we didn't create children nodes as an optimization.
|
|||
// See if all/some or none of the leaves are selected.
|
|||
selectionState = dataCount != selectedCount ? |
|||
SelectionState.PartiallySelected : |
|||
dataCount == selectedCount ? SelectionState.Selected : SelectionState.NotSelected; |
|||
} |
|||
else |
|||
{ |
|||
// There are child nodes, walk them individually and evaluate based on each child
|
|||
// being selected/not selected or partially selected.
|
|||
selectedCount = 0; |
|||
int notSelectedCount = 0; |
|||
for (int i = 0; i < ChildrenNodeCount; i++) |
|||
{ |
|||
var child = GetAt(i, false, default); |
|||
|
|||
if (child != null) |
|||
{ |
|||
// child is realized, ask it.
|
|||
var isChildSelected = IsSelectedWithPartial(i); |
|||
if (isChildSelected == null) |
|||
{ |
|||
selectionState = SelectionState.PartiallySelected; |
|||
break; |
|||
} |
|||
else if (isChildSelected == true) |
|||
{ |
|||
selectedCount++; |
|||
} |
|||
else |
|||
{ |
|||
notSelectedCount++; |
|||
} |
|||
} |
|||
else |
|||
{ |
|||
// not realized.
|
|||
if (IsSelected(i)) |
|||
{ |
|||
selectedCount++; |
|||
} |
|||
else |
|||
{ |
|||
notSelectedCount++; |
|||
} |
|||
} |
|||
|
|||
if (selectedCount > 0 && notSelectedCount > 0) |
|||
{ |
|||
selectionState = SelectionState.PartiallySelected; |
|||
break; |
|||
} |
|||
} |
|||
|
|||
if (selectionState != SelectionState.PartiallySelected) |
|||
{ |
|||
if (selectedCount != 0 && selectedCount != dataCount) |
|||
{ |
|||
selectionState = SelectionState.PartiallySelected; |
|||
} |
|||
else |
|||
{ |
|||
selectionState = selectedCount == dataCount ? SelectionState.Selected : SelectionState.NotSelected; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
return selectionState; |
|||
} |
|||
|
|||
private void PopulateSelectedItemsFromSelectedIndices() |
|||
{ |
|||
if (_selectedItems != null) |
|||
{ |
|||
_selectedItems.Clear(); |
|||
|
|||
foreach (var i in SelectedIndices) |
|||
{ |
|||
_selectedItems.Add(ItemsSourceView!.GetAt(i)); |
|||
} |
|||
} |
|||
} |
|||
|
|||
private List<object?> RecreateSelectionFromSelectedItems() |
|||
{ |
|||
var removed = new List<object?>(); |
|||
|
|||
_selected.Clear(); |
|||
SelectedCount = 0; |
|||
|
|||
for (var i = 0; i < _selectedItems!.Count; ++i) |
|||
{ |
|||
var item = _selectedItems[i]; |
|||
var index = ItemsSourceView!.IndexOf(item); |
|||
|
|||
if (index != -1) |
|||
{ |
|||
IndexRange.Add(_selected, new IndexRange(index, index)); |
|||
++SelectedCount; |
|||
} |
|||
else |
|||
{ |
|||
removed.Add(item); |
|||
_selectedItems.RemoveAt(i--); |
|||
} |
|||
} |
|||
|
|||
return removed; |
|||
} |
|||
|
|||
public enum SelectionState |
|||
{ |
|||
Selected, |
|||
NotSelected, |
|||
PartiallySelected |
|||
} |
|||
} |
|||
} |
|||
@ -1,110 +0,0 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
|
|||
#nullable enable |
|||
|
|||
namespace Avalonia.Controls |
|||
{ |
|||
internal class SelectionNodeOperation : ISelectedItemInfo |
|||
{ |
|||
private readonly SelectionNode _owner; |
|||
private List<IndexRange>? _selected; |
|||
private List<IndexRange>? _deselected; |
|||
private int _selectedCount = -1; |
|||
private int _deselectedCount = -1; |
|||
|
|||
public SelectionNodeOperation(SelectionNode owner) |
|||
{ |
|||
_owner = owner; |
|||
} |
|||
|
|||
public bool HasChanges => _selected?.Count > 0 || _deselected?.Count > 0; |
|||
public List<IndexRange>? SelectedRanges => _selected; |
|||
public List<IndexRange>? DeselectedRanges => _deselected; |
|||
public IndexPath Path => _owner.IndexPath; |
|||
public ItemsSourceView? Items => _owner.ItemsSourceView; |
|||
|
|||
public int SelectedCount |
|||
{ |
|||
get |
|||
{ |
|||
if (_selectedCount == -1) |
|||
{ |
|||
_selectedCount = (_selected != null) ? IndexRange.GetCount(_selected) : 0; |
|||
} |
|||
|
|||
return _selectedCount; |
|||
} |
|||
} |
|||
|
|||
public int DeselectedCount |
|||
{ |
|||
get |
|||
{ |
|||
if (_deselectedCount == -1) |
|||
{ |
|||
_deselectedCount = (_deselected != null) ? IndexRange.GetCount(_deselected) : 0; |
|||
} |
|||
|
|||
return _deselectedCount; |
|||
} |
|||
} |
|||
|
|||
public void Selected(IndexRange range) |
|||
{ |
|||
Add(range, ref _selected, _deselected); |
|||
_selectedCount = -1; |
|||
} |
|||
|
|||
public void Selected(IEnumerable<IndexRange> ranges) |
|||
{ |
|||
foreach (var range in ranges) |
|||
{ |
|||
Selected(range); |
|||
} |
|||
} |
|||
|
|||
public void Deselected(IndexRange range) |
|||
{ |
|||
Add(range, ref _deselected, _selected); |
|||
_deselectedCount = -1; |
|||
} |
|||
|
|||
public void Deselected(IEnumerable<IndexRange> ranges) |
|||
{ |
|||
foreach (var range in ranges) |
|||
{ |
|||
Deselected(range); |
|||
} |
|||
} |
|||
|
|||
private static void Add( |
|||
IndexRange range, |
|||
ref List<IndexRange>? add, |
|||
List<IndexRange>? remove) |
|||
{ |
|||
if (remove != null) |
|||
{ |
|||
var removed = new List<IndexRange>(); |
|||
IndexRange.Remove(remove, range, removed); |
|||
var selected = IndexRange.Subtract(range, removed); |
|||
|
|||
if (selected.Any()) |
|||
{ |
|||
add ??= new List<IndexRange>(); |
|||
|
|||
foreach (var r in selected) |
|||
{ |
|||
IndexRange.Add(add, r); |
|||
} |
|||
} |
|||
} |
|||
else |
|||
{ |
|||
add ??= new List<IndexRange>(); |
|||
IndexRange.Add(add, range); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,156 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Collections.Specialized; |
|||
using System.Linq; |
|||
using System.Runtime.CompilerServices; |
|||
using Avalonia.Threading; |
|||
using Avalonia.Utilities; |
|||
|
|||
#nullable enable |
|||
|
|||
namespace Avalonia.Controls.Utils |
|||
{ |
|||
internal interface ICollectionChangedListener |
|||
{ |
|||
void PreChanged(INotifyCollectionChanged sender, NotifyCollectionChangedEventArgs e); |
|||
void Changed(INotifyCollectionChanged sender, NotifyCollectionChangedEventArgs e); |
|||
void PostChanged(INotifyCollectionChanged sender, NotifyCollectionChangedEventArgs e); |
|||
} |
|||
|
|||
internal class CollectionChangedEventManager |
|||
{ |
|||
public static CollectionChangedEventManager Instance { get; } = new CollectionChangedEventManager(); |
|||
|
|||
private ConditionalWeakTable<INotifyCollectionChanged, Entry> _entries = |
|||
new ConditionalWeakTable<INotifyCollectionChanged, Entry>(); |
|||
|
|||
private CollectionChangedEventManager() |
|||
{ |
|||
} |
|||
|
|||
public void AddListener(INotifyCollectionChanged collection, ICollectionChangedListener listener) |
|||
{ |
|||
collection = collection ?? throw new ArgumentNullException(nameof(collection)); |
|||
listener = listener ?? throw new ArgumentNullException(nameof(listener)); |
|||
Dispatcher.UIThread.VerifyAccess(); |
|||
|
|||
if (!_entries.TryGetValue(collection, out var entry)) |
|||
{ |
|||
entry = new Entry(collection); |
|||
_entries.Add(collection, entry); |
|||
} |
|||
|
|||
foreach (var l in entry.Listeners) |
|||
{ |
|||
if (l.TryGetTarget(out var target) && target == listener) |
|||
{ |
|||
throw new InvalidOperationException( |
|||
"Collection listener already added for this collection/listener combination."); |
|||
} |
|||
} |
|||
|
|||
entry.Listeners.Add(new WeakReference<ICollectionChangedListener>(listener)); |
|||
} |
|||
|
|||
public void RemoveListener(INotifyCollectionChanged collection, ICollectionChangedListener listener) |
|||
{ |
|||
collection = collection ?? throw new ArgumentNullException(nameof(collection)); |
|||
listener = listener ?? throw new ArgumentNullException(nameof(listener)); |
|||
Dispatcher.UIThread.VerifyAccess(); |
|||
|
|||
if (_entries.TryGetValue(collection, out var entry)) |
|||
{ |
|||
var listeners = entry.Listeners; |
|||
|
|||
for (var i = 0; i < listeners.Count; ++i) |
|||
{ |
|||
if (listeners[i].TryGetTarget(out var target) && target == listener) |
|||
{ |
|||
listeners.RemoveAt(i); |
|||
|
|||
if (listeners.Count == 0) |
|||
{ |
|||
entry.Dispose(); |
|||
_entries.Remove(collection); |
|||
} |
|||
|
|||
return; |
|||
} |
|||
} |
|||
} |
|||
|
|||
throw new InvalidOperationException( |
|||
"Collection listener not registered for this collection/listener combination."); |
|||
} |
|||
|
|||
private class Entry : IWeakSubscriber<NotifyCollectionChangedEventArgs>, IDisposable |
|||
{ |
|||
private INotifyCollectionChanged _collection; |
|||
|
|||
public Entry(INotifyCollectionChanged collection) |
|||
{ |
|||
_collection = collection; |
|||
Listeners = new List<WeakReference<ICollectionChangedListener>>(); |
|||
WeakSubscriptionManager.Subscribe( |
|||
_collection, |
|||
nameof(INotifyCollectionChanged.CollectionChanged), |
|||
this); |
|||
} |
|||
|
|||
public List<WeakReference<ICollectionChangedListener>> Listeners { get; } |
|||
|
|||
public void Dispose() |
|||
{ |
|||
WeakSubscriptionManager.Unsubscribe( |
|||
_collection, |
|||
nameof(INotifyCollectionChanged.CollectionChanged), |
|||
this); |
|||
} |
|||
|
|||
void IWeakSubscriber<NotifyCollectionChangedEventArgs>.OnEvent(object sender, NotifyCollectionChangedEventArgs e) |
|||
{ |
|||
static void Notify( |
|||
INotifyCollectionChanged incc, |
|||
NotifyCollectionChangedEventArgs args, |
|||
List<WeakReference<ICollectionChangedListener>> listeners) |
|||
{ |
|||
foreach (var l in listeners) |
|||
{ |
|||
if (l.TryGetTarget(out var target)) |
|||
{ |
|||
target.PreChanged(incc, args); |
|||
} |
|||
} |
|||
|
|||
foreach (var l in listeners) |
|||
{ |
|||
if (l.TryGetTarget(out var target)) |
|||
{ |
|||
target.Changed(incc, args); |
|||
} |
|||
} |
|||
|
|||
foreach (var l in listeners) |
|||
{ |
|||
if (l.TryGetTarget(out var target)) |
|||
{ |
|||
target.PostChanged(incc, args); |
|||
} |
|||
} |
|||
} |
|||
|
|||
var l = Listeners.ToList(); |
|||
|
|||
if (Dispatcher.UIThread.CheckAccess()) |
|||
{ |
|||
Notify(_collection, e, l); |
|||
} |
|||
else |
|||
{ |
|||
var eCapture = e; |
|||
Dispatcher.UIThread.Post(() => Notify(_collection, eCapture, l)); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -1,189 +0,0 @@ |
|||
// This source file is adapted from the WinUI project.
|
|||
// (https://github.com/microsoft/microsoft-ui-xaml)
|
|||
//
|
|||
// Licensed to The Avalonia Project under MIT License, courtesy of The .NET Foundation.
|
|||
|
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
|
|||
#nullable enable |
|||
|
|||
namespace Avalonia.Controls.Utils |
|||
{ |
|||
internal static class SelectionTreeHelper |
|||
{ |
|||
public static void TraverseIndexPath( |
|||
SelectionNode root, |
|||
IndexPath path, |
|||
bool realizeChildren, |
|||
Action<SelectionNode, IndexPath, int, int> nodeAction) |
|||
{ |
|||
var node = root; |
|||
|
|||
for (int depth = 0; depth < path.GetSize(); depth++) |
|||
{ |
|||
int childIndex = path.GetAt(depth); |
|||
nodeAction(node, path, depth, childIndex); |
|||
|
|||
if (depth < path.GetSize() - 1) |
|||
{ |
|||
node = node.GetAt(childIndex, realizeChildren, path)!; |
|||
} |
|||
} |
|||
} |
|||
|
|||
public static void Traverse( |
|||
SelectionNode root, |
|||
bool realizeChildren, |
|||
Action<TreeWalkNodeInfo> nodeAction) |
|||
{ |
|||
var pendingNodes = new List<TreeWalkNodeInfo>(); |
|||
var current = new IndexPath(null); |
|||
|
|||
pendingNodes.Add(new TreeWalkNodeInfo(root, current)); |
|||
|
|||
while (pendingNodes.Count > 0) |
|||
{ |
|||
var nextNode = pendingNodes.Last(); |
|||
pendingNodes.RemoveAt(pendingNodes.Count - 1); |
|||
int count = realizeChildren ? nextNode.Node.DataCount : nextNode.Node.ChildrenNodeCount; |
|||
for (int i = count - 1; i >= 0; i--) |
|||
{ |
|||
var child = nextNode.Node.GetAt(i, realizeChildren, nextNode.Path); |
|||
var childPath = nextNode.Path.CloneWithChildIndex(i); |
|||
if (child != null) |
|||
{ |
|||
pendingNodes.Add(new TreeWalkNodeInfo(child, childPath, nextNode.Node)); |
|||
} |
|||
} |
|||
|
|||
// Queue the children first and then perform the action. This way
|
|||
// the action can remove the children in the action if necessary
|
|||
nodeAction(nextNode); |
|||
} |
|||
} |
|||
|
|||
public static void TraverseRangeRealizeChildren( |
|||
SelectionNode root, |
|||
IndexPath start, |
|||
IndexPath end, |
|||
Action<TreeWalkNodeInfo> nodeAction) |
|||
{ |
|||
var pendingNodes = new List<TreeWalkNodeInfo>(); |
|||
var current = start; |
|||
|
|||
// Build up the stack to account for the depth first walk up to the
|
|||
// start index path.
|
|||
TraverseIndexPath( |
|||
root, |
|||
start, |
|||
true, |
|||
(node, path, depth, childIndex) => |
|||
{ |
|||
var currentPath = StartPath(path, depth); |
|||
bool isStartPath = IsSubSet(start, currentPath); |
|||
bool isEndPath = IsSubSet(end, currentPath); |
|||
|
|||
int startIndex = depth < start.GetSize() && isStartPath ? start.GetAt(depth) : 0; |
|||
int endIndex = depth < end.GetSize() && isEndPath ? end.GetAt(depth) : node.DataCount - 1; |
|||
|
|||
for (int i = endIndex; i >= startIndex; i--) |
|||
{ |
|||
var child = node.GetAt(i, true, end); |
|||
if (child != null) |
|||
{ |
|||
var childPath = currentPath.CloneWithChildIndex(i); |
|||
pendingNodes.Add(new TreeWalkNodeInfo(child, childPath, node)); |
|||
} |
|||
} |
|||
}); |
|||
|
|||
// From the start index path, do a depth first walk as long as the
|
|||
// current path is less than the end path.
|
|||
while (pendingNodes.Count > 0) |
|||
{ |
|||
var info = pendingNodes.Last(); |
|||
pendingNodes.RemoveAt(pendingNodes.Count - 1); |
|||
int depth = info.Path.GetSize(); |
|||
bool isStartPath = IsSubSet(start, info.Path); |
|||
bool isEndPath = IsSubSet(end, info.Path); |
|||
int startIndex = depth < start.GetSize() && isStartPath ? start.GetAt(depth) : 0; |
|||
int endIndex = depth < end.GetSize() && isEndPath ? end.GetAt(depth) : info.Node.DataCount - 1; |
|||
for (int i = endIndex; i >= startIndex; i--) |
|||
{ |
|||
var child = info.Node.GetAt(i, true, end); |
|||
if (child != null) |
|||
{ |
|||
var childPath = info.Path.CloneWithChildIndex(i); |
|||
pendingNodes.Add(new TreeWalkNodeInfo(child, childPath, info.Node)); |
|||
} |
|||
} |
|||
|
|||
nodeAction(info); |
|||
|
|||
if (info.Path.CompareTo(end) == 0) |
|||
{ |
|||
// We reached the end index path. stop iterating.
|
|||
break; |
|||
} |
|||
} |
|||
} |
|||
|
|||
private static bool IsSubSet(IndexPath path, IndexPath subset) |
|||
{ |
|||
var subsetSize = subset.GetSize(); |
|||
if (path.GetSize() < subsetSize) |
|||
{ |
|||
return false; |
|||
} |
|||
|
|||
for (int i = 0; i < subsetSize; i++) |
|||
{ |
|||
if (path.GetAt(i) != subset.GetAt(i)) |
|||
{ |
|||
return false; |
|||
} |
|||
} |
|||
|
|||
return true; |
|||
} |
|||
|
|||
private static IndexPath StartPath(IndexPath path, int length) |
|||
{ |
|||
var subPath = new List<int>(); |
|||
for (int i = 0; i < length; i++) |
|||
{ |
|||
subPath.Add(path.GetAt(i)); |
|||
} |
|||
|
|||
return new IndexPath(subPath); |
|||
} |
|||
|
|||
public struct TreeWalkNodeInfo |
|||
{ |
|||
public TreeWalkNodeInfo(SelectionNode node, IndexPath indexPath, SelectionNode? parent) |
|||
{ |
|||
node = node ?? throw new ArgumentNullException(nameof(node)); |
|||
|
|||
Node = node; |
|||
Path = indexPath; |
|||
ParentNode = parent; |
|||
} |
|||
|
|||
public TreeWalkNodeInfo(SelectionNode node, IndexPath indexPath) |
|||
{ |
|||
node = node ?? throw new ArgumentNullException(nameof(node)); |
|||
|
|||
Node = node; |
|||
Path = indexPath; |
|||
ParentNode = null; |
|||
} |
|||
|
|||
public SelectionNode Node { get; } |
|||
public IndexPath Path { get; } |
|||
public SelectionNode? ParentNode { get; } |
|||
}; |
|||
|
|||
} |
|||
} |
|||
@ -0,0 +1,628 @@ |
|||
// <auto-generated/>
|
|||
|
|||
namespace Avalonia.Native.Interop |
|||
{ |
|||
/// <summary>
|
|||
/// No documentation.
|
|||
/// </summary>
|
|||
/// <unmanaged>AvnDragDropEffects</unmanaged>
|
|||
/// <unmanaged-short>AvnDragDropEffects</unmanaged-short>
|
|||
public enum AvnDragDropEffects : System.Int32 |
|||
{ |
|||
/// <summary>
|
|||
/// No documentation.
|
|||
/// </summary>
|
|||
/// <unmanaged>None</unmanaged>
|
|||
/// <unmanaged-short>None</unmanaged-short>
|
|||
None = unchecked ((System.Int32)(0)), |
|||
/// <summary>
|
|||
/// No documentation.
|
|||
/// </summary>
|
|||
/// <unmanaged>Copy</unmanaged>
|
|||
/// <unmanaged-short>Copy</unmanaged-short>
|
|||
Copy = unchecked ((System.Int32)(1)), |
|||
/// <summary>
|
|||
/// No documentation.
|
|||
/// </summary>
|
|||
/// <unmanaged>Move</unmanaged>
|
|||
/// <unmanaged-short>Move</unmanaged-short>
|
|||
Move = unchecked ((System.Int32)(2)), |
|||
/// <summary>
|
|||
/// No documentation.
|
|||
/// </summary>
|
|||
/// <unmanaged>Link</unmanaged>
|
|||
/// <unmanaged-short>Link</unmanaged-short>
|
|||
Link = unchecked ((System.Int32)(4))} |
|||
|
|||
/// <summary>
|
|||
/// No documentation.
|
|||
/// </summary>
|
|||
/// <unmanaged>AvnDragEventType</unmanaged>
|
|||
/// <unmanaged-short>AvnDragEventType</unmanaged-short>
|
|||
public enum AvnDragEventType : System.Int32 |
|||
{ |
|||
/// <summary>
|
|||
/// No documentation.
|
|||
/// </summary>
|
|||
/// <unmanaged>Enter</unmanaged>
|
|||
/// <unmanaged-short>Enter</unmanaged-short>
|
|||
Enter = unchecked ((System.Int32)(0)), |
|||
/// <summary>
|
|||
/// No documentation.
|
|||
/// </summary>
|
|||
/// <unmanaged>Over</unmanaged>
|
|||
/// <unmanaged-short>Over</unmanaged-short>
|
|||
Over = unchecked ((System.Int32)(1)), |
|||
/// <summary>
|
|||
/// No documentation.
|
|||
/// </summary>
|
|||
/// <unmanaged>Leave</unmanaged>
|
|||
/// <unmanaged-short>Leave</unmanaged-short>
|
|||
Leave = unchecked ((System.Int32)(2)), |
|||
/// <summary>
|
|||
/// No documentation.
|
|||
/// </summary>
|
|||
/// <unmanaged>Drop</unmanaged>
|
|||
/// <unmanaged-short>Drop</unmanaged-short>
|
|||
Drop = unchecked ((System.Int32)(3))} |
|||
|
|||
/// <summary>
|
|||
/// No documentation.
|
|||
/// </summary>
|
|||
/// <unmanaged>AvnExtendClientAreaChromeHints</unmanaged>
|
|||
/// <unmanaged-short>AvnExtendClientAreaChromeHints</unmanaged-short>
|
|||
public enum AvnExtendClientAreaChromeHints : System.Int32 |
|||
{ |
|||
/// <summary>
|
|||
/// No documentation.
|
|||
/// </summary>
|
|||
/// <unmanaged>AvnNoChrome</unmanaged>
|
|||
/// <unmanaged-short>AvnNoChrome</unmanaged-short>
|
|||
AvnNoChrome = unchecked ((System.Int32)(0)), |
|||
/// <summary>
|
|||
/// No documentation.
|
|||
/// </summary>
|
|||
/// <unmanaged>AvnSystemChrome</unmanaged>
|
|||
/// <unmanaged-short>AvnSystemChrome</unmanaged-short>
|
|||
AvnSystemChrome = unchecked ((System.Int32)(1)), |
|||
/// <summary>
|
|||
/// No documentation.
|
|||
/// </summary>
|
|||
/// <unmanaged>AvnPreferSystemChrome</unmanaged>
|
|||
/// <unmanaged-short>AvnPreferSystemChrome</unmanaged-short>
|
|||
AvnPreferSystemChrome = unchecked ((System.Int32)(2)), |
|||
/// <summary>
|
|||
/// No documentation.
|
|||
/// </summary>
|
|||
/// <unmanaged>AvnOSXThickTitleBar</unmanaged>
|
|||
/// <unmanaged-short>AvnOSXThickTitleBar</unmanaged-short>
|
|||
AvnOSXThickTitleBar = unchecked ((System.Int32)(8)), |
|||
/// <summary>
|
|||
/// No documentation.
|
|||
/// </summary>
|
|||
/// <unmanaged>AvnDefaultChrome</unmanaged>
|
|||
/// <unmanaged-short>AvnDefaultChrome</unmanaged-short>
|
|||
AvnDefaultChrome = unchecked ((System.Int32)(1))} |
|||
|
|||
/// <summary>
|
|||
/// No documentation.
|
|||
/// </summary>
|
|||
/// <unmanaged>AvnInputModifiers</unmanaged>
|
|||
/// <unmanaged-short>AvnInputModifiers</unmanaged-short>
|
|||
public enum AvnInputModifiers : System.Int32 |
|||
{ |
|||
/// <summary>
|
|||
/// No documentation.
|
|||
/// </summary>
|
|||
/// <unmanaged>AvnInputModifiersNone</unmanaged>
|
|||
/// <unmanaged-short>AvnInputModifiersNone</unmanaged-short>
|
|||
AvnInputModifiersNone = unchecked ((System.Int32)(0)), |
|||
/// <summary>
|
|||
/// No documentation.
|
|||
/// </summary>
|
|||
/// <unmanaged>Alt</unmanaged>
|
|||
/// <unmanaged-short>Alt</unmanaged-short>
|
|||
Alt = unchecked ((System.Int32)(1)), |
|||
/// <summary>
|
|||
/// No documentation.
|
|||
/// </summary>
|
|||
/// <unmanaged>Control</unmanaged>
|
|||
/// <unmanaged-short>Control</unmanaged-short>
|
|||
Control = unchecked ((System.Int32)(2)), |
|||
/// <summary>
|
|||
/// No documentation.
|
|||
/// </summary>
|
|||
/// <unmanaged>Shift</unmanaged>
|
|||
/// <unmanaged-short>Shift</unmanaged-short>
|
|||
Shift = unchecked ((System.Int32)(4)), |
|||
/// <summary>
|
|||
/// No documentation.
|
|||
/// </summary>
|
|||
/// <unmanaged>Windows</unmanaged>
|
|||
/// <unmanaged-short>Windows</unmanaged-short>
|
|||
Windows = unchecked ((System.Int32)(8)), |
|||
/// <summary>
|
|||
/// No documentation.
|
|||
/// </summary>
|
|||
/// <unmanaged>LeftMouseButton</unmanaged>
|
|||
/// <unmanaged-short>LeftMouseButton</unmanaged-short>
|
|||
LeftMouseButton = unchecked ((System.Int32)(16)), |
|||
/// <summary>
|
|||
/// No documentation.
|
|||
/// </summary>
|
|||
/// <unmanaged>RightMouseButton</unmanaged>
|
|||
/// <unmanaged-short>RightMouseButton</unmanaged-short>
|
|||
RightMouseButton = unchecked ((System.Int32)(32)), |
|||
/// <summary>
|
|||
/// No documentation.
|
|||
/// </summary>
|
|||
/// <unmanaged>MiddleMouseButton</unmanaged>
|
|||
/// <unmanaged-short>MiddleMouseButton</unmanaged-short>
|
|||
MiddleMouseButton = unchecked ((System.Int32)(64)), |
|||
/// <summary>
|
|||
/// No documentation.
|
|||
/// </summary>
|
|||
/// <unmanaged>XButton1MouseButton</unmanaged>
|
|||
/// <unmanaged-short>XButton1MouseButton</unmanaged-short>
|
|||
XButton1MouseButton = unchecked ((System.Int32)(128)), |
|||
/// <summary>
|
|||
/// No documentation.
|
|||
/// </summary>
|
|||
/// <unmanaged>XButton2MouseButton</unmanaged>
|
|||
/// <unmanaged-short>XButton2MouseButton</unmanaged-short>
|
|||
XButton2MouseButton = unchecked ((System.Int32)(256))} |
|||
|
|||
/// <summary>
|
|||
/// No documentation.
|
|||
/// </summary>
|
|||
/// <unmanaged>AvnMenuItemToggleType</unmanaged>
|
|||
/// <unmanaged-short>AvnMenuItemToggleType</unmanaged-short>
|
|||
public enum AvnMenuItemToggleType : System.Int32 |
|||
{ |
|||
/// <summary>
|
|||
/// No documentation.
|
|||
/// </summary>
|
|||
/// <unmanaged>None</unmanaged>
|
|||
/// <unmanaged-short>None</unmanaged-short>
|
|||
None = unchecked ((System.Int32)(0)), |
|||
/// <summary>
|
|||
/// No documentation.
|
|||
/// </summary>
|
|||
/// <unmanaged>CheckMark</unmanaged>
|
|||
/// <unmanaged-short>CheckMark</unmanaged-short>
|
|||
CheckMark = unchecked ((System.Int32)(1)), |
|||
/// <summary>
|
|||
/// No documentation.
|
|||
/// </summary>
|
|||
/// <unmanaged>Radio</unmanaged>
|
|||
/// <unmanaged-short>Radio</unmanaged-short>
|
|||
Radio = unchecked ((System.Int32)(2))} |
|||
|
|||
/// <summary>
|
|||
/// No documentation.
|
|||
/// </summary>
|
|||
/// <unmanaged>AvnPixelFormat</unmanaged>
|
|||
/// <unmanaged-short>AvnPixelFormat</unmanaged-short>
|
|||
public enum AvnPixelFormat : System.Int32 |
|||
{ |
|||
/// <summary>
|
|||
/// No documentation.
|
|||
/// </summary>
|
|||
/// <unmanaged>kAvnRgb565</unmanaged>
|
|||
/// <unmanaged-short>kAvnRgb565</unmanaged-short>
|
|||
KAvnRgb565 = unchecked ((System.Int32)(0)), |
|||
/// <summary>
|
|||
/// No documentation.
|
|||
/// </summary>
|
|||
/// <unmanaged>kAvnRgba8888</unmanaged>
|
|||
/// <unmanaged-short>kAvnRgba8888</unmanaged-short>
|
|||
KAvnRgba8888 = unchecked ((System.Int32)(1)), |
|||
/// <summary>
|
|||
/// No documentation.
|
|||
/// </summary>
|
|||
/// <unmanaged>kAvnBgra8888</unmanaged>
|
|||
/// <unmanaged-short>kAvnBgra8888</unmanaged-short>
|
|||
KAvnBgra8888 = unchecked ((System.Int32)(2))} |
|||
|
|||
/// <summary>
|
|||
/// No documentation.
|
|||
/// </summary>
|
|||
/// <unmanaged>AvnRawKeyEventType</unmanaged>
|
|||
/// <unmanaged-short>AvnRawKeyEventType</unmanaged-short>
|
|||
public enum AvnRawKeyEventType : System.Int32 |
|||
{ |
|||
/// <summary>
|
|||
/// No documentation.
|
|||
/// </summary>
|
|||
/// <unmanaged>KeyDown</unmanaged>
|
|||
/// <unmanaged-short>KeyDown</unmanaged-short>
|
|||
KeyDown = unchecked ((System.Int32)(0)), |
|||
/// <summary>
|
|||
/// No documentation.
|
|||
/// </summary>
|
|||
/// <unmanaged>KeyUp</unmanaged>
|
|||
/// <unmanaged-short>KeyUp</unmanaged-short>
|
|||
KeyUp = unchecked ((System.Int32)(1))} |
|||
|
|||
/// <summary>
|
|||
/// No documentation.
|
|||
/// </summary>
|
|||
/// <unmanaged>AvnRawMouseEventType</unmanaged>
|
|||
/// <unmanaged-short>AvnRawMouseEventType</unmanaged-short>
|
|||
public enum AvnRawMouseEventType : System.Int32 |
|||
{ |
|||
/// <summary>
|
|||
/// No documentation.
|
|||
/// </summary>
|
|||
/// <unmanaged>LeaveWindow</unmanaged>
|
|||
/// <unmanaged-short>LeaveWindow</unmanaged-short>
|
|||
LeaveWindow = unchecked ((System.Int32)(0)), |
|||
/// <summary>
|
|||
/// No documentation.
|
|||
/// </summary>
|
|||
/// <unmanaged>LeftButtonDown</unmanaged>
|
|||
/// <unmanaged-short>LeftButtonDown</unmanaged-short>
|
|||
LeftButtonDown = unchecked ((System.Int32)(1)), |
|||
/// <summary>
|
|||
/// No documentation.
|
|||
/// </summary>
|
|||
/// <unmanaged>LeftButtonUp</unmanaged>
|
|||
/// <unmanaged-short>LeftButtonUp</unmanaged-short>
|
|||
LeftButtonUp = unchecked ((System.Int32)(2)), |
|||
/// <summary>
|
|||
/// No documentation.
|
|||
/// </summary>
|
|||
/// <unmanaged>RightButtonDown</unmanaged>
|
|||
/// <unmanaged-short>RightButtonDown</unmanaged-short>
|
|||
RightButtonDown = unchecked ((System.Int32)(3)), |
|||
/// <summary>
|
|||
/// No documentation.
|
|||
/// </summary>
|
|||
/// <unmanaged>RightButtonUp</unmanaged>
|
|||
/// <unmanaged-short>RightButtonUp</unmanaged-short>
|
|||
RightButtonUp = unchecked ((System.Int32)(4)), |
|||
/// <summary>
|
|||
/// No documentation.
|
|||
/// </summary>
|
|||
/// <unmanaged>MiddleButtonDown</unmanaged>
|
|||
/// <unmanaged-short>MiddleButtonDown</unmanaged-short>
|
|||
MiddleButtonDown = unchecked ((System.Int32)(5)), |
|||
/// <summary>
|
|||
/// No documentation.
|
|||
/// </summary>
|
|||
/// <unmanaged>MiddleButtonUp</unmanaged>
|
|||
/// <unmanaged-short>MiddleButtonUp</unmanaged-short>
|
|||
MiddleButtonUp = unchecked ((System.Int32)(6)), |
|||
/// <summary>
|
|||
/// No documentation.
|
|||
/// </summary>
|
|||
/// <unmanaged>XButton1Down</unmanaged>
|
|||
/// <unmanaged-short>XButton1Down</unmanaged-short>
|
|||
XButton1Down = unchecked ((System.Int32)(7)), |
|||
/// <summary>
|
|||
/// No documentation.
|
|||
/// </summary>
|
|||
/// <unmanaged>XButton1Up</unmanaged>
|
|||
/// <unmanaged-short>XButton1Up</unmanaged-short>
|
|||
XButton1Up = unchecked ((System.Int32)(8)), |
|||
/// <summary>
|
|||
/// No documentation.
|
|||
/// </summary>
|
|||
/// <unmanaged>XButton2Down</unmanaged>
|
|||
/// <unmanaged-short>XButton2Down</unmanaged-short>
|
|||
XButton2Down = unchecked ((System.Int32)(9)), |
|||
/// <summary>
|
|||
/// No documentation.
|
|||
/// </summary>
|
|||
/// <unmanaged>XButton2Up</unmanaged>
|
|||
/// <unmanaged-short>XButton2Up</unmanaged-short>
|
|||
XButton2Up = unchecked ((System.Int32)(10)), |
|||
/// <summary>
|
|||
/// No documentation.
|
|||
/// </summary>
|
|||
/// <unmanaged>Move</unmanaged>
|
|||
/// <unmanaged-short>Move</unmanaged-short>
|
|||
Move = unchecked ((System.Int32)(11)), |
|||
/// <summary>
|
|||
/// No documentation.
|
|||
/// </summary>
|
|||
/// <unmanaged>Wheel</unmanaged>
|
|||
/// <unmanaged-short>Wheel</unmanaged-short>
|
|||
Wheel = unchecked ((System.Int32)(12)), |
|||
/// <summary>
|
|||
/// No documentation.
|
|||
/// </summary>
|
|||
/// <unmanaged>NonClientLeftButtonDown</unmanaged>
|
|||
/// <unmanaged-short>NonClientLeftButtonDown</unmanaged-short>
|
|||
NonClientLeftButtonDown = unchecked ((System.Int32)(13)), |
|||
/// <summary>
|
|||
/// No documentation.
|
|||
/// </summary>
|
|||
/// <unmanaged>TouchBegin</unmanaged>
|
|||
/// <unmanaged-short>TouchBegin</unmanaged-short>
|
|||
TouchBegin = unchecked ((System.Int32)(14)), |
|||
/// <summary>
|
|||
/// No documentation.
|
|||
/// </summary>
|
|||
/// <unmanaged>TouchUpdate</unmanaged>
|
|||
/// <unmanaged-short>TouchUpdate</unmanaged-short>
|
|||
TouchUpdate = unchecked ((System.Int32)(15)), |
|||
/// <summary>
|
|||
/// No documentation.
|
|||
/// </summary>
|
|||
/// <unmanaged>TouchEnd</unmanaged>
|
|||
/// <unmanaged-short>TouchEnd</unmanaged-short>
|
|||
TouchEnd = unchecked ((System.Int32)(16)), |
|||
/// <summary>
|
|||
/// No documentation.
|
|||
/// </summary>
|
|||
/// <unmanaged>TouchCancel</unmanaged>
|
|||
/// <unmanaged-short>TouchCancel</unmanaged-short>
|
|||
TouchCancel = unchecked ((System.Int32)(17))} |
|||
|
|||
/// <summary>
|
|||
/// No documentation.
|
|||
/// </summary>
|
|||
/// <unmanaged>AvnStandardCursorType</unmanaged>
|
|||
/// <unmanaged-short>AvnStandardCursorType</unmanaged-short>
|
|||
public enum AvnStandardCursorType : System.Int32 |
|||
{ |
|||
/// <summary>
|
|||
/// No documentation.
|
|||
/// </summary>
|
|||
/// <unmanaged>CursorArrow</unmanaged>
|
|||
/// <unmanaged-short>CursorArrow</unmanaged-short>
|
|||
CursorArrow = unchecked ((System.Int32)(0)), |
|||
/// <summary>
|
|||
/// No documentation.
|
|||
/// </summary>
|
|||
/// <unmanaged>CursorIbeam</unmanaged>
|
|||
/// <unmanaged-short>CursorIbeam</unmanaged-short>
|
|||
CursorIbeam = unchecked ((System.Int32)(1)), |
|||
/// <summary>
|
|||
/// No documentation.
|
|||
/// </summary>
|
|||
/// <unmanaged>CursorWait</unmanaged>
|
|||
/// <unmanaged-short>CursorWait</unmanaged-short>
|
|||
CursorWait = unchecked ((System.Int32)(2)), |
|||
/// <summary>
|
|||
/// No documentation.
|
|||
/// </summary>
|
|||
/// <unmanaged>CursorCross</unmanaged>
|
|||
/// <unmanaged-short>CursorCross</unmanaged-short>
|
|||
CursorCross = unchecked ((System.Int32)(3)), |
|||
/// <summary>
|
|||
/// No documentation.
|
|||
/// </summary>
|
|||
/// <unmanaged>CursorUpArrow</unmanaged>
|
|||
/// <unmanaged-short>CursorUpArrow</unmanaged-short>
|
|||
CursorUpArrow = unchecked ((System.Int32)(4)), |
|||
/// <summary>
|
|||
/// No documentation.
|
|||
/// </summary>
|
|||
/// <unmanaged>CursorSizeWestEast</unmanaged>
|
|||
/// <unmanaged-short>CursorSizeWestEast</unmanaged-short>
|
|||
CursorSizeWestEast = unchecked ((System.Int32)(5)), |
|||
/// <summary>
|
|||
/// No documentation.
|
|||
/// </summary>
|
|||
/// <unmanaged>CursorSizeNorthSouth</unmanaged>
|
|||
/// <unmanaged-short>CursorSizeNorthSouth</unmanaged-short>
|
|||
CursorSizeNorthSouth = unchecked ((System.Int32)(6)), |
|||
/// <summary>
|
|||
/// No documentation.
|
|||
/// </summary>
|
|||
/// <unmanaged>CursorSizeAll</unmanaged>
|
|||
/// <unmanaged-short>CursorSizeAll</unmanaged-short>
|
|||
CursorSizeAll = unchecked ((System.Int32)(7)), |
|||
/// <summary>
|
|||
/// No documentation.
|
|||
/// </summary>
|
|||
/// <unmanaged>CursorNo</unmanaged>
|
|||
/// <unmanaged-short>CursorNo</unmanaged-short>
|
|||
CursorNo = unchecked ((System.Int32)(8)), |
|||
/// <summary>
|
|||
/// No documentation.
|
|||
/// </summary>
|
|||
/// <unmanaged>CursorHand</unmanaged>
|
|||
/// <unmanaged-short>CursorHand</unmanaged-short>
|
|||
CursorHand = unchecked ((System.Int32)(9)), |
|||
/// <summary>
|
|||
/// No documentation.
|
|||
/// </summary>
|
|||
/// <unmanaged>CursorAppStarting</unmanaged>
|
|||
/// <unmanaged-short>CursorAppStarting</unmanaged-short>
|
|||
CursorAppStarting = unchecked ((System.Int32)(10)), |
|||
/// <summary>
|
|||
/// No documentation.
|
|||
/// </summary>
|
|||
/// <unmanaged>CursorHelp</unmanaged>
|
|||
/// <unmanaged-short>CursorHelp</unmanaged-short>
|
|||
CursorHelp = unchecked ((System.Int32)(11)), |
|||
/// <summary>
|
|||
/// No documentation.
|
|||
/// </summary>
|
|||
/// <unmanaged>CursorTopSide</unmanaged>
|
|||
/// <unmanaged-short>CursorTopSide</unmanaged-short>
|
|||
CursorTopSide = unchecked ((System.Int32)(12)), |
|||
/// <summary>
|
|||
/// No documentation.
|
|||
/// </summary>
|
|||
/// <unmanaged>CursorBottomSize</unmanaged>
|
|||
/// <unmanaged-short>CursorBottomSize</unmanaged-short>
|
|||
CursorBottomSize = unchecked ((System.Int32)(13)), |
|||
/// <summary>
|
|||
/// No documentation.
|
|||
/// </summary>
|
|||
/// <unmanaged>CursorLeftSide</unmanaged>
|
|||
/// <unmanaged-short>CursorLeftSide</unmanaged-short>
|
|||
CursorLeftSide = unchecked ((System.Int32)(14)), |
|||
/// <summary>
|
|||
/// No documentation.
|
|||
/// </summary>
|
|||
/// <unmanaged>CursorRightSide</unmanaged>
|
|||
/// <unmanaged-short>CursorRightSide</unmanaged-short>
|
|||
CursorRightSide = unchecked ((System.Int32)(15)), |
|||
/// <summary>
|
|||
/// No documentation.
|
|||
/// </summary>
|
|||
/// <unmanaged>CursorTopLeftCorner</unmanaged>
|
|||
/// <unmanaged-short>CursorTopLeftCorner</unmanaged-short>
|
|||
CursorTopLeftCorner = unchecked ((System.Int32)(16)), |
|||
/// <summary>
|
|||
/// No documentation.
|
|||
/// </summary>
|
|||
/// <unmanaged>CursorTopRightCorner</unmanaged>
|
|||
/// <unmanaged-short>CursorTopRightCorner</unmanaged-short>
|
|||
CursorTopRightCorner = unchecked ((System.Int32)(17)), |
|||
/// <summary>
|
|||
/// No documentation.
|
|||
/// </summary>
|
|||
/// <unmanaged>CursorBottomLeftCorner</unmanaged>
|
|||
/// <unmanaged-short>CursorBottomLeftCorner</unmanaged-short>
|
|||
CursorBottomLeftCorner = unchecked ((System.Int32)(18)), |
|||
/// <summary>
|
|||
/// No documentation.
|
|||
/// </summary>
|
|||
/// <unmanaged>CursorBottomRightCorner</unmanaged>
|
|||
/// <unmanaged-short>CursorBottomRightCorner</unmanaged-short>
|
|||
CursorBottomRightCorner = unchecked ((System.Int32)(19)), |
|||
/// <summary>
|
|||
/// No documentation.
|
|||
/// </summary>
|
|||
/// <unmanaged>CursorDragMove</unmanaged>
|
|||
/// <unmanaged-short>CursorDragMove</unmanaged-short>
|
|||
CursorDragMove = unchecked ((System.Int32)(20)), |
|||
/// <summary>
|
|||
/// No documentation.
|
|||
/// </summary>
|
|||
/// <unmanaged>CursorDragCopy</unmanaged>
|
|||
/// <unmanaged-short>CursorDragCopy</unmanaged-short>
|
|||
CursorDragCopy = unchecked ((System.Int32)(21)), |
|||
/// <summary>
|
|||
/// No documentation.
|
|||
/// </summary>
|
|||
/// <unmanaged>CursorDragLink</unmanaged>
|
|||
/// <unmanaged-short>CursorDragLink</unmanaged-short>
|
|||
CursorDragLink = unchecked ((System.Int32)(22)), |
|||
/// <summary>
|
|||
/// No documentation.
|
|||
/// </summary>
|
|||
/// <unmanaged>CursorNone</unmanaged>
|
|||
/// <unmanaged-short>CursorNone</unmanaged-short>
|
|||
CursorNone = unchecked ((System.Int32)(23))} |
|||
|
|||
/// <summary>
|
|||
/// No documentation.
|
|||
/// </summary>
|
|||
/// <unmanaged>AvnWindowEdge</unmanaged>
|
|||
/// <unmanaged-short>AvnWindowEdge</unmanaged-short>
|
|||
public enum AvnWindowEdge : System.Int32 |
|||
{ |
|||
/// <summary>
|
|||
/// No documentation.
|
|||
/// </summary>
|
|||
/// <unmanaged>WindowEdgeNorthWest</unmanaged>
|
|||
/// <unmanaged-short>WindowEdgeNorthWest</unmanaged-short>
|
|||
WindowEdgeNorthWest = unchecked ((System.Int32)(0)), |
|||
/// <summary>
|
|||
/// No documentation.
|
|||
/// </summary>
|
|||
/// <unmanaged>WindowEdgeNorth</unmanaged>
|
|||
/// <unmanaged-short>WindowEdgeNorth</unmanaged-short>
|
|||
WindowEdgeNorth = unchecked ((System.Int32)(1)), |
|||
/// <summary>
|
|||
/// No documentation.
|
|||
/// </summary>
|
|||
/// <unmanaged>WindowEdgeNorthEast</unmanaged>
|
|||
/// <unmanaged-short>WindowEdgeNorthEast</unmanaged-short>
|
|||
WindowEdgeNorthEast = unchecked ((System.Int32)(2)), |
|||
/// <summary>
|
|||
/// No documentation.
|
|||
/// </summary>
|
|||
/// <unmanaged>WindowEdgeWest</unmanaged>
|
|||
/// <unmanaged-short>WindowEdgeWest</unmanaged-short>
|
|||
WindowEdgeWest = unchecked ((System.Int32)(3)), |
|||
/// <summary>
|
|||
/// No documentation.
|
|||
/// </summary>
|
|||
/// <unmanaged>WindowEdgeEast</unmanaged>
|
|||
/// <unmanaged-short>WindowEdgeEast</unmanaged-short>
|
|||
WindowEdgeEast = unchecked ((System.Int32)(4)), |
|||
/// <summary>
|
|||
/// No documentation.
|
|||
/// </summary>
|
|||
/// <unmanaged>WindowEdgeSouthWest</unmanaged>
|
|||
/// <unmanaged-short>WindowEdgeSouthWest</unmanaged-short>
|
|||
WindowEdgeSouthWest = unchecked ((System.Int32)(5)), |
|||
/// <summary>
|
|||
/// No documentation.
|
|||
/// </summary>
|
|||
/// <unmanaged>WindowEdgeSouth</unmanaged>
|
|||
/// <unmanaged-short>WindowEdgeSouth</unmanaged-short>
|
|||
WindowEdgeSouth = unchecked ((System.Int32)(6)), |
|||
/// <summary>
|
|||
/// No documentation.
|
|||
/// </summary>
|
|||
/// <unmanaged>WindowEdgeSouthEast</unmanaged>
|
|||
/// <unmanaged-short>WindowEdgeSouthEast</unmanaged-short>
|
|||
WindowEdgeSouthEast = unchecked ((System.Int32)(7))} |
|||
|
|||
/// <summary>
|
|||
/// No documentation.
|
|||
/// </summary>
|
|||
/// <unmanaged>AvnWindowState</unmanaged>
|
|||
/// <unmanaged-short>AvnWindowState</unmanaged-short>
|
|||
public enum AvnWindowState : System.Int32 |
|||
{ |
|||
/// <summary>
|
|||
/// No documentation.
|
|||
/// </summary>
|
|||
/// <unmanaged>Normal</unmanaged>
|
|||
/// <unmanaged-short>Normal</unmanaged-short>
|
|||
Normal = unchecked ((System.Int32)(0)), |
|||
/// <summary>
|
|||
/// No documentation.
|
|||
/// </summary>
|
|||
/// <unmanaged>Minimized</unmanaged>
|
|||
/// <unmanaged-short>Minimized</unmanaged-short>
|
|||
Minimized = unchecked ((System.Int32)(1)), |
|||
/// <summary>
|
|||
/// No documentation.
|
|||
/// </summary>
|
|||
/// <unmanaged>Maximized</unmanaged>
|
|||
/// <unmanaged-short>Maximized</unmanaged-short>
|
|||
Maximized = unchecked ((System.Int32)(2)), |
|||
/// <summary>
|
|||
/// No documentation.
|
|||
/// </summary>
|
|||
/// <unmanaged>FullScreen</unmanaged>
|
|||
/// <unmanaged-short>FullScreen</unmanaged-short>
|
|||
FullScreen = unchecked ((System.Int32)(3))} |
|||
|
|||
/// <summary>
|
|||
/// No documentation.
|
|||
/// </summary>
|
|||
/// <unmanaged>SystemDecorations</unmanaged>
|
|||
/// <unmanaged-short>SystemDecorations</unmanaged-short>
|
|||
public enum SystemDecorations : System.Int32 |
|||
{ |
|||
/// <summary>
|
|||
/// No documentation.
|
|||
/// </summary>
|
|||
/// <unmanaged>SystemDecorationsNone</unmanaged>
|
|||
/// <unmanaged-short>SystemDecorationsNone</unmanaged-short>
|
|||
SystemDecorationsNone = unchecked ((System.Int32)(0)), |
|||
/// <summary>
|
|||
/// No documentation.
|
|||
/// </summary>
|
|||
/// <unmanaged>SystemDecorationsBorderOnly</unmanaged>
|
|||
/// <unmanaged-short>SystemDecorationsBorderOnly</unmanaged-short>
|
|||
SystemDecorationsBorderOnly = unchecked ((System.Int32)(1)), |
|||
/// <summary>
|
|||
/// No documentation.
|
|||
/// </summary>
|
|||
/// <unmanaged>SystemDecorationsFull</unmanaged>
|
|||
/// <unmanaged-short>SystemDecorationsFull</unmanaged-short>
|
|||
SystemDecorationsFull = unchecked ((System.Int32)(2))} |
|||
} |
|||
@ -0,0 +1,5 @@ |
|||
// <auto-generated/>
|
|||
|
|||
namespace Avalonia.Native.Interop |
|||
{ |
|||
} |
|||
File diff suppressed because it is too large
@ -0,0 +1,202 @@ |
|||
// <auto-generated/>
|
|||
|
|||
namespace Avalonia.Native |
|||
{ |
|||
internal static partial class LocalInterop |
|||
{ |
|||
public static unsafe int CalliThisCallint(void *thisObject, void *methodPtr) |
|||
{ |
|||
throw null; |
|||
} |
|||
|
|||
public static unsafe void CalliThisCallvoid(void *thisObject, void *methodPtr) |
|||
{ |
|||
throw null; |
|||
} |
|||
|
|||
public static unsafe void CalliThisCallvoid(void *thisObject, void *param0, void *methodPtr) |
|||
{ |
|||
throw null; |
|||
} |
|||
|
|||
public static unsafe void CalliThisCallvoid0(void *thisObject, Avalonia.Native.Interop.AvnPoint param0, void *methodPtr) |
|||
{ |
|||
throw null; |
|||
} |
|||
|
|||
public static unsafe void CalliThisCallvoid0(void *thisObject, int param0, System.UInt32 param1, int param2, Avalonia.Native.Interop.AvnPoint param3, Avalonia.Native.Interop.AvnVector param4, void *methodPtr) |
|||
{ |
|||
throw null; |
|||
} |
|||
|
|||
public static unsafe System.Byte CalliThisCallSystemByte(void *thisObject, int param0, System.UInt32 param1, int param2, System.UInt32 param3, void *methodPtr) |
|||
{ |
|||
throw null; |
|||
} |
|||
|
|||
public static unsafe System.Byte CalliThisCallSystemByte(void *thisObject, System.UInt32 param0, void *param1, void *methodPtr) |
|||
{ |
|||
throw null; |
|||
} |
|||
|
|||
public static unsafe void CalliThisCallvoid(void *thisObject, double param0, void *methodPtr) |
|||
{ |
|||
throw null; |
|||
} |
|||
|
|||
public static unsafe Avalonia.Native.Interop.AvnDragDropEffects CalliThisCallAvaloniaNativeInteropAvnDragDropEffects0(void *thisObject, int param0, Avalonia.Native.Interop.AvnPoint param1, int param2, int param3, void *param4, void *param5, void *methodPtr) |
|||
{ |
|||
throw null; |
|||
} |
|||
|
|||
public static unsafe System.Byte CalliThisCallSystemByte(void *thisObject, void *methodPtr) |
|||
{ |
|||
throw null; |
|||
} |
|||
|
|||
public static unsafe void CalliThisCallvoid(void *thisObject, int param0, void *methodPtr) |
|||
{ |
|||
throw null; |
|||
} |
|||
|
|||
public static unsafe int CalliThisCallint(void *thisObject, void *param0, void *methodPtr) |
|||
{ |
|||
throw null; |
|||
} |
|||
|
|||
public static unsafe int CalliThisCallint0(void *thisObject, Avalonia.Native.Interop.AvnSize param0, Avalonia.Native.Interop.AvnSize param1, void *methodPtr) |
|||
{ |
|||
throw null; |
|||
} |
|||
|
|||
public static unsafe int CalliThisCallint(void *thisObject, double param0, double param1, void *methodPtr) |
|||
{ |
|||
throw null; |
|||
} |
|||
|
|||
public static unsafe int CalliThisCallint0(void *thisObject, Avalonia.Native.Interop.AvnRect param0, void *methodPtr) |
|||
{ |
|||
throw null; |
|||
} |
|||
|
|||
public static unsafe int CalliThisCallint(void *thisObject, int param0, void *methodPtr) |
|||
{ |
|||
throw null; |
|||
} |
|||
|
|||
public static unsafe int CalliThisCallint0(void *thisObject, Avalonia.Native.Interop.AvnPoint param0, void *methodPtr) |
|||
{ |
|||
throw null; |
|||
} |
|||
|
|||
public static unsafe int CalliThisCallint0(void *thisObject, Avalonia.Native.Interop.AvnPoint param0, void *param1, void *methodPtr) |
|||
{ |
|||
throw null; |
|||
} |
|||
|
|||
public static unsafe int CalliThisCallint(void *thisObject, void *param0, void *param1, void *methodPtr) |
|||
{ |
|||
throw null; |
|||
} |
|||
|
|||
public static unsafe int CalliThisCallint(void *thisObject, System.Byte param0, void *methodPtr) |
|||
{ |
|||
throw null; |
|||
} |
|||
|
|||
public static unsafe int CalliThisCallint0(void *thisObject, int param0, Avalonia.Native.Interop.AvnPoint param1, void *param2, void *param3, void *param4, void *methodPtr) |
|||
{ |
|||
throw null; |
|||
} |
|||
|
|||
public static unsafe int CalliThisCallint0(void *thisObject, Avalonia.Native.Interop.AvnColor param0, void *methodPtr) |
|||
{ |
|||
throw null; |
|||
} |
|||
|
|||
public static unsafe int CalliThisCallint(void *thisObject, double param0, void *methodPtr) |
|||
{ |
|||
throw null; |
|||
} |
|||
|
|||
public static unsafe System.IntPtr CalliThisCallSystemIntPtr(void *thisObject, void *methodPtr) |
|||
{ |
|||
throw null; |
|||
} |
|||
|
|||
public static unsafe System.IntPtr CalliThisCallSystemIntPtr(void *thisObject, int param0, int param1, void *param2, void *methodPtr) |
|||
{ |
|||
throw null; |
|||
} |
|||
|
|||
public static unsafe void CalliThisCallvoid(void *thisObject, int param0, void *param1, void *methodPtr) |
|||
{ |
|||
throw null; |
|||
} |
|||
|
|||
public static unsafe void CalliThisCallvoid(void *thisObject, void *param0, void *param1, void *param2, void *param3, void *methodPtr) |
|||
{ |
|||
throw null; |
|||
} |
|||
|
|||
public static unsafe void CalliThisCallvoid(void *thisObject, void *param0, void *param1, System.Byte param2, void *param3, void *param4, void *param5, void *param6, void *methodPtr) |
|||
{ |
|||
throw null; |
|||
} |
|||
|
|||
public static unsafe void CalliThisCallvoid(void *thisObject, void *param0, void *param1, void *param2, void *param3, void *param4, void *param5, void *methodPtr) |
|||
{ |
|||
throw null; |
|||
} |
|||
|
|||
public static unsafe int CalliThisCallint(void *thisObject, int param0, void *param1, void *methodPtr) |
|||
{ |
|||
throw null; |
|||
} |
|||
|
|||
public static unsafe int CalliThisCallint(void *thisObject, void *param0, void *param1, int param2, void *methodPtr) |
|||
{ |
|||
throw null; |
|||
} |
|||
|
|||
public static unsafe System.IntPtr CalliThisCallSystemIntPtr(void *thisObject, void *param0, void *methodPtr) |
|||
{ |
|||
throw null; |
|||
} |
|||
|
|||
public static unsafe int CalliThisCallint(void *thisObject, void *param0, int param1, void *methodPtr) |
|||
{ |
|||
throw null; |
|||
} |
|||
|
|||
public static unsafe System.UInt32 CalliThisCallSystemUInt32(void *thisObject, void *methodPtr) |
|||
{ |
|||
throw null; |
|||
} |
|||
|
|||
public static unsafe int CalliThisCallint(void *thisObject, System.UInt32 param0, void *param1, void *methodPtr) |
|||
{ |
|||
throw null; |
|||
} |
|||
|
|||
public static unsafe void CalliThisCallvoid(void *thisObject, float param0, float param1, float param2, float param3, void *methodPtr) |
|||
{ |
|||
throw null; |
|||
} |
|||
|
|||
public static unsafe void CalliThisCallvoid(void *thisObject, float param0, float param1, void *methodPtr) |
|||
{ |
|||
throw null; |
|||
} |
|||
|
|||
public static unsafe int CalliThisCallint(void *thisObject, void *param0, void *param1, void *param2, void *methodPtr) |
|||
{ |
|||
throw null; |
|||
} |
|||
|
|||
public static unsafe void CalliThisCallvoid(void *thisObject, int param0, System.Byte param1, void *methodPtr) |
|||
{ |
|||
throw null; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,246 @@ |
|||
// <auto-generated/>
|
|||
|
|||
namespace Avalonia.Native.Interop |
|||
{ |
|||
/// <summary>
|
|||
/// No documentation.
|
|||
/// </summary>
|
|||
/// <unmanaged>AvnColor</unmanaged>
|
|||
/// <unmanaged-short>AvnColor</unmanaged-short>
|
|||
[System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential, Pack = 0, CharSet = System.Runtime.InteropServices.CharSet.Unicode)] |
|||
public partial struct AvnColor |
|||
{ |
|||
/// <summary>
|
|||
/// No documentation.
|
|||
/// </summary>
|
|||
/// <unmanaged>Alpha</unmanaged>
|
|||
/// <unmanaged-short>Alpha</unmanaged-short>
|
|||
public System.Byte Alpha; |
|||
/// <summary>
|
|||
/// No documentation.
|
|||
/// </summary>
|
|||
/// <unmanaged>Red</unmanaged>
|
|||
/// <unmanaged-short>Red</unmanaged-short>
|
|||
public System.Byte Red; |
|||
/// <summary>
|
|||
/// No documentation.
|
|||
/// </summary>
|
|||
/// <unmanaged>Green</unmanaged>
|
|||
/// <unmanaged-short>Green</unmanaged-short>
|
|||
public System.Byte Green; |
|||
/// <summary>
|
|||
/// No documentation.
|
|||
/// </summary>
|
|||
/// <unmanaged>Blue</unmanaged>
|
|||
/// <unmanaged-short>Blue</unmanaged-short>
|
|||
public System.Byte Blue; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// No documentation.
|
|||
/// </summary>
|
|||
/// <unmanaged>AvnFramebuffer</unmanaged>
|
|||
/// <unmanaged-short>AvnFramebuffer</unmanaged-short>
|
|||
[System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential, Pack = 0, CharSet = System.Runtime.InteropServices.CharSet.Unicode)] |
|||
public partial struct AvnFramebuffer |
|||
{ |
|||
/// <summary>
|
|||
/// No documentation.
|
|||
/// </summary>
|
|||
/// <unmanaged>Data</unmanaged>
|
|||
/// <unmanaged-short>Data</unmanaged-short>
|
|||
public System.IntPtr Data; |
|||
/// <summary>
|
|||
/// No documentation.
|
|||
/// </summary>
|
|||
/// <unmanaged>Width</unmanaged>
|
|||
/// <unmanaged-short>Width</unmanaged-short>
|
|||
public System.Int32 Width; |
|||
/// <summary>
|
|||
/// No documentation.
|
|||
/// </summary>
|
|||
/// <unmanaged>Height</unmanaged>
|
|||
/// <unmanaged-short>Height</unmanaged-short>
|
|||
public System.Int32 Height; |
|||
/// <summary>
|
|||
/// No documentation.
|
|||
/// </summary>
|
|||
/// <unmanaged>Stride</unmanaged>
|
|||
/// <unmanaged-short>Stride</unmanaged-short>
|
|||
public System.Int32 Stride; |
|||
/// <summary>
|
|||
/// No documentation.
|
|||
/// </summary>
|
|||
/// <unmanaged>Dpi</unmanaged>
|
|||
/// <unmanaged-short>Dpi</unmanaged-short>
|
|||
public Avalonia.Native.Interop.AvnVector Dpi; |
|||
/// <summary>
|
|||
/// No documentation.
|
|||
/// </summary>
|
|||
/// <unmanaged>PixelFormat</unmanaged>
|
|||
/// <unmanaged-short>PixelFormat</unmanaged-short>
|
|||
public Avalonia.Native.Interop.AvnPixelFormat PixelFormat; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// No documentation.
|
|||
/// </summary>
|
|||
/// <unmanaged>AvnPixelSize</unmanaged>
|
|||
/// <unmanaged-short>AvnPixelSize</unmanaged-short>
|
|||
[System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential, Pack = 0, CharSet = System.Runtime.InteropServices.CharSet.Unicode)] |
|||
public partial struct AvnPixelSize |
|||
{ |
|||
/// <summary>
|
|||
/// No documentation.
|
|||
/// </summary>
|
|||
/// <unmanaged>Width</unmanaged>
|
|||
/// <unmanaged-short>Width</unmanaged-short>
|
|||
public System.Int32 Width; |
|||
/// <summary>
|
|||
/// No documentation.
|
|||
/// </summary>
|
|||
/// <unmanaged>Height</unmanaged>
|
|||
/// <unmanaged-short>Height</unmanaged-short>
|
|||
public System.Int32 Height; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// No documentation.
|
|||
/// </summary>
|
|||
/// <unmanaged>AvnPoint</unmanaged>
|
|||
/// <unmanaged-short>AvnPoint</unmanaged-short>
|
|||
[System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential, Pack = 0, CharSet = System.Runtime.InteropServices.CharSet.Unicode)] |
|||
public partial struct AvnPoint |
|||
{ |
|||
/// <summary>
|
|||
/// No documentation.
|
|||
/// </summary>
|
|||
/// <unmanaged>X</unmanaged>
|
|||
/// <unmanaged-short>X</unmanaged-short>
|
|||
public System.Double X; |
|||
/// <summary>
|
|||
/// No documentation.
|
|||
/// </summary>
|
|||
/// <unmanaged>Y</unmanaged>
|
|||
/// <unmanaged-short>Y</unmanaged-short>
|
|||
public System.Double Y; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// No documentation.
|
|||
/// </summary>
|
|||
/// <unmanaged>AvnRect</unmanaged>
|
|||
/// <unmanaged-short>AvnRect</unmanaged-short>
|
|||
[System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential, Pack = 0, CharSet = System.Runtime.InteropServices.CharSet.Unicode)] |
|||
public partial struct AvnRect |
|||
{ |
|||
/// <summary>
|
|||
/// No documentation.
|
|||
/// </summary>
|
|||
/// <unmanaged>X</unmanaged>
|
|||
/// <unmanaged-short>X</unmanaged-short>
|
|||
public System.Double X; |
|||
/// <summary>
|
|||
/// No documentation.
|
|||
/// </summary>
|
|||
/// <unmanaged>Y</unmanaged>
|
|||
/// <unmanaged-short>Y</unmanaged-short>
|
|||
public System.Double Y; |
|||
/// <summary>
|
|||
/// No documentation.
|
|||
/// </summary>
|
|||
/// <unmanaged>Width</unmanaged>
|
|||
/// <unmanaged-short>Width</unmanaged-short>
|
|||
public System.Double Width; |
|||
/// <summary>
|
|||
/// No documentation.
|
|||
/// </summary>
|
|||
/// <unmanaged>Height</unmanaged>
|
|||
/// <unmanaged-short>Height</unmanaged-short>
|
|||
public System.Double Height; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// No documentation.
|
|||
/// </summary>
|
|||
/// <unmanaged>AvnScreen</unmanaged>
|
|||
/// <unmanaged-short>AvnScreen</unmanaged-short>
|
|||
[System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential, Pack = 0, CharSet = System.Runtime.InteropServices.CharSet.Unicode)] |
|||
public partial struct AvnScreen |
|||
{ |
|||
/// <summary>
|
|||
/// No documentation.
|
|||
/// </summary>
|
|||
/// <unmanaged>Bounds</unmanaged>
|
|||
/// <unmanaged-short>Bounds</unmanaged-short>
|
|||
public Avalonia.Native.Interop.AvnRect Bounds; |
|||
/// <summary>
|
|||
/// No documentation.
|
|||
/// </summary>
|
|||
/// <unmanaged>WorkingArea</unmanaged>
|
|||
/// <unmanaged-short>WorkingArea</unmanaged-short>
|
|||
public Avalonia.Native.Interop.AvnRect WorkingArea; |
|||
/// <summary>
|
|||
/// No documentation.
|
|||
/// </summary>
|
|||
/// <unmanaged>PixelDensity</unmanaged>
|
|||
/// <unmanaged-short>PixelDensity</unmanaged-short>
|
|||
public System.Single PixelDensity; |
|||
/// <summary>
|
|||
/// No documentation.
|
|||
/// </summary>
|
|||
/// <unmanaged>Primary</unmanaged>
|
|||
/// <unmanaged-short>Primary</unmanaged-short>
|
|||
public bool Primary |
|||
{ |
|||
get => 0 != _Primary; |
|||
set => _Primary = (System.Byte)(value ? 1 : 0); |
|||
} |
|||
|
|||
internal System.Byte _Primary; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// No documentation.
|
|||
/// </summary>
|
|||
/// <unmanaged>AvnSize</unmanaged>
|
|||
/// <unmanaged-short>AvnSize</unmanaged-short>
|
|||
[System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential, Pack = 0, CharSet = System.Runtime.InteropServices.CharSet.Unicode)] |
|||
public partial struct AvnSize |
|||
{ |
|||
/// <summary>
|
|||
/// No documentation.
|
|||
/// </summary>
|
|||
/// <unmanaged>Width</unmanaged>
|
|||
/// <unmanaged-short>Width</unmanaged-short>
|
|||
public System.Double Width; |
|||
/// <summary>
|
|||
/// No documentation.
|
|||
/// </summary>
|
|||
/// <unmanaged>Height</unmanaged>
|
|||
/// <unmanaged-short>Height</unmanaged-short>
|
|||
public System.Double Height; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// No documentation.
|
|||
/// </summary>
|
|||
/// <unmanaged>AvnVector</unmanaged>
|
|||
/// <unmanaged-short>AvnVector</unmanaged-short>
|
|||
[System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential, Pack = 0, CharSet = System.Runtime.InteropServices.CharSet.Unicode)] |
|||
public partial struct AvnVector |
|||
{ |
|||
/// <summary>
|
|||
/// No documentation.
|
|||
/// </summary>
|
|||
/// <unmanaged>X</unmanaged>
|
|||
/// <unmanaged-short>X</unmanaged-short>
|
|||
public System.Double X; |
|||
/// <summary>
|
|||
/// No documentation.
|
|||
/// </summary>
|
|||
/// <unmanaged>Y</unmanaged>
|
|||
/// <unmanaged-short>Y</unmanaged-short>
|
|||
public System.Double Y; |
|||
} |
|||
} |
|||
@ -0,0 +1,12 @@ |
|||
<Project Sdk="Microsoft.NET.Sdk"> |
|||
<PropertyGroup> |
|||
<TargetFramework>netstandard2.0</TargetFramework> |
|||
<PackageId>Avalonia.ReactiveUI</PackageId> |
|||
</PropertyGroup> |
|||
<ItemGroup> |
|||
<ProjectReference Include="..\..\packages\Avalonia\Avalonia.csproj" /> |
|||
</ItemGroup> |
|||
<ItemGroup> |
|||
<PackageReference Include="Pharmacist.Common" Version="1.8.1" /> |
|||
</ItemGroup> |
|||
</Project> |
|||
Some files were not shown because too many files changed in this diff
Loading…
Reference in new issue