Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

.NET Standard support #329

Draft
wants to merge 2 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 6 additions & 3 deletions Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,17 @@
<Import Project="build\SourceLink.props" Condition="'$(DisableSourceLink)' == ''" />
<PropertyGroup>
<Nullable>enable</Nullable>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<WarningsAsErrors>nullable</WarningsAsErrors>
<TargetFramework>net6.0</TargetFramework>
<LangVersion>12</LangVersion>
<!-- https://github.com/dotnet/msbuild/issues/2661 -->
<AddSyntheticProjectReferencesForSolutionDependencies>false</AddSyntheticProjectReferencesForSolutionDependencies>
<MSBuildEnableWorkloadResolver>false</MSBuildEnableWorkloadResolver>
</PropertyGroup>

<PropertyGroup Condition="'$(TargetFramework)' != 'netstandard2.0'">
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<WarningsAsErrors>nullable</WarningsAsErrors>
</PropertyGroup>

<PropertyGroup>
<AvaloniaVersion>11.0.0</AvaloniaVersion>
</PropertyGroup>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
<TargetFrameworks>netstandard2.0;net5.0;net6.0</TargetFrameworks>
<IsPackable>True</IsPackable>
<RootNamespace>Avalonia.Controls</RootNamespace>
</PropertyGroup>
Expand All @@ -12,4 +12,13 @@

<PackageReference Include="System.Reactive" Version="5.0.0" />
</ItemGroup>

<ItemGroup Condition="'$(TargetFramework)' == 'netstandard2.0'">
<PackageReference Include="Microsoft.Bcl.HashCode" Version="6.0.0" />
<PackageReference Include="System.ValueTuple" Version="4.5.0" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' != 'netstandard2.0'">
<Compile Remove="StandardExtensions\**" />
<None Remove="StandardExtensions\**" />
</ItemGroup>
</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@ internal class AnonymousRow<TModel> : IRow<TModel>, IModelIndexableRow
private int _modelIndex;
[AllowNull] private TModel _model;

#if NETSTANDARD2_0
object? IRow.Model => _model;
#endif

public object? Header => _modelIndex;
public TModel Model => _model;
public int ModelIndex => _modelIndex;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@ public class HierarchicalRow<TModel> : NotifyingBase,
private bool _isExpanded;
private bool? _showExpander;

#if NETSTANDARD2_0
object? IRow.Model => Model;
#endif

public HierarchicalRow(
IExpanderRowController<TModel> controller,
IExpanderColumn<TModel> expanderColumn,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,12 @@ public interface IRow<TModel> : IRow
/// </summary>
new TModel Model { get; }

#if !NETSTANDARD2_0
/// <summary>
/// Gets the untyped row model.
/// </summary>
object? IRow.Model => Model;
#endif

/// <summary>
/// Updates the model index due to a change in the data source.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -649,8 +649,8 @@ private Rect EstimateViewport(Size availableSize)
return new Rect(
0,
0,
double.IsFinite(availableSize.Width) ? availableSize.Width : 0,
double.IsFinite(availableSize.Height) ? availableSize.Height : 0);
Double.IsFinite(availableSize.Width) ? availableSize.Width : 0,
Double.IsFinite(availableSize.Height) ? availableSize.Height : 0);
}

private void RecycleElement(Control element, int index)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,15 @@ public interface ITreeDataGridSelectionInteraction
{
public event EventHandler? SelectionChanged;

bool IsCellSelected(int columnIndex, int rowIndex) => false;
bool IsRowSelected(IRow rowModel) => false;
bool IsRowSelected(int rowIndex) => false;
public void OnKeyDown(TreeDataGrid sender, KeyEventArgs e) { }
public void OnPreviewKeyDown(TreeDataGrid sender, KeyEventArgs e) { }
public void OnKeyUp(TreeDataGrid sender, KeyEventArgs e) { }
public void OnTextInput(TreeDataGrid sender, TextInputEventArgs e) { }
public void OnPointerPressed(TreeDataGrid sender, PointerPressedEventArgs e) { }
public void OnPointerMoved(TreeDataGrid sender, PointerEventArgs e) { }
public void OnPointerReleased(TreeDataGrid sender, PointerReleasedEventArgs e) { }
bool IsCellSelected(int columnIndex, int rowIndex);
bool IsRowSelected(IRow rowModel);
bool IsRowSelected(int rowIndex);
void OnKeyDown(TreeDataGrid sender, KeyEventArgs e);
void OnPreviewKeyDown(TreeDataGrid sender, KeyEventArgs e);
void OnKeyUp(TreeDataGrid sender, KeyEventArgs e);
void OnTextInput(TreeDataGrid sender, TextInputEventArgs e);
void OnPointerPressed(TreeDataGrid sender, PointerPressedEventArgs e);
void OnPointerMoved(TreeDataGrid sender, PointerEventArgs e);
void OnPointerReleased(TreeDataGrid sender, PointerReleasedEventArgs e);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -121,8 +121,14 @@ sender.Rows is null ||
};

var anchor = shift ? _rangeAnchor : GetAnchor();

#if NETSTANDARD
var columnIndex = Math.Min(Math.Max(anchor.x + x, 0), sender.Columns.Count - 1);
var rowIndex = Math.Min(Math.Max(anchor.y + y, 0), sender.Rows.Count - 1);
#else
var columnIndex = Math.Clamp(anchor.x + x, 0, sender.Columns.Count - 1);
var rowIndex = Math.Clamp(anchor.y + y, 0, sender.Rows.Count - 1);
#endif

if (!shift)
Select(columnIndex, rowIndex);
Expand Down Expand Up @@ -171,6 +177,18 @@ e.Source is Control source &&
}
}

bool ITreeDataGridSelectionInteraction.IsRowSelected(IRow rowModel) => false;

bool ITreeDataGridSelectionInteraction.IsRowSelected(int rowIndex) => false;

void ITreeDataGridSelectionInteraction.OnPreviewKeyDown(TreeDataGrid sender, KeyEventArgs e) { }

void ITreeDataGridSelectionInteraction.OnKeyUp(TreeDataGrid sender, KeyEventArgs e) { }

void ITreeDataGridSelectionInteraction.OnTextInput(TreeDataGrid sender, TextInputEventArgs e) { }

void ITreeDataGridSelectionInteraction.OnPointerMoved(TreeDataGrid sender, PointerEventArgs e) { }

private void BeginBatchUpdate()
{
_selectedColumns.BeginBatchUpdate();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -356,6 +356,12 @@ void ITreeDataGridSelectionInteraction.OnTextInput(TreeDataGrid sender, TextInpu
HandleTextInput(e.Text, sender, _source.Rows.ModelIndexToRowIndex(AnchorIndex));
}

bool ITreeDataGridSelectionInteraction.IsCellSelected(int columnIndex, int rowIndex) => false;

void ITreeDataGridSelectionInteraction.OnKeyUp(TreeDataGrid sender, KeyEventArgs e) { }

void ITreeDataGridSelectionInteraction.OnPointerMoved(TreeDataGrid sender, PointerEventArgs e) { }

protected internal override IEnumerable<TModel>? GetChildren(TModel node)
{
if (_source is HierarchicalTreeDataGridSource<TModel> treeSource)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -519,8 +519,10 @@ private int CommitSelect(IndexRanges selectedRanges)
{
var result = 0;

foreach (var (parent, ranges) in selectedRanges.Ranges)
foreach (var row in selectedRanges.Ranges)
{
var parent = row.Key;
var ranges = row.Value;
var node = GetOrCreateNode(parent);

if (node is not null)
Expand All @@ -537,8 +539,10 @@ private int CommitDeselect(IndexRanges selectedRanges)
{
var result = 0;

foreach (var (parent, ranges) in selectedRanges.Ranges)
foreach (var row in selectedRanges.Ranges)
{
var parent = row.Key;
var ranges = row.Value;
var node = GetOrCreateNode(parent);

if (node is not null)
Expand Down
107 changes: 107 additions & 0 deletions src/Avalonia.Controls.TreeDataGrid/StandardExtensions/BitOperations.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;

// Some routines inspired by the Stanford Bit Twiddling Hacks by Sean Eron Anderson:
// http://graphics.stanford.edu/~seander/bithacks.html

namespace System.Numerics
{
/// <summary>
/// Utility methods for intrinsic bit-twiddling operations.
/// The methods use hardware intrinsics when available on the underlying platform,
/// otherwise they use optimized software fallbacks.
/// </summary>
internal static class BitOperations
{
// C# no-alloc optimization that directly wraps the data section of the dll (similar to string constants)
// https://github.com/dotnet/roslyn/pull/24621

private static ReadOnlySpan<byte> Log2DeBruijn => // 32
[
00, 09, 01, 10, 13, 21, 02, 29,
11, 14, 16, 18, 22, 25, 03, 30,
08, 12, 20, 28, 15, 17, 24, 07,
19, 27, 23, 06, 26, 05, 04, 31
];

/// <summary>
/// Returns the integer (floor) log of the specified value, base 2.
/// Note that by convention, input value 0 returns 0 since log(0) is undefined.
/// </summary>
/// <param name="value">The value.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int Log2(uint value)
{
// The 0->0 contract is fulfilled by setting the LSB to 1.
// Log(1) is 0, and setting the LSB for values > 1 does not change the log2 result.
value |= 1;

// Fallback contract is 0->0
return Log2SoftwareFallback(value);
}

/// <summary>
/// Returns the integer (floor) log of the specified value, base 2.
/// Note that by convention, input value 0 returns 0 since log(0) is undefined.
/// </summary>
/// <param name="value">The value.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int Log2(ulong value)
{
value |= 1;

uint hi = (uint)(value >> 32);

if (hi == 0)
{
return Log2((uint)value);
}

return 32 + Log2(hi);
}

/// <summary>
/// Returns the integer (floor) log of the specified value, base 2.
/// Note that by convention, input value 0 returns 0 since log(0) is undefined.
/// </summary>
/// <param name="value">The value.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int Log2(nuint value)
{
#if TARGET_64BIT
return Log2((ulong)value);
#else
return Log2((uint)value);
#endif
}

/// <summary>
/// Returns the integer (floor) log of the specified value, base 2.
/// Note that by convention, input value 0 returns 0 since Log(0) is undefined.
/// Does not directly use any hardware intrinsics, nor does it incur branching.
/// </summary>
/// <param name="value">The value.</param>
private static int Log2SoftwareFallback(uint value)
{
// No AggressiveInlining due to large method size
// Has conventional contract 0->0 (Log(0) is undefined)

// Fill trailing zeros with ones, eg 00010010 becomes 00011111
value |= value >> 01;
value |= value >> 02;
value |= value >> 04;
value |= value >> 08;
value |= value >> 16;

// uint.MaxValue >> 27 is always in range [0 - 31] so we use Unsafe.AddByteOffset to avoid bounds check
return Unsafe.AddByteOffset(
// Using deBruijn sequence, k=2, n=5 (2^5=32) : 0b_0000_0111_1100_0100_1010_1100_1101_1101u
ref MemoryMarshal.GetReference(Log2DeBruijn),
// uint|long -> IntPtr cast on 32-bit platforms does expensive overflow checks not needed here
(IntPtr)(int)((value * 0x07C4ACDDu) >> 27));
}
}
}
10 changes: 10 additions & 0 deletions src/Avalonia.Controls.TreeDataGrid/StandardExtensions/Double.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
namespace Avalonia;


internal static class Double
{
public static bool IsFinite(double value)
{
return !double.IsNaN(value) && !double.IsInfinity(value);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
namespace System.Runtime.CompilerServices;

/// <summary>
/// A class which allows to use C# 9's init and record features
/// in older target frameworks like .NET Standard 2.0
/// </summary>
internal static class IsExternalInit { }
Loading