283 changed files with 7873 additions and 4011 deletions
@ -0,0 +1,5 @@ |
|||
<ProjectConfiguration> |
|||
<Settings> |
|||
<IgnoreThisComponentCompletely>True</IgnoreThisComponentCompletely> |
|||
</Settings> |
|||
</ProjectConfiguration> |
|||
@ -0,0 +1,27 @@ |
|||
<UserControl xmlns="https://github.com/avaloniaui" |
|||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" |
|||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" |
|||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" |
|||
xmlns:viewModels="using:ControlCatalog.ViewModels" |
|||
mc:Ignorable="d" |
|||
d:DesignWidth="800" |
|||
d:DesignHeight="450" |
|||
x:DataType="viewModels:RefreshContainerViewModel" |
|||
x:Class="ControlCatalog.Pages.RefreshContainerPage"> |
|||
<DockPanel HorizontalAlignment="Stretch" |
|||
Height="600" |
|||
VerticalAlignment="Top"> |
|||
<Label DockPanel.Dock="Top">A control that supports pull to refresh</Label> |
|||
<RefreshContainer Name="Refresh" |
|||
DockPanel.Dock="Bottom" |
|||
HorizontalAlignment="Stretch" |
|||
VerticalAlignment="Stretch" |
|||
PullDirection="TopToBottom" |
|||
RefreshRequested="RefreshContainerPage_RefreshRequested" |
|||
Margin="5"> |
|||
<ListBox HorizontalAlignment="Stretch" |
|||
VerticalAlignment="Top" |
|||
Items="{Binding Items}"/> |
|||
</RefreshContainer> |
|||
</DockPanel> |
|||
</UserControl> |
|||
@ -0,0 +1,36 @@ |
|||
using System.Threading.Tasks; |
|||
using Avalonia; |
|||
using Avalonia.Controls; |
|||
using Avalonia.Markup.Xaml; |
|||
using ControlCatalog.ViewModels; |
|||
|
|||
namespace ControlCatalog.Pages |
|||
{ |
|||
public class RefreshContainerPage : UserControl |
|||
{ |
|||
private RefreshContainerViewModel _viewModel; |
|||
|
|||
public RefreshContainerPage() |
|||
{ |
|||
this.InitializeComponent(); |
|||
|
|||
_viewModel = new RefreshContainerViewModel(); |
|||
|
|||
DataContext = _viewModel; |
|||
} |
|||
|
|||
private async void RefreshContainerPage_RefreshRequested(object? sender, RefreshRequestedEventArgs e) |
|||
{ |
|||
var deferral = e.GetDeferral(); |
|||
|
|||
await _viewModel.AddToTop(); |
|||
|
|||
deferral.Complete(); |
|||
} |
|||
|
|||
private void InitializeComponent() |
|||
{ |
|||
AvaloniaXamlLoader.Load(this); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,26 @@ |
|||
using System.Collections.ObjectModel; |
|||
using System.Linq; |
|||
using System.Reactive; |
|||
using System.Threading.Tasks; |
|||
using Avalonia.Controls.Notifications; |
|||
using ControlCatalog.Pages; |
|||
using MiniMvvm; |
|||
|
|||
namespace ControlCatalog.ViewModels |
|||
{ |
|||
public class RefreshContainerViewModel : ViewModelBase |
|||
{ |
|||
public ObservableCollection<string> Items { get; } |
|||
|
|||
public RefreshContainerViewModel() |
|||
{ |
|||
Items = new ObservableCollection<string>(Enumerable.Range(1, 200).Select(i => $"Item {i}")); |
|||
} |
|||
|
|||
public async Task AddToTop() |
|||
{ |
|||
await Task.Delay(3000); |
|||
Items.Insert(0, $"Item {200 - Items.Count}"); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,8 @@ |
|||
<Application |
|||
xmlns="https://github.com/avaloniaui" |
|||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" |
|||
x:Class="ReactiveUIDemo.App"> |
|||
<Application.Styles> |
|||
<FluentTheme /> |
|||
</Application.Styles> |
|||
</Application> |
|||
@ -0,0 +1,37 @@ |
|||
using Avalonia; |
|||
using Avalonia.Controls.ApplicationLifetimes; |
|||
using Avalonia.Markup.Xaml; |
|||
using Avalonia.ReactiveUI; |
|||
using ReactiveUI; |
|||
using ReactiveUIDemo.ViewModels; |
|||
using ReactiveUIDemo.Views; |
|||
using Splat; |
|||
|
|||
namespace ReactiveUIDemo |
|||
{ |
|||
public class App : Application |
|||
{ |
|||
public override void Initialize() |
|||
{ |
|||
AvaloniaXamlLoader.Load(this); |
|||
Locator.CurrentMutable.Register(() => new FooView(), typeof(IViewFor<FooViewModel>)); |
|||
Locator.CurrentMutable.Register(() => new BarView(), typeof(IViewFor<BarViewModel>)); |
|||
} |
|||
|
|||
public override void OnFrameworkInitializationCompleted() |
|||
{ |
|||
if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) |
|||
desktop.MainWindow = new MainWindow(); |
|||
base.OnFrameworkInitializationCompleted(); |
|||
} |
|||
|
|||
public static int Main(string[] args) |
|||
=> BuildAvaloniaApp().StartWithClassicDesktopLifetime(args); |
|||
|
|||
public static AppBuilder BuildAvaloniaApp() |
|||
=> AppBuilder.Configure<App>() |
|||
.UsePlatformDetect() |
|||
.UseReactiveUI() |
|||
.LogToTrace(); |
|||
} |
|||
} |
|||
@ -0,0 +1,19 @@ |
|||
<Window xmlns="https://github.com/avaloniaui" |
|||
xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml' |
|||
x:Class="ReactiveUIDemo.MainWindow" |
|||
xmlns:vm="using:ReactiveUIDemo.ViewModels" |
|||
xmlns:rxui="using:Avalonia.ReactiveUI" |
|||
Title="AvaloniaUI ReactiveUI Demo" |
|||
x:DataType="vm:MainWindowViewModel"> |
|||
<TabControl TabStripPlacement="Left"> |
|||
<TabItem Header="RoutedViewHost"> |
|||
<DockPanel DataContext="{Binding RoutedViewHost}"> |
|||
<StackPanel DockPanel.Dock="Bottom" Orientation="Horizontal" Spacing="8"> |
|||
<Button Command="{Binding ShowFoo}">Foo</Button> |
|||
<Button Command="{Binding ShowBar}">Bar</Button> |
|||
</StackPanel> |
|||
<rxui:RoutedViewHost Router="{Binding Router}"/> |
|||
</DockPanel> |
|||
</TabItem> |
|||
</TabControl> |
|||
</Window> |
|||
@ -0,0 +1,22 @@ |
|||
using ReactiveUIDemo.ViewModels; |
|||
using Avalonia; |
|||
using Avalonia.Controls; |
|||
using Avalonia.Markup.Xaml; |
|||
|
|||
namespace ReactiveUIDemo |
|||
{ |
|||
public class MainWindow : Window |
|||
{ |
|||
public MainWindow() |
|||
{ |
|||
this.InitializeComponent(); |
|||
this.DataContext = new MainWindowViewModel(); |
|||
this.AttachDevTools(); |
|||
} |
|||
|
|||
private void InitializeComponent() |
|||
{ |
|||
AvaloniaXamlLoader.Load(this); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,28 @@ |
|||
<Project Sdk="Microsoft.NET.Sdk"> |
|||
<PropertyGroup> |
|||
<OutputType>Exe</OutputType> |
|||
<TargetFramework>net6.0</TargetFramework> |
|||
<Nullable>enable</Nullable> |
|||
</PropertyGroup> |
|||
|
|||
<ItemGroup> |
|||
<ProjectReference Include="..\..\src\Avalonia.Diagnostics\Avalonia.Diagnostics.csproj" /> |
|||
<ProjectReference Include="..\..\src\Avalonia.ReactiveUI\Avalonia.ReactiveUI.csproj" /> |
|||
<ProjectReference Include="..\..\src\Avalonia.Themes.Fluent\Avalonia.Themes.Fluent.csproj" /> |
|||
</ItemGroup> |
|||
|
|||
<ItemGroup> |
|||
<Compile Update="Views\BarView.axaml.cs"> |
|||
<DependentUpon>BarView.axaml</DependentUpon> |
|||
</Compile> |
|||
<Compile Update="Views\FooView.axaml.cs"> |
|||
<DependentUpon>FooView.axaml</DependentUpon> |
|||
</Compile> |
|||
</ItemGroup> |
|||
|
|||
<Import Project="..\..\build\SampleApp.props" /> |
|||
<Import Project="..\..\build\ReferenceCoreLibraries.props" /> |
|||
<Import Project="..\..\build\BuildTargets.targets" /> |
|||
<Import Project="..\..\build\Rx.props" /> |
|||
<Import Project="..\..\build\ReactiveUI.props" /> |
|||
</Project> |
|||
@ -0,0 +1,11 @@ |
|||
using ReactiveUI; |
|||
|
|||
namespace ReactiveUIDemo.ViewModels |
|||
{ |
|||
internal class BarViewModel : ReactiveObject, IRoutableViewModel |
|||
{ |
|||
public BarViewModel(IScreen screen) => HostScreen = screen; |
|||
public string UrlPathSegment => "Bar"; |
|||
public IScreen HostScreen { get; } |
|||
} |
|||
} |
|||
@ -0,0 +1,11 @@ |
|||
using ReactiveUI; |
|||
|
|||
namespace ReactiveUIDemo.ViewModels |
|||
{ |
|||
internal class FooViewModel : ReactiveObject, IRoutableViewModel |
|||
{ |
|||
public FooViewModel(IScreen screen) => HostScreen = screen; |
|||
public string UrlPathSegment => "Foo"; |
|||
public IScreen HostScreen { get; } |
|||
} |
|||
} |
|||
@ -0,0 +1,9 @@ |
|||
using ReactiveUI; |
|||
|
|||
namespace ReactiveUIDemo.ViewModels |
|||
{ |
|||
internal class MainWindowViewModel : ReactiveObject |
|||
{ |
|||
public RoutedViewHostPageViewModel RoutedViewHost { get; } = new(); |
|||
} |
|||
} |
|||
@ -0,0 +1,21 @@ |
|||
using ReactiveUI; |
|||
|
|||
namespace ReactiveUIDemo.ViewModels |
|||
{ |
|||
internal class RoutedViewHostPageViewModel : ReactiveObject, IScreen |
|||
{ |
|||
public RoutedViewHostPageViewModel() |
|||
{ |
|||
Foo = new(this); |
|||
Bar = new(this); |
|||
Router.Navigate.Execute(Foo); |
|||
} |
|||
|
|||
public RoutingState Router { get; } = new(); |
|||
public FooViewModel Foo { get; } |
|||
public BarViewModel Bar { get; } |
|||
|
|||
public void ShowFoo() => Router.Navigate.Execute(Foo); |
|||
public void ShowBar() => Router.Navigate.Execute(Bar); |
|||
} |
|||
} |
|||
@ -0,0 +1,16 @@ |
|||
<UserControl xmlns="https://github.com/avaloniaui" |
|||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" |
|||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" |
|||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" |
|||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" |
|||
x:Class="ReactiveUIDemo.Views.BarView" |
|||
HorizontalAlignment="Stretch" |
|||
VerticalAlignment="Stretch"> |
|||
<Border Background="Blue"> |
|||
<TextBlock HorizontalAlignment="Center" |
|||
VerticalAlignment="Center" |
|||
FontSize="48"> |
|||
Bar! |
|||
</TextBlock> |
|||
</Border> |
|||
</UserControl> |
|||
@ -0,0 +1,28 @@ |
|||
using Avalonia.Controls; |
|||
using Avalonia.Markup.Xaml; |
|||
using ReactiveUI; |
|||
using ReactiveUIDemo.ViewModels; |
|||
|
|||
namespace ReactiveUIDemo.Views |
|||
{ |
|||
internal partial class BarView : UserControl, IViewFor<BarViewModel> |
|||
{ |
|||
public BarView() |
|||
{ |
|||
InitializeComponent(); |
|||
} |
|||
|
|||
public BarViewModel? ViewModel { get; set; } |
|||
|
|||
object? IViewFor.ViewModel |
|||
{ |
|||
get => ViewModel; |
|||
set => ViewModel = (BarViewModel?)value; |
|||
} |
|||
|
|||
private void InitializeComponent() |
|||
{ |
|||
AvaloniaXamlLoader.Load(this); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,16 @@ |
|||
<UserControl xmlns="https://github.com/avaloniaui" |
|||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" |
|||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" |
|||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" |
|||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" |
|||
x:Class="ReactiveUIDemo.Views.FooView" |
|||
HorizontalAlignment="Stretch" |
|||
VerticalAlignment="Stretch"> |
|||
<Border Background="Red"> |
|||
<TextBlock HorizontalAlignment="Center" |
|||
VerticalAlignment="Center" |
|||
FontSize="48"> |
|||
Foo! |
|||
</TextBlock> |
|||
</Border> |
|||
</UserControl> |
|||
@ -0,0 +1,28 @@ |
|||
using Avalonia.Controls; |
|||
using Avalonia.Markup.Xaml; |
|||
using ReactiveUI; |
|||
using ReactiveUIDemo.ViewModels; |
|||
|
|||
namespace ReactiveUIDemo.Views |
|||
{ |
|||
internal partial class FooView : UserControl, IViewFor<FooViewModel> |
|||
{ |
|||
public FooView() |
|||
{ |
|||
InitializeComponent(); |
|||
} |
|||
|
|||
public FooViewModel? ViewModel { get; set; } |
|||
|
|||
object? IViewFor.ViewModel |
|||
{ |
|||
get => ViewModel; |
|||
set => ViewModel = (FooViewModel?)value; |
|||
} |
|||
|
|||
private void InitializeComponent() |
|||
{ |
|||
AvaloniaXamlLoader.Load(this); |
|||
} |
|||
} |
|||
} |
|||
@ -1,32 +0,0 @@ |
|||
using Avalonia.OpenGL; |
|||
using Avalonia.OpenGL.Egl; |
|||
using Avalonia.OpenGL.Surfaces; |
|||
|
|||
namespace Avalonia.Android.OpenGL |
|||
{ |
|||
internal sealed class GlPlatformSurface : EglGlPlatformSurfaceBase |
|||
{ |
|||
private readonly EglPlatformOpenGlInterface _egl; |
|||
private readonly IEglWindowGlPlatformSurfaceInfo _info; |
|||
|
|||
private GlPlatformSurface(EglPlatformOpenGlInterface egl, IEglWindowGlPlatformSurfaceInfo info) |
|||
{ |
|||
_egl = egl; |
|||
_info = info; |
|||
} |
|||
|
|||
public override IGlPlatformSurfaceRenderTarget CreateGlRenderTarget() => |
|||
new GlRenderTarget(_egl, _info, _egl.CreateWindowSurface(_info.Handle), _info.Handle); |
|||
|
|||
public static GlPlatformSurface TryCreate(IEglWindowGlPlatformSurfaceInfo info) |
|||
{ |
|||
var feature = AvaloniaLocator.Current.GetService<IPlatformOpenGlInterface>(); |
|||
if (feature is EglPlatformOpenGlInterface egl) |
|||
{ |
|||
return new GlPlatformSurface(egl, info); |
|||
} |
|||
|
|||
return null; |
|||
} |
|||
} |
|||
} |
|||
@ -1,30 +0,0 @@ |
|||
using System; |
|||
|
|||
using Avalonia.OpenGL.Egl; |
|||
using Avalonia.OpenGL.Surfaces; |
|||
|
|||
namespace Avalonia.Android.OpenGL |
|||
{ |
|||
internal sealed class GlRenderTarget : EglPlatformSurfaceRenderTargetBase, IGlPlatformSurfaceRenderTargetWithCorruptionInfo |
|||
{ |
|||
private readonly EglGlPlatformSurfaceBase.IEglWindowGlPlatformSurfaceInfo _info; |
|||
private readonly EglSurface _surface; |
|||
private readonly IntPtr _handle; |
|||
|
|||
public GlRenderTarget( |
|||
EglPlatformOpenGlInterface egl, |
|||
EglGlPlatformSurfaceBase.IEglWindowGlPlatformSurfaceInfo info, |
|||
EglSurface surface, |
|||
IntPtr handle) |
|||
: base(egl) |
|||
{ |
|||
_info = info; |
|||
_surface = surface; |
|||
_handle = handle; |
|||
} |
|||
|
|||
public bool IsCorrupted => _handle != _info.Handle; |
|||
|
|||
public override IGlPlatformSurfaceRenderingSession BeginDraw() => BeginDraw(_surface, _info); |
|||
} |
|||
} |
|||
@ -0,0 +1,152 @@ |
|||
using Avalonia.Input.GestureRecognizers; |
|||
|
|||
namespace Avalonia.Input |
|||
{ |
|||
public class PullGestureRecognizer : StyledElement, IGestureRecognizer |
|||
{ |
|||
private IInputElement? _target; |
|||
private IGestureRecognizerActionsDispatcher? _actions; |
|||
private Point _initialPosition; |
|||
private int _gestureId; |
|||
private IPointer? _tracking; |
|||
private PullDirection _pullDirection; |
|||
|
|||
/// <summary>
|
|||
/// Defines the <see cref="PullDirection"/> property.
|
|||
/// </summary>
|
|||
public static readonly DirectProperty<PullGestureRecognizer, PullDirection> PullDirectionProperty = |
|||
AvaloniaProperty.RegisterDirect<PullGestureRecognizer, PullDirection>( |
|||
nameof(PullDirection), |
|||
o => o.PullDirection, |
|||
(o, v) => o.PullDirection = v); |
|||
|
|||
public PullDirection PullDirection |
|||
{ |
|||
get => _pullDirection; |
|||
set => SetAndRaise(PullDirectionProperty, ref _pullDirection, value); |
|||
} |
|||
|
|||
public PullGestureRecognizer(PullDirection pullDirection) |
|||
{ |
|||
PullDirection = pullDirection; |
|||
} |
|||
|
|||
public void Initialize(IInputElement target, IGestureRecognizerActionsDispatcher actions) |
|||
{ |
|||
_target = target; |
|||
_actions = actions; |
|||
|
|||
_target?.AddHandler(InputElement.PointerPressedEvent, OnPointerPressed, Interactivity.RoutingStrategies.Tunnel | Interactivity.RoutingStrategies.Bubble); |
|||
_target?.AddHandler(InputElement.PointerReleasedEvent, OnPointerReleased, Interactivity.RoutingStrategies.Tunnel | Interactivity.RoutingStrategies.Bubble); |
|||
} |
|||
|
|||
private void OnPointerPressed(object? sender, PointerPressedEventArgs e) |
|||
{ |
|||
PointerPressed(e); |
|||
} |
|||
|
|||
private void OnPointerReleased(object? sender, PointerReleasedEventArgs e) |
|||
{ |
|||
PointerReleased(e); |
|||
} |
|||
|
|||
public void PointerCaptureLost(IPointer pointer) |
|||
{ |
|||
if (_tracking == pointer) |
|||
{ |
|||
EndPull(); |
|||
} |
|||
} |
|||
|
|||
public void PointerMoved(PointerEventArgs e) |
|||
{ |
|||
if (_tracking == e.Pointer && _target is Visual visual) |
|||
{ |
|||
var currentPosition = e.GetPosition(visual); |
|||
_actions!.Capture(e.Pointer, this); |
|||
|
|||
Vector delta = default; |
|||
switch (PullDirection) |
|||
{ |
|||
case PullDirection.TopToBottom: |
|||
if (currentPosition.Y > _initialPosition.Y) |
|||
{ |
|||
delta = new Vector(0, currentPosition.Y - _initialPosition.Y); |
|||
} |
|||
break; |
|||
case PullDirection.BottomToTop: |
|||
if (currentPosition.Y < _initialPosition.Y) |
|||
{ |
|||
delta = new Vector(0, _initialPosition.Y - currentPosition.Y); |
|||
} |
|||
break; |
|||
case PullDirection.LeftToRight: |
|||
if (currentPosition.X > _initialPosition.X) |
|||
{ |
|||
delta = new Vector(currentPosition.X - _initialPosition.X, 0); |
|||
} |
|||
break; |
|||
case PullDirection.RightToLeft: |
|||
if (currentPosition.X < _initialPosition.X) |
|||
{ |
|||
delta = new Vector(_initialPosition.X - currentPosition.X, 0); |
|||
} |
|||
break; |
|||
} |
|||
|
|||
_target?.RaiseEvent(new PullGestureEventArgs(_gestureId, delta, PullDirection)); |
|||
} |
|||
} |
|||
|
|||
public void PointerPressed(PointerPressedEventArgs e) |
|||
{ |
|||
if (_target != null && _target is Visual visual && (e.Pointer.Type == PointerType.Touch || e.Pointer.Type == PointerType.Pen)) |
|||
{ |
|||
var position = e.GetPosition(visual); |
|||
|
|||
var canPull = false; |
|||
|
|||
var bounds = visual.Bounds; |
|||
|
|||
switch (PullDirection) |
|||
{ |
|||
case PullDirection.TopToBottom: |
|||
canPull = position.Y < bounds.Height * 0.1; |
|||
break; |
|||
case PullDirection.BottomToTop: |
|||
canPull = position.Y > bounds.Height - (bounds.Height * 0.1); |
|||
break; |
|||
case PullDirection.LeftToRight: |
|||
canPull = position.X < bounds.Width * 0.1; |
|||
break; |
|||
case PullDirection.RightToLeft: |
|||
canPull = position.X > bounds.Width - (bounds.Width * 0.1); |
|||
break; |
|||
} |
|||
|
|||
if (canPull) |
|||
{ |
|||
_gestureId = PullGestureEventArgs.GetNextFreeId(); |
|||
_tracking = e.Pointer; |
|||
_initialPosition = position; |
|||
} |
|||
} |
|||
} |
|||
|
|||
public void PointerReleased(PointerReleasedEventArgs e) |
|||
{ |
|||
if (_tracking == e.Pointer) |
|||
{ |
|||
EndPull(); |
|||
} |
|||
} |
|||
|
|||
private void EndPull() |
|||
{ |
|||
_tracking = null; |
|||
_initialPosition = default; |
|||
|
|||
_target?.RaiseEvent(new PullGestureEndedEventArgs(_gestureId, PullDirection)); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,43 @@ |
|||
using System; |
|||
using Avalonia.Interactivity; |
|||
|
|||
namespace Avalonia.Input |
|||
{ |
|||
public class PullGestureEventArgs : RoutedEventArgs |
|||
{ |
|||
public int Id { get; } |
|||
public Vector Delta { get; } |
|||
public PullDirection PullDirection { get; } |
|||
|
|||
private static int _nextId = 1; |
|||
|
|||
internal static int GetNextFreeId() => _nextId++; |
|||
|
|||
public PullGestureEventArgs(int id, Vector delta, PullDirection pullDirection) : base(Gestures.PullGestureEvent) |
|||
{ |
|||
Id = id; |
|||
Delta = delta; |
|||
PullDirection = pullDirection; |
|||
} |
|||
} |
|||
|
|||
public class PullGestureEndedEventArgs : RoutedEventArgs |
|||
{ |
|||
public int Id { get; } |
|||
public PullDirection PullDirection { get; } |
|||
|
|||
public PullGestureEndedEventArgs(int id, PullDirection pullDirection) : base(Gestures.PullGestureEndedEvent) |
|||
{ |
|||
Id = id; |
|||
PullDirection = pullDirection; |
|||
} |
|||
} |
|||
|
|||
public enum PullDirection |
|||
{ |
|||
TopToBottom, |
|||
BottomToTop, |
|||
LeftToRight, |
|||
RightToLeft |
|||
} |
|||
} |
|||
@ -0,0 +1,293 @@ |
|||
using System; |
|||
using System.Collections; |
|||
using System.Collections.Generic; |
|||
using System.Runtime.CompilerServices; |
|||
using Avalonia.Utilities; |
|||
|
|||
namespace Avalonia.Media.TextFormatting |
|||
{ |
|||
public readonly struct CharacterBufferRange : IReadOnlyList<char> |
|||
{ |
|||
/// <summary>
|
|||
/// Getting an empty character string
|
|||
/// </summary>
|
|||
public static CharacterBufferRange Empty => new CharacterBufferRange(); |
|||
|
|||
/// <summary>
|
|||
/// Construct <see cref="CharacterBufferRange"/> from character array
|
|||
/// </summary>
|
|||
/// <param name="characterArray">character array</param>
|
|||
/// <param name="offsetToFirstChar">character buffer offset to the first character</param>
|
|||
/// <param name="characterLength">character length</param>
|
|||
public CharacterBufferRange( |
|||
char[] characterArray, |
|||
int offsetToFirstChar, |
|||
int characterLength |
|||
) |
|||
: this( |
|||
new CharacterBufferReference(characterArray, offsetToFirstChar), |
|||
characterLength |
|||
) |
|||
{ } |
|||
|
|||
/// <summary>
|
|||
/// Construct <see cref="CharacterBufferRange"/> from string
|
|||
/// </summary>
|
|||
/// <param name="characterString">character string</param>
|
|||
/// <param name="offsetToFirstChar">character buffer offset to the first character</param>
|
|||
/// <param name="characterLength">character length</param>
|
|||
public CharacterBufferRange( |
|||
string characterString, |
|||
int offsetToFirstChar, |
|||
int characterLength |
|||
) |
|||
: this( |
|||
new CharacterBufferReference(characterString, offsetToFirstChar), |
|||
characterLength |
|||
) |
|||
{ } |
|||
|
|||
/// <summary>
|
|||
/// Construct a <see cref="CharacterBufferRange"/> from <see cref="CharacterBufferReference"/>
|
|||
/// </summary>
|
|||
/// <param name="characterBufferReference">character buffer reference</param>
|
|||
/// <param name="characterLength">number of characters</param>
|
|||
public CharacterBufferRange( |
|||
CharacterBufferReference characterBufferReference, |
|||
int characterLength |
|||
) |
|||
{ |
|||
if (characterLength < 0) |
|||
{ |
|||
throw new ArgumentOutOfRangeException("characterLength", "ParameterCannotBeNegative"); |
|||
} |
|||
|
|||
int maxLength = characterBufferReference.CharacterBuffer.Length > 0 ? |
|||
characterBufferReference.CharacterBuffer.Length - characterBufferReference.OffsetToFirstChar : |
|||
0; |
|||
|
|||
if (characterLength > maxLength) |
|||
{ |
|||
throw new ArgumentOutOfRangeException("characterLength", $"ParameterCannotBeGreaterThan {maxLength}"); |
|||
} |
|||
|
|||
CharacterBufferReference = characterBufferReference; |
|||
Length = characterLength; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Construct a <see cref="CharacterBufferRange"/> from part of another <see cref="CharacterBufferRange"/>
|
|||
/// </summary>
|
|||
internal CharacterBufferRange( |
|||
CharacterBufferRange characterBufferRange, |
|||
int offsetToFirstChar, |
|||
int characterLength |
|||
) : |
|||
this( |
|||
characterBufferRange.CharacterBuffer, |
|||
characterBufferRange.OffsetToFirstChar + offsetToFirstChar, |
|||
characterLength |
|||
) |
|||
{ } |
|||
|
|||
|
|||
/// <summary>
|
|||
/// Construct a <see cref="CharacterBufferRange"/> from string
|
|||
/// </summary>
|
|||
internal CharacterBufferRange( |
|||
string charString |
|||
) : |
|||
this( |
|||
charString, |
|||
0, |
|||
charString.Length |
|||
) |
|||
{ } |
|||
|
|||
|
|||
/// <summary>
|
|||
/// Construct <see cref="CharacterBufferRange"/> from memory buffer
|
|||
/// </summary>
|
|||
internal CharacterBufferRange( |
|||
ReadOnlyMemory<char> charBuffer, |
|||
int offsetToFirstChar, |
|||
int characterLength |
|||
) : |
|||
this( |
|||
new CharacterBufferReference(charBuffer, offsetToFirstChar), |
|||
characterLength |
|||
) |
|||
{ } |
|||
|
|||
|
|||
/// <summary>
|
|||
/// Construct a <see cref="CharacterBufferRange"/> by extracting text info from a text run
|
|||
/// </summary>
|
|||
internal CharacterBufferRange(TextRun textRun) |
|||
{ |
|||
CharacterBufferReference = textRun.CharacterBufferReference; |
|||
Length = textRun.Length; |
|||
} |
|||
|
|||
public char this[int index] |
|||
{ |
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
get |
|||
{ |
|||
#if DEBUG
|
|||
if (index.CompareTo(0) < 0 || index.CompareTo(Length) > 0) |
|||
{ |
|||
throw new ArgumentOutOfRangeException(nameof(index)); |
|||
} |
|||
#endif
|
|||
return Span[index]; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets a reference to the character buffer
|
|||
/// </summary>
|
|||
public CharacterBufferReference CharacterBufferReference { get; } |
|||
|
|||
/// <summary>
|
|||
/// Gets the number of characters in text source character store
|
|||
/// </summary>
|
|||
public int Length { get; } |
|||
|
|||
/// <summary>
|
|||
/// Gets a span from the character buffer range
|
|||
/// </summary>
|
|||
public ReadOnlySpan<char> Span => |
|||
CharacterBufferReference.CharacterBuffer.Span.Slice(CharacterBufferReference.OffsetToFirstChar, Length); |
|||
|
|||
/// <summary>
|
|||
/// Gets the character memory buffer
|
|||
/// </summary>
|
|||
internal ReadOnlyMemory<char> CharacterBuffer |
|||
{ |
|||
get { return CharacterBufferReference.CharacterBuffer; } |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets the character offset relative to the beginning of buffer to
|
|||
/// the first character of the run
|
|||
/// </summary>
|
|||
internal int OffsetToFirstChar |
|||
{ |
|||
get { return CharacterBufferReference.OffsetToFirstChar; } |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Indicate whether the character buffer range is empty
|
|||
/// </summary>
|
|||
internal bool IsEmpty |
|||
{ |
|||
get { return CharacterBufferReference.CharacterBuffer.Length == 0 || Length <= 0; } |
|||
} |
|||
|
|||
internal CharacterBufferRange Take(int length) |
|||
{ |
|||
if (IsEmpty) |
|||
{ |
|||
return this; |
|||
} |
|||
|
|||
if (length > Length) |
|||
{ |
|||
throw new ArgumentOutOfRangeException(nameof(length)); |
|||
} |
|||
|
|||
return new CharacterBufferRange(CharacterBufferReference, length); |
|||
} |
|||
|
|||
internal CharacterBufferRange Skip(int length) |
|||
{ |
|||
if (IsEmpty) |
|||
{ |
|||
return this; |
|||
} |
|||
|
|||
if (length > Length) |
|||
{ |
|||
throw new ArgumentOutOfRangeException(nameof(length)); |
|||
} |
|||
|
|||
if (length == Length) |
|||
{ |
|||
return new CharacterBufferRange(new CharacterBufferReference(), 0); |
|||
} |
|||
|
|||
var characterBufferReference = new CharacterBufferReference( |
|||
CharacterBufferReference.CharacterBuffer, |
|||
CharacterBufferReference.OffsetToFirstChar + length); |
|||
|
|||
return new CharacterBufferRange(characterBufferReference, Length - length); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Compute hash code
|
|||
/// </summary>
|
|||
public override int GetHashCode() |
|||
{ |
|||
return CharacterBufferReference.GetHashCode() ^ Length; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Test equality with the input object
|
|||
/// </summary>
|
|||
/// <param name="obj"> The object to test </param>
|
|||
public override bool Equals(object? obj) |
|||
{ |
|||
if (obj is CharacterBufferRange range) |
|||
{ |
|||
return Equals(range); |
|||
} |
|||
|
|||
return false; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Test equality with the input CharacterBufferRange
|
|||
/// </summary>
|
|||
/// <param name="value"> The CharacterBufferRange value to test </param>
|
|||
public bool Equals(CharacterBufferRange value) |
|||
{ |
|||
return CharacterBufferReference.Equals(value.CharacterBufferReference) |
|||
&& Length == value.Length; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Compare two CharacterBufferRange for equality
|
|||
/// </summary>
|
|||
/// <param name="left">left operand</param>
|
|||
/// <param name="right">right operand</param>
|
|||
/// <returns>whether or not two operands are equal</returns>
|
|||
public static bool operator ==(CharacterBufferRange left, CharacterBufferRange right) |
|||
{ |
|||
return left.Equals(right); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Compare two CharacterBufferRange for inequality
|
|||
/// </summary>
|
|||
/// <param name="left">left operand</param>
|
|||
/// <param name="right">right operand</param>
|
|||
/// <returns>whether or not two operands are equal</returns>
|
|||
public static bool operator !=(CharacterBufferRange left, CharacterBufferRange right) |
|||
{ |
|||
return !(left == right); |
|||
} |
|||
|
|||
int IReadOnlyCollection<char>.Count => Length; |
|||
|
|||
public IEnumerator<char> GetEnumerator() |
|||
{ |
|||
return new ImmutableReadOnlyListStructEnumerator<char>(this); |
|||
} |
|||
|
|||
IEnumerator IEnumerable.GetEnumerator() |
|||
{ |
|||
return GetEnumerator(); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,115 @@ |
|||
using System; |
|||
|
|||
namespace Avalonia.Media.TextFormatting |
|||
{ |
|||
/// <summary>
|
|||
/// Text character buffer reference
|
|||
/// </summary>
|
|||
public readonly struct CharacterBufferReference : IEquatable<CharacterBufferReference> |
|||
{ |
|||
/// <summary>
|
|||
/// Construct character buffer reference from character array
|
|||
/// </summary>
|
|||
/// <param name="characterArray">character array</param>
|
|||
/// <param name="offsetToFirstChar">character buffer offset to the first character</param>
|
|||
public CharacterBufferReference(char[] characterArray, int offsetToFirstChar = 0) |
|||
: this(characterArray.AsMemory(), offsetToFirstChar) |
|||
{ } |
|||
|
|||
/// <summary>
|
|||
/// Construct character buffer reference from string
|
|||
/// </summary>
|
|||
/// <param name="characterString">character string</param>
|
|||
/// <param name="offsetToFirstChar">character buffer offset to the first character</param>
|
|||
public CharacterBufferReference(string characterString, int offsetToFirstChar = 0) |
|||
: this(characterString.AsMemory(), offsetToFirstChar) |
|||
{ } |
|||
|
|||
/// <summary>
|
|||
/// Construct character buffer reference from memory buffer
|
|||
/// </summary>
|
|||
internal CharacterBufferReference(ReadOnlyMemory<char> characterBuffer, int offsetToFirstChar = 0) |
|||
{ |
|||
if (offsetToFirstChar < 0) |
|||
{ |
|||
throw new ArgumentOutOfRangeException("offsetToFirstChar", "ParameterCannotBeNegative"); |
|||
} |
|||
|
|||
// maximum offset is one less than CharacterBuffer.Count, except that zero is always a valid offset
|
|||
// even in the case of an empty or null character buffer
|
|||
var maxOffset = characterBuffer.Length == 0 ? 0 : Math.Max(0, characterBuffer.Length - 1); |
|||
if (offsetToFirstChar > maxOffset) |
|||
{ |
|||
throw new ArgumentOutOfRangeException("offsetToFirstChar", $"ParameterCannotBeGreaterThan, {maxOffset}"); |
|||
} |
|||
|
|||
CharacterBuffer = characterBuffer; |
|||
OffsetToFirstChar = offsetToFirstChar; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets the character memory buffer
|
|||
/// </summary>
|
|||
public ReadOnlyMemory<char> CharacterBuffer { get; } |
|||
|
|||
/// <summary>
|
|||
/// Gets the character offset relative to the beginning of buffer to
|
|||
/// the first character of the run
|
|||
/// </summary>
|
|||
public int OffsetToFirstChar { get; } |
|||
|
|||
/// <summary>
|
|||
/// Compute hash code
|
|||
/// </summary>
|
|||
public override int GetHashCode() |
|||
{ |
|||
return CharacterBuffer.IsEmpty ? 0 : CharacterBuffer.GetHashCode(); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Test equality with the input object
|
|||
/// </summary>
|
|||
/// <param name="obj"> The object to test. </param>
|
|||
public override bool Equals(object? obj) |
|||
{ |
|||
if (obj is CharacterBufferReference reference) |
|||
{ |
|||
return Equals(reference); |
|||
} |
|||
|
|||
return false; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Test equality with the input CharacterBufferReference
|
|||
/// </summary>
|
|||
/// <param name="value"> The characterBufferReference value to test </param>
|
|||
public bool Equals(CharacterBufferReference value) |
|||
{ |
|||
return CharacterBuffer.Equals(value.CharacterBuffer); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Compare two CharacterBufferReference for equality
|
|||
/// </summary>
|
|||
/// <param name="left">left operand</param>
|
|||
/// <param name="right">right operand</param>
|
|||
/// <returns>whether or not two operands are equal</returns>
|
|||
public static bool operator ==(CharacterBufferReference left, CharacterBufferReference right) |
|||
{ |
|||
return left.Equals(right); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Compare two CharacterBufferReference for inequality
|
|||
/// </summary>
|
|||
/// <param name="left">left operand</param>
|
|||
/// <param name="right">right operand</param>
|
|||
/// <returns>whether or not two operands are equal</returns>
|
|||
public static bool operator !=(CharacterBufferReference left, CharacterBufferReference right) |
|||
{ |
|||
return !(left == right); |
|||
} |
|||
} |
|||
} |
|||
|
|||
@ -0,0 +1,18 @@ |
|||
using System; |
|||
|
|||
namespace Avalonia.Platform; |
|||
|
|||
public interface IOptionalFeatureProvider |
|||
{ |
|||
/// <summary>
|
|||
/// Queries for an optional feature
|
|||
/// </summary>
|
|||
/// <param name="featureType">Feature type</param>
|
|||
public object? TryGetFeature(Type featureType); |
|||
} |
|||
|
|||
public static class OptionalFeatureProviderExtensions |
|||
{ |
|||
public static T? TryGetFeature<T>(this IOptionalFeatureProvider provider) where T : class => |
|||
(T?)provider.TryGetFeature(typeof(T)); |
|||
} |
|||
@ -0,0 +1,24 @@ |
|||
using System; |
|||
|
|||
namespace Avalonia.Rendering; |
|||
|
|||
struct OwnedDisposable<T> :IDisposable where T : class, IDisposable |
|||
{ |
|||
private readonly bool _owns; |
|||
private T? _value; |
|||
|
|||
public T Value => _value ?? throw new ObjectDisposedException("OwnedDisposable"); |
|||
|
|||
public OwnedDisposable(T value, bool owns) |
|||
{ |
|||
_owns = owns; |
|||
_value = value; |
|||
} |
|||
|
|||
public void Dispose() |
|||
{ |
|||
if(_owns) |
|||
_value?.Dispose(); |
|||
_value = null; |
|||
} |
|||
} |
|||
@ -0,0 +1,66 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Reactive.Disposables; |
|||
using Avalonia.Metadata; |
|||
using Avalonia.Platform; |
|||
|
|||
namespace Avalonia.Rendering; |
|||
|
|||
[Unstable] |
|||
// TODO: Make it internal once legacy renderers are removed
|
|||
public class PlatformRenderInterfaceContextManager |
|||
{ |
|||
private readonly IPlatformGraphics? _graphics; |
|||
private IPlatformRenderInterfaceContext? _backend; |
|||
private OwnedDisposable<IPlatformGraphicsContext>? _gpuContext; |
|||
|
|||
public PlatformRenderInterfaceContextManager(IPlatformGraphics? graphics) |
|||
{ |
|||
_graphics = graphics; |
|||
} |
|||
|
|||
public void EnsureValidBackendContext() |
|||
{ |
|||
if (_backend == null || _gpuContext?.Value.IsLost == true) |
|||
{ |
|||
_backend?.Dispose(); |
|||
_backend = null; |
|||
_gpuContext?.Dispose(); |
|||
_gpuContext = null; |
|||
|
|||
if (_graphics != null) |
|||
{ |
|||
if (_graphics.UsesSharedContext) |
|||
_gpuContext = new OwnedDisposable<IPlatformGraphicsContext>(_graphics.GetSharedContext(), false); |
|||
else |
|||
_gpuContext = new OwnedDisposable<IPlatformGraphicsContext>(_graphics.CreateContext(), true); |
|||
} |
|||
|
|||
_backend = AvaloniaLocator.Current.GetRequiredService<IPlatformRenderInterface>() |
|||
.CreateBackendContext(_gpuContext?.Value); |
|||
} |
|||
} |
|||
|
|||
public IPlatformRenderInterfaceContext Value |
|||
{ |
|||
get |
|||
{ |
|||
EnsureValidBackendContext(); |
|||
return _backend!; |
|||
} |
|||
} |
|||
|
|||
public IDisposable EnsureCurrent() |
|||
{ |
|||
EnsureValidBackendContext(); |
|||
if (_gpuContext.HasValue) |
|||
return _gpuContext.Value.Value.EnsureCurrent(); |
|||
return Disposable.Empty; |
|||
} |
|||
|
|||
public IRenderTarget CreateRenderTarget(IEnumerable<object> surfaces) |
|||
{ |
|||
EnsureValidBackendContext(); |
|||
return _backend!.CreateRenderTarget(surfaces); |
|||
} |
|||
} |
|||
@ -1,239 +0,0 @@ |
|||
using System; |
|||
using System.Collections; |
|||
using System.Collections.Generic; |
|||
using System.Diagnostics; |
|||
using System.Runtime.CompilerServices; |
|||
|
|||
namespace Avalonia.Utilities |
|||
{ |
|||
/// <summary>
|
|||
/// ReadOnlySlice enables the ability to work with a sequence within a region of memory and retains the position in within that region.
|
|||
/// </summary>
|
|||
/// <typeparam name="T">The type of elements in the slice.</typeparam>
|
|||
[DebuggerTypeProxy(typeof(ReadOnlySlice<>.ReadOnlySliceDebugView))] |
|||
public readonly record struct ReadOnlySlice<T> : IReadOnlyList<T> where T : struct |
|||
{ |
|||
private readonly int _bufferOffset; |
|||
|
|||
/// <summary>
|
|||
/// Gets an empty <see cref="ReadOnlySlice{T}"/>
|
|||
/// </summary>
|
|||
public static ReadOnlySlice<T> Empty => new ReadOnlySlice<T>(Array.Empty<T>()); |
|||
|
|||
private readonly ReadOnlyMemory<T> _buffer; |
|||
|
|||
public ReadOnlySlice(ReadOnlyMemory<T> buffer) : this(buffer, 0, buffer.Length) { } |
|||
|
|||
public ReadOnlySlice(ReadOnlyMemory<T> buffer, int start, int length, int bufferOffset = 0) |
|||
{ |
|||
#if DEBUG
|
|||
if (start.CompareTo(0) < 0) |
|||
{ |
|||
throw new ArgumentOutOfRangeException(nameof (start)); |
|||
} |
|||
|
|||
if (length.CompareTo(buffer.Length) > 0) |
|||
{ |
|||
throw new ArgumentOutOfRangeException(nameof (length)); |
|||
} |
|||
#endif
|
|||
|
|||
_buffer = buffer; |
|||
Start = start; |
|||
Length = length; |
|||
_bufferOffset = bufferOffset; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets the start.
|
|||
/// </summary>
|
|||
/// <value>
|
|||
/// The start.
|
|||
/// </value>
|
|||
public int Start { get; } |
|||
|
|||
/// <summary>
|
|||
/// Gets the end.
|
|||
/// </summary>
|
|||
/// <value>
|
|||
/// The end.
|
|||
/// </value>
|
|||
public int End => Start + Length - 1; |
|||
|
|||
/// <summary>
|
|||
/// Gets the length.
|
|||
/// </summary>
|
|||
/// <value>
|
|||
/// The length.
|
|||
/// </value>
|
|||
public int Length { get; } |
|||
|
|||
/// <summary>
|
|||
/// Gets a value that indicates whether this instance of <see cref="ReadOnlySlice{T}"/> is Empty.
|
|||
/// </summary>
|
|||
public bool IsEmpty => Length == 0; |
|||
|
|||
/// <summary>
|
|||
/// Get the underlying span.
|
|||
/// </summary>
|
|||
public ReadOnlySpan<T> Span => _buffer.Span.Slice(_bufferOffset, Length); |
|||
|
|||
/// <summary>
|
|||
/// Get the buffer offset.
|
|||
/// </summary>
|
|||
public int BufferOffset => _bufferOffset; |
|||
|
|||
/// <summary>
|
|||
/// Get the underlying buffer.
|
|||
/// </summary>
|
|||
public ReadOnlyMemory<T> Buffer => _buffer; |
|||
|
|||
/// <summary>
|
|||
/// Returns a value to specified element of the slice.
|
|||
/// </summary>
|
|||
/// <param name="index">The index of the element to return.</param>
|
|||
/// <returns>The <typeparamref name="T"/>.</returns>
|
|||
/// <exception cref="IndexOutOfRangeException">
|
|||
/// Thrown when index less than 0 or index greater than or equal to <see cref="Length"/>.
|
|||
/// </exception>
|
|||
public T this[int index] |
|||
{ |
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
get |
|||
{ |
|||
#if DEBUG
|
|||
if (index.CompareTo(0) < 0 || index.CompareTo(Length) > 0) |
|||
{ |
|||
throw new ArgumentOutOfRangeException(nameof (index)); |
|||
} |
|||
#endif
|
|||
return Span[index]; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Returns a sub slice of elements that start at the specified index and has the specified number of elements.
|
|||
/// </summary>
|
|||
/// <param name="start">The start of the sub slice.</param>
|
|||
/// <param name="length">The length of the sub slice.</param>
|
|||
/// <returns>A <see cref="ReadOnlySlice{T}"/> that contains the specified number of elements from the specified start.</returns>
|
|||
public ReadOnlySlice<T> AsSlice(int start, int length) |
|||
{ |
|||
if (IsEmpty) |
|||
{ |
|||
return this; |
|||
} |
|||
|
|||
if (length == 0) |
|||
{ |
|||
return Empty; |
|||
} |
|||
|
|||
if (start < 0 || _bufferOffset + start > _buffer.Length - 1) |
|||
{ |
|||
throw new ArgumentOutOfRangeException(nameof(start)); |
|||
} |
|||
|
|||
if (_bufferOffset + start + length > _buffer.Length) |
|||
{ |
|||
throw new ArgumentOutOfRangeException(nameof(length)); |
|||
} |
|||
|
|||
return new ReadOnlySlice<T>(_buffer, start, length, _bufferOffset); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Returns a specified number of contiguous elements from the start of the slice.
|
|||
/// </summary>
|
|||
/// <param name="length">The number of elements to return.</param>
|
|||
/// <returns>A <see cref="ReadOnlySlice{T}"/> that contains the specified number of elements from the start of this slice.</returns>
|
|||
public ReadOnlySlice<T> Take(int length) |
|||
{ |
|||
if (IsEmpty) |
|||
{ |
|||
return this; |
|||
} |
|||
|
|||
if (length > Length) |
|||
{ |
|||
throw new ArgumentOutOfRangeException(nameof(length)); |
|||
} |
|||
|
|||
return new ReadOnlySlice<T>(_buffer, Start, length, _bufferOffset); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Bypasses a specified number of elements in the slice and then returns the remaining elements.
|
|||
/// </summary>
|
|||
/// <param name="length">The number of elements to skip before returning the remaining elements.</param>
|
|||
/// <returns>A <see cref="ReadOnlySlice{T}"/> that contains the elements that occur after the specified index in this slice.</returns>
|
|||
public ReadOnlySlice<T> Skip(int length) |
|||
{ |
|||
if (IsEmpty) |
|||
{ |
|||
return this; |
|||
} |
|||
|
|||
if (length > Length) |
|||
{ |
|||
throw new ArgumentOutOfRangeException(nameof(length)); |
|||
} |
|||
|
|||
return new ReadOnlySlice<T>(_buffer, Start + length, Length - length, _bufferOffset + length); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Returns an enumerator for the slice.
|
|||
/// </summary>
|
|||
public ImmutableReadOnlyListStructEnumerator<T> GetEnumerator() |
|||
{ |
|||
return new ImmutableReadOnlyListStructEnumerator<T>(this); |
|||
} |
|||
|
|||
IEnumerator<T> IEnumerable<T>.GetEnumerator() |
|||
{ |
|||
return GetEnumerator(); |
|||
} |
|||
|
|||
IEnumerator IEnumerable.GetEnumerator() |
|||
{ |
|||
return GetEnumerator(); |
|||
} |
|||
|
|||
int IReadOnlyCollection<T>.Count => Length; |
|||
|
|||
T IReadOnlyList<T>.this[int index] => this[index]; |
|||
|
|||
public static implicit operator ReadOnlySlice<T>(T[] array) |
|||
{ |
|||
return new ReadOnlySlice<T>(array); |
|||
} |
|||
|
|||
public static implicit operator ReadOnlySlice<T>(ReadOnlyMemory<T> memory) |
|||
{ |
|||
return new ReadOnlySlice<T>(memory); |
|||
} |
|||
|
|||
public static implicit operator ReadOnlySpan<T>(ReadOnlySlice<T> slice) => slice.Span; |
|||
|
|||
internal class ReadOnlySliceDebugView |
|||
{ |
|||
private readonly ReadOnlySlice<T> _readOnlySlice; |
|||
|
|||
public ReadOnlySliceDebugView(ReadOnlySlice<T> readOnlySlice) |
|||
{ |
|||
_readOnlySlice = readOnlySlice; |
|||
} |
|||
|
|||
public int Start => _readOnlySlice.Start; |
|||
|
|||
public int End => _readOnlySlice.End; |
|||
|
|||
public int Length => _readOnlySlice.Length; |
|||
|
|||
public bool IsEmpty => _readOnlySlice.IsEmpty; |
|||
|
|||
public ReadOnlySpan<T> Items => _readOnlySlice.Span; |
|||
} |
|||
} |
|||
} |
|||
Some files were not shown because too many files changed in this diff
Loading…
Reference in new issue